内容简介:本实践案例中,我们首先会搭建和启动一条substrate链,再通过MetaMask这款著名的以太坊钱包浏览器插件,通过自定义RPC的方式,接入我们搭建好的substrate链。 然后我们会在remix这款智能合约在线IDE上的完成ERC20智能合约的开发、编译、部署、调试。remix将通过Injected Web3的方式接入我们的MetaMask钱包,借助MetaMask实现和substrate链的交互。本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
本实践案例中,我们首先会搭建和启动一条substrate链,再通过MetaMask这款著名的以太坊钱包浏览器插件,通过自定义RPC的方式,接入我们搭建好的substrate链。 然后我们会在remix这款智能合约在线IDE上的完成ERC20智能合约的开发、编译、部署、调试。remix将通过Injected Web3的方式接入我们的MetaMask钱包,借助MetaMask实现和substrate链的交互。
在Substrate链上跑Solidity ERC20智能合约
jasonruan 2020.07.18
1 前言
本实践案例中,我们首先会搭建和启动一条 substrate 链,再通过 MetaMask 这款著名的以太坊钱包浏览器插件,通过自定义 RPC 的方式,接入我们搭建好的 substrate 链。
然后我们会在 remix 这款智能合约在线 IDE 上的完成 ERC20 智能合约的开发、编译、部署、调试。 remix 将通过 Injected Web3 的方式接入我们的 MetaMask 钱包,借助 MetaMask 实现和 substrate 链的交互。
2 前置准备
2.1 substrate链启动
由于 Substrate 的 runtime 没有集成 EVM ,需要使用 Parity 另外一个项目: Frontier 。 Frontier 是 Substrate 的以太坊兼容层。
在这个项目中,增加了 pallet-evm 、 pallet-ethereum 、 rpc-ethereum ,可以最大程度的模拟以太坊的环境,以便可以无缝对接当前以太坊生态里面 DApp 及第三方工具,如 remix 、 metamask 等。
2.1.1 安装
$ git clone git@github.com:paritytech/frontier.git $ cd frontier (master)$ git log commit fcea97281661f0a0a4c28e46edbfb1238043471e Author: Wei Tang <wei@that.world> Date: Tue Jul 14 13:25:48 2020 +0200 (master)$ git submodule init 子模组 'vendor/ethereum' (https://github.com/rust-blockchain/ethereum) 已为路径 'vendor/ethereum' 注册 子模组 'vendor/evm' (https://github.com/rust-blockchain/evm) 已为路径 'vendor/evm' 注册 子模组 'vendor/substrate' (https://github.com/paritytech/substrate) 已为路径 'vendor/substrate' 注册 (master)$ git submodule update 正克隆到 'vendor/ethereum'... 处理 delta 中: 100% (930/930), done. 子模组路径 'vendor/ethereum':检出 '4fdd3c8ac849f91f5352bc7dfc189144a09aac1e' 正克隆到 'vendor/evm'... 子模组路径 'vendor/evm':检出 '84013f3ea23e436541f409c717134dd1536143de' 正克隆到 'vendor/substrate'... fatal: 引用不是一个树:b36fc2c849ae0cba434bb3ce82e0a4dda5bcbe7a 无法在子模组路径 'vendor/substrate' 中检出 'b36fc2c849ae0cba434bb3ce82e0a4dda5bcbe7a' # 获取子模块有报错,单独clone substrate工程来解决 $ cd vendor $ /bin/rm -rf substrate $ git clone git@github.com:paritytech/substrate.git # 注意:这里不能切换到最新的tag(v2.0.0-rc4),编译会报错,采用当前最新的master分支,可以正常编译frontier [Jason@RUAN:~/frontier/vendor/substrate] (master)$ git log commit 9b5a9cb9056f8ab049837d30b500fca78a6c362c Author: Shawn Tabrizi <shawntabrizi@gmail.com> Date: Thu Jul 16 02:21:15 2020 +0200
2.1.2 添加个人账号
在substrate frontier链的创世配置里面,内建我们在MetaMask上的自有账号,并初始化配置上足够的ETH数量,以便我们开发测试的需要。
-
拷贝个人测试账号地址
0x888a0bF014d1D6334002261BaC34a16f5Ce963ac
- 修改
frontier/template/node/src/chain_spec.rs,添加以下内容:
+ 29 use std::str::FromStr;
! 141 let built_in_evm_account =
! 142 H160::from_str("888a0bF014d1D6334002261BaC34a16f5Ce963ac").unwrap();
! 143 let mut evm_accounts = BTreeMap::new();
! 144 //let ten: u128 = 10;
! 145 evm_accounts.insert(
! 146 built_in_evm_account,
! 147 evm::GenesisAccount {
! 148 nonce: 0.into(),
! 149 balance: (2018030320150719 as u128 * 10_u128.pow(18)).into(), // 设置账户初始ETH余额,注意ETH有18位小数
! 150 storage: BTreeMap::new(),
! 151 code: WASM_BINARY.to_vec(),
! 152 },
! 153 );
2.1.3 编译
$ cargo build --release
2.1.4 运行
(master)$ ./target/release/frontier-template-node purge-chain --dev Are you sure to remove "/root/.local/share/frontier-template-node/chains/dev/db"? [y/N]: y "/root/.local/share/frontier-template-node/chains/dev/db" did not exist. (master)$ ./target/release/frontier-template-node --dev --ws-external --rpc-external --rpc-cors=all 2020-07-18 22:59:28 Substrate Node 2020-07-18 22:59:28 :v: version 2.0.0-dev-fcea972-x86_64-linux-gnu 2020-07-18 22:59:28 :heart: by Parity Technologies <admin@parity.io>, 2017-2020 2020-07-18 22:59:28 :clipboard: Chain specification: Development 2020-07-18 22:59:28 Node name: stimulating-hand-5352 2020-07-18 22:59:28 :bust_in_silhouette: Role: AUTHORITY 2020-07-18 22:59:28 :floppy_disk: Database: RocksDb at /root/.local/share/frontier-template-node/chains/dev/db 2020-07-18 22:59:28 ⛓ Native runtime: node-frontier-template-1 (node-frontier-template-1.tx1.au1) 2020-07-18 22:59:28 :hammer: Initializing Genesis block/state (state: 0x1e15…857d, header-hash: 0xa26e…22df) 2020-07-18 22:59:28 :older_man: Loading GRANDPA authority set from genesis on what appears to be first startup. 2020-07-18 22:59:29 ⏱ Loaded block-time = 6000 milliseconds from genesis on first-launch 2020-07-18 22:59:29 :package: Highest known block at #0 2020-07-18 22:59:29 Using default protocol ID "sup" because none is configured in the chain specs 2020-07-18 22:59:29 Local node identity is: 12D3KooWMgcWheT7cvvpaaKmqNLVPRpAkGLR9V1M7pfs43DxZU6c (legacy representation: QmRN3jaSxZV5dMk1P5tm1CB9oud8pU8SGZvrUFfuS9dNMQ) 2020-07-18 22:59:29 :part_alternation_mark: Prometheus server started at 127.0.0.1:9615 2020-07-18 22:59:30 :raised_hands: Starting consensus session on top of parent 0xa26e0b7f2abed8a4bcf87ca45d20d7628cf22225dd522385e7b49ee6c30522df 2020-07-18 22:59:30 :gift: Prepared block for proposing at 1 [hash: 0xd3558be2df939b94ba94d82ded332216b0b06a55596d0a61a858aeaf7acc16c4; parent_hash: 0xa26e…22df; extrinsics (1): [0xead1…ac1b]] 2020-07-18 22:59:30 :bookmark: Pre-sealed block for proposal at 1. Hash now 0x8aa38d1228213371249ff83404bd8576e3e0bce6e120f93a9fe08ec2dfdc2d86, previously 0xd3558be2df939b94ba94d82ded332216b0b06a55596d0a61a858aeaf7acc16c4. 2020-07-18 22:59:30 :sparkles: Imported #1 (0x8aa3…2d86) 2020-07-18 22:59:34 :zzz: Idle (0 peers), best: #1 (0x8aa3…2d86), finalized #0 (0xa26e…22df), ⬇ 0 ⬆ 0 2020-07-18 22:59:36 :raised_hands: Starting consensus session on top of parent 0x8aa38d1228213371249ff83404bd8576e3e0bce6e120f93a9fe08ec2dfdc2d86 2020-07-18 22:59:36 :gift: Prepared block for proposing at 2 [hash: 0x570be59726cf2018fab9e7bae77208a990921291fe8164bbc1df253f8142d3f6; parent_hash: 0x8aa3…2d86; extrinsics (1): [0xec7f…0d17]] 2020-07-18 22:59:36 :bookmark: Pre-sealed block for proposal at 2. Hash now 0xe9831c239f29745d79b0e72cc26c0db19ce4d0c7712a32dae4cc60f8ad538551, previously 0x570be59726cf2018fab9e7bae77208a990921291fe8164bbc1df253f8142d3f6. 2020-07-18 22:59:36 :sparkles: Imported #2 (0xe983…8551)
2.2 MetaMask接入frontier链
2.2.1 配置自定义RPC接入
这里 ChainID 填42,源于 runtime 中的定义:
parameter_types! {
pub const EVMModuleId: ModuleId = ModuleId(*b"py/evmpa");
pub const ChainId: u64 = 42;
}
impl evm::Trait for Runtime {
type ModuleId = EVMModuleId;
type FeeCalculator = FixedGasPrice;
type ConvertAccountId = HashTruncateConvertAccountId<BlakeTwo256>;
type Currency = Balances;
type Event = Event;
type Precompiles = ();
type ChainId = ChainId;
}
2.2.2 查看账户余额
2.2.3 开启Gas控制高级选项
开启该选项后,以便我们随意配置 gas limit 和 gas price :
2.3 remix接入MetaMask
- 使用
Injected Web3建立与MetaMask账号的连接
我们看到我们创建的账号已经成功接入 remix ,接下来,我们便可以进行 ERC20 合约的开发和部署了。
3 合约开发&部署
3.1 智能合约编写
我们要编写两个合约,其中 SafeMath.sol 是一个算数运算防溢出的安全库, xyc.sol 是一个 ERC20 智能合约。
3.1.1 SafeMath.sol
pragma solidity ^0.6.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
3.1.2 xyc.sol
pragma solidity ^0.6.0;
import "./SafeMath.sol";
abstract contract ERC20 {
function totalSupply() public virtual view returns (uint256);
function balanceOf(address owner) public virtual view returns (uint256);
function transfer(address to, uint256 value) public virtual returns (bool);
function transferFrom(address from, address to, uint256 value) public virtual returns (bool);
function approve(address spender, uint256 value) public virtual returns (bool);
function allowance(address owner, address spender) public virtual view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract xyc is ERC20 {
using SafeMath for uint256;
string name = "XuanYuan Coin";
string symbol = "xyc";
uint8 decimals;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
constructor(uint256 myTotalSupply, uint8 mydecimals) public {
_totalSupply = myTotalSupply;
decimals = mydecimals;
_balances[msg.sender] = myTotalSupply;
}
function totalSupply() public override view returns (uint256) {
return _totalSupply;
}
function balanceOf(address _owner) public override view returns (uint256) {
return _balances[_owner];
}
function transfer(address to, uint256 value) public override returns (bool) {
require(to != address(0), "ERC20 ERROR: transfer from zero address");
require(value <= _balances[msg.sender]);
require(_balances[to] + value >= _balances[to]);
_balances[msg.sender] = _balances[msg.sender].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public override returns (bool) {
require(to != address(0));
require(value <= _balances[from]);
require(value <= _allowances[from][msg.sender]);
require(_balances[to] + value >= _balances[to]);
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
_allowances[from][msg.sender] = _allowances[from][msg.sender].sub(value);
emit Transfer(from, to, value);
return true;
}
function approve(address spender, uint256 value) public override returns (bool) {
require(value <= _balances[msg.sender]);
_allowances[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function allowance(address _owner, address spender) public override view returns (uint256) {
return _allowances[_owner][spender];
}
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
3.2 智能合约编译
智能合约编译成功,接下来我们就将已经开发完成的 Solidity ERC20 智能合约部署到 Substrate 链上。
3.3 智能合约部署
- 选择要部署的合约,填写构造方法中的发行总量和精度两个参数,点击
transact按钮
注:当前版本需要在弹出的MetaMask对话框上,设置很大的gas limit值( 4294967295 )才能部署成功:(
4 合约功能测试
通过 remix 完成对 ERC20 合约的功能测试。
4.1 查看发行量&余额
可见所有发行代币都归属于合约部署方地址。
4.2 转账测试
- 给账户2:
0x8A832874304Be72b299C0ED96f22B11311c9051C,转账1000.00 XYC,查看账户1地址XYC代币数量为:9000.00,符合预期
- 在MetaMask上添加自定义代币
合约地址: 0xfE2dF1EE3783eBD46481e32d849acB49ec5004Bf
- 代币发行方地址所剩代币为:9000.00 XYC
- 代币接收方地址代币数量为:1000.00 XYC
4.3 授权代理转账测试
- 为账户2转账ETH,用于支付gas费
- 用户1授权用户2可代理转账的代币数量为2000.00 XYC
- 查看用户2所能授权转账用户1的XYC代币
| 账号 | 地址 | | --------- | -------------------------------------------- | | Jasonruan | 0x888a0bF014d1D6334002261BaC34a16f5Ce963ac | | Account 2 | 0x8A832874304Be72b299C0ED96f22B11311c9051C | | Account 3 | 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b |
- 用户2代理用户1给用户3转账2000.00 XYC
成功转账后,查看到用户2所能授权转账用户1的XYC代币数量为0
- 查看到账户3上XYC代币余额
- 再次查看账户1上的代币余额
在Substrate链上跑Solidity ERC20智能合约
jasonruan 2020.07.18
1 前言
本实践案例中,我们首先会搭建和启动一条 substrate 链,再通过 MetaMask 这款著名的以太坊钱包浏览器插件,通过自定义 RPC 的方式,接入我们搭建好的 substrate 链。
然后我们会在 remix 这款智能合约在线 IDE 上的完成 ERC20 智能合约的开发、编译、部署、调试。 remix 将通过 Injected Web3 的方式接入我们的 MetaMask 钱包,借助 MetaMask 实现和 substrate 链的交互。
2 前置准备
2.1 substrate链启动
由于 Substrate 的 runtime 没有集成 EVM ,需要使用 Parity 另外一个项目: Frontier 。 Frontier 是 Substrate 的以太坊兼容层。
在这个项目中,增加了 pallet-evm 、 pallet-ethereum 、 rpc-ethereum ,可以最大程度的模拟以太坊的环境,以便可以无缝对接当前以太坊生态里面 DApp 及第三方工具,如 remix 、 metamask 等。
2.1.1 安装
$ git clone git@github.com:paritytech/frontier.git $ cd frontier (master)$ git log commit fcea97281661f0a0a4c28e46edbfb1238043471e Author: Wei Tang <wei@that.world> Date: Tue Jul 14 13:25:48 2020 +0200 (master)$ git submodule init 子模组 'vendor/ethereum' (https://github.com/rust-blockchain/ethereum) 已为路径 'vendor/ethereum' 注册 子模组 'vendor/evm' (https://github.com/rust-blockchain/evm) 已为路径 'vendor/evm' 注册 子模组 'vendor/substrate' (https://github.com/paritytech/substrate) 已为路径 'vendor/substrate' 注册 (master)$ git submodule update 正克隆到 'vendor/ethereum'... 处理 delta 中: 100% (930/930), done. 子模组路径 'vendor/ethereum':检出 '4fdd3c8ac849f91f5352bc7dfc189144a09aac1e' 正克隆到 'vendor/evm'... 子模组路径 'vendor/evm':检出 '84013f3ea23e436541f409c717134dd1536143de' 正克隆到 'vendor/substrate'... fatal: 引用不是一个树:b36fc2c849ae0cba434bb3ce82e0a4dda5bcbe7a 无法在子模组路径 'vendor/substrate' 中检出 'b36fc2c849ae0cba434bb3ce82e0a4dda5bcbe7a' # 获取子模块有报错,单独clone substrate工程来解决 $ cd vendor $ /bin/rm -rf substrate $ git clone git@github.com:paritytech/substrate.git # 注意:这里不能切换到最新的tag(v2.0.0-rc4),编译会报错,采用当前最新的master分支,可以正常编译frontier [Jason@RUAN:~/frontier/vendor/substrate] (master)$ git log commit 9b5a9cb9056f8ab049837d30b500fca78a6c362c Author: Shawn Tabrizi <shawntabrizi@gmail.com> Date: Thu Jul 16 02:21:15 2020 +0200
2.1.2 添加个人账号
在substrate frontier链的创世配置里面,内建我们在MetaMask上的自有账号,并初始化配置上足够的ETH数量,以便我们开发测试的需要。
-
拷贝个人测试账号地址
0x888a0bF014d1D6334002261BaC34a16f5Ce963ac
- 修改
frontier/template/node/src/chain_spec.rs,添加以下内容:
+ 29 use std::str::FromStr;
! 141 let built_in_evm_account =
! 142 H160::from_str("888a0bF014d1D6334002261BaC34a16f5Ce963ac").unwrap();
! 143 let mut evm_accounts = BTreeMap::new();
! 144 //let ten: u128 = 10;
! 145 evm_accounts.insert(
! 146 built_in_evm_account,
! 147 evm::GenesisAccount {
! 148 nonce: 0.into(),
! 149 balance: (2018030320150719 as u128 * 10_u128.pow(18)).into(), // 设置账户初始ETH余额,注意ETH有18位小数
! 150 storage: BTreeMap::new(),
! 151 code: WASM_BINARY.to_vec(),
! 152 },
! 153 );
2.1.3 编译
$ cargo build --release
2.1.4 运行
(master)$ ./target/release/frontier-template-node purge-chain --dev Are you sure to remove "/root/.local/share/frontier-template-node/chains/dev/db"? [y/N]: y "/root/.local/share/frontier-template-node/chains/dev/db" did not exist. (master)$ ./target/release/frontier-template-node --dev --ws-external --rpc-external --rpc-cors=all 2020-07-18 22:59:28 Substrate Node 2020-07-18 22:59:28 :v: version 2.0.0-dev-fcea972-x86_64-linux-gnu 2020-07-18 22:59:28 :heart: by Parity Technologies <admin@parity.io>, 2017-2020 2020-07-18 22:59:28 :clipboard: Chain specification: Development 2020-07-18 22:59:28 Node name: stimulating-hand-5352 2020-07-18 22:59:28 :bust_in_silhouette: Role: AUTHORITY 2020-07-18 22:59:28 :floppy_disk: Database: RocksDb at /root/.local/share/frontier-template-node/chains/dev/db 2020-07-18 22:59:28 ⛓ Native runtime: node-frontier-template-1 (node-frontier-template-1.tx1.au1) 2020-07-18 22:59:28 :hammer: Initializing Genesis block/state (state: 0x1e15…857d, header-hash: 0xa26e…22df) 2020-07-18 22:59:28 :older_man: Loading GRANDPA authority set from genesis on what appears to be first startup. 2020-07-18 22:59:29 ⏱ Loaded block-time = 6000 milliseconds from genesis on first-launch 2020-07-18 22:59:29 :package: Highest known block at #0 2020-07-18 22:59:29 Using default protocol ID "sup" because none is configured in the chain specs 2020-07-18 22:59:29 Local node identity is: 12D3KooWMgcWheT7cvvpaaKmqNLVPRpAkGLR9V1M7pfs43DxZU6c (legacy representation: QmRN3jaSxZV5dMk1P5tm1CB9oud8pU8SGZvrUFfuS9dNMQ) 2020-07-18 22:59:29 :part_alternation_mark: Prometheus server started at 127.0.0.1:9615 2020-07-18 22:59:30 :raised_hands: Starting consensus session on top of parent 0xa26e0b7f2abed8a4bcf87ca45d20d7628cf22225dd522385e7b49ee6c30522df 2020-07-18 22:59:30 :gift: Prepared block for proposing at 1 [hash: 0xd3558be2df939b94ba94d82ded332216b0b06a55596d0a61a858aeaf7acc16c4; parent_hash: 0xa26e…22df; extrinsics (1): [0xead1…ac1b]] 2020-07-18 22:59:30 :bookmark: Pre-sealed block for proposal at 1. Hash now 0x8aa38d1228213371249ff83404bd8576e3e0bce6e120f93a9fe08ec2dfdc2d86, previously 0xd3558be2df939b94ba94d82ded332216b0b06a55596d0a61a858aeaf7acc16c4. 2020-07-18 22:59:30 :sparkles: Imported #1 (0x8aa3…2d86) 2020-07-18 22:59:34 :zzz: Idle (0 peers), best: #1 (0x8aa3…2d86), finalized #0 (0xa26e…22df), ⬇ 0 ⬆ 0 2020-07-18 22:59:36 :raised_hands: Starting consensus session on top of parent 0x8aa38d1228213371249ff83404bd8576e3e0bce6e120f93a9fe08ec2dfdc2d86 2020-07-18 22:59:36 :gift: Prepared block for proposing at 2 [hash: 0x570be59726cf2018fab9e7bae77208a990921291fe8164bbc1df253f8142d3f6; parent_hash: 0x8aa3…2d86; extrinsics (1): [0xec7f…0d17]] 2020-07-18 22:59:36 :bookmark: Pre-sealed block for proposal at 2. Hash now 0xe9831c239f29745d79b0e72cc26c0db19ce4d0c7712a32dae4cc60f8ad538551, previously 0x570be59726cf2018fab9e7bae77208a990921291fe8164bbc1df253f8142d3f6. 2020-07-18 22:59:36 :sparkles: Imported #2 (0xe983…8551)
2.2 MetaMask接入frontier链
2.2.1 配置自定义RPC接入
这里 ChainID 填42,源于 runtime 中的定义:
parameter_types! {
pub const EVMModuleId: ModuleId = ModuleId(*b"py/evmpa");
pub const ChainId: u64 = 42;
}
impl evm::Trait for Runtime {
type ModuleId = EVMModuleId;
type FeeCalculator = FixedGasPrice;
type ConvertAccountId = HashTruncateConvertAccountId<BlakeTwo256>;
type Currency = Balances;
type Event = Event;
type Precompiles = ();
type ChainId = ChainId;
}
2.2.2 查看账户余额
2.2.3 开启Gas控制高级选项
开启该选项后,以便我们随意配置 gas limit 和 gas price :
2.3 remix接入MetaMask
- 使用
Injected Web3建立与MetaMask账号的连接
我们看到我们创建的账号已经成功接入 remix ,接下来,我们便可以进行 ERC20 合约的开发和部署了。
3 合约开发&部署
3.1 智能合约编写
我们要编写两个合约,其中 SafeMath.sol 是一个算数运算防溢出的安全库, xyc.sol 是一个 ERC20 智能合约。
3.1.1 SafeMath.sol
pragma solidity ^0.6.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
3.1.2 xyc.sol
pragma solidity ^0.6.0;
import "./SafeMath.sol";
abstract contract ERC20 {
function totalSupply() public virtual view returns (uint256);
function balanceOf(address owner) public virtual view returns (uint256);
function transfer(address to, uint256 value) public virtual returns (bool);
function transferFrom(address from, address to, uint256 value) public virtual returns (bool);
function approve(address spender, uint256 value) public virtual returns (bool);
function allowance(address owner, address spender) public virtual view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract xyc is ERC20 {
using SafeMath for uint256;
string name = "XuanYuan Coin";
string symbol = "xyc";
uint8 decimals;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
constructor(uint256 myTotalSupply, uint8 mydecimals) public {
_totalSupply = myTotalSupply;
decimals = mydecimals;
_balances[msg.sender] = myTotalSupply;
}
function totalSupply() public override view returns (uint256) {
return _totalSupply;
}
function balanceOf(address _owner) public override view returns (uint256) {
return _balances[_owner];
}
function transfer(address to, uint256 value) public override returns (bool) {
require(to != address(0), "ERC20 ERROR: transfer from zero address");
require(value <= _balances[msg.sender]);
require(_balances[to] + value >= _balances[to]);
_balances[msg.sender] = _balances[msg.sender].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public override returns (bool) {
require(to != address(0));
require(value <= _balances[from]);
require(value <= _allowances[from][msg.sender]);
require(_balances[to] + value >= _balances[to]);
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
_allowances[from][msg.sender] = _allowances[from][msg.sender].sub(value);
emit Transfer(from, to, value);
return true;
}
function approve(address spender, uint256 value) public override returns (bool) {
require(value <= _balances[msg.sender]);
_allowances[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function allowance(address _owner, address spender) public override view returns (uint256) {
return _allowances[_owner][spender];
}
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
3.2 智能合约编译
智能合约编译成功,接下来我们就将已经开发完成的 Solidity ERC20 智能合约部署到 Substrate 链上。
3.3 智能合约部署
- 选择要部署的合约,填写构造方法中的发行总量和精度两个参数,点击
transact按钮
注:当前版本需要在弹出的MetaMask对话框上,设置很大的gas limit值( 4294967295 )才能部署成功:(
4 合约功能测试
通过 remix 完成对 ERC20 合约的功能测试。
4.1 查看发行量&余额
可见所有发行代币都归属于合约部署方地址。
4.2 转账测试
- 给账户2:
0x8A832874304Be72b299C0ED96f22B11311c9051C,转账1000.00 XYC,查看账户1地址XYC代币数量为:9000.00,符合预期
- 在MetaMask上添加自定义代币
合约地址: 0xfE2dF1EE3783eBD46481e32d849acB49ec5004Bf
- 代币发行方地址所剩代币为:9000.00 XYC
- 代币接收方地址代币数量为:1000.00 XYC
4.3 授权代理转账测试
- 为账户2转账ETH,用于支付gas费
- 用户1授权用户2可代理转账的代币数量为2000.00 XYC
- 查看用户2所能授权转账用户1的XYC代币
| 账号 | 地址 |
|---|---|
| Jasonruan | 0x888a0bF014d1D6334002261BaC34a16f5Ce963ac |
| Account 2 | 0x8A832874304Be72b299C0ED96f22B11311c9051C |
| Account 3 | 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b |
- 用户2代理用户1给用户3转账2000.00 XYC
成功转账后,查看到用户2所能授权转账用户1的XYC代币数量为0
- 查看到账户3上XYC代币余额
- 再次查看账户1上的代币余额
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 40分钟前
- 阅读 ( 13 )
- 学分 ( 0 )
- 分类:Polkadot
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Fluent Python
Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99
Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!