Docs
This commit is contained in:
parent
3ea2de8dbe
commit
ce4d1cf978
@ -94,16 +94,16 @@ To call a function in another contract you need the address to an instance of
|
|||||||
the contract. The type of the address must be a contract type, which consists
|
the contract. The type of the address must be a contract type, which consists
|
||||||
of a number of type definitions and entrypoint declarations. For instance,
|
of a number of type definitions and entrypoint declarations. For instance,
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
// A contract type
|
// A contract type
|
||||||
contract VotingType =
|
contract interface VotingType =
|
||||||
entrypoint vote : string => unit
|
entrypoint vote : string => unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Now given contract address of type `VotingType` you can call the `vote`
|
Now given contract address of type `VotingType` you can call the `vote`
|
||||||
entrypoint of that contract:
|
entrypoint of that contract:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
contract VoteTwice =
|
contract VoteTwice =
|
||||||
entrypoint voteTwice(v : VotingType, alt : string) =
|
entrypoint voteTwice(v : VotingType, alt : string) =
|
||||||
v.vote(alt)
|
v.vote(alt)
|
||||||
@ -114,7 +114,7 @@ Contract calls take two optional named arguments `gas : int` and `value : int`
|
|||||||
that lets you set a gas limit and provide tokens to a contract call. If omitted
|
that lets you set a gas limit and provide tokens to a contract call. If omitted
|
||||||
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
|
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
|
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
|
||||||
v.vote(value = fee, alt)
|
v.vote(value = fee, alt)
|
||||||
v.vote(value = fee, alt)
|
v.vote(value = fee, alt)
|
||||||
@ -136,7 +136,7 @@ To recover the underlying `address` of a contract instance there is a field
|
|||||||
`address : address`. For instance, to send tokens to the voting contract (given that it is payable)
|
`address : address`. For instance, to send tokens to the voting contract (given that it is payable)
|
||||||
without calling it you can write
|
without calling it you can write
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
entrypoint pay(v : VotingType, amount : int) =
|
entrypoint pay(v : VotingType, amount : int) =
|
||||||
Chain.spend(v.address, amount)
|
Chain.spend(v.address, amount)
|
||||||
```
|
```
|
||||||
@ -154,7 +154,7 @@ If the call fails the result is `None`, otherwise it's `Some(r)` where `r` is
|
|||||||
the return value of the call.
|
the return value of the call.
|
||||||
|
|
||||||
```sophia
|
```sophia
|
||||||
contract VotingType =
|
contract interface VotingType =
|
||||||
entrypoint : vote : string => unit
|
entrypoint : vote : string => unit
|
||||||
|
|
||||||
contract Voter =
|
contract Voter =
|
||||||
@ -171,10 +171,42 @@ However, note that errors that would normally consume all the gas in the
|
|||||||
transaction still only uses up the gas spent running the contract.
|
transaction still only uses up the gas spent running the contract.
|
||||||
|
|
||||||
|
|
||||||
|
### Contract factories and child contracts
|
||||||
|
|
||||||
|
Since the version 5.0.0 Sophia supports deploying contracts by other
|
||||||
|
contracts. This can be done in two ways:
|
||||||
|
|
||||||
|
- Contract cloning via [`Chain.clone`](sophia_stdlib.md#clone)
|
||||||
|
- Direct deploy via [`Chain.create`](sophia_stdlib.md#create)
|
||||||
|
|
||||||
|
These functions take variable number of arguments that must match the created
|
||||||
|
contract's `init` function. Beside that they take some additional named
|
||||||
|
arguments – please refer to their documentation for details.
|
||||||
|
|
||||||
|
While `Chain.clone` requires only a `contract interface` and a living instance
|
||||||
|
of a given contract on chain, `Chain.create` needs a full definition of a
|
||||||
|
to-create contract defined by the standard `contract` syntax, for example
|
||||||
|
|
||||||
|
```
|
||||||
|
contract IntHolder =
|
||||||
|
type state = int
|
||||||
|
entrypoint init(x) = x
|
||||||
|
entrypoint get() = state
|
||||||
|
|
||||||
|
main contract IntHolderFactory =
|
||||||
|
entrypoint new(x : int) : IntHolder =
|
||||||
|
let ih = Chain.create(x) : IntHolder
|
||||||
|
ih
|
||||||
|
```
|
||||||
|
|
||||||
|
In case of a presence of child contracts (`IntHolder` in this case), the main
|
||||||
|
contract must be pointed out with the `main` keyword as shown in the example.
|
||||||
|
|
||||||
|
|
||||||
### Mutable state
|
### Mutable state
|
||||||
|
|
||||||
Sophia does not have arbitrary mutable state, but only a limited form of
|
Sophia does not have arbitrary mutable state, but only a limited form of state
|
||||||
state associated with each contract instance.
|
associated with each contract instance.
|
||||||
|
|
||||||
- Each contract defines a type `state` encapsulating its mutable state.
|
- Each contract defines a type `state` encapsulating its mutable state.
|
||||||
The type `state` defaults to the `unit`.
|
The type `state` defaults to the `unit`.
|
||||||
@ -200,7 +232,7 @@ Top-level functions and entrypoints must be annotated with the
|
|||||||
`stateful` keyword to be allowed to affect the state of the running contract.
|
`stateful` keyword to be allowed to affect the state of the running contract.
|
||||||
For instance,
|
For instance,
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
stateful entrypoint set_state(s : state) =
|
stateful entrypoint set_state(s : state) =
|
||||||
put(s)
|
put(s)
|
||||||
```
|
```
|
||||||
@ -237,7 +269,7 @@ A concrete contract is by default *not* payable. Any attempt at spending to such
|
|||||||
a contract (either a `Chain.spend` or a normal spend transaction) will fail. If a
|
a contract (either a `Chain.spend` or a normal spend transaction) will fail. If a
|
||||||
contract shall be able to receive funds in this way it has to be declared `payable`:
|
contract shall be able to receive funds in this way it has to be declared `payable`:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
// A payable contract
|
// A payable contract
|
||||||
payable contract ExampleContract =
|
payable contract ExampleContract =
|
||||||
stateful entrypoint do_stuff() = ...
|
stateful entrypoint do_stuff() = ...
|
||||||
@ -253,7 +285,7 @@ A contract entrypoint is by default *not* payable. Any call to such a function
|
|||||||
that has a non-zero `value` will fail. Contract entrypoints that should be called
|
that has a non-zero `value` will fail. Contract entrypoints that should be called
|
||||||
with a non-zero value should be declared `payable`.
|
with a non-zero value should be declared `payable`.
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
payable stateful entrypoint buy(to : address) =
|
payable stateful entrypoint buy(to : address) =
|
||||||
if(Call.value > 42)
|
if(Call.value > 42)
|
||||||
transfer_item(to)
|
transfer_item(to)
|
||||||
@ -414,7 +446,7 @@ corresponding integer, so setting very high bits can be expensive).
|
|||||||
Type aliases can be introduced with the `type` keyword and can be
|
Type aliases can be introduced with the `type` keyword and can be
|
||||||
parameterized. For instance
|
parameterized. For instance
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
type number = int
|
type number = int
|
||||||
type string_map('a) = map(string, 'a)
|
type string_map('a) = map(string, 'a)
|
||||||
```
|
```
|
||||||
@ -434,7 +466,7 @@ datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)
|
|||||||
|
|
||||||
Elements of data types can be pattern matched against, using the `switch` construct:
|
Elements of data types can be pattern matched against, using the `switch` construct:
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
function get_left(x : one_or_both('a, 'b)) : option('a) =
|
function get_left(x : one_or_both('a, 'b)) : option('a) =
|
||||||
switch(x)
|
switch(x)
|
||||||
Left(x) => Some(x)
|
Left(x) => Some(x)
|
||||||
@ -443,7 +475,7 @@ function get_left(x : one_or_both('a, 'b)) : option('a) =
|
|||||||
```
|
```
|
||||||
|
|
||||||
or directly in the left-hand side:
|
or directly in the left-hand side:
|
||||||
```
|
```sophia
|
||||||
function
|
function
|
||||||
get_left : one_or_both('a, 'b) => option('a)
|
get_left : one_or_both('a, 'b) => option('a)
|
||||||
get_left(Left(x)) = Some(x)
|
get_left(Left(x)) = Some(x)
|
||||||
@ -461,7 +493,7 @@ elements of a list can be any of datatype but they must have the same
|
|||||||
type. The type of lists with elements of type `'e` is written
|
type. The type of lists with elements of type `'e` is written
|
||||||
`list('e)`. For example we can have the following lists:
|
`list('e)`. For example we can have the following lists:
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
[1, 33, 2, 666] : list(int)
|
[1, 33, 2, 666] : list(int)
|
||||||
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
|
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
|
||||||
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
|
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
|
||||||
@ -475,13 +507,13 @@ and returns the resulting list. So concatenating two lists
|
|||||||
|
|
||||||
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
|
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
|
||||||
Example syntax:
|
Example syntax:
|
||||||
```
|
```sophia
|
||||||
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
||||||
// yields [12,13,14,20,21,22,30,31,32]
|
// yields [12,13,14,20,21,22,30,31,32]
|
||||||
```
|
```
|
||||||
|
|
||||||
Lists can be constructed using the range syntax using special `..` operator:
|
Lists can be constructed using the range syntax using special `..` operator:
|
||||||
```
|
```sophia
|
||||||
[1..4] == [1,2,3,4]
|
[1..4] == [1,2,3,4]
|
||||||
```
|
```
|
||||||
The ranges are always ascending and have step equal to 1.
|
The ranges are always ascending and have step equal to 1.
|
||||||
@ -493,7 +525,7 @@ Please refer to the [standard library](sophia_stdlib.md#List) for the predefined
|
|||||||
|
|
||||||
A Sophia record type is given by a fixed set of fields with associated,
|
A Sophia record type is given by a fixed set of fields with associated,
|
||||||
possibly different, types. For instance
|
possibly different, types. For instance
|
||||||
```
|
```sophia
|
||||||
record account = { name : string,
|
record account = { name : string,
|
||||||
balance : int,
|
balance : int,
|
||||||
history : list(transaction) }
|
history : list(transaction) }
|
||||||
@ -510,12 +542,12 @@ Please refer to the [standard library](sophia_stdlib.md#Map) for the predefined
|
|||||||
|
|
||||||
A value of record type is constructed by giving a value for each of the fields.
|
A value of record type is constructed by giving a value for each of the fields.
|
||||||
For the example above,
|
For the example above,
|
||||||
```
|
```sophia
|
||||||
function new_account(name) =
|
function new_account(name) =
|
||||||
{name = name, balance = 0, history = []}
|
{name = name, balance = 0, history = []}
|
||||||
```
|
```
|
||||||
Maps are constructed similarly, with keys enclosed in square brackets
|
Maps are constructed similarly, with keys enclosed in square brackets
|
||||||
```
|
```sophia
|
||||||
function example_map() : map(string, int) =
|
function example_map() : map(string, int) =
|
||||||
{["key1"] = 1, ["key2"] = 2}
|
{["key1"] = 1, ["key2"] = 2}
|
||||||
```
|
```
|
||||||
@ -524,7 +556,7 @@ The empty map is written `{}`.
|
|||||||
#### Accessing values
|
#### Accessing values
|
||||||
|
|
||||||
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
|
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
|
||||||
```
|
```sophia
|
||||||
function get_balance(a : address, accounts : map(address, account)) =
|
function get_balance(a : address, accounts : map(address, account)) =
|
||||||
accounts[a].balance
|
accounts[a].balance
|
||||||
```
|
```
|
||||||
@ -549,14 +581,14 @@ in the map or execution fails, but a default value can be provided:
|
|||||||
`k` is not in the map.
|
`k` is not in the map.
|
||||||
|
|
||||||
Updates can be nested:
|
Updates can be nested:
|
||||||
```
|
```sophia
|
||||||
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
|
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
|
||||||
accounts{ [a].history = [] }
|
accounts{ [a].history = [] }
|
||||||
```
|
```
|
||||||
This is equivalent to `accounts{ [a] @ acc = acc{ history = [] } }` and thus
|
This is equivalent to `accounts{ [a] @ acc = acc{ history = [] } }` and thus
|
||||||
requires `a` to be present in the accounts map. To have `clear_history` create
|
requires `a` to be present in the accounts map. To have `clear_history` create
|
||||||
an account if `a` is not in the map you can write (given a function `empty_account`):
|
an account if `a` is not in the map you can write (given a function `empty_account`):
|
||||||
```
|
```sophia
|
||||||
accounts{ [a = empty_account()].history = [] }
|
accounts{ [a = empty_account()].history = [] }
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -627,7 +659,7 @@ For a functionality documentation refer to the [standard library](sophia_stdlib.
|
|||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
Example for an oracle answering questions of type `string` with answers of type `int`:
|
Example for an oracle answering questions of type `string` with answers of type `int`:
|
||||||
```
|
```sophia
|
||||||
contract Oracles =
|
contract Oracles =
|
||||||
|
|
||||||
stateful entrypoint registerOracle(acct : address,
|
stateful entrypoint registerOracle(acct : address,
|
||||||
@ -698,7 +730,7 @@ an account with address `addr`. In order to allow a contract `ct` to handle
|
|||||||
|
|
||||||
Armed with this information we can for example write a function that extends
|
Armed with this information we can for example write a function that extends
|
||||||
the name if it expires within 1000 blocks:
|
the name if it expires within 1000 blocks:
|
||||||
```
|
```sophia
|
||||||
stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =
|
stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =
|
||||||
switch(AENS.lookup(name))
|
switch(AENS.lookup(name))
|
||||||
None => ()
|
None => ()
|
||||||
@ -709,7 +741,7 @@ the name if it expires within 1000 blocks:
|
|||||||
|
|
||||||
And we can write functions that adds and removes keys from the pointers of the
|
And we can write functions that adds and removes keys from the pointers of the
|
||||||
name:
|
name:
|
||||||
```
|
```sophia
|
||||||
stateful entrypoint add_key(addr : address, name : string, key : string,
|
stateful entrypoint add_key(addr : address, name : string, key : string,
|
||||||
pt : AENS.pointee, sig : signature) =
|
pt : AENS.pointee, sig : signature) =
|
||||||
switch(AENS.lookup(name))
|
switch(AENS.lookup(name))
|
||||||
@ -768,7 +800,7 @@ The fields can appear in any order.
|
|||||||
Events are emitted by using the `Chain.event` function. The following function
|
Events are emitted by using the `Chain.event` function. The following function
|
||||||
will emit one Event of each kind in the example.
|
will emit one Event of each kind in the example.
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
entrypoint emit_events() : () =
|
entrypoint emit_events() : () =
|
||||||
Chain.event(TheFirstEvent(42))
|
Chain.event(TheFirstEvent(42))
|
||||||
Chain.event(AnotherEvent(Contract.address, "This is not indexed"))
|
Chain.event(AnotherEvent(Contract.address, "This is not indexed"))
|
||||||
@ -778,7 +810,7 @@ will emit one Event of each kind in the example.
|
|||||||
|
|
||||||
It is only possible to have one (1) `string` parameter in the event, but it can
|
It is only possible to have one (1) `string` parameter in the event, but it can
|
||||||
be placed in any position (and its value will end up in the `data` field), i.e.
|
be placed in any position (and its value will end up in the `data` field), i.e.
|
||||||
```
|
```sophia
|
||||||
AnotherEvent(string, indexed address)
|
AnotherEvent(string, indexed address)
|
||||||
|
|
||||||
...
|
...
|
||||||
@ -837,7 +869,7 @@ and `*/` and can be nested.
|
|||||||
|
|
||||||
```
|
```
|
||||||
contract elif else entrypoint false function if import include let mod namespace
|
contract elif else entrypoint false function if import include let mod namespace
|
||||||
private payable stateful switch true type record datatype
|
private payable stateful switch true type record datatype main interface
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Tokens
|
#### Tokens
|
||||||
@ -941,7 +973,7 @@ Args ::= '(' Sep(Pattern, ',') ')'
|
|||||||
Contract declarations must appear at the top-level.
|
Contract declarations must appear at the top-level.
|
||||||
|
|
||||||
For example,
|
For example,
|
||||||
```
|
```sophia
|
||||||
contract Test =
|
contract Test =
|
||||||
type t = int
|
type t = int
|
||||||
entrypoint add (x : t, y : t) = x + y
|
entrypoint add (x : t, y : t) = x + y
|
||||||
@ -1089,7 +1121,7 @@ In order of highest to lowest precedence.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
/*
|
/*
|
||||||
* A simple crowd-funding example
|
* A simple crowd-funding example
|
||||||
*/
|
*/
|
||||||
|
@ -765,6 +765,126 @@ Chain.gas_limit : int
|
|||||||
The gas limit of the current block.
|
The gas limit of the current block.
|
||||||
|
|
||||||
|
|
||||||
|
#### bytecode_hash
|
||||||
|
```
|
||||||
|
Chain.bytecode_hash : 'c => option(hash)
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns hash of the contract's bytecode (or `None` if it is nonexistent or
|
||||||
|
deployed before FATE2). The type `'c` must be instantiated with a contract.
|
||||||
|
The charged gas is affine to the size of the serialized bytecode.
|
||||||
|
|
||||||
|
|
||||||
|
#### create
|
||||||
|
```
|
||||||
|
Chain.create(value : int, ...) => 'c
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates and deploys a new instance of a contract `'c`. All of the unnamed
|
||||||
|
arguments will be passed to the `init` function. The charged gas is affine to
|
||||||
|
the size of the compiled child contract's bytecode. The `source_hash` onchain
|
||||||
|
entry of the newly created contract will be a SHA256 hash over concatenation of
|
||||||
|
|
||||||
|
- whole contract source code
|
||||||
|
- single null byte
|
||||||
|
- name of the child contract
|
||||||
|
|
||||||
|
|
||||||
|
The `value` argument (default `0`) is equivalent to the value in the contract
|
||||||
|
creation transaction – it sets the initial value of the newly created contract
|
||||||
|
charging the calling contract. Note that this won't be visible in `Call.value`
|
||||||
|
in the `init` call of the new contract. It will be included in
|
||||||
|
`Contract.balance`, however.
|
||||||
|
|
||||||
|
|
||||||
|
The type `'c` must be instantiated with a contract.
|
||||||
|
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
```
|
||||||
|
payable contract Auction =
|
||||||
|
record state = {supply: int, name: string}
|
||||||
|
entrypoint init(supply, name) = {supply: supply, name: name}
|
||||||
|
stateful payable entrypoint buy(amount) =
|
||||||
|
require(Call.value == amount, "amount_value_mismatch")
|
||||||
|
...
|
||||||
|
stateful entrypoint sell(amount) =
|
||||||
|
require(amount >= 0, "negative_amount")
|
||||||
|
...
|
||||||
|
|
||||||
|
main contract Market =
|
||||||
|
type state = list(Auction)
|
||||||
|
entrypoint init() = []
|
||||||
|
stateful entrypoint new(name : string) =
|
||||||
|
let auction = Chain.create(0, name) : Auction
|
||||||
|
put(new_auction::state)
|
||||||
|
```
|
||||||
|
|
||||||
|
The typechecker must be certain about the created contract's type, so it is
|
||||||
|
worth writing it explicitly as shown in the example.
|
||||||
|
|
||||||
|
#### clone
|
||||||
|
```
|
||||||
|
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||||
|
) => if(protected) option('c) else 'c
|
||||||
|
```
|
||||||
|
|
||||||
|
Clones the contract under the mandatory named argument `ref`. That means a new
|
||||||
|
contract of the same bytecode and the same `payable` parameter shall be created.
|
||||||
|
The resulting contract's public key can be predicted and in case it happens to
|
||||||
|
have some funds before its creation, its balance will remain or be increased by
|
||||||
|
the `value` parameter. **NOTE:** the `state` won't be copied and the contract
|
||||||
|
will be initialized with a regular call to the `init` function with the
|
||||||
|
remaining unnamed arguments. This operation is significantly cheaper than
|
||||||
|
`Chain.create` as it costs a fixed amount of gas.
|
||||||
|
|
||||||
|
|
||||||
|
The `gas` argument (default `Call.gas_left`) limits the gas supply for the
|
||||||
|
`init` call of the cloned contract.
|
||||||
|
|
||||||
|
|
||||||
|
The `value` argument (default `0`) is equivalent to the value in the contract
|
||||||
|
creation transaction – it sets the initial value of the newly created contract
|
||||||
|
charging the calling contract. Note that this won't be visible in `Call.value`
|
||||||
|
in the `init` call of the new contract. It will be included in
|
||||||
|
`Contract.balance`, however.
|
||||||
|
|
||||||
|
|
||||||
|
The `protected` argument (default `false`) works identically as in remote calls.
|
||||||
|
If set to `true` it will change the return type to `option('c)` and will catch
|
||||||
|
all errors such as `abort`, out of gas and wrong arguments. Note that it can
|
||||||
|
only take a boolean *literal*, so other expressions such as variables will be
|
||||||
|
rejected by the compiler.
|
||||||
|
|
||||||
|
|
||||||
|
The type `'c` must be instantiated with a contract.
|
||||||
|
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
payable contract interface Auction =
|
||||||
|
entrypoint init : (int, string) => void
|
||||||
|
stateful payable entrypoint buy : (int) => ()
|
||||||
|
stateful entrypoint sell : (int) => ()
|
||||||
|
|
||||||
|
main contract Market =
|
||||||
|
type state = list(Auction)
|
||||||
|
entrypoint init() = []
|
||||||
|
stateful entrypoint new_of(template : Auction, name : string) =
|
||||||
|
switch(Chain.clone(ref=template, protected=true, 0, name))
|
||||||
|
None => abort("Bad auction!")
|
||||||
|
Some(new_auction) =>
|
||||||
|
put(new_auction::state)
|
||||||
|
```
|
||||||
|
|
||||||
|
When cloning by an interface, `init` entrypoint declaration is required. It is a
|
||||||
|
good practice to set its return type to `void` in order to indicate that this
|
||||||
|
function is not supposed to be called and is state agnostic. Trivia: internal
|
||||||
|
implementation of the `init` function does not actually return `state`, but
|
||||||
|
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
||||||
|
|
||||||
|
|
||||||
#### event
|
#### event
|
||||||
```
|
```
|
||||||
Chain.event(e : event) : unit
|
Chain.event(e : event) : unit
|
||||||
|
Loading…
x
Reference in New Issue
Block a user