mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12287 from ethereum/abi.encodeCall
Implement typechecked abi.encodeCall()
This commit is contained in:
commit
835efea427
@ -1,6 +1,7 @@
|
||||
### 0.8.11 (unreleased)
|
||||
|
||||
Language Features:
|
||||
* General: New builtin function ``abi.encodeCall(functionPointer, (arg1, arg2, ...))`` that type-checks the arguments and returns the ABI-encoded function call data.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -80,6 +80,8 @@ Global Variables
|
||||
the given arguments. Note that this encoding can be ambiguous!
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes
|
||||
the given arguments starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeCall(function functionPointer, (...)) returns (bytes memory)``: ABI-encodes a call to ``functionPointer`` with the arguments found in the
|
||||
tuple. Performs a full type-check, ensuring the types match the function signature. Result equals ``abi.encodeWithSelector(functionPointer.selector, (...))``
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent
|
||||
to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``
|
||||
- ``bytes.concat(...) returns (bytes memory)``: :ref:`Concatenates variable number of
|
||||
|
@ -136,6 +136,7 @@ ABI Encoding and Decoding Functions
|
||||
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments. Note that packed encoding can be ambiguous!
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: ABI-encodes the given arguments starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)``
|
||||
- ``abi.encodeCall(function functionPointer, (...)) returns (bytes memory)``: ABI-encodes a call to ``functionPointer`` with the arguments found in the tuple. Performs a full type-check, ensuring the types match the function signature. Result equals ``abi.encodeWithSelector(functionPointer.selector, (...))``
|
||||
|
||||
.. note::
|
||||
These encoding functions can be used to craft data for external function calls without actually
|
||||
|
@ -1996,6 +1996,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
||||
_functionType->kind() == FunctionType::Kind::ABIEncode ||
|
||||
_functionType->kind() == FunctionType::Kind::ABIEncodePacked ||
|
||||
_functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
_functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
|
||||
_functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature,
|
||||
"ABI function has unexpected FunctionType::Kind."
|
||||
);
|
||||
@ -2020,6 +2021,13 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
||||
// Perform standard function call type checking
|
||||
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
||||
|
||||
// No further generic checks needed as we do a precise check for ABIEncodeCall
|
||||
if (_functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
typeCheckABIEncodeCallFunction(_functionCall);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check additional arguments for variadic functions
|
||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments();
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
@ -2078,6 +2086,110 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall)
|
||||
{
|
||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments();
|
||||
|
||||
// Expecting first argument to be the function pointer and second to be a tuple.
|
||||
if (arguments.size() != 2)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
6219_error,
|
||||
_functionCall.location(),
|
||||
"Expected two arguments: a function pointer followed by a tuple."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const functionPointerType = dynamic_cast<FunctionTypePointer>(type(*arguments.front()));
|
||||
|
||||
if (!functionPointerType)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
5511_error,
|
||||
arguments.front()->location(),
|
||||
"Expected first argument to be a function pointer, not \"" +
|
||||
type(*arguments.front())->canonicalName() +
|
||||
"\"."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionPointerType->kind() != FunctionType::Kind::External)
|
||||
{
|
||||
string msg = "Function must be \"public\" or \"external\".";
|
||||
SecondarySourceLocation ssl{};
|
||||
|
||||
if (functionPointerType->hasDeclaration())
|
||||
{
|
||||
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
||||
if (functionPointerType->declaration().scope() == m_currentContract)
|
||||
msg += " Did you forget to prefix \"this.\"?";
|
||||
}
|
||||
|
||||
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
solAssert(!functionPointerType->takesArbitraryParameters(), "Function must have fixed parameters.");
|
||||
|
||||
// Tuples with only one component become that component
|
||||
vector<ASTPointer<Expression const>> callArguments;
|
||||
|
||||
auto const* tupleType = dynamic_cast<TupleType const*>(type(*arguments[1]));
|
||||
if (tupleType)
|
||||
{
|
||||
auto const& argumentTuple = dynamic_cast<TupleExpression const&>(*arguments[1].get());
|
||||
callArguments = decltype(callArguments){argumentTuple.components().begin(), argumentTuple.components().end()};
|
||||
}
|
||||
else
|
||||
callArguments.push_back(arguments[1]);
|
||||
|
||||
if (functionPointerType->parameterTypes().size() != callArguments.size())
|
||||
{
|
||||
if (tupleType)
|
||||
m_errorReporter.typeError(
|
||||
7788_error,
|
||||
_functionCall.location(),
|
||||
"Expected " +
|
||||
to_string(functionPointerType->parameterTypes().size()) +
|
||||
" instead of " +
|
||||
to_string(callArguments.size()) +
|
||||
" components for the tuple parameter."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
7515_error,
|
||||
_functionCall.location(),
|
||||
"Expected a tuple with " +
|
||||
to_string(functionPointerType->parameterTypes().size()) +
|
||||
" components instead of a single non-tuple parameter."
|
||||
);
|
||||
}
|
||||
|
||||
// Use min() to check as much as we can before failing fatally
|
||||
size_t const numParameters = min(callArguments.size(), functionPointerType->parameterTypes().size());
|
||||
|
||||
for (size_t i = 0; i < numParameters; i++)
|
||||
{
|
||||
Type const& argType = *type(*callArguments[i]);
|
||||
BoolResult result = argType.isImplicitlyConvertibleTo(*functionPointerType->parameterTypes()[i]);
|
||||
if (!result)
|
||||
m_errorReporter.typeError(
|
||||
5407_error,
|
||||
callArguments[i]->location(),
|
||||
"Cannot implicitly convert component at position " +
|
||||
to_string(i) +
|
||||
" from \"" +
|
||||
argType.canonicalName() +
|
||||
"\" to \"" +
|
||||
functionPointerType->parameterTypes()[i]->canonicalName() +
|
||||
"\"" +
|
||||
(result.message().empty() ? "." : ": " + result.message())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::typeCheckBytesConcatFunction(
|
||||
FunctionCall const& _functionCall,
|
||||
FunctionType const* _functionType
|
||||
@ -2507,6 +2619,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
case FunctionType::Kind::ABIEncodeCall:
|
||||
{
|
||||
typeCheckABIEncodeFunctions(_functionCall, functionType);
|
||||
returnTypes = functionType->returnParameterTypes();
|
||||
|
@ -110,6 +110,9 @@ private:
|
||||
FunctionTypePointer _functionType
|
||||
);
|
||||
|
||||
/// Performs checks specific to the ABI encode functions of type ABIEncodeCall
|
||||
void typeCheckABIEncodeCallFunction(FunctionCall const& _functionCall);
|
||||
|
||||
/// Performs general checks and checks specific to bytes concat function call
|
||||
void typeCheckBytesConcatFunction(
|
||||
FunctionCall const& _functionCall,
|
||||
|
@ -367,6 +367,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{MagicType::Kind::ABI, "encode"},
|
||||
{MagicType::Kind::ABI, "encodePacked"},
|
||||
{MagicType::Kind::ABI, "encodeWithSelector"},
|
||||
{MagicType::Kind::ABI, "encodeCall"},
|
||||
{MagicType::Kind::ABI, "encodeWithSignature"},
|
||||
{MagicType::Kind::Message, "data"},
|
||||
{MagicType::Kind::Message, "sig"},
|
||||
|
@ -2935,6 +2935,7 @@ string FunctionType::richIdentifier() const
|
||||
case Kind::ABIEncode: id += "abiencode"; break;
|
||||
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
|
||||
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
|
||||
case Kind::ABIEncodeCall: id += "abiencodecall"; break;
|
||||
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
|
||||
case Kind::ABIDecode: id += "abidecode"; break;
|
||||
case Kind::MetaType: id += "metatype"; break;
|
||||
@ -3499,6 +3500,7 @@ bool FunctionType::isPure() const
|
||||
m_kind == Kind::ABIEncode ||
|
||||
m_kind == Kind::ABIEncodePacked ||
|
||||
m_kind == Kind::ABIEncodeWithSelector ||
|
||||
m_kind == Kind::ABIEncodeCall ||
|
||||
m_kind == Kind::ABIEncodeWithSignature ||
|
||||
m_kind == Kind::ABIDecode ||
|
||||
m_kind == Kind::MetaType ||
|
||||
@ -4001,6 +4003,15 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
true,
|
||||
StateMutability::Pure
|
||||
)},
|
||||
{"encodeCall", TypeProvider::function(
|
||||
TypePointers{},
|
||||
TypePointers{TypeProvider::array(DataLocation::Memory)},
|
||||
strings{},
|
||||
strings{1, ""},
|
||||
FunctionType::Kind::ABIEncodeCall,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
)},
|
||||
{"encodeWithSignature", TypeProvider::function(
|
||||
TypePointers{TypeProvider::array(DataLocation::Memory, true)},
|
||||
TypePointers{TypeProvider::array(DataLocation::Memory)},
|
||||
|
@ -1237,6 +1237,7 @@ public:
|
||||
ABIEncode,
|
||||
ABIEncodePacked,
|
||||
ABIEncodeWithSelector,
|
||||
ABIEncodeCall,
|
||||
ABIEncodeWithSignature,
|
||||
ABIDecode,
|
||||
GasLeft, ///< gasleft()
|
||||
|
@ -1236,28 +1236,47 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeCall:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
{
|
||||
bool const isPacked = function.kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
bool const hasSelectorOrSignature =
|
||||
function.kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
function.kind() == FunctionType::Kind::ABIEncodeCall ||
|
||||
function.kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||
|
||||
TypePointers argumentTypes;
|
||||
TypePointers targetTypes;
|
||||
|
||||
ASTNode::listAccept(arguments, *this);
|
||||
|
||||
if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
solAssert(arguments.size() == 2);
|
||||
|
||||
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||
solAssert(functionPtr);
|
||||
solAssert(functionPtr->sizeOnStack() == 2);
|
||||
|
||||
// Account for tuples with one component which become that component
|
||||
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
||||
argumentTypes = tupleType->components();
|
||||
else
|
||||
argumentTypes.emplace_back(arguments[1]->annotation().type);
|
||||
}
|
||||
else
|
||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
arguments[i]->accept(*this);
|
||||
// Do not keep the selector as part of the ABI encoded args
|
||||
if (!hasSelectorOrSignature || i > 0)
|
||||
argumentTypes.push_back(arguments[i]->annotation().type);
|
||||
}
|
||||
|
||||
utils().fetchFreeMemoryPointer();
|
||||
// stack now: [<selector>] <arg1> .. <argN> <free_mem>
|
||||
// stack now: [<selector/functionPointer/signature>] <arg1> .. <argN> <free_mem>
|
||||
|
||||
// adjust by 32(+4) bytes to accommodate the length(+selector)
|
||||
m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD;
|
||||
// stack now: [<selector>] <arg1> .. <argN> <data_encoding_area_start>
|
||||
// stack now: [<selector/functionPointer/signature>] <arg1> .. <argN> <data_encoding_area_start>
|
||||
|
||||
if (isPacked)
|
||||
{
|
||||
@ -1270,7 +1289,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
utils().abiEncode(argumentTypes, TypePointers());
|
||||
}
|
||||
utils().fetchFreeMemoryPointer();
|
||||
// stack: [<selector>] <data_encoding_area_end> <bytes_memory_ptr>
|
||||
// stack: [<selector/functionPointer/signature>] <data_encoding_area_end> <bytes_memory_ptr>
|
||||
|
||||
// size is end minus start minus length slot
|
||||
m_context.appendInlineAssembly(R"({
|
||||
@ -1278,16 +1297,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
})", {"mem_end", "mem_ptr"});
|
||||
m_context << Instruction::SWAP1;
|
||||
utils().storeFreeMemoryPointer();
|
||||
// stack: [<selector>] <memory ptr>
|
||||
// stack: [<selector/functionPointer/signature>] <memory ptr>
|
||||
|
||||
if (hasSelectorOrSignature)
|
||||
{
|
||||
// stack: <selector> <memory pointer>
|
||||
// stack: <selector/functionPointer/signature> <memory pointer>
|
||||
solAssert(arguments.size() >= 1, "");
|
||||
Type const* selectorType = arguments[0]->annotation().type;
|
||||
utils().moveIntoStack(selectorType->sizeOnStack());
|
||||
Type const* dataOnStack = selectorType;
|
||||
// stack: <memory pointer> <selector>
|
||||
|
||||
// stack: <memory pointer> <selector/functionPointer/signature>
|
||||
if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
{
|
||||
// hash the signature
|
||||
@ -1299,7 +1319,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
else
|
||||
{
|
||||
utils().fetchFreeMemoryPointer();
|
||||
// stack: <memory pointer> <selector> <free mem ptr>
|
||||
// stack: <memory pointer> <signature> <free mem ptr>
|
||||
utils().packedEncode(TypePointers{selectorType}, TypePointers());
|
||||
utils().toSizeAfterFreeMemoryPointer();
|
||||
m_context << Instruction::KECCAK256;
|
||||
@ -1308,10 +1328,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
dataOnStack = TypeProvider::fixedBytes(32);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (function.kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, "");
|
||||
// stack: <memory pointer> <functionPointer>
|
||||
// Extract selector from the stack
|
||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
// Conversion will be done below
|
||||
dataOnStack = TypeProvider::uint(32);
|
||||
}
|
||||
else
|
||||
solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, "");
|
||||
|
||||
utils().convertType(*dataOnStack, FixedBytesType(4), true);
|
||||
|
||||
|
@ -1104,29 +1104,57 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeCall:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
{
|
||||
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
solAssert(functionType->padArguments() != isPacked, "");
|
||||
bool const hasSelectorOrSignature =
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeCall ||
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||
|
||||
TypePointers argumentTypes;
|
||||
TypePointers targetTypes;
|
||||
vector<string> argumentVars;
|
||||
string selector;
|
||||
vector<ASTPointer<Expression const>> argumentsOfEncodeFunction;
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
solAssert(arguments.size() == 2, "");
|
||||
// Account for tuples with one component which become that component
|
||||
if (type(*arguments[1]).category() == Type::Category::Tuple)
|
||||
{
|
||||
auto const& tupleExpression = dynamic_cast<TupleExpression const&>(*arguments[1]);
|
||||
for (auto component: tupleExpression.components())
|
||||
argumentsOfEncodeFunction.push_back(component);
|
||||
}
|
||||
else
|
||||
argumentsOfEncodeFunction.push_back(arguments[1]);
|
||||
}
|
||||
else
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
// ignore selector
|
||||
if (hasSelectorOrSignature && i == 0)
|
||||
continue;
|
||||
argumentTypes.emplace_back(&type(*arguments[i]));
|
||||
targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked));
|
||||
argumentVars += IRVariable(*arguments[i]).stackSlots();
|
||||
argumentsOfEncodeFunction.push_back(arguments[i]);
|
||||
}
|
||||
|
||||
string selector;
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
for (auto const& argument: argumentsOfEncodeFunction)
|
||||
{
|
||||
argumentTypes.emplace_back(&type(*argument));
|
||||
targetTypes.emplace_back(type(*argument).fullEncodingType(false, true, isPacked));
|
||||
argumentVars += IRVariable(*argument).stackSlots();
|
||||
}
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
selector = convert(
|
||||
IRVariable(*arguments[0]).part("functionSelector"),
|
||||
*TypeProvider::fixedBytes(4)
|
||||
).name();
|
||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
{
|
||||
// hash the signature
|
||||
Type const& selectorType = type(*arguments.front());
|
||||
@ -1833,7 +1861,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
|
||||
define(_memberAccess) << requestedValue << "\n";
|
||||
}
|
||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeCall", "encodeWithSignature", "decode"}.count(member))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
@ -637,6 +637,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeCall:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
visitABIFunction(_funCall);
|
||||
break;
|
||||
@ -3041,6 +3042,7 @@ set<FunctionCall const*> SMTEncoder::collectABICalls(ASTNode const* _node)
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeCall:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
case FunctionType::Kind::ABIDecode:
|
||||
abiCalls.insert(&_funCall);
|
||||
|
@ -236,6 +236,15 @@ void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFuncti
|
||||
else
|
||||
solAssert(false, "Unexpected argument of abi.decode");
|
||||
}
|
||||
else if (t->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
// abi.encodeCall : (functionPointer, tuple_of_args_or_one_non_tuple_arg(arguments)) -> bytes
|
||||
solAssert(args.size() == 2, "Unexpected number of arguments for abi.encodeCall");
|
||||
|
||||
outTypes.emplace_back(TypeProvider::bytesMemory());
|
||||
inTypes.emplace_back(args.at(0)->annotation().type);
|
||||
inTypes.emplace_back(args.at(1)->annotation().type);
|
||||
}
|
||||
else
|
||||
{
|
||||
outTypes = returnTypes;
|
||||
|
@ -0,0 +1,51 @@
|
||||
pragma abicoder v2;
|
||||
contract C {
|
||||
type UnsignedNumber is uint256;
|
||||
enum Enum { First, Second, Third }
|
||||
|
||||
struct Struct {
|
||||
UnsignedNumber[] dynamicArray;
|
||||
uint256 justAnInt;
|
||||
string name;
|
||||
bytes someBytes;
|
||||
Enum theEnum;
|
||||
}
|
||||
|
||||
function callMeMaybe(Struct calldata _data, int256 _intVal, string memory _nameVal) external pure {
|
||||
assert(_data.dynamicArray.length == 3);
|
||||
assert(UnsignedNumber.unwrap(_data.dynamicArray[0]) == 0);
|
||||
assert(UnsignedNumber.unwrap(_data.dynamicArray[1]) == 1);
|
||||
assert(UnsignedNumber.unwrap(_data.dynamicArray[2]) == 2);
|
||||
assert(_data.justAnInt == 6);
|
||||
assert(keccak256(bytes(_data.name)) == keccak256("StructName"));
|
||||
assert(keccak256(_data.someBytes) == keccak256(bytes("1234")));
|
||||
assert(_data.theEnum == Enum.Second);
|
||||
assert(_intVal == 5);
|
||||
assert(keccak256(bytes(_nameVal)) == keccak256("TestName"));
|
||||
}
|
||||
|
||||
function callExternal() public returns (bool) {
|
||||
Struct memory structToSend;
|
||||
structToSend.dynamicArray = new UnsignedNumber[](3);
|
||||
structToSend.dynamicArray[0] = UnsignedNumber.wrap(0);
|
||||
structToSend.dynamicArray[1] = UnsignedNumber.wrap(1);
|
||||
structToSend.dynamicArray[2] = UnsignedNumber.wrap(2);
|
||||
structToSend.justAnInt = 6;
|
||||
structToSend.name = "StructName";
|
||||
structToSend.someBytes = bytes("1234");
|
||||
structToSend.theEnum = Enum.Second;
|
||||
|
||||
(bool success,) = address(this).call(abi.encodeCall(this.callMeMaybe, (
|
||||
structToSend,
|
||||
5,
|
||||
"TestName"
|
||||
)));
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// callExternal() -> true
|
@ -0,0 +1,63 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
contract C {
|
||||
bool sideEffectRan = false;
|
||||
|
||||
function(uint256, string memory) external fPointer;
|
||||
function fExternal(uint256 p, string memory t) external {}
|
||||
string xstor;
|
||||
function getExternalFunctionPointer() public returns (function(uint256, string memory) external) {
|
||||
sideEffectRan = true;
|
||||
return this.fExternal;
|
||||
}
|
||||
|
||||
function fSignatureFromLiteral() public pure returns (bytes memory) {
|
||||
return abi.encodeWithSignature("fExternal(uint256,string)", 1, "123");
|
||||
}
|
||||
function fSignatureFromLiteralCall() public view returns (bytes memory) {
|
||||
return abi.encodeCall(this.fExternal, (1, "123"));
|
||||
}
|
||||
function fSignatureFromMemory() public pure returns (bytes memory) {
|
||||
string memory x = "fExternal(uint256,string)";
|
||||
return abi.encodeWithSignature(x, 1, "123");
|
||||
}
|
||||
function fSignatureFromMemoryCall() public view returns (bytes memory) {
|
||||
return abi.encodeCall(this.fExternal, (1,"123"));
|
||||
}
|
||||
function fSignatureFromMemorys() public returns (bytes memory) {
|
||||
xstor = "fExternal(uint256,string)";
|
||||
return abi.encodeWithSignature(xstor, 1, "123");
|
||||
}
|
||||
function fPointerCall() public returns(bytes memory) {
|
||||
fPointer = this.fExternal;
|
||||
return abi.encodeCall(fPointer, (1, "123"));
|
||||
}
|
||||
function fLocalPointerCall() public returns(bytes memory) {
|
||||
function(uint256, string memory) external localFunctionPointer = this.fExternal;
|
||||
return abi.encodeCall(localFunctionPointer, (1, "123"));
|
||||
}
|
||||
function fReturnedFunctionPointer() public returns (bytes memory) {
|
||||
return abi.encodeCall(getExternalFunctionPointer(), (1, "123"));
|
||||
}
|
||||
|
||||
function assertConsistentSelectors() public {
|
||||
assert(keccak256(fSignatureFromLiteral()) == keccak256(fSignatureFromLiteralCall()));
|
||||
assert(keccak256(fSignatureFromMemory()) == keccak256(fSignatureFromMemoryCall()));
|
||||
assert(keccak256(fSignatureFromMemoryCall()) == keccak256(fSignatureFromMemorys()));
|
||||
assert(keccak256(fPointerCall()) == keccak256(fSignatureFromLiteral()));
|
||||
assert(keccak256(fLocalPointerCall()) == keccak256(fSignatureFromLiteral()));
|
||||
assert(keccak256(fReturnedFunctionPointer()) == keccak256(fSignatureFromLiteral()));
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// assertConsistentSelectors() ->
|
||||
// fSignatureFromLiteral() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fSignatureFromLiteralCall() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fSignatureFromMemory() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fSignatureFromMemoryCall() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fSignatureFromMemorys() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fPointerCall() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fLocalPointerCall() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
||||
// fReturnedFunctionPointer() -> 0x20, 0x84, 23450202028776381066253055403048136312616272755117076566855971503345107992576, 26959946667150639794667015087019630673637144422540572481103610249216, 1725436586697640946858688965569256363112777243042596638790631055949824, 86060793054017993816230018372407419485142305772921726565498526629888, 0
|
@ -0,0 +1,28 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
contract D {
|
||||
function something() external pure {}
|
||||
}
|
||||
|
||||
contract C {
|
||||
function something() external pure {}
|
||||
function test() external returns (bytes4) {
|
||||
function() external[2] memory x;
|
||||
x[0] = this.something;
|
||||
x[1] = (new D()).something;
|
||||
function() external f = x[1];
|
||||
bytes memory a = abi.encodeCall(x[0], ());
|
||||
bytes memory b = abi.encodeCall(x[1], ());
|
||||
bytes memory c = abi.encodeCall(f, ());
|
||||
assert(a.length == 4 && b.length == 4 && c.length == 4);
|
||||
assert(bytes4(a) == bytes4(b));
|
||||
assert(bytes4(a) == bytes4(c));
|
||||
assert(bytes4(a) == f.selector);
|
||||
return bytes4(a);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 0xa7a0d53700000000000000000000000000000000000000000000000000000000
|
@ -0,0 +1,48 @@
|
||||
pragma abicoder v2;
|
||||
|
||||
contract C {
|
||||
bool sideEffectRan = false;
|
||||
|
||||
function fNoArgs() external {}
|
||||
function fArray(uint[] memory x) external {}
|
||||
function fUint(uint x, uint y) external returns (uint a, uint b) {}
|
||||
|
||||
function fSignatureFromLiteralNoArgs() public pure returns (bytes memory) {
|
||||
return abi.encodeWithSignature("fNoArgs()");
|
||||
}
|
||||
function fPointerNoArgs() public view returns (bytes memory) {
|
||||
return abi.encodeCall(this.fNoArgs, ());
|
||||
}
|
||||
|
||||
function fSignatureFromLiteralArray() public pure returns (bytes memory) {
|
||||
uint[] memory x;
|
||||
return abi.encodeWithSignature("fArray(uint256[])", x);
|
||||
}
|
||||
function fPointerArray() public view returns (bytes memory) {
|
||||
uint[] memory x;
|
||||
return abi.encodeCall(this.fArray, x);
|
||||
}
|
||||
|
||||
function fSignatureFromLiteralUint() public pure returns (bytes memory) {
|
||||
return abi.encodeWithSignature("fUint(uint256,uint256)", 12, 13);
|
||||
}
|
||||
function fPointerUint() public view returns (bytes memory) {
|
||||
return abi.encodeCall(this.fUint, (12,13));
|
||||
}
|
||||
|
||||
function assertConsistentSelectors() public view {
|
||||
assert(keccak256(fSignatureFromLiteralNoArgs()) == keccak256(fPointerNoArgs()));
|
||||
assert(keccak256(fSignatureFromLiteralArray()) == keccak256(fPointerArray()));
|
||||
assert(keccak256(fSignatureFromLiteralUint()) == keccak256(fPointerUint()));
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// assertConsistentSelectors() ->
|
||||
// fSignatureFromLiteralNoArgs() -> 0x20, 0x04, 12200448252684243758085936796735499259670113115893304444050964496075123064832
|
||||
// fPointerNoArgs() -> 0x20, 4, 12200448252684243758085936796735499259670113115893304444050964496075123064832
|
||||
// fSignatureFromLiteralArray() -> 0x20, 0x44, 4612216551196396486909126966576324289294165774260092952932219511233230929920, 862718293348820473429344482784628181556388621521298319395315527974912, 0
|
||||
// fPointerArray() -> 0x20, 0x44, 4612216551196396486909126966576324289294165774260092952932219511233230929920, 862718293348820473429344482784628181556388621521298319395315527974912, 0
|
||||
// fPointerUint() -> 0x20, 0x44, 30372892641494467502622535050667754357470287521126424526399600764424271429632, 323519360005807677536004181044235568083645733070486869773243322990592, 350479306672958317330671196131255198757282877493027442254346933239808
|
||||
// fSignatureFromLiteralUint() -> 0x20, 0x44, 30372892641494467502622535050667754357470287521126424526399600764424271429632, 323519360005807677536004181044235568083645733070486869773243322990592, 350479306672958317330671196131255198757282877493027442254346933239808
|
@ -26,7 +26,6 @@ contract C {
|
||||
}
|
||||
struct S { uint a; string b; uint16 c; }
|
||||
function f4() public pure returns (bytes memory) {
|
||||
bytes4 x = 0x12345678;
|
||||
S memory s;
|
||||
s.a = 0x1234567;
|
||||
s.b = "Lorem ipsum dolor sit ethereum........";
|
||||
|
@ -0,0 +1,26 @@
|
||||
contract C {
|
||||
function callMeMaybe(uint a, uint b, uint[] memory c) external {}
|
||||
|
||||
function abiEncodeSimple(uint x, uint y, uint z, uint[] memory a, uint[] memory b) public view {
|
||||
require(x == y);
|
||||
bytes memory b1 = abi.encodeCall(this.callMeMaybe, (x, z, a));
|
||||
bytes memory b2 = abi.encodeCall(this.callMeMaybe, (y, z, a));
|
||||
assert(b1.length == b2.length);
|
||||
|
||||
bytes memory b3 = abi.encodeCall(this.callMeMaybe, (y, z, b));
|
||||
assert(b1.length == b3.length); // should fail
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// SMTEngine: all
|
||||
// SMTIgnoreCex: yes
|
||||
// ----
|
||||
// Warning 6031: (233-249): Internal error: Expression undefined for SMT solver.
|
||||
// Warning 6031: (298-314): Internal error: Expression undefined for SMT solver.
|
||||
// Warning 6031: (398-414): Internal error: Expression undefined for SMT solver.
|
||||
// Warning 1218: (330-360): CHC: Error trying to invoke SMT solver.
|
||||
// Warning 1218: (430-460): CHC: Error trying to invoke SMT solver.
|
||||
// Warning 6328: (330-360): CHC: Assertion violation might happen here.
|
||||
// Warning 6328: (430-460): CHC: Assertion violation might happen here.
|
||||
// Warning 4661: (330-360): BMC: Assertion violation happens here.
|
||||
// Warning 4661: (430-460): BMC: Assertion violation happens here.
|
82
test/libsolidity/syntaxTests/specialFunctions/encodeCall.sol
Normal file
82
test/libsolidity/syntaxTests/specialFunctions/encodeCall.sol
Normal file
@ -0,0 +1,82 @@
|
||||
interface I {
|
||||
function fExternal(uint256 p, string memory t) external;
|
||||
}
|
||||
|
||||
library L {
|
||||
function fExternal(uint256 p, string memory t) external {}
|
||||
}
|
||||
|
||||
contract C {
|
||||
using L for uint256;
|
||||
|
||||
function f(int a) public {}
|
||||
function f2(int a, string memory b) public {}
|
||||
function f3(int a, int b) public {}
|
||||
function f4() public {}
|
||||
function fInternal(uint256 p, string memory t) internal {}
|
||||
|
||||
function failFunctionArgsWrongType() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, ("test"));
|
||||
}
|
||||
function failFunctionArgsTooMany() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, (1, 2));
|
||||
}
|
||||
function failFunctionArgsTooFew0() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, ());
|
||||
}
|
||||
function failFunctionArgsTooFew1() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f);
|
||||
}
|
||||
function failFunctionPtrMissing() public returns(bytes memory) {
|
||||
return abi.encodeCall(1, this.f);
|
||||
}
|
||||
function failFunctionPtrWrongType() public returns(bytes memory) {
|
||||
return abi.encodeCall(abi.encodeCall, (1, 2, 3, "test"));
|
||||
}
|
||||
function failFunctionInternal() public returns(bytes memory) {
|
||||
return abi.encodeCall(fInternal, (1, "123"));
|
||||
}
|
||||
function failFunctionInternalFromVariable() public returns(bytes memory) {
|
||||
function(uint256, string memory) internal localFunctionPointer = fInternal;
|
||||
return abi.encodeCall(localFunctionPointer, (1, "123"));
|
||||
}
|
||||
function failFunctionArgsArrayLiteral() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f3, [1, 2]);
|
||||
}
|
||||
function failLibraryPointerCall() public returns (bytes memory) {
|
||||
return abi.encodeCall(L.fExternal, (1, "123"));
|
||||
}
|
||||
function failBoundLibraryPointerCall() public returns (bytes memory) {
|
||||
uint256 x = 1;
|
||||
return abi.encodeCall(x.fExternal, (1, "123"));
|
||||
}
|
||||
function failInterfacePointerCall() public returns (bytes memory) {
|
||||
return abi.encodeCall(I.fExternal, (1, "123"));
|
||||
}
|
||||
function successFunctionArgsIntLiteralTuple() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, (1));
|
||||
}
|
||||
function successFunctionArgsIntLiteral() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, 1);
|
||||
}
|
||||
function successFunctionArgsLiteralTuple() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f2, (1, "test"));
|
||||
}
|
||||
function successFunctionArgsEmptyTuple() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f4, ());
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 5407: (486-494): Cannot implicitly convert component at position 0 from "literal_string "test"" to "int256".
|
||||
// TypeError 7788: (576-606): Expected 1 instead of 2 components for the tuple parameter.
|
||||
// TypeError 7788: (687-713): Expected 1 instead of 0 components for the tuple parameter.
|
||||
// TypeError 6219: (794-816): Expected two arguments: a function pointer followed by a tuple.
|
||||
// TypeError 5511: (911-912): Expected first argument to be a function pointer, not "int_const 1".
|
||||
// TypeError 3509: (1018-1032): Function must be "public" or "external".
|
||||
// TypeError 3509: (1145-1154): Function must be "public" or "external". Did you forget to prefix "this."?
|
||||
// TypeError 3509: (1350-1370): Function must be "public" or "external".
|
||||
// TypeError 7515: (1469-1500): Expected a tuple with 2 components instead of a single non-tuple parameter.
|
||||
// TypeError 5407: (1493-1499): Cannot implicitly convert component at position 0 from "uint8[2]" to "int256".
|
||||
// TypeError 3509: (1596-1607): Function must be "public" or "external".
|
||||
// TypeError 3509: (1738-1749): Function must be "public" or "external".
|
||||
// TypeError 3509: (1860-1871): Function must be "public" or "external".
|
@ -0,0 +1,9 @@
|
||||
contract C {
|
||||
function f(int a, int b) public {}
|
||||
function failFunctionArgsIntLiteralNestedTuple() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, ((1,2)));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 7788: (139-170): Expected 2 instead of 1 components for the tuple parameter.
|
||||
// TypeError 5407: (163-168): Cannot implicitly convert component at position 0 from "tuple(int_const 1,int_const 2)" to "int256".
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
function f(int a) public {}
|
||||
function failFunctionArgsIntLiteralTuple() public returns(bytes memory) {
|
||||
return abi.encodeCall(this.f, (1,));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 8381: (149-153): Tuple component cannot be empty.
|
Loading…
Reference in New Issue
Block a user