pragma solidity >=0.4.22 <0.6.0; import "remix_tests.sol"; // this import is automatically injected by Remix. contract dapp { mapping (address => uint) public value; // 測試用 bytes4 METHOD1_IDENTIFIER; bytes4 METHOD2_IDENTIFIER; mapping (address => uint) public nonces; bytes32 public DOMAIN_SEPARATOR; constructor() public { DOMAIN_SEPARATOR = keccak256(abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes("dapp")), // contract name keccak256(bytes("0.1")), // contract version 1, // chain ID address(this) // contract address )); // 儲存每個 function 的簽名 hash METHOD1_IDENTIFIER = bytes4(keccak256(bytes("method1(address)"))); METHOD2_IDENTIFIER = bytes4(keccak256(bytes("method2(address,uint)"))); } function method1(address from) public { // 測試用 value[from] ++; } function method2(address from, uint x) public { // 測試用 value[from] = value[from] + x; } function metaSmile(address addr) public { // 測試用 bytes memory payload = abi.encodeWithSignature("smile(address)", addr); // "smile(address)" 正是 smile 的 function signature (bool success, bytes memory returnData) = address(this).call(payload); require(success); } function metaTx(address initiatorAddr, bytes calldata message, uint8 v, bytes32 r, bytes32 s, uint256 nonce) external returns (bool) { // *注意: (bytes4 functionSigEncoded, bytes memory paramPacked) = abi.decode(message, (bytes4, bytes)); bytes32 digest = keccak256( // 把 input argument 裡除簽名外全依 EIP712 規格打包 abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(initiatorAddr, message, nonce)) ) ); require( // 確認是 initiator 的簽名 initiatorAddr == ecrecover(digest, v, r, s), "dapp/ invalid metaTx" ); require( // 確認 nonce 正確,並更新 nonces nonce==nonces[initiatorAddr], "dapp/ incorrect nonce" ); nonces[initiatorAddr]++; // 用 low-level call 執行 message 內容 // *注意: 目前沒有檢查 message 內容 //(bool success, bytes memory returnData) = address(this).call(message); //require(success, "dapp/ metaTx execution failed"); // 改用 functionSigEncoded 查表看要呼叫合約內哪個 method。 // 一個潛在問題是,可以被 internal call 的不能是 external method!只能是 public。 // step1: 把 message 用 abi.decode() 拆成 bytes4 functionSigEncoded, bytes memory paramPacked (bytes4 identifier, bytes memory paramPacked) = abi.decode(message, (bytes4, bytes)); // step2: 和 bytes4 storage 的 functionSigEncoded_s 一個個比較,看要呼叫哪個 method。 require( identifier==METHOD1_IDENTIFIER || identifier == METHOD2_IDENTIFIER); if (identifier == METHOD1_IDENTIFIER) { // call method1 // step3: 依照該 method 的 arg type 把 paramPacked abi.decode(), 呼叫該 method。 method1(paramPacked); } else { // call method2 // step3: 依照該 method 的 arg type 把 paramPacked abi.decode(), 呼叫該 method。 (address from, uint x) = abi.decode(paramPacked, (address, uint)); method2(from, x); } return true; } }
0.6.0