DEFI深入浅出实战编程一入门篇

翻译者: 大杨, 链因智能CEO, 一家初创区块链技术公司,DEFI布道者。

技术点: DEFI,ETH,NODEJS,SOLIDITY

今天抽出了一些时间,翻译了CoinBase的Pete Kim 写的【Introduction to Building on DeFi with Ethereum and USDC — Part 1】DEFI编程入门学习的文章。

翻了下Pete Kim的twitter和他的github,发现是个全栈高手和社区活跃者,看上去应该是韩国人,技术很不错,人也很帅 😂。

最为美系的第一交易所CoinBase对整个Crypto生态的贡献是巨大的,本篇DEFI编程的入门文章也非常适合新手,看了有很想翻译的冲动,就花了2小时做了下整理,并在kovan的网络上实战了一把,对很多细节的地方做了补充,强烈推荐给大家。 经历DEFI,后续链因智能3年积累的相关技术都会进行开源,通过开源促进技术发展。

以下是文章的内容:

首先介绍 CoinBase 的价值观和愿景。

在Coinbase,我们的任务是建立一个开放的财务系统。我们坚信,促进经济自由将使世界变得更美好。分布式金融,简称DeFi-一种开放,无边界和可编程的金融-是我们伟大愿景不可分割的一部分。

智能合约

DeFi由运行在分布式网络(例如以太坊(“区块链”))上的数字货币(如美元代币(USDC))智能合约组成。智能合约的想法由来实际上很简单。数字货币和密码学的先驱尼克·萨博(Nick Szabo)最初在1997年提出了这个想法,他将自动售货机描述为智能合约的始祖

自动售货机是合同的自动化版本,以电子硬件的形式表示:
您通过向机器中投入钱来支付显示的价格,机器将为您分配饮料
您不支付显示的价格,也不分配饮料
如果您支付了显示的价格但机器没有分配饮料,或者即使您没有支付显示的价格也分配了饮料,则违反了合同
自动售货机无需人工干预即可完全自主地管理其合同义务。
现代智能合约的工作方式相同,但是合约条款被表示为可执行的计算机代码,而不是在硬件中实现。在其上运行智能合约的网络的分散性确保了它们以书面形式执行,并且没有任何单个实体能够弯曲规则或操纵结果。一个重要的警告是,因为网络逐字执行代码,所以错误的智能合约代码可能会导致意外的后果(“代码就是法律”)。

没有比现在更好的时机

许多人发现基于加密技术构建的区块链非常令人生畏,并认为只有坚韧的计算机科学家才能使用它。尽管就在几年前,情况确实如此,但自那时以来,工具和开发人员用户体验已有了显着改善,任何具有基本编程技能的人都可以开始构建。
DeFi生态系统目前正在爆炸性增长。 USDC在不到2年的时间里达到了$1B的市值,并且在不到3年的时间内,存储在各种DeFi服务中的资产总值超过$2B。确实没有一个哪个领域有这么好的时机快速发展。

DeFi Pulse
来源:DeFi Pulse

以下教程是开始开发自己的DeFi智能合约的简单指南。
我们希望,这样的指南将有助于建立全球开放的民主化金融体系

入门

本教程系列假定您具有JavaScript的经验,JavaScript是世界上使用最广泛的编程语言。您也可能被介绍过Solidity,这是一种在以太坊上使用的智能合约编程语言,这是世界上使用最广泛的智能合约区块链编程语言。最后,您将与DeFi应用常用稳定币USDC进行交互(这里我们Fake了一个USDC,不是真实USDC)。

搭建开发环境

首先,我们需要一个类似Unix的环境,并在其上安装Node.js v12.x(最新的LTS版本)。 MacOS本质上是Unix环境,Windows用户可以通过从Microsoft Store在WSL上安装Ubuntu来获得它。在MacOS和Windows中可以找到更详细的步骤。对于代码编辑器,强烈建议使用 Visual Studio Code,因为您要使用的项目模板已经预先配置好了,但是从技术上讲,您可以使用任何编辑器。哦,我更喜欢Vim而不是Emacs。

设置项目

设置Solidity项目需要一些工作,老实说,分心对于这个阶段的学习不是很有用,因此已经为您准备了一个预先配置的模板。

这块作者提供了一个很好的模版来跑,原地址: https://github.com/CoinbaseStablecoin/solidity-tutorial ,这里我做了一些修改,FORK了一个项目地址,大家需要看代码的可以用下面的地址,作者提供的模版非常方便,可以让动手,推荐使用作者的地址。

在终端中运行以下命令以下载并设置模板:

1
2
3
4
5
git clone https://github.com/CoinbaseStablecoin/solidity-tutorial.git # 作者原模版地址
git clone https://github.com/chaininout/solidity-tutorial.git # 这个是包含了最新代码的地址
cd solidity-tutorial
npm install -g yarn # Install yarn package manager 安装包管理器
yarn # Install project dependencies 安装依赖

当yarn尝试构建本机扩展时,您可能会看到一些编译错误。这些是可选的,可以忽略这些错误。只要您最后看到“完成”消息,就可以继续。

在Visual Studio Code中打开项目

在Visual Studio Code中打开项目文件夹(solidity-tutorial)。第一次打开项目时,Visual Studio Code可能会提示您安装扩展。继续并单击“全部安装”,这将向编辑器添加各种有用的扩展,例如自动代码格式和Solidity语法突出显示。

在以太坊上创建账户

在以太坊上进行任何操作之前,您需要拥有一个帐户。帐户通常称为“钱包”,因为它们可以包含ETH和USDC等数字资产。最终用户通常使用Coinbase Wallet或Metamask等以太坊钱包应用程序创建帐户,但是使用模板随附的出色的ethers.js库,以编程方式创建帐户也非常简单。

在src文件夹中创建一个名为createWallet.js的新JavaScript文件,然后输入以下代码:

保存文件,并使用Node执行代码,如下所示:

1
2
3
4
const ethers = require("ethers");
const wallet = ethers.Wallet.createRandom();
console.log(`Mnemonic: ${wallet.mnemonic.phrase}`);
console.log(`Address: ${wallet.address}`);

在shell命令行状态下执行:

1
node src/createWallet.js

执行后生成如下结果, 生成助记词和地址:

1
2
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008

刚才发生了什么? 好吧,您拥有了一个全新的以太坊账户。 “助记符”或可能更普遍地称为“恢复短语”是从帐户执行操作所需的密钥的人类可读表示,并且地址是帐户的名称和标识符。将它们复制到某个地方。附带说明一下,本文中显示的助记符已稍作更改,以阻止您使用它,请使用您自己的助记符

可以将它们视为银行帐户的密码和帐号,只需要几秒钟即可创建一个,并且不必填写申请表或共享任何个人信息。您也可以在任何地方运行此代码。
⚠️注意该帐户的记忆符号必须保密。如果丢失,您将永远无法访问您的帐户以及该帐户中存储的所有资产,没有人能够为您提供帮助!妥善保管!
从技术上讲,您本身并没有真正“创建”帐户。相反,您创建的是私钥/公钥对。如果您对引擎盖下实际发生的事情感到好奇,请阅读椭圆曲线密码学以及比特币和以太坊规范BIP39,BIP32,EIP55及其在此项目中的实现。

关于Gas燃料和挖矿

以太坊是一个分布式网络,由世界各地成千上万的计算机组成,它们并非完全免费。要在区块链上执行任何状态更改(例如存储和更新数据),您必须向网络运营商支付以太坊(ETH)的交易费,在以太坊上也称为“ GAS”。这与运营商为在链中添加新区块而获得的奖励一样,也激励着他们保持计算机正常运行。此过程称为“挖矿”,而网络运营商称为“矿工”。我们将在本教程的后面部分(GAS,GAS价格和GAS限制)再次讨论这一点。

获取测试网ETH

