In this tutorial, you’ll programmatically transfer USDC or EURC from one wallet
address to another on the Arc testnet with
Circle Dev-Controlled Wallets.
Prerequisites
Before you begin, make sure you have:
- A Circle Developer Console account
- An API key created in the Console:
Keys → Create a key → API key → Standard Key
- Your
Entity Secret registered
Step 1: Set up your project
In this step, you set up your local development environment and install the
required dependencies.
1.1. Create a new project
Create a new directory, navigate to it and initialize a new project.
mkdir transfer-funds
cd transfer-funds
npm init -y
npm pkg set type=module
1.2. Install dependencies
In the project directory, install the
Circle Dev-Controlled Wallets SDK. It is
also possible to
call the API directly
if you can’t use the SDK in your project.
npm install @circle-fin/developer-controlled-wallets
Create a .env file in the project directory with your Circle credentials,
replacing 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.
echo "CIRCLE_API_KEY={YOUR_API_KEY}
CIRCLE_ENTITY_SECRET={YOUR_ENTITY_SECRET}" > .env
Important: These are sensitive credentials. Do not commit them to version
control or share them publicly.
Step 2: Set up your wallets
In this step, you create two dev-controlled wallets and fund one of them with
testnet USDC or EURC to make the transfer. If you already have funded
dev-controlled wallets, skip to Step 3.
2.1. Create wallets
Import the Wallets SDK and initialize the client using your API key and Entity
Secret. Dev-controlled wallets are created in a
wallet set,
which serves as the source from which individual wallet keys are derived.
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const client = initiateDeveloperControlledWalletsClient({
apiKey: "<YOUR_API_KEY>",
entitySecret: "<YOUR_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: 2,
walletSetId: walletSetResponse.data?.walletSet?.id ?? "",
});
You should have two new Externally Owned Account (EOA) developer-controlled
wallets that you can also see from the
Circle Developer Console. The
API response will look similar to the following:
[
{
"id": "a2f67c91-b7e3-5df4-9c8e-42bbd51a9fcb",
"state": "LIVE",
"walletSetId": "5c3e9f20-8d4b-55a1-a63b-c21f44de8a72",
"custodyType": "DEVELOPER",
"refId": "",
"name": "",
"address": "0x9eab451f27dca39bd3f5d76ef28c86cc0b3a72df",
"blockchain": "ARC-TESTNET",
"accountType": "EOA",
"updateDate": "2025-11-07T01:35:03Z",
"createDate": "2025-11-07T01:35:03Z"
},
{
"id": "c84d12a4-f6a9-5df8-8b44-92ff7cc94e32",
"state": "LIVE",
"walletSetId": "5c3e9f20-8d4b-55a1-a63b-c21f44de8a72",
"custodyType": "DEVELOPER",
"refId": "",
"name": "",
"address": "0xb37ac90d1a657c04a8518b6f7bda2b37e4f8d221",
"blockchain": "ARC-TESTNET",
"accountType": "EOA",
"updateDate": "2025-11-07T01:35:06Z",
"createDate": "2025-11-07T01:35:06Z"
}
]
2.2. Fund a wallet with testnet stablecoins
Obtain testnet USDC or EURC from the Circle Faucet
or the Console Faucet to perform actions
like making transfers and paying gas fees. You’ll need a funded balance to
execute transactions using your dev-controlled wallet.
2.3. Check the wallet balances
You can check your wallet balances from the
Circle Developer Console or
programmatically by making a request to
GET /wallets/{id}/balances
with the specified wallet ID.
const response = await client.getWalletTokenBalance({
id: "<WALLET_ID>",
});
Step 3: Transfer USDC / EURC
In this step, you set up your script, execute the USDC or EURC transfer, and
check the result.
3.1 Setup the transfer script and execute the transfer
Replace the walletAddress field with the address of the funded wallet, and the
destinationAddress field with the address of the other wallet.
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const client = initiateDeveloperControlledWalletsClient({
apiKey: "<YOUR_API_KEY>",
entitySecret: "<YOUR_ENTITY_SECRET>",
});
const transferResponse = await client.createTransaction({
amount: ["0.1"], // Transfer 0.1 USDC
destinationAddress: "<RECIPIENT_ADDRESS>",
tokenAddress: "0x3600000000000000000000000000000000000000", // USDC contract address on Arc Testnet
blockchain: "ARC-TESTNET",
walletAddress: "<SENDER_ADDRESS>",
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log(transferResponse.data);
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
const client = initiateDeveloperControlledWalletsClient({
apiKey: "<YOUR_API_KEY>",
entitySecret: "<YOUR_ENTITY_SECRET>",
});
const transferResponse = await client.createTransaction({
amount: ["0.1"], // Transfer 0.1 EURC
destinationAddress: "<RECIPIENT_ADDRESS>",
tokenAddress: "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a", // EURC contract address on Arc Testnet
blockchain: "ARC-TESTNET",
walletAddress: "<SENDER_ADDRESS>",
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
console.log(transferResponse.data);
If the script runs successfully, you should receive a response object with a
transaction ID and the state of the transaction that looks similar to the
following:
{ "id": "70cb796b-5c43-5076-a790-3525f6d8424c", "state": "INITIATED" }
3.2 Verify the status of the transfer
You can check the status of the transfer with the getTransaction method by
providing the transaction ID obtained from the previous step.
const response = await client.getTransaction({
id: "<TRANSACTION_ID>",
});
console.log(response.data);
You can verify that the transaction was successful by checking the details in
the returned object, which should look similar to the following:
{
"data": {
"transaction": {
"id": "70cb796b-5c43-5076-a790-3525f6d8424c",
"blockchain": "ARC-TESTNET",
"tokenId": "15dc2b5d-0994-58b0-bf8c-3a0501148ee8",
"walletId": "f8a9993d-5a59-58df-b3e0-206c6e81ea57",
"sourceAddress": "0x3bafa3a0987699391a2003c3aa06d41b0209c397",
"destinationAddress": "0x1f5ee284571f4bb888ceda4cc7b6e5e15829a1ea",
"transactionType": "OUTBOUND",
"custodyType": "DEVELOPER",
"state": "COMPLETE",
"transactionScreeningEvaluation": {
"screeningDate": "2026-01-07T04:49:09Z"
},
"amounts": [
"0.1"
],
"nfts": null,
"txHash": "0x2e312718a2b6663cf20862e71b52dcb5404c9dd16510a08c5c9f47a78aabf644",
"blockHash": "0x7474b111d671de75e7b606b8e1a1508c9bcef0eafc718843accb900510f35bf9",
"blockHeight": 20375164,
"networkFee": "0.003556682015901",
"firstConfirmDate": "2026-01-07T04:49:11Z",
"operation": "TRANSFER",
"feeLevel": "MEDIUM",
"estimatedFee": {
"gasLimit": "21000",
"networkFee": "0.006916682015901",
"baseFee": "160",
"priorityFee": "9.365810281",
"maxFee": "329.365810281"
},
"refId": "",
"abiParameters": null,
"createDate": "2026-01-07T04:49:09Z",
"updateDate": "2026-01-07T04:49:11Z"
}
}
}
You can also copy the transaction hash (txHash) value and look up the
transaction details on the Arc Testnet explorer.
Summary
After completing this tutorial, you’ve successfully:
- Created dev-controlled wallets on Arc Testnet
- Funded your wallet with testnet tokens
- Transferred USDC or EURC from one wallet to another