本文介绍了如何使用EIP-7702标准和QuickNode Marketplace插件构建一个去中心化应用(dApp),用于批量交易ERC-20代币,将其合并为单一资产。通过Covalent Token API获取钱包余额,并通过Aerodrome和Velodrome Swap API获取最佳兑换报价,在Base和Optimism网络上执行批量兑换。
跨各种协议管理多个 ERC-20 代币可能会很快变成一件令人头疼的事情。随着时间的推移,钱包会收集“灰尘”(少量、低价值的代币),清理它们通常意味着需要多次批准、交换和支付费用。
EIP-7702 通过允许你授权智能合约在单个原子交易中执行多个操作来简化此过程。
在本指南中,你将构建一个去中心化应用程序 (dApp),它将多个 ERC-20 代币交换批量处理到一个无缝的交易中,并将它们转换为单个资产。你将使用 QuickNode Marketplace 插件 来获取代币余额,获取最佳交换报价,并在 Base 和 Optimism 网络上执行批量交换。
到最后,你将拥有一个功能齐全的 dApp,可以将繁琐的钱包清理变成快速、一键式的操作。
准备好开始了吗?
在深入研究代码之前,让我们从高层次了解应用程序的工作流程。此过程利用多个 API 来创建简单的前端体验。
下面的流程图提供了应用程序阶段的高级概述,展示了用户从连接他们的钱包到最终交易的过程。
为了获得更详细的技术视图,下面的时序图说明了幕后发生的通信。
我们应用程序的强大之处在于 EIP-7702 与专门的 QuickNode Marketplace 插件的结合。让我们分解每个组件。
EIP-7702 是一种标准,它允许外部账户 (EOA) 在单个交易中临时充当智能合约。这解决了 DeFi 中的一个主要摩擦点:执行多个操作。
传统上,交换十种不同的代币至少需要二十个单独的操作:十个 approve
交易和十个 swap
交易,每个都需要签名和 gas 费。
EIP-7702 通过引入一种可以包含 delegation
的新交易类型来简化此过程。此委托授权特定的实现合同代表用户执行一系列调用。这实现了:
从技术上讲,此功能通过 wallet_sendCalls
JSON-RPC 方法暴露给 dApp,我们将使用 wagmi 的 useSendCalls hook 轻松访问该方法。
EIP-7702 仍然很新,并且钱包支持正在发展。在撰写本文时,MetaMask 已经将 EIP-7702 集成到他们的钱包 UI 中,提供了无缝的用户体验。当你发起 EIP-7702 交易时,MetaMask 将提示你将钱包升级到智能账户。这是一个一次性的、可逆的过程,它将授权委托给 MetaMask 审计的实现合约 EIP7702DeleGator
。
由于安全问题,主要钱包可能会管理他们自己的委托合约,而不是允许 dApp 指定任意合约。因此,钱包开发是 EIP-7702 采用的主要重点。由于只有少数钱包支持 EIP-7702,我们将建议在本指南中使用 MetaMask。
首先在工作流程级别,你需要知道用户拥有哪些 ERC20 代币。为此,我们将使用 Covalent Token API 插件。此插件提供了一种简单快速的方法来获取代币余额,而不是手动查询每个代币的余额。
我们将使用 getTokenBalancesForWalletAddress
端点来填充用户的代币列表。
如何在应用程序中获取代币余额
```typescript
const response = await client.BalanceService.getTokenBalancesForWalletAddress(
chainName as any,
address
)
```
当你从 Covalent API 查询 getTokenBalancesForWalletAddress
端点时,你将收到一个详细的 JSON 对象。最重要的部分是 items 数组,其中每个对象代表用户钱包中的单个代币。
来自 Covalent Token API 的部分响应
```json
{
"address": "0x0a417ddb75dc491c90f044ea725e8329a1592d00",
"quote_currency": "USD",
"chain_id": 8453,
"items": [\
{\
"contract_decimals": 6,\
"contract_name": "USD Coin",\
"contract_ticker_symbol": "USDC",\
"contract_address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",\
"supports_erc": ["erc20"],\
"logo_url": "https://logos.covalenthq.com/tokens/8453/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.png",\
"native_token": false,\
"is_spam": false,\
"balance": "2503277",\
"quote": 2.5007737,\
"pretty_quote": "$2.50"\
// ...\
}\
]
}
```
从这个响应中,我们的应用程序将使用关键字段,如 contract_address
、balance
、quote
(用于其美元价值)和 is_spam
标志来构建代币投资组合列表。
除了获取用户当前的代币之外,我们的应用程序还需要一个他们可以交换的有效代币列表。为此,我们使用 Aerodrome/Velodrome Swap API 的 /v1/tokens
端点。
来自 Aerodrome Swap API 的部分响应
```json
{
"tokens": [\
{\
"address": "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",\
"symbol": "USDbC",\
"decimals": 6,\
"listed": true\
}\
// ...\
]
}
```
一旦用户选择要交换的代币,你需要找到最佳的交易途径。Aerodrome Swap API(在 Base 上)和 Velodrome Swap API(在 Optimism 上)Swap API 插件可在所有可用的流动性池中找到最佳价格。
/v1/quote
端点接受源代币、目标代币和金额,然后返回最佳的汇率。成功的响应将如下所示:
来自 Aerodrome Swap API 的部分响应
```json
{
"input": {
"token": {
"address": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
"symbol": "AERO",
"decimals": 18
},
"amount": 1,
"amount_wei": "1000000000000000000",
"price_usd": 0.8814186513914624,
"value_usd": 0.8814186513914624
},
"output": {
"token": {
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"symbol": "USDC",
"decimals": 6
},
"amount": 0.876388,
"amount_wei": "876388",
"min_amount": 0.872007,
"min_amount_wei": "872007",
"price_usd": 0.999567128249789,
"value_usd": 0.876008636392576
},
"route": {
"path": [\
{\
"pool_address": "0x6cDcb1C4A4D1C3C6d054b27AC5B77e89eAFb971d",\
"is_stable": false,\
"is_cl": false,\
"hop_number": 1\
}\
],
"hops": 1,
"type": "direct"
},
"execution_price": 0.876388,
"slippage": 0.005
}
```
v1/quote
端点接受源代币、目标代币和金额,并返回最佳的汇率。我们将使用它在用户确认交换之前向他们显示预期的输出。
在我们的前端,我们调用我们自己的 API 路由,该路由又查询此端点。然后,获取的报价数据用于填充确认屏幕。
如何在应用程序中获取交换报价
```typescript
const tokenBalance = BigInt(token.balance)
// Convert from wei to token units for API call
const tokenAmount = formatUnits(tokenBalance, token.contract_decimals)
const quoteUrl = `/api/swap/quote?chainId=${chainId}&from_token=${tokenAddr}&to_token=${outcomeTokenAddress}&amount=${tokenAmount}&slippage=${APP_CONFIG.SLIPPAGE_TOLERANCE}`
const response = await fetch(quoteUrl)
```
这使我们能够在用户签署最终交易之前向他们展示预期结果的清晰细分。
在获得报价后,v1/swap/build
端点会返回随时可以发送的交易 calldata,这是一个 JSON 对象,其中包含执行交换所需的一切。我们主要需要每个交易的 data
和 to
字段。
来自 Aerodrome Swap API 的部分响应
```json
"transactions": [\
{\
"type": "approval",\
"description": "Approve AERO for swap",\
"transaction": {\
"to": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",\
"data": "0x095ea7b3...", // 为了视觉清晰而截断\
"value": "0x0",\
"gas": "0x186a0",\
"gasPrice": "0x5c7742",\
"nonce": "0xc",\
"chainId": "0x2105",\
"from": "0x1539F7fBe3C26F5611DD4A449236180990F3e80F"\
}\
},\
{\
"type": "swap",\
"description": "Swap 1.0 AERO to USDC",\
"transaction": {\
"from": "0x1539F7fBe3C26F5611DD4A449236180990F3e80F",\
"to": "0x6Cb442acF35158D5eDa88fe602221b67B400Be3E",\
"value": "0x0",\
"data": "0x24856bc30...", // 为了视觉清晰而截断\
"nonce": "0xc",\
"chainId": "0x2105",\
"gas": "0x493e0",\
"gasPrice": "0x6af87c"\
}\
}\
]
```
我们的 use-swap-builder.ts
hook 为用户选择的每个代币调用此端点。然后,它处理每个响应以组装最终的调用数组。
如何在应用程序中构建交换交易
```typescript
const tokenAmount = formatUnits(BigInt(token.balance), token.contract_decimals)
const buildParams: SwapBuildParams = {
from_token: tokenAddr as Address,
to_token: outcomeTokenAddress as Address,
amount: tokenAmount, // Convert to token units, not wei
wallet_address: userWalletAddress as Address, // User's actual wallet address
slippage: APP_CONFIG.SLIPPAGE_TOLERANCE,
}
const buildUrl = `/api/swap/build?chainId=${chainId}`
const response = await fetch(buildUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(buildParams),
})
```
最后一步是一次发送所有准备好的交易。Wagmi 的 useSendCalls
hook 使这变得非常简单,并允许你传递一个交易对象数组以批量执行。
calls
参数是一个数组,其中每个对象代表我们想要执行的单个交易。我们将通过映射用户的选定代币并使用来自 /v1/swap/build
端点的数据来构建此数组。
我们还将 experimental_fallback
选项设置为 true
,如果钱包不支持 EIP-7702,则允许交易回退到常规交易。
来自应用程序的部分代码片段
```typescript
{
// Build transaction calls for batch execution using Swap APIs
const calls = await buildSwapCalls(
selectedTokens,
tokens,
outcomeToken,
chainId,
address
)
if (calls.length === 0) {
throw new Error('No valid swap calls could be built')
}
// Execute batch transaction using MetaMask EIP-7702, with fallback for non-supporting wallets
sendCalls({ calls, experimental_fallback: true })
}
```
现在,让我们设置项目并在你的本地机器上运行该应用程序。
在运行应用程序之前,你需要一个 QuickNode 端点和所需的插件,以及 WalletConnect 项目 ID。
由于这些 API 插件仅在主网上可用,因此你需要使用主网端点设置你的 QuickNode 帐户。
你只需要安装你计划支持的链的插件。免费套餐足以开始使用。
获取 Covalent API 密钥:单击 Covalent Token API 插件旁边的“登录到仪表板”。这将重定向到 Covalent 仪表板,你可以在其中找到你的 API 密钥。
获取交换 API URL:单击 Aerodrome 或 Velodrome Swap API 插件旁边的“入门”。这将向你显示你的应用程序中需要使用的基本 API URL。使用 /v1/...
部分之前的 URL,因为我们将在我们的代码中附加特定的端点。它应该看起来像这样:https://YOUR-QUICKNODE-ENDPOINT-URL/addon/YOUR-ADDON-ID
创建一个 Reown(以前称为 WalletConnect)项目:前往 Reown Cloud 并创建一个新项目。你可以随意命名。
获取项目 ID:创建项目后,你将被重定向到项目仪表板。在这里,你可以找到你的项目 ID,你需要在你的应用程序中使用它。
```bash
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/sample-dapps/token-sweeper-eip-7702
```
```bash
npm install
## 或
yarn install
## 或
pnpm install
```
.env.example
文件在项目的根目录中创建一个 .env.local
文件。```bash
cp .env.example .env.local
```
现在,打开 .env.local
并添加相关的环境变量。
```
## API 密钥(仅服务器端)
COVALENT_API_KEY=your_covalent_api_key_here
## WalletConnect 项目 ID(RainbowKit 所需 - 需要客户端)
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id_here
## RPC URLs
NEXT_PUBLIC_BASE_RPC_URL=your_base_rpc_url_here
NEXT_PUBLIC_OPTIMISM_RPC_URL=your_optimism_rpc_url_here
## Aerodrome/Velodrome API URLs(实际交换功能所需)
## 注意:这些现在仅在服务器端
AERODROME_BASE_API=your_quicknode_aerodrome_base_api_here
VELODROME_OPTIMISM_API=your_quicknode_velodrome_optimism_api_here
```
```bash
npm run dev
## 或
yarn dev
## 或
pnpm dev
```
打开你的浏览器到 http://localhost:3000
以查看应用程序的实际效果。
当你准备好部署时,像 Vercel 或 Netlify 这样的平台提供了与 Next.js 应用程序的无缝集成,从而提供了从开发到生产的简单途径。
你已成功构建了一个 Token Sweeper dApp,该 dApp 演示了 EIP-7702 在批量交易中的强大功能。此应用程序展示了 QuickNode 的 Marketplace 插件如何消除对复杂后端基础架构的需求,同时提供企业级功能。
Covalent 全面的代币数据、Aerodrome 和 Velodrome 优化的 DEX 路由以及 EIP-7702 的批量执行相结合,创造了一种无缝的用户体验,使用户能够以最小的摩擦交换代币。
如果你有任何问题,请随时使用我们的 Discord 服务器,或者使用下面的表格提供反馈。请关注我们的 Twitter 和我们的 Telegram 公告频道,及时了解最新信息。
如果你对新主题有任何反馈或要求,请告诉我们。我们很乐意听取你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!