-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Add abi.encode and abi.encodePacked #2980
Conversation
Needs tests with more types. |
c8c8553
to
6ad5c08
Compare
} | ||
function g(uint8 a) { | ||
bytes request = abi.encode( | ||
bytes4(keccak256(abi.encodePacked("f(uint8,string)"))), |
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.
This made me realise we could offer abi.encodeSelector(string)
as a shorthand for this. Though having it a library seems better, too bad abi
cannot be extended by libraries (yet).
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.
Yes, we do want that, but perhaps encodeWithSelector
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.
There are two options. I was going with:
abi.encode(
abi.encodeSelector("f(uint8,string)"),
a,
"He"
);
Otherwise we can have three functions with varargs:
abi.encode
abi.encodeWithSelector
(should enforce the first parameter being a string type ...)abi.encodePacked
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.
Unfortunately it does need the third version, otherwise it would be packed.
This means for encodeWithSelector
we need to enforce the first parameter to be string
(to be hashed) or bytes4
(already hashed).
} | ||
)"; | ||
compileAndRun(sourceCode, 0, "C"); | ||
ABI_CHECK(callContractFunction("g(uin8)"), encodeArgs(u256(42))); |
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.
Missing a t in uint8
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.
Also the encodeArgs
call has to be moved to an argument to callContractFunction, or, even shorter:
ABI_CHECK(callContractFunction("g(uint8)", 42), bytes());
efa36c1
to
c86fcc9
Compare
Need to add docs. |
The current API is as follows:
The reason for |
@chriseth do you agree with API proposed above? If so I should finish this PR. |
Yes, fine with the API. For |
The (so far) final API we have agreed on:
Plus raise a error if |
I would find this extremely useful; at the moment the combination of Solidity's stack size limitation and the lack of manual packed encoding is leading to a lot of inefficiencies in the Wyvern Exchange's contracts, - e.g. https://github.com/ProjectWyvern/wyvern-ethereum/blob/b31d60ae58ff251c236f9f95b499b24f26a4a78c/contracts/exchange/ExchangeCore.sol#L223 Anything I can help with to move this along? |
@axic what is still to be done here? |
Let's get this done! |
Rebased. |
Correct me if I'm wrong, but would this be serializing the bytes into the proper format and have an implicit "require" if the data cannot be formatted into the desired function signature? |
@VoR0220 the format is specified by the type of the arguments. There are some types that cannot be serialized (mappings are an example) in which case you would get a compile-time error. |
This is not relevant anymore, because we split the single overloaded function into two different functions. |
Fixed everything. |
return keccak256(uint8(1)); | ||
} | ||
function g() pure public returns (bytes) { | ||
return abi.encode(1); |
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.
This isn't tight packing? Should instead have the same 5 methods as tight_packing_literals.sol
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.
The idea was that these are the "good" cases that are variations of tight_pcaking_literals.sol
. Perhaps the naming of the file is bad.
I am sorry its still missing a test for struct encoding with I'd just do |
Ah right, that's clear then 😉 |
@axic are you talking about syntax or semantics tests for encoding structs? |
|
Ok! I did not add semantics tests because they should be the same as the regular encoder tests, but I will add some. |
syntax tests for encodePacked is in |
Sorry missed that, that should be fine. Only semantics missing then. |
Hm, and abi.encode for structs also has a semantics test. I will add structs to the encodeWithSelector and encodeWithSignature tests. |
It has a couple, but couldn't find any for structs. |
expectation = | ||
encodeArgs(0x20, 4 + 0x120) + | ||
bytes{0x7c, 0x79, 0x30, 0x02} + | ||
encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + |
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 think 0x1234567
is missing the last nibble 8
.
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.
that is s.a
, whose value is 0x1234567
.
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.
Ah right, in this test case x
is a stray value.
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.
oh yeah, do you want me to change it?
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.
If you want. It would be cleaner but I'm not adamant. Actually, lets keep it, once we have detection for side-effect free statements it will be detected :)
expectation = | ||
encodeArgs(0x20, 4 + 0x120) + | ||
bytes{0x12, 0x34, 0x56, 0x78} + | ||
encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + |
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.
Same here.
@chriseth it looks good to me. I cannot approve since I am the one opening the PR. I trust the ABI expectations are correct, but couldn't review all of them manually. |
I'm working with ERC827 standard (ethereum/EIPs#827) and there is an interesting functionality I need to implement that depends on transfer( _to, _value, bytes4(keccak256("setCounter()")).toBytes());
// The following doesn't work out.
// transfer( _to, _value, bytes4(keccak256("setCounter(uint16)"),_color).toBytes()); where toBytes() comes from the following library StringsAndBytes{
/* bytes32 (fixed-size array) to bytes (dynamically-sized array) */
function toBytes(bytes4 _bytes4) internal pure returns (bytes){
// string memory str = string(_bytes32);
// TypeError: Explicit type conversion not allowed from "bytes32" to "string storage pointer"
bytes memory bytesArray = new bytes(4);
for (uint256 i; i < 4; i++) {
bytesArray[i] = _bytes4[i];
}
return bytesArray;
}//
} And the token contract is: pragma solidity ^0.4.21;
import "ERC827Token.sol";
import "StringsAndBytes.sol";
contract SpecialToken is ERC827Token{
using StringsAndBytes for bytes4;
// Name of the token
string constant public name = "SpecialToken";
// Token abbreviation
string constant public symbol = "SpT";
// Decimal places
uint8 constant public decimals = 18;
function transferThis(address _to, uint256 _value, uint16 _color) {
transfer( _to, _value, bytes4(keccak256("setCounter()")).toBytes()); // works
_to.call(bytes4(keccak256("setCounter(uint16)")), 4); // works
// transfer( _to, _value, bytes4(keccak256("setCounter(uint16)"),_color).toBytes()); // does not work
}
}
contract ColoredCoin{
event coinColor (uint16 color);
function setCounter(){
emit coinColor(3);
}
function setCounter(uint16 _color){
emit coinColor(_color);
}
} Should I just await |
I think this PR isn't the proper location to discuss what alternatives are for a given programming task in Solidity. This feature should be part of the next release, which is out hopefully very soon (days?). However one must note, that it can easily take weeks for the frameworks (such as truffle) to pick a new version up. |
Fixes #1707. Continuation of #2851 (managed to overwrite that branch with something else)
Add the global
abi
with the following four functions:abi.encode(...) -> bytes
abi.encodePacked(...) -> bytes
abi.encodeWithSelector(bytes4, ...) -> bytes
abi.encodeWithSignature(string, ...) -> bytes
abi.encode
ABI-encodes the provided arguments according to their types.abi.encodePacked
performs packed encoding.abi.encodeWithSelector(a, x, ...)
acts likeabi.encode(x, ...)
but prepends the four bytes ina
.abi.encodeWithSignature(a, x, ...)
is identical toabi.encode(bytes4(keccak256(a)), x, ...)
.TODO: