| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- Functions
- =========
- A function can be declared inside a contract, in which case it has access to the contracts
- contract storage variables, other contract functions etc. Functions can be also be declared outside
- a contract.
- .. code-block:: solidity
- /// get_initial_bound is called from the constructor
- function get_initial_bound() returns (uint value) {
- value = 102;
- }
- contact foo {
- uint bound = get_initial_bound();
- /** set bound for get with bound */
- function set_bound(uint _bound) public {
- bound = _bound;
- }
- /// Clamp a value within a bound.
- /// The bound can be set with set_bound().
- function get_with_bound(uint value) view public return (uint) {
- if (value < bound) {
- return value;
- } else {
- return bound;
- }
- }
- }
- Function can have any number of arguments. Function arguments may have names;
- if they do not have names then they cannot be used in the function body, but they will
- be present in the public interface.
- The return values may have names as demonstrated in the get_initial_bound() function.
- When at all of the return values have a name, then the return statement is no
- longer required at the end of a function body. In stead of returning the values
- which are provided in the return statement, the values of the return variables at the end
- of the function is returned. It is still possible to explicitly return some values
- with a return statement.
- Functions which are declared ``public`` will be present in the ABI and are callable
- externally. If a function is declared ``private`` then it is not callable externally,
- but it can be called from within the contract. If a function is defined outside a
- contract, then it cannot have a visibility specifier (e.g. ``public``).
- Any DocComment before a function will be include in the ABI. Currently only Substrate
- supports documentation in the ABI.
- Arguments passing and return values
- ___________________________________
- Function arguments can be passed either by position or by name. When they are called
- by name, arguments can be in any order. However, functions with anonymous arguments
- (arguments without name) cannot be called this way.
- .. code-block:: solidity
- contract foo {
- function bar(uint32 x, bool y) public returns (uint32) {
- if (y) {
- return 2;
- }
- return 3;
- }
- function test() public {
- uint32 a = bar(102, false);
- a = bar({ y: true, x: 302 });
- }
- }
- If the function has a single return value, this can be assigned to a variable. If
- the function has multiple return values, these can be assigned using the :ref:`destructuring`
- assignment statement:
- .. code-block:: solidity
- contract foo {
- function bar1(uint32 x, bool y) public returns (address, byte32) {
- return (address(3), hex"01020304");
- }
- function bar2(uint32 x, bool y) public returns (bool) {
- return !y;
- }
- function test() public {
- (address f1, bytes32 f2) = bar1(102, false);
- bool f3 = bar2({x: 255, y: true})
- }
- }
- It is also possible to call functions on other contracts, which is also known as calling
- external functions. The called function must be declared public.
- Calling external functions requires ABI encoding the arguments, and ABI decoding the
- return values. This much more costly than an internal function call.
- .. code-block:: solidity
- contract foo {
- function bar1(uint32 x, bool y) public returns (address, byte32) {
- return (address(3), hex"01020304");
- }
- function bar2(uint32 x, bool y) public returns (bool) {
- return !y;
- }
- }
- contract bar {
- function test(foo f) public {
- (address f1, bytes32 f2) = f.bar1(102, false);
- bool f3 = f.bar2({x: 255, y: true})
- }
- }
- The syntax for calling external call is the same as the external call, except for
- that it must be done on a contract type variable. Any error in an external call can
- be handled with :ref:`try-catch`.
- Internal calls and externals calls
- ___________________________________
- An internal function call is executed by the current contract. This
- is much more efficient than an external call, which requires the
- address of the contract to call, whose arguments must be *abi encoded* (also known
- as serialization). Then, the runtime must set up the VM for the called contract
- (the callee), decode the arguments, and encode return values. Lastly,
- the first contract (the caller) must decode return values.
- A method call done on a contract type will always be an external call.
- Note that ``this`` returns the current contract, so ``this.foo()`` will do an
- external call, which is much more expensive than ``foo()``.
- Passing accounts with external calls on Solana
- ______________________________________________
- The Solana runtime allows you the specify the accounts to be passed for an
- external call. This is specified in an array of the struct ``AccountMeta``,
- see the section on :ref:`account_meta`.
- .. code-block:: solidity
- import {AccountMeta} from 'solana';
- contract SplToken {
- address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
- address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
- struct InitializeMintInstruction {
- uint8 instruction;
- uint8 decimals;
- address mintAuthority;
- uint8 freezeAuthorityOption;
- address freezeAuthority;
- }
- function create_mint_with_freezeauthority(uint8 decimals, address mintAuthority, address freezeAuthority) public {
- InitializeMintInstruction instr = InitializeMintInstruction({
- instruction: 0,
- decimals: decimals,
- mintAuthority: mintAuthority,
- freezeAuthorityOption: 1,
- freezeAuthority: freezeAuthority
- });
- AccountMeta[2] metas = [
- AccountMeta({pubkey: instr.mintAuthority, is_writable: true, is_signer: false}),
- AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
- ];
- tokenProgramId.call{accounts: metas}(instr);
- }
- }
- If ``{accounts}`` is not specified, then all account are passed.
- .. _passing_value_gas:
- Passing value and gas with external calls
- _________________________________________
- For external calls, value can be sent along with the call. The callee must be
- ``payable``. Likewise, a gas limit can be set.
- .. code-block:: solidity
- contract foo {
- function bar() public {
- other o = new other();
- o.feh{value: 102, gas: 5000}(102);
- }
- }
- contract other {
- function feh(uint32 x) public payable {
- // ...
- }
- }
- .. note::
- The gas cannot be set on Solana for external calls.
- State mutability
- ________________
- Some functions only read contract storage (also known as *state*), and others may write
- contract storage. Functions that do not write state can be executed off-chain. Off-chain
- execution is faster, does not require write access, and does not need any balance.
- Functions that do not write state come in two flavours: ``view`` and ``pure``. ``pure``
- functions may not read state, and ``view`` functions that do read state.
- Functions that do write state come in two flavours: ``payable`` and non-payable, the
- default. Functions that are not intended to receive any value, should not be marked
- ``payable``. The compiler will check that every call does not included any value, and
- there are runtime checks as well, which cause the function to be reverted if value is
- sent.
- A constructor can be marked ``payable``, in which case value can be passed with the
- constructor.
- .. note::
- If value is sent to a non-payable function on Parity Substrate, the call will be
- reverted. However there is no refund performed, so value will remain with the callee.
- ``payable`` on constructors is not enforced on Parity Substrate. Funds are needed
- for storage rent and there is a minimum deposit needed for the contract. As a result,
- constructors always receive value on Parity Substrate.
- Function overloading
- ____________________
- Multiple functions with the same name can be declared, as long as the arguments are
- different in at least one of two ways:
- - The number of arguments must be different
- - The type of at least one of the arguments is different
- A function cannot be overloaded by changing the return types or number of returned
- values. Here is an example of an overloaded function:
- .. code-block:: solidity
- contract shape {
- int64 bar;
- function abs(int val) public returns (int) {
- if (val >= 0) {
- return val;
- } else {
- return -val;
- }
- }
- function abs(int64 val) public returns (int64) {
- if (val >= 0) {
- return val;
- } else {
- return -val;
- }
- }
- function foo(int64 x) public {
- bar = abs(x);
- }
- }
- In the function foo, abs() is called with an ``int64`` so the second implementation
- of the function abs() is called.
- Function Modifiers
- __________________
- Function modifiers are used to check pre-conditions or post-conditions for a function call. First a
- new modifier must be declared which looks much like a function, but uses the ``modifier``
- keyword rather than ``function``.
- .. code-block:: solidity
- contract example {
- address owner;
- modifier only_owner() {
- require(msg.sender == owner);
- _;
- // insert post conditions here
- }
- function foo() only_owner public {
- // ...
- }
- }
- The function `foo` can only be run by the owner of the contract, else the ``require()`` in its
- modifier will fail. The special symbol ``_;`` will be replaced by body of the function. In fact,
- if you specify ``_;`` twice, the function will execute twice, which might not be a good idea.
- A modifier cannot have visibility (e.g. ``public``) or mutability (e.g. ``view``) specified,
- since a modifier is never externally callable. Modifiers can only be used by attaching them
- to functions.
- A modifier can have arguments, just like regular functions. Here if the price is less
- than 50, `foo()` itself will never be executed, and execution will return to the caller with
- nothing done since ``_;`` is not reached in the modifier and as result foo() is never
- executed.
- .. code-block:: solidity
- contract example {
- modifier check_price(int64 price) {
- if (price >= 50) {
- _;
- }
- }
- function foo(int64 price) check_price(price) public {
- // ...
- }
- }
- Multiple modifiers can be applied to single function. The modifiers are executed in the
- order of the modifiers specified on the function declaration. Execution will continue to the next modifier
- when the ``_;`` is reached. In
- this example, the `only_owner` modifier is run first, and if that reaches ``_;``, then
- `check_price` is executed. The body of function `foo()` is only reached once `check_price()`
- reaches ``_;``.
- .. code-block:: solidity
- contract example {
- address owner;
- // a modifier with no arguments does not need "()" in its declaration
- modifier only_owner {
- require(msg.sender == owner);
- _;
- }
- modifier check_price(int64 price) {
- if (price >= 50) {
- _;
- }
- }
- function foo(int64 price) only_owner check_price(price) public {
- // ...
- }
- }
- Modifiers can be inherited or declared ``virtual`` in a base contract and then overriden, exactly like
- functions can be.
- .. code-block:: solidity
- contract base {
- address owner;
- modifier only_owner {
- require(msg.sender == owner);
- _;
- }
- modifier check_price(int64 price) virtual {
- if (price >= 10) {
- _;
- }
- }
- }
- contract example is base {
- modifier check_price(int64 price) override {
- if (price >= 50) {
- _;
- }
- }
- function foo(int64 price) only_owner check_price(price) public {
- // ...
- }
- }
- Calling an external function using ``call()``
- _____________________________________________
- If you call a function on a contract, then the function selector and any arguments
- are ABI encoded for you, and any return values are decoded. Sometimes it is useful
- to call a function without abi encoding the arguments.
- You can call a contract directly by using the ``call()`` method on the address type.
- This takes a single argument, which should be the ABI encoded arguments. The return
- values are a ``boolean`` which indicates success if true, and the ABI encoded
- return value in ``bytes``.
- .. code-block:: solidity
- contract a {
- function test() public {
- b v = new b();
- // the following four lines are equivalent to "uint32 res = v.foo(3,5);"
- // Note that the signature is only hashed and not parsed. So, ensure that the
- // arguments are of the correct type.
- bytes data = abi.encodeWithSignature("foo(uint32,uint32)", uint32(3), uint32(5));
- (bool success, bytes rawresult) = address(v).call(data);
- assert(success == true);
- uint32 res = abi.decode(rawresult, (uint32));
- assert(res == 8);
- }
- }
- contract b {
- function foo(uint32 a, uint32 b) public returns (uint32) {
- return a + b;
- }
- }
- Any value or gas limit can be specified for the external call. Note that no check is done to see
- if the called function is ``payable``, since the compiler does not know what function you are
- calling.
- .. code-block:: solidity
- function test(address foo, bytes rawcalldata) public {
- (bool success, bytes rawresult) = foo.call{value: 102, gas: 1000}(rawcalldata);
- }
- .. note::
- ewasm also supports ``staticcall()`` and ``delegatecall()`` on the address type. These
- call types are not supported on Parity Substrate.
- .. _fallback_receive:
- fallback() and receive() function
- _________________________________
- When a function is called externally, either via an transaction or when one contract
- call a function on another contract, the correct function is dispatched based on the
- function selector in the raw encoded ABI call data. If there is no match, the call
- reverts, unless there is a ``fallback()`` or ``receive()`` function defined.
- If the call comes with value, then ``receive()`` is executed, otherwise ``fallback()``
- is executed. This made clear in the declarations; ``receive()`` must be declared
- ``payable``, and ``fallback()`` must not be declared ``payable``. If a call is made
- with value and no ``receive()`` function is defined, then the call reverts, likewise if
- call is made without value and no ``fallback()`` is defined, then the call also reverts.
- Both functions must be declared ``external``.
- .. code-block:: solidity
- contract test {
- int32 bar;
- function foo(uint32 x) public {
- bar = x;
- }
- fallback() external {
- // execute if function selector does not match "foo(uint32)" and no value sent
- }
- receive() payable external {
- // execute if function selector does not match "foo(uint32)" and value sent
- }
- }
|