This page shows common contract patterns. See Contract Factory for complete API and ERC Standards for token utilities.
Read Contract
Copy
Ask AI
import { Effect, Duration, Schedule, Layer } from 'effect'
import { Contract, Provider, HttpTransport, withTimeout, withRetrySchedule } from 'voltaire-effect'
const erc20Abi = [
{ type: 'function', name: 'balanceOf', inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
{ type: 'function', name: 'symbol', inputs: [], outputs: [{ type: 'string' }], stateMutability: 'view' },
{ type: 'function', name: 'decimals', inputs: [], outputs: [{ type: 'uint8' }], stateMutability: 'view' }
] as const
// Compose layers first, then provide once
const ProviderLayer = Provider.pipe(
Layer.provide(HttpTransport('https://mainnet.infura.io/v3/YOUR_KEY'))
)
const program = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', erc20Abi)
return yield* Effect.all({
symbol: token.read.symbol(),
decimals: token.read.decimals(),
balance: token.read.balanceOf('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
})
}).pipe(Effect.provide(ProviderLayer))
Write Contract
Copy
Ask AI
import { Layer } from 'effect'
import { Signer, LocalAccount, Provider, HttpTransport } from 'voltaire-effect'
import { Secp256k1Live, KeccakLive } from 'voltaire-effect/crypto'
import { Hex } from '@tevm/voltaire'
const privateKey = Hex.fromHex('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80')
// Compose all layers first
const CryptoLayer = Layer.mergeAll(Secp256k1Live, KeccakLive)
const TransportLayer = HttpTransport('https://eth.llamarpc.com')
const SignerLayer = Layer.mergeAll(
Signer.fromPrivateKey(privateKey, Provider),
CryptoLayer
).pipe(Layer.provide(TransportLayer))
const program = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', erc20Abi)
return yield* token.write.transfer('0x70997970C51812dc3A010C7d01b50e0d17dc79C8', 100_000_000n)
}).pipe(Effect.provide(SignerLayer))
Simulate (Dry Run)
Simulate a write before sending to catch reverts:Copy
Ask AI
// Compose layers first
const ProviderLayer = Provider.pipe(
Layer.provide(HttpTransport('https://eth.llamarpc.com'))
)
const program = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', erc20Abi)
return yield* token.simulate.transfer('0x70997970C51812dc3A010C7d01b50e0d17dc79C8', 100_000_000n)
}).pipe(Effect.provide(ProviderLayer))
Query Events
Query historical Transfer events from the blockchain:Copy
Ask AI
const eventAbi = [
{ type: 'event', name: 'Transfer', inputs: [
{ name: 'from', type: 'address', indexed: true },
{ name: 'to', type: 'address', indexed: true },
{ name: 'value', type: 'uint256', indexed: false }
]}
] as const
// Compose layers first
const ProviderLayer = Provider.pipe(
Layer.provide(HttpTransport('https://eth.llamarpc.com'))
)
const program = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', eventAbi)
return yield* token.getEvents('Transfer', { fromBlock: 'latest', toBlock: 'latest' })
}).pipe(Effect.provide(ProviderLayer))
Complete ERC20 Workflow
A full workflow: read token info, transfer, wait for confirmation, verify balance change:Copy
Ask AI
import { Effect, Layer } from 'effect'
import { Contract, waitForTransactionReceipt, Signer, Provider, HttpTransport } from 'voltaire-effect'
import { Secp256k1Live, KeccakLive } from 'voltaire-effect/crypto'
// Compose all layers first
const CryptoLayer = Layer.mergeAll(Secp256k1Live, KeccakLive)
const TransportLayer = HttpTransport('https://eth.llamarpc.com')
const SignerLayer = Layer.mergeAll(
Signer.fromPrivateKey(privateKey, Provider),
CryptoLayer
).pipe(Layer.provide(TransportLayer))
const program = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', erc20Abi)
const { symbol, decimals } = yield* Effect.all({ symbol: token.read.symbol(), decimals: token.read.decimals() })
const balanceBefore = yield* token.read.balanceOf('0x70997970C51812dc3A010C7d01b50e0d17dc79C8')
const txHash = yield* token.write.transfer('0x70997970C51812dc3A010C7d01b50e0d17dc79C8', 100_000_000n)
yield* waitForTransactionReceipt(txHash, { confirmations: 1 })
const balanceAfter = yield* token.read.balanceOf('0x70997970C51812dc3A010C7d01b50e0d17dc79C8')
return { symbol, decimals, txHash, transferred: balanceAfter - balanceBefore }
}).pipe(Effect.provide(SignerLayer))
Uniswap V2 Swap
A real-world example using Uniswap V2 Router:Copy
Ask AI
const routerAbi = [
{ type: 'function', name: 'swapExactTokensForTokens', inputs: [
{ name: 'amountIn', type: 'uint256' }, { name: 'amountOutMin', type: 'uint256' },
{ name: 'path', type: 'address[]' }, { name: 'to', type: 'address' }, { name: 'deadline', type: 'uint256' }
], outputs: [{ type: 'uint256[]' }], stateMutability: 'nonpayable' },
{ type: 'function', name: 'getAmountsOut', inputs: [
{ name: 'amountIn', type: 'uint256' }, { name: 'path', type: 'address[]' }
], outputs: [{ type: 'uint256[]' }], stateMutability: 'view' }
] as const
const myAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
// Compose all layers first
const CryptoLayer = Layer.mergeAll(Secp256k1Live, KeccakLive)
const TransportLayer = HttpTransport('https://eth.llamarpc.com')
const SignerLayer = Layer.mergeAll(
Signer.fromPrivateKey(privateKey, Provider),
CryptoLayer
).pipe(Layer.provide(TransportLayer))
const program = Effect.gen(function* () {
const router = yield* Contract('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', routerAbi)
const path = ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2']
const amounts = yield* router.read.getAmountsOut(1000_000_000n, path)
const amountOutMin = (amounts[1] * 95n) / 100n // 5% slippage
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200) // 20 minutes
return yield* router.write.swapExactTokensForTokens(1000_000_000n, amountOutMin, path, myAddress, deadline)
}).pipe(Effect.provide(SignerLayer))
See Also
- Contract Factory — Complete Contract API
- Provider Service — JSON-RPC operations and
readContract - Multicall — Batch multiple contract reads
- ERC Standards — ERC-20, ERC-721, ERC-1155 utilities
- Event Streaming — Real-time event watching
- Voltaire ABI — ABI encoding/decoding
Timeouts and Retries
Use Effect-idiomatic helpers for timeouts and retries:Copy
Ask AI
// Compose layers first
const ProviderLayer = Provider.pipe(
Layer.provide(HttpTransport('https://mainnet.infura.io/v3/YOUR_KEY'))
)
const resilientRead = Effect.gen(function* () {
const token = yield* Contract('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', erc20Abi)
return yield* token.read.balanceOf('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')
}).pipe(
// Timeout with Duration string or Duration.seconds(5)
withTimeout("5 seconds"),
// Retry with Effect Schedule
withRetrySchedule(
Schedule.exponential("500 millis").pipe(
Schedule.jittered,
Schedule.compose(Schedule.recurs(3))
)
),
Effect.provide(ProviderLayer)
)

