创建与MetaMask一起使用的可升级代理合同

时间:2019-05-05 11:34:43

标签: ethereum solidity smartcontracts

我正在创建一个连接到现有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) }
       }
   }
}
当我在带有ERC-20合约地址的metamask中添加令牌时,

函数 balanceOf 工作正常。但按代理合同应显示为零

function balanceOf(address tokenOwner) public view  returns (uint256) { 
    return balances[tokenOwner];
}

我的努力

为了测试,我编写了此函数:

function test(address theAddress) public view  returns (address) { 
    return theAddress ;
}

当我调用参数'0xC357c241b98B15B3A08aeC3AcD49fBC0cbD74fcE'ERC-20合约上的返回相同地址,但代理人返回 此值:

  

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

中可用

1 个答案:

答案 0 :(得分:0)

代理合同的工作方式略有不同。

let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)

代理合同中的 DELEGATECALL 通过_impl中指定的地址调用合同。因此,结果是,它在代理合同的环境中运行_impl代码(在您的情况下为ERC20)。结果,修改了代理的存储,而不是ERC20签约存储。链接至delegatecall works

所以我的建议是看看您如何初始化ERC20合同并设置其余额。

您将必须执行类似的操作

erc20Contract = await erc20Contract.at(proxy.address)

erc20Contract.initialize()

第一行为您提供erc20Contract的接口,位于代理合同的地址处。 第二行将在代理合同的地址和存储处重做构造函数的工作。