Solidity应用:ERC20

ERC20是一套基于以太坊网络的标准代币发行协议。有了ERC20,开发者们得以高效、可靠、低成本地创造专属自己项目的代币,是以太坊网络第一个真正意义上的应用。ERC20实现了代币转账的基本逻辑:账户余额、转账、授权转账、代币总供给、代币信息(可选):名称,代号,小数位数。
合约只要继承并实现IERC20的所有接口就代表合约满足了ERC20协议的标准。

IERC20接口

IERC20定义了2个事件:Transfer事件和Approval事件,分别在转账和授权时被触发;定义了6个函数,提供了转移代币的基本功能,并允许代币获得批准,以便其他链上第三方使用。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    // 转账时触发
    event Transfer(address indexed from, address indexed to, uint256 value);
    // 授权时触发
    event Approval(address indexed owner, address indexed spender, uint256 value);
    // 代币总供应量
    function totalSupply() external view returns (uint256);
    // 返回账户余额
    function balanceOf(address account) external view returns (uint256);
    // 转账
    function transfer(address to, uint256 amount) external returns (bool);
    // 返回授权额度
    function allowance(address owner, address spender) external view returns (uint256);
    // 授权
    function approve(address spender, uint256 amount) external returns (bool);
    // 授权转账
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

代币合约

