github.com/Tri-stone/burrow@v0.25.0/tests/jobs_fixtures/app51-user-account-forward-proxy-pattern/UserAccountTest.sol (about) 1 pragma solidity >=0.0.0; 2 3 import "./DefaultUserAccount.sol"; 4 5 contract UserAccountTest { 6 7 string constant SUCCESS = "success"; 8 string longString = "longString"; 9 10 string testServiceFunctionSig = "serviceInvocation(address,uint256,bytes32)"; 11 12 /** 13 * @dev Tests the DefaultUserAccount call forwarding logic 14 */ 15 function testCallForwarding() external returns (string memory) { 16 17 uint testState = 42; 18 bytes32 testKey = "myKey"; 19 TestService testService = new TestService(); 20 bool success; 21 22 DefaultUserAccount account = new DefaultUserAccount(); 23 24 // The bytes payload encoding the function signature and parameters for the forwarding call 25 bytes memory payload = abi.encodeWithSignature(testServiceFunctionSig, address(this), testState, testKey); 26 27 // test failures 28 // *IMPORTANT*: the use of the abi.encode function for this call is extremely important since sending the parameters individually via call(bytes4, args...) 29 // has known problems encoding the dynamic-size parameters correctly, see https://github.com/ethereum/solidity/issues/2884 30 (success, ) = address(account).call(abi.encodeWithSelector(bytes4(keccak256("forwardCall(address,bytes)")), address(0), payload)); 31 if (success) 32 return "Forwarding a call to an empty address should revert"; 33 (success, ) = account.forwardCall(address(testService), abi.encodeWithSignature("fakeFunction(bytes32)", testState)); 34 if (success) 35 return "Forwarding a call to a non-existent function should return false"; 36 37 // test successful invocation 38 bytes memory returnData; 39 (success, returnData) = account.forwardCall(address(testService), payload); 40 if (!success) return "Forwarding a call from an authorized address with correct payload should return true"; 41 if (testService.currentEntity() != address(this)) return "The testService should show this address as the current entity"; 42 if (testService.currentState() != testState) return "The testService should have the testState set"; 43 if (testService.currentKey() != testKey) return "The testService should have the testKey set"; 44 if (testService.lastCaller() != address(account)) return "The testService should show the DefaultUserAccount as the last caller"; 45 if (returnData.length != 32) return "ReturnData should be of size 32"; 46 // TODO ability to decode return data via abi requires 0.5.0. 47 // (bytes32 returnMessage) = abi.decode(returnData,(bytes32)); 48 if (toBytes32(returnData, 32) != testService.getSuccessMessage()) return "The function return data should match the service success message"; 49 50 // test different input/return data 51 payload = abi.encodeWithSignature("isStringLonger5(string)", longString); 52 (success, returnData) = account.forwardCall(address(testService), payload); 53 if (!success) return "isStringLonger5 invocation should succeed"; 54 if (returnData[31] != hex"01") return "isStringLonger5 should return true for longString"; // boolean is left-padded, so the value is at the end of the bytes 55 56 payload = abi.encodeWithSignature("getString()"); 57 (success, returnData) = account.forwardCall(address(testService), payload); 58 if (!success) return "getString invocation should succeed"; 59 (string memory RetString) = abi.decode(returnData, (string)); 60 string memory expected = "Hello World"; 61 // Solidity thinks string compare should be as hard as possible. 62 if (keccak256(abi.encodePacked(RetString)) != keccak256(abi.encodePacked(expected))) return "getString should return Hello World"; 63 64 return SUCCESS; 65 } 66 67 function toBytes32(bytes memory b, int offset) public pure returns (bytes32 result) { 68 assembly { 69 result := mload(add(b, offset)) 70 } 71 } 72 73 } 74 75 /** 76 * @dev Contract providing typical service functions to use as target for call forwarding. 77 */ 78 contract TestService { 79 80 address public currentEntity; 81 uint public currentState; 82 bytes32 public currentKey; 83 address public lastCaller; 84 string public storedString = "Hello World"; 85 86 function serviceInvocation(address _entity, uint _newState, bytes32 _key) public returns (bytes32) { 87 88 currentEntity = _entity; 89 currentState = _newState; 90 currentKey = _key; 91 lastCaller = msg.sender; 92 return "congrats"; 93 } 94 95 function getString() public view returns (string memory) { 96 return storedString; 97 } 98 99 function isStringLonger5(string memory _string) public pure returns (bool) { 100 if (bytes(_string).length > 5) 101 return true; 102 else 103 return false; 104 } 105 106 function getSuccessMessage() public pure returns (bytes32) { 107 return "congrats"; 108 } 109 }