Skip to main content
There are several types of wallets and wallet behaviors that compose supports.

Smart Wallets

If you just need to interact with smart contracts, then the easiest thing to do is just build one of our smart wallets. You’ll be able to see all of your wallets in the dashboard at https://app.goldsky.com/dashboard/compose/{appName}.

create a smart wallet

import { TaskContext } from "compose";

export async function main({ evm, env }: TaskContext) {
  // this is idempotent so the wallet is only created the first time this is called and is "retrieved" after that.
  // passing no args will create a "default" wallet for your app
  const wallet = await evm.wallet();
  console.log(wallet.address);

  // you can create multiple named wallets too.  This will generate multiple saved smart wallets that can be referenced by name in different 
  // tasks and task runs
  const walletOne = await evm.wallet({ name: "wallet-one" });
  const walletTwo = await evm.wallet({ name: "wallet-two" });

  // private key wallet
  const privateKeyWallet = await evm.wallet({ privateKey: env.MY_KEY });

  // now you can use these wallet to make transactions (see below for details)
}

Using wallets

Once you have a wallet created you can use it to write to smart contracts, see the full smart contract docs for more details
import { TaskContext } from "compose";

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

  const response = await fetch<{ bitcoin: { usd: number } }>(
    "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd"
  );

  // 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
  );
  const { hash } = await bitcoinOracleContract.setPrice(
    timestampAsBytes32,
    priceAsBytes32
  );
}
You can also make or simulate transactions with methods on the Wallet class:
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],
    {
      confirmations: 3, // this will not resolve the promise until the transaction has been seen in 3 blocks
      onReorg: {
        // this will replay the transaction with new nonce and new gas if it's reorged later on after the three confirmations have passed
        // see "Reorg Handling" for more info
        action: {
          type: "replay",
        },
        depth: 200,
      },
    }
  );
}

EOA wallets

You can also use EOS that you already own, this allows you to self-fund gas, send and receive tokens in your tasks, and interact with smart contracts in which an EOA you already own has privileges on particular contract methods. Currently we support storing your EOA private key in Compose’s [secret management system], but in the future you’ll be able to use private keys that are secured within TEEs. EOA wallets never pass their private keys outside of the task process and they sign requests passed in unsigned from the host process. When tasks run in TEEs they’ll be able to use private keys very securely within the TEE, never exposing it to any part of the stack ouside of the TEE. This will empower Compose to run the most security sensitive use cases. First, you’ll need to store the private key in Goldsky’s secret management system and reference it in your compose.yaml file. You can see details on how to do that in the Secrets docs. Once you have your private key secret stored, you can use it to create a wallet:
import { TaskContext } from "compose";

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

  // now you can use the wallet the same as any other wallet
}

Gas sponsoring

By default, smart wallets (wallets created without a private key) will use our gas sponsoring, allowing you to not think about and manage funding wallets for gas.
Private key wallets (EOA wallets) do not have gas sponsoring enabled by default and will need to be funded with gas tokens. However you can always override this behavior through configuration. When you use gas sponsoring you just pay the “gas bill” as part of your normal monthly goldsky bill, avoiding the need to deal with the complex budgetary and tax issues of purchasing gas tokens. When you don’t use gas sponsoring, you’ll need to get your wallet address from the compose dashboard at https://app.goldsky.com/dashboard/compose/{appName} and then transfer gas tokens to that wallet through a wallet or an exchange.

Override default gas sponsoring behavior

import { TaskContext } from "compose";

export async function main({ evm, env }: TaskContext) {
  // disable gas sponsoring for a smart wallet (default is true for smart wallets)
  const wallet = await evm.wallet({ name: "self-funded-wallet", sponsorGas: false });

  // enable gas sponsoring for an EOA wallet (default is false for private key wallets)
  const privateKeyWallet = await evm.wallet({ privateKey: env.MY_PRIVATE_KEY_SECRET, sponsorGas: true });
}

Full wallet interface

export interface WalletConfig {
  name?: string; // defaults to "default"
  privateKey?: string;
  sponsorGas?: boolean; // defaults to true if no privateKey and false if privateKey
}

export interface IWallet {
  readonly name: string;
  writeContract<Args = unknown[]>(
    chain: Chain,
    contractAddress: string,
    functionSig: string,
    args: Args,
    confirmation?: TransactionConfirmation,
    retryConfig?: HostFunctionRetryConfig
  ): Promise<{ hash: string }>;
  readContract<Args = unknown[]>(
    chain: Chain,
    contractAddress: string,
    functionSig: string,
    args: Args,
    retryConfig?: HostFunctionRetryConfig
  ): Promise<{ hash: string }>;
  simulate: (
    chain: Chain,
    contractAddress: string,
    functionSig: string,
    args: unknown[],
    retryConfig?: HostFunctionRetryConfig
  ) => Promise<unknown>;
  transfer(
    chain: Chain,
    amount: string;
    recipient: string;
  ): Promise<{ hash: string }>;
}

type Wallet = (config: WalletConfig) => Promise<IWallet>;