http://www.7klian.com

给你的ERC777代币建造一个本身的专属账本

        );
                .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
const { ether, makeInterfaceId, singletons, expectEvent } = require(‘@openzeppelin/test-helpers’);

假如你持有一个ERC777[1]代币,那么你就可以操作ERC777代币中的钩子函数要领,给本身布署一个账本合约来记录本身的账户每一次吸收到的代币数量和对方账户等信息
import “@openzeppelin/contracts/ownership/Ownable.sol”;
    it(‘验证吸收接口: TokensRecipient()’, async function () {
const initialSupply = ‘1000000000’;
            receiver,
    address addr = _addr == address(0) ? msg.sender : _addr;
        IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
});
        await TokensRecipientInstance.rejectTokens({ from: receiver });
        operator[_from] = _operator;
        allowTokensReceived = true;
function _callTokensToSend(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData) private {
以上我们利用了一些Openzeppelin的尺度库,canImplementInterfaceForAddress要领在ERC1820Implementer.sol合约文件中,通过第40行_registerInterfaceForAddress要领向canImplementInterfaceForAddress要领注册了同意成为发送账户msg.sender的TOKENS_RECIPIENT_INTERFACE_HASH接口. 在tokensReceived要领中我们将传入的生意业务数据一一记录在合约的变量中,譬喻通过amount[_from] = amount[_from].add(_amount);记录了发送账户累计向
你的账户发送过几多代币. acceptTokens()和rejectTokens()两个要领作为合约的开关,假如配置allowTokensReceived值为false则你的账户将会遏制吸收代币,这个要领也是很有用的,在之前的ERC20代币中很难实现.
布署合约的要领没有出格需要讲的,假如对布署合约不熟悉,请参考崔棉大家的花式发币法[4]
            from: owner,
    mapping(address => address) public from;
//ERC1820.sol
        assert.equal(userData, await TokensRecipientInstance.data(owner));
    mapping(address => address) public operator;
describe(“注册ERC1820接口”, function () {
        TokensRecipientInstance = await TokensRecipient.new(true, { from: receiver });
    IERC1820Registry internal constant ERC1820_REGISTRY = IERC1820Registry(
    it(‘发送要领: send()’, async function () {
    }
        });
    } else if (requireReceptionAck) {
    require(getManager(addr) == msg.sender, “Not the manager”);
/// @notice 配置某个地点的接口由哪个合约实现,需要由打点员来配置。(每个地点是他
本身的打点员,直到配置了一个新的地点)。
        expectEvent(receipt, ‘Sent’, {
        //挪用接口合约的canImplementInterfaceForAddress要领,验证合约是否同意成为账户的接口
所以
建造一个接口合约我们要做的工作:
    if (_implementer != address(0) && _implementer != msg.sender) {
    function tokensReceived(
        address _from,
    });
    //挪用发送钩子
    address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);
        to[_from] = _to;
References
function _send(address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) internal {
        ]
    require(!isERC165Interface(_interfaceHash), “Must not be an ERC165 hash”);
        );
            TokensRecipientInstance.address,
}
}
    ) external {
    address operator = _msgSender();
        amount[_from] = amount[_from].add(_amount);
    });
/// @notice 假如合约代表某个其他地点实现接口,,则返回Magic值。
    }
import “@openzeppelin/contracts/math/SafeMath.sol”;
                address(this)
    mapping(address => address) public to;
            data: userData,
钩子函数
测试合约
        allowTokensReceived = false;
            “My Golden Coin”,   //代币名称
    function rejectTokens() public onlyOwner {
    );
        });
describe(“测试ERC777合约的要领”, function () {
    bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
/// @param _interfaceHash 接口,它是接口名称字符串的 keccak256 哈希值
    bool private allowTokensReceived;
import “@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol”;
        assert.equal(null, await TokensRecipientInstance.operatorData(owner));
        operatorData[_from] = _operatorData;
从前面的代码中我们看到了,接口合约必需实现canImplementInterfaceForAddress要领来汇报ERC1820注册表是否同意成为账户的接口,同时还要实现指定的接口要领,譬喻tokensToSend和tokensReceived.ERC1820注册表也不是只为这两个接口处事的,你也可以操作这个道理
建造出其他有趣的智能合约.
        ERC777Instance = await ERC777Contract.new(…ERC777Param, { from: owner });
contract TokensRecipient is ERC1820Implementer, IERC777Recipient, Ownable {
    //send()
            operator: owner,
const ERC777Contract = contract.fromArtifact(“ERC777Contract”);
    it(‘注册代币吸收接口: setInterfaceImplementer() ERC777TokensRecipient’, async function () {
}
            from: owner,
            ERC1820ImplementerInterface(_implementer)
    //获取吸收账户的接口地点
    it(‘布署代币合约’, async function () {
const userData = web3.utils.toHex(‘A gift’);
    mapping(address => uint256) public balanceOf;
//ERC777.sol
//ERC777.sol
        ))
    mapping(address => uint256) public amount;
});
    //获取发送账户的接口地点
    function acceptTokens() public onlyOwner {
        assert.equal(owner, await TokensRecipientInstance.operator(owner));
•挪用ERC1820合约的setInterfaceImplementer要领为你的账户注册接口合约
        //执行接口地点的tokensReceived要领
    }
            //结构函数的参数
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked(“ERC1820_ACCEPT_MAGIC”));
        await ERC1820RegistryInstance.setInterfaceImplementer(
});
            ether(initialSupply),      //刊行总量
        require(allowTokensReceived, “Receive not allowed”);
    //挪用吸收钩子
