| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- 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.
- .. include:: ../examples/functions.sol
- :code: solidity
- 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.
- .. include:: ../examples/function_arguments.sol
- :code: solidity
- 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:
- .. include:: ../examples/function_destructing_arguments.sol
- :code: solidity
- 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.
- .. include:: ../examples/function_call_external.sol
- :code: solidity
- 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`.
- .. include:: ../examples/solana/function_call_external_accounts.sol
- :code: solidity
- If ``{accounts}`` is not specified, then all account are passed.
- Passing seeds with external calls on Solana
- ___________________________________________
- The Solana runtime allows you to specify the seeds to be passed for an
- external call. This is used for program derived addresses: the seeds are
- hashed with the calling program id to create program derived addresses.
- They will automatically have the signer bit set, which allows a contract to
- sign without using any private keys.
- .. include:: ../examples/solana/function_call_external_seeds.sol
- :code: solidity
- Now if the program derived address for the running program id and the seeds match the address
- ``addr`` and ``addr2``, then then the called program will run with signer and writable bits
- set for ``addr`` and ``addr2``. If they do not match, the Solana runtime will detect that
- the ``is_signer`` is set without the correct signature being provided.
- The seeds can provided in any other, which will be used to sign for multiple accounts. In the example
- above, the seed ``"test"`` is concatenated with the value of ``seed``, and that produces
- one account signature. In adition, ``"foo"`` is concatenated with ``"bar"`` to produce ``"foobar"``
- and then used to sign for another account.
- The ``seeds:`` call parameter is a slice of bytes slices; this means the literal can contain any
- number of elements, including 0 elements. The values can be ``bytes`` or anything that can be
- cast to ``bytes``.
- .. _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.
- .. include:: ../examples/substrate/function_call_external_gas.sol
- :code: solidity
- .. 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.
- Overriding function selector
- ____________________________
- When a function is called, the function selector and the arguments are serialized
- (also known as abi encoded) and passed to the program. The function selector is
- what the runtime program uses to determine what function was called. On Substrate, the
- function selector is generated using a deterministic hash value of the function
- name and the arguments types. On Solana, the selector is known as discriminator.
- The selector value can be overriden with the annotation
- ``@selector([0xde, 0xad, 0xbe, 0xa1])``.
- .. include:: ../examples/substrate/function_selector_override.sol
- :code: solidity
- The given example only works for Substrate, whose selectors are four bytes wide. On Solana, they are eight bytes wide.
- Only ``public`` and ``external`` functions have a selector, and can have their
- selector overriden. On Substrate, constructors have selectors too, so they
- can also have their selector overriden. If a function overrides another one in a
- base contract, then the selector of both must match.
- .. warning::
- On Solana, changing the selector may result in a mismatch between
- the contract metadata and the actual contract code, because the metadata does
- not explicitly store the selector.
- Use this feature carefully, as it may either break a contract or cause
- undefined behavior.
- 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:
- .. include:: ../examples/function_overloading.sol
- :code: solidity
- In the function foo, abs() is called with an ``int64`` so the second implementation
- of the function abs() is called.
- Both Substrate and Solana runtime require unique function names, so
- overloaded function names will be mangled in the ABI or the IDL.
- The function name will be concatenated with all of its argument types, separated by underscores, using the
- following rules:
- - Struct types are represented by their field types (preceded by an extra underscore).
- - Enum types are represented as their underlying ``uint8`` type.
- - Array types are recognizable by having ``Array`` appended.
- - Fixed size arrays will additionally have their length appended as well.
- The following example illustrates some overloaded functions and their mangled name:
- .. include:: ../examples/function_name_mangling.sol
- :code: solidity
- 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``.
- .. include:: ../examples/substrate/function_modifier.sol
- :code: solidity
- 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.
- On Solana, ``msg.sender`` does not exist, so the usual way to implement a similar test is using
- an `authority` accounts rather than an owner account.
- .. include:: ../examples/solana/use_authority.sol
- :code: solidity
- 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.
- .. include:: ../examples/function_modifier_arguments.sol
- :code: solidity
- 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 ``_;``.
- .. include:: ../examples/substrate/function_multiple_modifiers.sol
- :code: solidity
- Modifiers can be inherited or declared ``virtual`` in a base contract and then overriden, exactly like
- functions can be.
- .. include:: ../examples/substrate/function_override_modifiers.sol
- :code: solidity
- 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``.
- .. include:: ../examples/function_call.sol
- :code: solidity
- 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);
- }
- .. _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``.
- .. include:: ../examples/substrate/function_fallback_and_receive.sol
- :code: solidity
- .. note::
- On Solana, there is no mechanism to have some code executed if an account
- gets credited. So, `receive()` functions are not supported.
|