This tutorial guides you through deploying smart contracts on Arc Testnet with
Circle Contracts .
You’ll create a
Circle Dev-Controlled SCA Wallet ,
then deploy pre-audited contract templates (ERC-20, ERC-721, ERC-1155, Airdrop).
With SCA wallets,
Circle Gas Station automatically
sponsors your transaction fees on Arc Testnet.
These pre-audited templates represent building blocks: ERC-20 for money and
liquidity, ERC-721 for identity and unique rights, ERC-1155 for scalable
financial instruments, and Airdrops for distributing incentives. To learn more
about available templates, visit the
Templates Overview
to review all templates and their options.
Prerequisites
To complete this tutorial, you need:
Node.js v22+ installed
Circle Developer Account - Sign up on the
Developer Console
API Key - Create in the Console: Keys → Create a key → API key →
Standard Key
Entity Secret - Required to initialize the Circle Dev-Controlled Wallets
SDK. Learn how to
register your Entity Secret
Set up your environment
Before deploying any template, you need a working project and a funded
dev-controlled wallet on Arc Testnet. Complete the steps in this section once.
Then reuse the same wallet and credentials across all template deployments
below.
Step 1: Set up your project
Set up your local environment and install the required dependencies.
1.1. Create a new project
Create a new directory. Navigate to it and start a new project with default
settings.
mkdir hello-arc
cd hello-arc
npm init -y
npm pkg set type=module
# Add run scripts for wallet creation and contract deployment
npm pkg set scripts.create-wallet="tsx --env-file=.env create-wallet.ts"
npm pkg set scripts.check-balance="tsx --env-file=.env check-balance.ts"
npm pkg set scripts.deploy-erc20="tsx --env-file=.env deploy-erc20.ts"
npm pkg set scripts.deploy-erc721="tsx --env-file=.env deploy-erc721.ts"
npm pkg set scripts.deploy-erc1155="tsx --env-file=.env deploy-erc1155.ts"
npm pkg set scripts.deploy-airdrop="tsx --env-file=.env deploy-airdrop.ts"
npm pkg set scripts.check-transaction="tsx --env-file=.env check-transaction.ts"
npm pkg set scripts.get-contract="tsx --env-file=.env get-contract.ts"
1.2. Install dependencies
In the project directory, install the
Circle Dev-Controlled Wallets SDK
and the Circle Contracts SDK .
Dev-Controlled Wallets are Circle-managed wallets that your app controls via
APIs. You can deploy contracts and submit transactions without managing private
keys directly. You can also call the
Circle Wallets API and
Circle Contracts API
directly if you can’t use the SDKs in your project.
npm install @circle-fin/developer-controlled-wallets @circle-fin/smart-contract-platform
npm install --save-dev tsx typescript @types/node
This command creates a tsconfig.json file:
# Initialize a TypeScript project
npx tsc --init
Then, edit the tsconfig.json file:
# Replace the contents of the generated file
cat << 'EOF' > tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"types": ["node"]
}
}
EOF
Create a .env file in the project directory with your Circle credentials.
Replace these placeholders with your own credentials:
CIRCLE_API_KEY: your API key should be either environment-prefixed (for
example, TEST_API_KEY:abc123:def456 or LIVE_API_KEY:xyz:uvw) or
base64-encoded strings.
CIRCLE_ENTITY_SECRET: your entity secret should be 64 lowercase alphanumeric
characters.
CIRCLE_WEB3_API_KEY: (Python only) set to the same value as CIRCLE_API_KEY
for Python SDK compatibility.
echo "CIRCLE_API_KEY={YOUR_API_KEY}
CIRCLE_ENTITY_SECRET={YOUR_ENTITY_SECRET}
CIRCLE_WEB3_API_KEY={YOUR_API_KEY}
WALLET_ID={WALLET_ID_FROM_STEP_2}
WALLET_ADDRESS={WALLET_ADDRESS_FROM_STEP_2}
TRANSACTION_ID={TRANSACTION_ID_FROM_DEPLOYMENT}
CONTRACT_ID={CONTRACT_ID_FROM_DEPLOYMENT}" > .env
You’ll update WALLET_ID and WALLET_ADDRESS after creating your wallet in
Step 2, then update TRANSACTION_ID and CONTRACT_ID after running the
deployment script in Step 4.
Important: These are sensitive credentials. Do not commit them to version
control or share them publicly.
Step 2: Set up your wallet
Create a dev-controlled wallet and fund it with testnet USDC. This covers gas
fees for deploying your smart contract.
2.1. Create a wallet
Import the Wallets SDK and start the client with your API key and Entity Secret.
Dev-controlled wallets are created in a
wallet set .
The wallet set is the source from which wallet keys are derived.
create-wallet.ts
create_wallet.py
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
const client = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
// Create a wallet set
const walletSetResponse = await client . createWalletSet ({
name: "Wallet Set 1" ,
});
// Create a wallet on Arc Testnet
const walletsResponse = await client . createWallets ({
blockchains: [ "ARC-TESTNET" ],
count: 1 ,
walletSetId: walletSetResponse . data ?. walletSet ?. id ?? "" ,
accountType: "SCA" ,
});
console . log ( JSON . stringify ( walletsResponse . data , null , 2 ));
Run the script:
Response:
You should now have a newly created dev-controlled wallet. The API response will
look similar to the following:
{
"wallets" : [
{
"id" : "45692c3e-2ffa-5c5b-a99c-61366939114c" ,
"state" : "LIVE" ,
"walletSetId" : "ee58db40-22b4-55cb-9ce6-3444cb6efd2f" ,
"custodyType" : "DEVELOPER" ,
"address" : "0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"blockchain" : "ARC-TESTNET" ,
"accountType" : "SCA" ,
"updateDate" : "2026-01-20T09:39:16Z" ,
"createDate" : "2026-01-20T09:39:16Z" ,
"scaCore" : "circle_6900_singleowner_v3"
}
]
}
ERC-20
ERC-721
ERC-1155
Airdrop
Deploy an ERC-20 contract ERC-20 is the standard for fungible tokens. Use this template for tokenized
assets, treasury instruments, governance tokens, or programmable money. Step 3: Prepare for deployment Retrieve your wallet ID from Step 2. Ensure:
Wallet custody type is Dev-Controlled
Blockchain is Arc Testnet
Account type is SCA (Smart Contract Account, recommended for Gas Station
compatibility)
Note your wallet’s address for subsequent steps. 3.2. Understand deployment parameters Parameter Description idempotencyKeyA unique value to prevent duplicate requests. nameThe offchain contract name (visible in Circle Console only). Use MyTokenContract. walletIdThe ID of the wallet deploying the contract. Use your dev-controlled wallet ID. templateIdThe template identifier. Use a1b74add-23e0-4712-88d1-6b3009e85a86 for ERC-20. See Templates . blockchainThe network to deploy onto. Use ARC-TESTNET. entitySecretCiphertextThe re-encrypted entity secret. See Entity Secret Management . feeLevelThe fee level for transaction processing. Use MEDIUM. templateParametersThe onchain initialization parameters (see below).
3.3. Template parameters Required Parameters: Parameter Type Description nameString The onchain contract name. Use MyToken. defaultAdminString The address with administrator permissions. Use your Dev-Controlled Wallet address. primarySaleRecipientString The address that receives proceeds from first-time sales. Use your wallet address.
Optional Parameters: Parameter Type Description symbolString The token symbol (for example, MTK). platformFeeRecipientString The address that receives platform fees from sales. Set this when implementing platform fee revenue share. platformFeePercentFloat The platform fee percentage as decimal (for example, 0.1 for 10%). Requires platformFeeRecipient. contractUriString The URL for the contract metadata. trustedForwardersStrings[] A list of addresses that can forward ERC2771 meta-transactions to this contract.
Step 4: Deploy the smart contract Deploy by making a request to
POST /templates/{id}/deploy : deploy-erc20.ts
deploy_erc20.py
cURL
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const response = await circleContractSdk . deployContractTemplate ({
id: "a1b74add-23e0-4712-88d1-6b3009e85a86" ,
blockchain: "ARC-TESTNET" ,
name: "MyTokenContract" ,
walletId: process . env . WALLET_ID ,
templateParameters: {
name: "MyToken" ,
symbol: "MTK" ,
defaultAdmin: process . env . WALLET_ADDRESS ,
primarySaleRecipient: process . env . WALLET_ADDRESS ,
},
fee: {
type: "level" ,
config: {
feeLevel: "MEDIUM" ,
},
},
});
console . log ( JSON . stringify ( response . data , null , 2 ));
Run the script: Response: {
"contractIds" : [ "019c053d-1ed1-772b-91a8-6970003dad8d" ],
"transactionId" : "5b6185b2-f9a1-5645-9db2-ca5d9a330794"
}
A successful response indicates deployment has been initiated , not
completed. Use the transactionId to check the deployment status in the next
step.
4.1. Check deployment status You can check the status of the deployment from the
Circle Developer Console
or by calling
GET /transactions/{id} . After running the deployment script, copy the transactionId from the response
and update your .env file with TRANSACTION_ID={your-transaction-id}. Then
run the check-transaction script to verify deployment status. check-transaction.ts
check_transaction.py
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: process . env . TRANSACTION_ID ! ,
});
console . log ( JSON . stringify ( transactionResponse . data , null , 2 ));
Run the script: npm run check-transaction
Transaction status may show PENDING immediately after deployment. Wait 10-30
seconds and re-run check-transaction to see COMPLETE status.
Response: {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"walletId" : "45692c3e-2ffa-5c5b-a99c-61366939114c" ,
"sourceAddress" : "0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"transactionType" : "OUTBOUND" ,
"custodyType" : "DEVELOPER" ,
"state" : "COMPLETE" ,
"amounts" : [],
"nfts" : null ,
"txHash" : "0x3bfbab5d5ce0d1a5d682cbc742d3940cf59db0369d173b71ba2a3b8f43bfbcb1" ,
"blockHash" : "0x7d12148f9331556b31f84f58a41b7ff16eaaa47940f9e86733037d7ab74d858e" ,
"blockHeight" : 23686153 ,
"userOpHash" : "0x66befac1a371fcdddf1566215e4677127e111dff9253f306f7096fed8642a208" ,
"networkFee" : "0.044628774800664" ,
"firstConfirmDate" : "2026-01-26T08:59:56Z" ,
"operation" : "CONTRACT_EXECUTION" ,
"feeLevel" : "MEDIUM" ,
"estimatedFee" : {
"gasLimit" : "500797" ,
"networkFee" : "0.16506442157883425" ,
"baseFee" : "160" ,
"priorityFee" : "9.60345525" ,
"maxFee" : "329.60345525"
},
"refId" : "" ,
"abiFunctionSignature" : "mintTo(address,uint256)" ,
"abiParameters" : [
"0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"1000000000000000000"
],
"createDate" : "2026-01-26T08:59:54Z" ,
"updateDate" : "2026-01-26T08:59:56Z"
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} . After deployment completes, copy the contractIds[0] from the deployment
response and update your .env file with CONTRACT_ID={your-contract-id}. Then
run the get-contract script to retrieve the contract address. get-contract.ts
get_contract.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const contractResponse = await circleContractSdk . getContract ({
id: process . env . CONTRACT_ID ! ,
});
console . log ( JSON . stringify ( contractResponse . data , null , 2 ));
Run the script: Response: {
"contract" : {
"id" : "b7c35372-ce69-4ccd-bfaa-504c14634f0d" ,
"contractAddress" : "0x1234567890abcdef1234567890abcdef12345678" ,
"blockchain" : "ARC-TESTNET" ,
"status" : "COMPLETE"
}
}
Once your contract is deployed, you can interact with it from your application.
You’ll be able to view the contract both in the
Circle Developer Console
and on the Arc Testnet Explorer . Initial Supply: The contract starts with 0 token supply at deployment. Use
the mintTo function to create tokens and assign them to addresses as needed.
Deploy an ERC-721 contract ERC-721 is the standard for unique digital assets. Use this template for
ownership certificates, tokenized assets, unique financial instruments, or
distinct asset representation. Step 3: Prepare for deployment Retrieve your wallet ID from Step 2. Ensure:
Wallet custody type is Dev-Controlled
Blockchain is Arc Testnet
Account type is SCA (Smart Contract Account, recommended for Gas Station
compatibility)
Note your wallet’s address for subsequent steps. 3.2. Understand deployment parameters Parameter Description idempotencyKeyA unique value to prevent duplicate requests. nameThe offchain contract name (visible in Circle Console only). Use MyTokenContract. walletIdThe ID of the wallet deploying the contract. Use your dev-controlled wallet ID. templateIdThe template identifier. Use 76b83278-50e2-4006-8b63-5b1a2a814533 for ERC-721. See Templates . blockchainThe network to deploy onto. Use ARC-TESTNET. entitySecretCiphertextThe re-encrypted entity secret. See Entity Secret Management . feeLevelThe fee level for transaction processing. Use MEDIUM. templateParametersThe onchain initialization parameters (see below).
3.3. Template parameters Required Parameters: Parameter Type Description nameString The onchain contract name. Use MyToken. defaultAdminString The address with administrator permissions. Use your Dev-Controlled Wallet address. primarySaleRecipientString The address for first-time sale proceeds. Use your Dev-Controlled Wallet address. royaltyRecipientString The address for secondary sale royalties. Use your Dev-Controlled Wallet address. royaltyPercentFloat The royalty share as a decimal (for example, 0.01 for 1%). Use 0.01.
Optional Parameters: Parameter Type Description symbolString The token symbol (for example, MTK). platformFeeRecipientString The address that receives platform fees from sales. Set this when implementing platform fee revenue share. platformFeePercentFloat The platform fee percentage as decimal (for example, 0.1 for 10%). Requires platformFeeRecipient. contractUriString The URL for the contract metadata. trustedForwardersStrings[] A list of addresses that can forward ERC2771 meta-transactions to this contract.
Step 4: Deploy the smart contract Deploy by making a request to
POST /templates/{id}/deploy : deploy-erc721.ts
deploy_erc721.py
cURL
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const response = await circleContractSdk . deployContractTemplate ({
id: "76b83278-50e2-4006-8b63-5b1a2a814533" ,
blockchain: "ARC-TESTNET" ,
name: "MyTokenContract" ,
walletId: process . env . WALLET_ID ,
templateParameters: {
name: "MyToken" ,
symbol: "MTK" ,
defaultAdmin: process . env . WALLET_ADDRESS ,
primarySaleRecipient: process . env . WALLET_ADDRESS ,
royaltyRecipient: process . env . WALLET_ADDRESS ,
royaltyPercent: 0.01 ,
},
fee: {
type: "level" ,
config: {
feeLevel: "MEDIUM" ,
},
},
});
console . log ( JSON . stringify ( response . data , null , 2 ));
Run the script: Response: {
"contractIds" : [ "019c053d-1ed1-772b-91a8-6970003dad8d" ],
"transactionId" : "5b6185b2-f9a1-5645-9db2-ca5d9a330794"
}
A successful response indicates deployment has been initiated , not
completed. Use the transactionId to check the deployment status in the next
step.
4.1. Check deployment status Verify deployment with
GET /transactions/{id} . After running the deployment script, copy the transactionId from the response
and update your .env file with TRANSACTION_ID={your-transaction-id}. Then
run the check-transaction script to verify deployment status. check-transaction.ts
check_transaction.py
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: process . env . TRANSACTION_ID ! ,
});
console . log ( JSON . stringify ( transactionResponse . data , null , 2 ));
Run the script: npm run check-transaction
Transaction status may show PENDING immediately after deployment. Wait 10-30
seconds and re-run check-transaction to see COMPLETE status.
Response: {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"walletId" : "45692c3e-2ffa-5c5b-a99c-61366939114c" ,
"sourceAddress" : "0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"transactionType" : "OUTBOUND" ,
"custodyType" : "DEVELOPER" ,
"state" : "COMPLETE" ,
"amounts" : [],
"nfts" : null ,
"txHash" : "0x3bfbab5d5ce0d1a5d682cbc742d3940cf59db0369d173b71ba2a3b8f43bfbcb1" ,
"blockHash" : "0x7d12148f9331556b31f84f58a41b7ff16eaaa47940f9e86733037d7ab74d858e" ,
"blockHeight" : 23686153 ,
"userOpHash" : "0x66befac1a371fcdddf1566215e4677127e111dff9253f306f7096fed8642a208" ,
"networkFee" : "0.044628774800664" ,
"firstConfirmDate" : "2026-01-26T08:59:56Z" ,
"operation" : "CONTRACT_EXECUTION" ,
"feeLevel" : "MEDIUM" ,
"estimatedFee" : {
"gasLimit" : "500797" ,
"networkFee" : "0.16506442157883425" ,
"baseFee" : "160" ,
"priorityFee" : "9.60345525" ,
"maxFee" : "329.60345525"
},
"refId" : "" ,
"abiFunctionSignature" : "mintTo(address,uint256)" ,
"abiParameters" : [
"0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"1000000000000000000"
],
"createDate" : "2026-01-26T08:59:54Z" ,
"updateDate" : "2026-01-26T08:59:56Z"
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} . After deployment completes, copy the contractIds[0] from the deployment
response and update your .env file with CONTRACT_ID={your-contract-id}. Then
run the get-contract script to retrieve the contract address. get-contract.ts
get_contract.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const contractResponse = await circleContractSdk . getContract ({
id: process . env . CONTRACT_ID ! ,
});
console . log ( JSON . stringify ( contractResponse . data , null , 2 ));
Run the script: Response: {
"contract" : {
"id" : "b7c35372-ce69-4ccd-bfaa-504c14634f0d" ,
"contractAddress" : "0x1234567890abcdef1234567890abcdef12345678" ,
"blockchain" : "ARC-TESTNET" ,
"status" : "COMPLETE"
}
}
Deploy an ERC-1155 contract ERC-1155 is the standard for multi-asset token management. Use this template for
structured products, tiered assets, batch settlements, or managing diverse asset
portfolios. Step 3: Prepare for deployment Retrieve your wallet ID from Step 2. Ensure:
Wallet custody type is Dev-Controlled
Blockchain is Arc Testnet
Account type is SCA (Smart Contract Account, recommended for Gas Station
compatibility)
Note your wallet’s address for subsequent steps. 3.2. Understand deployment parameters Parameter Description idempotencyKeyA unique value to prevent duplicate requests. nameThe offchain contract name (visible in Circle Console only). Use MyMultiTokenContract. walletIdThe ID of the wallet deploying the contract. Use your dev-controlled wallet ID. templateIdThe template identifier. Use aea21da6-0aa2-4971-9a1a-5098842b1248 for ERC-1155. See Templates . blockchainThe network to deploy onto. Use ARC-TESTNET. entitySecretCiphertextThe re-encrypted entity secret. See Entity Secret Management . feeLevelThe fee level for transaction processing. Use MEDIUM. templateParametersThe onchain initialization parameters (see below).
3.3. Template parameters Required Parameters: Parameter Type Description nameString The onchain contract name. Use MyMultiToken. defaultAdminString The address with administrator permissions. Use your Dev-Controlled Wallet address. primarySaleRecipientString The address for first-time sale proceeds. Use your Dev-Controlled Wallet address. royaltyRecipientString The address for secondary sale royalties. Use your Dev-Controlled Wallet address. royaltyPercentFloat The royalty share as a decimal (for example, 0.01 for 1%). Use 0.01.
Optional Parameters: Parameter Type Description symbolString The token symbol (for example, MMTK). platformFeeRecipientString The address that receives platform fees from sales. Set this when implementing platform fee revenue share. platformFeePercentFloat The platform fee percentage as decimal (for example, 0.1 for 10%). Requires platformFeeRecipient. contractUriString The URL for the contract metadata. trustedForwardersStrings[] A list of addresses that can forward ERC2771 meta-transactions to this contract.
Step 4: Deploy the smart contract Deploy by making a request to
POST /templates/{id}/deploy : deploy-erc1155.ts
deploy_erc1155.py
cURL
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const response = await circleContractSdk . deployContractTemplate ({
id: "aea21da6-0aa2-4971-9a1a-5098842b1248" ,
blockchain: "ARC-TESTNET" ,
name: "MyMultiTokenContract" ,
walletId: process . env . WALLET_ID ,
templateParameters: {
name: "MyMultiToken" ,
symbol: "MMTK" ,
defaultAdmin: process . env . WALLET_ADDRESS ,
primarySaleRecipient: process . env . WALLET_ADDRESS ,
royaltyRecipient: process . env . WALLET_ADDRESS ,
royaltyPercent: 0.01 ,
},
fee: {
type: "level" ,
config: {
feeLevel: "MEDIUM" ,
},
},
});
console . log ( JSON . stringify ( response . data , null , 2 ));
Run the script: Response: {
"contractIds" : [ "019c053d-1ed1-772b-91a8-6970003dad8d" ],
"transactionId" : "5b6185b2-f9a1-5645-9db2-ca5d9a330794"
}
A successful response indicates deployment has been initiated , not
completed. Use the transactionId to check the deployment status in the next
step.
4.1. Check deployment status Verify deployment with
GET /transactions/{id} . After running the deployment script, copy the transactionId from the response
and update your .env file with TRANSACTION_ID={your-transaction-id}. Then
run the check-transaction script to verify deployment status. check-transaction.ts
check_transaction.py
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: process . env . TRANSACTION_ID ! ,
});
console . log ( JSON . stringify ( transactionResponse . data , null , 2 ));
Run the script: npm run check-transaction
Transaction status may show PENDING immediately after deployment. Wait 10-30
seconds and re-run check-transaction to see COMPLETE status.
Response: {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"walletId" : "45692c3e-2ffa-5c5b-a99c-61366939114c" ,
"sourceAddress" : "0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"transactionType" : "OUTBOUND" ,
"custodyType" : "DEVELOPER" ,
"state" : "COMPLETE" ,
"amounts" : [],
"nfts" : null ,
"txHash" : "0x3bfbab5d5ce0d1a5d682cbc742d3940cf59db0369d173b71ba2a3b8f43bfbcb1" ,
"blockHash" : "0x7d12148f9331556b31f84f58a41b7ff16eaaa47940f9e86733037d7ab74d858e" ,
"blockHeight" : 23686153 ,
"userOpHash" : "0x66befac1a371fcdddf1566215e4677127e111dff9253f306f7096fed8642a208" ,
"networkFee" : "0.044628774800664" ,
"firstConfirmDate" : "2026-01-26T08:59:56Z" ,
"operation" : "CONTRACT_EXECUTION" ,
"feeLevel" : "MEDIUM" ,
"estimatedFee" : {
"gasLimit" : "500797" ,
"networkFee" : "0.16506442157883425" ,
"baseFee" : "160" ,
"priorityFee" : "9.60345525" ,
"maxFee" : "329.60345525"
},
"refId" : "" ,
"abiFunctionSignature" : "mintTo(address,uint256)" ,
"abiParameters" : [
"0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"1000000000000000000"
],
"createDate" : "2026-01-26T08:59:54Z" ,
"updateDate" : "2026-01-26T08:59:56Z"
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} . After deployment completes, copy the contractIds[0] from the deployment
response and update your .env file with CONTRACT_ID={your-contract-id}. Then
run the get-contract script to retrieve the contract address. get-contract.ts
get_contract.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const contractResponse = await circleContractSdk . getContract ({
id: process . env . CONTRACT_ID ! ,
});
console . log ( JSON . stringify ( contractResponse . data , null , 2 ));
Run the script: Response: {
"contract" : {
"id" : "b7c35372-ce69-4ccd-bfaa-504c14634f0d" ,
"contractAddress" : "0x1234567890abcdef1234567890abcdef12345678" ,
"blockchain" : "ARC-TESTNET" ,
"status" : "COMPLETE"
}
}
Deploy an airdrop contract The Airdrop template enables mass token distribution to many recipients. Use
this template for treasury distributions, stakeholder settlements, operational
payments, or programmatic capital allocation. Step 3: Prepare for deployment Retrieve your wallet ID from Step 2. Ensure:
Wallet custody type is Dev-Controlled
Blockchain is Arc Testnet
Account type is SCA (Smart Contract Account, recommended for Gas Station
compatibility)
Note your wallet’s address for subsequent steps. 3.2. Understand deployment parameters Parameter Description idempotencyKeyA unique value to prevent duplicate requests. nameThe offchain contract name (visible in Circle Console only). Use MyAirdropContract. walletIdThe ID of the wallet deploying the contract. Use your dev-controlled wallet ID. templateIdThe template identifier. Use 13e322f2-18dc-4f57-8eed-4bddfc50f85e for Airdrop. See Templates . blockchainThe network to deploy onto. Use ARC-TESTNET. entitySecretCiphertextThe re-encrypted entity secret. See Entity Secret Management . feeLevelThe fee level for transaction processing. Use MEDIUM. templateParametersThe onchain initialization parameters (see below).
3.3. Template parameters Required Parameters: Parameter Type Description defaultAdminString The address with administrator permissions. Use your Dev-Controlled Wallet address.
Optional Parameters: Parameter Type Description contractURIString The URL for the contract metadata.
Step 4: Deploy the smart contract Deploy by making a request to
POST /templates/{id}/deploy : deploy-airdrop.ts
deploy_airdrop.py
cURL
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const response = await circleContractSdk . deployContractTemplate ({
id: "13e322f2-18dc-4f57-8eed-4bddfc50f85e" ,
blockchain: "ARC-TESTNET" ,
name: "MyAirdropContract" ,
walletId: process . env . WALLET_ID ,
templateParameters: {
defaultAdmin: process . env . WALLET_ADDRESS ,
},
fee: {
type: "level" ,
config: {
feeLevel: "MEDIUM" ,
},
},
});
console . log ( JSON . stringify ( response . data , null , 2 ));
Run the script: Response: {
"contractIds" : [ "019c053d-1ed1-772b-91a8-6970003dad8d" ],
"transactionId" : "5b6185b2-f9a1-5645-9db2-ca5d9a330794"
}
A successful response indicates deployment has been initiated , not
completed. Use the transactionId to check the deployment status in the next
step.
4.1. Check deployment status Verify deployment with
GET /transactions/{id} . After running the deployment script, copy the transactionId from the response
and update your .env file with TRANSACTION_ID={your-transaction-id}. Then
run the check-transaction script to verify deployment status. check-transaction.ts
check_transaction.py
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: process . env . TRANSACTION_ID ! ,
});
console . log ( JSON . stringify ( transactionResponse . data , null , 2 ));
Run the script: npm run check-transaction
Transaction status may show PENDING immediately after deployment. Wait 10-30
seconds and re-run check-transaction to see COMPLETE status.
Response: {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"walletId" : "45692c3e-2ffa-5c5b-a99c-61366939114c" ,
"sourceAddress" : "0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"transactionType" : "OUTBOUND" ,
"custodyType" : "DEVELOPER" ,
"state" : "COMPLETE" ,
"amounts" : [],
"nfts" : null ,
"txHash" : "0x3bfbab5d5ce0d1a5d682cbc742d3940cf59db0369d173b71ba2a3b8f43bfbcb1" ,
"blockHash" : "0x7d12148f9331556b31f84f58a41b7ff16eaaa47940f9e86733037d7ab74d858e" ,
"blockHeight" : 23686153 ,
"userOpHash" : "0x66befac1a371fcdddf1566215e4677127e111dff9253f306f7096fed8642a208" ,
"networkFee" : "0.044628774800664" ,
"firstConfirmDate" : "2026-01-26T08:59:56Z" ,
"operation" : "CONTRACT_EXECUTION" ,
"feeLevel" : "MEDIUM" ,
"estimatedFee" : {
"gasLimit" : "500797" ,
"networkFee" : "0.16506442157883425" ,
"baseFee" : "160" ,
"priorityFee" : "9.60345525" ,
"maxFee" : "329.60345525"
},
"refId" : "" ,
"abiFunctionSignature" : "mintTo(address,uint256)" ,
"abiParameters" : [
"0xbcf83d3b112cbf43b19904e376dd8dee01fe2758" ,
"1000000000000000000"
],
"createDate" : "2026-01-26T08:59:54Z" ,
"updateDate" : "2026-01-26T08:59:56Z"
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} . After deployment completes, copy the contractIds[0] from the deployment
response and update your .env file with CONTRACT_ID={your-contract-id}. Then
run the get-contract script to retrieve the contract address. get-contract.ts
get_contract.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleContractSdk = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
const contractResponse = await circleContractSdk . getContract ({
id: process . env . CONTRACT_ID ! ,
});
console . log ( JSON . stringify ( contractResponse . data , null , 2 ));
Run the script: Response: {
"contract" : {
"id" : "b7c35372-ce69-4ccd-bfaa-504c14634f0d" ,
"contractAddress" : "0x1234567890abcdef1234567890abcdef12345678" ,
"blockchain" : "ARC-TESTNET" ,
"status" : "COMPLETE"
}
}
Summary
After completing this tutorial, you’ve successfully:
Created a dev-controlled wallet on Arc Testnet
Funded your wallet with testnet USDC
Deployed a smart contract using Contract Templates
Retrieved your contract address