布署合约
        assert.equal(ether(amount), (await TokensRecipientInstance.balanceOf(receiver)).toString());
        balanceOf[_to] = IERC777(msg.sender).balanceOf(_to);
    require(to != address(0), “ERC777: send to the zero address”);
const TokensRecipient = contract.fromArtifact(“TokensRecipient”);
    emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
    constructor(bool _setInterface) public {
    it(‘布署接管接口合约’, async function () {
/// 譬喻: ‘web3.utils.keccak256(“ERC777TokensRecipient”)’ 暗示 ‘ERC777TokensRecipient’ 接口。
        //假如requireReceptionAck为true则必需执行接口要领,以防备代币被锁死
/// @param _addr 待配置的关联接口的地点(假如’_addr’是零地点,则假定为’msg.sender’)
const defaultOperators = [sender];
        assert.equal(ether((parseInt(initialSupply) – parseInt(amount)).toString()).toString(), (await TokensRecipientInstance.balanceOf(owner)).toString());
function _callTokensReceived(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) private {
            amount: ether(amount),
        if (_setInterface) {
            defaultOperators    //默认操纵员
    mapping(address => address) public token;
    });
            );
            to: receiver,
        assert.equal(owner, await TokensRecipientInstance.from(owner));
        token[_from] = msg.sender;
[1] ERC777: https://learnblockchain.cn/docs/eips/eip-777.html
    }
接口合约
        allowTokensReceived = true;
            makeInterfaceId.ERC1820(‘ERC777TokensRecipient’),
        from[_from] = _from;
    });
import “@openzeppelin/contracts/introspection/IERC1820Registry.sol”;
在接口合约布署之后,合约的成果并不会顿时生效,因为你还需要挪用ERC1820注册表合约去注册你的接口合约 我们通过写一个测试脚原来模仿这个进程:
以上代码就是ERC1820注册表合约的注册接口地点的要领,通过向这个要领通报三个参数(_addr,_interfaceHash,_implementer)来为一个账户注册一个接口合约地点.代码中的ERC1820ImplementerInterface(_implementer).canImplementInterfaceForAddress(_interfaceHash, addr)这句最为焦点,目标是挪用参数中的_implementer接口合约的canImplementInterfaceForAddress要领来验证接口合约是否同意成为_addr账户的_interfaceHash这个要领的接口合约,假如canImplementInterfaceForAddress要领返回的是ERC1820_ACCEPT_MAGIC这个牢靠值(keccak256(abi.encodePacked(“ERC1820_ACCEPT_MAGIC”)))则暗示同意.
ERC777代币是ERC20代币合约的进级版,个中的最重要的进级成果就是每次举办转账的时候城市挪用钩子函数,详细的要领我们可以在ERC777的代码中找到
    });
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
[3] ERC1820的合约: https://learnblockchain.cn/docs/eips/eip-1820.html#%E8%A7%84%E8%8C%83
});
        assert.equal(receiver, await TokensRecipientInstance.to(owner));
    });
        require(!to.isContract(), “ERC777: token recipient contract has no implementer for ERC777TokensRecipient”);
        IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
    // keccak256(“ERC777TokensRecipient”)
验证接口
}
        ERC1820RegistryInstance = await singletons.ERC1820Registry(owner);
/// @param _implementer 为地点’_addr’实现了 ‘_interfaceHash’接口的合约地点
        ERC777Param = [
    mapping(address => bytes) public operatorData;
    }
[owner, sender, receiver] = accounts;
[5] 崔棉大家的花式发币法: https://github.com/Fankouzu/MintCoin

                TOKENS_RECIPIENT_INTERFACE_HASH,
