第十一篇 墨客区块链(MOAC BlockChain) 详解带分发锁仓功能的ERC20合约

本文详细分析一个自带分发锁仓功能的ERC20智能合约。

ERC20智能合约的行为非常类似于传统的加密货币,例如在不同账户之间发送和接收、 查看通证总供应量或者查看某个地址可用的通证余额等。

1.合约分析

1.1 基础参数

string public constant name = "DAB Token";
string public constant symbol = "DABT";
uint public constant decimals = 18;
uint256 _totalSupply = 60000000 * 10**decimals;

标准erc20合约包含四个基础参数:

name:token名称;

symbol:token符号,通常采用三到四个字母的缩写;

decimals:小数位数, 最常见的小数位数是18;

totalSupply:token总量。

1.2 balance()

function balanceOf(address _owner) constant returns (uint256 balance) {
    return balances[_owner];
}

该函数接受一个地址作为参数,允许智能合约返回该地址的token余额。所以任何地址的token余额都是公开的。

1.3 approve()

//创建映射表记录通证持有者、被授权者以及授权数量
mapping(address => mapping (address => uint256)) allowed;

function approve(address _spender, uint256 _value) returns (bool success) {
    allowed[msg.sender][_spender] = _value;
    //当授权时触发Approval事件
    Approval(msg.sender, _spender, _value);
    return true;
}

此函数的调用方授权给定的地址可以从其地址中提款。

在这里,以及后面的代码片段中,你可能会看到一个变量msg 。 这是由外部应用程序提供的隐含字段,以便更好地与合约进行交互。EVM允许使用该字段来存储和处理由外部应用程序提供的数据。

在这个例子中,msg.sender是合约方法调用方的地址。

1.4 transfer()

mapping(address => uint256) balances;         //list of balance of each address 

//ERC 20 Standard Token interface transfer function
//Prevent transfers until freeze period is over.
//返回值为true时,表示转账成功
function transfer(address _to, uint256 _value) returns (bool success) {
    //如果还没开始,则返回
    if (now < baseStartTime) revert();

    //Default assumes totalSupply can't be over max (2^256 - 1).
    //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
    //如果发送方有足够的资金并且发送数量非0 ,则发送给指定地址
    if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
        //计算账号的已解锁资金
        uint _freeAmount = freeAmount(msg.sender);
        if (_freeAmount < _value) {
            return false;
        } 

        balances[msg.sender] -= _value;
        balances[_to] += _value;
        //触发Transfer事件
        Transfer(msg.sender, _to, _value);
        return true;
    } else {
        return false;
    }
}

该函数让调用方将指定数量的token发送到另一个地址。

1.5 transferFrom()

mapping(address => uint256) balances;                     
mapping(address => mapping (address => uint256)) allowed;

//ERC 20 Standard Token interface transfer function
//Prevent transfers until freeze period is over.         
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
    if (msg.sender != founder) revert();

    //same as above. Replace this line with the following if you want to protect against wrapping uints.
    if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
        uint _freeAmount = freeAmount(_from);
        if (_freeAmount < _value) {
            return false;
        } 

        balances[_to] += _value;
        balances[_from] -= _value;
        allowed[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    } else { return false; }
}

该函数允许智能合约自动执行转账流程并代表所有者发送指定数量的token。

为什么有了transfer(),还需要transferFrom()呢?

以日常生活通过转账来支付账单的例子说明他们的区别。

一种情况是,你自己去办转账汇款来支付账单,这就像使用transfer() :你需要自己执行,没有其他人的帮助。

在另一种情况下,你可以与银行签订自动代支付协议。这就像使用transferFrom() :银行的机器会自动以你的名义进行转账支付。有了这个函数,合约就可以代表你自动发送token到另一个地址,而无需你的干预。

1.6 distribute()

mapping(address => uint256) balances;         //list of balance of each address
mapping(address => uint256) distBalances;     //list of distributed balance of each address to calculate restricted amount

//Distribute tokens out.
function distribute(uint256 _amount, address _to) {
    if (msg.sender!=founder) revert();
    if (distributed + _amount > _totalSupply) revert();

    distributed += _amount;
    balances[_to] += _amount;
    distBalances[_to] += _amount;
}

