第十二篇 墨客区块链(MOAC BlockChain) ERC20合约的扩展功能

在《第四篇 在墨客区块链(MOAC BlockChain) 部署ERC-20合约》中,附件为标准erc20合约,在实际使用部署中,有很多项目需要用到扩展功能。本文在该erc20标准的基础上扩展合约功能。

1. 定义合约部署账号

从安全角度出发,本文的扩展功能大多需要合约部署账号权限。

本文将其定义为minter,并在合约部署时得到minter。

contract TokenDemo is Token {
    address minter;                   //定义合约部署账号		
    
    function TokenDemo(uint256 initialAmount, string tokenName, uint8 decimalUnits, string tokenSymbol) public {		
        minter = msg.sender;          //在合约部署时得到
    }

    //只能通过智能合约的部署账号才能调用的方法
    modifier onlyOwner() {
        require(msg.sender == minter);
        _;
    }
}

使用者需要将这两行代码分别增加到contract TokenDemo和function TokenDemo。

2.合约接收mc

默认情况下,合约是不能接收mc的。

function() payable public {}

加上这个函数后,合约地址就可以接收mc了。

payable关键词是编译器支持的,合约里的函数是否带有payable关键词,编译器会生成不一样的代码。如果对应的函数不支持转账,对应的函数的代码会通过revert指令撤销前面的转账操作。

如果函数没有任何参数,只能在合约销毁时(详见第8节),合约里的mc才会自动回到minter账号。

3.修改token基础参数

//修改name
function setName (string _value) public returns (bool) {
    require(msg.sender == minter);
    name = _value;
    return true;
}

//修改symbol
function setSymbol (string _value) public returns (bool) {
    require(msg.sender == minter);
    symbol = _value;
    return true;
}

4. 增加token总量

在标准erc20合约中,token总量在部署合约的时候确定,且不能做任何更改。

如果需要增加token总量,比较简单的方法是写一个增量的函数。

function addTokenTotal(uint256 _addAmount) public returns (bool success){
    require(msg.sender == minter);                        //只有合约部署账号得到授权
    require(_addAmount > 0);                              //增量必须大于0
		
    totalSupply += _addAmount * 10 ** decimals;           //增发后总量增加
    balances[msg.sender] += _addAmount * 10 ** decimals;  //增发量自动归到合约部署账号 
    return true;
}

直接添加该函数到合约即可。

5. 减少token总量

有增就有减,erc20 token也可以通过烧毁(burn)实现总量的减少。

contract Token{
    //事件,用来通知token被消耗(这里就不是转移, 是token用了就没了)
    event Burn(address indexed fromAddr, uint256 value);
}

contract TokenDemo is Token {
    //正如其名, 这个是烧币的.. ,用于把自己的 token 烧掉
    function burn(uint256 _value) public returns (bool success) {
        require(msg.sender == minter);                  //合约创建者才有该功能
        require(balances[msg.sender] >= _value);        //必须要有这么多
        balances[msg.sender] -= _value;
        totalSupply -= _value;
        Burn(msg.sender, _value);
        return true;
    }
	
    //这个是用户被授权后销毁token.....
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(msg.sender == minter);                  //合约创建者才有该功能
        require(balances[_from] >= _value);             //一样要有这么多
        require(_value <= allowed[_from][msg.sender]);  //授权后才能操作 
        balances[_from] -= _value;
        allowed[_from][msg.sender] -= _value;
        totalSupply -= _value;
        Burn(_from, _value);
        return true;
    }
}

代码写了说明,就不用解释了。直接添加事件和函数到合约即可。

6.修改合约管理地址

有些情况下,需要转移合约管理地址的权限。

function changeAdmin(address _newAdmin) public returns (bool) {
    require(msg.sender == minter);
    require(_newAdmin != address(0));
    minter = _newAdmin;
    return true;
    
    //如果需要转移资产,下面两行enable
    //balances[_newAdmin] = balances[_newAdmin].add(balances[minter]);
    //balances[admin] = 0;
}

7. 锁定某个地址的token

此功能实现指定地址所有该合约token的锁定。既可以锁定,也可以解锁。

contract TokenDemo is Token {	

    mapping(address => bool) freezed;
	
    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(!freezed[msg.sender]);       //交易需要判断地址是否被锁定    
        require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        require(_to != 0x0);
		
        balances[msg.sender] -= _value;      //从消息发送者账户中减去token数量_value
        balances[_to] += _value;             //往接收账户增加token数量_value
        Transfer(msg.sender, _to, _value);   //触发转币交易事件		
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(!freezed[_from]);            //交易需要判断地址是否被锁定
        require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);

        balances[_to] += _value;             //接收账户增加token数量_value
        balances[_from] -= _value;           //支出账户_from减去token数量_value
        allowed[_from][msg.sender] -= _value;//消息发送者可以从账户_from中转出的数量减少_value
        Transfer(_from, _to, _value);        //触发转币交易事件
        return true;
    }    
	
    //冻结账户
    function freezenAccount(address _freezen) public returns (bool success) {
        require(msg.sender == minter);       //合约创建者才能冻结账户
        require(_freezen != minter);         //不能冻结合约创建者账户
	
        freezed[_freezen] = true;
        return true;
    }
	
    //解冻被冻结的账户
    function unFreezenAccount(address _freezen) public returns (bool success) {
        require(msg.sender == minter);       //合约创建者才能解冻账户
		
        freezed[_freezen] = false;
        return true;
    }

    //查询账户冻结状态
    function getFrozenAccount(address _target) public view returns (bool) {
        require(_target != address(0));
        return freezed[_target];
    }
}

8. 销毁合约

function kill() public {
    require(msg.sender == minter);
    selfdestruct(minter);
}

在其他一些比较老的教程里面,你可能会看到suicide()方法,但是为了语言更好的可读性,这个方法目前已经重新命名,以后如有需要,大家直接调用selfdestruct()方法就好。

如果合约写了payable(见第2节),且接收了mc,执行销毁合约后,合约的mc会自动转到minter账号。

合约销毁之后转给该合约地址的mc,就不能再转移出来了。

9.判断地址是合约地址还是普通账号地址

既然给合约地址发送mc,依赖于合约是否有接收mc功能,说明普通的发送mc操作,可能成功,也可能失败。

那怎么判断一个地址是合约地址还是普通账号地址呢?

其实是根据该地址是否包含代码来判断的。以下为accountOrContractAddr.js,填入地址,直接node运行,可以得到结果。

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

//getCode()方法返回指定地址上代码的16进制字符串,由于普通账户地址处没有代码,因此将仅返回16进制前缀0x。
var code = chain3.mc.getCode("0xbe1b5b49fc5cf4300d0ee8699089XXXXXXXXXXXX");

if(code == '0x') 
    console.log('普通账户');
else 
    console.log('合约账户');

猜你喜欢

转载自blog.csdn.net/lyq13573221675/article/details/81448709