> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goldsky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# evm Smart Contracts

Compose allows you to interact with smart contracts with type safety and flexible wallet options. See [wallets](./wallets) for details on the different types of wallets.

## 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:

```bash theme={null}
compose codegen
```

The generated file is written to `.compose/generated/index.js`. On `compose deploy`, the CLI bundles this file alongside your tasks so the same typed classes are available in the cloud.

### Generated classes

For each ABI file (e.g., `src/contracts/BitcoinOracleContract.json`), Compose generates a typed class that you can access via `evm.contracts`:

```typescript theme={null}
import { TaskContext } from "compose";

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

  // Access the generated contract class via evm.contracts
  const bitcoinOracleContract = new evm.contracts.BitcoinOracleContract(
    env.ORACLE_ADDRESS as `0x${string}`,
    evm.chains.base,
    wallet
  );

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

  // Call a state-changing method — returns { hash, receipt, userOpHash? }
  const { hash } = await bitcoinOracleContract.setPrice(
    timestampAsBytes32,
    priceAsBytes32
  );

  // Call a view method — returns the decoded value directly
  const result = await bitcoinOracleContract.getLatestPrice();
}
```

Contract methods are called directly on the instance without `.write` or `.read` suffixes. View/pure functions return their decoded value directly, while state-changing functions return `{ hash, receipt, userOpHash? }` (the `userOpHash` is present only when gas sponsoring routes the transaction through a bundler).

## Decoding event logs

Each generated contract class includes a static `decodeEventLog()` method for decoding onchain events with full type safety. This is commonly used in tasks triggered by [onchain events](../../task-triggers#chain-event-triggers):

```typescript theme={null}
import { TaskContext, OnchainEvent } from "compose";

export async function main({ evm }: TaskContext, payload: OnchainEvent) {
  // Decode using a generated contract class — returns a typed union of all events in the ABI
  const decoded = await evm.contracts.MyContract.decodeEventLog(payload);

  if (decoded.eventName === "Transfer") {
    console.log("Transfer:", decoded.args);
  }

  // Or use the top-level decodeEventLog with any ABI
  const myAbi = [/* ... */];
  const decoded2 = await evm.decodeEventLog(myAbi, payload);
}
```

You can also use `decodeEventLog` with a generic type parameter for a single known event type:

```typescript theme={null}
const decoded = await evm.contracts.MyContract.decodeEventLog<TransferEventDecoded>(payload);
```

## Direct wallet methods

You can also interact with contracts directly through wallet methods without generated classes. This is useful for one-off calls or contracts you don't want to check an ABI into the repo for:

```typescript theme={null}
import { TaskContext } from "compose";

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

  // Write to a contract — returns { hash, receipt, userOpHash? }
  const { hash, receipt } = await wallet.writeContract(
    evm.chains.polygon,
    env.CONTRACT_ADDRESS as `0x${string}`,
    "reportPayouts(bytes32,uint256[])",
    [resultId, payouts]
  );

  // Read from a contract — returns the decoded value directly
  const balance = await wallet.readContract<bigint>(
    evm.chains.polygon,
    env.CONTRACT_ADDRESS as `0x${string}`,
    "balanceOf(address) returns (uint256)",
    [wallet.address]
  );
}
```

See the [wallets documentation](./wallets) for the full `writeContract`, `readContract`, and `sendTransaction` signatures, gas handling, confirmations, and reorg protection options.
