Circle's smart contract platform enables you to seamlessly exchange ETH for USDC through automated, programmable contracts. This guide will walk you through the entire process, from writing and compiling the contract to deployment and interaction.
Understanding the Process
Swapping ETH for USDC on a decentralized exchange like Uniswap involves converting ETH into Wrapped ETH (WETH) first, as Uniswap operates with ERC-20 tokens. Your smart contract will handle this conversion and execute the swap using Uniswap's liquidity pools.
Key components of this operation include:
- Smart Contract: Handles the logic of the swap.
- Remix IDE: Used for writing, testing, and compiling the contract code.
- Circle’s SDK: Simplifies the deployment and interaction with the contract on the blockchain.
- Programmable Wallet: Manages the transactions and pays for gas fees.
Prerequisites
Before you begin, ensure you have the following set up:
- Remix IDE: A web-based tool for Solidity development. You can access it directly through your browser.
- Circle Developer Account: Necessary to access the Smart Contract Platform and SDK.
- A Programmable Wallet: Required to deploy the contract and execute its functions. You can create one via Circle's developer dashboard.
- Testnet ETH: For testing purposes, you'll need ETH on a testnet like Polygon Amoy to pay for transaction fees.
Writing the Smart Contract
The smart contract contains the logic to interact with Uniswap's router for the token swap. Below is the Solidity code designed to swap WETH for USDC.
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
pragma abicoder v2;
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
contract SwapExamples {
ISwapRouter public immutable swapRouter;
address public constant WETH9 = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;
address public constant USDC = 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359;
uint24 public constant poolFee = 3000;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
TransferHelper.safeTransferFrom(WETH9, msg.sender, address(this), amountIn);
TransferHelper.safeApprove(WETH9, address(swapRouter), amountIn);
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: WETH9,
tokenOut: USDC,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
amountOut = swapRouter.exactInputSingle(params);
}
function swapExactOutputSingle(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
TransferHelper.safeTransferFrom(WETH9, msg.sender, address(this), amountInMaximum);
TransferHelper.safeApprove(WETH9, address(swapRouter), amountInMaximum);
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
tokenIn: WETH9,
tokenOut: USDC,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum,
sqrtPriceLimitX96: 0
});
amountIn = swapRouter.exactOutputSingle(params);
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(WETH9, address(swapRouter), 0);
TransferHelper.safeTransfer(WETH9, msg.sender, amountInMaximum - amountIn);
}
}
}This contract uses two primary functions: swapExactInputSingle for swapping a fixed amount of WETH for USDC and swapExactOutputSingle for swapping a variable amount of WETH for a fixed amount of USDC.
Compiling the Smart Contract
Before deployment, the Solidity code must be compiled into Application Binary Interface (ABI) and bytecode.
Steps to compile in Remix IDE:
- Open Remix IDE: Navigate to the Remix website in your browser.
- Create a New File: In the file explorer, create a new
.solfile and paste the contract code. - Compile: Go to the "Solidity Compiler" tab. Select compiler version
0.8.20or higher and click the "Compile" button. - Retrieve ABI and Bytecode: After successful compilation, go to "Compilation Details." Copy the ABI JSON and the bytecode. You will need these for deployment.
Why Compilation is Necessary
Compilation translates human-readable Solidity code into machine-readable bytecode that can be deployed to the Ethereum Virtual Machine (EVM). The ABI defines the interface for interacting with the deployed contract's functions.
Deploying the Smart Contract
Deployment involves sending the bytecode to the blockchain, which creates a live instance of the contract. Circle's SDK abstracts much of this complexity.
Install the required npm packages:
npm install @circle-fin/smart-contract-platform @circle-fin/developer-controlled-wallets --saveImport the packages and set up your deployment script:
const circleContractSdk = require('@circle-fin/smart-contract-platform');
const developerControlledWallets = require('@circle-fin/developer-controlled-wallets');Prepare the deployment parameters. You will need to provide:
nameanddescriptionfor your contract.- Your Circle
walletId. - The target
blockchain(e.g., 'MATIC-AMOY' for Polygon Amoy testnet). - A
feeconfiguration object. constructorParameters(for this contract, the address of the Uniswap SwapRouter).- Your encrypted
entitySecretCiphertextfor authentication. - The stringified
abiJSONandbytecode(prefixed with0x) from the compilation step.
Execute the deployment:
const response = await circleContractSdk.deployContract({
name: 'Swap Contract',
description: 'Contract for swapping WETH9 to USDC using Uniswap',
walletId: 'YOUR_WALLET_ID', // Replace with your walletId
blockchain: 'MATIC-AMOY',
fee: { type: 'level', config: { feeLevel: 'MEDIUM' } },
constructorParameters: ['0xUniswapRouterAddress'], // Constructor parameter
entitySecretCiphertext: 'YOUR_ENCRYPTED_ENTITY_SECRET', // Replace
abiJSON: '["YOUR_STRINGIFIED_ABI"]', // Replace
bytecode: '0xYOUR_BYTECODE' // Replace
});A successful deployment returns a contractId and transactionId. You can check the deployment status using the getContract method with the contractId.
Interacting with the Deployed Contract
Once deployed, you can execute the swap functions by creating a contract execution transaction.
To call the swapExactInputSingle function:
const response = await circleContractSdk.createContractExecutionTransaction({
walletId: 'YOUR_WALLET_ID', // Replace with your wallet ID
contractAddress: 'YOUR_DEPLOYED_CONTRACT_ADDRESS', // Replace
abiFunctionSignature: 'swapExactInputSingle(uint256)',
abiParameters: [1000], // The amount of WETH to swap
fee: { type: 'level', config: { feeLevel: 'MEDIUM' } }
});This transaction will execute the swap, converting the specified amount of WETH into USDC and sending it to your wallet.
👉 Explore more advanced DeFi strategies
Frequently Asked Questions
What is WETH and why do I need it?
WETH, or Wrapped ETH, is an ERC-20 token that represents Ether. Since many DeFi protocols like Uniswap are designed for ERC-20 tokens, you must convert your native ETH into WETH before you can swap it for other tokens like USDC.
Which blockchain networks can I use with Circle's platform?
Circle's Smart Contract Platform supports multiple networks, including Ethereum Mainnet and various testnets like Polygon Amoy. Always ensure you are using the correct network and have the corresponding native token for gas fees.
What are the gas fees for this process?
Gas fees vary depending on network congestion and the complexity of your contract interaction. Circle's SDK allows you to set a fee level (e.g., 'MEDIUM') to manage transaction costs predictably.
Can I use this method for other token pairs?
Absolutely. The same principles apply. You would need to modify the smart contract to use the correct token addresses for the new pair and adjust the Uniswap pool fee tier accordingly.
How do I get testnet tokens for development?
You can typically acquire testnet tokens from faucets provided by the blockchain network. For example, the Polygon Amoy testnet has a faucet where you can request MATIC tokens to pay for gas.
Is this secure for large swaps?
While the provided code is functional, setting amountOutMinimum to zero is risky for large swaps as it exposes you to potential slippage. In production, always use an oracle or price feed to set a minimum amount of tokens you expect to receive.
Conclusion
Leveraging Circle's Smart Contract Platform significantly simplifies the process of deploying and interacting with smart contracts. This guide provided a clear pathway from writing a swap contract to executing a token trade on Uniswap. By abstracting away complexities like gas fees and wallet management, Circle allows developers to focus on building core functionality. 👉 Get advanced methods for smart contract development