mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add `staticcall
to
address
`.
This commit is contained in:
parent
2ed793c4d3
commit
7ca0aaaf6f
@ -71,6 +71,7 @@ Breaking Changes:
|
|||||||
* View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode.
|
* View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode.
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Genreal: Add ``staticcall`` to ``address``.
|
||||||
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
|
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
|
||||||
* General: Support ``pop()`` for storage arrays.
|
* General: Support ``pop()`` for storage arrays.
|
||||||
* General: Scoping rules now follow the C99-style.
|
* General: Scoping rules now follow the C99-style.
|
||||||
|
@ -153,7 +153,7 @@ Mathematical and Cryptographic Functions
|
|||||||
|
|
||||||
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
|
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
|
||||||
|
|
||||||
.. index:: balance, send, transfer, call, callcode, delegatecall
|
.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
|
||||||
.. _address_related:
|
.. _address_related:
|
||||||
|
|
||||||
Address Related
|
Address Related
|
||||||
@ -171,6 +171,8 @@ Address Related
|
|||||||
issue low-level ``CALLCODE`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
|
issue low-level ``CALLCODE`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
``<address>.delegatecall(bytes memory) returns (bool)``:
|
``<address>.delegatecall(bytes memory) returns (bool)``:
|
||||||
issue low-level ``DELEGATECALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
|
issue low-level ``DELEGATECALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
|
``<address>.staticcall(bytes memory) returns (bool)``:
|
||||||
|
issue low-level ``STATICCALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
|
||||||
|
|
||||||
For more information, see the section on :ref:`address`.
|
For more information, see the section on :ref:`address`.
|
||||||
|
|
||||||
|
@ -1359,7 +1359,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
|
|||||||
if (
|
if (
|
||||||
kind == FunctionType::Kind::BareCall ||
|
kind == FunctionType::Kind::BareCall ||
|
||||||
kind == FunctionType::Kind::BareCallCode ||
|
kind == FunctionType::Kind::BareCallCode ||
|
||||||
kind == FunctionType::Kind::BareDelegateCall
|
kind == FunctionType::Kind::BareDelegateCall ||
|
||||||
|
kind == FunctionType::Kind::BareStaticCall
|
||||||
)
|
)
|
||||||
m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
|
m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
|
||||||
else if (kind == FunctionType::Kind::Send)
|
else if (kind == FunctionType::Kind::Send)
|
||||||
@ -1754,6 +1755,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall())
|
||||||
|
m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version.");
|
||||||
|
|
||||||
auto returnTypes =
|
auto returnTypes =
|
||||||
allowDynamicTypes ?
|
allowDynamicTypes ?
|
||||||
functionType->returnParameterTypes() :
|
functionType->returnParameterTypes() :
|
||||||
@ -1834,7 +1838,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
else if (
|
else if (
|
||||||
functionType->kind() == FunctionType::Kind::BareCall ||
|
functionType->kind() == FunctionType::Kind::BareCall ||
|
||||||
functionType->kind() == FunctionType::Kind::BareCallCode ||
|
functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||||
functionType->kind() == FunctionType::Kind::BareDelegateCall
|
functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||||
|
functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (arguments.empty())
|
if (arguments.empty())
|
||||||
@ -1882,7 +1887,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
if (
|
if (
|
||||||
functionType->kind() == FunctionType::Kind::BareCall ||
|
functionType->kind() == FunctionType::Kind::BareCall ||
|
||||||
functionType->kind() == FunctionType::Kind::BareCallCode ||
|
functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||||
functionType->kind() == FunctionType::Kind::BareDelegateCall
|
functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||||
|
functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||||
)
|
)
|
||||||
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
|
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
|
||||||
else if (
|
else if (
|
||||||
|
@ -621,6 +621,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
|
|||||||
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
|
||||||
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
|
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
|
||||||
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
|
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
|
||||||
|
{"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
|
||||||
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
|
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
|
||||||
};
|
};
|
||||||
else
|
else
|
||||||
@ -2522,6 +2523,7 @@ string FunctionType::richIdentifier() const
|
|||||||
case Kind::BareCall: id += "barecall"; break;
|
case Kind::BareCall: id += "barecall"; break;
|
||||||
case Kind::BareCallCode: id += "barecallcode"; break;
|
case Kind::BareCallCode: id += "barecallcode"; break;
|
||||||
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
|
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
|
||||||
|
case Kind::BareStaticCall: id += "barestaticcall"; break;
|
||||||
case Kind::Creation: id += "creation"; break;
|
case Kind::Creation: id += "creation"; break;
|
||||||
case Kind::Send: id += "send"; break;
|
case Kind::Send: id += "send"; break;
|
||||||
case Kind::Transfer: id += "transfer"; break;
|
case Kind::Transfer: id += "transfer"; break;
|
||||||
@ -2705,6 +2707,7 @@ unsigned FunctionType::sizeOnStack() const
|
|||||||
case Kind::BareCall:
|
case Kind::BareCall:
|
||||||
case Kind::BareCallCode:
|
case Kind::BareCallCode:
|
||||||
case Kind::BareDelegateCall:
|
case Kind::BareDelegateCall:
|
||||||
|
case Kind::BareStaticCall:
|
||||||
case Kind::Internal:
|
case Kind::Internal:
|
||||||
case Kind::ArrayPush:
|
case Kind::ArrayPush:
|
||||||
case Kind::ArrayPop:
|
case Kind::ArrayPop:
|
||||||
@ -2772,6 +2775,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
case Kind::BareCall:
|
case Kind::BareCall:
|
||||||
case Kind::BareCallCode:
|
case Kind::BareCallCode:
|
||||||
case Kind::BareDelegateCall:
|
case Kind::BareDelegateCall:
|
||||||
|
case Kind::BareStaticCall:
|
||||||
{
|
{
|
||||||
MemberList::MemberMap members;
|
MemberList::MemberMap members;
|
||||||
if (m_kind == Kind::External)
|
if (m_kind == Kind::External)
|
||||||
@ -2911,6 +2915,7 @@ bool FunctionType::isBareCall() const
|
|||||||
case Kind::BareCall:
|
case Kind::BareCall:
|
||||||
case Kind::BareCallCode:
|
case Kind::BareCallCode:
|
||||||
case Kind::BareDelegateCall:
|
case Kind::BareDelegateCall:
|
||||||
|
case Kind::BareStaticCall:
|
||||||
case Kind::ECRecover:
|
case Kind::ECRecover:
|
||||||
case Kind::SHA256:
|
case Kind::SHA256:
|
||||||
case Kind::RIPEMD160:
|
case Kind::RIPEMD160:
|
||||||
@ -3054,6 +3059,7 @@ bool FunctionType::padArguments() const
|
|||||||
case Kind::BareCall:
|
case Kind::BareCall:
|
||||||
case Kind::BareCallCode:
|
case Kind::BareCallCode:
|
||||||
case Kind::BareDelegateCall:
|
case Kind::BareDelegateCall:
|
||||||
|
case Kind::BareStaticCall:
|
||||||
case Kind::SHA256:
|
case Kind::SHA256:
|
||||||
case Kind::RIPEMD160:
|
case Kind::RIPEMD160:
|
||||||
case Kind::KECCAK256:
|
case Kind::KECCAK256:
|
||||||
|
@ -904,6 +904,7 @@ public:
|
|||||||
BareCall, ///< CALL without function hash
|
BareCall, ///< CALL without function hash
|
||||||
BareCallCode, ///< CALLCODE without function hash
|
BareCallCode, ///< CALLCODE without function hash
|
||||||
BareDelegateCall, ///< DELEGATECALL without function hash
|
BareDelegateCall, ///< DELEGATECALL without function hash
|
||||||
|
BareStaticCall, ///< STATICCALL without function hash
|
||||||
Creation, ///< external call using CREATE
|
Creation, ///< external call using CREATE
|
||||||
Send, ///< CALL, but without data and gas
|
Send, ///< CALL, but without data and gas
|
||||||
Transfer, ///< CALL, but without data and throws on error
|
Transfer, ///< CALL, but without data and throws on error
|
||||||
@ -935,7 +936,7 @@ public:
|
|||||||
ABIEncodeWithSelector,
|
ABIEncodeWithSelector,
|
||||||
ABIEncodeWithSignature,
|
ABIEncodeWithSignature,
|
||||||
ABIDecode,
|
ABIDecode,
|
||||||
GasLeft ///< gasleft()
|
GasLeft, ///< gasleft()
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Category category() const override { return Category::Function; }
|
virtual Category category() const override { return Category::Function; }
|
||||||
@ -1051,7 +1052,7 @@ public:
|
|||||||
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
|
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
|
||||||
bool equalExcludingStateMutability(FunctionType const& _other) const;
|
bool equalExcludingStateMutability(FunctionType const& _other) const;
|
||||||
|
|
||||||
/// @returns true if the ABI is used for this call (only meaningful for external calls)
|
/// @returns true if the ABI is NOT used for this call (only meaningful for external calls)
|
||||||
bool isBareCall() const;
|
bool isBareCall() const;
|
||||||
Kind const& kind() const { return m_kind; }
|
Kind const& kind() const { return m_kind; }
|
||||||
StateMutability stateMutability() const { return m_stateMutability; }
|
StateMutability stateMutability() const { return m_stateMutability; }
|
||||||
@ -1090,6 +1091,7 @@ public:
|
|||||||
case FunctionType::Kind::BareCall:
|
case FunctionType::Kind::BareCall:
|
||||||
case FunctionType::Kind::BareCallCode:
|
case FunctionType::Kind::BareCallCode:
|
||||||
case FunctionType::Kind::BareDelegateCall:
|
case FunctionType::Kind::BareDelegateCall:
|
||||||
|
case FunctionType::Kind::BareStaticCall:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -571,6 +571,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::BareCall:
|
case FunctionType::Kind::BareCall:
|
||||||
case FunctionType::Kind::BareCallCode:
|
case FunctionType::Kind::BareCallCode:
|
||||||
case FunctionType::Kind::BareDelegateCall:
|
case FunctionType::Kind::BareDelegateCall:
|
||||||
|
case FunctionType::Kind::BareStaticCall:
|
||||||
_functionCall.expression().accept(*this);
|
_functionCall.expression().accept(*this);
|
||||||
appendExternalFunctionCall(function, arguments);
|
appendExternalFunctionCall(function, arguments);
|
||||||
break;
|
break;
|
||||||
@ -1172,6 +1173,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
case FunctionType::Kind::BareCall:
|
case FunctionType::Kind::BareCall:
|
||||||
case FunctionType::Kind::BareCallCode:
|
case FunctionType::Kind::BareCallCode:
|
||||||
case FunctionType::Kind::BareDelegateCall:
|
case FunctionType::Kind::BareDelegateCall:
|
||||||
|
case FunctionType::Kind::BareStaticCall:
|
||||||
case FunctionType::Kind::Transfer:
|
case FunctionType::Kind::Transfer:
|
||||||
_memberAccess.expression().accept(*this);
|
_memberAccess.expression().accept(*this);
|
||||||
m_context << funType->externalIdentifier();
|
m_context << funType->externalIdentifier();
|
||||||
@ -1273,7 +1275,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
m_context << Instruction::BALANCE;
|
m_context << Instruction::BALANCE;
|
||||||
}
|
}
|
||||||
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
|
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
|
||||||
utils().convertType(
|
utils().convertType(
|
||||||
*_memberAccess.expression().annotation().type,
|
*_memberAccess.expression().annotation().type,
|
||||||
IntegerType(160, IntegerType::Modifier::Address),
|
IntegerType(160, IntegerType::Modifier::Address),
|
||||||
@ -1825,10 +1827,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
||||||
|
|
||||||
auto funKind = _functionType.kind();
|
auto funKind = _functionType.kind();
|
||||||
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
|
|
||||||
|
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||||
|
|
||||||
|
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
|
||||||
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
|
||||||
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
||||||
bool useStaticCall = _functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
|
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
|
||||||
|
|
||||||
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
|
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
|
||||||
unsigned retSize = 0;
|
unsigned retSize = 0;
|
||||||
|
@ -3592,6 +3592,19 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws)
|
|||||||
)YY";
|
)YY";
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
|
ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
|
||||||
|
|
||||||
|
if (dev::test::Options::get().evmVersion().hasStaticCall())
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"YY(
|
||||||
|
contract A {
|
||||||
|
function f() public returns (bool) {
|
||||||
|
return address(this).staticcall("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)YY";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(short_data_calls_fallback)
|
BOOST_AUTO_TEST_CASE(short_data_calls_fallback)
|
||||||
@ -4215,6 +4228,49 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
|||||||
BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11);
|
BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(generic_staticcall)
|
||||||
|
{
|
||||||
|
if (dev::test::Options::get().evmVersion().hasStaticCall())
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"**(
|
||||||
|
contract A {
|
||||||
|
uint public x;
|
||||||
|
constructor() public { x = 42; }
|
||||||
|
function pureFunction(uint256 p) public pure returns (uint256) { return p; }
|
||||||
|
function viewFunction(uint256 p) public view returns (uint256) { return p + x; }
|
||||||
|
function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; }
|
||||||
|
function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; }
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function f(address a) public view returns (bool)
|
||||||
|
{
|
||||||
|
return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23));
|
||||||
|
}
|
||||||
|
function g(address a) public view returns (bool)
|
||||||
|
{
|
||||||
|
return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23));
|
||||||
|
}
|
||||||
|
function h(address a) public view returns (bool)
|
||||||
|
{
|
||||||
|
return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23));
|
||||||
|
}
|
||||||
|
function i(address a, uint256 v) public view returns (bool)
|
||||||
|
{
|
||||||
|
return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)**";
|
||||||
|
compileAndRun(sourceCode, 0, "A");
|
||||||
|
u160 const c_addressA = m_contractAddress;
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true));
|
||||||
|
ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true));
|
||||||
|
ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false));
|
||||||
|
ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true));
|
||||||
|
ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(library_call_in_homestead)
|
BOOST_AUTO_TEST_CASE(library_call_in_homestead)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -12216,6 +12272,19 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address)
|
|||||||
compileAndRun(sourceCode, 0, "C");
|
compileAndRun(sourceCode, 0, "C");
|
||||||
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1)));
|
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1)));
|
||||||
ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1)));
|
ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1)));
|
||||||
|
|
||||||
|
if (dev::test::Options::get().evmVersion().hasStaticCall())
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"YY(
|
||||||
|
contract C {
|
||||||
|
function f() external returns (bool) {
|
||||||
|
return address(0x4242).staticcall("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)YY";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(delegatecall_return_value)
|
BOOST_AUTO_TEST_CASE(delegatecall_return_value)
|
||||||
|
@ -433,6 +433,22 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(address_staticcall)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f() public view returns(bool) {
|
||||||
|
return address(0x4242).staticcall("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (dev::test::Options::get().evmVersion().hasStaticCall())
|
||||||
|
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
|
||||||
|
else
|
||||||
|
CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version.");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user