Prerequisites
To complete this tutorial, you need:- Node.js v22+ installed
- Deployed contract on Arc - Complete the Deploy contracts tutorial first
- Contract address - The address of your deployed contract
- Funded wallet - Dev-controlled wallet with testnet USDC for gas fees
- API credentials - Your API key and Entity Secret from the Developer Console
This tutorial assumes you have a contract already deployed on Arc Testnet with
a funded dev-controlled wallet. If you don’t, follow the Deploy contracts
tutorial first.
- ERC-20
- ERC-721
- ERC-1155
- Airdrop
Interact with ERC-20 contracts
ERC-20 tokens support standard fungible token operations. You’ll learn to mint new tokens and transfer them between addresses.Mint tokens
Use themintTo function to mint tokens. The wallet must have MINTER_ROLE.Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,uint256)",
abiParameters: [
process.env.WALLET_ADDRESS,
"1000000000000000000", // 1 token with 18 decimals
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Token decimals: ERC-20 tokens typically use 18 decimals. To mint 1 token,
use
1000000000000000000 (1 × 10^18).Transfer tokens
Use thetransfer function to send tokens to another address.Report incorrect code
Copy
Ask AI
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "transfer(address,uint256)",
abiParameters: [
process.env.RECIPIENT_WALLET_ADDRESS,
"1000000000000000000", // 1 token with 18 decimals
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Complete script
Here’s the complete script combining mint and transfer operations:Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
async function main() {
// Mint tokens
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,uint256)",
abiParameters: [
process.env.WALLET_ADDRESS,
"1000000000000000000", // 1 token with 18 decimals
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Mint transaction:", mintResponse.data?.id);
// Transfer tokens
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "transfer(address,uint256)",
abiParameters: [
process.env.RECIPIENT_WALLET_ADDRESS,
"1000000000000000000", // 1 token with 18 decimals
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Transfer transaction:", transferResponse.data?.id);
}
main();
Report incorrect code
Copy
Ask AI
npx tsx --env-file=.env interact-erc20.ts
Check transaction status
After executing a contract function, check the transaction status usingGET /transactions/{id}:Report incorrect code
Copy
Ask AI
const transactionResponse = await circleDeveloperSdk.getTransaction({
id: "<TRANSACTION_ID>",
});
Report incorrect code
Copy
Ask AI
{
"data": {
"transaction": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"blockchain": "ARC-TESTNET",
"state": "COMPLETE",
"txHash": "0xb9e8774147fe086e5864828457f2bf3f8f25a356f6df4c920a677bfd3c3e9ac7"
}
}
}
View your transaction on the Arc Testnet
Explorer by searching for the
txHash.Interact with ERC-721 contracts
ERC-721 tokens are unique tokens. Each token has a unique ID and can have associated metadata stored on IPFS or other storage.Mint tokens
Use themintTo function to mint tokens. The wallet must have MINTER_ROLE.Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,string)",
abiParameters: [
process.env.WALLET_ADDRESS,
"ipfs://bafkreibdi6623n3xpf7ymk62ckb4bo75o3qemwkpfvp5i25j66itxvsoei",
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Metadata URI: The second parameter is the token metadata URI. It typically
points to an IPFS hash containing the token’s metadata (name, description,
image, etc.). You can use the example IPFS URI from the code sample for
testing.
Transfer tokens
Use thetransferFrom or safeTransferFrom function to transfer tokens between
addresses.Report incorrect code
Copy
Ask AI
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "safeTransferFrom(address,address,uint256)",
abiParameters: [
"<FROM_ADDRESS>",
"<TO_ADDRESS>",
"1", // Token ID
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Complete script
Here’s the complete script combining mint and transfer operations:Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
async function main() {
// Mint token
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,string)",
abiParameters: [
process.env.WALLET_ADDRESS,
"ipfs://bafkreibdi6623n3xpf7ymk62ckb4bo75o3qemwkpfvp5i25j66itxvsoei",
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Mint transaction:", mintResponse.data?.id);
// Transfer token (token ID 1)
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "safeTransferFrom(address,address,uint256)",
abiParameters: [
process.env.WALLET_ADDRESS,
process.env.RECIPIENT_WALLET_ADDRESS,
"1", // Token ID
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Transfer transaction:", transferResponse.data?.id);
}
main();
Report incorrect code
Copy
Ask AI
npx tsx --env-file=.env interact-erc721.ts
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Check transaction status
Check the transaction status usingGET /transactions/{id}:Report incorrect code
Copy
Ask AI
const transactionResponse = await circleDeveloperSdk.getTransaction({
id: "<TRANSACTION_ID>",
});
Report incorrect code
Copy
Ask AI
{
"data": {
"transaction": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"blockchain": "ARC-TESTNET",
"state": "COMPLETE",
"txHash": "0xb9e8774147fe086e5864828457f2bf3f8f25a356f6df4c920a677bfd3c3e9ac7"
}
}
}
Interact with ERC-1155 contracts
ERC-1155 contracts support multiple token types in a single contract. Each token has a unique ID and can be fungible or non-fungible.Mint tokens
Use themintTo function to mint tokens. The wallet must have MINTER_ROLE.
The first mint requires the maximum uint256 value to create token ID 0. For
subsequent mints, always use 0 which creates the next token ID.Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,uint256,string,uint256)",
abiParameters: [
process.env.WALLET_ADDRESS,
"115792089237316195423570985008687907853269984665640564039457584007913129639935", // Max uint256 = ID 0
"ipfs://bafkreibdi6623n3xpf7ymk62ckb4bo75o3qemwkpfvp5i25j66itxvsoei",
"1", // Amount
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
ERC-1155 Token ID Creation: The first mint of each token ID requires
passing the maximum uint256 value (
2^256 - 1 or
115792089237316195423570985008687907853269984665640564039457584007913129639935)
to create token ID 0 in the contract. For all subsequent mints, use 0 which
creates the next sequential token ID (1, 2, 3, etc.). This is an ERC-1155
standard requirement for lazy minting, where token IDs are created on demand
rather than pre-initialized.Batch transfer tokens
Use thesafeBatchTransferFrom function to transfer multiple token types in a
single transaction.Report incorrect code
Copy
Ask AI
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature:
"safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)",
abiParameters: [
"<FROM_ADDRESS>",
"<TO_ADDRESS>",
["0"], // Token IDs
["1"], // Amounts
"0x", // Empty bytes
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Complete script
Here’s the complete script combining mint and batch transfer operations:Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
async function main() {
// Mint tokens (token ID 0)
const mintResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "mintTo(address,uint256,string,uint256)",
abiParameters: [
process.env.WALLET_ADDRESS,
"115792089237316195423570985008687907853269984665640564039457584007913129639935", // Max uint256 = ID 0
"ipfs://bafkreibdi6623n3xpf7ymk62ckb4bo75o3qemwkpfvp5i25j66itxvsoei",
"1", // Amount
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Mint transaction:", mintResponse.data?.id);
// Batch transfer tokens
const transferResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature:
"safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)",
abiParameters: [
process.env.WALLET_ADDRESS,
process.env.RECIPIENT_WALLET_ADDRESS,
["0"], // Token IDs
["1"], // Amounts
"0x", // Empty bytes
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Transfer transaction:", transferResponse.data?.id);
}
main();
Report incorrect code
Copy
Ask AI
npx tsx --env-file=.env interact-erc1155.ts
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Check transaction status
Check the transaction status usingGET /transactions/{id}:Report incorrect code
Copy
Ask AI
const transactionResponse = await circleDeveloperSdk.getTransaction({
id: "<TRANSACTION_ID>",
});
Report incorrect code
Copy
Ask AI
{
"data": {
"transaction": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"blockchain": "ARC-TESTNET",
"state": "COMPLETE",
"txHash": "0xb9e8774147fe086e5864828457f2bf3f8f25a356f6df4c920a677bfd3c3e9ac7"
}
}
}
Execute airdrop operations
The Airdrop contract enables mass token distribution to multiple recipients.Prerequisites
Before executing an airdrop, you need:- A token contract address - Deploy one using the ERC-20, ERC-721, or ERC-1155 templates, or use an existing token
- Token balance - Your wallet must hold enough tokens to distribute
- Token approval - Call the
approveorsetApprovalForAllfunction on your token contract to allow the airdrop contract to transfer tokens
Execute an ERC-20 airdrop
Use theairdropERC20 function to distribute ERC-20 tokens to multiple
recipients.Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
const airdropResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "airdropERC20(address,(address,uint256)[])",
abiParameters: [
"<TOKEN_CONTRACT_ADDRESS>", // ERC-20 token contract address
[
["<RECIPIENT_ADDRESS_1>", "1000000000000000000"],
["<RECIPIENT_ADDRESS_2>", "2000000000000000000"],
],
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Token contract: The first parameter is the address of the ERC-20 token
contract you want to airdrop. You must deploy this contract first using the
Deploy contracts tutorial.
Execute an ERC-721 airdrop
Use theairdropERC721 function to distribute tokens to multiple recipients.Report incorrect code
Copy
Ask AI
const airdropResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "airdropERC721(address,(address,uint256)[])",
abiParameters: [
"<TOKEN_CONTRACT_ADDRESS>", // ERC-721 token contract address
[
["<RECIPIENT_ADDRESS_1>", "1"], // Token ID 1
["<RECIPIENT_ADDRESS_2>", "2"], // Token ID 2
],
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Execute an ERC-1155 airdrop
Use theairdropERC1155 function to distribute ERC-1155 tokens to multiple
recipients.Report incorrect code
Copy
Ask AI
const airdropResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "airdropERC1155(address,(address,uint256,uint256)[])",
abiParameters: [
"<TOKEN_CONTRACT_ADDRESS>", // ERC-1155 token contract address
[
["<RECIPIENT_ADDRESS_1>", "0", "10"], // Token ID 0, amount 10
["<RECIPIENT_ADDRESS_2>", "1", "5"], // Token ID 1, amount 5
],
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Complete script
Here’s the complete script for executing an ERC-20 airdrop. You can adapt it for ERC-721 or ERC-1155 by changing the function signature and parameters as shown in the examples previously:Report incorrect code
Copy
Ask AI
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY,
entitySecret: process.env.CIRCLE_ENTITY_SECRET,
});
async function main() {
// Execute ERC-20 airdrop
const airdropResponse =
await circleDeveloperSdk.createContractExecutionTransaction({
walletId: process.env.WALLET_ID,
abiFunctionSignature: "airdropERC20(address,(address,uint256)[])",
abiParameters: [
process.env.TOKEN_CONTRACT_ADDRESS, // ERC-20 token contract address
[
[process.env.RECIPIENT_ADDRESS_1, "1000000000000000000"],
[process.env.RECIPIENT_ADDRESS_2, "2000000000000000000"],
],
],
contractAddress: process.env.CONTRACT_ADDRESS,
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log("Airdrop transaction:", airdropResponse.data?.id);
// For ERC-721 airdrop, use:
// abiFunctionSignature: "airdropERC721(address,(address,uint256)[])"
// abiParameters: [tokenAddress, [[recipient1, tokenId1], [recipient2, tokenId2]]]
// For ERC-1155 airdrop, use:
// abiFunctionSignature: "airdropERC1155(address,(address,uint256,uint256)[])"
// abiParameters: [tokenAddress, [[recipient1, tokenId, amount], [recipient2, tokenId, amount]]]
}
main();
Report incorrect code
Copy
Ask AI
npx tsx --env-file=.env interact-airdrop.ts
Report incorrect code
Copy
Ask AI
{
"data": {
"id": "601a0815-f749-41d8-b193-22cadd2a8977",
"state": "INITIATED"
}
}
Check transaction status
Check the transaction status usingGET /transactions/{id}:Report incorrect code
Copy
Ask AI
const transactionResponse = await circleDeveloperSdk.getTransaction({
id: "<TRANSACTION_ID>",
});
Summary
After completing this tutorial, you’ve learned how to:- Execute contract functions using the Circle SDKs
- Mint and transfer tokens for your deployed contracts
- Check transaction status and retrieve transaction details
- Perform contract-specific operations based on token type