现在您已经有一个帐户,您应该存入一些ETH。我们不想在开发时浪费真钱,因此我们将获得一些假的ETH,用于在测试网络(“ testnet”)上进行开发和测试。有许多不同的以太坊测试网,但由于容易获得测试Token,因此我们将使用Kovan。首先,让我们使用以太坊的区块浏览器Etherscan检查当前余额。为此,您可以在浏览器中输入以下网址,然后用您之前创建的地址(从0x开始)替换 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008。
https://kovan.etherscan.io/address/0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
您应该看到您的余额为0 ETH。保持此选项卡处于打开状态,然后在其他选项卡中打开Kovan Ethereum Faucet 地址: https://faucet.kovan.network。在页面中,输入您的地址,然后单击“发送给我”按钮。交易可能只需要几秒钟到一分钟或一两分钟即可完成。再次检查Etherscan,您应该在列表中看到1 ETH的新余额和一笔传入交易。

DeFi Pulse
资料来源:https://faucet.kovan.network

以编程方式获取ETH余额

连接以太坊,使用Etherscan查看余额非常有用,但是也可以通过代码轻松查看余额。但是,在回到代码之前,我们需要一种连接到以太坊的方法。有很多方法可以做到这一点,包括自己在计算机上运行网络节点,但是到目前为止,最快,最简单的方法是通过诸如INFURA或Alchemy之类的托管节点提供程序来实现,也可以自建节点。前往INFURA,创建一个免费帐户并创建一个新项目以获取API密钥(项目ID),链接这里: https://infura.io/dashboard/ethereum 注册后在面板中创建自己的Project,然后拿到APIKEY。如果要自己搭建节点,也可以使用Go Ethereum(“ geth”)和Open Ethereum(以前称为Parity Ethereum)是两种使用最广泛的以太坊节点软件,这里后续我会在后续章节中列举节点搭建。

用代码查看ETH余额

首先,让我们编写代码以从助记符中读取并获取帐户。在src文件夹中创建一个名为wallet.js的新JavaScript文件,然后输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
const ethers = require("ethers");

// Replace the following with your own mnemonic 这里输入刚刚生成的助记词
const mnemonic =
"rabbit enforce proof always embrace tennis version reward scout shock license wing";
const wallet = ethers.Wallet.fromMnemonic(mnemonic);

console.log(`Mnemonic: ${wallet.mnemonic.phrase}`);
console.log(`Address: ${wallet.address}`);

module.exports = wallet;

用您自己的代码替换助记符字符串。请注意,在生产代码中,助记符不应该这样硬编码。相反,应从配置文件或环境变量中读取它,以免例如由于将其检入源代码存储库而意外泄漏。
执行代码,您应该能够看到与之前获得的地址相同的地址:

1
2
3
➜  solidity-tutorial git:(master) ✗ node src/wallet.js
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008

看到生成的地址:0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
接下来,在同一文件夹中创建一个名为provider.js的新文件。在此文件中,我们将使用我们先前获得的INFURA API密钥初始化提供者对象。确保将API密钥字符串替换为您自己的字符串:

1
2
3
4
5
6
7
8
const ethers = require("ethers");
// 注意这里我们使用的kovan的网络,下面是infura上申请的key
const provider = ethers.getDefaultProvider("kovan", {
// Replace the following with your own INFURA API key
infura: "c844845b06f84d379ba3fb3bba5a1f99",
});

module.exports = provider;

注意这里需要在truffle-config.js进行网络配置:

1
2
3
4
5
6
7
8
9
10
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*",
},
mainnet: infuraProvider("mainnet", 1),
ropsten: infuraProvider("ropsten", 3),
kovan: infuraProvider("kovan", 42), //这个是我们加上的测试网络,因为比较快
},

最后,我们将使用在同一文件夹中新建名为getBalance.js的新文件,并通过wallet.js和provider.js来获取ETH余额:

1
2
3
4
5
6
7
8
9
10
11
const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
const account = wallet.connect(provider);
const balance = await account.getBalance();
console.log(`ETH Balance: ${ethers.utils.formatUnits(balance, 18)}`);
}

main();

运行代码,您将看到ETH余额!

1
2
3
4
node src/getBalance.js
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
ETH Balance: 1.0

代币计价

