当你想在 DeFi 世界里把 ETH 兑换成 USDC 时,如果既要快速放心、又要支持后端自动化,Circle 的智能合约平台会是你手中的利器。本文带你一步步解锁「从智能合约编写到链上调用」的全部环节,并穿插 关键词:以太坊、USDC、智能合约、Remix IDE、Circle SDK、Uniswap、WETH、 Polygon 测试网、代币兑换、智能合约部署。
准备工作:让你事半功倍的三件套
在正式开始前,先准备好 三件核心装备:
- Remix IDE(在线版即可):用来编写、编译 Solidity 代码。
- Circle 智能合约平台:负责把合约部署到链上,并给你一把 SDK 万能钥匙。
- 可编程钱包:既是钱包负责 Gas,又能在后台自动签名,省去繁琐手工操作。
代码剖析:最小可运行的兑换合约
与 Uniswap 交互的合约其实比你想象中简单。下面这份精简合约实现了两个功能:
swapExactInputSingle:用固定数量 WETH 换最大数量 USDC。swapExactOutputSingle:用最少数量 WETH 换固定数量 USDC。
之所以先提到 WETH,是因为 Uniswap 内部用 WETH 计价,存入的 ETH 会自动转成 WETH(关键词:WETH、以太坊原生代币)。
// 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; // 0.3%
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
// 功能 1:固定 WETH 输入,求最大 USDC 输出
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);
}
// 功能 2:固定 USDC 输出,求最少 WETH 输入
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);
}
}
}步骤一:在 Remix 中编译并提取合约数据
关键词:Remix IDE、编译、ABI、字节码
- 打开 Remix:在浏览器输入 remix.ethereum.org。
- 新建 SwapExamples.sol,将上面代码原样贴入。
- Compiler 页签 → 选择 Solidity 0.8.20 → Compile SwapExamples.sol。
- 点击 Compilation Details → 复制
ABI并 JSON.stringify。
例:将[{"inputs":[...变成字符串'[{"inputs":[...}]'。 - 再复制 Bytecode,前面自带 0x 无需再手动添加。
步骤二:将合约塞进 Circle SDK「自动发射器」
关键词:Circle SDK、部署智能合约、钱包 ID、Polygon 测试网、Gas 抽象层
确保机器已装 Node,再敲两行命令:
npm install @circle-fin/smart-contract-platform \
@circle-fin/developer-controlled-wallets --save接着,在你的 .js 文件顶部:
const circleContractSdk = require('@circle-fin/smart-contract-platform');
const developerControlledWallets = require('@circle-fin/developer-controlled-wallets');核心参数清单(注意保密)
| 参数 | 示例 | 说明 |
|---|---|---|
| name | "Swap Contract" | 展示用 |
| walletId | "046b6c7f-..." | 您在 Circle 控制台生成的钱包 |
| blockchain | "MATIC-AMOY" | 本文演示 Polygon Amoy 测试网 |
| constructorParameters | ["0xYourWalletAddress"] | 通常是钱包地址 |
| entitySecretCiphertext | "0NtD3..." | 用 Circle CLI 加密后的实体密钥 |
| abiJSON | '[{"inputs":[...}]' | 字符串化 JSON |
| bytecode | "0x6080..." | Remix 获得的 Bytecode |
完整调用代码:
const response = await circleContractSdk.deployContract({
name: 'Swap Contract',
description: '合约:WETH 换 USDC',
walletId: '把你的 walletId 填进来',
blockchain: 'MATIC-AMOY',
fee: { type: 'level', config: { feeLevel: 'MEDIUM' } },
constructorParameters: ['0xYourWalletAddress'],
entitySecretCiphertext: '把你的加密实体密钥填进来',
abiJSON: JSON.stringify([{"inputs":[...}]), // 字符串化
bytecode: '0x6080...yourBytecode'
});
console.log(response.data);
// 输出示例:
// { "data": { "contractId": "0189db84-...", "transactionId": "7b989c65-..." } }部署成功后,获得 contractId + transactionId,用下面的代码查询 实时状态:
const check = await circleContractSdk.getContract({ id: '0189db84-...' });
console.log(check.data.contract.status); // 期待 "COMPLETE"步骤三:真正发起兑换交易
关键词:代币兑换、调用函数、gas 抽象
当合约状态 COMPLETE 后,你会看到 contractAddress: 0x1e124d...,接下来用它执行 swapExactInputSingle。
精简版代码(替换关键变量):
const executeRes = await developerControlledWallets.createContractExecutionTransaction({
walletId: 'ce714f5b-...',
contractAddress: '0x2f3A40A3db8a7...',
abiFunctionSignature: 'swapExactInputSingle(uint256)',
abiParameters: [1000], // 换 1000 wei 为单位的 WETH
fee: { type: 'level', config: { feeLevel: 'MEDIUM' } }
});
console.log(executeRes.data); // 包含 transactionId 与链上 txHash常见问题(FAQ)
- Q:为什么部署到 Polygon Amoy 而不是主网?
A:初学者先用测试网练手,打领水就能免 gas 费,待操作无误后可 一键改链 到主网。 - Q:需不需要手动把 ETH 换成 WETH?
A:不必。合约内部会自动把 ETH 存成 WETH,但主网调用需给足 ETH。 - Q:amountOutMinimum 设为 0 安全吗?
A:测试网随意,主网务必用预言机或滑点保护,否则会被 MEV 机器人 三明治攻击。 - Q:能否在同合约再加 ERC20 互换?
A:绝对可以,把新 Token 地址、新池费率填进去,再复用现有架构即可。 - Q:Circle 的 SDK 支持 Solana 或 BNB Chain 了吗?
A:截至目前仅支持 EVM 系链(如 Polygon、Avalanche C-Chain、Ethereum 主网等)。 未来列表将持续扩张。
写在最后
借助 Circle 提供的 gas 抽象化 与 一站式 SDK,你把原本需要 5 篇教程才能讲清楚的全流程,压缩到了 3 个核心步骤:
编译 → 部署 → 调用。
下一步,你可以把这套脚本嵌入 DApp 后端,每天自动把 以太坊收益换成 USDC;或者做成 Telegram Bot,一键帮朋友换汇。
圈重点:关键词:智能合约、Circle SDK、USDC 兑换、代币自动化、DeFi 进阶——抓住它们,你就能占领搜索高地。祝编码愉快,链上冲浪!