该函数允许合约管理者分发token。

刚开始的时候,所有token在合约里,而不在管理者地址,管理者通过distribute分发token。

1.7 setStartTime

function setStartTime(uint _startTime) {
    if (msg.sender!=founder) revert();
    baseStartTime = _startTime;
}

设置开始时间,可以根据需要修改时间。用于计算解锁token的数量。

1.8 freeAmount()

mapping(address => uint256) balances;         //list of balance of each address
mapping(address => uint256) distBalances;     //list of distributed balance of each address to calculate restricted amount

function freeAmount(address user) returns (uint256 amount) {
    //0) no restriction for founder
    if (user == founder) {
        return balances[user];
    }

    //1) no free amount before base start time;
    if (now < baseStartTime) {
        return 0;
    }

    //2) calculate number of months passed since base start time;
    uint monthDiff = (now - baseStartTime) / (30 days);

    //3) if it is over 15 months, free up everything.
    if (monthDiff > 15) {
        return balances[user];
    }

    //4) calculate amount of unrestricted within distributed amount.
    uint unrestricted = distBalances[user] / 10 + distBalances[user] * 6 / 100 * monthDiff;
    if (unrestricted > distBalances[user]) {
        unrestricted = distBalances[user];
    }

    //5) calculate total free amount including those not from distribution 
    if (unrestricted + balances[user] < distBalances[user]) {
        amount = 0;
    } else {
        amount = unrestricted + (balances[user] - distBalances[user]);
    }

    return amount;
}

计算解锁token数量,规则为:

0)如果是管理地址,全部释放;

1)如果还没有开始,返回0;

2)计算已经开始多少个月了,此实例中的token按照月份解锁,用户可以根据需要改成其他时间长度;

3)如果时间超过15个月,全部释放;

4)计算实际解锁token数量,本实例中第一个月释放10%,之后每个月释放6%;

5)计算所有可以自由流通的token数量(包含解锁账户中释放的token和通过交易得到的token)。

1.9 changeFounder()

//Change founder address (where ICO is being forwarded).
function changeFounder(address newFounder) {
    if (msg.sender!=founder) revert();
    founder = newFounder;
}

转移合约管理权限。

2.部署使用

2.1 部署合约

参考《第四篇 在墨客区块链(MOAC BlockChain) 部署ERC-20合约》进行合约部署。

合约部署完成后主账号并不显示拥有token。

2.2 设置开始时间

如果不想计算,可以直接通过命令:

> mc.getBlock(616325)

得到最新区块的信息,里面包含了时间戳,是从1970-1-1开始计时的,单位“秒”。如果设置到未来的时间,请自行计算。

比如我把开始时间设置到当前这个块。

该操作需要founder权限,完成后BaseStartTime就变成刚才的设置值了。

2.3 分发token

该合约的所有token都是通过Distribute分发的。如果分发给founder,所有token可以自由交易;如果分发给其他地址,则按照规则计算锁仓情况。

只有释放过的token,才能通过transfer进行交易。

注意:该步骤中的数量(amount)是带18位小数位的。

其他功能与普通ERC20合约相同,比如transfer、TransferFrom、approve、balanceOf等。本文不详细解释。

3.合约的调用

var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545'));

var contractAddress = "0x792a0762Fd251eEB722C4836Ca5a1cF005958249";

var abiString = '[ { "constant": true, ...... "type": "event" } ]';

//调用erc20合约
//查询余额
var address = "0x794E32311857411d05910bf04019Ba4c4fe2703C";
callContract1(chain3, contractAddress, address, abiString);
console.log("address:  ",address);

function callContract1(chain3, contractAddress, address, abiString){
    var abi = JSON.parse(abiString);
    var contract = chain3.mc.contract(abi);
    var token = contract.at(contractAddress);
    token.balanceOf.call(address, function(err, result){
        console.log(err, JSON.stringify(result));
    });
}

//查询free数量
callContract2(chain3, contractAddress, address, abiString);
function callContract2(chain3, contractAddress, address, abiString){
    var abi = JSON.parse(abiString);
    var contract = chain3.mc.contract(abi);
    var token = contract.at(contractAddress);
    token.freeAmount.call(address, function(err, result){
        console.log(err, JSON.stringify(result));
    });
}