我们刚刚创建的代码很容易解释,但是您可能想知道ethers.utils.formatUnits(balance,18)的作用。好吧,ETH实际上可以整除到小数点后18位,最小的面额单位称为“ wei”(发音为“ way”)。换句话说,一个ETH等于1,000,000,000,000,000,000 wei。另一个常见的面额是Gwei(发音为“ Giga-way”),为10亿魏。 getBalance方法碰巧以wei返回结果,因此我们必须通过将结果乘以10⁸将其转换回ETH。可以在这里找到所有面额的清单。
您还可以使用ethers.utils.formatEther(balance),这是ethers.utils.formatUnits(balance,18)的简写。

获取Testnet USDC

您帐户中的ETH感到有些孤单,所以我们也想获取其中的USDC。我在Kovan测试网上部署了一个FakeUSDC智能合约。它没有专门的水龙头网站,但合同包含一项功能,在调用该功能时,您将获得一些免费的Kovan测试网USDC。如果您导航到Etherscan中的合同代码选项卡,并在合同源代码中搜索gimmeSome。这就是我们要调用的功能,用于将一些USDC发送到我们的帐户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pragma solidity 0.6.12;

contract FakeUSDC is Ownable, ERC20 {
constructor() public Ownable() ERC20("FakeUSDC", "USDC") {
_setupDecimals(6);
}

function mint(address account, uint256 amount) external onlyOwner {
_mint(account, amount);
}

function burn(address account, uint256 amount) external onlyOwner {
_burn(account, amount);
}

/**
* @notice Give the caller 10 USDC (10,000,000 units)
* 这里我们加了个给测试账户发送代币的方法,能够免费给账户发送USDC测试代币
*/
function gimmeSome() external {
_mint(msg.sender, 10e6);
}
}

进行交易以调用智能合约

在以太坊智能合约中主要有两种类型的功能:读写和只读。前者可能导致存储在区块链中的数据发生变化,而后者纯粹是读取但从未写入。可以在不创建事务的情况下调用只读函数,因此无需支付交易费用,除非作为读写函数的一部分进行调用。另一方面,必须在交易内部调用读写功能,并且必须支付交易费(GAS)。调用gimmeSome函数会导致存储在区块链中的USDC余额发生变化,因此必须在交易内部调用它

调用智能合约功能需要一些额外的步骤,但这并不太困难。首先,我们需要找到我们要调用的函数的完整接口,也称为函数签名或函数原型。在合同源代码中再次查找gimmeSome,您将发现界面如下:

1
function gimmeSome() external

这是一个非常简单的函数,不带任何参数,并且被标记为外部函数,这意味着该函数只能从外部调用,而不能从该协定内的其他函数调用。因为我们将在事务中直接调用此函数。
显而易见,在以太坊主网络上部署的“真实” USDC合约中 不存在gimmeSome功能。 下面需要去部署智能合约,才能获取合约的地址,以便在下面调用:

1
MNEMONIC="viable glow tone pole input series weather slam blouse embark achieve position" INFURA_API_KEY="c844845b06f84d379ba3fb3bba5a1f99" truffle deploy --network=kovan

我们执行 truffle deploy 命令,在kovan测试网络中发布我们写好的合约。 签名的2个是地址的私钥和INFURA的KEY,这样能够用这个账户部署合约,部署完成后将现实如下过程,部署的合约数量和总共花费的GAS的费用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
3_init_FakeUSDC.js
==================

Deploying 'FakeUSDC'
--------------------
> transaction hash: 0x239e6922e55957d964dec79ffc2a8667b375c87185e47a334d824ffea89d9c0f
> Blocks: 1 Seconds: 5
> contract address: 0x4bB2ef39DD99A01BD5C11dDd25F890FeB45081e9 # 这个我们在后续要用到
> block number: 21121362
> block timestamp: 1600937752
> account: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
> balance: 0.95161372
> gas used: 1705672 (0x1a06c8)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.03411344 ETH


> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.03411344 ETH


Summary
=======
> Total deployments: 3
> Final cost: 0.03985546 ETH

拿到合约地址,在src文件夹中创建一个名为getTestnetUSDC.js的新文件,然后输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
const account = wallet.connect(provider);