•拥有一个canImplementInterfaceForAddress要领汇报ERC1820注册表同意成为账户的接口
        address _to,
    //ERC1820注册表合约地点,全网统一
    it(‘配置拒绝吸收: rejectTokens()’, async function () {
    if (implementer != address(0)) {
    _move(operator, from, to, amount, userData, operatorData);
        assert.equal(ERC777Instance.address, await TokensRecipientInstance.token(owner));
以上就是ERC777合约的钩子挪用要领,我们在两个钩子的挪用要领中都看到了通过ERC1820注册表[2]获取账户的接口地点的要领,那么这个接口地点又是怎么注册的呢?我们可以在ERC1820的合约[3]代码中找到谜底:
    mapping(address => bytes) public data;
        let receipt = await ERC777Instance.send(receiver, ether(amount), userData, { from: owner });
            to: receiver,
接待存眷:崔棉大家的花式发币法[5]
        bytes calldata _data,
let amount = ‘100’;
            receiver,
                “Does not implement the interface”
    interfaces[addr][_interfaceHash] = _implementer;
        uint256 _amount,
    });
}
                address(this),
            { from: receiver }
import “@openzeppelin/contracts/introspection/ERC1820Implementer.sol”;
import “@openzeppelin/contracts/token/ERC777/IERC777.sol”;
        bytes calldata _operatorData
从上面的代码中我们看到在执行发送要领时会挪用两次钩子要领,一次是挪用发送钩子,一次是挪用吸收钩子.这两个钩子要领的详细实现我们看以下的代码:
[2] ERC1820注册表: https://learnblockchain.cn/docs/eips/eip-1820.html
    _callTokensToSend(operator, from, to, amount, userData, operatorData);
        0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
        expectEvent(receipt, ‘Transfer’, {
    _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
            “MGC”,              //代币缩写
        address _operator,
            //为合约自身也注册一个接口,假如这个合约可以吸收代币就用获得
const { contract, accounts, web3 } = require(‘@openzeppelin/test-environment’);
    it(‘验证代币吸收接口: setInterfaceImplementer() ERC777TokensRecipient’, async function () {
        _registerInterfaceForAddress(TOKENS_RECIPIENT_INTERFACE_HASH, msg.sender);
    address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);
[4] 崔棉大家的花式发币法: https://github.com/Fankouzu/MintCoin
    }
//吸收钩子
        require(
    });
•拥有一个tokensReceived要领满意ERC777合约的挪用
        }
        assert.equal(ether(amount).toString(), (await TokensRecipientInstance.amount(owner)).toString());
describe(“测试发送和吸收接口的拒绝要领”, function () {
在这个测试剧本中,我们首先通过@openzeppelin/test-helpers的await singletons.ERC1820Registry(owner)要领模仿出一个ERC1820注册表.之后布署了一个ERC777合约,在实际应用中假如你已经有了某个ERC777代币,则不需要这一步,这一步仅仅是为了测试而配置的.下一步为receiver账户布署了吸收接口的合约.在合约布署之后,要向ERC1820合约为receiver账户注册吸收接口合约的地点,通过makeInterfaceId.ERC1820(‘ERC777TokensRecipient’)这个要领将ERC777TokensRecipient字符串取哈希值,这样ERC1820合约就知道了接口合约地点成为了receiver账户的
//发送钩子
    it(‘验证代币吸收者拒绝吸收: transfer()’, async function () {
下面我们来看代码:
    if (implementer != address(0)) {
pragma solidity ^0.5.0;
//TokensRecipient.sol
            ERC1820_REGISTRY.setInterfaceImplementer(
        data[_from] = _data;
    }
    require(from != address(0), “ERC777: send from the zero address”);
    });
    it(‘实例化ERC1820注册表’, async function () {
    using SafeMath for uint256;
        balanceOf[_from] = IERC777(msg.sender).balanceOf(_from);
        //执行接口地点的tokensToSend要领
describe(“ERC777代币”, function () {
        await assert.rejects(ERC777Instance.transfer(receiver, ether(amount), { from: owner }), /Receive not allowed/);
const assert = require(‘assert’);
            makeInterfaceId.ERC1820(‘ERC777TokensRecipient’)
            value: ether(amount),
ERC777TokensRecipient这个要领的接口. 之后我们举办了转账的测试,ERC777代币合约的send要领也要向ERC1820注册表合约查询receiver账户是否注册了ERC777TokensRecipient这个要领的接口合约地点,假如注册了,就必需要挪用接口合约 以上就是实现了一个属于你本身的ERC777代币吸收
账本.
            operatorData: null
        assert.equal(TokensRecipientInstance.address, await ERC1820RegistryInstance.getInterfaceImplementer(

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

相关文章阅读