balance - free就是被锁住的token数量。

附件 合约源码

    //ERC 20 token
    pragma solidity ^0.4.11;
    contract DABToken  {
        string public constant name = "DAB Token";
        string public constant symbol = "DABT";
        uint public constant decimals = 18;
        uint256 _totalSupply = 60000000 * 10**decimals;

        function totalSupply() constant returns (uint256 supply) {
            return _totalSupply;
        }

        function balanceOf(address _owner) constant returns (uint256 balance) {
            return balances[_owner];
        }

        function approve(address _spender, uint256 _value) returns (bool success) {
            allowed[msg.sender][_spender] = _value;
            Approval(msg.sender, _spender, _value);
            return true;
        }

        function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
          return allowed[_owner][_spender];
        }

        mapping(address => uint256) balances;         //list of balance of each address
        mapping(address => uint256) distBalances;     //list of distributed balance of each address to calculate restricted amount
        mapping(address => mapping (address => uint256)) allowed;

        uint public baseStartTime; //All other time spots are calculated based on this time spot.

        // Initial founder address (set in constructor)
        // All deposited will be instantly forwarded to this address.

        address public founder;
        uint256 public distributed = 0;

        event AllocateFounderTokens(address indexed sender);
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);

        //constructor
        function DABToken() {
            founder = msg.sender;
        }

        function setStartTime(uint _startTime) {
            if (msg.sender!=founder) revert();
            baseStartTime = _startTime;
        }

        //Distribute tokens out.
        function distribute(uint256 _amount, address _to) {
            if (msg.sender!=founder) revert();
            if (distributed + _amount > _totalSupply) revert();

            distributed += _amount;
            balances[_to] += _amount;
            distBalances[_to] += _amount;
        }

        //ERC 20 Standard Token interface transfer function
        //Prevent transfers until freeze period is over.
        function transfer(address _to, uint256 _value) returns (bool success) {
            if (now < baseStartTime) revert();

            //Default assumes totalSupply can't be over max (2^256 - 1).
            //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
            if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
                uint _freeAmount = freeAmount(msg.sender);
                if (_freeAmount < _value) {
                    return false;
                } 

                balances[msg.sender] -= _value;
                balances[_to] += _value;
                Transfer(msg.sender, _to, _value);
                return true;
            } else {
                return false;
            }
        }

        function freeAmount(address user) returns (uint256 amount) {
            //0) no restriction for founder
            if (user == founder) {
                return balances[user];
            }

            //1) no free amount before base start time;
            if (now < baseStartTime) {
                return 0;
            }

            //2) calculate number of months passed since base start time;
            uint monthDiff = (now - baseStartTime) / (30 days);

            //3) if it is over 15 months, free up everything.
            if (monthDiff > 15) {
                return balances[user];
            }

            //4) calculate amount of unrestricted within distributed amount.
            uint unrestricted = distBalances[user] / 10 + distBalances[user] * 6 / 100 * monthDiff;
            if (unrestricted > distBalances[user]) {
                unrestricted = distBalances[user];
            }

            //5) calculate total free amount including those not from distribution 
            if (unrestricted + balances[user] < distBalances[user]) {
                amount = 0;
            } else {
                amount = unrestricted + (balances[user] - distBalances[user]);
            }

            return amount;
        }

        //Change founder address (where ICO is being forwarded).
        function changeFounder(address newFounder) {
            if (msg.sender!=founder) revert();
            founder = newFounder;
        }

        //ERC 20 Standard Token interface transfer function
        //Prevent transfers until freeze period is over.         
        function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
            if (msg.sender != founder) revert();

            //same as above. Replace this line with the following if you want to protect against wrapping uints.
            if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
                uint _freeAmount = freeAmount(_from);
                if (_freeAmount < _value) {
                    return false;
                } 

                balances[_to] += _value;
                balances[_from] -= _value;
                allowed[_from][msg.sender] -= _value;
                Transfer(_from, _to, _value);
                return true;
            } else { return false; }
        }

        function() payable {
            if (!founder.call.value(msg.value)()) revert(); 
        }
    }

猜你喜欢

转载自blog.csdn.net/lyq13573221675/article/details/81359013
今日推荐