From 7556284dc69442212b63e567f7fa971c8db7b015 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 7 Dec 2022 14:35:23 +0000 Subject: [PATCH] Deployed 9ad8e26 to master with MkDocs 1.2.4 and mike 1.1.2 --- master/search/search_index.json | 2 +- master/sitemap.xml | 18 +++++++++--------- master/sitemap.xml.gz | Bin 202 -> 202 bytes master/sophia_stdlib/index.html | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/master/search/search_index.json b/master/search/search_index.json index 9c0ed2c..098ba70 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 Options to enable/disable certain optimizations. The ability to call a different instance of the current contract contract Main = entrypoint spend(x : int) : int = x entrypoint f(c : Main) : int = c.spend(10) Return a mapping from variables to FATE registers in the compilation output. Changed Type definitions serialised to ACI as typedefs field instead of type_defs to increase compatibility. Check contracts and entrypoints modifiers when implementing interfaces. Contracts can no longer be used as namespaces. Removed Fixed Typechecker crashes if Chain.create or Chain.clone are used without arguments. 7.0.1 Added Add CONTRIBUTING.md file. Changed Update Sophia syntax docs to include missing information about existing syntax. Fixed 404 Contract polymorphism crashes on non-obvious child contract typing. 7.0.0 Added Added support for EXIT opcode via exit : (string) => 'a function (behaves same as ABORT , but consumes all gas). Compiler warnings for the following: 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])) Allow binary operators to be used as lambdas function sum(l : list(int)) : int = foldl((+), 0, l) function logical_and(x, y) = (&&)(x, y) Contract interfaces polymorphism Changed Error messages have been restructured (less newlines) to provide more unified errors. Also pp_oneline/1 has been added. Ban empty record definitions (e.g. record r = {} would give an error). Removed Support for AEVM has been entirely wiped 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. and String. for byte arrays now only hash the actual byte array - not the internal ABI format. More strict checks for polymorphic oracles and higher order oracles and entrypoints. AENS.claim is updated with a NameFee field - to be able to do name auctions within contracts. Fixed a bug in Bytes.to_str for AEVM. New syntax for tuple types. Now 0-tuple type is encoded as unit instead of () and regular tuples are encoded by interspersing inner types with * , for instance int * string . Parens are not necessary. Note it only affects the types, values remain as their were before, so (1, \"a\") : int * string The AENS.transfer and AENS.revoke functions have been updated to take a name string instead of a name hash . Fixed a bug where the AEVM backend complained about a missing init function when trying to generate calldata from an ACI-generated interface. Compiler now returns the ABI-version in the compiler result map. Renamed Crypto.ecverify and Crypto.ecverify_secp256k1 into Crypto.verify_sig and Crypto.verify_sig_secp256k1 respectively. Removed 3.2.0 - 2019-06-28 Added New builtin function require : (bool, string) => () . Defined as function require(b, err) = if(!b) abort(err) New builtin functions 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. Changed Public contract functions must now be declared as entrypoints : 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). The return type of Chain.block_hash(height) has changed, it used to be int , where 0 denoted an incorrect height. New return type is option(hash) , where None represents an incorrect height. Event name hashes now use BLAKE2b instead of Keccak256. Fixed bugs when defining record types in namespaces. Fixed a bug in include path handling when passing options to the compiler. Removed 3.1.0 - 2019-06-03 Added Changed Keyword indexed is now optional for word typed ( bool , int , address , ...) event arguments. State variable pretty printing now produce 'a, 'b, ... instead of '1, '2, ... . ACI is restructured and improved: state and event types (if present) now appear at the top level. Namespaces and remote interfaces are no longer ignored. All type definitions are included in the interface rendering. API functions are renamed, new functions are contract_interface and render_aci_json . Fixed a bug in create_calldata / to_sophia_value - it can now handle negative literals. Removed 3.0.0 - 2019-05-21 Added stateful annotations are now properly enforced. Functions must be marked stateful in order to update the state or spend tokens. Primitives Contract.creator , Address.is_contract , Address.is_oracle , Oracle.check and Oracle.check_query has been added to Sophia. A byte array type bytes(N) has been added to generalize hash (== bytes(32)) and signature (== bytes(64)) and allow for byte arrays of arbitrary fixed length. Crypto.ecverify_secp256k1 has been added. Changed Address literals (+ Oracle, Oracle query and remote contracts) have been changed from # to address as ak_ , oracle ok_ , oracle query oq_ and remote contract ct_ . The compilation and typechecking of letfun (e.g. let m(f, xs) = map(f, xs) ) was not working properly and has been fixed. Removed let rec has been removed from the language, it has never worked. The standalone CLI compiler is served in the repo aeternity/aesophia_cli and has been completely removed from aesophia . 2.1.0 - 2019-04-11 Added Stubs (not yet wired up) for compilation to FATE Add functions specific for Calldata decoding Support for Auth.tx_hash , not available in AEVM until Fortuna release Changed Improvements to the ACI generator 2.0.0 - 2019-03-11 Added Add Crypto.ecverify to the compiler. Add Crypto.sha3 , Crypto.blake2 , Crypto.sha256 , String.blake2 and String.sha256 to the compiler. Add the bits type for working with bit fields in Sophia. Add Namespaces to Sophia in order to simplify using library contracts, etc. Add a missig type check on the init function - detects programmer errors earlier. Add the ACI (Aeternity Contract Interface) generator. Changed Use native bit shift operations in builtin functions, reducing gas cost. Improve type checking of record fields - generates more understandable error messages. Improved, more coherent, error messages. Simplify calldata creation - instead of passing a compiled contract, simply pass a (stubbed) contract string.","title":"Changelog"},{"location":"CHANGELOG/#changelog","text":"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 .","title":"Changelog"},{"location":"CHANGELOG/#unreleased","text":"","title":"Unreleased"},{"location":"CHANGELOG/#added","text":"Options to enable/disable certain optimizations. The ability to call a different instance of the current contract contract Main = entrypoint spend(x : int) : int = x entrypoint f(c : Main) : int = c.spend(10) Return a mapping from variables to FATE registers in the compilation output.","title":"Added"},{"location":"CHANGELOG/#changed","text":"Type definitions serialised to ACI as typedefs field instead of type_defs to increase compatibility. Check contracts and entrypoints modifiers when implementing interfaces. Contracts can no longer be used as namespaces.","title":"Changed"},{"location":"CHANGELOG/#removed","text":"","title":"Removed"},{"location":"CHANGELOG/#fixed","text":"Typechecker crashes if Chain.create or Chain.clone are used without arguments.","title":"Fixed"},{"location":"CHANGELOG/#701","text":"","title":"7.0.1"},{"location":"CHANGELOG/#added_1","text":"Add CONTRIBUTING.md file.","title":"Added"},{"location":"CHANGELOG/#changed_1","text":"Update Sophia syntax docs to include missing information about existing syntax.","title":"Changed"},{"location":"CHANGELOG/#fixed_1","text":"404 Contract polymorphism crashes on non-obvious child contract typing.","title":"Fixed"},{"location":"CHANGELOG/#700","text":"","title":"7.0.0"},{"location":"CHANGELOG/#added_2","text":"Added support for EXIT opcode via exit : (string) => 'a function (behaves same as ABORT , but consumes all gas). Compiler warnings for the following: 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])) Allow binary operators to be used as lambdas function sum(l : list(int)) : int = foldl((+), 0, l) function logical_and(x, y) = (&&)(x, y) Contract interfaces polymorphism","title":"Added"},{"location":"CHANGELOG/#changed_2","text":"Error messages have been restructured (less newlines) to provide more unified errors. Also pp_oneline/1 has been added. Ban empty record definitions (e.g. record r = {} would give an error).","title":"Changed"},{"location":"CHANGELOG/#removed_1","text":"Support for AEVM has been entirely wiped","title":"Removed"},{"location":"CHANGELOG/#610-2021-10-20","text":"","title":"6.1.0 - 2021-10-20"},{"location":"CHANGELOG/#added_3","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_3","text":"Fixed the ACI renderer, it shouldn't drop the stateful modifier","title":"Changed"},{"location":"CHANGELOG/#602-2021-07-05","text":"","title":"6.0.2 2021-07-05"},{"location":"CHANGELOG/#changed_4","text":"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","title":"Changed"},{"location":"CHANGELOG/#601-2021-06-24","text":"","title":"6.0.1 2021-06-24"},{"location":"CHANGELOG/#changed_5","text":"Fixed a bug in calldata encoding for contracts containing multiple contracts Fixed a missing include in the Frac standard library","title":"Changed"},{"location":"CHANGELOG/#600-2021-05-26","text":"","title":"6.0.0 2021-05-26"},{"location":"CHANGELOG/#added_4","text":"Child contracts Chain.clone Chain.create Chain.bytecode_hash Minor support for variadic functions void type that represents an empty type Call.fee builtin","title":"Added"},{"location":"CHANGELOG/#changed_6","text":"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","title":"Changed"},{"location":"CHANGELOG/#removed_2","text":"","title":"Removed"},{"location":"CHANGELOG/#500-2021-04-30","text":"","title":"5.0.0 2021-04-30"},{"location":"CHANGELOG/#added_5","text":"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.","title":"Added"},{"location":"CHANGELOG/#changed_7","text":"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.","title":"Changed"},{"location":"CHANGELOG/#removed_3","text":"","title":"Removed"},{"location":"CHANGELOG/#430","text":"","title":"4.3.0"},{"location":"CHANGELOG/#added_6","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_8","text":"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","title":"Changed"},{"location":"CHANGELOG/#removed_4","text":"let definitions are no longer supported in the toplevel of the contract type declarations are no longer supported","title":"Removed"},{"location":"CHANGELOG/#420-2020-01-15","text":"","title":"4.2.0 - 2020-01-15"},{"location":"CHANGELOG/#added_7","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_9","text":"FATE code generator improvements. Bug fix: Handle qualified constructors in patterns. Bug fix: Allow switching also on negative numbers.","title":"Changed"},{"location":"CHANGELOG/#removed_5","text":"","title":"Removed"},{"location":"CHANGELOG/#410-2019-11-26","text":"","title":"4.1.0 - 2019-11-26"},{"location":"CHANGELOG/#added_8","text":"Support encoding and decoding bit fields in call arguments and results.","title":"Added"},{"location":"CHANGELOG/#changed_10","text":"Various improvements to FATE code generator.","title":"Changed"},{"location":"CHANGELOG/#removed_6","text":"","title":"Removed"},{"location":"CHANGELOG/#400-2019-10-11","text":"","title":"4.0.0 - 2019-10-11"},{"location":"CHANGELOG/#added_9","text":"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.","title":"Added"},{"location":"CHANGELOG/#changed_11","text":"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. and String. for byte arrays now only hash the actual byte array - not the internal ABI format. More strict checks for polymorphic oracles and higher order oracles and entrypoints. AENS.claim is updated with a NameFee field - to be able to do name auctions within contracts. Fixed a bug in Bytes.to_str for AEVM. New syntax for tuple types. Now 0-tuple type is encoded as unit instead of () and regular tuples are encoded by interspersing inner types with * , for instance int * string . Parens are not necessary. Note it only affects the types, values remain as their were before, so (1, \"a\") : int * string The AENS.transfer and AENS.revoke functions have been updated to take a name string instead of a name hash . Fixed a bug where the AEVM backend complained about a missing init function when trying to generate calldata from an ACI-generated interface. Compiler now returns the ABI-version in the compiler result map. Renamed Crypto.ecverify and Crypto.ecverify_secp256k1 into Crypto.verify_sig and Crypto.verify_sig_secp256k1 respectively.","title":"Changed"},{"location":"CHANGELOG/#removed_7","text":"","title":"Removed"},{"location":"CHANGELOG/#320-2019-06-28","text":"","title":"3.2.0 - 2019-06-28"},{"location":"CHANGELOG/#added_10","text":"New builtin function require : (bool, string) => () . Defined as function require(b, err) = if(!b) abort(err) New builtin functions 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.","title":"Added"},{"location":"CHANGELOG/#changed_12","text":"Public contract functions must now be declared as entrypoints : 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). The return type of Chain.block_hash(height) has changed, it used to be int , where 0 denoted an incorrect height. New return type is option(hash) , where None represents an incorrect height. Event name hashes now use BLAKE2b instead of Keccak256. Fixed bugs when defining record types in namespaces. Fixed a bug in include path handling when passing options to the compiler.","title":"Changed"},{"location":"CHANGELOG/#removed_8","text":"","title":"Removed"},{"location":"CHANGELOG/#310-2019-06-03","text":"","title":"3.1.0 - 2019-06-03"},{"location":"CHANGELOG/#added_11","text":"","title":"Added"},{"location":"CHANGELOG/#changed_13","text":"Keyword indexed is now optional for word typed ( bool , int , address , ...) event arguments. State variable pretty printing now produce 'a, 'b, ... instead of '1, '2, ... . ACI is restructured and improved: state and event types (if present) now appear at the top level. Namespaces and remote interfaces are no longer ignored. All type definitions are included in the interface rendering. API functions are renamed, new functions are contract_interface and render_aci_json . Fixed a bug in create_calldata / to_sophia_value - it can now handle negative literals.","title":"Changed"},{"location":"CHANGELOG/#removed_9","text":"","title":"Removed"},{"location":"CHANGELOG/#300-2019-05-21","text":"","title":"3.0.0 - 2019-05-21"},{"location":"CHANGELOG/#added_12","text":"stateful annotations are now properly enforced. Functions must be marked stateful in order to update the state or spend tokens. Primitives Contract.creator , Address.is_contract , Address.is_oracle , Oracle.check and Oracle.check_query has been added to Sophia. A byte array type bytes(N) has been added to generalize hash (== bytes(32)) and signature (== bytes(64)) and allow for byte arrays of arbitrary fixed length. Crypto.ecverify_secp256k1 has been added.","title":"Added"},{"location":"CHANGELOG/#changed_14","text":"Address literals (+ Oracle, Oracle query and remote contracts) have been changed from # to address as ak_ , oracle ok_ , oracle query oq_ and remote contract ct_ . The compilation and typechecking of letfun (e.g. let m(f, xs) = map(f, xs) ) was not working properly and has been fixed.","title":"Changed"},{"location":"CHANGELOG/#removed_10","text":"let rec has been removed from the language, it has never worked. The standalone CLI compiler is served in the repo aeternity/aesophia_cli and has been completely removed from aesophia .","title":"Removed"},{"location":"CHANGELOG/#210-2019-04-11","text":"","title":"2.1.0 - 2019-04-11"},{"location":"CHANGELOG/#added_13","text":"Stubs (not yet wired up) for compilation to FATE Add functions specific for Calldata decoding Support for Auth.tx_hash , not available in AEVM until Fortuna release","title":"Added"},{"location":"CHANGELOG/#changed_15","text":"Improvements to the ACI generator","title":"Changed"},{"location":"CHANGELOG/#200-2019-03-11","text":"","title":"2.0.0 - 2019-03-11"},{"location":"CHANGELOG/#added_14","text":"Add Crypto.ecverify to the compiler. Add Crypto.sha3 , Crypto.blake2 , Crypto.sha256 , String.blake2 and String.sha256 to the compiler. Add the bits type for working with bit fields in Sophia. Add Namespaces to Sophia in order to simplify using library contracts, etc. Add a missig type check on the init function - detects programmer errors earlier. Add the ACI (Aeternity Contract Interface) generator.","title":"Added"},{"location":"CHANGELOG/#changed_16","text":"Use native bit shift operations in builtin functions, reducing gas cost. Improve type checking of record fields - generates more understandable error messages. Improved, more coherent, error messages. Simplify calldata creation - instead of passing a compiled contract, simply pass a (stubbed) contract string.","title":"Changed"},{"location":"aeso_aci/","text":"aeso_aci Module aeso_aci The ACI interface encoder and decoder. Description This module provides an interface to generate and convert between Sophia contracts and a suitable JSON encoding of contract interface. As yet the interface is very basic. Encoding this contract: contract Answers = record state = { a : answers } 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\" } ] }, \"typedefs\" : [ { \"name\" : \"answers\" , \"typedef\" : { \"map\" : [ \"string\" , \"int\" ] }, \"vars\" : [] } ] } } When that encoding is decoded the following include definition is generated: contract Answers = record state = {a : Answers.answers} type answers = map(string, int) function init : () => Answers.state function new_answer : (string, int) => map(string, int) Types - type aci_type () :: json | string . - type json () :: jsx : json_term (). - type json_text () :: binary (). Exports contract_interface(aci_type(), string()) -> {ok, json() | string()} | {error, term()} Generate the JSON encoding of the interface to a contract. The type definitions and non-private functions are included in the JSON string. render_aci_json(json() | json_text()) -> string(). Take a JSON encoding of a contract interface and generate a contract interface that can be included in another contract. Example run This is an example of using the ACI generator from an Erlang shell. The file called aci_test.aes contains the contract in the description from which we 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\" >> }]}, typedefs => [#{ 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.","title":"aeso_aci"},{"location":"aeso_aci/#aeso_aci","text":"","title":"aeso_aci"},{"location":"aeso_aci/#module","text":"","title":"Module"},{"location":"aeso_aci/#aeso_aci_1","text":"The ACI interface encoder and decoder.","title":"aeso_aci"},{"location":"aeso_aci/#description","text":"This module provides an interface to generate and convert between Sophia contracts and a suitable JSON encoding of contract interface. As yet the interface is very basic. Encoding this contract: contract Answers = record state = { a : answers } 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\" } ] }, \"typedefs\" : [ { \"name\" : \"answers\" , \"typedef\" : { \"map\" : [ \"string\" , \"int\" ] }, \"vars\" : [] } ] } } When that encoding is decoded the following include definition is generated: contract Answers = record state = {a : Answers.answers} type answers = map(string, int) function init : () => Answers.state function new_answer : (string, int) => map(string, int)","title":"Description"},{"location":"aeso_aci/#types","text":"- type aci_type () :: json | string . - type json () :: jsx : json_term (). - type json_text () :: binary ().","title":"Types"},{"location":"aeso_aci/#exports","text":"","title":"Exports"},{"location":"aeso_aci/#example-run","text":"This is an example of using the ACI generator from an Erlang shell. The file called aci_test.aes contains the contract in the description from which we 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\" >> }]}, typedefs => [#{ 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.","title":"Example run"},{"location":"aeso_compiler/","text":"aeso_compiler Module aeso_compiler The Sophia compiler Description This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded. Types contract_string () = string () | binary () contract_map () = #{ bytecode => binary (), compiler_version => binary (), contract_souce => string (), type_info => type_info ()} type_info () errorstring () = binary () Exports file(File) file(File, Options) -> CompRet from_string(ContractString, Options) -> CompRet Types 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_ast - print the AST of the code pp_types - print information about the types pp_typed_ast - print the AST with type information at each node pp_assembler - print the generated assembler code The option include_child_contract_symbols includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain. The option debug_info includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers. Options to control which compiler optimizations should run: By default all optimizations are turned on, to disable an optimization, it should be explicitly set to false and passed as a compiler option. List of optimizations: optimize_inliner optimize_inline_local_functions optimize_bind_subexpressions optimize_let_floating optimize_simplifier optimize_drop_unused_lets optimize_push_consume optimize_one_shot_var optimize_write_to_dead_var optimize_inline_switch_target optimize_swap_push optimize_swap_pop optimize_swap_write optimize_constant_propagation optimize_prune_impossible_branches optimize_single_successful_branch optimize_inline_store optimize_float_switch_bod check_call(ContractString, Options) -> CheckRet Types ContractString = string() | binary() CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term} Types = [Type] Type = term() Check a call in contract through the __call function. version() -> {ok, Version} | {error, term()} Types Version = binary () Get the current version of the Sophia compiler.","title":"aeso_compiler"},{"location":"aeso_compiler/#aeso_compiler","text":"","title":"aeso_compiler"},{"location":"aeso_compiler/#module","text":"","title":"Module"},{"location":"aeso_compiler/#aeso_compiler_1","text":"The Sophia compiler","title":"aeso_compiler"},{"location":"aeso_compiler/#description","text":"This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded.","title":"Description"},{"location":"aeso_compiler/#types","text":"contract_string () = string () | binary () contract_map () = #{ bytecode => binary (), compiler_version => binary (), contract_souce => string (), type_info => type_info ()} type_info () errorstring () = binary ()","title":"Types"},{"location":"aeso_compiler/#exports","text":"","title":"Exports"},{"location":"sophia/","text":"This file has been moved here","title":"Sophia"},{"location":"sophia_examples/","text":"Contract examples Crowdfunding /* * 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 ) }) Repositories This is a list with repositories that include smart contracts written in Sophia: aepp-sophia-examples A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.","title":"Contract examples"},{"location":"sophia_examples/#contract-examples","text":"","title":"Contract examples"},{"location":"sophia_examples/#crowdfunding","text":"/* * 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 ) })","title":"Crowdfunding"},{"location":"sophia_examples/#repositories","text":"This is a list with repositories that include smart contracts written in Sophia: aepp-sophia-examples A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.","title":"Repositories"},{"location":"sophia_features/","text":"Features Contracts The main unit of code in Sophia is the contract . A contract implementation, or simply a contract, is the code for a smart contract and consists of a list of types, entrypoints and local functions. Only the entrypoints can be called from outside the contract. A contract instance is an entity living on the block chain (or in a state channel). Each instance has an address that can be used to call its entrypoints, either from another contract or in a call transaction. A contract may define a type state encapsulating its local state. When creating a new contract the init entrypoint is executed and the state is initialized to its return value. The language offers some primitive functions to interact with the blockchain and contracts. Please refer to the Chain , Contract and the Call namespaces in the documentation. Calling other contracts 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 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 ) 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 ) 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). To construct a value of a contract type you can give a contract address literal (for instance ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay ), or convert an account address to a contract address using Address.to_contract . Note that if the contract does not exist, or it doesn't have the entrypoint, or the type of the entrypoint does not match the stated contract type, the call 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 ) Protected contract calls 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 ). The protected argument must be a literal boolean, and when set to true changes the type of the contract call, wrapping the result in an option type. If the call fails the result is 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\" 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. However, note that errors that would normally consume all the gas in the transaction still only uses up the gas spent running the contract. Any side effects (state change, token transfers, etc.) made by a failing protected call is rolled back, just like they would be in the unprotected case. Contract factories and child contracts Since the version 6.0.0 Sophia supports deploying contracts by other contracts. This can be done in two ways: Contract cloning via Chain.clone Direct deploy via Chain.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 \u2013 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 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. Contract interfaces and polymorphism Contracts can implement one or multiple interfaces, the contract has to define every entrypoint from the implemented interface and the entrypoints in both the contract and implemented interface should have compatible types. contract interface Animal = entrypoint sound : () => string contract Cat : Animal = entrypoint sound() = \"Cat sound\" Contract interfaces can extend other interfaces. An extended interface has to declare all entrypoints from every parent interface. All the declarations in the extended interface must have types compatible with the declarations from the parent interface. contract interface II = entrypoint f : () => unit contract interface I : II = entrypoint f : () => unit entrypoint g : () => unit contract C : I = entrypoint f() = () entrypoint g() = () It is only possible to implement (or extend) an interface that has been already defined earlier in the file (or in an included file). Therefore recursive interface implementation is not allowed in Sophia. // The following code would show an error contract interface X : Z = entrypoint x : () => int contract interface Y : X = entrypoint x : () => int entrypoint y : () => int contract interface Z : Y = entrypoint x : () => int entrypoint y : () => int entrypoint z : () => int contract C : Z = entrypoint x() = 1 entrypoint y() = 1 entrypoint z() = 1 Adding or removing modifiers When a contract or a contract interface implements another contract interface , the payable and stateful modifiers can be kept or changed, both in the contract and in the entrypoints, according to the following rules: A payable contract or interface can implement a payable interface or a non- payable interface. A non- payable contract or interface can only implement a non- payable interface, and cannot implement a payable interface. A payable entrypoint can implement a payable entrypoint or a non- payable entrypoint. A non- payable entrypoint can only implement a non- payable entrypoint, and cannot implement a payable entrypoint. A non- stateful entrypoint can implement a stateful entrypoint or a non- stateful entrypoint. A stateful entrypoint can only implement a stateful entrypoint, and cannot implement a non- stateful entrypoint. Subtyping and variance Subtyping in Sophia follows common rules that take type variance into account. As described by Wikipedia , Variance refers to how subtyping between more complex types relates to subtyping between their components. This concept plays an important role in complex types such as tuples, datatype s and functions. Depending on the context, it can apply to positions in the structure of a type, or type parameters of generic types. There are four kinds of variances: covariant contravariant invariant bivariant A type is said to be on a \"covariant\" position when it describes output or a result of some computation. Analogously, position is \"contravariant\" when it is an input, or a parameter. Intuitively, when a part of the type is produced by values of it, it is covariant. When it is consumed, it is contravariant. When a type appears to be simultaneously input and output, it is described as invariant. If a type is neither of those (that is, it's unused) it's bivariant. Furthermore, whenever a complex type appears on a contravariant position, all its covariant components become contravariant and vice versa. Variance influences how subtyping is applied. Types on covariant positions are subtyped normally, while contravariant the opposite way. Invariant types have to be exactly the same in order for subtyping to work. Bivariant types are always compatible. A good example of where it matters can be pictured by subtyping of function types. Let us assume there is a contract interface Animal and two contracts that implement it: Dog and Cat . contract interface Animal = entrypoint age : () => int contract Dog : Animal = entrypoint age () = // ... entrypoint woof () = \"woof\" contract Cat : Animal = entrypoint age () = // ... entrypoint meow () = \"meow\" The assumption of this exercise is that cats do not bark (because Cat does not define the woof entrypoint). If subtyping rules were applied naively, that is if we let Dog => Dog be a subtype of Animal => Animal , the following code would break: let f : ( Dog ) => string = d => d . woof () let g : ( Animal ) => string = f let c : Cat = Chain . create () g ( c ) // Cat barking! That is because arguments of functions are contravariant, as opposed to return the type which is covariant. Because of that, the assignment of f to g is invalid - while Dog is a subtype of Animal , Dog => string is not a subtype of Animal => string . However, Animal => string is a subtype of Dog => string . More than that, (Dog => Animal) => Dog is a subtype of (Animal => Dog) => Animal . This has consequences on how user-defined generic types work. A type variable gains its variance from its role in the type definition as shown in the example: datatype co ( 'a ) = Co ( 'a ) // co is covariant on 'a datatype ct ( 'a ) = Ct ( 'a => unit ) // ct is contravariant on 'a datatype in ( 'a ) = In ( 'a => 'a ) // in is invariant on 'a datatype bi ( 'a ) = Bi // bi is bivariant on 'a The following facts apply here: co('a) is a subtype of co('b) when 'a is a subtype of 'b` ct('a) is a subtype of ct('b) when 'b is a subtype of 'a` in('a) is a subtype of in('b) when 'a is equal to 'b` bi('a) is a subtype of `bi('b) always That altogether induce the following rules of subtyping in Sophia: A function type (Args1) => Ret1 is a subtype of (Args2) => Ret2 when Ret1 is a subtype of Ret2 and each argument type from Args2 is a subtype of its counterpart in Args1 . A list type list(A) is a subtype of list(B) if A is a subtype of B . An option type option(A) is a subtype of option(B) if A is a subtype of B . A map type map(A1, A2) is a subtype of map(B1, B2) if A1 is a subtype of B1 , and A2 is a subtype of B2 . An oracle type oracle(A1, A2) is a subtype of oracle(B1, B2) if B1 is a subtype of A1 , and A2 is a subtype of B2 . An oracle_query type oracle_query(A1, A2) is a subtype of oracle_query(B1, B2) if A1 is a subtype of B1 , and A2 is a subtype of B2 . A user-defined datatype t(Args1) is a subtype of t(Args2) When a user-defined type t('a) is covariant in 'a , then t(A) is a subtype of t(B) when A is a subtype of B . When a user-defined type t('a) is contravariant in 'a , then t(A) is a subtype of t(B) when B is a subtype of A . When a user-defined type t('a) is binvariant in 'a , then t(A) is a subtype of t(B) when either A is a subtype of B or when B is a subtype of A . When a user-defined type t('a) is invariant in 'a , then t(A) can never be a subtype of t(B) . Mutable state Sophia does not have arbitrary mutable state, but only a limited form of state associated with each contract instance. Each contract defines a type state encapsulating its mutable state. The type state defaults to the unit . The initial state of a contract is computed by the contract's init function. The init function is pure and returns the initial state as its return value. If the type state is unit , the init function defaults to returning the value () . At contract creation time, the init function is executed and its result is stored as the contract state. The value of the state is accessible from inside the contract through an implicitly bound variable state . State updates are performed by calling a function put : state => unit . Aside from the put function (and similar functions for transactions and events), the language is purely functional. Functions modifying the state need to be annotated with the stateful keyword (see below). To make it convenient to update parts of a deeply nested state Sophia provides special syntax for map/record updates. Stateful functions 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 ) Without the stateful annotation the compiler does not allow the call to put . A stateful annotation is required to Use a stateful primitive function. These are put Chain.spend Oracle.register Oracle.query Oracle.respond Oracle.extend AENS.preclaim AENS.claim AENS.transfer AENS.revoke AENS.update Call a stateful function in the current contract Call another contract with a non-zero value argument. A stateful annotation is not required to Read the contract state. Issue an event using the event function. Call another contract with value = 0 , even if the called function is stateful. Payable Payable contracts 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 () = ... If in doubt, it is possible to check if an address is payable using Address.is_payable(addr) . Payable entrypoints A contract entrypoint is by default not payable. Any call to such a function (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\" ) Namespaces Code can be split into libraries using the namespace construct. Namespaces 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 )) 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\" using Pair contract C = type state = int entrypoint init() = let p = (1, 2) 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\" 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 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 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 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 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. Splitting code over multiple files Code from another file can be included in a contract using an include statement. These must appear at the top-level (outside the main contract). The included file can contain one or more namespaces and abstract contracts. For example, if the file library.aes contains 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 )) 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, so even cyclic includes should be working without any special tinkering. Standard library Sophia offers standard library which exposes some primitive operations and some higher level utilities. The builtin 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 ))) Types Sophia has the following types: Type Description Example int A 2-complement integer -1 address \u00e6ternity address, 32 bytes Call.origin bool A Boolean true bits A bit field Bits.none bytes(n) A byte array with n bytes #fedcba9876543210 string An array of bytes \"Foo\" list A homogeneous immutable singly linked list. [1, 2, 3] ('a, 'b) => 'c A function. Parentheses can be skipped if there is only one argument (x : int, y : int) => x + y tuple An ordered heterogeneous array (42, \"Foo\", true) record An immutable key value store with fixed key names and typed values record balance = { owner: address, value: int } map An immutable key value store with dynamic mapping of keys of one type to values of one type type accounts = map(string, address) option('a) An optional value either None or Some('a) Some(42) state A user defined type holding the contract state record state = { owner: address, magic_key: bytes(4) } event An append only list of blockchain events (or log entries) datatype event = EventX(indexed int, string) hash A 32-byte hash - equivalent to bytes(32) signature A signature - equivalent to bytes(64) Chain.ttl Time-to-live (fixed height or relative to current block) FixedTTL(1050) RelativeTTL(50) oracle('a, 'b) And oracle answering questions of type 'a with answers of type 'b Oracle.register(acct, qfee, ttl) oracle_query('a, 'b) A specific oracle query Oracle.query(o, q, qfee, qttl, rttl) contract A user defined, typed, contract address function call_remote(r : RemoteContract) = r.fun() Literals Type Constant/Literal example(s) int -1 , 2425 , 4598275923475723498573485768 address ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt bool true , false bits Bits.none , Bits.all bytes(8) #fedcba9876543210 string \"This is a string\" list [1, 2, 3] , [(true, 24), (false, 19), (false, -42)] tuple (42, \"Foo\", true) record { owner = Call.origin, value = 100000000 } map {[\"foo\"] = 19, [\"bar\"] = 42} , {} option(int) Some(42) , None state state{ owner = Call.origin, magic_key = #a298105f } event EventX(0, \"Hello\") hash #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f signature #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f Chain.ttl FixedTTL(1050) , RelativeTTL(50) oracle('a, 'b) ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 oracle_query('a, 'b) oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY contract ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ Arithmetic Sophia integers ( int ) are represented by arbitrary-sized signed words and support the following arithmetic operations: - addition ( x + y ) - subtraction ( x - y ) - multiplication ( x * y ) - division ( x / y ), truncated towards zero - remainder ( x mod y ), satisfying y * (x / y) + x mod y == x for non-zero y - exponentiation ( x ^ y ) All operations are safe with respect to overflow and underflow. The division and modulo operations throw an arithmetic error if the right-hand operand is zero. Bit fields Sophia integers do not support bit arithmetic. Instead there is a separate type bits . See the standard library documentation . A bit field can be of arbitrary size (but it is still represented by the corresponding integer, so setting very high bits can be expensive). Type aliases Type aliases can be introduced with the type keyword and can be parameterized. For instance type number = int type string_map ( 'a ) = map ( string , 'a ) A type alias and its definition can be used interchangeably. Sophia does not support higher-kinded types, meaning that following type alias is invalid: type wrap('f, 'a) = 'f('a) Algebraic data types Sophia supports algebraic data types (variant types) and pattern matching. Data types are declared by giving a list of constructors with their respective arguments. For instance, 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: function get_left ( x : one_or_both ( 'a , 'b )) : option ( 'a ) = switch ( x ) Left ( x ) => Some ( x ) Right ( _ ) => None Both ( x , _ ) => Some ( x ) or directly in the left-hand side: function get_left : one_or_both ( 'a , 'b ) => option ( 'a ) get_left ( Left ( x )) = Some ( x ) get_left ( Right ( _ )) = None get_left ( Both ( x , _ )) = Some ( x ) NOTE: Data types cannot currently be recursive. Sophia also supports the assignment of patterns to variables: function f ( x ) = switch ( x ) h1 :: ( t = h2 :: _ ) => ( h1 + h2 ) :: t // same as `h1::h2::k => (h1 + h2)::h2::k` _ => x function g ( p : int * option ( int )) : int = let ( a , ( o = Some ( b ))) = p // o is equal to Pair.snd(p) b Guards are boolean expressions that can be used on patterns in both switch statements and functions definitions. If a guard expression evaluates to true , then the corresponding body will be used. Otherwise, the next pattern will be checked: function get_left_if_positive ( x : one_or_both ( int , 'b )) : option ( int ) = switch ( x ) Left ( x ) | x > 0 => Some ( x ) Both ( x , _ ) | x > 0 => Some ( x ) _ => None function get_left_if_positive : one_or_both ( int , 'b ) => option ( int ) get_left_if_positive ( Left ( x )) | x > 0 = Some ( x ) get_left_if_positive ( Both ( x , _ )) | x > 0 = Some ( x ) get_left_if_positive ( _ ) = None Guards cannot be stateful even when used inside a stateful function. Lists A Sophia list is a dynamically sized, homogenous, immutable, singly linked list. A list is constructed with the syntax [1, 2, 3] . The 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 list('e) . For example we can have the following lists: [ 1 , 33 , 2 , 666 ] : list ( int ) [( 1 , \"aaa\" ), ( 10 , \"jjj\" ), ( 666 , \"the beast\" )] : list ( int * string ) [{[ 1 ] = \"aaa\" , [ 10 ] = \"jjj\" }, {[ 5 ] = \"eee\" , [ 666 ] = \"the beast\" }] : list ( map ( int , string )) New elements can be prepended to the front of a list with the :: operator. So 42 :: [1, 2, 3] returns the list [42, 1, 2, 3] . The concatenation operator ++ appends its second argument to its first and returns the resulting list. So concatenating two lists [1, 22, 33] ++ [10, 18, 55] returns the list [1, 22, 33, 10, 18, 55] . 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] Lists can be constructed using the range syntax using special .. operator: [ 1 .. 4 ] == [ 1 , 2 , 3 , 4 ] The ranges are always ascending and have step equal to 1. Please refer to the standard library for the predefined functionalities. Maps and records A Sophia record type is given by a fixed set of fields with associated, possibly different, types. For instance record account = { name : string , balance : int , history : list ( transaction ) } Maps, on the other hand, can contain an arbitrary number of key-value bindings, but of a fixed type. The type of maps with keys of type 'k and values of type 'v is written map('k, 'v) . The key type can be any type that does not contain a map or a function type. Please refer to the standard library for the predefined functionalities. Constructing maps and records A value of record type is constructed by giving a value for each of the fields. For the example above, function new_account ( name ) = { name = name , balance = 0 , history = []} Maps are constructed similarly, with keys enclosed in square brackets function example_map () : map ( string , int ) = {[ \"key1\" ] = 1 , [ \"key2\" ] = 2 } The empty map is written {} . Accessing values Record fields access is written r.f and map lookup m[k] . For instance, function get_balance ( a : address , accounts : map ( address , account )) = accounts [ a ]. balance Looking up a non-existing key in a map results in contract execution failing. A default value to return for non-existing keys can be provided using the syntax m[k = default] . See also Map.member and Map.lookup below. Updating a value Record field updates are written r{f = v} . This creates a new record value which is the same as r , but with the value of the field f replaced by v . Similarly, m{[k] = v} constructs a map with the same values as m except that k maps to v . It makes no difference if m has a mapping for k or not. It is possible to give a name to the old value of a field or mapping in an update: instead of acc{ balance = acc.balance + 100 } it is possible to write acc{ balance @ b = b + 100 } , binding b to acc.balance . When giving a name to a map value ( m{ [k] @ x = v } ), the corresponding key must be present in the map or execution fails, but a default value can be provided: m{ [k = default] @ x = v } . In this case x is bound to default if k is not in the map. Updates can be nested: function clear_history ( a : address , accounts : map ( address , account )) : map ( address , account ) = accounts { [ a ]. history = [] } 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 an account if a is not in the map you can write (given a function empty_account ): accounts { [ a = empty_account ()]. history = [] } Map implementation Internally in the VM maps are implemented as hash maps and support fast lookup and update. Large maps can be stored in the contract state and the size of the map does not contribute to the gas costs of a contract call reading or updating it. Strings There is a builtin type string , which can be seen as an array of bytes. Strings can be compared for equality ( == , != ), used as keys in maps and records, and used in builtin functions String.length , String.concat and the hash functions described below. Please refer to the String library documentation . Chars There is a builtin type char (the underlying representation being an integer), mainly used to manipulate strings via String.to_list / String.from_list . Characters can also be introduced as character literals (`'x', '+', ...). Please refer to the Char library documentation . Byte arrays Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system, for example the literal #cafe creates a two-element array of bytes ca (202) and fe (254) and thus is a value of type bytes(2) . Please refer to the Bytes library documentation . Cryptographic builtins Libraries Crypto and String provide functions to hash objects, verify signatures etc. The hash is a type alias for bytes(32) . Authorization interface When a Generalized account is authorized, the authorization function needs access to the transaction and the transaction hash for the wrapped transaction. (A GAMetaTx wrapping a transaction.) The transaction and the transaction hash is available in the primitive Auth.tx and Auth.tx_hash respectively, they are only available during authentication if invoked by a normal contract call they return None . Oracle interface You can attach an oracle to the current contract and you can interact with oracles through the Oracle interface. For a full description of how Oracle works see Oracles . For a functionality documentation refer to the standard library . Example Example for an oracle answering questions of type string with answers of type int : contract Oracles = stateful entrypoint registerOracle ( acct : address , sign : signature , // Signed network id + oracle address + contract address qfee : int , ttl : Chain . ttl ) : oracle ( string , int ) = Oracle . register ( acct , signature = sign , qfee , ttl ) entrypoint queryFee ( o : oracle ( string , int )) : int = Oracle . query_fee ( o ) payable stateful entrypoint createQuery ( o : oracle_query ( string , int ), q : string , qfee : int , qttl : Chain . ttl , rttl : int ) : oracle_query ( string , int ) = require ( qfee =< Call . value , \"insufficient value for qfee\" ) Oracle . query ( o , q , qfee , qttl , RelativeTTL ( rttl )) stateful entrypoint extendOracle ( o : oracle ( string , int ), ttl : Chain . ttl ) : unit = Oracle . extend ( o , ttl ) stateful entrypoint signExtendOracle ( o : oracle ( string , int ), sign : signature , // Signed network id + oracle address + contract address ttl : Chain . ttl ) : unit = Oracle . extend ( o , signature = sign , ttl ) stateful entrypoint respond ( o : oracle ( string , int ), q : oracle_query ( string , int ), sign : signature , // Signed network id + oracle query id + contract address r : int ) = Oracle . respond ( o , q , signature = sign , r ) entrypoint getQuestion ( o : oracle ( string , int ), q : oracle_query ( string , int )) : string = Oracle . get_question ( o , q ) entrypoint hasAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) = switch ( Oracle . get_answer ( o , q )) None => false Some ( _ ) => true entrypoint getAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) : option ( int ) = Oracle . get_answer ( o , q ) Sanity checks When an Oracle literal is passed to a contract, no deep checks are performed. For extra safety Oracle.check and Oracle.check_query functions are provided. AENS interface Contracts can interact with the \u00e6ternity naming system . For this purpose the AENS library was exposed. Example In this example we assume that the name name already exists, and is owned by an account with address addr . In order to allow a contract ct to handle name the account holder needs to create a signature sig of addr | name.hash | ct.address . Armed with this information we can for example write a function that extends the name if it expires within 1000 blocks: stateful entrypoint extend_if_necessary ( addr : address , name : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , FixedTTL ( expiry ), _ )) => if ( Chain . block_height + 1000 > expiry ) AENS . update ( addr , name , Some ( RelativeTTL ( 50000 )), None , None , signature = sig ) And we can write functions that adds and removes keys from the pointers of the name: stateful entrypoint add_key ( addr : address , name : string , key : string , pt : AENS . pointee , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => AENS . update ( addr , name , None , None , Some ( ptrs {[ key ] = pt }), signature = sig ) stateful entrypoint delete_key ( addr : address , name : string , key : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => let ptrs = Map . delete ( key , ptrs ) AENS . update ( addr , name , None , None , Some ( ptrs ), signature = sig ) Note: From the Iris hardfork more strict rules apply for AENS pointers, when a Sophia contract lookup or update (bad) legacy pointers, the bad keys are automatically removed so they will not appear in the pointers map. Events Sophia contracts log structured messages to an event log in the resulting blockchain transaction. The event log is quite similar to Events in Solidity . Events are further discussed in the protocol . To use events a contract must declare a datatype event , and events are then logged using the Chain.event function: datatype event = Event1 ( int , int , string ) | Event2 ( string , address ) Chain . event ( e : event ) : unit The event can have 0-3 indexed fields, and an optional payload field. A field is indexed if it fits in a 32-byte word, i.e. - bool - int - bits - address - oracle(_, _) - oracle_query(_, _) - contract types - bytes(n) for n \u2264 32, in particular hash The payload field must be either a string or a byte array of more than 32 bytes. The fields can appear in any order. NOTE: Indexing is not part of the core \u00e6ternity node. Events are emitted by using the Chain.event function. The following function will emit one Event of each kind in the example. entrypoint emit_events () : () = Chain . event ( Event1 ( 42 , 34 , \"foo\" )) Chain . event ( Event2 ( \"This is not indexed\" , Contract . address )) Argument order 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. AnotherEvent ( string , indexed address ) ... Chain . event ( AnotherEvent ( \"This is not indexed\" , Contract . address )) would yield exactly the same result in the example above! Compiler pragmas To enforce that a contract is only compiled with specific versions of the Sophia compiler, you can give one or more @compiler pragmas at the top-level (typically at the beginning) of a file. For instance, to enforce that a contract is compiled with version 4.3 of the compiler you write @ compiler >= 4 . 3 @ compiler < 4 . 4 Valid operators in compiler pragmas are < , =< , == , >= , and > . Version numbers are given as a sequence of non-negative integers separated by dots. Trailing zeros are ignored, so 4.0.0 == 4 . If a constraint is violated an error is reported and compilation fails. Exceptions Contracts can fail with an (uncatchable) exception using the built-in function abort ( reason : string ) : 'a Calling abort causes the top-level call transaction to return an error result containing the reason string. Only the gas used up to and including the abort call is charged. This is different from termination due to a crash which consumes all available gas. For convenience the following function is also built-in: function require ( b : bool , err : string ) = if ( ! b ) abort ( err ) Aside from that, there is an almost equivalent function exit exit ( reason : string ) : 'a Just like abort , it breaks the execution with the given reason. The difference however is in the gas consumption \u2014 while abort returns unused gas, a call to exit burns it all. Delegation signature Some chain operations ( Oracle. and AENS. ) have an optional delegation signature. This is typically used when a user/accounts would like to allow a contract to act on it's behalf. The exact data to be signed varies for the different operations, but in all cases you should prepend the signature data with the network_id ( ae_mainnet for the \u00e6ternity mainnet, etc.).","title":"Features"},{"location":"sophia_features/#features","text":"","title":"Features"},{"location":"sophia_features/#contracts","text":"The main unit of code in Sophia is the contract . A contract implementation, or simply a contract, is the code for a smart contract and consists of a list of types, entrypoints and local functions. Only the entrypoints can be called from outside the contract. A contract instance is an entity living on the block chain (or in a state channel). Each instance has an address that can be used to call its entrypoints, either from another contract or in a call transaction. A contract may define a type state encapsulating its local state. When creating a new contract the init entrypoint is executed and the state is initialized to its return value. The language offers some primitive functions to interact with the blockchain and contracts. Please refer to the Chain , Contract and the Call namespaces in the documentation.","title":"Contracts"},{"location":"sophia_features/#calling-other-contracts","text":"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 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 ) 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 ) 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). To construct a value of a contract type you can give a contract address literal (for instance ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay ), or convert an account address to a contract address using Address.to_contract . Note that if the contract does not exist, or it doesn't have the entrypoint, or the type of the entrypoint does not match the stated contract type, the call 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 )","title":"Calling other contracts"},{"location":"sophia_features/#protected-contract-calls","text":"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 ). The protected argument must be a literal boolean, and when set to true changes the type of the contract call, wrapping the result in an option type. If the call fails the result is 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\" 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. However, note that errors that would normally consume all the gas in the transaction still only uses up the gas spent running the contract. Any side effects (state change, token transfers, etc.) made by a failing protected call is rolled back, just like they would be in the unprotected case.","title":"Protected contract calls"},{"location":"sophia_features/#contract-factories-and-child-contracts","text":"Since the version 6.0.0 Sophia supports deploying contracts by other contracts. This can be done in two ways: Contract cloning via Chain.clone Direct deploy via Chain.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 \u2013 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 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.","title":"Contract factories and child contracts"},{"location":"sophia_features/#contract-interfaces-and-polymorphism","text":"Contracts can implement one or multiple interfaces, the contract has to define every entrypoint from the implemented interface and the entrypoints in both the contract and implemented interface should have compatible types. contract interface Animal = entrypoint sound : () => string contract Cat : Animal = entrypoint sound() = \"Cat sound\" Contract interfaces can extend other interfaces. An extended interface has to declare all entrypoints from every parent interface. All the declarations in the extended interface must have types compatible with the declarations from the parent interface. contract interface II = entrypoint f : () => unit contract interface I : II = entrypoint f : () => unit entrypoint g : () => unit contract C : I = entrypoint f() = () entrypoint g() = () It is only possible to implement (or extend) an interface that has been already defined earlier in the file (or in an included file). Therefore recursive interface implementation is not allowed in Sophia. // The following code would show an error contract interface X : Z = entrypoint x : () => int contract interface Y : X = entrypoint x : () => int entrypoint y : () => int contract interface Z : Y = entrypoint x : () => int entrypoint y : () => int entrypoint z : () => int contract C : Z = entrypoint x() = 1 entrypoint y() = 1 entrypoint z() = 1","title":"Contract interfaces and polymorphism"},{"location":"sophia_features/#mutable-state","text":"Sophia does not have arbitrary mutable state, but only a limited form of state associated with each contract instance. Each contract defines a type state encapsulating its mutable state. The type state defaults to the unit . The initial state of a contract is computed by the contract's init function. The init function is pure and returns the initial state as its return value. If the type state is unit , the init function defaults to returning the value () . At contract creation time, the init function is executed and its result is stored as the contract state. The value of the state is accessible from inside the contract through an implicitly bound variable state . State updates are performed by calling a function put : state => unit . Aside from the put function (and similar functions for transactions and events), the language is purely functional. Functions modifying the state need to be annotated with the stateful keyword (see below). To make it convenient to update parts of a deeply nested state Sophia provides special syntax for map/record updates.","title":"Mutable state"},{"location":"sophia_features/#stateful-functions","text":"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 ) Without the stateful annotation the compiler does not allow the call to put . A stateful annotation is required to Use a stateful primitive function. These are put Chain.spend Oracle.register Oracle.query Oracle.respond Oracle.extend AENS.preclaim AENS.claim AENS.transfer AENS.revoke AENS.update Call a stateful function in the current contract Call another contract with a non-zero value argument. A stateful annotation is not required to Read the contract state. Issue an event using the event function. Call another contract with value = 0 , even if the called function is stateful.","title":"Stateful functions"},{"location":"sophia_features/#payable","text":"","title":"Payable"},{"location":"sophia_features/#payable-contracts","text":"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 () = ... If in doubt, it is possible to check if an address is payable using Address.is_payable(addr) .","title":"Payable contracts"},{"location":"sophia_features/#payable-entrypoints","text":"A contract entrypoint is by default not payable. Any call to such a function (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\" )","title":"Payable entrypoints"},{"location":"sophia_features/#namespaces","text":"Code can be split into libraries using the namespace construct. Namespaces 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 )) 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\" using Pair contract C = type state = int entrypoint init() = let p = (1, 2) 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\" 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 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 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 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 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.","title":"Namespaces"},{"location":"sophia_features/#splitting-code-over-multiple-files","text":"Code from another file can be included in a contract using an include statement. These must appear at the top-level (outside the main contract). The included file can contain one or more namespaces and abstract contracts. For example, if the file library.aes contains 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 )) 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, so even cyclic includes should be working without any special tinkering.","title":"Splitting code over multiple files"},{"location":"sophia_features/#standard-library","text":"Sophia offers standard library which exposes some primitive operations and some higher level utilities. The builtin 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 )))","title":"Standard library"},{"location":"sophia_features/#types","text":"Sophia has the following types: Type Description Example int A 2-complement integer -1 address \u00e6ternity address, 32 bytes Call.origin bool A Boolean true bits A bit field Bits.none bytes(n) A byte array with n bytes #fedcba9876543210 string An array of bytes \"Foo\" list A homogeneous immutable singly linked list. [1, 2, 3] ('a, 'b) => 'c A function. Parentheses can be skipped if there is only one argument (x : int, y : int) => x + y tuple An ordered heterogeneous array (42, \"Foo\", true) record An immutable key value store with fixed key names and typed values record balance = { owner: address, value: int } map An immutable key value store with dynamic mapping of keys of one type to values of one type type accounts = map(string, address) option('a) An optional value either None or Some('a) Some(42) state A user defined type holding the contract state record state = { owner: address, magic_key: bytes(4) } event An append only list of blockchain events (or log entries) datatype event = EventX(indexed int, string) hash A 32-byte hash - equivalent to bytes(32) signature A signature - equivalent to bytes(64) Chain.ttl Time-to-live (fixed height or relative to current block) FixedTTL(1050) RelativeTTL(50) oracle('a, 'b) And oracle answering questions of type 'a with answers of type 'b Oracle.register(acct, qfee, ttl) oracle_query('a, 'b) A specific oracle query Oracle.query(o, q, qfee, qttl, rttl) contract A user defined, typed, contract address function call_remote(r : RemoteContract) = r.fun()","title":"Types"},{"location":"sophia_features/#literals","text":"Type Constant/Literal example(s) int -1 , 2425 , 4598275923475723498573485768 address ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt bool true , false bits Bits.none , Bits.all bytes(8) #fedcba9876543210 string \"This is a string\" list [1, 2, 3] , [(true, 24), (false, 19), (false, -42)] tuple (42, \"Foo\", true) record { owner = Call.origin, value = 100000000 } map {[\"foo\"] = 19, [\"bar\"] = 42} , {} option(int) Some(42) , None state state{ owner = Call.origin, magic_key = #a298105f } event EventX(0, \"Hello\") hash #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f signature #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f Chain.ttl FixedTTL(1050) , RelativeTTL(50) oracle('a, 'b) ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 oracle_query('a, 'b) oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY contract ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ","title":"Literals"},{"location":"sophia_features/#arithmetic","text":"Sophia integers ( int ) are represented by arbitrary-sized signed words and support the following arithmetic operations: - addition ( x + y ) - subtraction ( x - y ) - multiplication ( x * y ) - division ( x / y ), truncated towards zero - remainder ( x mod y ), satisfying y * (x / y) + x mod y == x for non-zero y - exponentiation ( x ^ y ) All operations are safe with respect to overflow and underflow. The division and modulo operations throw an arithmetic error if the right-hand operand is zero.","title":"Arithmetic"},{"location":"sophia_features/#bit-fields","text":"Sophia integers do not support bit arithmetic. Instead there is a separate type bits . See the standard library documentation . A bit field can be of arbitrary size (but it is still represented by the corresponding integer, so setting very high bits can be expensive).","title":"Bit fields"},{"location":"sophia_features/#type-aliases","text":"Type aliases can be introduced with the type keyword and can be parameterized. For instance type number = int type string_map ( 'a ) = map ( string , 'a ) A type alias and its definition can be used interchangeably. Sophia does not support higher-kinded types, meaning that following type alias is invalid: type wrap('f, 'a) = 'f('a)","title":"Type aliases"},{"location":"sophia_features/#algebraic-data-types","text":"Sophia supports algebraic data types (variant types) and pattern matching. Data types are declared by giving a list of constructors with their respective arguments. For instance, 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: function get_left ( x : one_or_both ( 'a , 'b )) : option ( 'a ) = switch ( x ) Left ( x ) => Some ( x ) Right ( _ ) => None Both ( x , _ ) => Some ( x ) or directly in the left-hand side: function get_left : one_or_both ( 'a , 'b ) => option ( 'a ) get_left ( Left ( x )) = Some ( x ) get_left ( Right ( _ )) = None get_left ( Both ( x , _ )) = Some ( x ) NOTE: Data types cannot currently be recursive. Sophia also supports the assignment of patterns to variables: function f ( x ) = switch ( x ) h1 :: ( t = h2 :: _ ) => ( h1 + h2 ) :: t // same as `h1::h2::k => (h1 + h2)::h2::k` _ => x function g ( p : int * option ( int )) : int = let ( a , ( o = Some ( b ))) = p // o is equal to Pair.snd(p) b Guards are boolean expressions that can be used on patterns in both switch statements and functions definitions. If a guard expression evaluates to true , then the corresponding body will be used. Otherwise, the next pattern will be checked: function get_left_if_positive ( x : one_or_both ( int , 'b )) : option ( int ) = switch ( x ) Left ( x ) | x > 0 => Some ( x ) Both ( x , _ ) | x > 0 => Some ( x ) _ => None function get_left_if_positive : one_or_both ( int , 'b ) => option ( int ) get_left_if_positive ( Left ( x )) | x > 0 = Some ( x ) get_left_if_positive ( Both ( x , _ )) | x > 0 = Some ( x ) get_left_if_positive ( _ ) = None Guards cannot be stateful even when used inside a stateful function.","title":"Algebraic data types"},{"location":"sophia_features/#lists","text":"A Sophia list is a dynamically sized, homogenous, immutable, singly linked list. A list is constructed with the syntax [1, 2, 3] . The 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 list('e) . For example we can have the following lists: [ 1 , 33 , 2 , 666 ] : list ( int ) [( 1 , \"aaa\" ), ( 10 , \"jjj\" ), ( 666 , \"the beast\" )] : list ( int * string ) [{[ 1 ] = \"aaa\" , [ 10 ] = \"jjj\" }, {[ 5 ] = \"eee\" , [ 666 ] = \"the beast\" }] : list ( map ( int , string )) New elements can be prepended to the front of a list with the :: operator. So 42 :: [1, 2, 3] returns the list [42, 1, 2, 3] . The concatenation operator ++ appends its second argument to its first and returns the resulting list. So concatenating two lists [1, 22, 33] ++ [10, 18, 55] returns the list [1, 22, 33, 10, 18, 55] . 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] Lists can be constructed using the range syntax using special .. operator: [ 1 .. 4 ] == [ 1 , 2 , 3 , 4 ] The ranges are always ascending and have step equal to 1. Please refer to the standard library for the predefined functionalities.","title":"Lists"},{"location":"sophia_features/#maps-and-records","text":"A Sophia record type is given by a fixed set of fields with associated, possibly different, types. For instance record account = { name : string , balance : int , history : list ( transaction ) } Maps, on the other hand, can contain an arbitrary number of key-value bindings, but of a fixed type. The type of maps with keys of type 'k and values of type 'v is written map('k, 'v) . The key type can be any type that does not contain a map or a function type. Please refer to the standard library for the predefined functionalities.","title":"Maps and records"},{"location":"sophia_features/#constructing-maps-and-records","text":"A value of record type is constructed by giving a value for each of the fields. For the example above, function new_account ( name ) = { name = name , balance = 0 , history = []} Maps are constructed similarly, with keys enclosed in square brackets function example_map () : map ( string , int ) = {[ \"key1\" ] = 1 , [ \"key2\" ] = 2 } The empty map is written {} .","title":"Constructing maps and records"},{"location":"sophia_features/#accessing-values","text":"Record fields access is written r.f and map lookup m[k] . For instance, function get_balance ( a : address , accounts : map ( address , account )) = accounts [ a ]. balance Looking up a non-existing key in a map results in contract execution failing. A default value to return for non-existing keys can be provided using the syntax m[k = default] . See also Map.member and Map.lookup below.","title":"Accessing values"},{"location":"sophia_features/#updating-a-value","text":"Record field updates are written r{f = v} . This creates a new record value which is the same as r , but with the value of the field f replaced by v . Similarly, m{[k] = v} constructs a map with the same values as m except that k maps to v . It makes no difference if m has a mapping for k or not. It is possible to give a name to the old value of a field or mapping in an update: instead of acc{ balance = acc.balance + 100 } it is possible to write acc{ balance @ b = b + 100 } , binding b to acc.balance . When giving a name to a map value ( m{ [k] @ x = v } ), the corresponding key must be present in the map or execution fails, but a default value can be provided: m{ [k = default] @ x = v } . In this case x is bound to default if k is not in the map. Updates can be nested: function clear_history ( a : address , accounts : map ( address , account )) : map ( address , account ) = accounts { [ a ]. history = [] } 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 an account if a is not in the map you can write (given a function empty_account ): accounts { [ a = empty_account ()]. history = [] }","title":"Updating a value"},{"location":"sophia_features/#map-implementation","text":"Internally in the VM maps are implemented as hash maps and support fast lookup and update. Large maps can be stored in the contract state and the size of the map does not contribute to the gas costs of a contract call reading or updating it.","title":"Map implementation"},{"location":"sophia_features/#strings","text":"There is a builtin type string , which can be seen as an array of bytes. Strings can be compared for equality ( == , != ), used as keys in maps and records, and used in builtin functions String.length , String.concat and the hash functions described below. Please refer to the String library documentation .","title":"Strings"},{"location":"sophia_features/#chars","text":"There is a builtin type char (the underlying representation being an integer), mainly used to manipulate strings via String.to_list / String.from_list . Characters can also be introduced as character literals (`'x', '+', ...). Please refer to the Char library documentation .","title":"Chars"},{"location":"sophia_features/#byte-arrays","text":"Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system, for example the literal #cafe creates a two-element array of bytes ca (202) and fe (254) and thus is a value of type bytes(2) . Please refer to the Bytes library documentation .","title":"Byte arrays"},{"location":"sophia_features/#cryptographic-builtins","text":"Libraries Crypto and String provide functions to hash objects, verify signatures etc. The hash is a type alias for bytes(32) .","title":"Cryptographic builtins"},{"location":"sophia_features/#authorization-interface","text":"When a Generalized account is authorized, the authorization function needs access to the transaction and the transaction hash for the wrapped transaction. (A GAMetaTx wrapping a transaction.) The transaction and the transaction hash is available in the primitive Auth.tx and Auth.tx_hash respectively, they are only available during authentication if invoked by a normal contract call they return None .","title":"Authorization interface"},{"location":"sophia_features/#oracle-interface","text":"You can attach an oracle to the current contract and you can interact with oracles through the Oracle interface. For a full description of how Oracle works see Oracles . For a functionality documentation refer to the standard library .","title":"Oracle interface"},{"location":"sophia_features/#example","text":"Example for an oracle answering questions of type string with answers of type int : contract Oracles = stateful entrypoint registerOracle ( acct : address , sign : signature , // Signed network id + oracle address + contract address qfee : int , ttl : Chain . ttl ) : oracle ( string , int ) = Oracle . register ( acct , signature = sign , qfee , ttl ) entrypoint queryFee ( o : oracle ( string , int )) : int = Oracle . query_fee ( o ) payable stateful entrypoint createQuery ( o : oracle_query ( string , int ), q : string , qfee : int , qttl : Chain . ttl , rttl : int ) : oracle_query ( string , int ) = require ( qfee =< Call . value , \"insufficient value for qfee\" ) Oracle . query ( o , q , qfee , qttl , RelativeTTL ( rttl )) stateful entrypoint extendOracle ( o : oracle ( string , int ), ttl : Chain . ttl ) : unit = Oracle . extend ( o , ttl ) stateful entrypoint signExtendOracle ( o : oracle ( string , int ), sign : signature , // Signed network id + oracle address + contract address ttl : Chain . ttl ) : unit = Oracle . extend ( o , signature = sign , ttl ) stateful entrypoint respond ( o : oracle ( string , int ), q : oracle_query ( string , int ), sign : signature , // Signed network id + oracle query id + contract address r : int ) = Oracle . respond ( o , q , signature = sign , r ) entrypoint getQuestion ( o : oracle ( string , int ), q : oracle_query ( string , int )) : string = Oracle . get_question ( o , q ) entrypoint hasAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) = switch ( Oracle . get_answer ( o , q )) None => false Some ( _ ) => true entrypoint getAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) : option ( int ) = Oracle . get_answer ( o , q )","title":"Example"},{"location":"sophia_features/#sanity-checks","text":"When an Oracle literal is passed to a contract, no deep checks are performed. For extra safety Oracle.check and Oracle.check_query functions are provided.","title":"Sanity checks"},{"location":"sophia_features/#aens-interface","text":"Contracts can interact with the \u00e6ternity naming system . For this purpose the AENS library was exposed.","title":"AENS interface"},{"location":"sophia_features/#example_1","text":"In this example we assume that the name name already exists, and is owned by an account with address addr . In order to allow a contract ct to handle name the account holder needs to create a signature sig of addr | name.hash | ct.address . Armed with this information we can for example write a function that extends the name if it expires within 1000 blocks: stateful entrypoint extend_if_necessary ( addr : address , name : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , FixedTTL ( expiry ), _ )) => if ( Chain . block_height + 1000 > expiry ) AENS . update ( addr , name , Some ( RelativeTTL ( 50000 )), None , None , signature = sig ) And we can write functions that adds and removes keys from the pointers of the name: stateful entrypoint add_key ( addr : address , name : string , key : string , pt : AENS . pointee , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => AENS . update ( addr , name , None , None , Some ( ptrs {[ key ] = pt }), signature = sig ) stateful entrypoint delete_key ( addr : address , name : string , key : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => let ptrs = Map . delete ( key , ptrs ) AENS . update ( addr , name , None , None , Some ( ptrs ), signature = sig ) Note: From the Iris hardfork more strict rules apply for AENS pointers, when a Sophia contract lookup or update (bad) legacy pointers, the bad keys are automatically removed so they will not appear in the pointers map.","title":"Example"},{"location":"sophia_features/#events","text":"Sophia contracts log structured messages to an event log in the resulting blockchain transaction. The event log is quite similar to Events in Solidity . Events are further discussed in the protocol . To use events a contract must declare a datatype event , and events are then logged using the Chain.event function: datatype event = Event1 ( int , int , string ) | Event2 ( string , address ) Chain . event ( e : event ) : unit The event can have 0-3 indexed fields, and an optional payload field. A field is indexed if it fits in a 32-byte word, i.e. - bool - int - bits - address - oracle(_, _) - oracle_query(_, _) - contract types - bytes(n) for n \u2264 32, in particular hash The payload field must be either a string or a byte array of more than 32 bytes. The fields can appear in any order. NOTE: Indexing is not part of the core \u00e6ternity node. Events are emitted by using the Chain.event function. The following function will emit one Event of each kind in the example. entrypoint emit_events () : () = Chain . event ( Event1 ( 42 , 34 , \"foo\" )) Chain . event ( Event2 ( \"This is not indexed\" , Contract . address ))","title":"Events"},{"location":"sophia_features/#argument-order","text":"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. AnotherEvent ( string , indexed address ) ... Chain . event ( AnotherEvent ( \"This is not indexed\" , Contract . address )) would yield exactly the same result in the example above!","title":"Argument order"},{"location":"sophia_features/#compiler-pragmas","text":"To enforce that a contract is only compiled with specific versions of the Sophia compiler, you can give one or more @compiler pragmas at the top-level (typically at the beginning) of a file. For instance, to enforce that a contract is compiled with version 4.3 of the compiler you write @ compiler >= 4 . 3 @ compiler < 4 . 4 Valid operators in compiler pragmas are < , =< , == , >= , and > . Version numbers are given as a sequence of non-negative integers separated by dots. Trailing zeros are ignored, so 4.0.0 == 4 . If a constraint is violated an error is reported and compilation fails.","title":"Compiler pragmas"},{"location":"sophia_features/#exceptions","text":"Contracts can fail with an (uncatchable) exception using the built-in function abort ( reason : string ) : 'a Calling abort causes the top-level call transaction to return an error result containing the reason string. Only the gas used up to and including the abort call is charged. This is different from termination due to a crash which consumes all available gas. For convenience the following function is also built-in: function require ( b : bool , err : string ) = if ( ! b ) abort ( err ) Aside from that, there is an almost equivalent function exit exit ( reason : string ) : 'a Just like abort , it breaks the execution with the given reason. The difference however is in the gas consumption \u2014 while abort returns unused gas, a call to exit burns it all.","title":"Exceptions"},{"location":"sophia_features/#delegation-signature","text":"Some chain operations ( Oracle. and AENS. ) have an optional delegation signature. This is typically used when a user/accounts would like to allow a contract to act on it's behalf. The exact data to be signed varies for the different operations, but in all cases you should prepend the signature data with the network_id ( ae_mainnet for the \u00e6ternity mainnet, etc.).","title":"Delegation signature"},{"location":"sophia_stdlib/","text":"Standard library Sophia language offers standard library that consists of several namespaces. Some of them are already in the scope and do not need any actions to be used, while the others require some files to be included. The out-of-the-box namespaces are: Address AENS Auth Bits Bytes Call Chain Char Contract Crypto Int Map Oracle The following ones need to be included as regular files with .aes suffix, for example include \"List.aes\" Bitwise BLS12_381 Func Frac List Option Pair Set String Triple Builtin namespaces They are available without any explicit includes. Address to_str Address.to_str(a : address) : string Base58 encoded string is_contract Address.is_contract(a : address) : bool Is the address a contract is_oracle Address.is_oracle(a : address) : bool Is the address a registered oracle is_payable Address.is_payable(a : address) : bool Can the address be spent to to_contract Address.to_contract(a : address) : C Cast address to contract type C (where C is a contract) AENS The following functionality is available for interacting with the \u00e6ternity naming system (AENS). If owner is equal to Contract.address the signature signature is ignored, and can be left out since it is a named argument. Otherwise we need a signature to prove that we are allowed to do AENS operations on behalf of owner . The signature is tied to a network id , i.e. the signature material should be prefixed by the network id. Types name datatype name = Name(address, Chain.ttl, map(string, AENS.pointee)) pointee datatype pointee = AccountPt(address) | OraclePt(address) | ContractPt(address) | ChannelPt(address) Functions resolve AENS.resolve(name : string, key : string) : option('a) Name resolution. Here name should be a registered name and key one of the attributes associated with this name (for instance \"account_pubkey\" ). The return type ( 'a ) must be resolved at compile time to an atomic type and the value is type checked against this type at run time. lookup AENS.lookup(name : string) : option(AENS.name) If name is an active name AENS.lookup returns a name object. The three arguments to Name are owner , expiry and a map of the pointees for the name. Note: the expiry of the name is always a fixed TTL. For example: let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup(\"example.chain\") preclaim AENS.preclaim(owner : address, commitment_hash : hash, ) : unit The signature should be over network id + owner address + Contract.address (concatenated as byte arrays). claim AENS.claim(owner : address, name : string, salt : int, name_fee : int, ) : unit The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. transfer AENS.transfer(owner : address, new_owner : address, name : string, ) : unit Transfers name to the new owner. The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. revoke AENS.revoke(owner : address, name : string, ) : unit Revokes the name to extend the ownership time. The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. update AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int), new_ptrs : map(string, AENS.pointee), ) : unit Updates the name. If the optional parameters are set to None that parameter will not be updated, for example if None is passed as expiry the expiry block of the name is not changed. Auth tx Auth.tx : option(Chain.tx) Where Chain.tx is (built-in) defined like: namespace Chain = record tx = { paying_for : option(Chain.paying_for_tx) , ga_metas : list(Chain.ga_meta_tx) , actor : address , fee : int , ttl : int , tx : Chain.base_tx } datatype ga_meta_tx = GAMetaTx(address, int) datatype paying_for_tx = PayingForTx(address, int) datatype base_tx = SpendTx(address, int, string) | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string) | NameRevokeTx(hash) | NameTransferTx(address, string) | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) | | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address) | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address) | ContractCreateTx(int) | ContractCallTx(address, int) | GAAttachTx tx_hash Auth.tx_hash : option(hash) Gets the transaction hash during authentication. Bits none Bits.none : bits A bit field with all bits cleared all Bits.all : bits A bit field with all bits set set Bits.set(b : bits, i : int) : bits Set bit i clear Bits.clear(b : bits, i : int) : bits Clear bit i test Bits.test(b : bits, i : int) : bool Check if bit i is set sum Bits.sum(b : bits) : int Count the number of set bits union Bits.union(a : bits, b : bits) : bits Bitwise disjunction intersection Bits.intersection(a : bits, b : bits) : bits Bitwise conjunction difference Bits.difference(a : bits, b : bits) : bits Each bit is true if and only if it was 1 in a and 0 in b Bytes to_int Bytes.to_int(b : bytes(n)) : int Interprets the byte array as a big endian integer to_str Bytes.to_str(b : bytes(n)) : string Returns the hexadecimal representation of the byte array concat Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n) Concatenates two byte arrays split Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n) Splits a byte array at given index Call Values related to the call to the current contract origin Call.origin : address The address of the account that signed the call transaction that led to this call. caller Call.caller : address The address of the entity (possibly another contract) calling the contract. value Call.value : int The amount of coins transferred to the contract in the call. gas_price Call.gas_price : int The gas price of the current call. fee Call.fee : int The fee of the current call. gas_left Call.gas_left() : int The amount of gas left for the current call. Chain Values and functions related to the chain itself and other entities that live on it. Types tx record tx = { paying_for : option(Chain.paying_for_tx) , ga_metas : list(Chain.ga_meta_tx) , actor : address , fee : int , ttl : int , tx : Chain.base_tx } ga_meta_tx datatype ga_meta_tx = GAMetaTx(address, int) paying_for_tx datatype paying_for_tx = PayingForTx(address, int) base_tx datatype base_tx = SpendTx(address, int, string) | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string) | NameRevokeTx(hash) | NameTransferTx(address, string) | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) | | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address) | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address) | ContractCreateTx(int) | ContractCallTx(address, int) | GAAttachTx Functions balance Chain.balance(a : address) : int The balance of account a . block_hash Chain.block_hash(h : int) : option(bytes(32)) The hash of the block at height h . h has to be within 256 blocks from the current height of the chain or else the function will return None . NOTE: In FATE VM version 1 Chain.block_height was not considered an allowed height. From FATE VM version 2 (IRIS) it will return the block hash of the current generation. block_height Chain.block_height : int\" The height of the current block (i.e. the block in which the current call will be included). coinbase Chain.coinbase : address The address of the account that mined the current block. timestamp Chain.timestamp : int The timestamp of the current block. difficulty Chain.difficulty : int The difficulty of the current block. gas Chain.gas_limit : int The gas limit of the current block. bytecode_hash Chain.bytecode_hash : 'c => option(hash) Returns the 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 increases linearly to the size of the serialized bytecode of the deployed contract. 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 increases linearly with the size of the compiled child contract's bytecode. The source_hash on-chain entry of the newly created contract will be the SHA256 hash over concatenation of whole contract source code single null byte name of the child contract The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the value parameter. The value argument (default 0 ) is equivalent to the value in the contract creation transaction \u2013 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 new_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. 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. The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the value parameter. 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 \u2013 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 Chain.event(e : event) : unit Emits the event. To use this function one needs to define the event type as a datatype in the contract. Char to_int Char.to_int(c : char) : int Returns the UTF-8 codepoint of a character #### from_int Char.from_int(i : int) : option(char) Opposite of to_int . Returns None if the integer doesn't correspond to a single (normalized) codepoint. Contract Values related to the current contract creator Contract.creator : address Address of the entity that signed the contract creation transaction address Contract.address : address Address of the contract account balance Contract.balance : int Amount of coins in the contract account Crypto sha3 Crypto.sha3(x : 'a) : hash Hash any object to SHA3 sha256 Crypto.sha256(x : 'a) : hash Hash any object to SHA256 blake2b Crypto.blake2b(x : 'a) : hash Hash any object to blake2b verify_sig Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool Checks if the signature of msg was made using private key corresponding to the pubkey ecverify_secp256k1 Crypto.ecverify_secp256k1(msg : hash, addr : bytes(20), sig : bytes(65)) : bool Verifies a signature for a msg against an Ethereum style address. Note that the signature should be 65 bytes and include the recovery identifier byte V . The expected organization of the signature is ( V || R || S ). ecrecover_secp256k1 Crypto.ecrecover_secp256k1(msg : hash, sig : bytes(65)) : option(bytes(20)) Recovers the Ethereum style address from a msg hash and respective ECDSA-signature. Note that the signature should be 65 bytes and include the recovery identifier byte V . The expected organization of the signature is ( V || R || S ). verify_sig_secp256k1 Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : bool Verifies a standard 64-byte ECDSA signature ( R || S ). Int to_str Int.to_str : int => string Casts integer to string using decimal representation Map lookup Map.lookup(k : 'k, m : map('k, 'v)) : option('v) Returns the value under a key in given map as Some or None if the key is not present lookup_default Map.lookup_default(k : 'k, m : map('k, 'v), v : 'v) : 'v Returns the value under a key in given map or the default value v if the key is not present member Map.member(k : 'k, m : map('k, 'v)) : bool Checks if the key is present in the map delete Map.delete(k : 'k, m : map('k, 'v)) : map('k, 'v) Removes the key from the map size Map.size(m : map('k, 'v)) : int Returns the number of elements in the map to_list Map.to_list(m : map('k, 'v)) : list('k * 'v) Returns a list containing pairs of keys and their respective elements. from_list Map.from_list(m : list('k * 'v)) : map('k, 'v) Turns a list of pairs of form (key, value) into a map Oracle register Oracle.register(, acct : address, qfee : int, ttl : Chain.ttl) : oracle('a, 'b) Registers new oracle answering questions of type 'a with answers of type 'b . The acct is the address of the oracle to register (can be the same as the contract). signature is a signature proving that the contract is allowed to register the account - the network id + account address + contract address (concatenated as byte arrays) is signed with the private key of the account, proving you have the private key of the oracle to be. If the address is the same as the contract sign is ignored and can be left out entirely. The qfee is the minimum query fee to be paid by a user when asking a question of the oracle. The ttl is the Time To Live for the oracle in key blocks, either relative to the current key block height ( RelativeTTL(delta) ) or a fixed key block height ( FixedTTL(height) ). The type 'a is the type of the question to ask. The type 'b is the type of the oracle answers. Examples: Oracle.register(addr0, 25, RelativeTTL(400)) Oracle.register(addr1, 25, RelativeTTL(500), signature = sign1) get_question Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a Checks what was the question of query q on oracle o respond Oracle.respond(, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit Responds to the question q on o . Unless the contract address is the same as the oracle address the signature (which is an optional, named argument) needs to be provided. Proving that we have the private key of the oracle by signing the network id + oracle query id + contract address extend Oracle.extend(, o : oracle('a, 'b), ttl : Chain.ttl) : unit Extends TTL of an oracle. * singature is a named argument and thus optional. Must be the same as for Oracle.register * o is the oracle being extended * ttl must be RelativeTTL . The time to live of o will be extended by this value. query_fee Oracle.query_fee(o : oracle('a, 'b)) : int Returns the query fee of the oracle query Oracle.query(o : oracle('a, 'b), q : 'a, qfee : int, qttl : Chain.ttl, rttl : Chain.ttl) : oracle_query('a, 'b) Asks the oracle a question. * The qfee is the query fee debited to the contract account ( Contract.address ). * The qttl controls the last height at which the oracle can submit a response and can be either fixed or relative. * The rttl must be relative and controls how long an answer is kept on the chain. The call fails if the oracle could expire before an answer. get_answer Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b) Checks what is the optional query answer expiry Oracle.expiry(o : oracle('a, 'b)) : int Ask the oracle when it expires. The result is the block height at which it will happen. check Oracle.check(o : oracle('a, 'b)) : bool Returns true iff the oracle o exists and has correct type check_query Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool It returns true iff the oracle query exist and has the expected type. Includable namespaces These need to be explicitly included (with .aes suffix) Bitwise Bitwise operations on arbitrary precision integers. bsr Bitwise.bsr(n : int, x : int) : int Logical bit shift x right n positions. bsl Bitwise.bsl(n : int, x : int) : int Logical bit shift x left n positions. bsli Bitwise.bsli(n : int, x : int, lim : int) : int Logical bit shift x left n positions, limit to lim bits. band Bitwise.band(x : int, y : int) : int Bitwise and of x and y . bor Bitwise.bor(x : int, y : int) : int Bitwise or of x and y . bxor Bitwise.bxor(x : int, y : int) : int Bitwise xor of x and y . bnot Bitwise.bnot(x : int) : int Bitwise not of x . Defined and implemented as bnot(x) = bxor(x, -1) . uband Bitwise.uband(x : int, y : int) : int Bitwise and of non-negative numbers x and y . ubor Bitwise.ubor(x : int, y : int) : int Bitwise or of non-negative x and y . ubxor Bitwise.ubxor(x : int, y : int) : int Bitwise xor of non-negative x and y . BLS12_381 Types fr Built-in (Montgomery) integer representation 32 bytes fp Built-in (Montgomery) integer representation 48 bytes fp2 record fp2 = { x1 : fp, x2 : fp }` g1 record g1 = { x : fp, y : fp, z : fp } g2 record g2 = { x : fp2, y : fp2, z : fp2 } gt record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp } Functions pairing_check BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool Pairing check of a list of points, xs and ys should be of equal length. int_to_fr BLS12_381.int_to_fr(x : int) : fr Convert an integer to an fr - a 32 bytes internal (Montgomery) integer representation. int_to_fp BLS12_381.int_to_fp(x : int) : fp Convert an integer to an fp - a 48 bytes internal (Montgomery) integer representation. fr_to_int BLS12_381.fr_to_int(x : fr) : int Convert a fr value into an integer. fp_to_int BLS12_381.fp_to_int(x : fp) : int Convert a fp value into an integer. mk_g1 BLS12_381.mk_g1(x : int, y : int, z : int) : g1 Construct a g1 point from three integers. mk_g2 BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 Construct a g2 point from six integers. g1_neg BLS12_381.g1_neg(p : g1) : g1 Negate a g1 value. g1_norm BLS12_381.g1_norm(p : g1) : g1 Normalize a g1 value. g1_valid BLS12_381.g1_valid(p : g1) : bool Check that a g1 value is a group member. g1_is_zero BLS12_381.g1_is_zero(p : g1) : bool Check if a g1 value corresponds to the zero value of the group. g1_add BLS12_381.g1_add(p : g1, q : g1) : g1 Add two g1 values. g1_mul BLS12_381.g1_mul(k : fr, p : g1) : g1 Scalar multiplication for g1 . g2_neg BLS12_381.g2_neg(p : g2) : g2 Negate a g2 value. g2_norm BLS12_381.g2_norm(p : g2) : g2 Normalize a g2 value. g2_valid BLS12_381.g2_valid(p : g2) : bool Check that a g2 value is a group member. g2_is_zero BLS12_381.g2_is_zero(p : g2) : bool Check if a g2 value corresponds to the zero value of the group. g2_add BLS12_381.g2_add(p : g2, q : g2) : g2 Add two g2 values. g2_mul BLS12_381.g2_mul(k : fr, p : g2) : g2 Scalar multiplication for g2 . gt_inv BLS12_381.gt_inv(p : gt) : gt Invert a gt value. gt_add BLS12_381.gt_add(p : gt, q : gt) : gt Add two gt values. gt_mul BLS12_381.gt_mul(p : gt, q : gt) : gt Multiply two gt values. gt_pow BLS12_381.gt_pow(p : gt, k : fr) : gt Calculate exponentiation p ^ k . gt_is_one BLS12_381.gt_is_one(p : gt) : bool Compare a gt value to the unit value of the Gt group. pairing BLS12_381.pairing(p : g1, q : g2) : gt Compute the pairing of a g1 value and a g2 value. miller_loop BLS12_381.miller_loop(p : g1, q : g2) : gt Do the Miller loop stage of pairing for g1 and g2 . final_exp BLS12_381.final_exp(p : gt) : gt Perform the final exponentiation step of pairing for a gt value. Func Functional combinators. id Func.id(x : 'a) : 'a Identity function. Returns its argument. const Func.const(x : 'a) : 'b => 'a = (y) => x Constant function constructor. Given x returns a function that returns x regardless of its argument. flip Func.flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c Switches order of arguments of arity 2 function. comp Func.comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c Function composition. comp(f, g)(x) == f(g(x)) . pipe Func.pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c Flipped function composition. pipe(f, g)(x) == g(f(x)) . rapply Func.rapply(x : 'a, f : 'a => 'b) : 'b Reverse application. rapply(x, f) == f(x) . recur Func.recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res The Z combinator. Allows performing local recursion and having anonymous recursive lambdas. To make function A => B recursive the user needs to transform it to take two arguments instead \u2013 one of type A => B which is going to work as a self-reference, and the other one of type A which is the original argument. Therefore, transformed function should have (A => B, A) => B signature. Example usage: let factorial = recur((fac, n) => if(n < 2) 1 else n * fac(n - 1)) If the function is going to take more than one argument it will need to be either tuplified or have curried out latter arguments. Example (factorial with custom step): // tuplified version let factorial_t(n, step) = let fac(rec, args) = let (n, step) = args if(n < 2) 1 else n * rec((n - step, step)) recur(fac)((n, step)) // curried version let factorial_c(n, step) = let fac(rec, n) = (step) => if(n < 2) 1 else n * rec(n - 1)(step) recur(fac)(n)(step) iter Func.iter(n : int, f : 'a => 'a) : 'a => 'a n th composition of f with itself, for instance iter(3, f) is equivalent to (x) => f(f(f(x))) . curry Func.curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) Func.curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) Turns a function that takes n arguments into a curried function that takes one argument and returns a function that waits for the rest in the same manner. For instance curry2((a, b) => a + b)(1)(2) == 3 . uncurry Func.uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c Func.uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd Opposite to curry . tuplify Func.tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c Func.tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd Turns a function that takes n arguments into a function that takes an n-tuple. untuplify Func.untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c Func.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd Opposite to tuplify . Frac This namespace provides operations on rational numbers. A rational number is represented as a fraction of two integers which are stored internally in the frac datatype. The datatype consists of three constructors Neg/2 , Zero/0 and Pos/2 which determine the sign of the number. Both values stored in Neg and Pos need to be strictly positive integers. However, when creating a frac you should never use the constructors explicitly. Instead of that, always use provided functions like make_frac or from_int . This helps keeping the internal representation well defined. The described below functions take care of the normalization of the fractions \u2013 they won't grow if it is unnecessary. Please note that the size of frac can be still very big while the value is actually very close to a natural number \u2013 the division of two extremely big prime numbers will be as big as both of them. To face this issue the optimize function is provided. It will approximate the value of the fraction to fit in the given error margin and to shrink its size as much as possible. Important note: frac must not be compared using standard < -like operators. The operator comparison is not possible to overload at this moment, nor the language provides checkers to prevent unintended usage of them. Therefore the typechecker will allow that and the results of such comparison will be unspecified. You should use lt , geq , eq etc instead. Types frac datatype frac = Pos(int, int) | Zero | Neg(int, int) Internal representation of fractional numbers. First integer encodes the numerator and the second the denominator \u2013 both must be always positive, as the sign is being handled by the choice of the constructor. Functions make_frac Frac.make_frac(n : int, d : int) : frac Creates a fraction out of numerator and denominator. Automatically normalizes, so make_frac(2, 4) and make_frac(1, 2) will yield same results. num Frac.num(f : frac) : int Returns the numerator of a fraction. den Frac.den(f : frac) : int Returns the denominator of a fraction. to_pair Frac.to_pair(f : frac) : int * int Turns a fraction into a pair of numerator and denominator. sign Frac.sign(f : frac) : int Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively. to_str Frac.to_str(f : frac) : string Conversion to string. Does not display division by 1 or denominator if equals zero. simplify Frac.simplify(f : frac) : frac Reduces fraction to normal form if for some reason it is not in it. eq Frac.eq(a : frac, b : frac) : bool Checks if a is equal to b . neq Frac.neq(a : frac, b : frac) : bool Checks if a is not equal to b . geq Frac.geq(a : frac, b : frac) : bool Checks if a is greater or equal to b . leq Frac.leq(a : frac, b : frac) : bool Checks if a is lesser or equal to b . gt Frac.gt(a : frac, b : frac) : bool Checks if a is greater than b . lt Frac.lt(a : frac, b : frac) : bool Checks if a is lesser than b . min Frac.min(a : frac, b : frac) : frac Chooses lesser of the two fractions. max Frac.max(a : frac, b : frac) : frac Chooses greater of the two fractions. abs Frac.abs(f : frac) : frac Absolute value. from_int Frac.from_int(n : int) : frac From integer conversion. Effectively make_frac(n, 1) . floor Frac.floor(f : frac) : int Rounds a fraction to the nearest lesser or equal integer. ceil Frac.ceil(f : frac) : int Rounds a fraction to the nearest greater or equal integer. round_to_zero Frac.round_to_zero(f : frac) : int Rounds a fraction towards zero. Effectively ceil if lesser than zero and floor if greater. round_from_zero Frac.round_from_zero(f : frac) : int Rounds a fraction from zero. Effectively ceil if greater than zero and floor if lesser. round Frac.round(f : frac) : int Rounds a fraction to a nearest integer. If two integers are in the same distance it will choose the even one. add Frac.add(a : frac, b : frac) : frac Sum of the fractions. neg Frac.neg(a : frac) : frac Negation of the fraction. sub Frac.sub(a : frac, b : frac) : frac Subtraction of two fractions. inv Frac.inv(a : frac) : frac Inverts a fraction. Throws error if a is zero. mul Frac.mul(a : frac, b : frac) : frac Multiplication of two fractions. div Frac.div(a : frac, b : frac) : frac Division of two fractions. int_exp Frac.int_exp(b : frac, e : int) : frac Takes b to the power of e . The exponent can be a negative value. optimize Frac.optimize(f : frac, loss : frac) : frac Shrink the internal size of a fraction as much as possible by approximating it to the point where the error would exceed the loss value. is_sane Frac.is_sane(f : frac) : bool For debugging. If it ever returns false in a code that doesn't call frac constructors or accept arbitrary frac s from the surface you should report it as a bug If you expect getting calls with malformed frac s in your contract, you should use this function to verify the input. List This module contains common operations on lists like constructing, querying, traversing etc. is_empty List.is_empty(l : list('a)) : bool Returns true iff the list is equal to [] . first List.first(l : list('a)) : option('a) Returns Some of the first element of a list or None if the list is empty. tail List.tail(l : list('a)) : option(list('a)) Returns Some of a list without its first element or None if the list is empty. last List.last(l : list('a)) : option('a) Returns Some of the last element of a list or None if the list is empty. contains List.contains(e : 'a, l : list('a)) : bool Checks if list l contains element e . Equivalent to List.find(x => x == e, l) != None . find List.find(p : 'a => bool, l : list('a)) : option('a) Finds first element of l fulfilling predicate p as Some or None if no such element exists. find_indices List.find_indices(p : 'a => bool, l : list('a)) : list(int) Returns list of all indices of elements from l that fulfill the predicate p . nth List.nth(n : int, l : list('a)) : option('a) Gets n th element of l as Some or None if l is shorter than n + 1 or n is negative. get List.get(n : int, l : list('a)) : 'a Gets n th element of l forcefully, throwing and error if l is shorter than n + 1 or n is negative. length List.length(l : list('a)) : int Returns length of a list. from_to List.from_to(a : int, b : int) : list(int) Creates an ascending sequence of all integer numbers between a and b (including a and b ). from_to_step List.from_to_step(a : int, b : int, step : int) : list(int) Creates an ascending sequence of integer numbers betweeen a and b jumping by given step . Includes a and takes b only if (b - a) mod step == 0 . step should be bigger than 0. replace_at List.replace_at(n : int, e : 'a, l : list('a)) : list('a) Replaces n th element of l with e . Throws an error if n is negative or would cause an overflow. insert_at List.insert_at(n : int, e : 'a, l : list('a)) : list('a) Inserts e into l to be on position n by shifting following elements further. For instance, insert_at(2, 9, [1,2,3,4]) will yield [1,2,9,3,4] . insert_by List.insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) Assuming that cmp represents < comparison, inserts x before the first element in the list l which is greater than it. For instance, insert_by((a, b) => a < b, 4, [1,2,3,5,6,7]) will yield [1,2,3,4,5,6,7] foldr List.foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b Right fold of a list. Assuming l = [x, y, z] will return f(x, f(y, f(z, nil))) . Not tail recursive. foldl List.foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b Left fold of a list. Assuming l = [x, y, z] will return f(f(f(acc, x), y), z) . Tail recursive. foreach List.foreach(l : list('a), f : 'a => unit) : unit Evaluates f on each element of a list. reverse List.reverse(l : list('a)) : list('a) Returns a copy of l with reversed order of elements. map List.map(f : 'a => 'b, l : list('a)) : list('b) Maps function f over a list. For instance map((x) => x == 0, [1, 2, 0, 3, 0]) will yield [false, false, true, false, true] flat_map List.flat_map(f : 'a => list('b), l : list('a)) : list('b) Maps f over a list and then flattens it. For instance flat_map((x) => [x, x * 10], [1, 2, 3]) will yield [1, 10, 2, 20, 3, 30] filter List.filter(p : 'a => bool, l : list('a)) : list('a) Filters out elements of l that fulfill predicate p . For instance filter((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3]) will yield [1, 1, 2] take List.take(n : int, l : list('a)) : list('a) Takes n first elements of l . Fails if n is negative. If n is greater than length of a list it will return whole list. drop List.drop(n : int, l : list('a)) : list('a) Removes n first elements of l . Fails if n is negative. If n is greater than length of a list it will return [] . take_while List.take_while(p : 'a => bool, l : list('a)) : list('a) Returns longest prefix of l in which all elements fulfill p . drop_while List.drop_while(p : 'a => bool, l : list('a)) : list('a) Removes longest prefix from l in which all elements fulfill p . partition List.partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) Separates elements of l that fulfill p and these that do not. Elements fulfilling predicate will be in the right list. For instance partition((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3]) will yield ([1, 1, 2], [-1, -2, 0, -3]) flatten List.flatten(ll : list(list('a))) : list('a) Flattens a list of lists into a one list. all List.all(p : 'a => bool, l : list('a)) : bool Checks if all elements of a list fulfill predicate p . any List.any(p : 'a => bool, l : list('a)) : bool Checks if any element of a list fulfills predicate p . sum List.sum(l : list(int)) : int Sums elements of a list. Returns 0 if the list is empty. product List.product(l : list(int)) : int Multiplies elements of a list. Returns 1 if the list is empty. zip_with List.zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) \"zips\" two lists with a function. n-th element of resulting list will be equal to f(x1, x2) where x1 and x2 are n-th elements of l1 and l2 respectively. Will cut off the tail of the longer list. For instance zip_with((a, b) => a + b, [1,2], [1,2,3]) will yield [2,4] zip List.zip(l1 : list('a), l2 : list('b)) : list('a * 'b) Special case of zip_with where the zipping function is (a, b) => (a, b) . unzip List.unzip(l : list('a * 'b)) : list('a) * list('b) Opposite to the zip operation. Takes a list of pairs and returns pair of lists with respective elements on same indices. merge List.merge(lesser_cmp : ('a, 'a) => bool, l1 : list('a), l2 : list('a)) : list('a) Merges two sorted lists into a single sorted list. O(length(l1) + length(l2)) sort List.sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) Sorts a list using given comparator. lesser_cmp(x, y) should return true iff x < y . If lesser_cmp is not transitive or there exists an element x such that lesser_cmp(x, x) or there exists a pair of elements x and y such that lesser_cmp(x, y) && lesser_cmp(y, x) then the result is undefined. O(length(l) * log_2(length(l))). intersperse List.intersperse(delim : 'a, l : list('a)) : list('a) Intersperses elements of l with delim . Does nothing on empty lists and singletons. For instance intersperse(0, [1, 2, 3, 4]) will yield [1, 0, 2, 0, 3, 0, 4] enumerate List.enumerate(l : list('a)) : list(int * 'a) Equivalent to zip with [0..length(l)] , but slightly faster. Option Common operations on option types and lists of option s. is_none Option.is_none(o : option('a)) : bool Returns true iff o == None is_some Option.is_some(o : option('a)) : bool Returns true iff o is not None . match Option.match(n : 'b, s : 'a => 'b, o : option('a)) : 'b Behaves like pattern matching on option using two case functions. default Option.default(def : 'a, o : option('a)) : 'a Escapes option wrapping by providing default value for None . force Option.force(o : option('a)) : 'a Forcefully escapes the option wrapping assuming it is Some . Aborts on None . force_msg Option.force_msg(o : option('a), err : string) : 'a Forcefully escapes the option wrapping assuming it is Some . Aborts with err error message on None . contains Option.contains(e : 'a, o : option('a)) : bool Returns true if and only if o contains element equal to e . Equivalent to Option.match(false, x => x == e, o) . on_elem Option.on_elem(o : option('a), f : 'a => unit) : unit Evaluates f on element under Some . Does nothing on None . map Option.map(f : 'a => 'b, o : option('a)) : option('b) Maps element under Some . Leaves None unchanged. map2 Option.map2(f : ('a, 'b) => 'c, o1 : option('a), o2 : option('b)) : option('c) Applies arity 2 function over two option s' elements. Returns Some iff both of o1 and o2 were Some , or None otherwise. For instance map2((a, b) => a + b, Some(1), Some(2)) will yield Some(3) and map2((a, b) => a + b, Some(1), None) will yield None . map3 Option.map3(f : ('a, 'b, 'c) => 'd, o1 : option('a), o2 : option('b), o3 : option('c)) : option('d) Same as map2 but with arity 3 function. app_over Option.app_over(f : option ('a => 'b), o : option('a)) : option('b) Applies function under option over argument under option . If either of them is None the result will be None as well. For instance app_over(Some((x) => x + 1), Some(1)) will yield Some(2) and app_over(Some((x) => x + 1), None) will yield None . flat_map Option.flat_map(f : 'a => option('b), o : option('a)) : option('b) Performs monadic bind on an option . Extracts element from o (if present) and forms new option from it. For instance flat_map((x) => Some(x + 1), Some(1)) will yield Some(2) and flat_map((x) => Some(x + 1), None) will yield None . to_list Option.to_list(o : option('a)) : list('a) Turns o into an empty (if None ) or singleton (if Some ) list. filter_options Option.filter_options(l : list(option('a))) : list('a) Removes None s from list and unpacks all remaining Some s. For instance filter_options([Some(1), None, Some(2)]) will yield [1, 2] . seq_options Option.seq_options(l : list (option('a))) : option (list('a)) Tries to unpack all elements of a list from Some s. Returns None if at least element of l is None . For instance seq_options([Some(1), Some(2)]) will yield Some([1, 2]) , but seq_options([Some(1), Some(2), None]) will yield None . choose Option.choose(o1 : option('a), o2 : option('a)) : option('a) Out of two option s choose the one that is Some , or None if both are None s. choose_first Option.choose_first(l : list(option('a))) : option('a) Same as choose , but chooses from a list insted of two arguments. Pair Common operations on 2-tuples. fst Pair.fst(t : ('a * 'b)) : 'a First element projection. snd Pair.snd(t : ('a * 'b)) : 'b Second element projection. map1 Pair.map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) Applies function over first element. map2 Pair.map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) Applies function over second element. bimap Pair.bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) Applies functions over respective elements. swap Pair.swap(t : ('a * 'b)) : ('b * 'a) Swaps elements. Set Types record set('a) = { to_map : map('a, unit) } Functions new Set.new() : set('a) Returns an empty set member member(e : 'a, s : set('a)) : bool Checks if the element e is present in the set s insert insert(e : 'a, s : set('a)) : set('a) Inserts the element e in the set s delete Set.delete(e : 'a, s : set('a)) : set('a) Removes the element e from the set s size size(s : set('a)) : int Returns the number of elements in the set s to_list Set.to_list(s : set('a)) : list('a) Returns a list containing the elements of the set s from_list Set.from_list(l : list('a)) : set('a) Turns the list l into a set filter Set.filter(p : 'a => bool, s : set('a)) : set('a) Filters out elements of s that fulfill predicate p fold Set.fold(f : ('a, 'b) => 'b, acc : 'b, s : set('a)) : 'b Folds the function f over every element in the set s and returns the final value of the accumulator acc . subtract Set.subtract(s1 : set('a), s2 : set('a)) : set('a) Returns the elements of s1 that are not members of s2 intersection Set.intersection(s1 : set('a), s2 : set('a)) : set('a) Returns the intersection of the two sets s1 and s2 intersection_list Set.intersection_list(sets : list(set('a))) : set('a) Returns the intersection of all the sets in the given list union Set.union(s1 : set('a), s2 : set('a)) : set('a) Returns the union of the two sets s1 and s2 union_list Set.union_list(sets : list(set('a))) : set('a) Returns the union of all the sets in the given list String Operations on the string type. A string is a UTF-8 encoded byte array. length length(s : string) : int The length of a string. Note: not equivalent to byte size of the string, rather List.length(String.to_list(s)) concat concat(s1 : string, s2 : string) : string Concatenates s1 and s2 . concats concats(ss : list(string)) : string Concatenates a list of strings. to_list to_list(s : string) : list(char) Converts a string to a list of char - the code points are normalized, but composite characters are possibly converted to multiple char s. For example the string \"\ud83d\ude1ci\u0307\" is converted to [128540,105,775] - where the smiley is the first code point and the strangely dotted i becomes [105, 775] . from_list from_list(cs : list(char)) : string Converts a list of characters into a normalized UTF-8 string. to_lower to_lower(s : string) : string Converts a string to lowercase. to_upper to_upper(s : string) : string Converts a string to uppercase. at at(ix : int, s : string) : option(char) Returns the character/codepoint at (zero-based) index ix . Basically the equivalent to List.nth(ix, String.to_list(s)) . split split(ix : int, s:string) : string * string Splits a string at (zero-based) index ix . contains contains(str : string, pat : string) : option(int) Searches for pat in str , returning Some(ix) if pat is a substring of str starting at position ix , otherwise returns None . tokens tokens(str : string, pat : string) : list(string) Splits str into tokens, pat is the divider of tokens. to_int to_int(s : string) : option(int) Converts a decimal (\"123\", \"-253\") or a hexadecimal (\"0xa2f\", \"-0xBBB\") string into an integer. If the string doesn't contain a valid number None is returned. sha3 sha3(s : string) : hash Computes the SHA3/Keccak hash of the string. sha256 sha256(s : string) : hash Computes the SHA256 hash of the string. blake2b blake2b(s : string) : hash Computes the Blake2B hash of the string. Triple fst Triple.fst(t : ('a * 'b * 'c)) : 'a First element projection. snd Triple.snd(t : ('a * 'b * 'c)) : 'b Second element projection. thd Triple.thd(t : ('a * 'b * 'c)) : 'c Third element projection. map1 Triple.map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) Applies function over first element. map2 Triple.map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) Applies function over second element. map3 Triple.map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) Applies function over third element. trimap Triple.trimap(f : 'a => 'x, g : 'b => 'y, h : 'c => 'z, t : ('a * 'b * 'c)) : ('x * 'y * 'z) Applies functions over respective elements. swap Triple.swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) Swaps first and third element. rotr Triple.rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) Cyclic rotation of the elements to the right. rotl Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) Cyclic rotation of the elements to the left.","title":"Standard library"},{"location":"sophia_stdlib/#standard-library","text":"Sophia language offers standard library that consists of several namespaces. Some of them are already in the scope and do not need any actions to be used, while the others require some files to be included. The out-of-the-box namespaces are: Address AENS Auth Bits Bytes Call Chain Char Contract Crypto Int Map Oracle The following ones need to be included as regular files with .aes suffix, for example include \"List.aes\" Bitwise BLS12_381 Func Frac List Option Pair Set String Triple","title":"Standard library"},{"location":"sophia_stdlib/#builtin-namespaces","text":"They are available without any explicit includes.","title":"Builtin namespaces"},{"location":"sophia_stdlib/#address","text":"","title":"Address"},{"location":"sophia_stdlib/#aens","text":"The following functionality is available for interacting with the \u00e6ternity naming system (AENS). If owner is equal to Contract.address the signature signature is ignored, and can be left out since it is a named argument. Otherwise we need a signature to prove that we are allowed to do AENS operations on behalf of owner . The signature is tied to a network id , i.e. the signature material should be prefixed by the network id.","title":"AENS"},{"location":"sophia_stdlib/#auth","text":"","title":"Auth"},{"location":"sophia_stdlib/#bits","text":"","title":"Bits"},{"location":"sophia_stdlib/#bytes","text":"","title":"Bytes"},{"location":"sophia_stdlib/#call","text":"Values related to the call to the current contract","title":"Call"},{"location":"sophia_stdlib/#chain","text":"Values and functions related to the chain itself and other entities that live on it.","title":"Chain"},{"location":"sophia_stdlib/#char","text":"","title":"Char"},{"location":"sophia_stdlib/#contract","text":"Values related to the current contract","title":"Contract"},{"location":"sophia_stdlib/#crypto","text":"","title":"Crypto"},{"location":"sophia_stdlib/#int","text":"","title":"Int"},{"location":"sophia_stdlib/#map","text":"","title":"Map"},{"location":"sophia_stdlib/#oracle","text":"","title":"Oracle"},{"location":"sophia_stdlib/#includable-namespaces","text":"These need to be explicitly included (with .aes suffix)","title":"Includable namespaces"},{"location":"sophia_stdlib/#bitwise","text":"Bitwise operations on arbitrary precision integers.","title":"Bitwise"},{"location":"sophia_stdlib/#bls12_381","text":"","title":"BLS12\u000295\u0003381"},{"location":"sophia_stdlib/#func","text":"Functional combinators.","title":"Func"},{"location":"sophia_stdlib/#frac","text":"This namespace provides operations on rational numbers. A rational number is represented as a fraction of two integers which are stored internally in the frac datatype. The datatype consists of three constructors Neg/2 , Zero/0 and Pos/2 which determine the sign of the number. Both values stored in Neg and Pos need to be strictly positive integers. However, when creating a frac you should never use the constructors explicitly. Instead of that, always use provided functions like make_frac or from_int . This helps keeping the internal representation well defined. The described below functions take care of the normalization of the fractions \u2013 they won't grow if it is unnecessary. Please note that the size of frac can be still very big while the value is actually very close to a natural number \u2013 the division of two extremely big prime numbers will be as big as both of them. To face this issue the optimize function is provided. It will approximate the value of the fraction to fit in the given error margin and to shrink its size as much as possible. Important note: frac must not be compared using standard < -like operators. The operator comparison is not possible to overload at this moment, nor the language provides checkers to prevent unintended usage of them. Therefore the typechecker will allow that and the results of such comparison will be unspecified. You should use lt , geq , eq etc instead.","title":"Frac"},{"location":"sophia_stdlib/#list","text":"This module contains common operations on lists like constructing, querying, traversing etc.","title":"List"},{"location":"sophia_stdlib/#option","text":"Common operations on option types and lists of option s.","title":"Option"},{"location":"sophia_stdlib/#pair","text":"Common operations on 2-tuples.","title":"Pair"},{"location":"sophia_stdlib/#set_1","text":"","title":"Set"},{"location":"sophia_stdlib/#string","text":"Operations on the string type. A string is a UTF-8 encoded byte array.","title":"String"},{"location":"sophia_stdlib/#triple","text":"","title":"Triple"},{"location":"sophia_syntax/","text":"Syntax Lexical syntax Comments Single line comments start with // and block comments are enclosed in /* and */ and can be nested. Keywords contract include let switch type record datatype if elif else function stateful payable true false mod public entrypoint private indexed namespace interface main using as for hiding Tokens Id = [a-z_][A-Za-z0-9_']* identifiers start with a lower case letter. Con = [A-Z][A-Za-z0-9_']* constructors start with an upper case letter. QId = (Con\\.)+Id qualified identifiers (e.g. Map.member ) QCon = (Con\\.)+Con qualified constructor TVar = 'Id type variable (e.g 'a , 'b ) Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* integer literal with optional _ separators Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* byte array literal with optional _ separators String string literal enclosed in \" with escape character \\ Char character literal enclosed in ' with escape character \\ AccountAddress base58-encoded 32 byte account pubkey with ak_ prefix ContractAddress base58-encoded 32 byte contract address with ct_ prefix OracleAddress base58-encoded 32 byte oracle address with ok_ prefix OracleQueryId base58-encoded 32 byte oracle query id with oq_ prefix Valid string escape codes are Escape ASCII \\b 8 \\t 9 \\n 10 \\v 11 \\f 12 \\r 13 \\e 27 \\xHexDigits HexDigits See the identifier encoding scheme for the details on the base58 literals. Layout blocks Sophia uses Python-style layout rules to group declarations and statements. A layout block with more than one element must start on a separate line and be indented more than the currently enclosing layout block. Blocks with a single element can be written on the same line as the previous token. Each element of the block must share the same indentation and no part of an element may be indented less than the indentation of the block. For instance contract Layout = function foo () = 0 // no layout function bar () = // layout block starts on next line let x = foo () // indented more than 2 spaces x + 1 // the '+' is indented more than the 'x' Notation In describing the syntax below, we use the following conventions: Upper-case identifiers denote non-terminals (like Expr ) or terminals with some associated value (like Id ). Keywords and symbols are enclosed in single quotes: 'let' or '=' . Choices are separated by vertical bars: | . Optional elements are enclosed in [ square brackets ] . ( Parentheses ) are used for grouping. Zero or more repetitions are denoted by a postfix * , and one or more repetitions by a + . Block(X) denotes a layout block of X s. Sep(X, S) is short for [X (S X)*] , i.e. a possibly empty sequence of X s separated by S s. Sep1(X, S) is short for X (S X)* , i.e. same as Sep , but must not be empty. Declarations A Sophia file consists of a sequence of declarations in a layout block. File ::= Block ( TopDecl ) TopDecl ::= [ ' payable ' ] [ ' main ' ] ' contract ' Con [ Implement ] '=' Block ( Decl ) | ' contract ' ' interface ' Con [ Implement ] '=' Block ( Decl ) | ' namespace ' Con '=' Block ( Decl ) | '@ compiler ' PragmaOp Version | ' include ' String | Using Implement ::= ':' Sep1 ( Con , ',' ) Decl ::= ' type ' Id [ '(' TVar * ')' ] '=' TypeAlias | ' record ' Id [ '(' TVar * ')' ] '=' RecordType | ' datatype ' Id [ '(' TVar * ')' ] '=' DataType | ( EModifier * ' entrypoint ' | FModifier * ' function ' ) Block ( FunDecl ) | Using FunDecl ::= Id ':' Type // Type signature | Id Args [ ':' Type ] '=' Block ( Stmt ) // Definition | Id Args [ ':' Type ] Block ( GuardedDef ) // Guarded definitions GuardedDef ::= '|' Sep1 ( Expr , ',' ) '=' Block ( Stmt ) Using ::= ' using ' Con [ ' as ' Con ] [ UsingParts ] UsingParts ::= ' for ' ' [ ' Sep1 ( Id , ',' ) ']' | ' hiding ' ' [ ' Sep1 ( Id , ',' ) ']' PragmaOp ::= '<' | ' =< ' | ' == ' | ' >= ' | '>' Version ::= Sep1 ( Int , '.' ) EModifier ::= ' payable ' | ' stateful ' FModifier ::= ' stateful ' | ' private ' Args ::= '(' Sep ( Pattern , ',' ) ')' Contract declarations must appear at the top-level. For example, contract Test = type t = int entrypoint add ( x : t , y : t ) = x + y There are three forms of type declarations: type aliases (declared with the type keyword), record type definitions ( record ) and data type definitions ( datatype ): TypeAlias ::= Type RecordType ::= '{' Sep ( FieldType , ',' ) '}' DataType ::= Sep1 ( ConDecl , '|' ) FieldType ::= Id ':' Type ConDecl ::= Con [ '(' Sep1 ( Type , ',' ) ')' ] For example, record point ( 'a ) = { x : 'a , y : 'a } datatype shape ( 'a ) = Circle ( point ( 'a ), 'a ) | Rect ( point ( 'a ), point ( 'a )) type int_shape = shape ( int ) Types Type ::= Domain ' => ' Type // Function type | Type '(' Sep ( Type , ',' ) ')' // Type application | '(' Type ')' // Parens | ' unit ' | Sep ( Type , '*' ) // Tuples | Id | QId | TVar Domain ::= Type // Single argument | '(' Sep ( Type , ',' ) ')' // Multiple arguments The function type arrow associates to the right. Example, 'a => list ( 'a ) => ( int * list ( 'a )) Statements Function bodies are blocks of statements , where a statement is one of the following Stmt ::= ' switch ' ' ( ' Expr ')' Block ( Case ) | ' if ' ' ( ' Expr ')' Block ( Stmt ) | ' elif ' ' ( ' Expr ')' Block ( Stmt ) | ' else ' Block ( Stmt ) | ' let ' LetDef | Using | Expr LetDef ::= Id Args [ ':' Type ] '=' Block ( Stmt ) // Function definition | Pattern '=' Block ( Stmt ) // Value definition Case ::= Pattern ' => ' Block ( Stmt ) | Pattern Block ( GuardedCase ) GuardedCase ::= '|' Sep1 ( Expr , ',' ) ' => ' Block ( Stmt ) Pattern ::= Expr if statements can be followed by zero or more elif statements and an optional final else statement. For example, let x : int = 4 switch ( f ( x )) None => 0 Some ( y ) => if ( y > 10 ) \"too big\" elif ( y < 3 ) \"too small\" else \"just right\" Expressions Expr ::= '(' LamArgs ')' ' => ' Block ( Stmt ) // Anonymous function (x) => x + 1 | '(' BinOp ')' // Operator lambda (+) | ' if ' ' ( ' Expr ')' Expr ' else ' Expr // If expression if(x < y) y else x | Expr ':' Type // Type annotation 5 : int | Expr BinOp Expr // Binary operator x + y | UnOp Expr // Unary operator ! b | Expr '(' Sep ( Expr , ',' ) ')' // Application f(x, y) | Expr '.' Id // Projection state.x | Expr '[' Expr ']' // Map lookup map[key] | Expr '{' Sep ( FieldUpdate , ',' ) '}' // Record or map update r{ fld[key].x = y } | '[' Sep ( Expr , ',' ) ']' // List [1, 2, 3] | '[' Expr '|' Sep ( Generator , ',' ) ']' // List comprehension [k | x <- [1], if (f(x)), let k = x+1] | '[' Expr ' .. ' Expr ']' // List range [1..n] | '{' Sep ( FieldUpdate , ',' ) '}' // Record or map value {x = 0, y = 1}, {[key] = val} | '(' Expr ')' // Parens (1 + 2) * 3 | '(' Expr '=' Expr ')' // Assign pattern (y = x::_) | Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, \"foo\", '%' | AccountAddress | ContractAddress // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers Generator ::= Pattern ' <- ' Expr // Generator | ' if ' ' ( ' Expr ')' // Guard | LetDef // Definition LamArgs ::= '(' Sep ( LamArg , ',' ) ')' LamArg ::= Id [ ':' Type ] FieldUpdate ::= Path '=' Expr Path ::= Id // Record field | '[' Expr ']' // Map key | Path '.' Id // Nested record field | Path '[' Expr ']' // Nested map key BinOp ::= ' || ' | ' && ' | '<' | '>' | ' =< ' | ' >= ' | ' == ' | ' != ' | ' :: ' | ' ++ ' | '+' | '-' | '*' | '/' | ' mod ' | '^' | ' |> ' UnOp ::= '-' | '!' Operators types Operators Type - + * / mod ^ arithmetic operators ! && || logical operators == != < > =< >= comparison operators :: ++ list operators |> functional operators Operator precedence In order of highest to lowest precedence. Operators Associativity ! right ^ left * / mod left - (unary) right + - left :: ++ right < > =< >= == != none && right || right |> left","title":"Syntax"},{"location":"sophia_syntax/#syntax","text":"","title":"Syntax"},{"location":"sophia_syntax/#lexical-syntax","text":"","title":"Lexical syntax"},{"location":"sophia_syntax/#comments","text":"Single line comments start with // and block comments are enclosed in /* and */ and can be nested.","title":"Comments"},{"location":"sophia_syntax/#keywords","text":"contract include let switch type record datatype if elif else function stateful payable true false mod public entrypoint private indexed namespace interface main using as for hiding","title":"Keywords"},{"location":"sophia_syntax/#tokens","text":"Id = [a-z_][A-Za-z0-9_']* identifiers start with a lower case letter. Con = [A-Z][A-Za-z0-9_']* constructors start with an upper case letter. QId = (Con\\.)+Id qualified identifiers (e.g. Map.member ) QCon = (Con\\.)+Con qualified constructor TVar = 'Id type variable (e.g 'a , 'b ) Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* integer literal with optional _ separators Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* byte array literal with optional _ separators String string literal enclosed in \" with escape character \\ Char character literal enclosed in ' with escape character \\ AccountAddress base58-encoded 32 byte account pubkey with ak_ prefix ContractAddress base58-encoded 32 byte contract address with ct_ prefix OracleAddress base58-encoded 32 byte oracle address with ok_ prefix OracleQueryId base58-encoded 32 byte oracle query id with oq_ prefix Valid string escape codes are Escape ASCII \\b 8 \\t 9 \\n 10 \\v 11 \\f 12 \\r 13 \\e 27 \\xHexDigits HexDigits See the identifier encoding scheme for the details on the base58 literals.","title":"Tokens"},{"location":"sophia_syntax/#layout-blocks","text":"Sophia uses Python-style layout rules to group declarations and statements. A layout block with more than one element must start on a separate line and be indented more than the currently enclosing layout block. Blocks with a single element can be written on the same line as the previous token. Each element of the block must share the same indentation and no part of an element may be indented less than the indentation of the block. For instance contract Layout = function foo () = 0 // no layout function bar () = // layout block starts on next line let x = foo () // indented more than 2 spaces x + 1 // the '+' is indented more than the 'x'","title":"Layout blocks"},{"location":"sophia_syntax/#notation","text":"In describing the syntax below, we use the following conventions: Upper-case identifiers denote non-terminals (like Expr ) or terminals with some associated value (like Id ). Keywords and symbols are enclosed in single quotes: 'let' or '=' . Choices are separated by vertical bars: | . Optional elements are enclosed in [ square brackets ] . ( Parentheses ) are used for grouping. Zero or more repetitions are denoted by a postfix * , and one or more repetitions by a + . Block(X) denotes a layout block of X s. Sep(X, S) is short for [X (S X)*] , i.e. a possibly empty sequence of X s separated by S s. Sep1(X, S) is short for X (S X)* , i.e. same as Sep , but must not be empty.","title":"Notation"},{"location":"sophia_syntax/#declarations","text":"A Sophia file consists of a sequence of declarations in a layout block. File ::= Block ( TopDecl ) TopDecl ::= [ ' payable ' ] [ ' main ' ] ' contract ' Con [ Implement ] '=' Block ( Decl ) | ' contract ' ' interface ' Con [ Implement ] '=' Block ( Decl ) | ' namespace ' Con '=' Block ( Decl ) | '@ compiler ' PragmaOp Version | ' include ' String | Using Implement ::= ':' Sep1 ( Con , ',' ) Decl ::= ' type ' Id [ '(' TVar * ')' ] '=' TypeAlias | ' record ' Id [ '(' TVar * ')' ] '=' RecordType | ' datatype ' Id [ '(' TVar * ')' ] '=' DataType | ( EModifier * ' entrypoint ' | FModifier * ' function ' ) Block ( FunDecl ) | Using FunDecl ::= Id ':' Type // Type signature | Id Args [ ':' Type ] '=' Block ( Stmt ) // Definition | Id Args [ ':' Type ] Block ( GuardedDef ) // Guarded definitions GuardedDef ::= '|' Sep1 ( Expr , ',' ) '=' Block ( Stmt ) Using ::= ' using ' Con [ ' as ' Con ] [ UsingParts ] UsingParts ::= ' for ' ' [ ' Sep1 ( Id , ',' ) ']' | ' hiding ' ' [ ' Sep1 ( Id , ',' ) ']' PragmaOp ::= '<' | ' =< ' | ' == ' | ' >= ' | '>' Version ::= Sep1 ( Int , '.' ) EModifier ::= ' payable ' | ' stateful ' FModifier ::= ' stateful ' | ' private ' Args ::= '(' Sep ( Pattern , ',' ) ')' Contract declarations must appear at the top-level. For example, contract Test = type t = int entrypoint add ( x : t , y : t ) = x + y There are three forms of type declarations: type aliases (declared with the type keyword), record type definitions ( record ) and data type definitions ( datatype ): TypeAlias ::= Type RecordType ::= '{' Sep ( FieldType , ',' ) '}' DataType ::= Sep1 ( ConDecl , '|' ) FieldType ::= Id ':' Type ConDecl ::= Con [ '(' Sep1 ( Type , ',' ) ')' ] For example, record point ( 'a ) = { x : 'a , y : 'a } datatype shape ( 'a ) = Circle ( point ( 'a ), 'a ) | Rect ( point ( 'a ), point ( 'a )) type int_shape = shape ( int )","title":"Declarations"},{"location":"sophia_syntax/#types","text":"Type ::= Domain ' => ' Type // Function type | Type '(' Sep ( Type , ',' ) ')' // Type application | '(' Type ')' // Parens | ' unit ' | Sep ( Type , '*' ) // Tuples | Id | QId | TVar Domain ::= Type // Single argument | '(' Sep ( Type , ',' ) ')' // Multiple arguments The function type arrow associates to the right. Example, 'a => list ( 'a ) => ( int * list ( 'a ))","title":"Types"},{"location":"sophia_syntax/#statements","text":"Function bodies are blocks of statements , where a statement is one of the following Stmt ::= ' switch ' ' ( ' Expr ')' Block ( Case ) | ' if ' ' ( ' Expr ')' Block ( Stmt ) | ' elif ' ' ( ' Expr ')' Block ( Stmt ) | ' else ' Block ( Stmt ) | ' let ' LetDef | Using | Expr LetDef ::= Id Args [ ':' Type ] '=' Block ( Stmt ) // Function definition | Pattern '=' Block ( Stmt ) // Value definition Case ::= Pattern ' => ' Block ( Stmt ) | Pattern Block ( GuardedCase ) GuardedCase ::= '|' Sep1 ( Expr , ',' ) ' => ' Block ( Stmt ) Pattern ::= Expr if statements can be followed by zero or more elif statements and an optional final else statement. For example, let x : int = 4 switch ( f ( x )) None => 0 Some ( y ) => if ( y > 10 ) \"too big\" elif ( y < 3 ) \"too small\" else \"just right\"","title":"Statements"},{"location":"sophia_syntax/#expressions","text":"Expr ::= '(' LamArgs ')' ' => ' Block ( Stmt ) // Anonymous function (x) => x + 1 | '(' BinOp ')' // Operator lambda (+) | ' if ' ' ( ' Expr ')' Expr ' else ' Expr // If expression if(x < y) y else x | Expr ':' Type // Type annotation 5 : int | Expr BinOp Expr // Binary operator x + y | UnOp Expr // Unary operator ! b | Expr '(' Sep ( Expr , ',' ) ')' // Application f(x, y) | Expr '.' Id // Projection state.x | Expr '[' Expr ']' // Map lookup map[key] | Expr '{' Sep ( FieldUpdate , ',' ) '}' // Record or map update r{ fld[key].x = y } | '[' Sep ( Expr , ',' ) ']' // List [1, 2, 3] | '[' Expr '|' Sep ( Generator , ',' ) ']' // List comprehension [k | x <- [1], if (f(x)), let k = x+1] | '[' Expr ' .. ' Expr ']' // List range [1..n] | '{' Sep ( FieldUpdate , ',' ) '}' // Record or map value {x = 0, y = 1}, {[key] = val} | '(' Expr ')' // Parens (1 + 2) * 3 | '(' Expr '=' Expr ')' // Assign pattern (y = x::_) | Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, \"foo\", '%' | AccountAddress | ContractAddress // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers Generator ::= Pattern ' <- ' Expr // Generator | ' if ' ' ( ' Expr ')' // Guard | LetDef // Definition LamArgs ::= '(' Sep ( LamArg , ',' ) ')' LamArg ::= Id [ ':' Type ] FieldUpdate ::= Path '=' Expr Path ::= Id // Record field | '[' Expr ']' // Map key | Path '.' Id // Nested record field | Path '[' Expr ']' // Nested map key BinOp ::= ' || ' | ' && ' | '<' | '>' | ' =< ' | ' >= ' | ' == ' | ' != ' | ' :: ' | ' ++ ' | '+' | '-' | '*' | '/' | ' mod ' | '^' | ' |> ' UnOp ::= '-' | '!'","title":"Expressions"},{"location":"sophia_syntax/#operators-types","text":"Operators Type - + * / mod ^ arithmetic operators ! && || logical operators == != < > =< >= comparison operators :: ++ list operators |> functional operators","title":"Operators types"},{"location":"sophia_syntax/#operator-precedence","text":"In order of highest to lowest precedence. Operators Associativity ! right ^ left * / mod left - (unary) right + - left :: ++ right < > =< >= == != none && right || right |> left","title":"Operator precedence"}]} \ No newline at end of file +{"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 Options to enable/disable certain optimizations. The ability to call a different instance of the current contract contract Main = entrypoint spend(x : int) : int = x entrypoint f(c : Main) : int = c.spend(10) Return a mapping from variables to FATE registers in the compilation output. Changed Type definitions serialised to ACI as typedefs field instead of type_defs to increase compatibility. Check contracts and entrypoints modifiers when implementing interfaces. Contracts can no longer be used as namespaces. Removed Fixed Typechecker crashes if Chain.create or Chain.clone are used without arguments. 7.0.1 Added Add CONTRIBUTING.md file. Changed Update Sophia syntax docs to include missing information about existing syntax. Fixed 404 Contract polymorphism crashes on non-obvious child contract typing. 7.0.0 Added Added support for EXIT opcode via exit : (string) => 'a function (behaves same as ABORT , but consumes all gas). Compiler warnings for the following: 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])) Allow binary operators to be used as lambdas function sum(l : list(int)) : int = foldl((+), 0, l) function logical_and(x, y) = (&&)(x, y) Contract interfaces polymorphism Changed Error messages have been restructured (less newlines) to provide more unified errors. Also pp_oneline/1 has been added. Ban empty record definitions (e.g. record r = {} would give an error). Removed Support for AEVM has been entirely wiped 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. and String. for byte arrays now only hash the actual byte array - not the internal ABI format. More strict checks for polymorphic oracles and higher order oracles and entrypoints. AENS.claim is updated with a NameFee field - to be able to do name auctions within contracts. Fixed a bug in Bytes.to_str for AEVM. New syntax for tuple types. Now 0-tuple type is encoded as unit instead of () and regular tuples are encoded by interspersing inner types with * , for instance int * string . Parens are not necessary. Note it only affects the types, values remain as their were before, so (1, \"a\") : int * string The AENS.transfer and AENS.revoke functions have been updated to take a name string instead of a name hash . Fixed a bug where the AEVM backend complained about a missing init function when trying to generate calldata from an ACI-generated interface. Compiler now returns the ABI-version in the compiler result map. Renamed Crypto.ecverify and Crypto.ecverify_secp256k1 into Crypto.verify_sig and Crypto.verify_sig_secp256k1 respectively. Removed 3.2.0 - 2019-06-28 Added New builtin function require : (bool, string) => () . Defined as function require(b, err) = if(!b) abort(err) New builtin functions 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. Changed Public contract functions must now be declared as entrypoints : 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). The return type of Chain.block_hash(height) has changed, it used to be int , where 0 denoted an incorrect height. New return type is option(hash) , where None represents an incorrect height. Event name hashes now use BLAKE2b instead of Keccak256. Fixed bugs when defining record types in namespaces. Fixed a bug in include path handling when passing options to the compiler. Removed 3.1.0 - 2019-06-03 Added Changed Keyword indexed is now optional for word typed ( bool , int , address , ...) event arguments. State variable pretty printing now produce 'a, 'b, ... instead of '1, '2, ... . ACI is restructured and improved: state and event types (if present) now appear at the top level. Namespaces and remote interfaces are no longer ignored. All type definitions are included in the interface rendering. API functions are renamed, new functions are contract_interface and render_aci_json . Fixed a bug in create_calldata / to_sophia_value - it can now handle negative literals. Removed 3.0.0 - 2019-05-21 Added stateful annotations are now properly enforced. Functions must be marked stateful in order to update the state or spend tokens. Primitives Contract.creator , Address.is_contract , Address.is_oracle , Oracle.check and Oracle.check_query has been added to Sophia. A byte array type bytes(N) has been added to generalize hash (== bytes(32)) and signature (== bytes(64)) and allow for byte arrays of arbitrary fixed length. Crypto.ecverify_secp256k1 has been added. Changed Address literals (+ Oracle, Oracle query and remote contracts) have been changed from # to address as ak_ , oracle ok_ , oracle query oq_ and remote contract ct_ . The compilation and typechecking of letfun (e.g. let m(f, xs) = map(f, xs) ) was not working properly and has been fixed. Removed let rec has been removed from the language, it has never worked. The standalone CLI compiler is served in the repo aeternity/aesophia_cli and has been completely removed from aesophia . 2.1.0 - 2019-04-11 Added Stubs (not yet wired up) for compilation to FATE Add functions specific for Calldata decoding Support for Auth.tx_hash , not available in AEVM until Fortuna release Changed Improvements to the ACI generator 2.0.0 - 2019-03-11 Added Add Crypto.ecverify to the compiler. Add Crypto.sha3 , Crypto.blake2 , Crypto.sha256 , String.blake2 and String.sha256 to the compiler. Add the bits type for working with bit fields in Sophia. Add Namespaces to Sophia in order to simplify using library contracts, etc. Add a missig type check on the init function - detects programmer errors earlier. Add the ACI (Aeternity Contract Interface) generator. Changed Use native bit shift operations in builtin functions, reducing gas cost. Improve type checking of record fields - generates more understandable error messages. Improved, more coherent, error messages. Simplify calldata creation - instead of passing a compiled contract, simply pass a (stubbed) contract string.","title":"Changelog"},{"location":"CHANGELOG/#changelog","text":"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 .","title":"Changelog"},{"location":"CHANGELOG/#unreleased","text":"","title":"Unreleased"},{"location":"CHANGELOG/#added","text":"Options to enable/disable certain optimizations. The ability to call a different instance of the current contract contract Main = entrypoint spend(x : int) : int = x entrypoint f(c : Main) : int = c.spend(10) Return a mapping from variables to FATE registers in the compilation output.","title":"Added"},{"location":"CHANGELOG/#changed","text":"Type definitions serialised to ACI as typedefs field instead of type_defs to increase compatibility. Check contracts and entrypoints modifiers when implementing interfaces. Contracts can no longer be used as namespaces.","title":"Changed"},{"location":"CHANGELOG/#removed","text":"","title":"Removed"},{"location":"CHANGELOG/#fixed","text":"Typechecker crashes if Chain.create or Chain.clone are used without arguments.","title":"Fixed"},{"location":"CHANGELOG/#701","text":"","title":"7.0.1"},{"location":"CHANGELOG/#added_1","text":"Add CONTRIBUTING.md file.","title":"Added"},{"location":"CHANGELOG/#changed_1","text":"Update Sophia syntax docs to include missing information about existing syntax.","title":"Changed"},{"location":"CHANGELOG/#fixed_1","text":"404 Contract polymorphism crashes on non-obvious child contract typing.","title":"Fixed"},{"location":"CHANGELOG/#700","text":"","title":"7.0.0"},{"location":"CHANGELOG/#added_2","text":"Added support for EXIT opcode via exit : (string) => 'a function (behaves same as ABORT , but consumes all gas). Compiler warnings for the following: 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])) Allow binary operators to be used as lambdas function sum(l : list(int)) : int = foldl((+), 0, l) function logical_and(x, y) = (&&)(x, y) Contract interfaces polymorphism","title":"Added"},{"location":"CHANGELOG/#changed_2","text":"Error messages have been restructured (less newlines) to provide more unified errors. Also pp_oneline/1 has been added. Ban empty record definitions (e.g. record r = {} would give an error).","title":"Changed"},{"location":"CHANGELOG/#removed_1","text":"Support for AEVM has been entirely wiped","title":"Removed"},{"location":"CHANGELOG/#610-2021-10-20","text":"","title":"6.1.0 - 2021-10-20"},{"location":"CHANGELOG/#added_3","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_3","text":"Fixed the ACI renderer, it shouldn't drop the stateful modifier","title":"Changed"},{"location":"CHANGELOG/#602-2021-07-05","text":"","title":"6.0.2 2021-07-05"},{"location":"CHANGELOG/#changed_4","text":"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","title":"Changed"},{"location":"CHANGELOG/#601-2021-06-24","text":"","title":"6.0.1 2021-06-24"},{"location":"CHANGELOG/#changed_5","text":"Fixed a bug in calldata encoding for contracts containing multiple contracts Fixed a missing include in the Frac standard library","title":"Changed"},{"location":"CHANGELOG/#600-2021-05-26","text":"","title":"6.0.0 2021-05-26"},{"location":"CHANGELOG/#added_4","text":"Child contracts Chain.clone Chain.create Chain.bytecode_hash Minor support for variadic functions void type that represents an empty type Call.fee builtin","title":"Added"},{"location":"CHANGELOG/#changed_6","text":"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","title":"Changed"},{"location":"CHANGELOG/#removed_2","text":"","title":"Removed"},{"location":"CHANGELOG/#500-2021-04-30","text":"","title":"5.0.0 2021-04-30"},{"location":"CHANGELOG/#added_5","text":"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.","title":"Added"},{"location":"CHANGELOG/#changed_7","text":"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.","title":"Changed"},{"location":"CHANGELOG/#removed_3","text":"","title":"Removed"},{"location":"CHANGELOG/#430","text":"","title":"4.3.0"},{"location":"CHANGELOG/#added_6","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_8","text":"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","title":"Changed"},{"location":"CHANGELOG/#removed_4","text":"let definitions are no longer supported in the toplevel of the contract type declarations are no longer supported","title":"Removed"},{"location":"CHANGELOG/#420-2020-01-15","text":"","title":"4.2.0 - 2020-01-15"},{"location":"CHANGELOG/#added_7","text":"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","title":"Added"},{"location":"CHANGELOG/#changed_9","text":"FATE code generator improvements. Bug fix: Handle qualified constructors in patterns. Bug fix: Allow switching also on negative numbers.","title":"Changed"},{"location":"CHANGELOG/#removed_5","text":"","title":"Removed"},{"location":"CHANGELOG/#410-2019-11-26","text":"","title":"4.1.0 - 2019-11-26"},{"location":"CHANGELOG/#added_8","text":"Support encoding and decoding bit fields in call arguments and results.","title":"Added"},{"location":"CHANGELOG/#changed_10","text":"Various improvements to FATE code generator.","title":"Changed"},{"location":"CHANGELOG/#removed_6","text":"","title":"Removed"},{"location":"CHANGELOG/#400-2019-10-11","text":"","title":"4.0.0 - 2019-10-11"},{"location":"CHANGELOG/#added_9","text":"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.","title":"Added"},{"location":"CHANGELOG/#changed_11","text":"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. and String. for byte arrays now only hash the actual byte array - not the internal ABI format. More strict checks for polymorphic oracles and higher order oracles and entrypoints. AENS.claim is updated with a NameFee field - to be able to do name auctions within contracts. Fixed a bug in Bytes.to_str for AEVM. New syntax for tuple types. Now 0-tuple type is encoded as unit instead of () and regular tuples are encoded by interspersing inner types with * , for instance int * string . Parens are not necessary. Note it only affects the types, values remain as their were before, so (1, \"a\") : int * string The AENS.transfer and AENS.revoke functions have been updated to take a name string instead of a name hash . Fixed a bug where the AEVM backend complained about a missing init function when trying to generate calldata from an ACI-generated interface. Compiler now returns the ABI-version in the compiler result map. Renamed Crypto.ecverify and Crypto.ecverify_secp256k1 into Crypto.verify_sig and Crypto.verify_sig_secp256k1 respectively.","title":"Changed"},{"location":"CHANGELOG/#removed_7","text":"","title":"Removed"},{"location":"CHANGELOG/#320-2019-06-28","text":"","title":"3.2.0 - 2019-06-28"},{"location":"CHANGELOG/#added_10","text":"New builtin function require : (bool, string) => () . Defined as function require(b, err) = if(!b) abort(err) New builtin functions 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.","title":"Added"},{"location":"CHANGELOG/#changed_12","text":"Public contract functions must now be declared as entrypoints : 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). The return type of Chain.block_hash(height) has changed, it used to be int , where 0 denoted an incorrect height. New return type is option(hash) , where None represents an incorrect height. Event name hashes now use BLAKE2b instead of Keccak256. Fixed bugs when defining record types in namespaces. Fixed a bug in include path handling when passing options to the compiler.","title":"Changed"},{"location":"CHANGELOG/#removed_8","text":"","title":"Removed"},{"location":"CHANGELOG/#310-2019-06-03","text":"","title":"3.1.0 - 2019-06-03"},{"location":"CHANGELOG/#added_11","text":"","title":"Added"},{"location":"CHANGELOG/#changed_13","text":"Keyword indexed is now optional for word typed ( bool , int , address , ...) event arguments. State variable pretty printing now produce 'a, 'b, ... instead of '1, '2, ... . ACI is restructured and improved: state and event types (if present) now appear at the top level. Namespaces and remote interfaces are no longer ignored. All type definitions are included in the interface rendering. API functions are renamed, new functions are contract_interface and render_aci_json . Fixed a bug in create_calldata / to_sophia_value - it can now handle negative literals.","title":"Changed"},{"location":"CHANGELOG/#removed_9","text":"","title":"Removed"},{"location":"CHANGELOG/#300-2019-05-21","text":"","title":"3.0.0 - 2019-05-21"},{"location":"CHANGELOG/#added_12","text":"stateful annotations are now properly enforced. Functions must be marked stateful in order to update the state or spend tokens. Primitives Contract.creator , Address.is_contract , Address.is_oracle , Oracle.check and Oracle.check_query has been added to Sophia. A byte array type bytes(N) has been added to generalize hash (== bytes(32)) and signature (== bytes(64)) and allow for byte arrays of arbitrary fixed length. Crypto.ecverify_secp256k1 has been added.","title":"Added"},{"location":"CHANGELOG/#changed_14","text":"Address literals (+ Oracle, Oracle query and remote contracts) have been changed from # to address as ak_ , oracle ok_ , oracle query oq_ and remote contract ct_ . The compilation and typechecking of letfun (e.g. let m(f, xs) = map(f, xs) ) was not working properly and has been fixed.","title":"Changed"},{"location":"CHANGELOG/#removed_10","text":"let rec has been removed from the language, it has never worked. The standalone CLI compiler is served in the repo aeternity/aesophia_cli and has been completely removed from aesophia .","title":"Removed"},{"location":"CHANGELOG/#210-2019-04-11","text":"","title":"2.1.0 - 2019-04-11"},{"location":"CHANGELOG/#added_13","text":"Stubs (not yet wired up) for compilation to FATE Add functions specific for Calldata decoding Support for Auth.tx_hash , not available in AEVM until Fortuna release","title":"Added"},{"location":"CHANGELOG/#changed_15","text":"Improvements to the ACI generator","title":"Changed"},{"location":"CHANGELOG/#200-2019-03-11","text":"","title":"2.0.0 - 2019-03-11"},{"location":"CHANGELOG/#added_14","text":"Add Crypto.ecverify to the compiler. Add Crypto.sha3 , Crypto.blake2 , Crypto.sha256 , String.blake2 and String.sha256 to the compiler. Add the bits type for working with bit fields in Sophia. Add Namespaces to Sophia in order to simplify using library contracts, etc. Add a missig type check on the init function - detects programmer errors earlier. Add the ACI (Aeternity Contract Interface) generator.","title":"Added"},{"location":"CHANGELOG/#changed_16","text":"Use native bit shift operations in builtin functions, reducing gas cost. Improve type checking of record fields - generates more understandable error messages. Improved, more coherent, error messages. Simplify calldata creation - instead of passing a compiled contract, simply pass a (stubbed) contract string.","title":"Changed"},{"location":"aeso_aci/","text":"aeso_aci Module aeso_aci The ACI interface encoder and decoder. Description This module provides an interface to generate and convert between Sophia contracts and a suitable JSON encoding of contract interface. As yet the interface is very basic. Encoding this contract: contract Answers = record state = { a : answers } 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\" } ] }, \"typedefs\" : [ { \"name\" : \"answers\" , \"typedef\" : { \"map\" : [ \"string\" , \"int\" ] }, \"vars\" : [] } ] } } When that encoding is decoded the following include definition is generated: contract Answers = record state = {a : Answers.answers} type answers = map(string, int) function init : () => Answers.state function new_answer : (string, int) => map(string, int) Types - type aci_type () :: json | string . - type json () :: jsx : json_term (). - type json_text () :: binary (). Exports contract_interface(aci_type(), string()) -> {ok, json() | string()} | {error, term()} Generate the JSON encoding of the interface to a contract. The type definitions and non-private functions are included in the JSON string. render_aci_json(json() | json_text()) -> string(). Take a JSON encoding of a contract interface and generate a contract interface that can be included in another contract. Example run This is an example of using the ACI generator from an Erlang shell. The file called aci_test.aes contains the contract in the description from which we 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\" >> }]}, typedefs => [#{ 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.","title":"aeso_aci"},{"location":"aeso_aci/#aeso_aci","text":"","title":"aeso_aci"},{"location":"aeso_aci/#module","text":"","title":"Module"},{"location":"aeso_aci/#aeso_aci_1","text":"The ACI interface encoder and decoder.","title":"aeso_aci"},{"location":"aeso_aci/#description","text":"This module provides an interface to generate and convert between Sophia contracts and a suitable JSON encoding of contract interface. As yet the interface is very basic. Encoding this contract: contract Answers = record state = { a : answers } 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\" } ] }, \"typedefs\" : [ { \"name\" : \"answers\" , \"typedef\" : { \"map\" : [ \"string\" , \"int\" ] }, \"vars\" : [] } ] } } When that encoding is decoded the following include definition is generated: contract Answers = record state = {a : Answers.answers} type answers = map(string, int) function init : () => Answers.state function new_answer : (string, int) => map(string, int)","title":"Description"},{"location":"aeso_aci/#types","text":"- type aci_type () :: json | string . - type json () :: jsx : json_term (). - type json_text () :: binary ().","title":"Types"},{"location":"aeso_aci/#exports","text":"","title":"Exports"},{"location":"aeso_aci/#example-run","text":"This is an example of using the ACI generator from an Erlang shell. The file called aci_test.aes contains the contract in the description from which we 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\" >> }]}, typedefs => [#{ 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.","title":"Example run"},{"location":"aeso_compiler/","text":"aeso_compiler Module aeso_compiler The Sophia compiler Description This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded. Types contract_string () = string () | binary () contract_map () = #{ bytecode => binary (), compiler_version => binary (), contract_souce => string (), type_info => type_info ()} type_info () errorstring () = binary () Exports file(File) file(File, Options) -> CompRet from_string(ContractString, Options) -> CompRet Types 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_ast - print the AST of the code pp_types - print information about the types pp_typed_ast - print the AST with type information at each node pp_assembler - print the generated assembler code The option include_child_contract_symbols includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain. The option debug_info includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers. Options to control which compiler optimizations should run: By default all optimizations are turned on, to disable an optimization, it should be explicitly set to false and passed as a compiler option. List of optimizations: optimize_inliner optimize_inline_local_functions optimize_bind_subexpressions optimize_let_floating optimize_simplifier optimize_drop_unused_lets optimize_push_consume optimize_one_shot_var optimize_write_to_dead_var optimize_inline_switch_target optimize_swap_push optimize_swap_pop optimize_swap_write optimize_constant_propagation optimize_prune_impossible_branches optimize_single_successful_branch optimize_inline_store optimize_float_switch_bod check_call(ContractString, Options) -> CheckRet Types ContractString = string() | binary() CheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term} Types = [Type] Type = term() Check a call in contract through the __call function. version() -> {ok, Version} | {error, term()} Types Version = binary () Get the current version of the Sophia compiler.","title":"aeso_compiler"},{"location":"aeso_compiler/#aeso_compiler","text":"","title":"aeso_compiler"},{"location":"aeso_compiler/#module","text":"","title":"Module"},{"location":"aeso_compiler/#aeso_compiler_1","text":"The Sophia compiler","title":"aeso_compiler"},{"location":"aeso_compiler/#description","text":"This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded.","title":"Description"},{"location":"aeso_compiler/#types","text":"contract_string () = string () | binary () contract_map () = #{ bytecode => binary (), compiler_version => binary (), contract_souce => string (), type_info => type_info ()} type_info () errorstring () = binary ()","title":"Types"},{"location":"aeso_compiler/#exports","text":"","title":"Exports"},{"location":"sophia/","text":"This file has been moved here","title":"Sophia"},{"location":"sophia_examples/","text":"Contract examples Crowdfunding /* * 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 ) }) Repositories This is a list with repositories that include smart contracts written in Sophia: aepp-sophia-examples A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.","title":"Contract examples"},{"location":"sophia_examples/#contract-examples","text":"","title":"Contract examples"},{"location":"sophia_examples/#crowdfunding","text":"/* * 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 ) })","title":"Crowdfunding"},{"location":"sophia_examples/#repositories","text":"This is a list with repositories that include smart contracts written in Sophia: aepp-sophia-examples A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.","title":"Repositories"},{"location":"sophia_features/","text":"Features Contracts The main unit of code in Sophia is the contract . A contract implementation, or simply a contract, is the code for a smart contract and consists of a list of types, entrypoints and local functions. Only the entrypoints can be called from outside the contract. A contract instance is an entity living on the block chain (or in a state channel). Each instance has an address that can be used to call its entrypoints, either from another contract or in a call transaction. A contract may define a type state encapsulating its local state. When creating a new contract the init entrypoint is executed and the state is initialized to its return value. The language offers some primitive functions to interact with the blockchain and contracts. Please refer to the Chain , Contract and the Call namespaces in the documentation. Calling other contracts 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 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 ) 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 ) 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). To construct a value of a contract type you can give a contract address literal (for instance ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay ), or convert an account address to a contract address using Address.to_contract . Note that if the contract does not exist, or it doesn't have the entrypoint, or the type of the entrypoint does not match the stated contract type, the call 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 ) Protected contract calls 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 ). The protected argument must be a literal boolean, and when set to true changes the type of the contract call, wrapping the result in an option type. If the call fails the result is 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\" 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. However, note that errors that would normally consume all the gas in the transaction still only uses up the gas spent running the contract. Any side effects (state change, token transfers, etc.) made by a failing protected call is rolled back, just like they would be in the unprotected case. Contract factories and child contracts Since the version 6.0.0 Sophia supports deploying contracts by other contracts. This can be done in two ways: Contract cloning via Chain.clone Direct deploy via Chain.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 \u2013 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 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. Contract interfaces and polymorphism Contracts can implement one or multiple interfaces, the contract has to define every entrypoint from the implemented interface and the entrypoints in both the contract and implemented interface should have compatible types. contract interface Animal = entrypoint sound : () => string contract Cat : Animal = entrypoint sound() = \"Cat sound\" Contract interfaces can extend other interfaces. An extended interface has to declare all entrypoints from every parent interface. All the declarations in the extended interface must have types compatible with the declarations from the parent interface. contract interface II = entrypoint f : () => unit contract interface I : II = entrypoint f : () => unit entrypoint g : () => unit contract C : I = entrypoint f() = () entrypoint g() = () It is only possible to implement (or extend) an interface that has been already defined earlier in the file (or in an included file). Therefore recursive interface implementation is not allowed in Sophia. // The following code would show an error contract interface X : Z = entrypoint x : () => int contract interface Y : X = entrypoint x : () => int entrypoint y : () => int contract interface Z : Y = entrypoint x : () => int entrypoint y : () => int entrypoint z : () => int contract C : Z = entrypoint x() = 1 entrypoint y() = 1 entrypoint z() = 1 Adding or removing modifiers When a contract or a contract interface implements another contract interface , the payable and stateful modifiers can be kept or changed, both in the contract and in the entrypoints, according to the following rules: A payable contract or interface can implement a payable interface or a non- payable interface. A non- payable contract or interface can only implement a non- payable interface, and cannot implement a payable interface. A payable entrypoint can implement a payable entrypoint or a non- payable entrypoint. A non- payable entrypoint can only implement a non- payable entrypoint, and cannot implement a payable entrypoint. A non- stateful entrypoint can implement a stateful entrypoint or a non- stateful entrypoint. A stateful entrypoint can only implement a stateful entrypoint, and cannot implement a non- stateful entrypoint. Subtyping and variance Subtyping in Sophia follows common rules that take type variance into account. As described by Wikipedia , Variance refers to how subtyping between more complex types relates to subtyping between their components. This concept plays an important role in complex types such as tuples, datatype s and functions. Depending on the context, it can apply to positions in the structure of a type, or type parameters of generic types. There are four kinds of variances: covariant contravariant invariant bivariant A type is said to be on a \"covariant\" position when it describes output or a result of some computation. Analogously, position is \"contravariant\" when it is an input, or a parameter. Intuitively, when a part of the type is produced by values of it, it is covariant. When it is consumed, it is contravariant. When a type appears to be simultaneously input and output, it is described as invariant. If a type is neither of those (that is, it's unused) it's bivariant. Furthermore, whenever a complex type appears on a contravariant position, all its covariant components become contravariant and vice versa. Variance influences how subtyping is applied. Types on covariant positions are subtyped normally, while contravariant the opposite way. Invariant types have to be exactly the same in order for subtyping to work. Bivariant types are always compatible. A good example of where it matters can be pictured by subtyping of function types. Let us assume there is a contract interface Animal and two contracts that implement it: Dog and Cat . contract interface Animal = entrypoint age : () => int contract Dog : Animal = entrypoint age () = // ... entrypoint woof () = \"woof\" contract Cat : Animal = entrypoint age () = // ... entrypoint meow () = \"meow\" The assumption of this exercise is that cats do not bark (because Cat does not define the woof entrypoint). If subtyping rules were applied naively, that is if we let Dog => Dog be a subtype of Animal => Animal , the following code would break: let f : ( Dog ) => string = d => d . woof () let g : ( Animal ) => string = f let c : Cat = Chain . create () g ( c ) // Cat barking! That is because arguments of functions are contravariant, as opposed to return the type which is covariant. Because of that, the assignment of f to g is invalid - while Dog is a subtype of Animal , Dog => string is not a subtype of Animal => string . However, Animal => string is a subtype of Dog => string . More than that, (Dog => Animal) => Dog is a subtype of (Animal => Dog) => Animal . This has consequences on how user-defined generic types work. A type variable gains its variance from its role in the type definition as shown in the example: datatype co ( 'a ) = Co ( 'a ) // co is covariant on 'a datatype ct ( 'a ) = Ct ( 'a => unit ) // ct is contravariant on 'a datatype in ( 'a ) = In ( 'a => 'a ) // in is invariant on 'a datatype bi ( 'a ) = Bi // bi is bivariant on 'a The following facts apply here: co('a) is a subtype of co('b) when 'a is a subtype of 'b` ct('a) is a subtype of ct('b) when 'b is a subtype of 'a` in('a) is a subtype of in('b) when 'a is equal to 'b` bi('a) is a subtype of `bi('b) always That altogether induce the following rules of subtyping in Sophia: A function type (Args1) => Ret1 is a subtype of (Args2) => Ret2 when Ret1 is a subtype of Ret2 and each argument type from Args2 is a subtype of its counterpart in Args1 . A list type list(A) is a subtype of list(B) if A is a subtype of B . An option type option(A) is a subtype of option(B) if A is a subtype of B . A map type map(A1, A2) is a subtype of map(B1, B2) if A1 is a subtype of B1 , and A2 is a subtype of B2 . An oracle type oracle(A1, A2) is a subtype of oracle(B1, B2) if B1 is a subtype of A1 , and A2 is a subtype of B2 . An oracle_query type oracle_query(A1, A2) is a subtype of oracle_query(B1, B2) if A1 is a subtype of B1 , and A2 is a subtype of B2 . A user-defined datatype t(Args1) is a subtype of t(Args2) When a user-defined type t('a) is covariant in 'a , then t(A) is a subtype of t(B) when A is a subtype of B . When a user-defined type t('a) is contravariant in 'a , then t(A) is a subtype of t(B) when B is a subtype of A . When a user-defined type t('a) is binvariant in 'a , then t(A) is a subtype of t(B) when either A is a subtype of B or when B is a subtype of A . When a user-defined type t('a) is invariant in 'a , then t(A) can never be a subtype of t(B) . Mutable state Sophia does not have arbitrary mutable state, but only a limited form of state associated with each contract instance. Each contract defines a type state encapsulating its mutable state. The type state defaults to the unit . The initial state of a contract is computed by the contract's init function. The init function is pure and returns the initial state as its return value. If the type state is unit , the init function defaults to returning the value () . At contract creation time, the init function is executed and its result is stored as the contract state. The value of the state is accessible from inside the contract through an implicitly bound variable state . State updates are performed by calling a function put : state => unit . Aside from the put function (and similar functions for transactions and events), the language is purely functional. Functions modifying the state need to be annotated with the stateful keyword (see below). To make it convenient to update parts of a deeply nested state Sophia provides special syntax for map/record updates. Stateful functions 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 ) Without the stateful annotation the compiler does not allow the call to put . A stateful annotation is required to Use a stateful primitive function. These are put Chain.spend Oracle.register Oracle.query Oracle.respond Oracle.extend AENS.preclaim AENS.claim AENS.transfer AENS.revoke AENS.update Call a stateful function in the current contract Call another contract with a non-zero value argument. A stateful annotation is not required to Read the contract state. Issue an event using the event function. Call another contract with value = 0 , even if the called function is stateful. Payable Payable contracts 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 () = ... If in doubt, it is possible to check if an address is payable using Address.is_payable(addr) . Payable entrypoints A contract entrypoint is by default not payable. Any call to such a function (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\" ) Namespaces Code can be split into libraries using the namespace construct. Namespaces 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 )) 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\" using Pair contract C = type state = int entrypoint init() = let p = (1, 2) 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\" 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 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 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 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 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. Splitting code over multiple files Code from another file can be included in a contract using an include statement. These must appear at the top-level (outside the main contract). The included file can contain one or more namespaces and abstract contracts. For example, if the file library.aes contains 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 )) 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, so even cyclic includes should be working without any special tinkering. Standard library Sophia offers standard library which exposes some primitive operations and some higher level utilities. The builtin 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 ))) Types Sophia has the following types: Type Description Example int A 2-complement integer -1 address \u00e6ternity address, 32 bytes Call.origin bool A Boolean true bits A bit field Bits.none bytes(n) A byte array with n bytes #fedcba9876543210 string An array of bytes \"Foo\" list A homogeneous immutable singly linked list. [1, 2, 3] ('a, 'b) => 'c A function. Parentheses can be skipped if there is only one argument (x : int, y : int) => x + y tuple An ordered heterogeneous array (42, \"Foo\", true) record An immutable key value store with fixed key names and typed values record balance = { owner: address, value: int } map An immutable key value store with dynamic mapping of keys of one type to values of one type type accounts = map(string, address) option('a) An optional value either None or Some('a) Some(42) state A user defined type holding the contract state record state = { owner: address, magic_key: bytes(4) } event An append only list of blockchain events (or log entries) datatype event = EventX(indexed int, string) hash A 32-byte hash - equivalent to bytes(32) signature A signature - equivalent to bytes(64) Chain.ttl Time-to-live (fixed height or relative to current block) FixedTTL(1050) RelativeTTL(50) oracle('a, 'b) And oracle answering questions of type 'a with answers of type 'b Oracle.register(acct, qfee, ttl) oracle_query('a, 'b) A specific oracle query Oracle.query(o, q, qfee, qttl, rttl) contract A user defined, typed, contract address function call_remote(r : RemoteContract) = r.fun() Literals Type Constant/Literal example(s) int -1 , 2425 , 4598275923475723498573485768 address ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt bool true , false bits Bits.none , Bits.all bytes(8) #fedcba9876543210 string \"This is a string\" list [1, 2, 3] , [(true, 24), (false, 19), (false, -42)] tuple (42, \"Foo\", true) record { owner = Call.origin, value = 100000000 } map {[\"foo\"] = 19, [\"bar\"] = 42} , {} option(int) Some(42) , None state state{ owner = Call.origin, magic_key = #a298105f } event EventX(0, \"Hello\") hash #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f signature #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f Chain.ttl FixedTTL(1050) , RelativeTTL(50) oracle('a, 'b) ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 oracle_query('a, 'b) oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY contract ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ Arithmetic Sophia integers ( int ) are represented by arbitrary-sized signed words and support the following arithmetic operations: - addition ( x + y ) - subtraction ( x - y ) - multiplication ( x * y ) - division ( x / y ), truncated towards zero - remainder ( x mod y ), satisfying y * (x / y) + x mod y == x for non-zero y - exponentiation ( x ^ y ) All operations are safe with respect to overflow and underflow. The division and modulo operations throw an arithmetic error if the right-hand operand is zero. Bit fields Sophia integers do not support bit arithmetic. Instead there is a separate type bits . See the standard library documentation . A bit field can be of arbitrary size (but it is still represented by the corresponding integer, so setting very high bits can be expensive). Type aliases Type aliases can be introduced with the type keyword and can be parameterized. For instance type number = int type string_map ( 'a ) = map ( string , 'a ) A type alias and its definition can be used interchangeably. Sophia does not support higher-kinded types, meaning that following type alias is invalid: type wrap('f, 'a) = 'f('a) Algebraic data types Sophia supports algebraic data types (variant types) and pattern matching. Data types are declared by giving a list of constructors with their respective arguments. For instance, 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: function get_left ( x : one_or_both ( 'a , 'b )) : option ( 'a ) = switch ( x ) Left ( x ) => Some ( x ) Right ( _ ) => None Both ( x , _ ) => Some ( x ) or directly in the left-hand side: function get_left : one_or_both ( 'a , 'b ) => option ( 'a ) get_left ( Left ( x )) = Some ( x ) get_left ( Right ( _ )) = None get_left ( Both ( x , _ )) = Some ( x ) NOTE: Data types cannot currently be recursive. Sophia also supports the assignment of patterns to variables: function f ( x ) = switch ( x ) h1 :: ( t = h2 :: _ ) => ( h1 + h2 ) :: t // same as `h1::h2::k => (h1 + h2)::h2::k` _ => x function g ( p : int * option ( int )) : int = let ( a , ( o = Some ( b ))) = p // o is equal to Pair.snd(p) b Guards are boolean expressions that can be used on patterns in both switch statements and functions definitions. If a guard expression evaluates to true , then the corresponding body will be used. Otherwise, the next pattern will be checked: function get_left_if_positive ( x : one_or_both ( int , 'b )) : option ( int ) = switch ( x ) Left ( x ) | x > 0 => Some ( x ) Both ( x , _ ) | x > 0 => Some ( x ) _ => None function get_left_if_positive : one_or_both ( int , 'b ) => option ( int ) get_left_if_positive ( Left ( x )) | x > 0 = Some ( x ) get_left_if_positive ( Both ( x , _ )) | x > 0 = Some ( x ) get_left_if_positive ( _ ) = None Guards cannot be stateful even when used inside a stateful function. Lists A Sophia list is a dynamically sized, homogenous, immutable, singly linked list. A list is constructed with the syntax [1, 2, 3] . The 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 list('e) . For example we can have the following lists: [ 1 , 33 , 2 , 666 ] : list ( int ) [( 1 , \"aaa\" ), ( 10 , \"jjj\" ), ( 666 , \"the beast\" )] : list ( int * string ) [{[ 1 ] = \"aaa\" , [ 10 ] = \"jjj\" }, {[ 5 ] = \"eee\" , [ 666 ] = \"the beast\" }] : list ( map ( int , string )) New elements can be prepended to the front of a list with the :: operator. So 42 :: [1, 2, 3] returns the list [42, 1, 2, 3] . The concatenation operator ++ appends its second argument to its first and returns the resulting list. So concatenating two lists [1, 22, 33] ++ [10, 18, 55] returns the list [1, 22, 33, 10, 18, 55] . 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] Lists can be constructed using the range syntax using special .. operator: [ 1 .. 4 ] == [ 1 , 2 , 3 , 4 ] The ranges are always ascending and have step equal to 1. Please refer to the standard library for the predefined functionalities. Maps and records A Sophia record type is given by a fixed set of fields with associated, possibly different, types. For instance record account = { name : string , balance : int , history : list ( transaction ) } Maps, on the other hand, can contain an arbitrary number of key-value bindings, but of a fixed type. The type of maps with keys of type 'k and values of type 'v is written map('k, 'v) . The key type can be any type that does not contain a map or a function type. Please refer to the standard library for the predefined functionalities. Constructing maps and records A value of record type is constructed by giving a value for each of the fields. For the example above, function new_account ( name ) = { name = name , balance = 0 , history = []} Maps are constructed similarly, with keys enclosed in square brackets function example_map () : map ( string , int ) = {[ \"key1\" ] = 1 , [ \"key2\" ] = 2 } The empty map is written {} . Accessing values Record fields access is written r.f and map lookup m[k] . For instance, function get_balance ( a : address , accounts : map ( address , account )) = accounts [ a ]. balance Looking up a non-existing key in a map results in contract execution failing. A default value to return for non-existing keys can be provided using the syntax m[k = default] . See also Map.member and Map.lookup below. Updating a value Record field updates are written r{f = v} . This creates a new record value which is the same as r , but with the value of the field f replaced by v . Similarly, m{[k] = v} constructs a map with the same values as m except that k maps to v . It makes no difference if m has a mapping for k or not. It is possible to give a name to the old value of a field or mapping in an update: instead of acc{ balance = acc.balance + 100 } it is possible to write acc{ balance @ b = b + 100 } , binding b to acc.balance . When giving a name to a map value ( m{ [k] @ x = v } ), the corresponding key must be present in the map or execution fails, but a default value can be provided: m{ [k = default] @ x = v } . In this case x is bound to default if k is not in the map. Updates can be nested: function clear_history ( a : address , accounts : map ( address , account )) : map ( address , account ) = accounts { [ a ]. history = [] } 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 an account if a is not in the map you can write (given a function empty_account ): accounts { [ a = empty_account ()]. history = [] } Map implementation Internally in the VM maps are implemented as hash maps and support fast lookup and update. Large maps can be stored in the contract state and the size of the map does not contribute to the gas costs of a contract call reading or updating it. Strings There is a builtin type string , which can be seen as an array of bytes. Strings can be compared for equality ( == , != ), used as keys in maps and records, and used in builtin functions String.length , String.concat and the hash functions described below. Please refer to the String library documentation . Chars There is a builtin type char (the underlying representation being an integer), mainly used to manipulate strings via String.to_list / String.from_list . Characters can also be introduced as character literals (`'x', '+', ...). Please refer to the Char library documentation . Byte arrays Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system, for example the literal #cafe creates a two-element array of bytes ca (202) and fe (254) and thus is a value of type bytes(2) . Please refer to the Bytes library documentation . Cryptographic builtins Libraries Crypto and String provide functions to hash objects, verify signatures etc. The hash is a type alias for bytes(32) . Authorization interface When a Generalized account is authorized, the authorization function needs access to the transaction and the transaction hash for the wrapped transaction. (A GAMetaTx wrapping a transaction.) The transaction and the transaction hash is available in the primitive Auth.tx and Auth.tx_hash respectively, they are only available during authentication if invoked by a normal contract call they return None . Oracle interface You can attach an oracle to the current contract and you can interact with oracles through the Oracle interface. For a full description of how Oracle works see Oracles . For a functionality documentation refer to the standard library . Example Example for an oracle answering questions of type string with answers of type int : contract Oracles = stateful entrypoint registerOracle ( acct : address , sign : signature , // Signed network id + oracle address + contract address qfee : int , ttl : Chain . ttl ) : oracle ( string , int ) = Oracle . register ( acct , signature = sign , qfee , ttl ) entrypoint queryFee ( o : oracle ( string , int )) : int = Oracle . query_fee ( o ) payable stateful entrypoint createQuery ( o : oracle_query ( string , int ), q : string , qfee : int , qttl : Chain . ttl , rttl : int ) : oracle_query ( string , int ) = require ( qfee =< Call . value , \"insufficient value for qfee\" ) Oracle . query ( o , q , qfee , qttl , RelativeTTL ( rttl )) stateful entrypoint extendOracle ( o : oracle ( string , int ), ttl : Chain . ttl ) : unit = Oracle . extend ( o , ttl ) stateful entrypoint signExtendOracle ( o : oracle ( string , int ), sign : signature , // Signed network id + oracle address + contract address ttl : Chain . ttl ) : unit = Oracle . extend ( o , signature = sign , ttl ) stateful entrypoint respond ( o : oracle ( string , int ), q : oracle_query ( string , int ), sign : signature , // Signed network id + oracle query id + contract address r : int ) = Oracle . respond ( o , q , signature = sign , r ) entrypoint getQuestion ( o : oracle ( string , int ), q : oracle_query ( string , int )) : string = Oracle . get_question ( o , q ) entrypoint hasAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) = switch ( Oracle . get_answer ( o , q )) None => false Some ( _ ) => true entrypoint getAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) : option ( int ) = Oracle . get_answer ( o , q ) Sanity checks When an Oracle literal is passed to a contract, no deep checks are performed. For extra safety Oracle.check and Oracle.check_query functions are provided. AENS interface Contracts can interact with the \u00e6ternity naming system . For this purpose the AENS library was exposed. Example In this example we assume that the name name already exists, and is owned by an account with address addr . In order to allow a contract ct to handle name the account holder needs to create a signature sig of addr | name.hash | ct.address . Armed with this information we can for example write a function that extends the name if it expires within 1000 blocks: stateful entrypoint extend_if_necessary ( addr : address , name : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , FixedTTL ( expiry ), _ )) => if ( Chain . block_height + 1000 > expiry ) AENS . update ( addr , name , Some ( RelativeTTL ( 50000 )), None , None , signature = sig ) And we can write functions that adds and removes keys from the pointers of the name: stateful entrypoint add_key ( addr : address , name : string , key : string , pt : AENS . pointee , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => AENS . update ( addr , name , None , None , Some ( ptrs {[ key ] = pt }), signature = sig ) stateful entrypoint delete_key ( addr : address , name : string , key : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => let ptrs = Map . delete ( key , ptrs ) AENS . update ( addr , name , None , None , Some ( ptrs ), signature = sig ) Note: From the Iris hardfork more strict rules apply for AENS pointers, when a Sophia contract lookup or update (bad) legacy pointers, the bad keys are automatically removed so they will not appear in the pointers map. Events Sophia contracts log structured messages to an event log in the resulting blockchain transaction. The event log is quite similar to Events in Solidity . Events are further discussed in the protocol . To use events a contract must declare a datatype event , and events are then logged using the Chain.event function: datatype event = Event1 ( int , int , string ) | Event2 ( string , address ) Chain . event ( e : event ) : unit The event can have 0-3 indexed fields, and an optional payload field. A field is indexed if it fits in a 32-byte word, i.e. - bool - int - bits - address - oracle(_, _) - oracle_query(_, _) - contract types - bytes(n) for n \u2264 32, in particular hash The payload field must be either a string or a byte array of more than 32 bytes. The fields can appear in any order. NOTE: Indexing is not part of the core \u00e6ternity node. Events are emitted by using the Chain.event function. The following function will emit one Event of each kind in the example. entrypoint emit_events () : () = Chain . event ( Event1 ( 42 , 34 , \"foo\" )) Chain . event ( Event2 ( \"This is not indexed\" , Contract . address )) Argument order 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. AnotherEvent ( string , indexed address ) ... Chain . event ( AnotherEvent ( \"This is not indexed\" , Contract . address )) would yield exactly the same result in the example above! Compiler pragmas To enforce that a contract is only compiled with specific versions of the Sophia compiler, you can give one or more @compiler pragmas at the top-level (typically at the beginning) of a file. For instance, to enforce that a contract is compiled with version 4.3 of the compiler you write @ compiler >= 4 . 3 @ compiler < 4 . 4 Valid operators in compiler pragmas are < , =< , == , >= , and > . Version numbers are given as a sequence of non-negative integers separated by dots. Trailing zeros are ignored, so 4.0.0 == 4 . If a constraint is violated an error is reported and compilation fails. Exceptions Contracts can fail with an (uncatchable) exception using the built-in function abort ( reason : string ) : 'a Calling abort causes the top-level call transaction to return an error result containing the reason string. Only the gas used up to and including the abort call is charged. This is different from termination due to a crash which consumes all available gas. For convenience the following function is also built-in: function require ( b : bool , err : string ) = if ( ! b ) abort ( err ) Aside from that, there is an almost equivalent function exit exit ( reason : string ) : 'a Just like abort , it breaks the execution with the given reason. The difference however is in the gas consumption \u2014 while abort returns unused gas, a call to exit burns it all. Delegation signature Some chain operations ( Oracle. and AENS. ) have an optional delegation signature. This is typically used when a user/accounts would like to allow a contract to act on it's behalf. The exact data to be signed varies for the different operations, but in all cases you should prepend the signature data with the network_id ( ae_mainnet for the \u00e6ternity mainnet, etc.).","title":"Features"},{"location":"sophia_features/#features","text":"","title":"Features"},{"location":"sophia_features/#contracts","text":"The main unit of code in Sophia is the contract . A contract implementation, or simply a contract, is the code for a smart contract and consists of a list of types, entrypoints and local functions. Only the entrypoints can be called from outside the contract. A contract instance is an entity living on the block chain (or in a state channel). Each instance has an address that can be used to call its entrypoints, either from another contract or in a call transaction. A contract may define a type state encapsulating its local state. When creating a new contract the init entrypoint is executed and the state is initialized to its return value. The language offers some primitive functions to interact with the blockchain and contracts. Please refer to the Chain , Contract and the Call namespaces in the documentation.","title":"Contracts"},{"location":"sophia_features/#calling-other-contracts","text":"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 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 ) 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 ) 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). To construct a value of a contract type you can give a contract address literal (for instance ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay ), or convert an account address to a contract address using Address.to_contract . Note that if the contract does not exist, or it doesn't have the entrypoint, or the type of the entrypoint does not match the stated contract type, the call 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 )","title":"Calling other contracts"},{"location":"sophia_features/#protected-contract-calls","text":"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 ). The protected argument must be a literal boolean, and when set to true changes the type of the contract call, wrapping the result in an option type. If the call fails the result is 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\" 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. However, note that errors that would normally consume all the gas in the transaction still only uses up the gas spent running the contract. Any side effects (state change, token transfers, etc.) made by a failing protected call is rolled back, just like they would be in the unprotected case.","title":"Protected contract calls"},{"location":"sophia_features/#contract-factories-and-child-contracts","text":"Since the version 6.0.0 Sophia supports deploying contracts by other contracts. This can be done in two ways: Contract cloning via Chain.clone Direct deploy via Chain.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 \u2013 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 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.","title":"Contract factories and child contracts"},{"location":"sophia_features/#contract-interfaces-and-polymorphism","text":"Contracts can implement one or multiple interfaces, the contract has to define every entrypoint from the implemented interface and the entrypoints in both the contract and implemented interface should have compatible types. contract interface Animal = entrypoint sound : () => string contract Cat : Animal = entrypoint sound() = \"Cat sound\" Contract interfaces can extend other interfaces. An extended interface has to declare all entrypoints from every parent interface. All the declarations in the extended interface must have types compatible with the declarations from the parent interface. contract interface II = entrypoint f : () => unit contract interface I : II = entrypoint f : () => unit entrypoint g : () => unit contract C : I = entrypoint f() = () entrypoint g() = () It is only possible to implement (or extend) an interface that has been already defined earlier in the file (or in an included file). Therefore recursive interface implementation is not allowed in Sophia. // The following code would show an error contract interface X : Z = entrypoint x : () => int contract interface Y : X = entrypoint x : () => int entrypoint y : () => int contract interface Z : Y = entrypoint x : () => int entrypoint y : () => int entrypoint z : () => int contract C : Z = entrypoint x() = 1 entrypoint y() = 1 entrypoint z() = 1","title":"Contract interfaces and polymorphism"},{"location":"sophia_features/#mutable-state","text":"Sophia does not have arbitrary mutable state, but only a limited form of state associated with each contract instance. Each contract defines a type state encapsulating its mutable state. The type state defaults to the unit . The initial state of a contract is computed by the contract's init function. The init function is pure and returns the initial state as its return value. If the type state is unit , the init function defaults to returning the value () . At contract creation time, the init function is executed and its result is stored as the contract state. The value of the state is accessible from inside the contract through an implicitly bound variable state . State updates are performed by calling a function put : state => unit . Aside from the put function (and similar functions for transactions and events), the language is purely functional. Functions modifying the state need to be annotated with the stateful keyword (see below). To make it convenient to update parts of a deeply nested state Sophia provides special syntax for map/record updates.","title":"Mutable state"},{"location":"sophia_features/#stateful-functions","text":"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 ) Without the stateful annotation the compiler does not allow the call to put . A stateful annotation is required to Use a stateful primitive function. These are put Chain.spend Oracle.register Oracle.query Oracle.respond Oracle.extend AENS.preclaim AENS.claim AENS.transfer AENS.revoke AENS.update Call a stateful function in the current contract Call another contract with a non-zero value argument. A stateful annotation is not required to Read the contract state. Issue an event using the event function. Call another contract with value = 0 , even if the called function is stateful.","title":"Stateful functions"},{"location":"sophia_features/#payable","text":"","title":"Payable"},{"location":"sophia_features/#payable-contracts","text":"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 () = ... If in doubt, it is possible to check if an address is payable using Address.is_payable(addr) .","title":"Payable contracts"},{"location":"sophia_features/#payable-entrypoints","text":"A contract entrypoint is by default not payable. Any call to such a function (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\" )","title":"Payable entrypoints"},{"location":"sophia_features/#namespaces","text":"Code can be split into libraries using the namespace construct. Namespaces 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 )) 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\" using Pair contract C = type state = int entrypoint init() = let p = (1, 2) 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\" 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 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 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 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 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.","title":"Namespaces"},{"location":"sophia_features/#splitting-code-over-multiple-files","text":"Code from another file can be included in a contract using an include statement. These must appear at the top-level (outside the main contract). The included file can contain one or more namespaces and abstract contracts. For example, if the file library.aes contains 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 )) 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, so even cyclic includes should be working without any special tinkering.","title":"Splitting code over multiple files"},{"location":"sophia_features/#standard-library","text":"Sophia offers standard library which exposes some primitive operations and some higher level utilities. The builtin 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 )))","title":"Standard library"},{"location":"sophia_features/#types","text":"Sophia has the following types: Type Description Example int A 2-complement integer -1 address \u00e6ternity address, 32 bytes Call.origin bool A Boolean true bits A bit field Bits.none bytes(n) A byte array with n bytes #fedcba9876543210 string An array of bytes \"Foo\" list A homogeneous immutable singly linked list. [1, 2, 3] ('a, 'b) => 'c A function. Parentheses can be skipped if there is only one argument (x : int, y : int) => x + y tuple An ordered heterogeneous array (42, \"Foo\", true) record An immutable key value store with fixed key names and typed values record balance = { owner: address, value: int } map An immutable key value store with dynamic mapping of keys of one type to values of one type type accounts = map(string, address) option('a) An optional value either None or Some('a) Some(42) state A user defined type holding the contract state record state = { owner: address, magic_key: bytes(4) } event An append only list of blockchain events (or log entries) datatype event = EventX(indexed int, string) hash A 32-byte hash - equivalent to bytes(32) signature A signature - equivalent to bytes(64) Chain.ttl Time-to-live (fixed height or relative to current block) FixedTTL(1050) RelativeTTL(50) oracle('a, 'b) And oracle answering questions of type 'a with answers of type 'b Oracle.register(acct, qfee, ttl) oracle_query('a, 'b) A specific oracle query Oracle.query(o, q, qfee, qttl, rttl) contract A user defined, typed, contract address function call_remote(r : RemoteContract) = r.fun()","title":"Types"},{"location":"sophia_features/#literals","text":"Type Constant/Literal example(s) int -1 , 2425 , 4598275923475723498573485768 address ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt bool true , false bits Bits.none , Bits.all bytes(8) #fedcba9876543210 string \"This is a string\" list [1, 2, 3] , [(true, 24), (false, 19), (false, -42)] tuple (42, \"Foo\", true) record { owner = Call.origin, value = 100000000 } map {[\"foo\"] = 19, [\"bar\"] = 42} , {} option(int) Some(42) , None state state{ owner = Call.origin, magic_key = #a298105f } event EventX(0, \"Hello\") hash #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f signature #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f Chain.ttl FixedTTL(1050) , RelativeTTL(50) oracle('a, 'b) ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 oracle_query('a, 'b) oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY contract ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ","title":"Literals"},{"location":"sophia_features/#arithmetic","text":"Sophia integers ( int ) are represented by arbitrary-sized signed words and support the following arithmetic operations: - addition ( x + y ) - subtraction ( x - y ) - multiplication ( x * y ) - division ( x / y ), truncated towards zero - remainder ( x mod y ), satisfying y * (x / y) + x mod y == x for non-zero y - exponentiation ( x ^ y ) All operations are safe with respect to overflow and underflow. The division and modulo operations throw an arithmetic error if the right-hand operand is zero.","title":"Arithmetic"},{"location":"sophia_features/#bit-fields","text":"Sophia integers do not support bit arithmetic. Instead there is a separate type bits . See the standard library documentation . A bit field can be of arbitrary size (but it is still represented by the corresponding integer, so setting very high bits can be expensive).","title":"Bit fields"},{"location":"sophia_features/#type-aliases","text":"Type aliases can be introduced with the type keyword and can be parameterized. For instance type number = int type string_map ( 'a ) = map ( string , 'a ) A type alias and its definition can be used interchangeably. Sophia does not support higher-kinded types, meaning that following type alias is invalid: type wrap('f, 'a) = 'f('a)","title":"Type aliases"},{"location":"sophia_features/#algebraic-data-types","text":"Sophia supports algebraic data types (variant types) and pattern matching. Data types are declared by giving a list of constructors with their respective arguments. For instance, 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: function get_left ( x : one_or_both ( 'a , 'b )) : option ( 'a ) = switch ( x ) Left ( x ) => Some ( x ) Right ( _ ) => None Both ( x , _ ) => Some ( x ) or directly in the left-hand side: function get_left : one_or_both ( 'a , 'b ) => option ( 'a ) get_left ( Left ( x )) = Some ( x ) get_left ( Right ( _ )) = None get_left ( Both ( x , _ )) = Some ( x ) NOTE: Data types cannot currently be recursive. Sophia also supports the assignment of patterns to variables: function f ( x ) = switch ( x ) h1 :: ( t = h2 :: _ ) => ( h1 + h2 ) :: t // same as `h1::h2::k => (h1 + h2)::h2::k` _ => x function g ( p : int * option ( int )) : int = let ( a , ( o = Some ( b ))) = p // o is equal to Pair.snd(p) b Guards are boolean expressions that can be used on patterns in both switch statements and functions definitions. If a guard expression evaluates to true , then the corresponding body will be used. Otherwise, the next pattern will be checked: function get_left_if_positive ( x : one_or_both ( int , 'b )) : option ( int ) = switch ( x ) Left ( x ) | x > 0 => Some ( x ) Both ( x , _ ) | x > 0 => Some ( x ) _ => None function get_left_if_positive : one_or_both ( int , 'b ) => option ( int ) get_left_if_positive ( Left ( x )) | x > 0 = Some ( x ) get_left_if_positive ( Both ( x , _ )) | x > 0 = Some ( x ) get_left_if_positive ( _ ) = None Guards cannot be stateful even when used inside a stateful function.","title":"Algebraic data types"},{"location":"sophia_features/#lists","text":"A Sophia list is a dynamically sized, homogenous, immutable, singly linked list. A list is constructed with the syntax [1, 2, 3] . The 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 list('e) . For example we can have the following lists: [ 1 , 33 , 2 , 666 ] : list ( int ) [( 1 , \"aaa\" ), ( 10 , \"jjj\" ), ( 666 , \"the beast\" )] : list ( int * string ) [{[ 1 ] = \"aaa\" , [ 10 ] = \"jjj\" }, {[ 5 ] = \"eee\" , [ 666 ] = \"the beast\" }] : list ( map ( int , string )) New elements can be prepended to the front of a list with the :: operator. So 42 :: [1, 2, 3] returns the list [42, 1, 2, 3] . The concatenation operator ++ appends its second argument to its first and returns the resulting list. So concatenating two lists [1, 22, 33] ++ [10, 18, 55] returns the list [1, 22, 33, 10, 18, 55] . 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] Lists can be constructed using the range syntax using special .. operator: [ 1 .. 4 ] == [ 1 , 2 , 3 , 4 ] The ranges are always ascending and have step equal to 1. Please refer to the standard library for the predefined functionalities.","title":"Lists"},{"location":"sophia_features/#maps-and-records","text":"A Sophia record type is given by a fixed set of fields with associated, possibly different, types. For instance record account = { name : string , balance : int , history : list ( transaction ) } Maps, on the other hand, can contain an arbitrary number of key-value bindings, but of a fixed type. The type of maps with keys of type 'k and values of type 'v is written map('k, 'v) . The key type can be any type that does not contain a map or a function type. Please refer to the standard library for the predefined functionalities.","title":"Maps and records"},{"location":"sophia_features/#constructing-maps-and-records","text":"A value of record type is constructed by giving a value for each of the fields. For the example above, function new_account ( name ) = { name = name , balance = 0 , history = []} Maps are constructed similarly, with keys enclosed in square brackets function example_map () : map ( string , int ) = {[ \"key1\" ] = 1 , [ \"key2\" ] = 2 } The empty map is written {} .","title":"Constructing maps and records"},{"location":"sophia_features/#accessing-values","text":"Record fields access is written r.f and map lookup m[k] . For instance, function get_balance ( a : address , accounts : map ( address , account )) = accounts [ a ]. balance Looking up a non-existing key in a map results in contract execution failing. A default value to return for non-existing keys can be provided using the syntax m[k = default] . See also Map.member and Map.lookup below.","title":"Accessing values"},{"location":"sophia_features/#updating-a-value","text":"Record field updates are written r{f = v} . This creates a new record value which is the same as r , but with the value of the field f replaced by v . Similarly, m{[k] = v} constructs a map with the same values as m except that k maps to v . It makes no difference if m has a mapping for k or not. It is possible to give a name to the old value of a field or mapping in an update: instead of acc{ balance = acc.balance + 100 } it is possible to write acc{ balance @ b = b + 100 } , binding b to acc.balance . When giving a name to a map value ( m{ [k] @ x = v } ), the corresponding key must be present in the map or execution fails, but a default value can be provided: m{ [k = default] @ x = v } . In this case x is bound to default if k is not in the map. Updates can be nested: function clear_history ( a : address , accounts : map ( address , account )) : map ( address , account ) = accounts { [ a ]. history = [] } 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 an account if a is not in the map you can write (given a function empty_account ): accounts { [ a = empty_account ()]. history = [] }","title":"Updating a value"},{"location":"sophia_features/#map-implementation","text":"Internally in the VM maps are implemented as hash maps and support fast lookup and update. Large maps can be stored in the contract state and the size of the map does not contribute to the gas costs of a contract call reading or updating it.","title":"Map implementation"},{"location":"sophia_features/#strings","text":"There is a builtin type string , which can be seen as an array of bytes. Strings can be compared for equality ( == , != ), used as keys in maps and records, and used in builtin functions String.length , String.concat and the hash functions described below. Please refer to the String library documentation .","title":"Strings"},{"location":"sophia_features/#chars","text":"There is a builtin type char (the underlying representation being an integer), mainly used to manipulate strings via String.to_list / String.from_list . Characters can also be introduced as character literals (`'x', '+', ...). Please refer to the Char library documentation .","title":"Chars"},{"location":"sophia_features/#byte-arrays","text":"Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system, for example the literal #cafe creates a two-element array of bytes ca (202) and fe (254) and thus is a value of type bytes(2) . Please refer to the Bytes library documentation .","title":"Byte arrays"},{"location":"sophia_features/#cryptographic-builtins","text":"Libraries Crypto and String provide functions to hash objects, verify signatures etc. The hash is a type alias for bytes(32) .","title":"Cryptographic builtins"},{"location":"sophia_features/#authorization-interface","text":"When a Generalized account is authorized, the authorization function needs access to the transaction and the transaction hash for the wrapped transaction. (A GAMetaTx wrapping a transaction.) The transaction and the transaction hash is available in the primitive Auth.tx and Auth.tx_hash respectively, they are only available during authentication if invoked by a normal contract call they return None .","title":"Authorization interface"},{"location":"sophia_features/#oracle-interface","text":"You can attach an oracle to the current contract and you can interact with oracles through the Oracle interface. For a full description of how Oracle works see Oracles . For a functionality documentation refer to the standard library .","title":"Oracle interface"},{"location":"sophia_features/#example","text":"Example for an oracle answering questions of type string with answers of type int : contract Oracles = stateful entrypoint registerOracle ( acct : address , sign : signature , // Signed network id + oracle address + contract address qfee : int , ttl : Chain . ttl ) : oracle ( string , int ) = Oracle . register ( acct , signature = sign , qfee , ttl ) entrypoint queryFee ( o : oracle ( string , int )) : int = Oracle . query_fee ( o ) payable stateful entrypoint createQuery ( o : oracle_query ( string , int ), q : string , qfee : int , qttl : Chain . ttl , rttl : int ) : oracle_query ( string , int ) = require ( qfee =< Call . value , \"insufficient value for qfee\" ) Oracle . query ( o , q , qfee , qttl , RelativeTTL ( rttl )) stateful entrypoint extendOracle ( o : oracle ( string , int ), ttl : Chain . ttl ) : unit = Oracle . extend ( o , ttl ) stateful entrypoint signExtendOracle ( o : oracle ( string , int ), sign : signature , // Signed network id + oracle address + contract address ttl : Chain . ttl ) : unit = Oracle . extend ( o , signature = sign , ttl ) stateful entrypoint respond ( o : oracle ( string , int ), q : oracle_query ( string , int ), sign : signature , // Signed network id + oracle query id + contract address r : int ) = Oracle . respond ( o , q , signature = sign , r ) entrypoint getQuestion ( o : oracle ( string , int ), q : oracle_query ( string , int )) : string = Oracle . get_question ( o , q ) entrypoint hasAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) = switch ( Oracle . get_answer ( o , q )) None => false Some ( _ ) => true entrypoint getAnswer ( o : oracle ( string , int ), q : oracle_query ( string , int )) : option ( int ) = Oracle . get_answer ( o , q )","title":"Example"},{"location":"sophia_features/#sanity-checks","text":"When an Oracle literal is passed to a contract, no deep checks are performed. For extra safety Oracle.check and Oracle.check_query functions are provided.","title":"Sanity checks"},{"location":"sophia_features/#aens-interface","text":"Contracts can interact with the \u00e6ternity naming system . For this purpose the AENS library was exposed.","title":"AENS interface"},{"location":"sophia_features/#example_1","text":"In this example we assume that the name name already exists, and is owned by an account with address addr . In order to allow a contract ct to handle name the account holder needs to create a signature sig of addr | name.hash | ct.address . Armed with this information we can for example write a function that extends the name if it expires within 1000 blocks: stateful entrypoint extend_if_necessary ( addr : address , name : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , FixedTTL ( expiry ), _ )) => if ( Chain . block_height + 1000 > expiry ) AENS . update ( addr , name , Some ( RelativeTTL ( 50000 )), None , None , signature = sig ) And we can write functions that adds and removes keys from the pointers of the name: stateful entrypoint add_key ( addr : address , name : string , key : string , pt : AENS . pointee , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => AENS . update ( addr , name , None , None , Some ( ptrs {[ key ] = pt }), signature = sig ) stateful entrypoint delete_key ( addr : address , name : string , key : string , sig : signature ) = switch ( AENS . lookup ( name )) None => () Some ( AENS . Name ( _ , _ , ptrs )) => let ptrs = Map . delete ( key , ptrs ) AENS . update ( addr , name , None , None , Some ( ptrs ), signature = sig ) Note: From the Iris hardfork more strict rules apply for AENS pointers, when a Sophia contract lookup or update (bad) legacy pointers, the bad keys are automatically removed so they will not appear in the pointers map.","title":"Example"},{"location":"sophia_features/#events","text":"Sophia contracts log structured messages to an event log in the resulting blockchain transaction. The event log is quite similar to Events in Solidity . Events are further discussed in the protocol . To use events a contract must declare a datatype event , and events are then logged using the Chain.event function: datatype event = Event1 ( int , int , string ) | Event2 ( string , address ) Chain . event ( e : event ) : unit The event can have 0-3 indexed fields, and an optional payload field. A field is indexed if it fits in a 32-byte word, i.e. - bool - int - bits - address - oracle(_, _) - oracle_query(_, _) - contract types - bytes(n) for n \u2264 32, in particular hash The payload field must be either a string or a byte array of more than 32 bytes. The fields can appear in any order. NOTE: Indexing is not part of the core \u00e6ternity node. Events are emitted by using the Chain.event function. The following function will emit one Event of each kind in the example. entrypoint emit_events () : () = Chain . event ( Event1 ( 42 , 34 , \"foo\" )) Chain . event ( Event2 ( \"This is not indexed\" , Contract . address ))","title":"Events"},{"location":"sophia_features/#argument-order","text":"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. AnotherEvent ( string , indexed address ) ... Chain . event ( AnotherEvent ( \"This is not indexed\" , Contract . address )) would yield exactly the same result in the example above!","title":"Argument order"},{"location":"sophia_features/#compiler-pragmas","text":"To enforce that a contract is only compiled with specific versions of the Sophia compiler, you can give one or more @compiler pragmas at the top-level (typically at the beginning) of a file. For instance, to enforce that a contract is compiled with version 4.3 of the compiler you write @ compiler >= 4 . 3 @ compiler < 4 . 4 Valid operators in compiler pragmas are < , =< , == , >= , and > . Version numbers are given as a sequence of non-negative integers separated by dots. Trailing zeros are ignored, so 4.0.0 == 4 . If a constraint is violated an error is reported and compilation fails.","title":"Compiler pragmas"},{"location":"sophia_features/#exceptions","text":"Contracts can fail with an (uncatchable) exception using the built-in function abort ( reason : string ) : 'a Calling abort causes the top-level call transaction to return an error result containing the reason string. Only the gas used up to and including the abort call is charged. This is different from termination due to a crash which consumes all available gas. For convenience the following function is also built-in: function require ( b : bool , err : string ) = if ( ! b ) abort ( err ) Aside from that, there is an almost equivalent function exit exit ( reason : string ) : 'a Just like abort , it breaks the execution with the given reason. The difference however is in the gas consumption \u2014 while abort returns unused gas, a call to exit burns it all.","title":"Exceptions"},{"location":"sophia_features/#delegation-signature","text":"Some chain operations ( Oracle. and AENS. ) have an optional delegation signature. This is typically used when a user/accounts would like to allow a contract to act on it's behalf. The exact data to be signed varies for the different operations, but in all cases you should prepend the signature data with the network_id ( ae_mainnet for the \u00e6ternity mainnet, etc.).","title":"Delegation signature"},{"location":"sophia_stdlib/","text":"Standard library Sophia language offers standard library that consists of several namespaces. Some of them are already in the scope and do not need any actions to be used, while the others require some files to be included. The out-of-the-box namespaces are: Address AENS Auth Bits Bytes Call Chain Char Contract Crypto Int Map Oracle The following ones need to be included as regular files with .aes suffix, for example include \"List.aes\" Bitwise BLS12_381 Func Frac List Option Pair Set String Triple Builtin namespaces They are available without any explicit includes. Address to_str Address.to_str(a : address) : string Base58 encoded string is_contract Address.is_contract(a : address) : bool Is the address a contract is_oracle Address.is_oracle(a : address) : bool Is the address a registered oracle is_payable Address.is_payable(a : address) : bool Can the address be spent to to_contract Address.to_contract(a : address) : C Cast address to contract type C (where C is a contract) AENS The following functionality is available for interacting with the \u00e6ternity naming system (AENS). If owner is equal to Contract.address the signature signature is ignored, and can be left out since it is a named argument. Otherwise we need a signature to prove that we are allowed to do AENS operations on behalf of owner . The signature is tied to a network id , i.e. the signature material should be prefixed by the network id. Types name datatype name = Name(address, Chain.ttl, map(string, AENS.pointee)) pointee datatype pointee = AccountPt(address) | OraclePt(address) | ContractPt(address) | ChannelPt(address) Functions resolve AENS.resolve(name : string, key : string) : option('a) Name resolution. Here name should be a registered name and key one of the attributes associated with this name (for instance \"account_pubkey\" ). The return type ( 'a ) must be resolved at compile time to an atomic type and the value is type checked against this type at run time. lookup AENS.lookup(name : string) : option(AENS.name) If name is an active name AENS.lookup returns a name object. The three arguments to Name are owner , expiry and a map of the pointees for the name. Note: the expiry of the name is always a fixed TTL. For example: let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup(\"example.chain\") preclaim AENS.preclaim(owner : address, commitment_hash : hash, ) : unit The signature should be over network id + owner address + Contract.address (concatenated as byte arrays). claim AENS.claim(owner : address, name : string, salt : int, name_fee : int, ) : unit The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. transfer AENS.transfer(owner : address, new_owner : address, name : string, ) : unit Transfers name to the new owner. The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. revoke AENS.revoke(owner : address, name : string, ) : unit Revokes the name to extend the ownership time. The signature should be over network id + owner address + name_hash + Contract.address (concatenated as byte arrays) using the private key of the owner account for signing. update AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int), new_ptrs : map(string, AENS.pointee), ) : unit Updates the name. If the optional parameters are set to None that parameter will not be updated, for example if None is passed as expiry the expiry block of the name is not changed. Auth tx Auth.tx : option(Chain.tx) Where Chain.tx is (built-in) defined like: namespace Chain = record tx = { paying_for : option(Chain.paying_for_tx) , ga_metas : list(Chain.ga_meta_tx) , actor : address , fee : int , ttl : int , tx : Chain.base_tx } datatype ga_meta_tx = GAMetaTx(address, int) datatype paying_for_tx = PayingForTx(address, int) datatype base_tx = SpendTx(address, int, string) | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string) | NameRevokeTx(hash) | NameTransferTx(address, string) | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) | | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address) | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address) | ContractCreateTx(int) | ContractCallTx(address, int) | GAAttachTx tx_hash Auth.tx_hash : option(hash) Gets the transaction hash during authentication. Bits none Bits.none : bits A bit field with all bits cleared all Bits.all : bits A bit field with all bits set set Bits.set(b : bits, i : int) : bits Set bit i clear Bits.clear(b : bits, i : int) : bits Clear bit i test Bits.test(b : bits, i : int) : bool Check if bit i is set sum Bits.sum(b : bits) : int Count the number of set bits union Bits.union(a : bits, b : bits) : bits Bitwise disjunction intersection Bits.intersection(a : bits, b : bits) : bits Bitwise conjunction difference Bits.difference(a : bits, b : bits) : bits Each bit is true if and only if it was 1 in a and 0 in b Bytes to_int Bytes.to_int(b : bytes(n)) : int Interprets the byte array as a big endian integer to_str Bytes.to_str(b : bytes(n)) : string Returns the hexadecimal representation of the byte array concat Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n) Concatenates two byte arrays split Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n) Splits a byte array at given index Call Values related to the call to the current contract origin Call.origin : address The address of the account that signed the call transaction that led to this call. caller Call.caller : address The address of the entity (possibly another contract) calling the contract. value Call.value : int The amount of coins transferred to the contract in the call. gas_price Call.gas_price : int The gas price of the current call. fee Call.fee : int The fee of the current call. gas_left Call.gas_left() : int The amount of gas left for the current call. Chain Values and functions related to the chain itself and other entities that live on it. Types tx record tx = { paying_for : option(Chain.paying_for_tx) , ga_metas : list(Chain.ga_meta_tx) , actor : address , fee : int , ttl : int , tx : Chain.base_tx } ga_meta_tx datatype ga_meta_tx = GAMetaTx(address, int) paying_for_tx datatype paying_for_tx = PayingForTx(address, int) base_tx datatype base_tx = SpendTx(address, int, string) | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string) | NameRevokeTx(hash) | NameTransferTx(address, string) | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) | | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address) | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address) | ContractCreateTx(int) | ContractCallTx(address, int) | GAAttachTx Functions balance Chain.balance(a : address) : int The balance of account a . block_hash Chain.block_hash(h : int) : option(bytes(32)) The hash of the block at height h . h has to be within 256 blocks from the current height of the chain or else the function will return None . NOTE: In FATE VM version 1 Chain.block_height was not considered an allowed height. From FATE VM version 2 (IRIS) it will return the block hash of the current generation. block_height Chain.block_height : int\" The height of the current block (i.e. the block in which the current call will be included). coinbase Chain.coinbase : address The address of the account that mined the current block. timestamp Chain.timestamp : int The timestamp of the current block (unix time, milliseconds). difficulty Chain.difficulty : int The difficulty of the current block. gas Chain.gas_limit : int The gas limit of the current block. bytecode_hash Chain.bytecode_hash : 'c => option(hash) Returns the 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 increases linearly to the size of the serialized bytecode of the deployed contract. 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 increases linearly with the size of the compiled child contract's bytecode. The source_hash on-chain entry of the newly created contract will be the SHA256 hash over concatenation of whole contract source code single null byte name of the child contract The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the value parameter. The value argument (default 0 ) is equivalent to the value in the contract creation transaction \u2013 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 new_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. 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. The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the value parameter. 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 \u2013 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 Chain.event(e : event) : unit Emits the event. To use this function one needs to define the event type as a datatype in the contract. Char to_int Char.to_int(c : char) : int Returns the UTF-8 codepoint of a character #### from_int Char.from_int(i : int) : option(char) Opposite of to_int . Returns None if the integer doesn't correspond to a single (normalized) codepoint. Contract Values related to the current contract creator Contract.creator : address Address of the entity that signed the contract creation transaction address Contract.address : address Address of the contract account balance Contract.balance : int Amount of coins in the contract account Crypto sha3 Crypto.sha3(x : 'a) : hash Hash any object to SHA3 sha256 Crypto.sha256(x : 'a) : hash Hash any object to SHA256 blake2b Crypto.blake2b(x : 'a) : hash Hash any object to blake2b verify_sig Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool Checks if the signature of msg was made using private key corresponding to the pubkey ecverify_secp256k1 Crypto.ecverify_secp256k1(msg : hash, addr : bytes(20), sig : bytes(65)) : bool Verifies a signature for a msg against an Ethereum style address. Note that the signature should be 65 bytes and include the recovery identifier byte V . The expected organization of the signature is ( V || R || S ). ecrecover_secp256k1 Crypto.ecrecover_secp256k1(msg : hash, sig : bytes(65)) : option(bytes(20)) Recovers the Ethereum style address from a msg hash and respective ECDSA-signature. Note that the signature should be 65 bytes and include the recovery identifier byte V . The expected organization of the signature is ( V || R || S ). verify_sig_secp256k1 Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : bool Verifies a standard 64-byte ECDSA signature ( R || S ). Int to_str Int.to_str : int => string Casts integer to string using decimal representation Map lookup Map.lookup(k : 'k, m : map('k, 'v)) : option('v) Returns the value under a key in given map as Some or None if the key is not present lookup_default Map.lookup_default(k : 'k, m : map('k, 'v), v : 'v) : 'v Returns the value under a key in given map or the default value v if the key is not present member Map.member(k : 'k, m : map('k, 'v)) : bool Checks if the key is present in the map delete Map.delete(k : 'k, m : map('k, 'v)) : map('k, 'v) Removes the key from the map size Map.size(m : map('k, 'v)) : int Returns the number of elements in the map to_list Map.to_list(m : map('k, 'v)) : list('k * 'v) Returns a list containing pairs of keys and their respective elements. from_list Map.from_list(m : list('k * 'v)) : map('k, 'v) Turns a list of pairs of form (key, value) into a map Oracle register Oracle.register(, acct : address, qfee : int, ttl : Chain.ttl) : oracle('a, 'b) Registers new oracle answering questions of type 'a with answers of type 'b . The acct is the address of the oracle to register (can be the same as the contract). signature is a signature proving that the contract is allowed to register the account - the network id + account address + contract address (concatenated as byte arrays) is signed with the private key of the account, proving you have the private key of the oracle to be. If the address is the same as the contract sign is ignored and can be left out entirely. The qfee is the minimum query fee to be paid by a user when asking a question of the oracle. The ttl is the Time To Live for the oracle in key blocks, either relative to the current key block height ( RelativeTTL(delta) ) or a fixed key block height ( FixedTTL(height) ). The type 'a is the type of the question to ask. The type 'b is the type of the oracle answers. Examples: Oracle.register(addr0, 25, RelativeTTL(400)) Oracle.register(addr1, 25, RelativeTTL(500), signature = sign1) get_question Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a Checks what was the question of query q on oracle o respond Oracle.respond(, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit Responds to the question q on o . Unless the contract address is the same as the oracle address the signature (which is an optional, named argument) needs to be provided. Proving that we have the private key of the oracle by signing the network id + oracle query id + contract address extend Oracle.extend(, o : oracle('a, 'b), ttl : Chain.ttl) : unit Extends TTL of an oracle. * singature is a named argument and thus optional. Must be the same as for Oracle.register * o is the oracle being extended * ttl must be RelativeTTL . The time to live of o will be extended by this value. query_fee Oracle.query_fee(o : oracle('a, 'b)) : int Returns the query fee of the oracle query Oracle.query(o : oracle('a, 'b), q : 'a, qfee : int, qttl : Chain.ttl, rttl : Chain.ttl) : oracle_query('a, 'b) Asks the oracle a question. * The qfee is the query fee debited to the contract account ( Contract.address ). * The qttl controls the last height at which the oracle can submit a response and can be either fixed or relative. * The rttl must be relative and controls how long an answer is kept on the chain. The call fails if the oracle could expire before an answer. get_answer Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b) Checks what is the optional query answer expiry Oracle.expiry(o : oracle('a, 'b)) : int Ask the oracle when it expires. The result is the block height at which it will happen. check Oracle.check(o : oracle('a, 'b)) : bool Returns true iff the oracle o exists and has correct type check_query Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool It returns true iff the oracle query exist and has the expected type. Includable namespaces These need to be explicitly included (with .aes suffix) Bitwise Bitwise operations on arbitrary precision integers. bsr Bitwise.bsr(n : int, x : int) : int Logical bit shift x right n positions. bsl Bitwise.bsl(n : int, x : int) : int Logical bit shift x left n positions. bsli Bitwise.bsli(n : int, x : int, lim : int) : int Logical bit shift x left n positions, limit to lim bits. band Bitwise.band(x : int, y : int) : int Bitwise and of x and y . bor Bitwise.bor(x : int, y : int) : int Bitwise or of x and y . bxor Bitwise.bxor(x : int, y : int) : int Bitwise xor of x and y . bnot Bitwise.bnot(x : int) : int Bitwise not of x . Defined and implemented as bnot(x) = bxor(x, -1) . uband Bitwise.uband(x : int, y : int) : int Bitwise and of non-negative numbers x and y . ubor Bitwise.ubor(x : int, y : int) : int Bitwise or of non-negative x and y . ubxor Bitwise.ubxor(x : int, y : int) : int Bitwise xor of non-negative x and y . BLS12_381 Types fr Built-in (Montgomery) integer representation 32 bytes fp Built-in (Montgomery) integer representation 48 bytes fp2 record fp2 = { x1 : fp, x2 : fp }` g1 record g1 = { x : fp, y : fp, z : fp } g2 record g2 = { x : fp2, y : fp2, z : fp2 } gt record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp } Functions pairing_check BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool Pairing check of a list of points, xs and ys should be of equal length. int_to_fr BLS12_381.int_to_fr(x : int) : fr Convert an integer to an fr - a 32 bytes internal (Montgomery) integer representation. int_to_fp BLS12_381.int_to_fp(x : int) : fp Convert an integer to an fp - a 48 bytes internal (Montgomery) integer representation. fr_to_int BLS12_381.fr_to_int(x : fr) : int Convert a fr value into an integer. fp_to_int BLS12_381.fp_to_int(x : fp) : int Convert a fp value into an integer. mk_g1 BLS12_381.mk_g1(x : int, y : int, z : int) : g1 Construct a g1 point from three integers. mk_g2 BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 Construct a g2 point from six integers. g1_neg BLS12_381.g1_neg(p : g1) : g1 Negate a g1 value. g1_norm BLS12_381.g1_norm(p : g1) : g1 Normalize a g1 value. g1_valid BLS12_381.g1_valid(p : g1) : bool Check that a g1 value is a group member. g1_is_zero BLS12_381.g1_is_zero(p : g1) : bool Check if a g1 value corresponds to the zero value of the group. g1_add BLS12_381.g1_add(p : g1, q : g1) : g1 Add two g1 values. g1_mul BLS12_381.g1_mul(k : fr, p : g1) : g1 Scalar multiplication for g1 . g2_neg BLS12_381.g2_neg(p : g2) : g2 Negate a g2 value. g2_norm BLS12_381.g2_norm(p : g2) : g2 Normalize a g2 value. g2_valid BLS12_381.g2_valid(p : g2) : bool Check that a g2 value is a group member. g2_is_zero BLS12_381.g2_is_zero(p : g2) : bool Check if a g2 value corresponds to the zero value of the group. g2_add BLS12_381.g2_add(p : g2, q : g2) : g2 Add two g2 values. g2_mul BLS12_381.g2_mul(k : fr, p : g2) : g2 Scalar multiplication for g2 . gt_inv BLS12_381.gt_inv(p : gt) : gt Invert a gt value. gt_add BLS12_381.gt_add(p : gt, q : gt) : gt Add two gt values. gt_mul BLS12_381.gt_mul(p : gt, q : gt) : gt Multiply two gt values. gt_pow BLS12_381.gt_pow(p : gt, k : fr) : gt Calculate exponentiation p ^ k . gt_is_one BLS12_381.gt_is_one(p : gt) : bool Compare a gt value to the unit value of the Gt group. pairing BLS12_381.pairing(p : g1, q : g2) : gt Compute the pairing of a g1 value and a g2 value. miller_loop BLS12_381.miller_loop(p : g1, q : g2) : gt Do the Miller loop stage of pairing for g1 and g2 . final_exp BLS12_381.final_exp(p : gt) : gt Perform the final exponentiation step of pairing for a gt value. Func Functional combinators. id Func.id(x : 'a) : 'a Identity function. Returns its argument. const Func.const(x : 'a) : 'b => 'a = (y) => x Constant function constructor. Given x returns a function that returns x regardless of its argument. flip Func.flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c Switches order of arguments of arity 2 function. comp Func.comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c Function composition. comp(f, g)(x) == f(g(x)) . pipe Func.pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c Flipped function composition. pipe(f, g)(x) == g(f(x)) . rapply Func.rapply(x : 'a, f : 'a => 'b) : 'b Reverse application. rapply(x, f) == f(x) . recur Func.recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res The Z combinator. Allows performing local recursion and having anonymous recursive lambdas. To make function A => B recursive the user needs to transform it to take two arguments instead \u2013 one of type A => B which is going to work as a self-reference, and the other one of type A which is the original argument. Therefore, transformed function should have (A => B, A) => B signature. Example usage: let factorial = recur((fac, n) => if(n < 2) 1 else n * fac(n - 1)) If the function is going to take more than one argument it will need to be either tuplified or have curried out latter arguments. Example (factorial with custom step): // tuplified version let factorial_t(n, step) = let fac(rec, args) = let (n, step) = args if(n < 2) 1 else n * rec((n - step, step)) recur(fac)((n, step)) // curried version let factorial_c(n, step) = let fac(rec, n) = (step) => if(n < 2) 1 else n * rec(n - 1)(step) recur(fac)(n)(step) iter Func.iter(n : int, f : 'a => 'a) : 'a => 'a n th composition of f with itself, for instance iter(3, f) is equivalent to (x) => f(f(f(x))) . curry Func.curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) Func.curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) Turns a function that takes n arguments into a curried function that takes one argument and returns a function that waits for the rest in the same manner. For instance curry2((a, b) => a + b)(1)(2) == 3 . uncurry Func.uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c Func.uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd Opposite to curry . tuplify Func.tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c Func.tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd Turns a function that takes n arguments into a function that takes an n-tuple. untuplify Func.untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c Func.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd Opposite to tuplify . Frac This namespace provides operations on rational numbers. A rational number is represented as a fraction of two integers which are stored internally in the frac datatype. The datatype consists of three constructors Neg/2 , Zero/0 and Pos/2 which determine the sign of the number. Both values stored in Neg and Pos need to be strictly positive integers. However, when creating a frac you should never use the constructors explicitly. Instead of that, always use provided functions like make_frac or from_int . This helps keeping the internal representation well defined. The described below functions take care of the normalization of the fractions \u2013 they won't grow if it is unnecessary. Please note that the size of frac can be still very big while the value is actually very close to a natural number \u2013 the division of two extremely big prime numbers will be as big as both of them. To face this issue the optimize function is provided. It will approximate the value of the fraction to fit in the given error margin and to shrink its size as much as possible. Important note: frac must not be compared using standard < -like operators. The operator comparison is not possible to overload at this moment, nor the language provides checkers to prevent unintended usage of them. Therefore the typechecker will allow that and the results of such comparison will be unspecified. You should use lt , geq , eq etc instead. Types frac datatype frac = Pos(int, int) | Zero | Neg(int, int) Internal representation of fractional numbers. First integer encodes the numerator and the second the denominator \u2013 both must be always positive, as the sign is being handled by the choice of the constructor. Functions make_frac Frac.make_frac(n : int, d : int) : frac Creates a fraction out of numerator and denominator. Automatically normalizes, so make_frac(2, 4) and make_frac(1, 2) will yield same results. num Frac.num(f : frac) : int Returns the numerator of a fraction. den Frac.den(f : frac) : int Returns the denominator of a fraction. to_pair Frac.to_pair(f : frac) : int * int Turns a fraction into a pair of numerator and denominator. sign Frac.sign(f : frac) : int Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively. to_str Frac.to_str(f : frac) : string Conversion to string. Does not display division by 1 or denominator if equals zero. simplify Frac.simplify(f : frac) : frac Reduces fraction to normal form if for some reason it is not in it. eq Frac.eq(a : frac, b : frac) : bool Checks if a is equal to b . neq Frac.neq(a : frac, b : frac) : bool Checks if a is not equal to b . geq Frac.geq(a : frac, b : frac) : bool Checks if a is greater or equal to b . leq Frac.leq(a : frac, b : frac) : bool Checks if a is lesser or equal to b . gt Frac.gt(a : frac, b : frac) : bool Checks if a is greater than b . lt Frac.lt(a : frac, b : frac) : bool Checks if a is lesser than b . min Frac.min(a : frac, b : frac) : frac Chooses lesser of the two fractions. max Frac.max(a : frac, b : frac) : frac Chooses greater of the two fractions. abs Frac.abs(f : frac) : frac Absolute value. from_int Frac.from_int(n : int) : frac From integer conversion. Effectively make_frac(n, 1) . floor Frac.floor(f : frac) : int Rounds a fraction to the nearest lesser or equal integer. ceil Frac.ceil(f : frac) : int Rounds a fraction to the nearest greater or equal integer. round_to_zero Frac.round_to_zero(f : frac) : int Rounds a fraction towards zero. Effectively ceil if lesser than zero and floor if greater. round_from_zero Frac.round_from_zero(f : frac) : int Rounds a fraction from zero. Effectively ceil if greater than zero and floor if lesser. round Frac.round(f : frac) : int Rounds a fraction to a nearest integer. If two integers are in the same distance it will choose the even one. add Frac.add(a : frac, b : frac) : frac Sum of the fractions. neg Frac.neg(a : frac) : frac Negation of the fraction. sub Frac.sub(a : frac, b : frac) : frac Subtraction of two fractions. inv Frac.inv(a : frac) : frac Inverts a fraction. Throws error if a is zero. mul Frac.mul(a : frac, b : frac) : frac Multiplication of two fractions. div Frac.div(a : frac, b : frac) : frac Division of two fractions. int_exp Frac.int_exp(b : frac, e : int) : frac Takes b to the power of e . The exponent can be a negative value. optimize Frac.optimize(f : frac, loss : frac) : frac Shrink the internal size of a fraction as much as possible by approximating it to the point where the error would exceed the loss value. is_sane Frac.is_sane(f : frac) : bool For debugging. If it ever returns false in a code that doesn't call frac constructors or accept arbitrary frac s from the surface you should report it as a bug If you expect getting calls with malformed frac s in your contract, you should use this function to verify the input. List This module contains common operations on lists like constructing, querying, traversing etc. is_empty List.is_empty(l : list('a)) : bool Returns true iff the list is equal to [] . first List.first(l : list('a)) : option('a) Returns Some of the first element of a list or None if the list is empty. tail List.tail(l : list('a)) : option(list('a)) Returns Some of a list without its first element or None if the list is empty. last List.last(l : list('a)) : option('a) Returns Some of the last element of a list or None if the list is empty. contains List.contains(e : 'a, l : list('a)) : bool Checks if list l contains element e . Equivalent to List.find(x => x == e, l) != None . find List.find(p : 'a => bool, l : list('a)) : option('a) Finds first element of l fulfilling predicate p as Some or None if no such element exists. find_indices List.find_indices(p : 'a => bool, l : list('a)) : list(int) Returns list of all indices of elements from l that fulfill the predicate p . nth List.nth(n : int, l : list('a)) : option('a) Gets n th element of l as Some or None if l is shorter than n + 1 or n is negative. get List.get(n : int, l : list('a)) : 'a Gets n th element of l forcefully, throwing and error if l is shorter than n + 1 or n is negative. length List.length(l : list('a)) : int Returns length of a list. from_to List.from_to(a : int, b : int) : list(int) Creates an ascending sequence of all integer numbers between a and b (including a and b ). from_to_step List.from_to_step(a : int, b : int, step : int) : list(int) Creates an ascending sequence of integer numbers betweeen a and b jumping by given step . Includes a and takes b only if (b - a) mod step == 0 . step should be bigger than 0. replace_at List.replace_at(n : int, e : 'a, l : list('a)) : list('a) Replaces n th element of l with e . Throws an error if n is negative or would cause an overflow. insert_at List.insert_at(n : int, e : 'a, l : list('a)) : list('a) Inserts e into l to be on position n by shifting following elements further. For instance, insert_at(2, 9, [1,2,3,4]) will yield [1,2,9,3,4] . insert_by List.insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) Assuming that cmp represents < comparison, inserts x before the first element in the list l which is greater than it. For instance, insert_by((a, b) => a < b, 4, [1,2,3,5,6,7]) will yield [1,2,3,4,5,6,7] foldr List.foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b Right fold of a list. Assuming l = [x, y, z] will return f(x, f(y, f(z, nil))) . Not tail recursive. foldl List.foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b Left fold of a list. Assuming l = [x, y, z] will return f(f(f(acc, x), y), z) . Tail recursive. foreach List.foreach(l : list('a), f : 'a => unit) : unit Evaluates f on each element of a list. reverse List.reverse(l : list('a)) : list('a) Returns a copy of l with reversed order of elements. map List.map(f : 'a => 'b, l : list('a)) : list('b) Maps function f over a list. For instance map((x) => x == 0, [1, 2, 0, 3, 0]) will yield [false, false, true, false, true] flat_map List.flat_map(f : 'a => list('b), l : list('a)) : list('b) Maps f over a list and then flattens it. For instance flat_map((x) => [x, x * 10], [1, 2, 3]) will yield [1, 10, 2, 20, 3, 30] filter List.filter(p : 'a => bool, l : list('a)) : list('a) Filters out elements of l that fulfill predicate p . For instance filter((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3]) will yield [1, 1, 2] take List.take(n : int, l : list('a)) : list('a) Takes n first elements of l . Fails if n is negative. If n is greater than length of a list it will return whole list. drop List.drop(n : int, l : list('a)) : list('a) Removes n first elements of l . Fails if n is negative. If n is greater than length of a list it will return [] . take_while List.take_while(p : 'a => bool, l : list('a)) : list('a) Returns longest prefix of l in which all elements fulfill p . drop_while List.drop_while(p : 'a => bool, l : list('a)) : list('a) Removes longest prefix from l in which all elements fulfill p . partition List.partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) Separates elements of l that fulfill p and these that do not. Elements fulfilling predicate will be in the right list. For instance partition((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3]) will yield ([1, 1, 2], [-1, -2, 0, -3]) flatten List.flatten(ll : list(list('a))) : list('a) Flattens a list of lists into a one list. all List.all(p : 'a => bool, l : list('a)) : bool Checks if all elements of a list fulfill predicate p . any List.any(p : 'a => bool, l : list('a)) : bool Checks if any element of a list fulfills predicate p . sum List.sum(l : list(int)) : int Sums elements of a list. Returns 0 if the list is empty. product List.product(l : list(int)) : int Multiplies elements of a list. Returns 1 if the list is empty. zip_with List.zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) \"zips\" two lists with a function. n-th element of resulting list will be equal to f(x1, x2) where x1 and x2 are n-th elements of l1 and l2 respectively. Will cut off the tail of the longer list. For instance zip_with((a, b) => a + b, [1,2], [1,2,3]) will yield [2,4] zip List.zip(l1 : list('a), l2 : list('b)) : list('a * 'b) Special case of zip_with where the zipping function is (a, b) => (a, b) . unzip List.unzip(l : list('a * 'b)) : list('a) * list('b) Opposite to the zip operation. Takes a list of pairs and returns pair of lists with respective elements on same indices. merge List.merge(lesser_cmp : ('a, 'a) => bool, l1 : list('a), l2 : list('a)) : list('a) Merges two sorted lists into a single sorted list. O(length(l1) + length(l2)) sort List.sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) Sorts a list using given comparator. lesser_cmp(x, y) should return true iff x < y . If lesser_cmp is not transitive or there exists an element x such that lesser_cmp(x, x) or there exists a pair of elements x and y such that lesser_cmp(x, y) && lesser_cmp(y, x) then the result is undefined. O(length(l) * log_2(length(l))). intersperse List.intersperse(delim : 'a, l : list('a)) : list('a) Intersperses elements of l with delim . Does nothing on empty lists and singletons. For instance intersperse(0, [1, 2, 3, 4]) will yield [1, 0, 2, 0, 3, 0, 4] enumerate List.enumerate(l : list('a)) : list(int * 'a) Equivalent to zip with [0..length(l)] , but slightly faster. Option Common operations on option types and lists of option s. is_none Option.is_none(o : option('a)) : bool Returns true iff o == None is_some Option.is_some(o : option('a)) : bool Returns true iff o is not None . match Option.match(n : 'b, s : 'a => 'b, o : option('a)) : 'b Behaves like pattern matching on option using two case functions. default Option.default(def : 'a, o : option('a)) : 'a Escapes option wrapping by providing default value for None . force Option.force(o : option('a)) : 'a Forcefully escapes the option wrapping assuming it is Some . Aborts on None . force_msg Option.force_msg(o : option('a), err : string) : 'a Forcefully escapes the option wrapping assuming it is Some . Aborts with err error message on None . contains Option.contains(e : 'a, o : option('a)) : bool Returns true if and only if o contains element equal to e . Equivalent to Option.match(false, x => x == e, o) . on_elem Option.on_elem(o : option('a), f : 'a => unit) : unit Evaluates f on element under Some . Does nothing on None . map Option.map(f : 'a => 'b, o : option('a)) : option('b) Maps element under Some . Leaves None unchanged. map2 Option.map2(f : ('a, 'b) => 'c, o1 : option('a), o2 : option('b)) : option('c) Applies arity 2 function over two option s' elements. Returns Some iff both of o1 and o2 were Some , or None otherwise. For instance map2((a, b) => a + b, Some(1), Some(2)) will yield Some(3) and map2((a, b) => a + b, Some(1), None) will yield None . map3 Option.map3(f : ('a, 'b, 'c) => 'd, o1 : option('a), o2 : option('b), o3 : option('c)) : option('d) Same as map2 but with arity 3 function. app_over Option.app_over(f : option ('a => 'b), o : option('a)) : option('b) Applies function under option over argument under option . If either of them is None the result will be None as well. For instance app_over(Some((x) => x + 1), Some(1)) will yield Some(2) and app_over(Some((x) => x + 1), None) will yield None . flat_map Option.flat_map(f : 'a => option('b), o : option('a)) : option('b) Performs monadic bind on an option . Extracts element from o (if present) and forms new option from it. For instance flat_map((x) => Some(x + 1), Some(1)) will yield Some(2) and flat_map((x) => Some(x + 1), None) will yield None . to_list Option.to_list(o : option('a)) : list('a) Turns o into an empty (if None ) or singleton (if Some ) list. filter_options Option.filter_options(l : list(option('a))) : list('a) Removes None s from list and unpacks all remaining Some s. For instance filter_options([Some(1), None, Some(2)]) will yield [1, 2] . seq_options Option.seq_options(l : list (option('a))) : option (list('a)) Tries to unpack all elements of a list from Some s. Returns None if at least element of l is None . For instance seq_options([Some(1), Some(2)]) will yield Some([1, 2]) , but seq_options([Some(1), Some(2), None]) will yield None . choose Option.choose(o1 : option('a), o2 : option('a)) : option('a) Out of two option s choose the one that is Some , or None if both are None s. choose_first Option.choose_first(l : list(option('a))) : option('a) Same as choose , but chooses from a list insted of two arguments. Pair Common operations on 2-tuples. fst Pair.fst(t : ('a * 'b)) : 'a First element projection. snd Pair.snd(t : ('a * 'b)) : 'b Second element projection. map1 Pair.map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) Applies function over first element. map2 Pair.map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) Applies function over second element. bimap Pair.bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) Applies functions over respective elements. swap Pair.swap(t : ('a * 'b)) : ('b * 'a) Swaps elements. Set Types record set('a) = { to_map : map('a, unit) } Functions new Set.new() : set('a) Returns an empty set member member(e : 'a, s : set('a)) : bool Checks if the element e is present in the set s insert insert(e : 'a, s : set('a)) : set('a) Inserts the element e in the set s delete Set.delete(e : 'a, s : set('a)) : set('a) Removes the element e from the set s size size(s : set('a)) : int Returns the number of elements in the set s to_list Set.to_list(s : set('a)) : list('a) Returns a list containing the elements of the set s from_list Set.from_list(l : list('a)) : set('a) Turns the list l into a set filter Set.filter(p : 'a => bool, s : set('a)) : set('a) Filters out elements of s that fulfill predicate p fold Set.fold(f : ('a, 'b) => 'b, acc : 'b, s : set('a)) : 'b Folds the function f over every element in the set s and returns the final value of the accumulator acc . subtract Set.subtract(s1 : set('a), s2 : set('a)) : set('a) Returns the elements of s1 that are not members of s2 intersection Set.intersection(s1 : set('a), s2 : set('a)) : set('a) Returns the intersection of the two sets s1 and s2 intersection_list Set.intersection_list(sets : list(set('a))) : set('a) Returns the intersection of all the sets in the given list union Set.union(s1 : set('a), s2 : set('a)) : set('a) Returns the union of the two sets s1 and s2 union_list Set.union_list(sets : list(set('a))) : set('a) Returns the union of all the sets in the given list String Operations on the string type. A string is a UTF-8 encoded byte array. length length(s : string) : int The length of a string. Note: not equivalent to byte size of the string, rather List.length(String.to_list(s)) concat concat(s1 : string, s2 : string) : string Concatenates s1 and s2 . concats concats(ss : list(string)) : string Concatenates a list of strings. to_list to_list(s : string) : list(char) Converts a string to a list of char - the code points are normalized, but composite characters are possibly converted to multiple char s. For example the string \"\ud83d\ude1ci\u0307\" is converted to [128540,105,775] - where the smiley is the first code point and the strangely dotted i becomes [105, 775] . from_list from_list(cs : list(char)) : string Converts a list of characters into a normalized UTF-8 string. to_lower to_lower(s : string) : string Converts a string to lowercase. to_upper to_upper(s : string) : string Converts a string to uppercase. at at(ix : int, s : string) : option(char) Returns the character/codepoint at (zero-based) index ix . Basically the equivalent to List.nth(ix, String.to_list(s)) . split split(ix : int, s:string) : string * string Splits a string at (zero-based) index ix . contains contains(str : string, pat : string) : option(int) Searches for pat in str , returning Some(ix) if pat is a substring of str starting at position ix , otherwise returns None . tokens tokens(str : string, pat : string) : list(string) Splits str into tokens, pat is the divider of tokens. to_int to_int(s : string) : option(int) Converts a decimal (\"123\", \"-253\") or a hexadecimal (\"0xa2f\", \"-0xBBB\") string into an integer. If the string doesn't contain a valid number None is returned. sha3 sha3(s : string) : hash Computes the SHA3/Keccak hash of the string. sha256 sha256(s : string) : hash Computes the SHA256 hash of the string. blake2b blake2b(s : string) : hash Computes the Blake2B hash of the string. Triple fst Triple.fst(t : ('a * 'b * 'c)) : 'a First element projection. snd Triple.snd(t : ('a * 'b * 'c)) : 'b Second element projection. thd Triple.thd(t : ('a * 'b * 'c)) : 'c Third element projection. map1 Triple.map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) Applies function over first element. map2 Triple.map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) Applies function over second element. map3 Triple.map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) Applies function over third element. trimap Triple.trimap(f : 'a => 'x, g : 'b => 'y, h : 'c => 'z, t : ('a * 'b * 'c)) : ('x * 'y * 'z) Applies functions over respective elements. swap Triple.swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) Swaps first and third element. rotr Triple.rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) Cyclic rotation of the elements to the right. rotl Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) Cyclic rotation of the elements to the left.","title":"Standard library"},{"location":"sophia_stdlib/#standard-library","text":"Sophia language offers standard library that consists of several namespaces. Some of them are already in the scope and do not need any actions to be used, while the others require some files to be included. The out-of-the-box namespaces are: Address AENS Auth Bits Bytes Call Chain Char Contract Crypto Int Map Oracle The following ones need to be included as regular files with .aes suffix, for example include \"List.aes\" Bitwise BLS12_381 Func Frac List Option Pair Set String Triple","title":"Standard library"},{"location":"sophia_stdlib/#builtin-namespaces","text":"They are available without any explicit includes.","title":"Builtin namespaces"},{"location":"sophia_stdlib/#address","text":"","title":"Address"},{"location":"sophia_stdlib/#aens","text":"The following functionality is available for interacting with the \u00e6ternity naming system (AENS). If owner is equal to Contract.address the signature signature is ignored, and can be left out since it is a named argument. Otherwise we need a signature to prove that we are allowed to do AENS operations on behalf of owner . The signature is tied to a network id , i.e. the signature material should be prefixed by the network id.","title":"AENS"},{"location":"sophia_stdlib/#auth","text":"","title":"Auth"},{"location":"sophia_stdlib/#bits","text":"","title":"Bits"},{"location":"sophia_stdlib/#bytes","text":"","title":"Bytes"},{"location":"sophia_stdlib/#call","text":"Values related to the call to the current contract","title":"Call"},{"location":"sophia_stdlib/#chain","text":"Values and functions related to the chain itself and other entities that live on it.","title":"Chain"},{"location":"sophia_stdlib/#char","text":"","title":"Char"},{"location":"sophia_stdlib/#contract","text":"Values related to the current contract","title":"Contract"},{"location":"sophia_stdlib/#crypto","text":"","title":"Crypto"},{"location":"sophia_stdlib/#int","text":"","title":"Int"},{"location":"sophia_stdlib/#map","text":"","title":"Map"},{"location":"sophia_stdlib/#oracle","text":"","title":"Oracle"},{"location":"sophia_stdlib/#includable-namespaces","text":"These need to be explicitly included (with .aes suffix)","title":"Includable namespaces"},{"location":"sophia_stdlib/#bitwise","text":"Bitwise operations on arbitrary precision integers.","title":"Bitwise"},{"location":"sophia_stdlib/#bls12_381","text":"","title":"BLS12\u000295\u0003381"},{"location":"sophia_stdlib/#func","text":"Functional combinators.","title":"Func"},{"location":"sophia_stdlib/#frac","text":"This namespace provides operations on rational numbers. A rational number is represented as a fraction of two integers which are stored internally in the frac datatype. The datatype consists of three constructors Neg/2 , Zero/0 and Pos/2 which determine the sign of the number. Both values stored in Neg and Pos need to be strictly positive integers. However, when creating a frac you should never use the constructors explicitly. Instead of that, always use provided functions like make_frac or from_int . This helps keeping the internal representation well defined. The described below functions take care of the normalization of the fractions \u2013 they won't grow if it is unnecessary. Please note that the size of frac can be still very big while the value is actually very close to a natural number \u2013 the division of two extremely big prime numbers will be as big as both of them. To face this issue the optimize function is provided. It will approximate the value of the fraction to fit in the given error margin and to shrink its size as much as possible. Important note: frac must not be compared using standard < -like operators. The operator comparison is not possible to overload at this moment, nor the language provides checkers to prevent unintended usage of them. Therefore the typechecker will allow that and the results of such comparison will be unspecified. You should use lt , geq , eq etc instead.","title":"Frac"},{"location":"sophia_stdlib/#list","text":"This module contains common operations on lists like constructing, querying, traversing etc.","title":"List"},{"location":"sophia_stdlib/#option","text":"Common operations on option types and lists of option s.","title":"Option"},{"location":"sophia_stdlib/#pair","text":"Common operations on 2-tuples.","title":"Pair"},{"location":"sophia_stdlib/#set_1","text":"","title":"Set"},{"location":"sophia_stdlib/#string","text":"Operations on the string type. A string is a UTF-8 encoded byte array.","title":"String"},{"location":"sophia_stdlib/#triple","text":"","title":"Triple"},{"location":"sophia_syntax/","text":"Syntax Lexical syntax Comments Single line comments start with // and block comments are enclosed in /* and */ and can be nested. Keywords contract include let switch type record datatype if elif else function stateful payable true false mod public entrypoint private indexed namespace interface main using as for hiding Tokens Id = [a-z_][A-Za-z0-9_']* identifiers start with a lower case letter. Con = [A-Z][A-Za-z0-9_']* constructors start with an upper case letter. QId = (Con\\.)+Id qualified identifiers (e.g. Map.member ) QCon = (Con\\.)+Con qualified constructor TVar = 'Id type variable (e.g 'a , 'b ) Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* integer literal with optional _ separators Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* byte array literal with optional _ separators String string literal enclosed in \" with escape character \\ Char character literal enclosed in ' with escape character \\ AccountAddress base58-encoded 32 byte account pubkey with ak_ prefix ContractAddress base58-encoded 32 byte contract address with ct_ prefix OracleAddress base58-encoded 32 byte oracle address with ok_ prefix OracleQueryId base58-encoded 32 byte oracle query id with oq_ prefix Valid string escape codes are Escape ASCII \\b 8 \\t 9 \\n 10 \\v 11 \\f 12 \\r 13 \\e 27 \\xHexDigits HexDigits See the identifier encoding scheme for the details on the base58 literals. Layout blocks Sophia uses Python-style layout rules to group declarations and statements. A layout block with more than one element must start on a separate line and be indented more than the currently enclosing layout block. Blocks with a single element can be written on the same line as the previous token. Each element of the block must share the same indentation and no part of an element may be indented less than the indentation of the block. For instance contract Layout = function foo () = 0 // no layout function bar () = // layout block starts on next line let x = foo () // indented more than 2 spaces x + 1 // the '+' is indented more than the 'x' Notation In describing the syntax below, we use the following conventions: Upper-case identifiers denote non-terminals (like Expr ) or terminals with some associated value (like Id ). Keywords and symbols are enclosed in single quotes: 'let' or '=' . Choices are separated by vertical bars: | . Optional elements are enclosed in [ square brackets ] . ( Parentheses ) are used for grouping. Zero or more repetitions are denoted by a postfix * , and one or more repetitions by a + . Block(X) denotes a layout block of X s. Sep(X, S) is short for [X (S X)*] , i.e. a possibly empty sequence of X s separated by S s. Sep1(X, S) is short for X (S X)* , i.e. same as Sep , but must not be empty. Declarations A Sophia file consists of a sequence of declarations in a layout block. File ::= Block ( TopDecl ) TopDecl ::= [ ' payable ' ] [ ' main ' ] ' contract ' Con [ Implement ] '=' Block ( Decl ) | ' contract ' ' interface ' Con [ Implement ] '=' Block ( Decl ) | ' namespace ' Con '=' Block ( Decl ) | '@ compiler ' PragmaOp Version | ' include ' String | Using Implement ::= ':' Sep1 ( Con , ',' ) Decl ::= ' type ' Id [ '(' TVar * ')' ] '=' TypeAlias | ' record ' Id [ '(' TVar * ')' ] '=' RecordType | ' datatype ' Id [ '(' TVar * ')' ] '=' DataType | ( EModifier * ' entrypoint ' | FModifier * ' function ' ) Block ( FunDecl ) | Using FunDecl ::= Id ':' Type // Type signature | Id Args [ ':' Type ] '=' Block ( Stmt ) // Definition | Id Args [ ':' Type ] Block ( GuardedDef ) // Guarded definitions GuardedDef ::= '|' Sep1 ( Expr , ',' ) '=' Block ( Stmt ) Using ::= ' using ' Con [ ' as ' Con ] [ UsingParts ] UsingParts ::= ' for ' ' [ ' Sep1 ( Id , ',' ) ']' | ' hiding ' ' [ ' Sep1 ( Id , ',' ) ']' PragmaOp ::= '<' | ' =< ' | ' == ' | ' >= ' | '>' Version ::= Sep1 ( Int , '.' ) EModifier ::= ' payable ' | ' stateful ' FModifier ::= ' stateful ' | ' private ' Args ::= '(' Sep ( Pattern , ',' ) ')' Contract declarations must appear at the top-level. For example, contract Test = type t = int entrypoint add ( x : t , y : t ) = x + y There are three forms of type declarations: type aliases (declared with the type keyword), record type definitions ( record ) and data type definitions ( datatype ): TypeAlias ::= Type RecordType ::= '{' Sep ( FieldType , ',' ) '}' DataType ::= Sep1 ( ConDecl , '|' ) FieldType ::= Id ':' Type ConDecl ::= Con [ '(' Sep1 ( Type , ',' ) ')' ] For example, record point ( 'a ) = { x : 'a , y : 'a } datatype shape ( 'a ) = Circle ( point ( 'a ), 'a ) | Rect ( point ( 'a ), point ( 'a )) type int_shape = shape ( int ) Types Type ::= Domain ' => ' Type // Function type | Type '(' Sep ( Type , ',' ) ')' // Type application | '(' Type ')' // Parens | ' unit ' | Sep ( Type , '*' ) // Tuples | Id | QId | TVar Domain ::= Type // Single argument | '(' Sep ( Type , ',' ) ')' // Multiple arguments The function type arrow associates to the right. Example, 'a => list ( 'a ) => ( int * list ( 'a )) Statements Function bodies are blocks of statements , where a statement is one of the following Stmt ::= ' switch ' ' ( ' Expr ')' Block ( Case ) | ' if ' ' ( ' Expr ')' Block ( Stmt ) | ' elif ' ' ( ' Expr ')' Block ( Stmt ) | ' else ' Block ( Stmt ) | ' let ' LetDef | Using | Expr LetDef ::= Id Args [ ':' Type ] '=' Block ( Stmt ) // Function definition | Pattern '=' Block ( Stmt ) // Value definition Case ::= Pattern ' => ' Block ( Stmt ) | Pattern Block ( GuardedCase ) GuardedCase ::= '|' Sep1 ( Expr , ',' ) ' => ' Block ( Stmt ) Pattern ::= Expr if statements can be followed by zero or more elif statements and an optional final else statement. For example, let x : int = 4 switch ( f ( x )) None => 0 Some ( y ) => if ( y > 10 ) \"too big\" elif ( y < 3 ) \"too small\" else \"just right\" Expressions Expr ::= '(' LamArgs ')' ' => ' Block ( Stmt ) // Anonymous function (x) => x + 1 | '(' BinOp ')' // Operator lambda (+) | ' if ' ' ( ' Expr ')' Expr ' else ' Expr // If expression if(x < y) y else x | Expr ':' Type // Type annotation 5 : int | Expr BinOp Expr // Binary operator x + y | UnOp Expr // Unary operator ! b | Expr '(' Sep ( Expr , ',' ) ')' // Application f(x, y) | Expr '.' Id // Projection state.x | Expr '[' Expr ']' // Map lookup map[key] | Expr '{' Sep ( FieldUpdate , ',' ) '}' // Record or map update r{ fld[key].x = y } | '[' Sep ( Expr , ',' ) ']' // List [1, 2, 3] | '[' Expr '|' Sep ( Generator , ',' ) ']' // List comprehension [k | x <- [1], if (f(x)), let k = x+1] | '[' Expr ' .. ' Expr ']' // List range [1..n] | '{' Sep ( FieldUpdate , ',' ) '}' // Record or map value {x = 0, y = 1}, {[key] = val} | '(' Expr ')' // Parens (1 + 2) * 3 | '(' Expr '=' Expr ')' // Assign pattern (y = x::_) | Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, \"foo\", '%' | AccountAddress | ContractAddress // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers Generator ::= Pattern ' <- ' Expr // Generator | ' if ' ' ( ' Expr ')' // Guard | LetDef // Definition LamArgs ::= '(' Sep ( LamArg , ',' ) ')' LamArg ::= Id [ ':' Type ] FieldUpdate ::= Path '=' Expr Path ::= Id // Record field | '[' Expr ']' // Map key | Path '.' Id // Nested record field | Path '[' Expr ']' // Nested map key BinOp ::= ' || ' | ' && ' | '<' | '>' | ' =< ' | ' >= ' | ' == ' | ' != ' | ' :: ' | ' ++ ' | '+' | '-' | '*' | '/' | ' mod ' | '^' | ' |> ' UnOp ::= '-' | '!' Operators types Operators Type - + * / mod ^ arithmetic operators ! && || logical operators == != < > =< >= comparison operators :: ++ list operators |> functional operators Operator precedence In order of highest to lowest precedence. Operators Associativity ! right ^ left * / mod left - (unary) right + - left :: ++ right < > =< >= == != none && right || right |> left","title":"Syntax"},{"location":"sophia_syntax/#syntax","text":"","title":"Syntax"},{"location":"sophia_syntax/#lexical-syntax","text":"","title":"Lexical syntax"},{"location":"sophia_syntax/#comments","text":"Single line comments start with // and block comments are enclosed in /* and */ and can be nested.","title":"Comments"},{"location":"sophia_syntax/#keywords","text":"contract include let switch type record datatype if elif else function stateful payable true false mod public entrypoint private indexed namespace interface main using as for hiding","title":"Keywords"},{"location":"sophia_syntax/#tokens","text":"Id = [a-z_][A-Za-z0-9_']* identifiers start with a lower case letter. Con = [A-Z][A-Za-z0-9_']* constructors start with an upper case letter. QId = (Con\\.)+Id qualified identifiers (e.g. Map.member ) QCon = (Con\\.)+Con qualified constructor TVar = 'Id type variable (e.g 'a , 'b ) Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* integer literal with optional _ separators Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)* byte array literal with optional _ separators String string literal enclosed in \" with escape character \\ Char character literal enclosed in ' with escape character \\ AccountAddress base58-encoded 32 byte account pubkey with ak_ prefix ContractAddress base58-encoded 32 byte contract address with ct_ prefix OracleAddress base58-encoded 32 byte oracle address with ok_ prefix OracleQueryId base58-encoded 32 byte oracle query id with oq_ prefix Valid string escape codes are Escape ASCII \\b 8 \\t 9 \\n 10 \\v 11 \\f 12 \\r 13 \\e 27 \\xHexDigits HexDigits See the identifier encoding scheme for the details on the base58 literals.","title":"Tokens"},{"location":"sophia_syntax/#layout-blocks","text":"Sophia uses Python-style layout rules to group declarations and statements. A layout block with more than one element must start on a separate line and be indented more than the currently enclosing layout block. Blocks with a single element can be written on the same line as the previous token. Each element of the block must share the same indentation and no part of an element may be indented less than the indentation of the block. For instance contract Layout = function foo () = 0 // no layout function bar () = // layout block starts on next line let x = foo () // indented more than 2 spaces x + 1 // the '+' is indented more than the 'x'","title":"Layout blocks"},{"location":"sophia_syntax/#notation","text":"In describing the syntax below, we use the following conventions: Upper-case identifiers denote non-terminals (like Expr ) or terminals with some associated value (like Id ). Keywords and symbols are enclosed in single quotes: 'let' or '=' . Choices are separated by vertical bars: | . Optional elements are enclosed in [ square brackets ] . ( Parentheses ) are used for grouping. Zero or more repetitions are denoted by a postfix * , and one or more repetitions by a + . Block(X) denotes a layout block of X s. Sep(X, S) is short for [X (S X)*] , i.e. a possibly empty sequence of X s separated by S s. Sep1(X, S) is short for X (S X)* , i.e. same as Sep , but must not be empty.","title":"Notation"},{"location":"sophia_syntax/#declarations","text":"A Sophia file consists of a sequence of declarations in a layout block. File ::= Block ( TopDecl ) TopDecl ::= [ ' payable ' ] [ ' main ' ] ' contract ' Con [ Implement ] '=' Block ( Decl ) | ' contract ' ' interface ' Con [ Implement ] '=' Block ( Decl ) | ' namespace ' Con '=' Block ( Decl ) | '@ compiler ' PragmaOp Version | ' include ' String | Using Implement ::= ':' Sep1 ( Con , ',' ) Decl ::= ' type ' Id [ '(' TVar * ')' ] '=' TypeAlias | ' record ' Id [ '(' TVar * ')' ] '=' RecordType | ' datatype ' Id [ '(' TVar * ')' ] '=' DataType | ( EModifier * ' entrypoint ' | FModifier * ' function ' ) Block ( FunDecl ) | Using FunDecl ::= Id ':' Type // Type signature | Id Args [ ':' Type ] '=' Block ( Stmt ) // Definition | Id Args [ ':' Type ] Block ( GuardedDef ) // Guarded definitions GuardedDef ::= '|' Sep1 ( Expr , ',' ) '=' Block ( Stmt ) Using ::= ' using ' Con [ ' as ' Con ] [ UsingParts ] UsingParts ::= ' for ' ' [ ' Sep1 ( Id , ',' ) ']' | ' hiding ' ' [ ' Sep1 ( Id , ',' ) ']' PragmaOp ::= '<' | ' =< ' | ' == ' | ' >= ' | '>' Version ::= Sep1 ( Int , '.' ) EModifier ::= ' payable ' | ' stateful ' FModifier ::= ' stateful ' | ' private ' Args ::= '(' Sep ( Pattern , ',' ) ')' Contract declarations must appear at the top-level. For example, contract Test = type t = int entrypoint add ( x : t , y : t ) = x + y There are three forms of type declarations: type aliases (declared with the type keyword), record type definitions ( record ) and data type definitions ( datatype ): TypeAlias ::= Type RecordType ::= '{' Sep ( FieldType , ',' ) '}' DataType ::= Sep1 ( ConDecl , '|' ) FieldType ::= Id ':' Type ConDecl ::= Con [ '(' Sep1 ( Type , ',' ) ')' ] For example, record point ( 'a ) = { x : 'a , y : 'a } datatype shape ( 'a ) = Circle ( point ( 'a ), 'a ) | Rect ( point ( 'a ), point ( 'a )) type int_shape = shape ( int )","title":"Declarations"},{"location":"sophia_syntax/#types","text":"Type ::= Domain ' => ' Type // Function type | Type '(' Sep ( Type , ',' ) ')' // Type application | '(' Type ')' // Parens | ' unit ' | Sep ( Type , '*' ) // Tuples | Id | QId | TVar Domain ::= Type // Single argument | '(' Sep ( Type , ',' ) ')' // Multiple arguments The function type arrow associates to the right. Example, 'a => list ( 'a ) => ( int * list ( 'a ))","title":"Types"},{"location":"sophia_syntax/#statements","text":"Function bodies are blocks of statements , where a statement is one of the following Stmt ::= ' switch ' ' ( ' Expr ')' Block ( Case ) | ' if ' ' ( ' Expr ')' Block ( Stmt ) | ' elif ' ' ( ' Expr ')' Block ( Stmt ) | ' else ' Block ( Stmt ) | ' let ' LetDef | Using | Expr LetDef ::= Id Args [ ':' Type ] '=' Block ( Stmt ) // Function definition | Pattern '=' Block ( Stmt ) // Value definition Case ::= Pattern ' => ' Block ( Stmt ) | Pattern Block ( GuardedCase ) GuardedCase ::= '|' Sep1 ( Expr , ',' ) ' => ' Block ( Stmt ) Pattern ::= Expr if statements can be followed by zero or more elif statements and an optional final else statement. For example, let x : int = 4 switch ( f ( x )) None => 0 Some ( y ) => if ( y > 10 ) \"too big\" elif ( y < 3 ) \"too small\" else \"just right\"","title":"Statements"},{"location":"sophia_syntax/#expressions","text":"Expr ::= '(' LamArgs ')' ' => ' Block ( Stmt ) // Anonymous function (x) => x + 1 | '(' BinOp ')' // Operator lambda (+) | ' if ' ' ( ' Expr ')' Expr ' else ' Expr // If expression if(x < y) y else x | Expr ':' Type // Type annotation 5 : int | Expr BinOp Expr // Binary operator x + y | UnOp Expr // Unary operator ! b | Expr '(' Sep ( Expr , ',' ) ')' // Application f(x, y) | Expr '.' Id // Projection state.x | Expr '[' Expr ']' // Map lookup map[key] | Expr '{' Sep ( FieldUpdate , ',' ) '}' // Record or map update r{ fld[key].x = y } | '[' Sep ( Expr , ',' ) ']' // List [1, 2, 3] | '[' Expr '|' Sep ( Generator , ',' ) ']' // List comprehension [k | x <- [1], if (f(x)), let k = x+1] | '[' Expr ' .. ' Expr ']' // List range [1..n] | '{' Sep ( FieldUpdate , ',' ) '}' // Record or map value {x = 0, y = 1}, {[key] = val} | '(' Expr ')' // Parens (1 + 2) * 3 | '(' Expr '=' Expr ')' // Assign pattern (y = x::_) | Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, \"foo\", '%' | AccountAddress | ContractAddress // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers Generator ::= Pattern ' <- ' Expr // Generator | ' if ' ' ( ' Expr ')' // Guard | LetDef // Definition LamArgs ::= '(' Sep ( LamArg , ',' ) ')' LamArg ::= Id [ ':' Type ] FieldUpdate ::= Path '=' Expr Path ::= Id // Record field | '[' Expr ']' // Map key | Path '.' Id // Nested record field | Path '[' Expr ']' // Nested map key BinOp ::= ' || ' | ' && ' | '<' | '>' | ' =< ' | ' >= ' | ' == ' | ' != ' | ' :: ' | ' ++ ' | '+' | '-' | '*' | '/' | ' mod ' | '^' | ' |> ' UnOp ::= '-' | '!'","title":"Expressions"},{"location":"sophia_syntax/#operators-types","text":"Operators Type - + * / mod ^ arithmetic operators ! && || logical operators == != < > =< >= comparison operators :: ++ list operators |> functional operators","title":"Operators types"},{"location":"sophia_syntax/#operator-precedence","text":"In order of highest to lowest precedence. Operators Associativity ! right ^ left * / mod left - (unary) right + - left :: ++ right < > =< >= == != none && right || right |> left","title":"Operator precedence"}]} \ No newline at end of file diff --git a/master/sitemap.xml b/master/sitemap.xml index 2a76fa3..cf6ac8a 100644 --- a/master/sitemap.xml +++ b/master/sitemap.xml @@ -2,47 +2,47 @@ None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily None - 2022-11-23 + 2022-12-07 daily \ No newline at end of file diff --git a/master/sitemap.xml.gz b/master/sitemap.xml.gz index 34459c93a30cd95482b8dc203563f133f1e619dc..279ec20e486735586005cb2c5e52a622b1a1e7b4 100644 GIT binary patch literal 202 zcmV;*05$&~iwFp-q>y6*|8r?{Wo=<_E_iKh0PU1L4#OY}h4-EUu?GlCe?-XEu~T~h zL}-FUAdP9$-oE5VRnAbwi=UsL{B0KHyLISgda%J4I42Av<8`R4uM2#;p0fikL(-AB-sGZX0#xxqL12LEsP)Y6NRUpycdveE=Yz4RxhXE2@3ym|97-)?yI?ksxg0LHr#7bM3>JLPNRniAix=j?zhvE$ER zK}VFr4ocj{*qIx|2N+9~ug(O#{%yq&Ycau|uiG1GAcnTd%~$JI(2+)(Z)- z^R-|(V_8vTY@Z^Fw~DD&x<=oFeu!Ec`{8uu%tZP_ZtxAh!T%dRwe+II7uqr&9?}E= E06~^y0RR91 diff --git a/master/sophia_stdlib/index.html b/master/sophia_stdlib/index.html index 06a6c6d..408865b 100644 --- a/master/sophia_stdlib/index.html +++ b/master/sophia_stdlib/index.html @@ -1062,7 +1062,7 @@ the current generation.

timestamp
Chain.timestamp : int
 
-

The timestamp of the current block.

+

The timestamp of the current block (unix time, milliseconds).

difficulty
Chain.difficulty : int