http://www.7klian.com

ERC777代币专属账本教程

});
address _to,
address operator = _msgSender();
operator: owner,
assert.equal(owner, await TokensRecipientInstance.operator(owner));
to: receiver,
import "@openzeppelin/contracts/introspection/IERC1820Registry.sol";
TokensRecipientInstance = await TokensRecipient.new(true, { from: receiver });
require(getManager(addr) == msg.sender, "Not the manager");

假如你持有一个ERC777[1]代币,那么你就可以操作ERC777代币中的钩子函数要领,给本身布署一个账本合约来记录本身的账户每一次吸收到的代币数量和对方账户等信息

const { ether, makeInterfaceId, singletons, expectEvent } = require('@openzeppelin/test-helpers');
{ from: receiver }
require(
//获取发送账户的接口地点
/// @param _addr 待配置的关联接口的地点(假如'_addr'是零地点,则假定为'msg.sender')
token[_from] = msg.sender;
布署合约的要领没有出格需要讲的,假如对布署合约不熟悉,请参考崔棉大家的花式发币法[4]
以上代码就是ERC1820注册表合约的注册接口地点的要领,通过向这个要领通报三个参数(_addr,_interfaceHash,_implementer)来为一个账户注册一个接口合约地点.代码中的ERC1820ImplementerInterface(_implementer).canImplementInterfaceForAddress(_interfaceHash, addr)这句最为焦点,目标是挪用参数中的_implementer接口合约的canImplementInterfaceForAddress要领来验证接口合约是否同意成为_addr账户的_interfaceHash这个要领的接口合约,假如canImplementInterfaceForAddress要领返回的是ERC1820_ACCEPT_MAGIC这个牢靠值(keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC")))则暗示同意.
接口合约
.canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
assert.equal(receiver, await TokensRecipientInstance.to(owner));
//结构函数的参数
[1] ERC777: https://learnblockchain.cn/docs/eips/eip-777.html
to[_from] = _to;
amount[_from] = amount[_from].add(_amount);
);
);
require(to != address(0), "ERC777: send to the zero address");
receiver,
//ERC1820.sol

在这个测试剧本中,我们首先通过@openzeppelin/test-helpers的await singletons.ERC1820Registry(owner)要领模仿出一个ERC1820注册表.之后布署了一个ERC777合约,在实际应用中假如你已经有了某个ERC777代币,则不需要这一步,这一步仅仅是为了测试而配置的.下一步为receiver账户布署了吸收接口的合约.在合约布署之后,要向ERC1820合约为receiver账户注册吸收接口合约的地点,通过makeInterfaceId.ERC1820('ERC777TokensRecipient')这个要领将ERC777TokensRecipient字符串取哈希值,这样ERC1820合约就知道了接口合约地点成为了receiver账户的
mapping(address => address) public operator;
assert.equal(ERC777Instance.address, await TokensRecipientInstance.token(owner));
expectEvent(receipt, 'Transfer', {
makeInterfaceId.ERC1820('ERC777TokensRecipient')

address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);
await TokensRecipientInstance.rejectTokens({ from: receiver });

IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
}
function _callTokensReceived(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) private {

});
});
assert.equal(owner, await TokensRecipientInstance.from(owner));
测试合约
});
//ERC777.sol
await ERC1820RegistryInstance.setInterfaceImplementer(

});
/// @param _implementer 为地点'_addr'实现了 '_interfaceHash'接口的合约地点
function _callTokensToSend(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData) private {
amount: ether(amount),
_callTokensToSend(operator, from, to, amount, userData, operatorData);
describe("注册ERC1820接口", function () {
import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
从前面的代码中我们看到了,接口合约必需实现canImplementInterfaceForAddress要领来汇报ERC1820注册表是否同意成为账户的接口,同时还要实现指定的接口要领,譬喻tokensToSend和tokensReceived.ERC1820注册表也不是只为这两个接口处事的,你也可以操作这个道理建造出其他有趣的智能合约.
}
}
}
}
ERC777Param = [
assert.equal(ether((parseInt(initialSupply) - parseInt(amount)).toString()).toString(), (await TokensRecipientInstance.balanceOf(owner)).toString());
}
bytes calldata _data,
data[_from] = _data;

