This tutorial guides you through deploying smart contracts on Arc Testnet with
Circle Contracts .
You’ll create a
Circle Dev-Controlled Wallet ,
fund it with the Circle Faucet , then deploy
pre-audited contract templates (ERC-20, ERC-721, ERC-1155, Airdrop).
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
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 -D tsx typescript @types/node
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}" > .env
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" ,
});
Run the script:
npx tsx --env-file=.env create-wallet.ts
Response:
You should now have a newly created dev-controlled wallet. The API response will
look similar to the following:
{
"data" : {
"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"
}
]
}
}
2.2. Fund the wallet with testnet USDC
Use the Circle Faucet to get testnet USDC. You’ll
need a funded balance to deploy contracts, mint tokens, and pay gas fees.
To fund your wallet:
Copy your wallet address from the response in step 2.1
Visit faucet.circle.com
Select Arc Testnet as the network
Paste your wallet address
Request testnet USDC
2.3. Check the wallet’s balance
You can check your wallet’s balance from the
Developer Console . You can
also check it by making a request to
GET /wallets/{id}/balances
with the wallet ID of the wallet you created.
const response = await client . getWalletTokenBalance ({
id: "<WALLET_ID>" ,
});
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) or EOA
(Externally Owned Account)
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 { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
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" ,
},
},
});
Run the script: npx tsx --env-file=.env deploy-erc20.ts
Response: {
"data" : {
"contractIds" : [ "xxxxxxxx-xxxx-7xxx-8xxx-xxxxxxxxxxxx" ],
"transactionId" : "xxxxxxxx-xxxx-5xxx-axxx-xxxxxxxxxxxx"
}
}
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} : const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: "<TRANSACTION_ID>" ,
});
Response: {
"data" : {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"state" : "COMPLETE"
}
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} : const contractResponse = await circleContractSdk . getContract ({
id: response . data ?. contractIds ?.[ 0 ],
});
Response: {
"data" : {
"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) or EOA
(Externally Owned Account)
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 { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
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" ,
},
},
});
Run the script: npx tsx --env-file=.env deploy-erc721.ts
Response: {
"data" : {
"contractIds" : [ "xxxxxxxx-xxxx-7xxx-8xxx-xxxxxxxxxxxx" ],
"transactionId" : "xxxxxxxx-xxxx-5xxx-axxx-xxxxxxxxxxxx"
}
}
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} : const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: "<TRANSACTION_ID>" ,
});
Response: {
"data" : {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"state" : "COMPLETE"
}
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} : const contractResponse = await circleContractSdk . getContract ({
id: response . data ?. contractIds ?.[ 0 ],
});
Response: {
"data" : {
"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) or EOA
(Externally Owned Account)
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 { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
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" ,
},
},
});
Run the script: npx tsx --env-file=.env deploy-erc1155.ts
Response: {
"data" : {
"contractIds" : [ "xxxxxxxx-xxxx-7xxx-8xxx-xxxxxxxxxxxx" ],
"transactionId" : "xxxxxxxx-xxxx-5xxx-axxx-xxxxxxxxxxxx"
}
}
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} : const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: "<TRANSACTION_ID>" ,
});
Response: {
"data" : {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"state" : "COMPLETE"
}
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} : const contractResponse = await circleContractSdk . getContract ({
id: response . data ?. contractIds ?.[ 0 ],
});
Response: {
"data" : {
"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) or EOA
(Externally Owned Account)
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 { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets" ;
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const circleDeveloperSdk = initiateDeveloperControlledWalletsClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
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" ,
},
},
});
Run the script: npx tsx --env-file=.env deploy-airdrop.ts
Response: {
"data" : {
"contractIds" : [ "xxxxxxxx-xxxx-7xxx-8xxx-xxxxxxxxxxxx" ],
"transactionId" : "xxxxxxxx-xxxx-5xxx-axxx-xxxxxxxxxxxx"
}
}
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} : const transactionResponse = await circleDeveloperSdk . getTransaction ({
id: "<TRANSACTION_ID>" ,
});
Response: {
"data" : {
"transaction" : {
"id" : "601a0815-f749-41d8-b193-22cadd2a8977" ,
"blockchain" : "ARC-TESTNET" ,
"state" : "COMPLETE"
}
}
}
4.2. Get the contract address After deployment completes, retrieve the contract address using
GET /contracts/{id} : const contractResponse = await circleContractSdk . getContract ({
id: response . data ?. contractIds ?.[ 0 ],
});
Response: {
"data" : {
"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