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  }