ethers-token 使用指南:让 DApp 开发少掉 80% 的 token 烦恼

·

随着 DeFi 的爆炸式增长,DApp 开发工具ERC20 合约调用ethers.js智能合约测试token 单位换算 等关键词已经成为开发者日常搜索的高频词。本文带你一次性吃透 ethers-token 这个小而美的库,看看它是如何用“人类语言”解决 token 交互痛点的。


为什么需要 ethers-token?

在 Ethereum 上做交互,最常见的动作无非三件事:

  1. 给用户看 1 DAI 就是 1 DAI,而不是 1000000000000000000
  2. 给用户转 100 USDC 就是 100 USDC,而不是 100000000
  3. 授权、查余额、交换资产时不用再写冗长的 decimals 乘除。

然而现实往往是:

ethers-token 用“一句话建对象、一两个函数调授权”的方式,把问题打了个对折。


没有 ethers-token 之前的真实痛点

下面是一段经典(折磨)场景的精简代码:

场景:在本地 Hardhat 叉子节点,用 WETH/USDC 池子做 100 DAI 兑换。
// L19–L20:先声明!再去 hook 里填!
let DAI: Contract, USDC: Contract, UniswapV2Router: Contract;

before(async () => {
  DAI = await ethers.getContractAt('IERC20', DAI_ADDR);
  USDC = await ethers.getContractAt('IERC20', USDC_ADDR);
});

it('swap 100 DAI for USDC', async () => {
  // L30, 35:单位换算写死人
  const amountIn = ethers.utils.parseUnits('100', 18);
  const amountOutMin = ethers.utils.parseUnits('99', 6);

  await DAI.approve(UNISWAP_ROUTER, amountIn);
  await UniswapV2Router.swapExactTokensForTokens(
    amountIn,
    amountOutMin,
    [DAI_ADDR, USDC_ADDR],
    signer.address,
    deadline
  );
});

问题总结:

  1. 变量声明散布:测试逻辑被 let foo: Contract 污染。
  2. 单位换算难记:100 个 DAI=100 * 10^18,100 个 USDC=100 * 10^6
  3. 语义割裂:我们看的是“100 DAI”,代码里全是 BigNumber

有了 ethers-token 以后的世界

只需两段代码,世界瞬间清净。

// token.config.ts
import { Token } from 'ethers-token';
import { providers } from 'ethers';

const provider = new providers.JsonRpcProvider();

export const DAI = Token.erc20({
  address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
  provider,
  symbol: 'DAI',
  decimals: 18,
});

export const USDC = Token.erc20({
  address: '0xA0b86a33E6441d24E6e6c511F8B88e33DBe53C50',
  provider,
  symbol: 'USDC',
  decimals: 6,
});
// swap.test.ts
import { DAI, USDC } from './token.config';

it('swap 100 DAI for USDC with ethers-token', async () => {
  // L45: approve 直接链上方法 + 语义化数字
  await DAI(100).from(signer).approve(UNISWAP_ROUTER);

  // L49: swapExactTokensForTokens 的传参更直观
  await UniswapV2Router.swapExactTokensForTokens(
    DAI(100),           // ← 100 DAI
    USDC(99),           // ← 最少得到 99 USDC
    [DAI.address, USDC.address],
    signer.address,
    deadline,
  );

  const usdcBalance = await USDC.balanceOf(signer.address);
  expect(usdcBalance).gte(USDC(99)); // ← 直接用对象比较
});

亮点提炼:


实战案例:批量授权 & 批量转账

场景:一个空投合约需要给 1000 个赢家分别发放 1 USDC 并提前授权。

传统写法:

const usdcDecimals = 6;
for (const winner of winnerList) {
  await USDC.connect(owner).approve(winner, ethers.utils.parseUnits('1', usdcDecimals));
  await USDC.connect(owner).transfer(winner, ethers.utils.parseUnits('1', usdcDecimals));
}

使用 ethers-token

for (const winner of winnerList) {
  // 1 USDC 值语义化
  const value = USDC(1);
  await value.from(owner).approve(winner);
  await value.from(owner).transfer(winner);
}

不仅阅读体验好,还能防止 decimals 写少的低级 Bug。


FAQ:你最关心的 6 个高频疑问

Q1:ethers-token 支持 NFT 吗?
A:目前 0.x 版本主攻 ERC20 & native token;ERC721 已在路线图,可先在 issue 提需求占位。

Q2:和 ethers v6 的兼容如何?
A:最新 1.0 候选版已面向 ethers v6,接口完全一致,直接 npm i ethers-token@next 即可尝鲜。

Q3:测试网配置会不会很繁琐?
A:只需把 provider 指到 Goerli、Sepolia 等测试网 RPC,其余 token 配置仅替换 address 即可。

Q4:会和现有类型定义冲突吗?
A:库内自带 TypeChain 兼容声明,不会与现有 d.ts 冲突。

Q5:gas cost 会飙升吗?
A:ethers-token 只做 JS/TS 封装,实际链上仍是标准合约调用,Gas 与原生写法相同。

Q6:能否一听就会的 1 分钟上手示例?
A:

npm i ethers-token ethers
npx ts-node << EOF
import { providers } from 'ethers';
import { Token } from 'ethers-token';
const provider = new providers.JsonRpcProvider();
const USDT = Token.erc20({ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', provider, decimals: 6 });
console.log(await USDT.balanceOf('vitalik.eth')); // ← 一键查巨鲸余额
EOF

结语:工具虽小,价值巨大

对于每天跟 DApp 开发工具智能合约测试token 单位换算 打交道的你我,ethers-token 带来的最大收益是——把链上世界翻译成人类语言

想看到它在更复杂场景下的玩法?
👉 点击解锁更多链上花式 DeFi 组合案例

如果本文帮到你,别吝啬给 ethers-token 仓库点个 ⭐,Issues 区也欢迎一起吐槽和贡献。下一章我们将一起拆解 MetaMask Snap 与 eip-6963 的多钱包支持方案,敬请期待!