-
Notifications
You must be signed in to change notification settings - Fork 242
encodeParam and encodeParams functions #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from 44 commits
37b2612
85dda17
e3b49ea
bc0e525
a97c86d
fee7fc1
ecf2f5c
8542235
a85c5aa
b3ba05d
79100ce
9b4f8f3
b056828
0aa8d3c
e96f7c0
1fa2b95
da862e2
480b2f6
bdeee78
04c2048
2530b15
45727e3
97327f3
c44a025
b647866
aa08e27
b05c010
9c84dc3
dbce97e
dd2f8f2
22ec764
93e5ba1
ad1b7f7
3a8fb64
c4e0fb1
a026e22
5c61d10
c37d4fd
3d8c54f
9734f9c
8cf6039
3c909ef
584f4bc
05d3d26
d2268c8
722e25e
dbb0e06
db9342f
b18c1a5
37f8eff
9ff0bda
032f577
7366642
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /* | ||
| * SPDX-License-Identitifer: MIT | ||
| */ | ||
|
|
||
| pragma solidity ^0.4.24; | ||
|
|
||
|
|
||
| contract ACLParams { | ||
|
|
||
| enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } // op types | ||
|
|
||
| struct Param { | ||
| uint8 id; | ||
| uint8 op; | ||
| uint240 value; // even though value is an uint240 it can store addresses | ||
| // in the case of 32 byte hashes losing 2 bytes precision isn't a huge deal | ||
| // op and id take less than 1 byte each so it can be kept in 1 sstore | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol) | ||
| // and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143) | ||
|
|
||
| pragma solidity ^0.4.24; | ||
|
|
||
| import "../lib/token/ERC20.sol"; | ||
|
|
||
|
|
||
| library SafeERC20 { | ||
| // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`: | ||
| // https://github.com/ethereum/solidity/issues/3544 | ||
| bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb; | ||
|
|
||
| string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED"; | ||
| string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED"; | ||
|
|
||
| function invokeAndCheckSuccess(address _addr, bytes memory _calldata) | ||
| private | ||
| returns (bool) | ||
| { | ||
| bool ret; | ||
| assembly { | ||
| let ptr := mload(0x40) // free memory pointer | ||
|
|
||
| let success := call( | ||
| gas, // forward all gas | ||
| _addr, // address | ||
| 0, // no value | ||
| add(_calldata, 0x20), // calldata start | ||
| mload(_calldata), // calldata length | ||
| ptr, // write output over free memory | ||
| 0x20 // uint256 return | ||
| ) | ||
|
|
||
| if gt(success, 0) { | ||
| // Check number of bytes returned from last function call | ||
| switch returndatasize | ||
|
|
||
| // No bytes returned: assume success | ||
| case 0 { | ||
| ret := 1 | ||
| } | ||
|
|
||
| // 32 bytes returned: check if non-zero | ||
| case 0x20 { | ||
| // Only return success if returned data was true | ||
| // Already have output in ptr | ||
| ret := eq(mload(ptr), 1) | ||
| } | ||
|
|
||
| // Not sure what was returned: don't mark as success | ||
| default { } | ||
| } | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| function staticInvoke(address _addr, bytes memory _calldata) | ||
| private | ||
| view | ||
| returns (bool, uint256) | ||
| { | ||
| bool success; | ||
| uint256 ret; | ||
| assembly { | ||
| let ptr := mload(0x40) // free memory pointer | ||
|
|
||
| success := staticcall( | ||
| gas, // forward all gas | ||
| _addr, // address | ||
| add(_calldata, 0x20), // calldata start | ||
| mload(_calldata), // calldata length | ||
| ptr, // write output over free memory | ||
| 0x20 // uint256 return | ||
| ) | ||
|
|
||
| if gt(success, 0) { | ||
| ret := mload(ptr) | ||
| } | ||
| } | ||
| return (success, ret); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false). | ||
| * Note that this makes an external call to the token. | ||
| */ | ||
| function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) { | ||
| bytes memory transferCallData = abi.encodeWithSelector( | ||
| TRANSFER_SELECTOR, | ||
| _to, | ||
| _amount | ||
| ); | ||
| return invokeAndCheckSuccess(_token, transferCallData); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false). | ||
| * Note that this makes an external call to the token. | ||
| */ | ||
| function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) { | ||
| bytes memory transferFromCallData = abi.encodeWithSelector( | ||
| _token.transferFrom.selector, | ||
| _from, | ||
| _to, | ||
| _amount | ||
| ); | ||
| return invokeAndCheckSuccess(_token, transferFromCallData); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false). | ||
| * Note that this makes an external call to the token. | ||
| */ | ||
| function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) { | ||
| bytes memory approveCallData = abi.encodeWithSelector( | ||
| _token.approve.selector, | ||
| _spender, | ||
| _amount | ||
| ); | ||
| return invokeAndCheckSuccess(_token, approveCallData); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Static call into ERC20.balanceOf(). | ||
| * Reverts if the call fails for some reason (should never fail). | ||
| */ | ||
| function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) { | ||
| bytes memory balanceOfCallData = abi.encodeWithSelector( | ||
| _token.balanceOf.selector, | ||
| _owner | ||
| ); | ||
|
|
||
| (bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData); | ||
| require(success, ERROR_TOKEN_BALANCE_REVERTED); | ||
|
|
||
| return tokenBalance; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Static call into ERC20.allowance(). | ||
| * Reverts if the call fails for some reason (should never fail). | ||
| */ | ||
| function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) { | ||
| bytes memory allowanceCallData = abi.encodeWithSelector( | ||
| _token.allowance.selector, | ||
| _owner, | ||
| _spender | ||
| ); | ||
|
|
||
| (bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData); | ||
| require(success, ERROR_TOKEN_ALLOWANCE_REVERTED); | ||
|
|
||
| return allowance; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| pragma solidity 0.4.24; | ||
|
|
||
| import "./helpers/Assert.sol"; | ||
| import "../acl/ACLSyntaxSugar.sol"; | ||
| import "../acl/ACL.sol"; | ||
|
|
||
|
|
||
| contract TestACLHelpers is ACL { | ||
|
|
||
| function testEncodeParam() public { | ||
| Param memory param = Param(2, uint8(Op.EQ), 5294967297); | ||
|
|
||
| uint256 encodedParam = encodeParam(param); | ||
|
|
||
| (uint32 id, uint32 op, uint32 value) = decodeParamsList(encodedParam); | ||
|
|
||
| Assert.equal(uint256(param.id), uint256(id), "Encoded id is not equal"); | ||
| Assert.equal(uint256(param.op), uint256(op), "Encoded op is not equal"); | ||
| Assert.equal(uint256(param.value), uint256(value), "Encoded value is not equal"); | ||
| } | ||
|
|
||
| function testEncodeParams() public { | ||
| Param[] memory params = new Param[](4); | ||
|
|
||
| params[0] = Param(LOGIC_OP_PARAM_ID, uint8(Op.IF_ELSE), encodeIfElse(1, 2, 3)); | ||
| params[1] = Param(LOGIC_OP_PARAM_ID, uint8(Op.AND), encodeOperator(2, 3)); | ||
| params[2] = Param(2, uint8(Op.EQ), 1); | ||
| params[3] = Param(3, uint8(Op.NEQ), 2); | ||
|
|
||
| uint256[] memory encodedParam = encodeParams(params); | ||
|
|
||
| for (uint256 i = 0; i < 4; i++) { | ||
| (uint32 id, uint32 op, uint32 value) = decodeParamsList(encodedParam[i]); | ||
|
|
||
| Assert.equal(uint256(params[i].id), uint256(id), "Encoded id is not equal"); | ||
| Assert.equal(uint256(params[i].op), uint256(op), "Encoded op is not equal"); | ||
| Assert.equal(uint256(params[i].value), uint256(value), "Encoded value is not equal"); | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's test
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done :)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think it's a good idea, but let's wait for @sohkai opinion, maybe there is a reason I don't know for them to be there.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I didn't notice they were only in the tests. I think moving them could be helpful; AFAIK they're not simply because we only used them in test functionality. We should either add comments to explain how they work or make |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add the MIT header here (e.g. see https://github.com/aragon/aragonOS/blob/dev/contracts/acl/IACL.sol#L1)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! ;) Is there a rule of thumb on which file an MIT header should be added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to write more documentation on this, but on all the unpinned contracts (since they're the only ones meant to be used by apps, which frees them up to use any licensing model they want).