const usdc = new ethers.Contract(
"0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4", //这里是我们发布的合约地址
["function gimmeSome() external"], //我们本次需要调用的函数名
account
);

const tx = await usdc.gimmeSome({ gasPrice: 20e9 }); //指定gas
console.log(`Transaction hash: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
console.log(`Gas used: ${receipt.gasUsed.toString()}`);
}

main();

该代码首先使用我们感兴趣的函数gimmeSome的接口实例化一个合同对象(新ethers.Contract),并将其指向testnet Fake USDC合同的地址:0x68ec⋯69c4。然后,您可以调用列出的任何功能。 gimmeSome函数本身不会接受任何参数,但是您可以将事务选项指定为最后一个参数。在这种情况下,我们给它20 Gwei的GAS价格,这将加快交易速度。本质上,与网络交互的所有方法都是异步的,并返回Promise,因此我们使用的是JavaScript的await表达式。然后,代码将打印交易哈希,这是您交易的唯一标识符,可用于跟踪进度。然后等待直到确认交易。
运行代码,您将看到类似以下内容:

1
node src/getTestnetUSDC.js

输出结果:

1
2
3
4
5
6
➜  solidity-tutorial git:(master) ✗ node src/getTestnetUSDC.js
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
Transaction hash: 0xf7ca007d794ea7d530da36b8cc55f6dce3c99276d65d45e0d32ea10290505666
Transaction confirmed in block 21121386
Gas used: 65221

瞧!您已经使用代码进行了首次以太坊交易!在Kovan Etherscan中检查您的地址和交易哈希。现在您应该看到,由于执行交易所支付的汽油费用,您现在拥有10个测试网USDC和略少于1 ETH。

ℹ️如果您在Etherscan中检查交易,您会发现这是将 (0)ETH与4字节数据一起发送到合约地址的交易。如果函数调用具有参数,则将不止4个字节的数据。如果您想了解此数据的编码方式,请阅读以太坊合约ABI规范。

GAS,GAS价格和GAS限额

之前,我提到过,我们为交易提供20 Gwei的价格,以加快交易速度,并且脚本还显示使用的GAS量。这些都是什么意思?嗯,以太坊是一个由网络运营商组成的网络。将其视为一台世界计算机。它不是一台免费的计算机,您在该计算机上运行的每条指令都需要钱。该计算机也被世界各地的所有人共享,这意味着每个人都必须互相竞争才能在此计算机上花费时间。

我们如何做到这一点?

好吧,我们可以在这台计算机上拍卖时间,而您愿意为在此计算机上运行的每条计算指令支付的费用越多,网络运营商(矿工)将给您更多的时间。这种确定不是完美的,因为它可能会导致只有富人才能使用此系统的特权。但是,这是我们拥有的最差的解决方案,直到系统具有更高的可伸缩性并可以容纳更多的交易为止。
回到区块链的术语上,“已使用的GAS”是您运行交易后消耗的计算资源量,“GAS价格”是您愿意为每单位天然气支付多少费用。通常,您愿意支付的费用越高,交易的优先级就越高,并且网络会更快地对其进行确认。在我们的案例中,我们使用20 Gwei作为GAS价格,使用的GAS为35121(您还可以通过在Etherscan中检查交易来找到此价格),因此总GAS成本为35121 20 Gwei = 702,420 Gwei 或0.00070242 ETH。
由于用GAS要花钱,因此您可能需要设置您愿意花费的最大GAS上限。幸运的是,您可以设置“GAS限制”。如果交易最终需要的GAS超过指定的GAS上限,则交易将失败,而不是继续执行并消耗比您愿意支付的更多的GAS。要注意的一个副作用是,*如果执行由于限制而最终失败,那么已花费的汽油量将不会退还给您

调用智能合约功能以读取数据

您可以检查是否在Etherscan上收到了10 USDC,但请通过代码检查余额来确认这一点。
我们在src文件夹中的创建个新文件getFakeUSDCBalance.js,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
const account = wallet.connect(provider);

// Define FakeUSDC contract interface
const usdc = new ethers.Contract(
"0x4bB2ef39DD99A01BD5C11dDd25F890FeB45081e9",
[
"function balanceOf(address _owner) public view returns (uint256 balance)",
],
account
);

// get eth
const ethBalance = await account.getBalance();
console.log(`ETH Balance: ${ethers.utils.formatEther(ethBalance)}`);

// Call FackUSDC balanceOf function
const usdcBalance = await usdc.balanceOf(account.address);
console.log(`USDC Balance: ${ethers.utils.formatUnits(usdcBalance, 6)}`);
}

main();

USDC是ERC20代币,因此它包含ERC20规范中定义的所有方法。 balanceOf是其中之一,其接口直接来自规格。 balanceOf是一个只读函数,因此可以免费调用,不需要作为事务提交。 最后,重要的是要注意,USDC使用6个小数位精度,而许多其他ERC20令牌使用18个精度。

您可以在此处了解有关Solidity函数的更多信息。
运行代码,现在您还将看到USDC余额:执行:

1
2
3
4
5
➜  solidity-tutorial git:(master) ✗ node src/getFakeUSDCBalance.js 
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
ETH Balance: 0.9497621
USDC Balance: 10.0 //这里拿到了 USDC返回的数量

转移ETH和USDC

现在,让我们看看如何使用帐户中的ETH和USDC。

转移ETH

在src文件夹中创建transferETH.js并输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main(args) {
const account = wallet.connect(provider);
let to, value;

// Parse the first argument - recipient address
try {
to = ethers.utils.getAddress(args[0]);
} catch {
console.error(`Invalid recipient address: ${args[0]}`);
process.exit(1);
}

// Parse the second argument - amount
try {
value = ethers.utils.parseEther(args[1]);
if (value.isNegative()) {
throw new Error();
}
} catch {
console.error(`Invalid amount: ${args[1]}`);
process.exit(1);
}
const valueFormatted = ethers.utils.formatEther(value);

// Check that the account has sufficient balance
const balance = await account.getBalance();
if (balance.lt(value)) {
const balanceFormatted = ethers.utils.formatEther(balance);

console.error(
`Insufficient balance to send ${valueFormatted} (You have ${balanceFormatted})`
);
process.exit(1);
}

console.log(`Transferring ${valueFormatted} ETH to ${to}...`);

// Submit transaction
const tx = await account.sendTransaction({ to, value, gasPrice: 20e9 });
console.log(`Transaction hash: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
}

