mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add abi.decode(bytes data, (...))
This commit is contained in:
parent
3c5226cefb
commit
9328ea4c3c
@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
|
||||||
|
{
|
||||||
|
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
|
||||||
|
if (arguments.size() != 2)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_functionCall.location(),
|
||||||
|
"This function takes two arguments, but " +
|
||||||
|
toString(arguments.size()) +
|
||||||
|
" were provided."
|
||||||
|
);
|
||||||
|
if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
arguments.front()->location(),
|
||||||
|
"Invalid type for argument in function call. "
|
||||||
|
"Invalid implicit conversion from " +
|
||||||
|
type(*arguments.front())->toString() +
|
||||||
|
" to bytes memory requested."
|
||||||
|
);
|
||||||
|
|
||||||
|
TypePointer returnType = make_shared<TupleType>();
|
||||||
|
|
||||||
|
if (arguments.size() < 2)
|
||||||
|
return returnType;
|
||||||
|
|
||||||
|
// The following is a rather syntactic restriction, but we check it here anyway:
|
||||||
|
// The second argument has to be a tuple expression containing type names.
|
||||||
|
TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get());
|
||||||
|
if (!tupleExpression)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
arguments[1]->location(),
|
||||||
|
"The second argument to \"abi.decode\" has to be a tuple of types."
|
||||||
|
);
|
||||||
|
return returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<TypePointer> components;
|
||||||
|
for (auto const& typeArgument: tupleExpression->components())
|
||||||
|
{
|
||||||
|
solAssert(typeArgument, "");
|
||||||
|
if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get()))
|
||||||
|
{
|
||||||
|
TypePointer actualType = argTypeType->actualType();
|
||||||
|
solAssert(actualType, "");
|
||||||
|
// We force memory because the parser currently cannot handle
|
||||||
|
// data locations. Furthermore, storage can be a little dangerous and
|
||||||
|
// calldata is not really implemented anyway.
|
||||||
|
actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
|
||||||
|
solAssert(
|
||||||
|
!actualType->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
!actualType->dataStoredIn(DataLocation::Storage),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
if (!actualType->fullEncodingType(false, _abiEncoderV2, false))
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
typeArgument->location(),
|
||||||
|
"Decoding type " + actualType->toString(false) + " not supported."
|
||||||
|
);
|
||||||
|
components.push_back(actualType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name.");
|
||||||
|
components.push_back(make_shared<TupleType>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return make_shared<TupleType>(components);
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
||||||
{
|
{
|
||||||
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
||||||
@ -1727,7 +1796,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
|
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
|
||||||
|
|
||||||
|
if (functionType->kind() == FunctionType::Kind::ABIDecode)
|
||||||
|
_functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
|
||||||
|
else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
|
||||||
{
|
{
|
||||||
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
|
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
@ -1782,8 +1855,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
else if (isPositionalCall)
|
else if (isPositionalCall)
|
||||||
{
|
{
|
||||||
bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < arguments.size(); ++i)
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
auto const& argType = type(*arguments[i]);
|
auto const& argType = type(*arguments[i]);
|
||||||
@ -1796,7 +1867,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
|
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
|
||||||
errored = true;
|
errored = true;
|
||||||
}
|
}
|
||||||
if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
|
if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments()))
|
||||||
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
|
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
|
||||||
}
|
}
|
||||||
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
||||||
|
@ -91,6 +91,11 @@ private:
|
|||||||
// and reports an error, if not.
|
// and reports an error, if not.
|
||||||
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
|
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
|
||||||
|
|
||||||
|
/// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
|
||||||
|
/// return type (which is basically the second argument) if successful. It returns
|
||||||
|
/// the empty tuple type or error.
|
||||||
|
TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
|
||||||
|
|
||||||
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
|
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
|
||||||
virtual void endVisit(UsingForDirective const& _usingFor) override;
|
virtual void endVisit(UsingForDirective const& _usingFor) override;
|
||||||
virtual bool visit(StructDefinition const& _struct) override;
|
virtual bool visit(StructDefinition const& _struct) override;
|
||||||
|
@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
// we can ignore the kind of magic and only look at the name of the member
|
// we can ignore the kind of magic and only look at the name of the member
|
||||||
set<string> static const pureMembers{
|
set<string> static const pureMembers{
|
||||||
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
|
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
|
||||||
};
|
};
|
||||||
if (!pureMembers.count(member))
|
if (!pureMembers.count(member))
|
||||||
mutability = StateMutability::View;
|
mutability = StateMutability::View;
|
||||||
|
@ -2553,6 +2553,7 @@ string FunctionType::richIdentifier() const
|
|||||||
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
|
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
|
||||||
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
|
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
|
||||||
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
|
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
|
||||||
|
case Kind::ABIDecode: id += "abidecode"; break;
|
||||||
default: solAssert(false, "Unknown function location."); break;
|
default: solAssert(false, "Unknown function location."); break;
|
||||||
}
|
}
|
||||||
id += "_" + stateMutabilityToString(m_stateMutability);
|
id += "_" + stateMutabilityToString(m_stateMutability);
|
||||||
@ -2959,7 +2960,8 @@ bool FunctionType::isPure() const
|
|||||||
m_kind == Kind::ABIEncode ||
|
m_kind == Kind::ABIEncode ||
|
||||||
m_kind == Kind::ABIEncodePacked ||
|
m_kind == Kind::ABIEncodePacked ||
|
||||||
m_kind == Kind::ABIEncodeWithSelector ||
|
m_kind == Kind::ABIEncodeWithSelector ||
|
||||||
m_kind == Kind::ABIEncodeWithSignature;
|
m_kind == Kind::ABIEncodeWithSignature ||
|
||||||
|
m_kind == Kind::ABIDecode;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
||||||
@ -3315,6 +3317,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
|||||||
FunctionType::Kind::ABIEncodeWithSignature,
|
FunctionType::Kind::ABIEncodeWithSignature,
|
||||||
true,
|
true,
|
||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
|
)},
|
||||||
|
{"decode", make_shared<FunctionType>(
|
||||||
|
TypePointers(),
|
||||||
|
TypePointers(),
|
||||||
|
strings{},
|
||||||
|
strings{},
|
||||||
|
FunctionType::Kind::ABIDecode,
|
||||||
|
true,
|
||||||
|
StateMutability::Pure
|
||||||
)}
|
)}
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
|
@ -934,6 +934,7 @@ public:
|
|||||||
ABIEncodePacked,
|
ABIEncodePacked,
|
||||||
ABIEncodeWithSelector,
|
ABIEncodeWithSelector,
|
||||||
ABIEncodeWithSignature,
|
ABIEncodeWithSignature,
|
||||||
|
ABIDecode,
|
||||||
GasLeft ///< gasleft()
|
GasLeft ///< gasleft()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1070,6 +1070,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// stack now: <memory pointer>
|
// stack now: <memory pointer>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::ABIDecode:
|
||||||
|
{
|
||||||
|
arguments.front()->accept(*this);
|
||||||
|
TypePointer firstArgType = arguments.front()->annotation().type;
|
||||||
|
TypePointers const& targetTypes = dynamic_cast<TupleType const&>(*_functionCall.annotation().type).components();
|
||||||
|
if (
|
||||||
|
*firstArgType == ArrayType(DataLocation::CallData) ||
|
||||||
|
*firstArgType == ArrayType(DataLocation::CallData, true)
|
||||||
|
)
|
||||||
|
utils().abiDecode(targetTypes, false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
utils().convertType(*firstArgType, ArrayType(DataLocation::Memory));
|
||||||
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
|
m_context << Instruction::SWAP1 << Instruction::MLOAD;
|
||||||
|
// stack now: <mem_pos> <length>
|
||||||
|
|
||||||
|
utils().abiDecode(targetTypes, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case FunctionType::Kind::GasLeft:
|
case FunctionType::Kind::GasLeft:
|
||||||
m_context << Instruction::GAS;
|
m_context << Instruction::GAS;
|
||||||
break;
|
break;
|
||||||
|
@ -13195,6 +13195,203 @@ BOOST_AUTO_TEST_CASE(senders_balance)
|
|||||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27)));
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_trivial)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(bytes memory data) public pure returns (uint) {
|
||||||
|
return abi.decode(data, (uint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20, 33), encodeArgs(u256(33)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_encode_decode_simple)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"XX(
|
||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint, bytes memory) {
|
||||||
|
bytes memory arg = "abcdefg";
|
||||||
|
return abi.decode(abi.encode(uint(33), arg), (uint, bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)XX";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f()"),
|
||||||
|
encodeArgs(33, 0x40, 7, "abcdefg")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_simple)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(bytes memory data) public pure returns (uint, bytes memory) {
|
||||||
|
return abi.decode(data, (uint, bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
|
||||||
|
encodeArgs(33, 0x40, 7, "abcdefg")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_v2)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; uint[] b; }
|
||||||
|
function f() public pure returns (S memory) {
|
||||||
|
S memory s;
|
||||||
|
s.a = 8;
|
||||||
|
s.b = new uint[](3);
|
||||||
|
s.b[0] = 9;
|
||||||
|
s.b[1] = 10;
|
||||||
|
s.b[2] = 11;
|
||||||
|
return abi.decode(abi.encode(s), (S));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f()"),
|
||||||
|
encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_simple_storage)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
bytes data;
|
||||||
|
function f(bytes memory _data) public returns (uint, bytes memory) {
|
||||||
|
data = _data;
|
||||||
|
return abi.decode(data, (uint, bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
|
||||||
|
encodeArgs(33, 0x40, 7, "abcdefg")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_v2_storage)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
bytes data;
|
||||||
|
struct S { uint a; uint[] b; }
|
||||||
|
function f() public returns (S memory) {
|
||||||
|
S memory s;
|
||||||
|
s.a = 8;
|
||||||
|
s.b = new uint[](3);
|
||||||
|
s.b[0] = 9;
|
||||||
|
s.b[1] = 10;
|
||||||
|
s.b[2] = 11;
|
||||||
|
data = abi.encode(s);
|
||||||
|
return abi.decode(data, (S));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f()"),
|
||||||
|
encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_calldata)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata data) external pure returns (uint, bytes memory r) {
|
||||||
|
return abi.decode(data, (uint, bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
|
||||||
|
encodeArgs(33, 0x40, 7, "abcdefg")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_v2_calldata)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
struct S { uint a; uint[] b; }
|
||||||
|
function f(bytes calldata data) external pure returns (S memory) {
|
||||||
|
return abi.decode(data, (S));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 0x20 * 7, 0x20, 33, 0x40, 3, 10, 11, 12),
|
||||||
|
encodeArgs(0x20, 33, 0x40, 3, 10, 11, 12)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_static_array)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata data) external pure returns (uint[2][3] memory) {
|
||||||
|
return abi.decode(data, (uint[2][3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6),
|
||||||
|
encodeArgs(1, 2, 3, 4, 5, 6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_static_array_v2)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata data) external pure returns (uint[2][3] memory) {
|
||||||
|
return abi.decode(data, (uint[2][3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6),
|
||||||
|
encodeArgs(1, 2, 3, 4, 5, 6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(abi_decode_dynamic_array)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata data) external pure returns (uint[] memory) {
|
||||||
|
return abi.decode(data, (uint[]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(
|
||||||
|
callContractFunction("f(bytes)", 0x20, 6 * 0x20, 0x20, 4, 3, 4, 5, 6),
|
||||||
|
encodeArgs(0x20, 4, 3, 4, 5, 6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(write_storage_external)
|
BOOST_AUTO_TEST_CASE(write_storage_external)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
// This restriction might be lifted in the future
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
abi.decode("abc", (bytes calldata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (121-129): Expected ',' but got 'calldata'
|
@ -0,0 +1,12 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
abi.decode();
|
||||||
|
abi.decode(msg.data);
|
||||||
|
abi.decode(msg.data, uint, uint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (46-58): This function takes two arguments, but 0 were provided.
|
||||||
|
// TypeError: (64-84): This function takes two arguments, but 1 were provided.
|
||||||
|
// TypeError: (90-122): This function takes two arguments, but 3 were provided.
|
||||||
|
// TypeError: (111-115): The second argument to "abi.decode" has to be a tuple of types.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
abi.decode("abc", (bytes memory, uint[][2] memory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (71-77): Expected ',' but got 'memory'
|
@ -0,0 +1,10 @@
|
|||||||
|
pragma experimental "ABIEncoderV2";
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S { uint x; uint[] b; }
|
||||||
|
function f() public pure returns (S memory, bytes memory, uint[][2] memory) {
|
||||||
|
return abi.decode("abc", (S, bytes, uint[][2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (0-35): Experimental features are turned on. Do not use experimental features on live deployments.
|
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
abi.decode("abc", uint);
|
||||||
|
abi.decode("abc", this);
|
||||||
|
abi.decode("abc", f());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (64-68): The second argument to "abi.decode" has to be a tuple of types.
|
||||||
|
// TypeError: (93-97): The second argument to "abi.decode" has to be a tuple of types.
|
||||||
|
// TypeError: (122-125): The second argument to "abi.decode" has to be a tuple of types.
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint, bytes32, C) {
|
||||||
|
return abi.decode("abc", (uint, bytes32, C));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
return abi.decode("abc", (uint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,8 @@
|
|||||||
|
// This restriction might be lifted in the future
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
abi.decode("abc", (bytes storage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (109-116): Expected ',' but got 'storage'
|
Loading…
Reference in New Issue
Block a user