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.
|
||||
|
||||
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: Support ``pop()`` for storage arrays.
|
||||
* 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.
|
||||
|
||||
.. index:: balance, send, transfer, call, callcode, delegatecall
|
||||
.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
|
||||
.. _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
|
||||
``<address>.delegatecall(bytes memory) returns (bool)``:
|
||||
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`.
|
||||
|
||||
|
@ -1359,7 +1359,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
|
||||
if (
|
||||
kind == FunctionType::Kind::BareCall ||
|
||||
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.");
|
||||
else if (kind == FunctionType::Kind::Send)
|
||||
@ -1754,6 +1755,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
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 =
|
||||
allowDynamicTypes ?
|
||||
functionType->returnParameterTypes() :
|
||||
@ -1834,7 +1838,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
else if (
|
||||
functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
functionType->kind() == FunctionType::Kind::BareDelegateCall
|
||||
functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||
functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||
)
|
||||
{
|
||||
if (arguments.empty())
|
||||
@ -1882,7 +1887,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
if (
|
||||
functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
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.";
|
||||
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)},
|
||||
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
|
||||
{"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)}
|
||||
};
|
||||
else
|
||||
@ -2522,6 +2523,7 @@ string FunctionType::richIdentifier() const
|
||||
case Kind::BareCall: id += "barecall"; break;
|
||||
case Kind::BareCallCode: id += "barecallcode"; break;
|
||||
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
|
||||
case Kind::BareStaticCall: id += "barestaticcall"; break;
|
||||
case Kind::Creation: id += "creation"; break;
|
||||
case Kind::Send: id += "send"; break;
|
||||
case Kind::Transfer: id += "transfer"; break;
|
||||
@ -2705,6 +2707,7 @@ unsigned FunctionType::sizeOnStack() const
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
case Kind::BareStaticCall:
|
||||
case Kind::Internal:
|
||||
case Kind::ArrayPush:
|
||||
case Kind::ArrayPop:
|
||||
@ -2772,6 +2775,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
case Kind::BareStaticCall:
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
if (m_kind == Kind::External)
|
||||
@ -2911,6 +2915,7 @@ bool FunctionType::isBareCall() const
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
case Kind::BareStaticCall:
|
||||
case Kind::ECRecover:
|
||||
case Kind::SHA256:
|
||||
case Kind::RIPEMD160:
|
||||
@ -3054,6 +3059,7 @@ bool FunctionType::padArguments() const
|
||||
case Kind::BareCall:
|
||||
case Kind::BareCallCode:
|
||||
case Kind::BareDelegateCall:
|
||||
case Kind::BareStaticCall:
|
||||
case Kind::SHA256:
|
||||
case Kind::RIPEMD160:
|
||||
case Kind::KECCAK256:
|
||||
|
@ -904,6 +904,7 @@ public:
|
||||
BareCall, ///< CALL without function hash
|
||||
BareCallCode, ///< CALLCODE without function hash
|
||||
BareDelegateCall, ///< DELEGATECALL without function hash
|
||||
BareStaticCall, ///< STATICCALL without function hash
|
||||
Creation, ///< external call using CREATE
|
||||
Send, ///< CALL, but without data and gas
|
||||
Transfer, ///< CALL, but without data and throws on error
|
||||
@ -935,7 +936,7 @@ public:
|
||||
ABIEncodeWithSelector,
|
||||
ABIEncodeWithSignature,
|
||||
ABIDecode,
|
||||
GasLeft ///< gasleft()
|
||||
GasLeft, ///< gasleft()
|
||||
};
|
||||
|
||||
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.
|
||||
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;
|
||||
Kind const& kind() const { return m_kind; }
|
||||
StateMutability stateMutability() const { return m_stateMutability; }
|
||||
@ -1090,6 +1091,7 @@ public:
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::BareStaticCall:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -571,6 +571,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::BareStaticCall:
|
||||
_functionCall.expression().accept(*this);
|
||||
appendExternalFunctionCall(function, arguments);
|
||||
break;
|
||||
@ -1172,6 +1173,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::BareStaticCall:
|
||||
case FunctionType::Kind::Transfer:
|
||||
_memberAccess.expression().accept(*this);
|
||||
m_context << funType->externalIdentifier();
|
||||
@ -1273,7 +1275,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
);
|
||||
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(
|
||||
*_memberAccess.expression().annotation().type,
|
||||
IntegerType(160, IntegerType::Modifier::Address),
|
||||
@ -1825,10 +1827,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
|
||||
|
||||
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 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();
|
||||
unsigned retSize = 0;
|
||||
|
@ -3592,6 +3592,19 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws)
|
||||
)YY";
|
||||
compileAndRun(sourceCode);
|
||||
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)
|
||||
@ -4215,6 +4228,49 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
||||
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)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@ -12216,6 +12272,19 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address)
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
ABI_CHECK(callContractFunction("f()"), 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)
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user