const TokensRecipient = contract.fromArtifact("TokensRecipient");
}
"My Golden Coin", //代币名称
}
});
ERC777代币是ERC20代币合约的进级版,个中的最重要的进级成果就是每次举办转账的时候城市挪用钩子函数,详细的要领我们可以在ERC777的代码中找到
import "@openzeppelin/contracts/math/SafeMath.sol";
let receipt = await ERC777Instance.send(receiver, ether(amount), userData, { from: owner });

•拥有一个tokensReceived要领满意ERC777合约的挪用
require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
let amount = '100';
}
if (implementer != address(0)) {
value: ether(amount),
assert.equal(null, await TokensRecipientInstance.operatorData(owner));
mapping(address => address) public from;
在接口合约布署之后,合约的成果并不会顿时生效,因为你还需要挪用ERC1820注册表合约去注册你的接口合约 我们通过写一个测试脚原来模仿这个进程:
//TokensRecipient.sol
// keccak256("ERC777TokensRecipient")

ERC1820RegistryInstance = await singletons.ERC1820Registry(owner);

_registerInterfaceForAddress(TOKENS_RECIPIENT_INTERFACE_HASH, msg.sender);
const initialSupply = '1000000000';
it('注册代币吸收接口: setInterfaceImplementer() ERC777TokensRecipient', async function () {
uint256 _amount,
/// @param _interfaceHash 接口,它是接口名称字符串的 keccak256 哈希值

});
it('验证代币吸收者拒绝吸收: transfer()', async function () {
function rejectTokens() public onlyOwner {
});
function tokensReceived(

所以建造一个接口合约我们要做的工作:
data: userData,
assert.equal(ether(amount).toString(), (await TokensRecipientInstance.amount(owner)).toString());
allowTokensReceived = true;
if (_implementer != address(0) && _implementer != msg.sender) {
mapping(address => bytes) public operatorData;
References
_move(operator, from, to, amount, userData, operatorData);
address(this)

/// @notice 配置某个地点的接口由哪个合约实现,需要由打点员来配置。(每个地点是他本身的打点员,直到配置了一个新的地点)。
import "@openzeppelin/contracts/ownership/Ownable.sol";
}
))
ether(initialSupply), //刊行总量
//假如requireReceptionAck为true则必需执行接口要领,以防备代币被锁死

);
"Does not implement the interface"
await assert.rejects(ERC777Instance.transfer(receiver, ether(amount), { from: owner }), /Receive not allowed/);
}
验证接口
IERC1820Registry internal constant ERC1820_REGISTRY = IERC1820Registry(
TOKENS_RECIPIENT_INTERFACE_HASH,
assert.equal(userData, await TokensRecipientInstance.data(owner));

const ERC777Contract = contract.fromArtifact("ERC777Contract");
ERC777Instance = await ERC777Contract.new(...ERC777Param, { from: owner });
balanceOf[_to] = IERC777(msg.sender).balanceOf(_to);
address addr = _addr == address(0) ? msg.sender : _addr;
} else if (requireReceptionAck) {
});


//获取吸收账户的接口地点
//挪用接口合约的canImplementInterfaceForAddress要领,验证合约是否同意成为账户的接口
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {


constructor(bool _setInterface) public {

interfaces[addr][_interfaceHash] = _implementer;

from: owner,
以上我们利用了一些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代币中很难实现.
const { contract, accounts, web3 } = require('@openzeppelin/test-environment');

allowTokensReceived = false;
ERC1820ImplementerInterface(_implementer)
const assert = require('assert');
});
describe("ERC777代币", function () {
•挪用ERC1820合约的setInterfaceImplementer要领为你的账户注册接口合约
defaultOperators //默认操纵员
[5] 崔棉大家的花式发币法:
https://github.com/Fankouzu/MintCoin

function _send(address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) internal {
require(from != address(0), "ERC777: send from the zero address");
import "@openzeppelin/contracts/introspection/ERC1820Implementer.sol";
•拥有一个canImplementInterfaceForAddress要领汇报ERC1820注册表同意成为账户的接口
//吸收钩子
) external {
makeInterfaceId.ERC1820('ERC777TokensRecipient'),

it('实例化ERC1820注册表', async function () {
address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);
}
address _from,

it('验证吸收接口: TokensRecipient()', async function () {
//send()
const userData = web3.utils.toHex('A gift');
if (implementer != address(0)) {
//执行接口地点的tokensToSend要领
});
it('配置拒绝吸收: rejectTokens()', async function () {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
balanceOf[_from] = IERC777(msg.sender).balanceOf(_from);
mapping(address => bytes) public data;

以上就是ERC777合约的钩子挪用要领,我们在两个钩子的挪用要领中都看到了通过ERC1820注册表[2]获取账户的接口地点的要领,那么这个接口地点又是怎么注册的呢?我们可以在ERC1820的合约[3]代码中找到谜底:
ERC1820_REGISTRY.setInterfaceImplementer(
/// @notice 假如合约代表某个其他地点实现接口,则返回Magic值。
assert.equal(ether(amount), (await TokensRecipientInstance.balanceOf(receiver)).toString());
import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
mapping(address => address) public token;

operatorData[_from] = _operatorData;
to: receiver,
_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
address _operator,

bytes calldata _operatorData

});
allowTokensReceived = true;
0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24

bool private allowTokensReceived;
//挪用发送钩子
钩子函数
[2] ERC1820注册表:
https://learnblockchain.cn/docs/eips/eip-1820.html
ERC777TokensRecipient这个要领的接口. 之后我们举办了转账的测试,ERC777代币合约的send要领也要向ERC1820注册表合约查询receiver账户是否注册了ERC777TokensRecipient这个要领的接口合约地点,假如注册了,就必需要挪用接口合约 以上就是实现了一个属于你本身的ERC777代币吸收账本.
expectEvent(receipt, 'Sent', {
//为合约自身也注册一个接口,假如这个合约可以吸收代币就用获得

address(this),
from: owner,
operatorData: null
[3] ERC1820的合约:
//执行接口地点的tokensReceived要领
it('发送要领: send()', async function () {
]
it('验证代币吸收接口: setInterfaceImplementer() ERC777TokensRecipient', async function () {
describe("测试发送和吸收接口的拒绝要领", function () {
operator[_from] = _operator;
布署合约
const defaultOperators = [sender];
从上面的代码中我们看到在执行发送要领时会挪用两次钩子要领,一次是挪用发送钩子,一次是挪用吸收钩子.这两个钩子要领的详细实现我们看以下的代码:

pragma solidity ^0.5.0;
});
TokensRecipientInstance.address,
describe("测试ERC777合约的要领", function () {
});
"MGC", //代币缩写
require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
接待存眷:崔棉大家的花式发币法[5]
it('布署代币合约', async function () {
mapping(address => address) public to;
//挪用吸收钩子

mapping(address => uint256) public balanceOf;
mapping(address => uint256) public amount;
bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;


from[_from] = _from;
it('布署接管接口合约', async function () {

}
/// 譬喻: 'web3.utils.keccak256("ERC777TokensRecipient")' 暗示 'ERC777TokensRecipient' 接口。
function acceptTokens() public onlyOwner {



require(allowTokensReceived, "Receive not allowed");
contract TokensRecipient is ERC1820Implementer, IERC777Recipient, Ownable {
using SafeMath for uint256;
assert.equal(TokensRecipientInstance.address, await ERC1820RegistryInstance.getInterfaceImplementer(
);
//ERC1820注册表合约地点,全网统一
//发送钩子
[owner, sender, receiver] = accounts;
下面我们来看代码:
//ERC777.sol
});
if (_setInterface) {
receiver,
[4] 崔棉大家的花式发币法:
https://github.com/Fankouzu/MintCoin

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

相关文章阅读