实际合约开发中我们更多的是使用openzeppelin写好的示例代码,这里自己重新复习感受一下erc20币种的标准.
首先定义IERC20.sol接口文件示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
给出权限类合约Ownerable.sol示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Ownerable {
//该合约链上记录该地址的合约对象
address public owner ;
constructor(){
owner = msg.sender ;
}
//使用modifile判断权限是不是某个人
modifier ownerMySelf{
require(owner==msg.sender,'is not contract owner address not permission');
_;
}
//
function setOwner(address _account) external ownerMySelf{
require(_account!=address(0),'the address is zero address ');
owner = _account ;
}
}
币种的实现子类比如MyToken.sol文件 示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IERC20.sol";
import "./Ownerable.sol";
//erc20是IERC20的实现类 是一个抽象类合约
contract MyToken is Ownerable, IERC20 {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor() {
uint decimal = decimals();
uint totalMoney= 2024 * 10**decimal; //这个是币种总额度
_mint(totalMoney);
}
function _mint(uint amount) public ownerMySelf {
_balances[msg.sender] += amount;
_totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
function decimals() public pure returns(uint){
return 18;
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
//查询当前用户有多少余额
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount)
external
returns (bool)
{
_balances[msg.sender] -= amount;
_balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
//这个函数是张三要调用的 张三授权李四或者合约 比如 20块钱可以花
function approve(address spender, uint256 amount) public returns (bool) {
_allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
//这个是被授权方调用 比如李四调用或者合约调用
function transferFrom(
address sender,
address recipient,
uint256 amount
) public returns (bool) {
require(allowance(sender,msg.sender)>=amount,'erc20 not allowance');
_allowances[sender][msg.sender] -= amount;
_balances[sender] -= amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function burn(uint256 amount) public {
_balances[msg.sender] -= amount;
_totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
接下来写一个 在另外一个合约中,也就是代理合约中发起转账 ,示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IERC20.sol";
import "./Ownerable.sol";
import './MyToken.sol';
contract ProxyErc20{
MyToken public myToken ;
constructor(address _addr) {
myToken = MyToken(_addr) ;
}
//转账erc20币种
function sendErc20ToUser(address _to,uint _amount) public payable {
IERC20(myToken).transferFrom(msg.sender,_to,_amount);
}
function getErc20Address() public view returns(address){
return address(myToken);
}
}
这是用户给另外一个合约发送erc20代币的情况
1.调用逻辑 用户a 首先检查allowance授权ProxyErc20合约的额度是否已经授权足够
2.如果不够 用户a 调用approve方法授权ProxyErc20合约发起授权
3.用户a 调用ProxyErc20合约的sendErc20发送代币到指定账号