How to Swap ETH for USDC Using Circle's Smart Contract Platform

·

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:

Prerequisites

Before you begin, ensure you have the following set up:

  1. Remix IDE: A web-based tool for Solidity development. You can access it directly through your browser.
  2. Circle Developer Account: Necessary to access the Smart Contract Platform and SDK.
  3. A Programmable Wallet: Required to deploy the contract and execute its functions. You can create one via Circle's developer dashboard.
  4. 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:

  1. Open Remix IDE: Navigate to the Remix website in your browser.
  2. Create a New File: In the file explorer, create a new .sol file and paste the contract code.
  3. Compile: Go to the "Solidity Compiler" tab. Select compiler version 0.8.20 or higher and click the "Compile" button.
  4. 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 --save

Import 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:

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