Skip to main content
Compose allows you to interact with smart contracts with type safety and flexible wallet options. See wallets for details on the different types of wallets. We also have some built-in contract support for common interfaces like erc20.

Code generation

To interact with smart contracts with full type safety, place your ABI JSON files in the src/contracts/ folder. Compose automatically generates TypeScript classes when you run compose start or compose deploy. You can also manually trigger code generation:
compose codegen

Generated classes

For each ABI file (e.g., src/contracts/USDC.json), Compose generates a typed class that you can access via evm.contracts:
import { TaskContext } from "compose";

export async function main({ evm, env, fetch }: TaskContext) {
  const wallet = await evm.wallet({ name: "my-wallet" });

  // Access generated contract class via evm.contracts
  const usdc = new evm.contracts.USDC(
    "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    evm.chains.baseSepolia,
    wallet
  );

  // Convert timestamp and price to bytes32 format
  const timestamp = Date.now();
  const bitcoinPrice = response.bitcoin.usd;
  const timestampAsBytes32 = `0x${timestamp.toString(16).padStart(64, "0")}`;
  const priceAsBytes32 = `0x${Math.round(bitcoinPrice * 100).toString(16).padStart(64, "0")}`;

  const bitcoinOracleContract = new context.evm.contracts.BitcoinOracleContract(
    env.ORACLE_ADDRESS,
    evm.chains.base,
    wallet
  );

  // execute the "setPrice" method
  const { hash } = await bitcoinOracleContract.setPrice(
    timestampAsBytes32,
    priceAsBytes32
  );

  // read a "getLatestPrice" view method
  const result = await bitcoinOracleContract.getLatestPrice();
}
Contract methods are called directly on the instance without .write or .read suffixes. View functions return their result directly, while state-changing functions return { hash, receipt }.

Decoding event logs

Each generated contract class includes a static decodeEventLog() method for decoding onchain events with full type safety:
import { TaskContext } from "compose";

export async function main({ evm, env }: TaskContext) {
  const wallet = await evm.wallet();

  const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
  const payouts = [1000n, 2000n, 3000n];

  const { hash } = await wallet.writeContract(
    evm.chains.polygon,
    env.CONTRACT_ADDRESS as `0x${string}`,
    "reportPayouts(bytes32,uint256[])",
    [resultId, payouts]
  );

  // Read from a contract
  const balance = await wallet.readContract(
    evm.chains.polygon,
    env.CONTRACT_ADDRESS,
    "balanceOf(address) returns (uint256)",
    [walletAddress]
  );
}
Read more about wallet methods in the wallets documentation.