Learn how to build a multi-currency stablecoin wallet with Circle’s developer-controlled wallets SDK that allows for swapping between USDC and EURC.

Building a multi-currency wallet is no small feat. Between having to juggle multiple ledgers, navigate the complexities of the different payment rails integrations and manage reconciliation and treasury operations, it could take months before a minimum viable product (MVP) could even be launched.
Stablecoins can provide a solution that involves a lower amount of technical complexity given that they run on a single unified settlement layer. Circle, through its regulated1 affiliates, is the issuer of USDC, the world’s largest regulated1 U.S. dollar denominated stablecoin, and EURC, a globally accessible fully reserved euro-denominated stablecoin that is MiCA compliant.
A practical use-case for this would be onchain stablecoin foreign exchange (FX), where smart contracts are used to programmatically handle stablecoin swaps directly, instead of having to pass through multiple intermediaries and clearing systems.
In this blog post, we will go through the key steps needed to build a multi-currency wallet that can enable your end users to swap between USDC and EURC with Circle Wallets.
Prerequisites
- Sign up for a Circle Developer Account: To obtain the API key to make use of Circle's Smart Contract Platform NodeJS SDK and Circle's Developer Controlled Wallets NodeJS SDK
- Created a Developer Controlled Wallet: For deploying smart contracts and executing contract functions.
- Obtain testnet ETH, USDC and EURC: Fund the developer-controlled wallet to pay gas fees for transactions and test the token swaps
- Get Sepolia ETH from the Google’s Ethereum Sepolia Faucet
- Get testnet USDC and EURC from Circle’s Testnet Faucet
Building your multi-currency wallet experience
The developer-controlled wallets SDK allows you to retrieve the information to build out a smooth user-friendly experience for your end users. The SDK exposes methods that let you easily retrieve account information like the wallet address, transaction history and token balances.

One thing that is particularly useful is the ability to have more fine-grained control over token balances and transactions displayed in your users’ wallets. You can do this via the Monitored Tokens feature, which lets you manage and filter out tokens unrelated to what you’re trying to build.
In our case, we only want the wallet to display USDC and EURC, and possibly ETH (for gas fees). We can add these 3 tokens into the monitored token list via the createMonitoredTokens
method.
const response = await client.createMonitoredTokens({
tokenIds: [
'979869da-9115-5f7d-917d-12d434e56ae7', // ETH-SEPOLIA
'c22b378a-843a-59b6-aaf5-bcba622729e6', // EURC on Sepolia
'5797fbd6-3795-519d-84ca-ec4c5f80c3b1' // USDC on Sepolia
],
})
Once you’ve whitelisted your tokens of choice, your API calls for obtaining wallet balances, transactions and webhook callbacks will be monitored and only return the tokens on your list. However, if you do need to pull all tokens in your wallets, you can modify the scope to MONITOR_ALL
.
const response = await client.updateMonitoredTokensScope({
scope: 'MONITOR_ALL',
})
You could also set the parameter includeAll
to true
when making API calls for listing balances or transactions to indicate you would like the full list for all tokens.
const response = await client.getWalletTokenBalance({
id: WALLET_ID,
includeAll: true
})
Making a swap
We will focus on the server functions needed to make the swap in this blog post, but you can refer to REPLIT_LINK_OR_GITHUB_REPO for a simple framework-free sample application that demonstrates what the end-to-end flow might look like.
The developer-controlled wallets SDK makes it easier for your wallet to interact with smart contracts. The createContractExecutionTransaction
method lets us create a transaction which executes a smart contract. We already have a simplified single-path swap contract deployed onto Ethereum Sepolia, if you want to see how it works.
To start using the SDK, you must initialise the client with your API key and entity secret.
import { initiateDeveloperControlledWalletsClient } from '@circle-fin/developer-controlled-wallets'
const client = initiateDeveloperControlledWalletsClient({
apiKey: 'YOUR_KEY',
entitySecret: 'YOUR_SECRET'
})
There are two transactions involved in making a token swap. The first is to approve the transaction to the swap contract, and the next transaction executes the swap function.
// 1) Approve EURC to your swap contract
const approveTxn = await client.createContractExecutionTransaction({
walletId: WALLET_ID,
contractAddress: EURC_ADDRESS,
abiFunctionSignature: "approve(address,uint256)",
abiParameters: [SWAP_CONTRACT_ADDRESS, rawAmount],
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
// 2) Call the swap function
const swapTxn = await client.createContractExecutionTransaction({
walletId: WALLET_ID,
contractAddress: SWAP_CONTRACT_ADDRESS,
abiFunctionSignature: "swapEURCforUSDC_ExactIn(uint256)",
abiParameters: [rawAmount],
fee: { type: "level", config: { feeLevel: "MEDIUM" } },
});
Wrapping up
This tutorial gives developers a starting point to build out a multi-currency wallet and enable your end users to have the capability to swap between different stablecoins. We would also love to hear your feedback on what you thought about this tutorial, or if you encountered any issues while trying it out. You can join our Discord to share your thoughts and engage with other like-minded developers in our community.
1 USDC and EURC are issued by regulated affiliates of Circle. A list of Circle’s regulatory authorizations can be found here.
Circle Wallets are provided by Circle Technology Services, LLC (“CTS”). CTS is a software provider and does not provide regulated financial or advisory services. You are solely responsible for services you provide to users, including obtaining any necessary licenses or approvals and otherwise complying with applicable laws. For additional details, please see the Circle Developer Terms of Service.