值得注意的是,支持多个构造函数的附加功能不会影响代理合约字节码的验证,因为初始化 tx 调用数据(输入)可以通过首先使用代理合约 ABI,然后使用逻辑合约 ABI 来解码。
下面的合约显示了建议的代理合约实现。
contractProxy{// Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"constructor(bytesmemoryconstructData,addresscontractLogic)public{// save the code addressassembly{// solium-disable-linesstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7,contractLogic)}(boolsuccess,bytesmemory_)=contractLogic.delegatecall(constructData);// solium-disable-linerequire(success,"Construction failed");}function()externalpayable{assembly{// solium-disable-lineletcontractLogic:=sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)calldatacopy(0x0,0x0,calldatasize)letsuccess:=delegatecall(sub(gas,10000),contractLogic,0x0,calldatasize,0,0)letretSz:=returndatasizereturndatacopy(0,0,retSz)switchsuccesscase0{revert(0,retSz)}default{return(0,retSz)}}}}
contractProxiable{// Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"functionupdateCodeAddress(addressnewAddress)internal{require(bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)==Proxiable(newAddress).proxiableUUID(),"Not compatible");assembly{// solium-disable-linesstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7,newAddress)}}functionproxiableUUID()publicpurereturns(bytes32){return0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7;}}
contractOwnedisProxiable{// ensures no one can manipulate this contract once it is deployed// 确保合约一经部署,无人可以操纵addresspublicowner=address(1);functionconstructor1()public{// ensures this can be called only once per *proxy* contract deployed// 确保每个*代理*合约只能调用一次require(owner==address(0));owner=msg.sender;}functionupdateCode(addressnewCode)onlyOwnerpublic{updateCodeAddress(newCode);}modifieronlyOwner(){require(msg.sender==owner,"Only owner is allowed to perform this action");_;}}
ERC-20 代币
代理合约
pragmasolidity^0.5.1;contractProxy{// Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"constructor(bytesmemoryconstructData,addresscontractLogic)public{// save the code address// 保存代码地址assembly{// solium-disable-linesstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7,contractLogic)}(boolsuccess,bytesmemory_)=contractLogic.delegatecall(constructData);// solium-disable-linerequire(success,"Construction failed");}function()externalpayable{assembly{// solium-disable-lineletcontractLogic:=sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)calldatacopy(0x0,0x0,calldatasize)letsuccess:=delegatecall(sub(gas,10000),contractLogic,0x0,calldatasize,0,0)letretSz:=returndatasizereturndatacopy(0,0,retSz)switchsuccesscase0{revert(0,retSz)}default{return(0,retSz)}}}}
代币逻辑合约
contractProxiable{// Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"functionupdateCodeAddress(addressnewAddress)internal{require(bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)==Proxiable(newAddress).proxiableUUID(),"Not compatible");assembly{// solium-disable-linesstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7,newAddress)}}functionproxiableUUID()publicpurereturns(bytes32){return0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7;}}contractOwned{addressowner;functionsetOwner(address_owner)internal{owner=_owner;}modifieronlyOwner(){require(msg.sender==owner,"Only owner is allowed to perform this action");_;}}contractLibraryLockDataLayout{boolpublicinitialized=false;}contractLibraryLockisLibraryLockDataLayout{// Ensures no one can manipulate the Logic Contract once it is deployed.// 确保合约一经部署后无人可以操纵。// PARITY WALLET HACK PREVENTION// PARITY 钱包黑客攻击预防modifierdelegatedOnly(){require(initialized==true,"The library is locked. No direct 'call' is allowed");_;}functioninitialize()internal{initialized=true;}}contractERC20DataLayoutisLibraryLockDataLayout{uint256publictotalSupply;mapping(address=>uint256)publictokens;}contractERC20{// ...functiontransfer(addressto,uint256amount)public{require(tokens[msg.sender]>=amount,"Not enough funds for transfer");tokens[to]+=amount;tokens[msg.sender]-=amount;}}contractMyTokenisERC20DataLayout,ERC20,Owned,Proxiable,LibraryLock{functionconstructor1(uint256_initialSupply)public{totalSupply=_initialSupply;tokens[msg.sender]=_initialSupply;initialize();setOwner(msg.sender);}functionupdateCode(addressnewCode)publiconlyOwnerdelegatedOnly{updateCodeAddress(newCode);}functiontransfer(addressto,uint256amount)publicdelegatedOnly{ERC20.transfer(to,amount);}}