main(process.argv.slice(2));

这段代码虽然比以前的代码长,但实际上只是到目前为止所学到的一切的结合。该脚本接受两个命令行参数。第一个是收件人地址,第二个是发送金额。然后,它确保提供的地址有效,提供的金额不为负,并且帐户具有足够的余额以能够发送所请求的金额。然后,它提交交易并等待确认。
使用我们之前创建的createWallet.js脚本创建一个新帐户,然后尝试向新地址汇款

1
2
3
➜  solidity-tutorial git:(master) ✗ node src/createWallet.js 
Mnemonic: put among keep license two negative version uniform claw zebra health input
Address: 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0

我们建了个新地址,在将0.1 ETH转移到 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0 新地址上:

1
2
3
4
5
6
➜  solidity-tutorial git:(master) ✗ node src/transferETH.js 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0 0.1
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
Transferring 0.1 ETH to 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0...
Transaction hash: 0x91cb73c1ed608eec56eb6c81319bc232c8a0a7d0023eda87d1631ac45f29f49a
Transaction confirmed in block 21121715

您可以在Etherscan中验证结果。

我们还要测试验证逻辑是否有效, 以下是一些无效的例子:

1
2
3
4
5
6
7
8
9
10
11
12
$ node src/transferETH.js foo
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid address: foo
$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 0.1.2
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid amount: 0.1.2
$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 -0.1
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid amount: -0.1
$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 100
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Insufficient balance to send 100.0 (You have 0.89328474)

转移USDC

