diff --git a/master/CHANGELOG/index.html b/master/CHANGELOG/index.html
index 5fc4af9..5456913 100644
--- a/master/CHANGELOG/index.html
+++ b/master/CHANGELOG/index.html
@@ -1307,8 +1307,7 @@ and this project adheres to Semant
There are also convenience functions
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
-
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
Changed
@@ -1326,14 +1325,12 @@ and this project adheres to Semant
AENS.name, AENS.pointee, Chain.ttl, Chain.base_tx, Chain.ga_meta_tx, Chain.paying_for_tx
) to
the calldata and result decoderswitch(x)
- a::[] | a > 10 => 1
- _ => 2
-
function
- f(a::[]) | a > 10 = 1
- f(_) = 2
-
switch(x)
+ a::[] | a > 10 => 1
+ _ => 2
+ function
+ f(a::[]) | a > 10 = 1
+ f(_) = 2
Changed
split
, concat
, to_upper
,
@@ -1413,14 +1409,13 @@ contract C =
transaction and the transaction hash is available Auth.tx
, it is only
available during authentication if invoked by a normal contract call
it returns None
. Example:
- switch(Auth.tx)
- None => abort("Not in Auth context")
- Some(tx0) =>
- switch(tx0.tx)
- Chain.SpendTx(_, amount, _) => amount > 400
- Chain.ContractCallTx(_, _) => true
- _ => false
-
switch(Auth.tx)
+ None => abort("Not in Auth context")
+ Some(tx0) =>
+ switch(tx0.tx)
+ Chain.SpendTx(_, amount, _) => amount > 400
+ Chain.ContractCallTx(_, _) => true
+ _ => false
- A debug mode is a added to the compiler. Right now its only use is to
turn off hermetization.
function
- length : list('a) => int
- length([]) = 0
- length(x :: xs) = 1 + length(xs)
-
function
+ length : list('a) => int
+ length([]) = 0
+ length(x :: xs) = 1 + length(xs)
function somes(xs : list(option('a))) : list('a) =
- [ x | Some(x) <- xs ]
-
function somes(xs : list(option('a))) : list('a) =
+ [ x | Some(x) <- xs ]
function test(m : map(int, int)) =
- let Some(x) = Map.lookup(m, 0)
- x
-
function test(m : map(int, int)) =
+ let Some(x) = Map.lookup(m, 0)
+ x
[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]
-
[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]
payable
is introduced. Contracts, and enpoints,
that shall be able to receive funds should be marked as payable. Address.is_payable(a)
can be used to check if an (contract) address is payable or not.require : (bool, string) => ()
. Defined as
- function require(b, err) = if(!b) abort(err)
-
function require(b, err) = if(!b) abort(err)
Bytes.to_str : bytes(_) => string
-Bytes.to_int : bytes(_) => int
-
Bytes.to_str : bytes(_) => string
+ Bytes.to_int : bytes(_) => int
for converting a byte array to a hex string and interpreting it as a
big-endian encoded integer respectively.contract Example =
- // Exported
- entrypoint exported_fun(x) = local_fun(x)
- // Not exported
- function local_fun(x) = x
-
contract Example =
+ // Exported
+ entrypoint exported_fun(x) = local_fun(x)
+ // Not exported
+ function local_fun(x) = x
Functions in namespaces still use function
(and private function
for
private functions).Chain.block_hash(height)
has changed, it used to
diff --git a/master/aeso_aci/index.html b/master/aeso_aci/index.html
index cff3577..c45c799 100644
--- a/master/aeso_aci/index.html
+++ b/master/aeso_aci/index.html
@@ -394,81 +394,80 @@
Sophia contracts and a suitable JSON encoding of contract
interface. As yet the interface is very basic.
Encoding this contract:
-contract Answers =
+```
+contract Answers =
record state = { a : answers }
- type answers() = map(string, int)
-
- stateful function init() = { a = {} }
+ type answers() = map(string, int)
+stateful function init() = { a = {} }
private function the_answer() = 42
function new_answer(q : string, a : int) : answers() = { [q] = a }
-
generates the following JSON structure representing the contract interface:
-{
- "contract": {
- "functions": [
- {
- "arguments": [],
- "name": "init",
- "returns": "Answers.state",
- "stateful": true
- },
- {
- "arguments": [
- {
- "name": "q",
- "type": "string"
- },
- {
- "name": "a",
- "type": "int"
- }
- ],
- "name": "new_answer",
- "returns": {
- "map": [
- "string",
- "int"
- ]
- },
- "stateful": false
- }
- ],
- "name": "Answers",
- "state": {
- "record": [
- {
- "name": "a",
- "type": "Answers.answers"
- }
- ]
- },
- "type_defs": [
- {
- "name": "answers",
- "typedef": {
- "map": [
- "string",
- "int"
- ]
- },
- "vars": []
- }
- ]
- }
-}
-
json
+{
+ "contract": {
+ "functions": [
+ {
+ "arguments": [],
+ "name": "init",
+ "returns": "Answers.state",
+ "stateful": true
+ },
+ {
+ "arguments": [
+ {
+ "name": "q",
+ "type": "string"
+ },
+ {
+ "name": "a",
+ "type": "int"
+ }
+ ],
+ "name": "new_answer",
+ "returns": {
+ "map": [
+ "string",
+ "int"
+ ]
+ },
+ "stateful": false
+ }
+ ],
+ "name": "Answers",
+ "state": {
+ "record": [
+ {
+ "name": "a",
+ "type": "Answers.answers"
+ }
+ ]
+ },
+ "type_defs": [
+ {
+ "name": "answers",
+ "typedef": {
+ "map": [
+ "string",
+ "int"
+ ]
+ },
+ "vars": []
+ }
+ ]
+ }
+}
When that encoding is decoded the following include definition is generated:
-contract Answers =
+contract Answers =
record state = {a : Answers.answers}
type answers = map(string, int)
function init : () => Answers.state
- function new_answer : (string, int) => map(string, int)
-
-type aci_type() :: json | string.
--type json() :: jsx:json_term().
--type json_text() :: binary().
-
erlang
+-type aci_type() :: json | string.
+-type json() :: jsx:json_term().
+-type json_text() :: binary().
Generate the JSON encoding of the interface to a contract. The type definitions
@@ -482,36 +481,36 @@ called aci_test.aes
contains the contract in the description from w
want to generate files aci_test.json
which is the JSON encoding of the
contract interface and aci_test.include
which is the contract definition to
be included inside another contract.
1> {ok,Contract} = file:read_file("aci_test.aes").
-{ok,<<"contract Answers =\n record state = { a : answers }\n type answers() = map(string, int)\n\n stateful function"...>>}
-2> {ok,JsonACI} = aeso_aci:contract_interface(json, Contract).
-{ok,[#{contract =>
- #{functions =>
- [#{arguments => [],name => <<"init">>,
- returns => <<"Answers.state">>,stateful => true},
- #{arguments =>
- [#{name => <<"q">>,type => <<"string">>},
- #{name => <<"a">>,type => <<"int">>}],
- name => <<"new_answer">>,
- returns => #{<<"map">> => [<<"string">>,<<"int">>]},
- stateful => false}],
- name => <<"Answers">>,
- state =>
- #{record =>
- [#{name => <<"a">>,type => <<"Answers.answers">>}]},
- type_defs =>
- [#{name => <<"answers">>,
- typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
- vars => []}]}}]}
-3> file:write_file("aci_test.aci", jsx:encode(JsonACI)).
-ok
-4> {ok,InterfaceStub} = aeso_aci:render_aci_json(JsonACI).
-{ok,<<"contract Answers =\n record state = {a : Answers.answers}\n type answers = map(string, int)\n function init "...>>}
-5> file:write_file("aci_test.include", InterfaceStub).
-ok
-6> jsx:prettify(jsx:encode(JsonACI)).
-<<"[\n {\n \"contract\": {\n \"functions\": [\n {\n \"arguments\": [],\n \"name\": \"init\",\n "...>>
-
erlang
+1> {ok,Contract} = file:read_file("aci_test.aes").
+{ok,<<"contract Answers =\n record state = { a : answers }\n type answers() = map(string, int)\n\n stateful function"...>>}
+2> {ok,JsonACI} = aeso_aci:contract_interface(json, Contract).
+{ok,[#{contract =>
+ #{functions =>
+ [#{arguments => [],name => <<"init">>,
+ returns => <<"Answers.state">>,stateful => true},
+ #{arguments =>
+ [#{name => <<"q">>,type => <<"string">>},
+ #{name => <<"a">>,type => <<"int">>}],
+ name => <<"new_answer">>,
+ returns => #{<<"map">> => [<<"string">>,<<"int">>]},
+ stateful => false}],
+ name => <<"Answers">>,
+ state =>
+ #{record =>
+ [#{name => <<"a">>,type => <<"Answers.answers">>}]},
+ type_defs =>
+ [#{name => <<"answers">>,
+ typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
+ vars => []}]}}]}
+3> file:write_file("aci_test.aci", jsx:encode(JsonACI)).
+ok
+4> {ok,InterfaceStub} = aeso_aci:render_aci_json(JsonACI).
+{ok,<<"contract Answers =\n record state = {a : Answers.answers}\n type answers = map(string, int)\n function init "...>>}
+5> file:write_file("aci_test.include", InterfaceStub).
+ok
+6> jsx:prettify(jsx:encode(JsonACI)).
+<<"[\n {\n \"contract\": {\n \"functions\": [\n {\n \"arguments\": [],\n \"name\": \"init\",\n "...>>
The final call to jsx:prettify(jsx:encode(JsonACI))
returns the encoding in a
more easily readable form. This is what is shown in the description above.
This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded.
contract_string() = string() | binary()
-contract_map() = #{bytecode => binary(),
- compiler_version => binary(),
- contract_souce => string(),
- type_info => type_info()}
-type_info()
-errorstring() = binary()
-
erlang
+contract_string() = string() | binary()
+contract_map() = #{bytecode => binary(),
+ compiler_version => binary(),
+ contract_souce => string(),
+ type_info => type_info()}
+type_info()
+errorstring() = binary()
Types
-ContractString = contract_string()
-Options = [Option]
-CompRet = {ok,ContractMap} | {error,ErrorString}
-ContractMap = contract_map()
-ErrorString = errorstring()
-
erlang
+ContractString = contract_string()
+Options = [Option]
+CompRet = {ok,ContractMap} | {error,ErrorString}
+ContractMap = contract_map()
+ErrorString = errorstring()
Compile a contract defined in a file or in a string.
The pp_ options all print to standard output the following:
pp_sophia_code
- print the input Sophia code.
pp_bytecode
- print the bytecode instructions
Types -
ContractString = string() | binary()
+ContractString = string() | binary()
CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}
Types = [Type]
-Type = term()
-
__call
function.
Types -
{ok,TypeRep} | {error, badtype}
-
erlang
+ {ok,TypeRep} | {error, badtype}
Get the type representation of a type declaration.
Types
-Version = binary()
-
erlang
+Version = binary()
Get the current version of the Sophia compiler.
diff --git a/master/search/search_index.json b/master/search/search_index.json index fd24661..b73188d 100644 --- a/master/search/search_index.json +++ b/master/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Introduction Sophia is a functional language designed for smart contract development. It is strongly typed and has restricted mutable state. Sophia is customized for smart contracts, which can be published to a blockchain. Thus some features of conventional languages, such as floating point arithmetic, are not present in Sophia, and some \u00e6ternity blockchain specific primitives, constructions and types have been added. Note For rapid prototyping of smart contracts check out AEstudio ! For playing around and diving deeper into the language itself check out the REPL !","title":"Introduction"},{"location":"#introduction","text":"Sophia is a functional language designed for smart contract development. It is strongly typed and has restricted mutable state. Sophia is customized for smart contracts, which can be published to a blockchain. Thus some features of conventional languages, such as floating point arithmetic, are not present in Sophia, and some \u00e6ternity blockchain specific primitives, constructions and types have been added. Note For rapid prototyping of smart contracts check out AEstudio ! For playing around and diving deeper into the language itself check out the REPL !","title":"Introduction"},{"location":"CHANGELOG/","text":"Changelog All notable changes to this project will be documented in this file. The format is based on Keep a Changelog , and this project adheres to Semantic Versioning . Unreleased Added Compiler warnings for the follwing: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value. The pipe operator |> [1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3])) Changed Error messages have been restructured (less newlines) to provide more unified errors. Also pp_oneline/1 has been added. Removed 6.1.0 - 2021-10-20 Added Bitwise stdlib Set stdlib Option.force_msg Loading namespaces into the current scope (e.g. using Pair ) Assign patterns to variables (e.g. let x::(t = y::_) = [1, 2, 3, 4] where t == [2, 3, 4] ) Add builtin types ( AENS.name, AENS.pointee, Chain.ttl, Chain.base_tx, Chain.ga_meta_tx, Chain.paying_for_tx ) to the calldata and result decoder Patterns guards switch(x) a::[] | a > 10 => 1 _ => 2 function f(a::[]) | a > 10 = 1 f(_) = 2 Changed Fixed the ACI renderer, it shouldn't drop the stateful modifier 6.0.2 2021-07-05 Changed List.from_to_step now forbids non-positive step (this change does not alter the behavior of the previously deployed contracts) Fixed leaking state between contracts 6.0.1 2021-06-24 Changed Fixed a bug in calldata encoding for contracts containing multiple contracts Fixed a missing include in the Frac standard library 6.0.0 2021-05-26 Added Child contracts Chain.clone Chain.create Chain.bytecode_hash Minor support for variadic functions void type that represents an empty type Call.fee builtin Changed Contract interfaces must be now invocated by contract interface keywords main keyword to indicate the main contract in case there are child contracts around List.sum and List.product no longer use List.foldl Removed 5.0.0 2021-04-30 Added A new and improved String standard library has been added. Use it by include \"String.aes\" . It includes functions for turning strings into lists of characters for detailed manipulation. For example: include \"String.aes\" contract C = entrypoint filter_all_a(s: string) : string = String.from_list(List.filter((c : char) => c != 'a', String.to_list(s))) will return a list with all a 's removed. There are also convenience functions split , concat , to_upper , to_lower , etc. All String functions in FATEv2 operate on unicode code points. - Operations for pairing-based cryptography has been added the operations are in the standard library BLS12_381 . With these operations it is possible to do Zero Knowledge-proofs, etc. The operations are for the BLS12-381 curve (as the name suggests). - Calls to functions in other contracts (i.e. remote calls ) can now be protected . If a contract call fails for any reason (for instance, the remote contract crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong type) the parent call also fails. To make it possible to recover from failures, contract calls takes a named argument protected : bool (default false ). If protected = true the result of the contract call is wrapped in an option , and Some(value) indicates a succesful execution and None indicates that the contract call failed. Note: any gas consumed until the failure is still charged, but all side effects in the remote contract are rolled back on failure. - A new chain operation AENS.update is supported. - New chain exploring operations AENS.lookup and Oracle.expiry to look up an AENS record and the expiry of an Oracle respectively, are added. - Transaction introspection ( Auth.tx ) has been added. When a Generalized account is authorized, the authorization function needs access to the transaction (and the transaction hash) for the wrapped transaction. The transaction and the transaction hash is available Auth.tx , it is only available during authentication if invoked by a normal contract call it returns None . Example: switch(Auth.tx) None => abort(\"Not in Auth context\") Some(tx0) => switch(tx0.tx) Chain.SpendTx(_, amount, _) => amount > 400 Chain.ContractCallTx(_, _) => true _ => false - A debug mode is a added to the compiler. Right now its only use is to turn off hermetization. Changed The function Chain.block_hash(height) is now (in FATEv2) defined for the current height - this used to be an error. Standard library: Sort is optimized to do mergesort and a contains function is added. Improved type errors and explicit errors for some syntax errors (empty code blocks, etc.). Compiler optimization: The ACI is generated alongside bytecode. This means that multiple compiler passes can be avoided. Compiler optimization: Improved parsing (less stack used when transpiled). A bug where constraints were handled out of order fixed. Fixed calldata decoding for singleton records. Improved the documentation w.r.t. signatures, especially stressing the fact that the network ID is a part of what is signed. Removed 4.3.0 Added Added documentation (moved from protocol ) Frac.aes \u2013 library for rational numbers Added some more meaningful error messages Exported several parsing functionalities With option keep_included it is possible to see which files were included during the parse There is a function run_parser that be used to evaluate any parsing rule Exported parsers: body , type and decl Changed Performance improvements in the standard library Fixed ACI encoder to handle - unary operator Fixed including by absolute path Fixed variant type printing in the ACI error messages Fixed pretty printing of combined function clauses Removed let definitions are no longer supported in the toplevel of the contract type declarations are no longer supported 4.2.0 - 2020-01-15 Added Allow separate entrypoint/function type signature and definition, and pattern matching in left-hand sides: function length : list('a) => int length([]) = 0 length(x :: xs) = 1 + length(xs) Allow pattern matching in list comprehension generators (filtering out match failures): function somes(xs : list(option('a))) : list('a) = [ x | Some(x) <- xs ] Allow pattern matching in let-bindings (aborting on match failures): function test(m : map(int, int)) = let Some(x) = Map.lookup(m, 0) x Changed FATE code generator improvements. Bug fix: Handle qualified constructors in patterns. Bug fix: Allow switching also on negative numbers. Removed 4.1.0 - 2019-11-26 Added Support encoding and decoding bit fields in call arguments and results. Changed Various improvements to FATE code generator. Removed 4.0.0 - 2019-10-11 Added Address.to_contract - casts an address to a (any) contract type. Pragma to check compiler version, e.g. @compiler >= 4.0 . Handle numeric escapes, i.e. \"\\x19Ethereum Signed Message:\\n\" , and similar strings. Bytes.concat and Bytes.split are added to be able to (de-)construct byte arrays. [a..b] language construct, returning the list of numbers between a and b (inclusive). Returns the empty list if a > b . Standard libraries Checks that init is not called from other functions. FATE backend - the compiler is able to produce VM code for both AEVM and FATE . Many of the APIs now take {backend, aevm | fate} to decide wich backend to produce artifacts for. New builtin functions Crypto.ecrecover_secp256k1: (hash, bytes(65)) => option(bytes(20)) and Crypto.ecverify_secp256k1 : (hash, bytes(20), bytes(65)) => bool for recovering and verifying an Ethereum address for a message hash and a signature. Sophia supports list comprehensions known from languages like Python, Haskell or Erlang. Example syntax: [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] A new contract, and endpoint, modifier payable is introduced. Contracts, and enpoints, that shall be able to receive funds should be marked as payable. Address.is_payable(a) can be used to check if an (contract) address is payable or not. Changed Nice type error if contract function is called as from a namespace. Fail on function definitions in contracts other than the main contract. Bug fix in variable optimization - don't discard writes to the store/state. Bug fixes in error reporting. Bug fix in variable liveness analysis for FATE. Error messages are changed into a uniform format, and more helpful messages have been added. Crypto./*
- * A simple crowd-funding example
- */
-contract FundMe =
-
- record spend_args = { recipient : address,
- amount : int }
-
- record state = { contributions : map(address, int),
- total : int,
- beneficiary : address,
- deadline : int,
- goal : int }
-
- stateful function spend(args : spend_args) =
- Chain.spend(args.recipient, args.amount)
-
- entrypoint init(beneficiary, deadline, goal) : state =
- { contributions = {},
- beneficiary = beneficiary,
- deadline = deadline,
- total = 0,
- goal = goal }
-
- function is_contributor(addr) =
- Map.member(addr, state.contributions)
-
- stateful entrypoint contribute() =
- if(Chain.block_height >= state.deadline)
- spend({ recipient = Call.caller, amount = Call.value }) // Refund money
- false
- else
- let amount =
- switch(Map.lookup(Call.caller, state.contributions))
- None => Call.value
- Some(n) => n + Call.value
- put(state{ contributions[Call.caller] = amount,
- total @ tot = tot + Call.value })
- true
-
- stateful entrypoint withdraw() =
- if(Chain.block_height < state.deadline)
- abort("Cannot withdraw before deadline")
- if(Call.caller == state.beneficiary)
- withdraw_beneficiary()
- elif(is_contributor(Call.caller))
- withdraw_contributor()
- else
- abort("Not a contributor or beneficiary")
-
- stateful function withdraw_beneficiary() =
- require(state.total >= state.goal, "Project was not funded")
- spend({recipient = state.beneficiary,
- amount = Contract.balance })
-
- stateful function withdraw_contributor() =
- if(state.total >= state.goal)
- abort("Project was funded")
- let to = Call.caller
- spend({recipient = to,
- amount = state.contributions[to]})
- put(state{ contributions @ c = Map.delete(to, c) })
-
```sophia +/ + * A simple crowd-funding example + / +contract FundMe =
+record spend_args = { recipient : address, + amount : int }
+record state = { contributions : map(address, int), + total : int, + beneficiary : address, + deadline : int, + goal : int }
+stateful function spend(args : spend_args) = + Chain.spend(args.recipient, args.amount)
+entrypoint init(beneficiary, deadline, goal) : state = + { contributions = {}, + beneficiary = beneficiary, + deadline = deadline, + total = 0, + goal = goal }
+function is_contributor(addr) = + Map.member(addr, state.contributions)
+stateful entrypoint contribute() = + if(Chain.block_height >= state.deadline) + spend({ recipient = Call.caller, amount = Call.value }) // Refund money + false + else + let amount = + switch(Map.lookup(Call.caller, state.contributions)) + None => Call.value + Some(n) => n + Call.value + put(state{ contributions[Call.caller] = amount, + total @ tot = tot + Call.value }) + true
+stateful entrypoint withdraw() = + if(Chain.block_height < state.deadline) + abort("Cannot withdraw before deadline") + if(Call.caller == state.beneficiary) + withdraw_beneficiary() + elif(is_contributor(Call.caller)) + withdraw_contributor() + else + abort("Not a contributor or beneficiary")
+stateful function withdraw_beneficiary() = + require(state.total >= state.goal, "Project was not funded") + spend({recipient = state.beneficiary, + amount = Contract.balance })
+stateful function withdraw_contributor() = + if(state.total >= state.goal) + abort("Project was funded") + let to = Call.caller + spend({recipient = to, + amount = state.contributions[to]}) + put(state{ contributions @ c = Map.delete(to, c) }) +```
This is a list with repositories that include smart contracts written in Sophia:
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 of a number of type definitions and entrypoint declarations. For instance,
-// A contract type
-contract interface VotingType =
- entrypoint vote : string => unit
-
sophia
+// A contract type
+contract interface VotingType =
+ entrypoint vote : string => unit
Now given contract address of type VotingType
you can call the vote
entrypoint of that contract:
contract VoteTwice =
- entrypoint voteTwice(v : VotingType, alt : string) =
- v.vote(alt)
- v.vote(alt)
-
sophia
+contract VoteTwice =
+ entrypoint voteTwice(v : VotingType, alt : string) =
+ v.vote(alt)
+ v.vote(alt)
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
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
- v.vote(value = fee, alt)
- v.vote(value = fee, alt)
-
sophia
+ entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
+ v.vote(value = fee, alt)
+ v.vote(value = fee, alt)
Named arguments can be given in any order.
Note that reentrant calls are not permitted. In other words, when calling another contract it cannot call you back (directly or indirectly).
@@ -1057,9 +1057,9 @@ fails.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)
without calling it you can write
entrypoint pay(v : VotingType, amount : int) =
- Chain.spend(v.address, amount)
-
sophia
+ entrypoint pay(v : VotingType, amount : int) =
+ Chain.spend(v.address, amount)
If a contract call fails for any reason (for instance, the remote contract
crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong
@@ -1069,15 +1069,15 @@ contract calls takes a named argument protected : bool
(default None
, otherwise it's Some(r)
where r
is
the return value of the call.
contract interface VotingType =
- entrypoint : vote : string => unit
-
-contract Voter =
- entrypoint tryVote(v : VotingType, alt : string) =
- switch(v.vote(alt, protected = true) : option(unit))
- None => "Voting failed"
- Some(_) => "Voting successful"
-
```sophia +contract interface VotingType = + entrypoint : vote : string => unit
+contract Voter = + entrypoint tryVote(v : VotingType, alt : string) = + switch(v.vote(alt, protected = true) : option(unit)) + None => "Voting failed" + Some(_) => "Voting successful" +```
Any gas that was consumed by the contract call before the failure stays
consumed, which means that in order to protect against the remote contract
running out of gas it is necessary to set a gas limit using the gas
argument.
@@ -1098,16 +1098,16 @@ arguments – please refer to their documentation for the details.
While Chain.clone
requires only a contract interface
and a living instance
of a given contract on the 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 =
- stateful entrypoint new(x : int) : IntHolder =
- let ih = Chain.create(x) : IntHolder
- ih
-
```sophia +contract IntHolder = + type state = int + entrypoint init(x) = x + entrypoint get() = state
+main contract IntHolderFactory = + stateful 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.
Top-level functions and entrypoints must be annotated with the
stateful
keyword to be allowed to affect the state of the running contract.
For instance,
stateful entrypoint set_state(s : state) =
- put(s)
-
sophia
+ stateful entrypoint set_state(s : state) =
+ put(s)
Without the stateful
annotation the compiler does not allow the call to
put
. A stateful
annotation is required to
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
contract shall be able to receive funds in this way it has to be declared payable
:
// A payable contract
-payable contract ExampleContract =
- stateful entrypoint do_stuff() = ...
-
sophia
+// A payable contract
+payable contract ExampleContract =
+ stateful entrypoint do_stuff() = ...
If in doubt, it is possible to check if an address is payable using
Address.is_payable(addr)
.
(either a Remote call or a contract call transaction)
that has a non-zero value
will fail. Contract entrypoints that should be called
with a non-zero value should be declared payable
.
-payable stateful entrypoint buy(to : address) =
- if(Call.value > 42)
- transfer_item(to)
- else
- abort("Value too low")
-
+sophia
+payable stateful entrypoint buy(to : address) =
+ if(Call.value > 42)
+ transfer_item(to)
+ else
+ abort("Value too low")
Note: In the æternity VM (AEVM) contracts and entrypoints were by default
payable until the Lima release.
Namespaces
@@ -1192,65 +1192,61 @@ can appear at the top-level and can contain type and function definitions, but
not entrypoints. Outside the namespace you can refer to the (non-private) names
by qualifying them with the namespace (Namespace.name
).
For example,
-namespace Library =
- type number = int
- function inc(x : number) : number = x + 1
-
-contract MyContract =
- entrypoint plus2(x) : Library.number =
- Library.inc(Library.inc(x))
-
+```sophia
+namespace Library =
+ type number = int
+ function inc(x : number) : number = x + 1
+contract MyContract =
+ entrypoint plus2(x) : Library.number =
+ Library.inc(Library.inc(x))
+```
Functions in namespaces have access to the same environment (including the
Chain
, Call
, and Contract
, builtin namespaces) as function in a contract,
with the exception of state
, put
and Chain.event
since these are
dependent on the specific state and event types of the contract.
To avoid mentioning the namespace every time it is used, Sophia allows
including the namespace in the current scope with the using
keyword:
-
include "Pair.aes"
+include "Pair.aes"
using Pair
contract C =
type state = int
entrypoint init() =
let p = (1, 2)
- fst(p) // this is the same as Pair.fst(p)
-
+ fst(p) // this is the same as Pair.fst(p)
It is also possible to make an alias for the namespace with the as
keyword:
-
include "Pair.aes"
+include "Pair.aes"
contract C =
using Pair as P
type state = int
entrypoint init() =
let p = (1, 2)
- P.fst(p) // this is the same as Pair.fst(p)
-
Having the same alias for multiple namespaces is possible and it allows referening functions that are defined in different namespaces and have different names with the same alias: -
namespace Xa = function f() = 1
+namespace Xa = function f() = 1
namespace Xb = function g() = 2
contract Cntr =
using Xa as A
using Xb as A
type state = int
- entrypoint init() = A.f() + A.g()
-
Note that using functions with the same name would result in an ambiguous name error: -
namespace Xa = function f() = 1
+```
+namespace Xa = function f() = 1
namespace Xb = function f() = 2
contract Cntr =
using Xa as A
using Xb as A
- type state = int
-
- // the next line has an error because f is defined in both Xa and Xb
+ type state = int
+// the next line has an error because f is defined in both Xa and Xb
entrypoint init() = A.f()
-
Importing specific parts of a namespace or hiding these parts can also be done like this: -
using Pair for [fst, snd] // this will only import fst and snd
-using Triple hiding [fst, snd] // this will import everything except for fst and snd
-
using Pair for [fst, snd] // this will only import fst and snd
+using Triple hiding [fst, snd] // this will import everything except for fst and snd
Note that it is possible to use a namespace in the top level of the file, in the contract level, namespace level, or in the function level.
library.aes
contains
-namespace Library =
- function inc(x) = x + 1
-
sophia
+namespace Library =
+ function inc(x) = x + 1
you can use it from another file using an include
:
include "library.aes"
-contract MyContract =
- entrypoint plus2(x) = Library.inc(Library.inc(x))
-
sophia
+include "library.aes"
+contract MyContract =
+ entrypoint plus2(x) = Library.inc(Library.inc(x))
This behaves as if the contents of library.aes
was textually inserted into
the file, except that error messages will refer to the original source
locations. The language will try to include each file at most one time automatically,
@@ -1277,14 +1273,14 @@ namespaces like Chain
, Contract
, Map
are included by default and are supported internally by the compiler.
Others like List
, Frac
, Option
need to be manually included using the
include
directive. For example
-
include "List.aes"
-include "Pair.aes"
--- Map is already there!
-
-namespace C =
- entrypoint keys(m : map('a, 'b)) : list('a) =
- List.map(Pair.fst, (Map.to_list(m)))
-
namespace C = + entrypoint keys(m : map('a, 'b)) : list('a) = + List.map(Pair.fst, (Map.to_list(m))) +```
Sophia has the following types: