创建可与 MetaMask 一起使用的可升级代理合约
create upgradable proxy contract that works with MetaMask
我正在创建一个连接到现有 ERC-20
合约的代理合约。
该代理合约应该能够连接到元掩码并显示代币余额。
在带有代理地址的元掩码中添加令牌时一切正常,它正确显示符号和十进制数但不平衡。而是显示为零。
代理合约代码:
contract Proxy {
address private _implementation;
event Upgraded(address indexed implementation);
function implementation() public view returns (address) {
return _implementation;
}
function upgradeTo(address impl) public {
_implementation = impl;
emit Upgraded(impl);
}
function () payable external {
address _impl = implementation();
require(_impl != address(0));
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
函数 balanceOf 当我在具有 ERC-20
合约地址的元掩码中添加令牌时工作正常。但代理合约显示为零
function balanceOf(address tokenOwner) public view returns (uint256) {
return balances[tokenOwner];
}
我的努力
为了测试我写了这个函数:
function test(address theAddress) public view returns (address) {
return theAddress ;
}
当我调用参数时 '0xC357c241b98B15B3A08aeC3AcD49fBC0cbD74fcE'
在 ERC-20
合同 returns 相同地址但在代理 returns
这个值:
0xc357c241b98b19150f7f8f1d47ad1cd500000000
我做的另一个测试是这个功能:
function test2(string memory theString) public view returns (string memory) {
return theString ;
}
此功能在代理和 ERC-20
合约上都能正常工作!!
谢谢大家。
编辑 1
我的测试 web3.js
var interval ;
document.addEventListener('DOMContentLoaded', function() {
interval = setInterval(run , 1000);
}, false);
function run(){
web3 = new Web3(web3.currentProvider);
console.log("call");
if(web3.eth.accounts[0] === undefined)
return;
clearInterval(interval);
console.log(web3.eth.accounts[0]);
web3.eth.defaultAccount = web3.eth.accounts[0];
var CoursetroContract = web3.eth.contract( JSON.parse(`[
{
"constant": true,
"inputs": [
{
"name": "theAddress",
"type": "address"
}
],
"name": "test",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "theString",
"type": "string"
}
],
"name": "test2",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]`));
var contract = CoursetroContract.at('0xd3744cac3a2f796c16b45e5be582c1c5f3039482'); //proxy
//var contract = CoursetroContract.at('0xd025c8835b2a4bd2f9eeb1d682db224f7b301868'); //erc20
contract.test(0xC357c241b98B15B3A08aeC3AcD49fBC0cbD74fcE,
function(err,result){
console.log("err" ,err);
console.log("result" , result);
}
);
编辑 2
此合约地址已在 Ropsten Testnet
中可用
代理合约的工作方式略有不同。
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
您的代理合约中的 DELEGATECALL
通过 _impl
中指定的地址调用合约。因此,它在代理合同的环境中运行 _impl 代码(在您的例子中是 ERC20)。因此,代理的存储被修改,而不是 ERC20 合约存储。 Link如何delegatecall works.
所以我的建议是查看您如何初始化 ERC20 合约并设置其余额。
你必须这样做
erc20Contract = await erc20Contract.at(proxy.address)
erc20Contract.initialize()
第一行是erc20Contract在代理合约地址的接口。
第二行将在代理合约的地址和存储处重做构造函数的工作。
我正在创建一个连接到现有 ERC-20
合约的代理合约。
该代理合约应该能够连接到元掩码并显示代币余额。
在带有代理地址的元掩码中添加令牌时一切正常,它正确显示符号和十进制数但不平衡。而是显示为零。
代理合约代码:
contract Proxy {
address private _implementation;
event Upgraded(address indexed implementation);
function implementation() public view returns (address) {
return _implementation;
}
function upgradeTo(address impl) public {
_implementation = impl;
emit Upgraded(impl);
}
function () payable external {
address _impl = implementation();
require(_impl != address(0));
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
函数 balanceOf 当我在具有 ERC-20
合约地址的元掩码中添加令牌时工作正常。但代理合约显示为零
function balanceOf(address tokenOwner) public view returns (uint256) {
return balances[tokenOwner];
}
我的努力
为了测试我写了这个函数:
function test(address theAddress) public view returns (address) {
return theAddress ;
}
当我调用参数时 '0xC357c241b98B15B3A08aeC3AcD49fBC0cbD74fcE'
在 ERC-20
合同 returns 相同地址但在代理 returns
这个值:
0xc357c241b98b19150f7f8f1d47ad1cd500000000
我做的另一个测试是这个功能:
function test2(string memory theString) public view returns (string memory) {
return theString ;
}
此功能在代理和 ERC-20
合约上都能正常工作!!
谢谢大家。
编辑 1
我的测试 web3.js
var interval ;
document.addEventListener('DOMContentLoaded', function() {
interval = setInterval(run , 1000);
}, false);
function run(){
web3 = new Web3(web3.currentProvider);
console.log("call");
if(web3.eth.accounts[0] === undefined)
return;
clearInterval(interval);
console.log(web3.eth.accounts[0]);
web3.eth.defaultAccount = web3.eth.accounts[0];
var CoursetroContract = web3.eth.contract( JSON.parse(`[
{
"constant": true,
"inputs": [
{
"name": "theAddress",
"type": "address"
}
],
"name": "test",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "theString",
"type": "string"
}
],
"name": "test2",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]`));
var contract = CoursetroContract.at('0xd3744cac3a2f796c16b45e5be582c1c5f3039482'); //proxy
//var contract = CoursetroContract.at('0xd025c8835b2a4bd2f9eeb1d682db224f7b301868'); //erc20
contract.test(0xC357c241b98B15B3A08aeC3AcD49fBC0cbD74fcE,
function(err,result){
console.log("err" ,err);
console.log("result" , result);
}
);
编辑 2
此合约地址已在 Ropsten Testnet
代理合约的工作方式略有不同。
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
您的代理合约中的 DELEGATECALL
通过 _impl
中指定的地址调用合约。因此,它在代理合同的环境中运行 _impl 代码(在您的例子中是 ERC20)。因此,代理的存储被修改,而不是 ERC20 合约存储。 Link如何delegatecall works.
所以我的建议是查看您如何初始化 ERC20 合约并设置其余额。
你必须这样做
erc20Contract = await erc20Contract.at(proxy.address)
erc20Contract.initialize()
第一行是erc20Contract在代理合约地址的接口。 第二行将在代理合约的地址和存储处重做构造函数的工作。