Smart contracts live on a blockchain and programmatically enforce and execute the terms of an agreement. Integrating smart contracts with API Wallets enables:
Complex, multi-step operations triggered by a single user action.
Offchain actions with onchain verification and execution.
Limitless interactions with any onchain app or protocol.
However, any other contracts require providing the ABI. If you are interested in cached ABIs for more common smart contracts, reach out to us in the #wallet-api channel of the CDP Discord.A contract ABI (Application Binary Interface) is the way to interact with a smart contract, both externally and contract-to-contract. It is a file defining the various methods and parameters of the contract.The SDK also supports payable transactions, which are transactions that send ETH to the contract. See the Basename Registration guide for an example of a payable transaction.
Here’s an example of using invokeContract to transfer an ERC-721 NFT:
Copy
Ask AI
import { Coinbase, Wallet } from "@coinbase/coinbase-sdk";import os from "os";const coinbase = Coinbase.configureFromJson({ filePath: `${os.homedir()}/Downloads/cdp_api_key.json`, useServerSigner: true });const wallet = await Wallet.create({ networkId: Coinbase.networks.EthereumMainnet });// Since this is an ERC-721 NFT, there's no need to define the ABIconst transferFromArgs = { from: "0xFrom", to: "0xmyEthereumAddress", tokenId: "1000",};const contractInvocation = await wallet.invokeContract({ contractAddress: "0xYourNFTContractAddress", method: "transferFrom", args: transferFromArgs,});// Wait for the contract invocation transaction to land on-chain.await contractInvocation.wait();
The CDP SDK supports parsing transaction receipts and logs from contract invocations. Here’s an example.
Copy
Ask AI
const transferArgs = { to: "0xmyEthereumAddress", value: "1",};const contractInvocation = await wallet.invokeContract({ contractAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract on Base Sepolia method: "transfer", args: transferArgs,});await contractInvocation.wait();// Get the transaction content and receipt.const transactionContent = contractInvocation.getTransaction().content();const receipt = transactionContent.receipt;const logs = receipt.logs;for (const log of logs) { console.log(log.address); // Contract address that emitted the log event console.log(log.topics); // Array of decoded topics for the log event console.log(log.data); // Data for the log event}
Smart contract support being queried and returning state data without explicitly modifying their state. Readable functions are useful for retrieving current contract state, calculating values, or checking conditions before performing transactions.Including an ABI is optional, but without it, the CDP SDK will not be able to determine the return type of the function, and the return value will not be parsed. With an ABI, Node.js types are inferred and can be used directly. In Python, the SDK will automatically convert to the expected type at runtime.
Here’s an example of reading from a smart contract using the CDP SDK:
Copy
Ask AI
import { Coinbase, readContract } from "@coinbase/coinbase-sdk";// balance is automatically of type bigintconst balance = await readContract({networkId: "base-mainnet",abi: erc20Abi,contractAddress: USDC_BASE_MAINNET_ADDRESS as 0x${string},method: "balanceOf",args: { account: "0xF977814e90dA44bFA03b6295A0616a897441aceC" },});console.log(balance);// balance is of type unknown, so as bigint is neededconst balance = await readContract({networkId: "base-mainnet",contractAddress: USDC_BASE_MAINNET_ADDRESS as 0x${string},method: "balanceOf",args: { account: "0xF977814e90dA44bFA03b6295A0616a897441aceC" },}) as bigint;