基于IERC20发行一个代币,代币名称My Token,简称MT,铸币函数(mint)和销毁函数(burn)是IERC20中不存在的

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ERC20代币合约
contract MyToken is IERC20 {
    address _owner;
    mapping(address => uint) public balances;
    mapping(address => mapping(address => uint)) public allowance;

    uint public totalSupply;

    string public name;
    string public symbol;
    uint8 public decimals = 18;

    modifier onlyOwner() {
        require(_owner == msg.sender, "not owner");
        _;
    }

    constructor(string memory _name, string memory _symbol) {
        _owner = msg.sender;
        name = _name;
        symbol = _symbol;

        // 启动最初的代币,给合约部署者发放代币
        balances[msg.sender] += 10000;
        totalSupply += 10000;
    }

    function balanceOf(address account) external view returns(uint) {
        return balances[account];
    }

    function _transfer(address from, address to, uint amount) internal {
        require(to != address(0), "transfer to 0x0");
        require(balances[from] >= amount, "balance not enough!");
        balances[from] -= amount;
        balances[to] += amount;
        emit Transfer(from, to, amount);
    }

    function transfer(address recipient, uint256 amount) external returns (bool) {
        _transfer(msg.sender, recipient, amount);

        return true;
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        allowance[owner][spender] = amount;
        emit Approval(msg.sender, spender, amount);
    }

    function approve(address spender, uint256 amount) external returns (bool){
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool){
        require(allowance[sender][msg.sender] >= amount, "allowance not enough!");
        _approve(sender, msg.sender, allowance[sender][msg.sender] - amount);
        
        _transfer(sender, recipient, amount);
        return true;
    }

    // 铸造代币:从address(0)转到指定地址
    // 只有合约拥有者或特殊权限的人才能调用
    // 由于是转账,也要触发Transfer事件
    function mint(address account,uint amount) external onlyOwner {
        require(account != address(0), "mint to 0x0");
        balances[account] += amount;
        totalSupply += amount;
        emit Transfer(address(0), msg.sender, amount);
    }

    // 销毁代币:将指定地址的币转到address(0)
    // 只有合约拥有者或特殊权限的人才能调用
    // 由于是转账,也要触发Transfer事件
    function burn(address account, uint amount) external onlyOwner {
        require(account != address(0), "burn from 0x0");
        require(balances[account] >= amount, "no more token to burn");
        balances[msg.sender] -= amount;
        totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
}

在这里插入图片描述

水龙头合约

很多项目早期的时候为了扩大影响力,让别人可以免费领取代币,下面实现一个水龙头合约,限制每个钱包地址只能领取一次,一次100个代币:
整体思路:
1、代币合约向水龙头合约发送一定数量代币
2、水龙头合约调用代币合约的transfer方法转账

contract Faucet {
    // 一次领取的数量
    uint private constant AMMOUNTONCE = 100;
    // 领取记录的映射
    mapping(address => bool) public recordAddr;
    // 代币的合约地址
    address public tokenAddr;
    // 构造函数设置代币合约地址
    constructor(address _tokenAddr) {
        tokenAddr = _tokenAddr;
    }
    
    event SendToken(address indexed receiver, uint indexed amount);
    
    // 领取代币
    function receive() external {
        require(!recordAddr[msg.sender], "already received token!");
        IERC20 token = IERC20(tokenAddr);
        require(token.balanceOf(address(this)) >= AMMOUNTONCE, "token not enough!");
        token.transfer(msg.sender, AMMOUNTONCE);
        recordAddr[msg.sender] = true;
        emit SendToken(msg.sender, AMMOUNTONCE);
    }
}

部署水龙头合约,并传入MT代币的合约地址:
在这里插入图片描述
向水龙头合约发送1000个代币:
在这里插入图片描述
remix地址切换为0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,并执行水龙头合约receiveToken函数,查询余额已经有100个代币了:
在这里插入图片描述

空投合约

所谓空投,就是作为项目方,在项目的首发阶段为了做一些运营活动活跃用户,开发新用户,在初期给用户空投项目方的代币,或者ETH的行为。为了拿到空投资格,用户通常需要完成一些简单的任务,因为每次接收空投的用户很多,项目方不可能一笔一笔的转账。利用智能合约批量发放ERC20代币,可以显著提高空投效率。

// 空投合约
contract Airdrop {
	// 接收ETH
    receive() external payable {}
    // 获取余额
    function getBalance() external view returns(uint) {
        return address(this).balance;
    }
    // 计算总量
    function getSum(uint[] calldata _amounts) public pure returns(uint _sum) {
        for(uint i=0; i<_amounts.length; i++) {
            _sum += _amounts[i];
        }
    }
    // 空投ETH
    function batchTransferETH(address[] calldata _addresses, uint[] calldata _amounts) public payable returns(bool) {
        require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
        uint sumAmount = getSum(_amounts);
        require(address(this).balance >= sumAmount, "amount not enough!");
        for(uint i=0; i<_addresses.length; i++) {
            payable(_addresses[i]).transfer(_amounts[i]);
        }
        return true;
    }

    // 空投token
    // 向空投合约授权一定数量代币,用transferFrom函数发送空投
    function batchTransferToken(address _tokenAddr, address[] calldata _addresses, uint[] calldata _amounts) public returns(bool) {
        require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
        uint sumAmount = getSum(_amounts);
        IERC20 token = IERC20(_tokenAddr);
        require(token.allowance(msg.sender, address(this)) >= sumAmount, "token not enough!");
        for(uint i = 0; i < _addresses.length; i++) {
            token.transferFrom(msg.sender, _addresses[i], _amounts[i]);
        }
        return true;
    }
}

测试空投EHT:
1、向合约转入3个ETH:
在这里插入图片描述
2、查看余额:
在这里插入图片描述
3、执行batchTransferETH函数,传入参数:[“0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2”, “0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db”]、[1000000000000000000,2000000000000000000],再次查看余额已经为0:
在这里插入图片描述
空投的两个地址也接收到了相应数量的ETH:
在这里插入图片描述
测试空投代币:
1、部署上面的ERC20合约,先mint 10000 MT代币:
在这里插入图片描述
2、利用ERC20代币合约中的approve()函数,给Airdrop空投合约授权5000 个MT代币:
在这里插入图片描述

3、执行batchTransferToken函数,传入参数:[“0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2”, “0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db”]、[1000,2000],再次查看余额已经为0:
在这里插入图片描述
在ERC20合约中查询余额:
在这里插入图片描述
在这里插入图片描述
代币空投成功!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值