viem-goviem-go

Writing Contracts

Send transactions to modify smart contract state

Send transactions to modify smart contract state. Unlike read operations, writes require gas and must be signed.

Preparing a Contract Write

Use PrepareContractWrite to build a transaction for a contract call:

1import (
2 "github.com/ethereum/go-ethereum/common"
3 "github.com/ChefBingbong/viem-go/client"
4 "math/big"
5)
6
7// Prepare the transaction
8tx, err := publicClient.PrepareContractWrite(ctx, client.PrepareContractWriteOptions{
9 Address: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
10 ABI: `[{
11 "name": "transfer",
12 "type": "function",
13 "inputs": [
14 {"name": "to", "type": "address"},
15 {"name": "amount", "type": "uint256"}
16 ],
17 "outputs": [{"type": "bool"}]
18 }]`,
19 FunctionName: "transfer",
20 Args: []any{
21 common.HexToAddress("0xRecipient..."),
22 big.NewInt(1000000), // 1 USDC (6 decimals)
23 },
24 From: senderAddress,
25})
26if err != nil {
27 log.Fatal(err)
28}
29
30fmt.Printf("To: %s\n", tx.To.Hex())
31fmt.Printf("Data: 0x%x\n", tx.Data)
32fmt.Printf("Gas: %d\n", tx.Gas)

Parameters

PrepareContractWrite uses PrepareContractWriteOptions. wallet.WriteContract uses WriteContractParameters; WriteContractSync uses WriteContractSyncParameters (embeds WriteContractParameters plus polling/timeout). wallet.DeployContract uses DeployContractParameters.

Address (required for write)

  • Type: common.Address (PrepareContractWrite) or string (WriteContractParameters)
  • The contract address to call.

ABI (required)

  • Type: anystring, []byte, or *abi.ABI
  • Contract ABI for encoding the function call.

FunctionName (required for write)

  • Type: string
  • The name of the function to call (e.g. "transfer", "approve").

Args (optional)

  • Type: []any
  • Default: nil
  • Function arguments in order.

From / Account (required for write)

  • Type: common.Address (PrepareContractWrite) or Account (WriteContractParameters: optional if client has default account)
  • Sender address. For WriteContract / WriteContractSync, omit if the Wallet Client has a default account.

Value (optional)

  • Type: *big.Int
  • Default: nil
  • ETH to send with the transaction (for payable functions).

Gas, GasPrice, MaxFeePerGas, MaxPriorityFeePerGas (optional)

  • Type: uint64 (Gas for PrepareContractWrite), *big.Int (Gas in wallet params), *big.Int (GasPrice, MaxFeePerGas, MaxPriorityFeePerGas)
  • Default: Gas is estimated if not set; fees from chain or client defaults.
  • Override gas limit or fee parameters.

Nonce (optional)

  • Type: *uint64 (PrepareContractWrite) or *int (wallet params)
  • Default: Fetched from chain (pending nonce) if not provided.

WriteContractSync-only: PollingInterval, ThrowOnReceiptRevert, Timeout (optional)

  • PollingInterval: time.Duration — interval to poll for receipt. Default: client’s polling interval.
  • ThrowOnReceiptRevert: *bool — if true, return error when receipt status is reverted. Default: true.
  • Timeout: *time.Duration — max wait for receipt. Default: derived from chain block time.

Signing and Sending

After preparing, sign and send the transaction:

1import (
2 "github.com/ChefBingbong/viem-go/accounts"
3 "github.com/ChefBingbong/viem-go/utils/transaction"
4)
5
6// 1. Create account from private key
7account, _ := accounts.PrivateKeyToAccount("0x...")
8
9// 2. Prepare the transaction
10tx, _ := publicClient.PrepareContractWrite(ctx, client.PrepareContractWriteOptions{
11 Address: contractAddress,
12 ABI: abi,
13 FunctionName: "transfer",
14 Args: []any{recipient, amount},
15 From: common.HexToAddress(account.Address),
16})
17
18// 3. Convert to transaction for signing
19signableTx := &transaction.Transaction{
20 To: tx.To,
21 Data: tx.Data,
22 Value: tx.Value,
23 Gas: tx.Gas,
24 GasPrice: tx.GasPrice,
25 Nonce: *tx.Nonce,
26 ChainID: tx.ChainID,
27}
28
29// 4. Sign the transaction
30signedTx, err := account.SignTransaction(signableTx)
31if err != nil {
32 log.Fatal(err)
33}
34
35// 5. Send the signed transaction
36txHash, err := walletClient.SendRawTransaction(ctx, []byte(signedTx))
37if err != nil {
38 log.Fatal(err)
39}
40
41fmt.Printf("Transaction sent: %s\n", txHash.Hex())
42
43// 6. Wait for receipt
44receipt, err := publicClient.WaitForTransactionReceipt(ctx, txHash)
45fmt.Printf("Status: %d\n", receipt.Status)

Using Typed Contract Bindings

The ERC20 binding includes write methods:

import "github.com/ChefBingbong/viem-go/contracts/erc20"

token, _ := erc20.New(usdcAddress, publicClient)

// Prepare a transfer
tx, err := token.PrepareTransfer(ctx, recipient, amount, senderAddress)
if err != nil {
    log.Fatal(err)
}

// Sign and send...

Payable Functions

For functions that accept ETH, use the Value parameter:

tx, err := publicClient.PrepareContractWrite(ctx, client.PrepareContractWriteOptions{
    Address:      wethAddress,
    ABI:          `[{"name":"deposit","type":"function","inputs":[],"stateMutability":"payable"}]`,
    FunctionName: "deposit",
    From:         senderAddress,
    Value:        big.NewInt(1e18), // 1 ETH
})

Encoding Function Data

If you need just the calldata without a full transaction:

1import "github.com/ChefBingbong/viem-go/client"
2
3data, err := client.EncodeFunctionData(client.EncodeFunctionDataOptions{
4 ABI: `[{
5 "name": "transfer",
6 "type": "function",
7 "inputs": [
8 {"name": "to", "type": "address"},
9 {"name": "amount", "type": "uint256"}
10 ]
11 }]`,
12 FunctionName: "transfer",
13 Args: []any{recipient, amount},
14})
15
16fmt.Printf("Calldata: 0x%x\n", data)
17// Output: 0xa9059cbb000000000000000000000000...

Gas Estimation

Gas is automatically estimated, but you can override:

// Let it estimate (adds 20% buffer)
tx, _ := publicClient.PrepareContractWrite(ctx, client.PrepareContractWriteOptions{
    // ...
})
fmt.Printf("Estimated gas: %d\n", tx.Gas)

// Or specify manually
tx, _ := publicClient.PrepareContractWrite(ctx, client.PrepareContractWriteOptions{
    // ...
    Gas: 100000,
})

DeployContract

To deploy a new contract, use wallet.DeployContract with bytecode and optional constructor args. See Deploy Contract.

EstimateContractGas

Estimate gas for a write without sending: use public.EstimateContractGas. See Estimate Contract Gas.

EncodeFunctionData

client.EncodeFunctionData(opts) returns only the calldata (selector + encoded args) without building a full transaction. Useful for multicall or custom tx building. See ABI Encoding for encoding with a parsed ABI.

Error handling

tx, err := publicClient.PrepareContractWrite(ctx, opts)
if err != nil {
    if strings.Contains(err.Error(), "execution reverted") {
        log.Println("Transaction would revert")
    }
    return err
}

See also