您将能够复用大部分代码。主要区别在于USDC有6个小数位,并且您必须使用ERC20规格的传递函数来执行交易。您还需要将参数“ to”和“ value”传递给转让智能合约功能,而不是以太坊交易本身。在同一文件夹中创建transferUSDC.js并输入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main(args) {
const account = wallet.connect(provider);

// Define balanceOf and transfer functions in the contract
const usdc = new ethers.Contract(
"0x4bB2ef39DD99A01BD5C11dDd25F890FeB45081e9",
[
"function balanceOf(address _owner) public view returns (uint256 balance)",
"function transfer(address _to, uint256 _value) public returns (bool success)",
],
account
);

let to, value;

// Parse the first argument - recipient address
try {
to = ethers.utils.getAddress(args[0]);
} catch {
console.error(`Invalid address: ${args[0]}`);
process.exit(1);
}

// Parse the second argument - amount
try {
value = ethers.utils.parseUnits(args[1], 6);
if (value.isNegative()) {
throw new Error();
}
} catch {
console.error(`Invalid amount: ${args[1]}`);
process.exit(1);
}
const valueFormatted = ethers.utils.formatUnits(value, 6);

// Check that the account has sufficient balance
const balance = await usdc.balanceOf(account.address);
if (balance.lt(value)) {
const balanceFormatted = ethers.utils.formatUnits(balance, 6);

console.error(
`Insufficient balance to send ${valueFormatted} (You have ${balanceFormatted})`
);
process.exit(1);
}

console.log(`Transferring ${valueFormatted} USDC to ${to}...`);

// Submit a transaction to call the transfer function
const tx = await usdc.transfer(to, value, { gasPrice: 20e9 });
console.log(`Transaction hash: ${tx.hash}`);

const receipt = await tx.wait();
console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
}

main(process.argv.slice(2));

尝试一下向新地址转1个FakeUSDC,它应该也可以正常工作:

1
2
3
4
5
6
➜  solidity-tutorial git:(master) ✗ node src/transferUSDC.js 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0 1
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
Transferring 1.0 USDC to 0x7B6d22b2130e585f5dacb40dE9Ff9DbD23E5CDB0...
Transaction hash: 0xb81d2844a086750f5b6e3a71778fc4362cc913cc9960aacdce7028c627e7f49d
Transaction confirmed in block 21121797

恭喜你,完成了!

最后看看之前的地址还有多少个ETH和USDC

1
2
3
4
5
➜  solidity-tutorial git:(master) ✗ node src/getFakeUSDCBalance.js                                        
Mnemonic: viable glow tone pole input series weather slam blouse embark achieve position
Address: 0x6598f11d48Da2bfA897d5Eb3968223f98dabc008
ETH Balance: 0.84831602
USDC Balance: 9.0 // 这里的USDC就剩下9个了

在本教程中,您学习了如何生成帐户,查询余额,转移代币以及调用智能合约。您可能以为您对加密还不很了解,但是实际上您已经足够了解能够构建自己的加密钱包应用程序了。我们一直在编写命令行脚本来简化操作,但是如何使用基于Web的漂亮图形界面来完成作业来构建脚本呢?
在本教程系列的下一部分中,我们将使用Solidity从零开始编写我们自己的以太坊智能合约,并学习如何构建自己的可与USDC交换的代币。我们还将使用今天学到的技术与该合约进行互动。敬请关注。这个是之前作者的下一篇, 之后大杨会加上如何使用脚本自动化的调用Uniswap进行自动交易。

如果您对分布式金融DEFI的未来感兴趣,请关注Chaininout链因智能,大杨作为创始人,会努力的科普DEFI技能,促进生态发展。

作者原文中有CoinBase的免责声明,这里略过,以下也是常规的我们的免责声明。

以上表达的观点是作者的观点,并不代表Chaininout链因智能的观点和立场。信息仅用于一般教育目的,并不构成对金融产品的投资或其他建议。大杨和Chaininout对本篇内容上任何信息的准确性,完整性,及时性,适用性或有效性不做任何陈述,对于该信息的任何错误,遗漏或延误,或因其造成的任何损失,伤害或损害,概不负责。显示或使用。除非另有说明,否则本文提供的所有图像均为大杨的财产,所有商标均为其各自所有者的财产。

坚持原创技术分享,您的支持将鼓励xinqiyang继续创作!