From 19f9f3338c22f9c3337f5606259c5522b8a428fe Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 5 Sep 2023 08:08:26 +0000 Subject: [PATCH] Deployed 33229c3 to v7.4.0 with MkDocs 1.4.2 and mike 1.1.2 --- latest/404.html | 6 +- latest/CHANGELOG/index.html | 6 +- latest/aeso_aci/index.html | 6 +- latest/aeso_compiler/index.html | 6 +- latest/index.html | 6 +- latest/sophia/index.html | 6 +- latest/sophia_examples/index.html | 6 +- latest/sophia_features/index.html | 6 +- latest/sophia_stdlib/index.html | 6 +- latest/sophia_syntax/index.html | 6 +- v7.4.0/404.html | 402 + v7.4.0/CHANGELOG/index.html | 2260 ++++++ v7.4.0/aeso_aci/index.html | 596 ++ v7.4.0/aeso_compiler/index.html | 532 ++ v7.4.0/assets/images/favicon.png | Bin 0 -> 1870 bytes .../assets/javascripts/bundle.a00a7c5e.min.js | 29 + .../javascripts/bundle.a00a7c5e.min.js.map | 8 + .../javascripts/lunr/min/lunr.ar.min.js | 1 + .../javascripts/lunr/min/lunr.da.min.js | 18 + .../javascripts/lunr/min/lunr.de.min.js | 18 + .../javascripts/lunr/min/lunr.du.min.js | 18 + .../javascripts/lunr/min/lunr.es.min.js | 18 + .../javascripts/lunr/min/lunr.fi.min.js | 18 + .../javascripts/lunr/min/lunr.fr.min.js | 18 + .../javascripts/lunr/min/lunr.hi.min.js | 1 + .../javascripts/lunr/min/lunr.hu.min.js | 18 + .../javascripts/lunr/min/lunr.it.min.js | 18 + .../javascripts/lunr/min/lunr.ja.min.js | 1 + .../javascripts/lunr/min/lunr.jp.min.js | 1 + .../javascripts/lunr/min/lunr.ko.min.js | 1 + .../javascripts/lunr/min/lunr.multi.min.js | 1 + .../javascripts/lunr/min/lunr.nl.min.js | 18 + .../javascripts/lunr/min/lunr.no.min.js | 18 + .../javascripts/lunr/min/lunr.pt.min.js | 18 + .../javascripts/lunr/min/lunr.ro.min.js | 18 + .../javascripts/lunr/min/lunr.ru.min.js | 18 + .../lunr/min/lunr.stemmer.support.min.js | 1 + .../javascripts/lunr/min/lunr.sv.min.js | 18 + .../javascripts/lunr/min/lunr.ta.min.js | 1 + .../javascripts/lunr/min/lunr.th.min.js | 1 + .../javascripts/lunr/min/lunr.tr.min.js | 18 + .../javascripts/lunr/min/lunr.vi.min.js | 1 + .../javascripts/lunr/min/lunr.zh.min.js | 1 + v7.4.0/assets/javascripts/lunr/tinyseg.js | 206 + v7.4.0/assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.db81ec45.min.js | 42 + .../workers/search.db81ec45.min.js.map | 8 + .../assets/stylesheets/main.0d440cfe.min.css | 1 + .../stylesheets/main.0d440cfe.min.css.map | 1 + .../stylesheets/palette.2505c338.min.css | 1 + .../stylesheets/palette.2505c338.min.css.map | 1 + v7.4.0/favicon.png | Bin 0 -> 1045 bytes v7.4.0/index.html | 443 ++ v7.4.0/search/search_index.json | 1 + v7.4.0/sitemap.xml | 48 + v7.4.0/sitemap.xml.gz | Bin 0 -> 203 bytes v7.4.0/sophia/index.html | 411 + v7.4.0/sophia_examples/index.html | 564 ++ v7.4.0/sophia_features/index.html | 2142 ++++++ v7.4.0/sophia_stdlib/index.html | 2258 ++++++ v7.4.0/sophia_syntax/index.html | 983 +++ versions.json | 2 +- 62 files changed, 17957 insertions(+), 31 deletions(-) create mode 100644 v7.4.0/404.html create mode 100644 v7.4.0/CHANGELOG/index.html create mode 100644 v7.4.0/aeso_aci/index.html create mode 100644 v7.4.0/aeso_compiler/index.html create mode 100644 v7.4.0/assets/images/favicon.png create mode 100644 v7.4.0/assets/javascripts/bundle.a00a7c5e.min.js create mode 100644 v7.4.0/assets/javascripts/bundle.a00a7c5e.min.js.map create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 v7.4.0/assets/javascripts/lunr/tinyseg.js create mode 100644 v7.4.0/assets/javascripts/lunr/wordcut.js create mode 100644 v7.4.0/assets/javascripts/workers/search.db81ec45.min.js create mode 100644 v7.4.0/assets/javascripts/workers/search.db81ec45.min.js.map create mode 100644 v7.4.0/assets/stylesheets/main.0d440cfe.min.css create mode 100644 v7.4.0/assets/stylesheets/main.0d440cfe.min.css.map create mode 100644 v7.4.0/assets/stylesheets/palette.2505c338.min.css create mode 100644 v7.4.0/assets/stylesheets/palette.2505c338.min.css.map create mode 100644 v7.4.0/favicon.png create mode 100644 v7.4.0/index.html create mode 100644 v7.4.0/search/search_index.json create mode 100644 v7.4.0/sitemap.xml create mode 100644 v7.4.0/sitemap.xml.gz create mode 100644 v7.4.0/sophia/index.html create mode 100644 v7.4.0/sophia_examples/index.html create mode 100644 v7.4.0/sophia_features/index.html create mode 100644 v7.4.0/sophia_stdlib/index.html create mode 100644 v7.4.0/sophia_syntax/index.html diff --git a/latest/404.html b/latest/404.html index 74036ec..efa086d 100644 --- a/latest/404.html +++ b/latest/404.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../v7.3.0/404.html... + Redirecting to ../v7.4.0/404.html... \ No newline at end of file diff --git a/latest/CHANGELOG/index.html b/latest/CHANGELOG/index.html index ef4a81d..3f2c94e 100644 --- a/latest/CHANGELOG/index.html +++ b/latest/CHANGELOG/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/CHANGELOG/... + Redirecting to ../../v7.4.0/CHANGELOG/... \ No newline at end of file diff --git a/latest/aeso_aci/index.html b/latest/aeso_aci/index.html index 4db51fc..c0ab7ce 100644 --- a/latest/aeso_aci/index.html +++ b/latest/aeso_aci/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/aeso_aci/... + Redirecting to ../../v7.4.0/aeso_aci/... \ No newline at end of file diff --git a/latest/aeso_compiler/index.html b/latest/aeso_compiler/index.html index 9c39e5a..0ebb101 100644 --- a/latest/aeso_compiler/index.html +++ b/latest/aeso_compiler/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/aeso_compiler/... + Redirecting to ../../v7.4.0/aeso_compiler/... \ No newline at end of file diff --git a/latest/index.html b/latest/index.html index 048fb89..b53ea5f 100644 --- a/latest/index.html +++ b/latest/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../v7.3.0/... + Redirecting to ../v7.4.0/... \ No newline at end of file diff --git a/latest/sophia/index.html b/latest/sophia/index.html index 34dc518..0faa176 100644 --- a/latest/sophia/index.html +++ b/latest/sophia/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/sophia/... + Redirecting to ../../v7.4.0/sophia/... \ No newline at end of file diff --git a/latest/sophia_examples/index.html b/latest/sophia_examples/index.html index 63620c1..9298a27 100644 --- a/latest/sophia_examples/index.html +++ b/latest/sophia_examples/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/sophia_examples/... + Redirecting to ../../v7.4.0/sophia_examples/... \ No newline at end of file diff --git a/latest/sophia_features/index.html b/latest/sophia_features/index.html index a4eaaca..0a8e649 100644 --- a/latest/sophia_features/index.html +++ b/latest/sophia_features/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/sophia_features/... + Redirecting to ../../v7.4.0/sophia_features/... \ No newline at end of file diff --git a/latest/sophia_stdlib/index.html b/latest/sophia_stdlib/index.html index 86f07f6..37388ce 100644 --- a/latest/sophia_stdlib/index.html +++ b/latest/sophia_stdlib/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/sophia_stdlib/... + Redirecting to ../../v7.4.0/sophia_stdlib/... \ No newline at end of file diff --git a/latest/sophia_syntax/index.html b/latest/sophia_syntax/index.html index 6d36824..ffae9aa 100644 --- a/latest/sophia_syntax/index.html +++ b/latest/sophia_syntax/index.html @@ -4,13 +4,13 @@ Redirecting - Redirecting to ../../v7.3.0/sophia_syntax/... + Redirecting to ../../v7.4.0/sophia_syntax/... \ No newline at end of file diff --git a/v7.4.0/404.html b/v7.4.0/404.html new file mode 100644 index 0000000..97c50f5 --- /dev/null +++ b/v7.4.0/404.html @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/CHANGELOG/index.html b/v7.4.0/CHANGELOG/index.html new file mode 100644 index 0000000..051652d --- /dev/null +++ b/v7.4.0/CHANGELOG/index.html @@ -0,0 +1,2260 @@ + + + + + + + + + + + + + + + + + + + + Changelog - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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

+

Changed

+

Removed

+

Fixed

+

7.4.0

+

Changed

+
    +
  • Names of lifted lambdas now consist of parent function's name and their + position in the source code.
  • +
+

Fixed

+
    +
  • Lifted lambdas get their names assigned deterministically.
  • +
+

7.3.0

+

Fixed

+
    +
  • Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.
  • +
  • Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.
  • +
+

7.2.1

+

Fixed

+
    +
  • Fixed bugs with the newly added debugging symbols
  • +
+

7.2.0

+

Added

+
    +
  • Toplevel compile-time constants +
    namespace N =
    +  let nc = 1
    +contract C =
    +  let cc = 2
    +
  • +
  • API functions for encoding/decoding Sophia values to/from FATE.
  • +
+

Removed

+
    +
  • Remove the mapping from variables to FATE registers from the compilation output.
  • +
+

Fixed

+
    +
  • Warning about unused include when there is no include.
  • +
+

7.1.0

+

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.
  • +
  • Hole expression.
  • +
+

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.
  • +
  • Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.
  • +
+

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 – 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.<hash_fun> and String.<hash_fun> 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 #<hex> to address as ak_<base58check>, oracle ok_<base58check>, + oracle query oq_<base58check> and remote contract ct_<base58check>.
  • +
  • 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.
  • +
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/aeso_aci/index.html b/v7.4.0/aeso_aci/index.html new file mode 100644 index 0000000..98e447e --- /dev/null +++ b/v7.4.0/aeso_aci/index.html @@ -0,0 +1,596 @@ + + + + + + + + + + + + + + + + + + aeso_aci - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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.

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/aeso_compiler/index.html b/v7.4.0/aeso_compiler/index.html new file mode 100644 index 0000000..d0dfd3a --- /dev/null +++ b/v7.4.0/aeso_compiler/index.html @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + aeso_compiler - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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.

+

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.

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/assets/images/favicon.png b/v7.4.0/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/v7.4.0/assets/javascripts/bundle.a00a7c5e.min.js b/v7.4.0/assets/javascripts/bundle.a00a7c5e.min.js new file mode 100644 index 0000000..88ee663 --- /dev/null +++ b/v7.4.0/assets/javascripts/bundle.a00a7c5e.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function ne(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,ne())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(ne,z){d.append(z,ne)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,ne=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,ne.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},ne=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=ne;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):de,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},ne=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;ne(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(ne,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(ne,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),G())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>he(e)))),N(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),G())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return pe(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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 æternity 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!
  • +
+
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/search/search_index.json b/v7.4.0/search/search_index.json new file mode 100644 index 0000000..c6034d9 --- /dev/null +++ b/v7.4.0/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"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!
"},{"location":"CHANGELOG/","title":"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.

"},{"location":"CHANGELOG/#unreleased","title":"Unreleased","text":""},{"location":"CHANGELOG/#added","title":"Added","text":""},{"location":"CHANGELOG/#changed","title":"Changed","text":""},{"location":"CHANGELOG/#removed","title":"Removed","text":""},{"location":"CHANGELOG/#fixed","title":"Fixed","text":""},{"location":"CHANGELOG/#740","title":"7.4.0","text":""},{"location":"CHANGELOG/#changed_1","title":"Changed","text":"
  • Names of lifted lambdas now consist of parent function's name and their position in the source code.
"},{"location":"CHANGELOG/#fixed_1","title":"Fixed","text":"
  • Lifted lambdas get their names assigned deterministically.
"},{"location":"CHANGELOG/#730","title":"7.3.0","text":""},{"location":"CHANGELOG/#fixed_2","title":"Fixed","text":"
  • Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.
  • Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.
"},{"location":"CHANGELOG/#721","title":"7.2.1","text":""},{"location":"CHANGELOG/#fixed_3","title":"Fixed","text":"
  • Fixed bugs with the newly added debugging symbols
"},{"location":"CHANGELOG/#720","title":"7.2.0","text":""},{"location":"CHANGELOG/#added_1","title":"Added","text":"
  • Toplevel compile-time constants
    namespace N =\n  let nc = 1\ncontract C =\n  let cc = 2\n
  • API functions for encoding/decoding Sophia values to/from FATE.
"},{"location":"CHANGELOG/#removed_1","title":"Removed","text":"
  • Remove the mapping from variables to FATE registers from the compilation output.
"},{"location":"CHANGELOG/#fixed_4","title":"Fixed","text":"
  • Warning about unused include when there is no include.
"},{"location":"CHANGELOG/#710","title":"7.1.0","text":""},{"location":"CHANGELOG/#added_2","title":"Added","text":"
  • Options to enable/disable certain optimizations.
  • The ability to call a different instance of the current contract
    contract Main =\n  entrypoint spend(x : int) : int = x\n  entrypoint f(c : Main) : int = c.spend(10)\n
  • Return a mapping from variables to FATE registers in the compilation output.
  • Hole expression.
"},{"location":"CHANGELOG/#changed_2","title":"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.
  • Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.
"},{"location":"CHANGELOG/#fixed_5","title":"Fixed","text":"
  • Typechecker crashes if Chain.create or Chain.clone are used without arguments.
"},{"location":"CHANGELOG/#701","title":"7.0.1","text":""},{"location":"CHANGELOG/#added_3","title":"Added","text":"
  • Add CONTRIBUTING.md file.
"},{"location":"CHANGELOG/#changed_3","title":"Changed","text":"
  • Update Sophia syntax docs to include missing information about existing syntax.
"},{"location":"CHANGELOG/#fixed_6","title":"Fixed","text":"
  • 404 Contract polymorphism crashes on non-obvious child contract typing.
"},{"location":"CHANGELOG/#700","title":"7.0.0","text":""},{"location":"CHANGELOG/#added_4","title":"Added","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]))\n
  • Allow binary operators to be used as lambdas
    function sum(l : list(int)) : int = foldl((+), 0, l)\nfunction logical_and(x, y) = (&&)(x, y)\n
  • Contract interfaces polymorphism
"},{"location":"CHANGELOG/#changed_4","title":"Changed","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).
"},{"location":"CHANGELOG/#removed_2","title":"Removed","text":"
  • Support for AEVM has been entirely wiped
"},{"location":"CHANGELOG/#610-2021-10-20","title":"6.1.0 - 2021-10-20","text":""},{"location":"CHANGELOG/#added_5","title":"Added","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)\n  a::[] | a > 10 => 1\n  _              => 2\n
    function\n  f(a::[]) | a > 10 = 1\n  f(_)              = 2\n
"},{"location":"CHANGELOG/#changed_5","title":"Changed","text":"
  • Fixed the ACI renderer, it shouldn't drop the stateful modifier
"},{"location":"CHANGELOG/#602-2021-07-05","title":"6.0.2 2021-07-05","text":""},{"location":"CHANGELOG/#changed_6","title":"Changed","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
"},{"location":"CHANGELOG/#601-2021-06-24","title":"6.0.1 2021-06-24","text":""},{"location":"CHANGELOG/#changed_7","title":"Changed","text":"
  • Fixed a bug in calldata encoding for contracts containing multiple contracts
  • Fixed a missing include in the Frac standard library
"},{"location":"CHANGELOG/#600-2021-05-26","title":"6.0.0 2021-05-26","text":""},{"location":"CHANGELOG/#added_6","title":"Added","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
"},{"location":"CHANGELOG/#changed_8","title":"Changed","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
"},{"location":"CHANGELOG/#removed_3","title":"Removed","text":""},{"location":"CHANGELOG/#500-2021-04-30","title":"5.0.0 2021-04-30","text":""},{"location":"CHANGELOG/#added_7","title":"Added","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\"\ncontract C =\n  entrypoint filter_all_a(s: string) : string =\n    String.from_list(List.filter((c : char) => c != 'a', String.to_list(s)))\n
    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)\n  None      => abort(\"Not in Auth context\")\n  Some(tx0) =>\n    switch(tx0.tx)\n      Chain.SpendTx(_, amount, _)  => amount > 400\n      Chain.ContractCallTx(_, _)   => true\n      _                            => false\n
- A debug mode is a added to the compiler. Right now its only use is to turn off hermetization.

"},{"location":"CHANGELOG/#changed_9","title":"Changed","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.
"},{"location":"CHANGELOG/#removed_4","title":"Removed","text":""},{"location":"CHANGELOG/#430","title":"4.3.0","text":""},{"location":"CHANGELOG/#added_8","title":"Added","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
"},{"location":"CHANGELOG/#changed_10","title":"Changed","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
"},{"location":"CHANGELOG/#removed_5","title":"Removed","text":"
  • let definitions are no longer supported in the toplevel of the contract
  • type declarations are no longer supported
"},{"location":"CHANGELOG/#420-2020-01-15","title":"4.2.0 - 2020-01-15","text":""},{"location":"CHANGELOG/#added_9","title":"Added","text":"
  • Allow separate entrypoint/function type signature and definition, and pattern matching in left-hand sides:
      function\n    length : list('a) => int\n    length([])      = 0\n    length(x :: xs) = 1 + length(xs)\n
  • Allow pattern matching in list comprehension generators (filtering out match failures):
      function somes(xs : list(option('a))) : list('a) =\n    [ x | Some(x) <- xs ]\n
  • Allow pattern matching in let-bindings (aborting on match failures):
      function test(m : map(int, int)) =\n      let Some(x) = Map.lookup(m, 0)\n      x\n
"},{"location":"CHANGELOG/#changed_11","title":"Changed","text":"
  • FATE code generator improvements.
  • Bug fix: Handle qualified constructors in patterns.
  • Bug fix: Allow switching also on negative numbers.
"},{"location":"CHANGELOG/#removed_6","title":"Removed","text":""},{"location":"CHANGELOG/#410-2019-11-26","title":"4.1.0 - 2019-11-26","text":""},{"location":"CHANGELOG/#added_10","title":"Added","text":"
  • Support encoding and decoding bit fields in call arguments and results.
"},{"location":"CHANGELOG/#changed_12","title":"Changed","text":"
  • Various improvements to FATE code generator.
"},{"location":"CHANGELOG/#removed_7","title":"Removed","text":""},{"location":"CHANGELOG/#400-2019-10-11","title":"4.0.0 - 2019-10-11","text":""},{"location":"CHANGELOG/#added_11","title":"Added","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]]\n// yields [12,13,14,20,21,22,30,31,32]\n
  • 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.
"},{"location":"CHANGELOG/#changed_13","title":"Changed","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.<hash_fun> and String.<hash_fun> 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.
"},{"location":"CHANGELOG/#removed_8","title":"Removed","text":""},{"location":"CHANGELOG/#320-2019-06-28","title":"3.2.0 - 2019-06-28","text":""},{"location":"CHANGELOG/#added_12","title":"Added","text":"
  • New builtin function require : (bool, string) => (). Defined as
    function require(b, err) = if(!b) abort(err)\n
  • New builtin functions
    Bytes.to_str : bytes(_) => string\nBytes.to_int : bytes(_) => int\n
    for converting a byte array to a hex string and interpreting it as a big-endian encoded integer respectively.
"},{"location":"CHANGELOG/#changed_14","title":"Changed","text":"
  • Public contract functions must now be declared as entrypoints:
    contract Example =\n  // Exported\n  entrypoint exported_fun(x) = local_fun(x)\n  // Not exported\n  function local_fun(x) = x\n
    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.
"},{"location":"CHANGELOG/#removed_9","title":"Removed","text":""},{"location":"CHANGELOG/#310-2019-06-03","title":"3.1.0 - 2019-06-03","text":""},{"location":"CHANGELOG/#added_13","title":"Added","text":""},{"location":"CHANGELOG/#changed_15","title":"Changed","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.
"},{"location":"CHANGELOG/#removed_10","title":"Removed","text":""},{"location":"CHANGELOG/#300-2019-05-21","title":"3.0.0 - 2019-05-21","text":""},{"location":"CHANGELOG/#added_14","title":"Added","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.
"},{"location":"CHANGELOG/#changed_16","title":"Changed","text":"
  • Address literals (+ Oracle, Oracle query and remote contracts) have been changed from #<hex> to address as ak_<base58check>, oracle ok_<base58check>, oracle query oq_<base58check> and remote contract ct_<base58check>.
  • The compilation and typechecking of letfun (e.g. let m(f, xs) = map(f, xs)) was not working properly and has been fixed.
"},{"location":"CHANGELOG/#removed_11","title":"Removed","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.
"},{"location":"CHANGELOG/#210-2019-04-11","title":"2.1.0 - 2019-04-11","text":""},{"location":"CHANGELOG/#added_15","title":"Added","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
"},{"location":"CHANGELOG/#changed_17","title":"Changed","text":"
  • Improvements to the ACI generator
"},{"location":"CHANGELOG/#200-2019-03-11","title":"2.0.0 - 2019-03-11","text":""},{"location":"CHANGELOG/#added_16","title":"Added","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.
"},{"location":"CHANGELOG/#changed_18","title":"Changed","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.
"},{"location":"aeso_aci/","title":"aeso_aci","text":""},{"location":"aeso_aci/#module","title":"Module","text":""},{"location":"aeso_aci/#aeso_aci_1","title":"aeso_aci","text":"

The ACI interface encoder and decoder.

"},{"location":"aeso_aci/#description","title":"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 =\n  record state = { a : answers }\n  type answers() = map(string, int)\n\n  stateful function init() = { a = {} }\n  private function the_answer() = 42\n  function new_answer(q : string, a : int) : answers() = { [q] = a }\n

generates the following JSON structure representing the contract interface:

{\n\"contract\": {\n\"functions\": [\n{\n\"arguments\": [],\n\"name\": \"init\",\n\"returns\": \"Answers.state\",\n\"stateful\": true\n},\n{\n\"arguments\": [\n{\n\"name\": \"q\",\n\"type\": \"string\"\n},\n{\n\"name\": \"a\",\n\"type\": \"int\"\n}\n],\n\"name\": \"new_answer\",\n\"returns\": {\n\"map\": [\n\"string\",\n\"int\"\n]\n},\n\"stateful\": false\n}\n],\n\"name\": \"Answers\",\n\"state\": {\n\"record\": [\n{\n\"name\": \"a\",\n\"type\": \"Answers.answers\"\n}\n]\n},\n\"typedefs\": [\n{\n\"name\": \"answers\",\n\"typedef\": {\n\"map\": [\n\"string\",\n\"int\"\n]\n},\n\"vars\": []\n}\n]\n}\n}\n

When that encoding is decoded the following include definition is generated:

contract Answers =\n  record state = {a : Answers.answers}\n  type answers = map(string, int)\n  function init : () => Answers.state\n  function new_answer : (string, int) => map(string, int)\n
"},{"location":"aeso_aci/#types","title":"Types","text":"
-type aci_type()  :: json | string.\n-type json()      :: jsx:json_term().\n-type json_text() :: binary().\n
"},{"location":"aeso_aci/#exports","title":"Exports","text":""},{"location":"aeso_aci/#contract_interfaceaci_type-string-ok-json-string-error-term","title":"contract_interface(aci_type(), string()) -> {ok, json() | string()} | {error, term()}","text":"

Generate the JSON encoding of the interface to a contract. The type definitions and non-private functions are included in the JSON string.

"},{"location":"aeso_aci/#render_aci_jsonjson-json_text-string","title":"render_aci_json(json() | json_text()) -> string().","text":"

Take a JSON encoding of a contract interface and generate a contract interface that can be included in another contract.

"},{"location":"aeso_aci/#example-run","title":"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\").\n{ok,<<\"contract Answers =\\n  record state = { a : answers }\\n  type answers() = map(string, int)\\n\\n  stateful function\"...>>}\n2> {ok,JsonACI} = aeso_aci:contract_interface(json, Contract).\n{ok,[#{contract =>\n#{functions =>\n[#{arguments => [],name => <<\"init\">>,\nreturns => <<\"Answers.state\">>,stateful => true},\n#{arguments =>\n[#{name => <<\"q\">>,type => <<\"string\">>},\n#{name => <<\"a\">>,type => <<\"int\">>}],\nname => <<\"new_answer\">>,\nreturns => #{<<\"map\">> => [<<\"string\">>,<<\"int\">>]},\nstateful => false}],\nname => <<\"Answers\">>,\nstate =>\n#{record =>\n[#{name => <<\"a\">>,type => <<\"Answers.answers\">>}]},\ntypedefs =>\n[#{name => <<\"answers\">>,\ntypedef => #{<<\"map\">> => [<<\"string\">>,<<\"int\">>]},\nvars => []}]}}]}\n3> file:write_file(\"aci_test.aci\", jsx:encode(JsonACI)).\nok\n4> {ok,InterfaceStub} = aeso_aci:render_aci_json(JsonACI).\n{ok,<<\"contract Answers =\\n  record state = {a : Answers.answers}\\n  type answers = map(string, int)\\n  function init \"...>>}\n5> file:write_file(\"aci_test.include\", InterfaceStub).\nok\n6> jsx:prettify(jsx:encode(JsonACI)).\n<<\"[\\n  {\\n    \\\"contract\\\": {\\n      \\\"functions\\\": [\\n        {\\n          \\\"arguments\\\": [],\\n          \\\"name\\\": \\\"init\\\",\\n        \"...>>\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.

"},{"location":"aeso_compiler/","title":"aeso_compiler","text":""},{"location":"aeso_compiler/#module","title":"Module","text":""},{"location":"aeso_compiler/#aeso_compiler_1","title":"aeso_compiler","text":"

The Sophia compiler

"},{"location":"aeso_compiler/#description","title":"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.

"},{"location":"aeso_compiler/#types","title":"Types","text":"
contract_string() = string() | binary()\ncontract_map() = #{bytecode => binary(),\ncompiler_version => binary(),\ncontract_souce => string(),\ntype_info => type_info()}\ntype_info()\nerrorstring() = binary()\n
"},{"location":"aeso_compiler/#exports","title":"Exports","text":""},{"location":"aeso_compiler/#filefile","title":"file(File)","text":""},{"location":"aeso_compiler/#filefile-options-compret","title":"file(File, Options) -> CompRet","text":""},{"location":"aeso_compiler/#from_stringcontractstring-options-compret","title":"from_string(ContractString, Options) -> CompRet","text":"

Types

ContractString = contract_string()\nOptions = [Option]\nCompRet = {ok,ContractMap} | {error,ErrorString}\nContractMap = contract_map()\nErrorString = errorstring()\n

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.

"},{"location":"aeso_compiler/#options-to-control-which-compiler-optimizations-should-run","title":"Options to control which compiler optimizations should run:","text":"

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
"},{"location":"aeso_compiler/#check_callcontractstring-options-checkret","title":"check_call(ContractString, Options) -> CheckRet","text":"

Types

ContractString = string() | binary()\nCheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}\nTypes = [Type]\nType = term()\n
Check a call in contract through the __call function.

"},{"location":"aeso_compiler/#version-ok-version-error-term","title":"version() -> {ok, Version} | {error, term()}","text":"

Types

Version = binary()\n

Get the current version of the Sophia compiler.

"},{"location":"sophia/","title":"Sophia","text":"

This file has been moved here

"},{"location":"sophia_examples/","title":"Contract examples","text":""},{"location":"sophia_examples/#crowdfunding","title":"Crowdfunding","text":"
/*\n * A simple crowd-funding example\n */\ncontract FundMe =\n\nrecord spend_args = { recipient : address,\namount    : int }\n\nrecord state = { contributions : map(address, int),\ntotal         : int,\nbeneficiary   : address,\ndeadline      : int,\ngoal          : int }\n\nstateful function spend(args : spend_args) =\nChain.spend(args.recipient, args.amount)\n\nentrypoint init(beneficiary, deadline, goal) : state =\n{ contributions = {},\nbeneficiary   = beneficiary,\ndeadline      = deadline,\ntotal         = 0,\ngoal          = goal }\n\nfunction is_contributor(addr) =\nMap.member(addr, state.contributions)\n\nstateful entrypoint contribute() =\nif(Chain.block_height >= state.deadline)\nspend({ recipient = Call.caller, amount = Call.value }) // Refund money\nfalse\nelse\nlet amount =\nswitch(Map.lookup(Call.caller, state.contributions))\nNone    => Call.value\nSome(n) => n + Call.value\nput(state{ contributions[Call.caller] = amount,\ntotal @ tot = tot + Call.value })\ntrue\n\nstateful entrypoint withdraw() =\nif(Chain.block_height < state.deadline)\nabort(\"Cannot withdraw before deadline\")\nif(Call.caller == state.beneficiary)\nwithdraw_beneficiary()\nelif(is_contributor(Call.caller))\nwithdraw_contributor()\nelse\nabort(\"Not a contributor or beneficiary\")\n\nstateful function withdraw_beneficiary() =\nrequire(state.total >= state.goal, \"Project was not funded\")\nspend({recipient = state.beneficiary,\namount    = Contract.balance })\n\nstateful function withdraw_contributor() =\nif(state.total >= state.goal)\nabort(\"Project was funded\")\nlet to = Call.caller\nspend({recipient = to,\namount    = state.contributions[to]})\nput(state{ contributions @ c = Map.delete(to, c) })\n
"},{"location":"sophia_examples/#repositories","title":"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.
"},{"location":"sophia_features/","title":"Features","text":""},{"location":"sophia_features/#contracts","title":"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.

"},{"location":"sophia_features/#calling-other-contracts","title":"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\ncontract interface VotingType =\nentrypoint vote : string => unit\n

Now given contract address of type VotingType you can call the vote entrypoint of that contract:

contract VoteTwice =\nentrypoint voteTwice(v : VotingType, alt : string) =\nv.vote(alt)\nv.vote(alt)\n

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) =\nv.vote(value = fee, alt)\nv.vote(value = fee, alt)\n

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) =\nChain.spend(v.address, amount)\n
"},{"location":"sophia_features/#protected-contract-calls","title":"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 =\nentrypoint : vote : string => unit\n\ncontract Voter =\nentrypoint tryVote(v : VotingType, alt : string) =\nswitch(v.vote(alt, protected = true) : option(unit))\nNone    => \"Voting failed\"\nSome(_) => \"Voting successful\"\n

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.

"},{"location":"sophia_features/#contract-factories-and-child-contracts","title":"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 =\ntype state = int\nentrypoint init(x) = x\nentrypoint get() = state\n\nmain contract IntHolderFactory =\nstateful entrypoint new(x : int) : IntHolder =\nlet ih = Chain.create(x) : IntHolder\nih\n

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.

"},{"location":"sophia_features/#contract-interfaces-and-polymorphism","title":"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 =\n  entrypoint sound : () => string\n\n contract Cat : Animal =\n  entrypoint sound() = \"Cat sound\"\n

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 =\n  entrypoint f : () => unit\n\ncontract interface I : II =\n  entrypoint f : () => unit\n  entrypoint g : () => unit\n\ncontract C : I =\n  entrypoint f() = ()\n  entrypoint g() = ()\n

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\n\ncontract interface X : Z =\n     entrypoint x : () => int\n\n contract interface Y : X =\n     entrypoint x : () => int\n     entrypoint y : () => int\n\n contract interface Z : Y =\n     entrypoint x : () => int\n     entrypoint y : () => int\n     entrypoint z : () => int\n\n contract C : Z =\n     entrypoint x() = 1\n     entrypoint y() = 1\n     entrypoint z() = 1\n
"},{"location":"sophia_features/#adding-or-removing-modifiers","title":"Adding or removing modifiers","text":"

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:

  1. A payable contract or interface can implement a payable interface or a non-payable interface.
  2. A non-payable contract or interface can only implement a non-payable interface, and cannot implement a payable interface.
  3. A payable entrypoint can implement a payable entrypoint or a non-payable entrypoint.
  4. A non-payable entrypoint can only implement a non-payable entrypoint, and cannot implement a payable entrypoint.
  5. A non-stateful entrypoint can implement a stateful entrypoint or a non-stateful entrypoint.
  6. A stateful entrypoint can only implement a stateful entrypoint, and cannot implement a non-stateful entrypoint.
"},{"location":"sophia_features/#subtyping-and-variance","title":"Subtyping and variance","text":"

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, datatypes 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 =\nentrypoint age : () => int\n\ncontract Dog : Animal =\nentrypoint age() = // ...\nentrypoint woof() = \"woof\"\n\ncontract Cat : Animal =\nentrypoint age() = // ...\nentrypoint meow() = \"meow\"\n

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()\nlet g : (Animal) => string  = f\nlet c : Cat = Chain.create()\ng(c)  // Cat barking!\n

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\ndatatype ct('a) = Ct('a => unit) // ct is contravariant on 'a\ndatatype in('a) = In('a => 'a) // in is invariant on 'a\ndatatype bi('a) = Bi // bi is bivariant on 'a\n

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).

"},{"location":"sophia_features/#mutable-state","title":"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.

"},{"location":"sophia_features/#stateful-functions","title":"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) =\nput(s)\n

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.
"},{"location":"sophia_features/#payable","title":"Payable","text":""},{"location":"sophia_features/#payable-contracts","title":"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\npayable contract ExampleContract =\nstateful entrypoint do_stuff() = ...\n

If in doubt, it is possible to check if an address is payable using Address.is_payable(addr).

"},{"location":"sophia_features/#payable-entrypoints","title":"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) =\nif(Call.value > 42)\ntransfer_item(to)\nelse\nabort(\"Value too low\")\n
"},{"location":"sophia_features/#namespaces","title":"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 =\ntype number = int\nfunction inc(x : number) : number = x + 1\n\ncontract MyContract =\nentrypoint plus2(x) : Library.number =\nLibrary.inc(Library.inc(x))\n

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\"\nusing Pair\ncontract C =\n  type state = int\n  entrypoint init() =\n    let p = (1, 2)\n    fst(p)  // this is the same as Pair.fst(p)\n

It is also possible to make an alias for the namespace with the as keyword:

include \"Pair.aes\"\ncontract C =\n  using Pair as P\n  type state = int\n  entrypoint init() =\n    let p = (1, 2)\n    P.fst(p)  // this is the same as Pair.fst(p)\n

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\nnamespace Xb = function g() = 2\ncontract Cntr =\n  using Xa as A\n  using Xb as A\n  type state = int\n  entrypoint init() = A.f() + A.g()\n

Note that using functions with the same name would result in an ambiguous name error:

namespace Xa = function f() = 1\nnamespace Xb = function f() = 2\ncontract Cntr =\n  using Xa as A\n  using Xb as A\n  type state = int\n\n  // the next line has an error because f is defined in both Xa and Xb\n  entrypoint init() = A.f()\n

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\nusing Triple hiding [fst, snd]  // this will import everything except for fst and snd\n

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.

"},{"location":"sophia_features/#splitting-code-over-multiple-files","title":"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 =\nfunction inc(x) = x + 1\n

you can use it from another file using an include:

include \"library.aes\"\ncontract MyContract =\nentrypoint plus2(x) = Library.inc(Library.inc(x))\n

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.

"},{"location":"sophia_features/#standard-library","title":"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\"\ninclude \"Pair.aes\"\n-- Map is already there!\n\nnamespace C =\nentrypoint keys(m : map('a, 'b)) : list('a) =\nList.map(Pair.fst, (Map.to_list(m)))\n

"},{"location":"sophia_features/#types","title":"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()"},{"location":"sophia_features/#literals","title":"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"},{"location":"sophia_features/#hole-expression","title":"Hole expression","text":"

Hole expressions, written as ???, are expressions that are used as a placeholder. During compilation, the compiler will generate a type error indication the type of the hole expression.

include \"List.aes\"\ncontract C =\n    entrypoint f() =\n        List.sum(List.map(???, [1,2,3]))\n

A hole expression found in the example above will generate the error Found a hole of type `(int) => int`. This says that the compiler expects a function from int to int in place of the ??? placeholder.

"},{"location":"sophia_features/#constants","title":"Constants","text":"

Constants in Sophia are contract-level bindings that can be used in either contracts or namespaces. The value of a constant can be a literal, another constant, or arithmetic operations applied to other constants. Lists, tuples, maps, and records can also be used to define a constant as long as their elements are also constants.

The following visibility rules apply to constants: * Constants defined inside a contract are private in that contract. Thus, cannot be accessed through instances of their defining contract. * Constants defined inside a namespace are public. Thus, can be used in other contracts or namespaces. * Constants cannot be defined inside a contract interface.

When a constant is shadowed, it can be accessed using its qualified name:

contract C =\n  let c = 1\n  entrypoint f() =\n    let c = 2\n    c + C.c  // the result is 3\n

The name of the constant must be an id; therefore, no pattern matching is allowed when defining a constant:

contract C\n  let x::y::_ = [1,2,3]  // this will result in an error\n
"},{"location":"sophia_features/#arithmetic","title":"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.

"},{"location":"sophia_features/#bit-fields","title":"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).

"},{"location":"sophia_features/#type-aliases","title":"Type aliases","text":"

Type aliases can be introduced with the type keyword and can be parameterized. For instance

type number = int\ntype string_map('a) = map(string, 'a)\n

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)

"},{"location":"sophia_features/#algebraic-data-types","title":"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)\n

Elements of data types can be pattern matched against, using the switch construct:

function get_left(x : one_or_both('a, 'b)) : option('a) =\nswitch(x)\nLeft(x)    => Some(x)\nRight(_)   => None\nBoth(x, _) => Some(x)\n

or directly in the left-hand side:

function\nget_left : one_or_both('a, 'b) => option('a)\nget_left(Left(x))    = Some(x)\nget_left(Right(_))   = None\nget_left(Both(x, _)) = Some(x)\n

NOTE: Data types cannot currently be recursive.

Sophia also supports the assignment of patterns to variables:

function f(x) = switch(x)\nh1::(t = h2::_) => (h1 + h2)::t  // same as `h1::h2::k => (h1 + h2)::h2::k`\n_ => x\n\nfunction g(p : int * option(int)) : int =\nlet (a, (o = Some(b))) = p  // o is equal to Pair.snd(p)\nb\n

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) =\nswitch(x)\nLeft(x)    | x > 0 => Some(x)\nBoth(x, _) | x > 0 => Some(x)\n_                  => None\n
function\nget_left_if_positive : one_or_both(int, 'b) => option(int)\nget_left_if_positive(Left(x))    | x > 0 = Some(x)\nget_left_if_positive(Both(x, _)) | x > 0 = Some(x)\nget_left_if_positive(_)                  = None\n

Guards cannot be stateful even when used inside a stateful function.

"},{"location":"sophia_features/#lists","title":"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)\n[(1, \"aaa\"), (10, \"jjj\"), (666, \"the beast\")]                     : list(int * string)\n[{[1] = \"aaa\", [10] = \"jjj\"}, {[5] = \"eee\", [666] = \"the beast\"}] : list(map(int, string))\n

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]]\n// yields [12,13,14,20,21,22,30,31,32]\n

Lists can be constructed using the range syntax using special .. operator:

[1..4] == [1,2,3,4]\n
The ranges are always ascending and have step equal to 1.

Please refer to the standard library for the predefined functionalities.

"},{"location":"sophia_features/#maps-and-records","title":"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,\nbalance : int,\nhistory : list(transaction) }\n

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.

"},{"location":"sophia_features/#constructing-maps-and-records","title":"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) =\n{name = name, balance = 0, history = []}\n
Maps are constructed similarly, with keys enclosed in square brackets
  function example_map() : map(string, int) =\n{[\"key1\"] = 1, [\"key2\"] = 2}\n
The empty map is written {}.

"},{"location":"sophia_features/#accessing-values","title":"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)) =\naccounts[a].balance\n
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.

"},{"location":"sophia_features/#updating-a-value","title":"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) =\naccounts{ [a].history = [] }\n
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 = [] }\n

"},{"location":"sophia_features/#map-implementation","title":"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.

"},{"location":"sophia_features/#strings","title":"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.

"},{"location":"sophia_features/#chars","title":"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.

"},{"location":"sophia_features/#byte-arrays","title":"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.

"},{"location":"sophia_features/#cryptographic-builtins","title":"Cryptographic builtins","text":"

Libraries Crypto and String provide functions to hash objects, verify signatures etc. The hash is a type alias for bytes(32).

"},{"location":"sophia_features/#authorization-interface","title":"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.

"},{"location":"sophia_features/#oracle-interface","title":"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.

"},{"location":"sophia_features/#example","title":"Example","text":"

Example for an oracle answering questions of type string with answers of type int:

contract Oracles =\n\nstateful entrypoint registerOracle(acct : address,\nsign : signature,   // Signed network id + oracle address + contract address\nqfee : int,\nttl  : Chain.ttl) : oracle(string, int) =\nOracle.register(acct, signature = sign, qfee, ttl)\n\nentrypoint queryFee(o : oracle(string, int)) : int =\nOracle.query_fee(o)\n\npayable stateful entrypoint createQuery(o    : oracle_query(string, int),\nq    : string,\nqfee : int,\nqttl : Chain.ttl,\nrttl : int) : oracle_query(string, int) =\nrequire(qfee =< Call.value, \"insufficient value for qfee\")\nOracle.query(o, q, qfee, qttl, RelativeTTL(rttl))\n\nstateful entrypoint extendOracle(o   : oracle(string, int),\nttl : Chain.ttl) : unit =\nOracle.extend(o, ttl)\n\nstateful entrypoint signExtendOracle(o    : oracle(string, int),\nsign : signature,   // Signed network id + oracle address + contract address\nttl  : Chain.ttl) : unit =\nOracle.extend(o, signature = sign, ttl)\n\nstateful entrypoint respond(o    : oracle(string, int),\nq    : oracle_query(string, int),\nsign : signature,        // Signed network id + oracle query id + contract address\nr    : int) =\nOracle.respond(o, q, signature = sign, r)\n\nentrypoint getQuestion(o : oracle(string, int),\nq : oracle_query(string, int)) : string =\nOracle.get_question(o, q)\n\nentrypoint hasAnswer(o : oracle(string, int),\nq : oracle_query(string, int)) =\nswitch(Oracle.get_answer(o, q))\nNone    => false\nSome(_) => true\n\nentrypoint getAnswer(o : oracle(string, int),\nq : oracle_query(string, int)) : option(int) =\nOracle.get_answer(o, q)\n

"},{"location":"sophia_features/#sanity-checks","title":"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.

"},{"location":"sophia_features/#aens-interface","title":"AENS interface","text":"

Contracts can interact with the \u00e6ternity naming system. For this purpose the AENS library was exposed.

"},{"location":"sophia_features/#example_1","title":"Example","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) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, FixedTTL(expiry), _)) =>\nif(Chain.block_height + 1000 > expiry)\nAENS.update(addr, name, Some(RelativeTTL(50000)), None, None, signature = sig)\n

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,\npt : AENS.pointee, sig : signature) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, _, ptrs)) =>\nAENS.update(addr, name, None, None, Some(ptrs{[key] = pt}), signature = sig)\n\nstateful entrypoint delete_key(addr : address, name : string,\nkey : string, sig : signature) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, _, ptrs)) =>\nlet ptrs = Map.delete(key, ptrs)\nAENS.update(addr, name, None, None, Some(ptrs), signature = sig)\n

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.

"},{"location":"sophia_features/#events","title":"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\n= Event1(int, int, string)\n| Event2(string, address)\n\nChain.event(e : event) : unit\n

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() : () =\nChain.event(Event1(42, 34, \"foo\"))\nChain.event(Event2(\"This is not indexed\", Contract.address))\n
"},{"location":"sophia_features/#argument-order","title":"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)\n\n...\n\nChain.event(AnotherEvent(\"This is not indexed\", Contract.address))\n
would yield exactly the same result in the example above!

"},{"location":"sophia_features/#compiler-pragmas","title":"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\n@compiler <  4.4\n

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.

"},{"location":"sophia_features/#exceptions","title":"Exceptions","text":"

Contracts can fail with an (uncatchable) exception using the built-in function

abort(reason : string) : 'a\n

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) =\nif(!b) abort(err)\n

Aside from that, there is an almost equivalent function exit

exit(reason : string) : 'a\n

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.

"},{"location":"sophia_features/#delegation-signature","title":"Delegation signature","text":"

Some chain operations (Oracle.<operation> and AENS.<operation>) 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.).

"},{"location":"sophia_stdlib/","title":"Standard library","text":""},{"location":"sophia_stdlib/#standard-library","title":"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\"\n

  • Bitwise
  • BLS12_381
  • Func
  • Frac
  • List
  • Option
  • Pair
  • Set
  • String
  • Triple
"},{"location":"sophia_stdlib/#builtin-namespaces","title":"Builtin namespaces","text":"

They are available without any explicit includes.

"},{"location":"sophia_stdlib/#address","title":"Address","text":""},{"location":"sophia_stdlib/#to_str","title":"to_str","text":"
Address.to_str(a : address) : string\n

Base58 encoded string

"},{"location":"sophia_stdlib/#is_contract","title":"is_contract","text":"
Address.is_contract(a : address) : bool\n

Is the address a contract

"},{"location":"sophia_stdlib/#is_oracle","title":"is_oracle","text":"
Address.is_oracle(a : address) : bool\n

Is the address a registered oracle

"},{"location":"sophia_stdlib/#is_payable","title":"is_payable","text":"
Address.is_payable(a : address) : bool\n

Can the address be spent to

"},{"location":"sophia_stdlib/#to_contract","title":"to_contract","text":"
Address.to_contract(a : address) : C\n

Cast address to contract type C (where C is a contract)

"},{"location":"sophia_stdlib/#aens","title":"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.

"},{"location":"sophia_stdlib/#types","title":"Types","text":""},{"location":"sophia_stdlib/#name","title":"name","text":"
datatype name = Name(address, Chain.ttl, map(string, AENS.pointee))\n
"},{"location":"sophia_stdlib/#pointee","title":"pointee","text":"
datatype pointee = AccountPt(address) | OraclePt(address)\n                 | ContractPt(address) | ChannelPt(address)\n
"},{"location":"sophia_stdlib/#functions","title":"Functions","text":""},{"location":"sophia_stdlib/#resolve","title":"resolve","text":"
AENS.resolve(name : string, key : string) : option('a)\n

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.

"},{"location":"sophia_stdlib/#lookup","title":"lookup","text":"
AENS.lookup(name : string) : option(AENS.name)\n

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\")\n

"},{"location":"sophia_stdlib/#preclaim","title":"preclaim","text":"
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit\n

The signature should be over network id + owner address + Contract.address (concatenated as byte arrays).

"},{"location":"sophia_stdlib/#claim","title":"claim","text":"
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit\n

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.

"},{"location":"sophia_stdlib/#transfer","title":"transfer","text":"
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit\n

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.

"},{"location":"sophia_stdlib/#revoke","title":"revoke","text":"
AENS.revoke(owner : address, name : string, <signature : signature>) : unit\n

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.

"},{"location":"sophia_stdlib/#update","title":"update","text":"
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),\n            new_ptrs : option(map(string, AENS.pointee)), <signature : signature>) : unit\n

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.

"},{"location":"sophia_stdlib/#auth","title":"Auth","text":""},{"location":"sophia_stdlib/#tx","title":"tx","text":"
Auth.tx : option(Chain.tx)\n

Where Chain.tx is (built-in) defined like:

namespace Chain =\n  record tx = { paying_for : option(Chain.paying_for_tx)\n              , ga_metas : list(Chain.ga_meta_tx)\n              , actor : address\n              , fee   : int\n              , ttl   : int\n              , tx    : Chain.base_tx }\n\n  datatype ga_meta_tx    = GAMetaTx(address, int)\n  datatype paying_for_tx = PayingForTx(address, int)\n  datatype base_tx = SpendTx(address, int, string)\n                   | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx\n                   | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)\n                   | NameRevokeTx(hash) | NameTransferTx(address, string)\n                   | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |\n                   | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)\n                   | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)\n                   | ContractCreateTx(int) | ContractCallTx(address, int)\n                   | GAAttachTx\n

"},{"location":"sophia_stdlib/#tx_hash","title":"tx_hash","text":"
Auth.tx_hash : option(hash)\n

Gets the transaction hash during authentication.

"},{"location":"sophia_stdlib/#bits","title":"Bits","text":""},{"location":"sophia_stdlib/#none","title":"none","text":"
Bits.none : bits\n

A bit field with all bits cleared

"},{"location":"sophia_stdlib/#all","title":"all","text":"
Bits.all : bits\n

A bit field with all bits set

"},{"location":"sophia_stdlib/#set","title":"set","text":"
Bits.set(b : bits, i : int) : bits\n

Set bit i

"},{"location":"sophia_stdlib/#clear","title":"clear","text":"
Bits.clear(b : bits, i : int) : bits\n

Clear bit i

"},{"location":"sophia_stdlib/#test","title":"test","text":"
Bits.test(b : bits, i : int) : bool\n

Check if bit i is set

"},{"location":"sophia_stdlib/#sum","title":"sum","text":"
Bits.sum(b : bits) : int\n

Count the number of set bits

"},{"location":"sophia_stdlib/#union","title":"union","text":"
Bits.union(a : bits, b : bits) : bits\n

Bitwise disjunction

"},{"location":"sophia_stdlib/#intersection","title":"intersection","text":"
Bits.intersection(a : bits, b : bits) : bits\n

Bitwise conjunction

"},{"location":"sophia_stdlib/#difference","title":"difference","text":"
Bits.difference(a : bits, b : bits) : bits\n

Each bit is true if and only if it was 1 in a and 0 in b

"},{"location":"sophia_stdlib/#bytes","title":"Bytes","text":""},{"location":"sophia_stdlib/#to_int","title":"to_int","text":"
Bytes.to_int(b : bytes(n)) : int\n

Interprets the byte array as a big endian integer

"},{"location":"sophia_stdlib/#to_str_1","title":"to_str","text":"
Bytes.to_str(b : bytes(n)) : string\n

Returns the hexadecimal representation of the byte array

"},{"location":"sophia_stdlib/#concat","title":"concat","text":"
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)\n

Concatenates two byte arrays

"},{"location":"sophia_stdlib/#split","title":"split","text":"
Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)\n

Splits a byte array at given index

"},{"location":"sophia_stdlib/#call","title":"Call","text":"

Values related to the call to the current contract

"},{"location":"sophia_stdlib/#origin","title":"origin","text":"
Call.origin : address\n

The address of the account that signed the call transaction that led to this call.

"},{"location":"sophia_stdlib/#caller","title":"caller","text":"
Call.caller : address\n

The address of the entity (possibly another contract) calling the contract.

"},{"location":"sophia_stdlib/#value","title":"value","text":"
Call.value : int\n

The amount of coins transferred to the contract in the call.

"},{"location":"sophia_stdlib/#gas_price","title":"gas_price","text":"
Call.gas_price : int\n

The gas price of the current call.

"},{"location":"sophia_stdlib/#fee","title":"fee","text":"
Call.fee : int\n

The fee of the current call.

"},{"location":"sophia_stdlib/#gas_left","title":"gas_left","text":"
Call.gas_left() : int\n

The amount of gas left for the current call.

"},{"location":"sophia_stdlib/#chain","title":"Chain","text":"

Values and functions related to the chain itself and other entities that live on it.

"},{"location":"sophia_stdlib/#types_1","title":"Types","text":""},{"location":"sophia_stdlib/#tx_1","title":"tx","text":"
record tx = { paying_for : option(Chain.paying_for_tx)\n            , ga_metas : list(Chain.ga_meta_tx)\n            , actor : address\n            , fee   : int\n            , ttl   : int\n            , tx    : Chain.base_tx }\n
"},{"location":"sophia_stdlib/#ga_meta_tx","title":"ga_meta_tx","text":"
datatype ga_meta_tx    = GAMetaTx(address, int)\n
"},{"location":"sophia_stdlib/#paying_for_tx","title":"paying_for_tx","text":"
datatype paying_for_tx = PayingForTx(address, int)\n
"},{"location":"sophia_stdlib/#base_tx","title":"base_tx","text":"
datatype base_tx = SpendTx(address, int, string)\n                 | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx\n                 | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)\n                 | NameRevokeTx(hash) | NameTransferTx(address, string)\n                 | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |\n                 | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)\n                 | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)\n                 | ContractCreateTx(int) | ContractCallTx(address, int)\n                 | GAAttachTx\n
"},{"location":"sophia_stdlib/#functions_1","title":"Functions","text":""},{"location":"sophia_stdlib/#balance","title":"balance","text":"
Chain.balance(a : address) : int\n

The balance of account a.

"},{"location":"sophia_stdlib/#block_hash","title":"block_hash","text":"
Chain.block_hash(h : int) : option(bytes(32))\n

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.

"},{"location":"sophia_stdlib/#block_height","title":"block_height","text":"
Chain.block_height : int\"\n

The height of the current block (i.e. the block in which the current call will be included).

"},{"location":"sophia_stdlib/#bytecode_hash","title":"bytecode_hash","text":"
Chain.bytecode_hash : 'c => option(hash)\n

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.

"},{"location":"sophia_stdlib/#create","title":"create","text":"
Chain.create(value : int, ...) => 'c\n

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 =\n  record state = {supply: int, name: string}\n  entrypoint init(supply, name) = {supply: supply, name: name}\n  stateful payable entrypoint buy(amount) =\n    require(Call.value == amount, \"amount_value_mismatch\")\n    ...\n  stateful entrypoint sell(amount) =\n    require(amount >= 0, \"negative_amount\")\n    ...\n\nmain contract Market =\n  type state = list(Auction)\n  entrypoint init() = []\n  stateful entrypoint new(name : string) =\n    let new_auction = Chain.create(0, name) : Auction\n    put(new_auction::state)\n

The typechecker must be certain about the created contract's type, so it is worth writing it explicitly as shown in the example.

"},{"location":"sophia_stdlib/#clone","title":"clone","text":"
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...\n              ) => if(protected) option('c) else 'c\n

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 =\n  entrypoint init : (int, string) => void\n  stateful payable entrypoint buy : (int) => ()\n  stateful entrypoint sell : (int) => ()\n\nmain contract Market =\n  type state = list(Auction)\n  entrypoint init() = []\n  stateful entrypoint new_of(template : Auction, name : string) =\n    switch(Chain.clone(ref=template, protected=true, 0, name))\n      None => abort(\"Bad auction!\")\n      Some(new_auction) =>\n        put(new_auction::state)\n

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.

"},{"location":"sophia_stdlib/#coinbase","title":"coinbase","text":"
Chain.coinbase : address\n

The address of the account that mined the current block.

"},{"location":"sophia_stdlib/#difficulty","title":"difficulty","text":"
Chain.difficulty : int\n

The difficulty of the current block.

"},{"location":"sophia_stdlib/#event","title":"event","text":"
Chain.event(e : event) : unit\n

Emits the event. To use this function one needs to define the event type as a datatype in the contract.

"},{"location":"sophia_stdlib/#gas_limit","title":"gas_limit","text":"
Chain.gas_limit : int\n

The gas limit of the current block.

"},{"location":"sophia_stdlib/#spend","title":"spend","text":"
Chain.spend(to : address, amount : int) : unit\n

Spend amount tokens to to. Will fail (and abort the contract) if contract doesn't have amount tokens to transfer, or, if to is not payable.

"},{"location":"sophia_stdlib/#timestamp","title":"timestamp","text":"
Chain.timestamp : int\n

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

"},{"location":"sophia_stdlib/#char","title":"Char","text":""},{"location":"sophia_stdlib/#to_int_1","title":"to_int","text":"

Char.to_int(c : char) : int

Returns the UTF-8 codepoint of a character\n\n\n#### from_int\n
Char.from_int(i : int) : option(char)

Opposite of to_int. Returns None if the integer doesn't correspond to a single (normalized) codepoint.

"},{"location":"sophia_stdlib/#contract","title":"Contract","text":"

Values related to the current contract

"},{"location":"sophia_stdlib/#creator","title":"creator","text":"
Contract.creator : address\n

Address of the entity that signed the contract creation transaction

"},{"location":"sophia_stdlib/#address_1","title":"address","text":"
Contract.address : address\n

Address of the contract account

"},{"location":"sophia_stdlib/#balance_1","title":"balance","text":"
Contract.balance : int\n

Amount of coins in the contract account

"},{"location":"sophia_stdlib/#crypto","title":"Crypto","text":""},{"location":"sophia_stdlib/#sha3","title":"sha3","text":"
Crypto.sha3(x : 'a) : hash\n

Hash any object to SHA3

"},{"location":"sophia_stdlib/#sha256","title":"sha256","text":"
Crypto.sha256(x : 'a) : hash\n

Hash any object to SHA256

"},{"location":"sophia_stdlib/#blake2b","title":"blake2b","text":"
Crypto.blake2b(x : 'a) : hash\n

Hash any object to blake2b

"},{"location":"sophia_stdlib/#verify_sig","title":"verify_sig","text":"
Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool\n

Checks if the signature of msg was made using private key corresponding to the pubkey

"},{"location":"sophia_stdlib/#ecverify_secp256k1","title":"ecverify_secp256k1","text":"
Crypto.ecverify_secp256k1(msg : hash, addr : bytes(20), sig : bytes(65)) : bool\n

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).

"},{"location":"sophia_stdlib/#ecrecover_secp256k1","title":"ecrecover_secp256k1","text":"
Crypto.ecrecover_secp256k1(msg : hash, sig : bytes(65)) : option(bytes(20))\n

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).

"},{"location":"sophia_stdlib/#verify_sig_secp256k1","title":"verify_sig_secp256k1","text":"
Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : bool\n

Verifies a standard 64-byte ECDSA signature (R || S).

"},{"location":"sophia_stdlib/#int","title":"Int","text":""},{"location":"sophia_stdlib/#to_str_2","title":"to_str","text":"
Int.to_str : int => string\n

Casts integer to string using decimal representation

"},{"location":"sophia_stdlib/#map","title":"Map","text":""},{"location":"sophia_stdlib/#lookup_1","title":"lookup","text":"

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

"},{"location":"sophia_stdlib/#lookup_default","title":"lookup_default","text":"

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

"},{"location":"sophia_stdlib/#member","title":"member","text":"

Map.member(k : 'k, m : map('k, 'v)) : bool

Checks if the key is present in the map

"},{"location":"sophia_stdlib/#delete","title":"delete","text":"

Map.delete(k : 'k, m : map('k, 'v)) : map('k, 'v)

Removes the key from the map

"},{"location":"sophia_stdlib/#size","title":"size","text":"

Map.size(m : map('k, 'v)) : int

Returns the number of elements in the map

"},{"location":"sophia_stdlib/#to_list","title":"to_list","text":"

Map.to_list(m : map('k, 'v)) : list('k * 'v)

Returns a list containing pairs of keys and their respective elements.

"},{"location":"sophia_stdlib/#from_list","title":"from_list","text":"

Map.from_list(m : list('k * 'v)) : map('k, 'v)

Turns a list of pairs of form (key, value) into a map

"},{"location":"sophia_stdlib/#oracle","title":"Oracle","text":""},{"location":"sophia_stdlib/#register","title":"register","text":"
Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain.ttl) : oracle('a, 'b)\n

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))\n  Oracle.register(addr1, 25, RelativeTTL(500), signature = sign1)\n

"},{"location":"sophia_stdlib/#get_question","title":"get_question","text":"
Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a\n

Checks what was the question of query q on oracle o

"},{"location":"sophia_stdlib/#respond","title":"respond","text":"
Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit\n

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

"},{"location":"sophia_stdlib/#extend","title":"extend","text":"
Oracle.extend(<signature : bytes(64)>, o : oracle('a, 'b), ttl : Chain.ttl) : unit\n

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.

"},{"location":"sophia_stdlib/#query_fee","title":"query_fee","text":"
Oracle.query_fee(o : oracle('a, 'b)) : int\n

Returns the query fee of the oracle

"},{"location":"sophia_stdlib/#query","title":"query","text":"
Oracle.query(o : oracle('a, 'b), q : 'a, qfee : int, qttl : Chain.ttl, rttl : Chain.ttl) : oracle_query('a, 'b)\n

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.

"},{"location":"sophia_stdlib/#get_answer","title":"get_answer","text":"
Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)\n

Checks what is the optional query answer

"},{"location":"sophia_stdlib/#expiry","title":"expiry","text":"
Oracle.expiry(o : oracle('a, 'b)) : int\n

Ask the oracle when it expires. The result is the block height at which it will happen.

"},{"location":"sophia_stdlib/#check","title":"check","text":"
Oracle.check(o : oracle('a, 'b)) : bool\n

Returns true iff the oracle o exists and has correct type

"},{"location":"sophia_stdlib/#check_query","title":"check_query","text":"
Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool\n

It returns true iff the oracle query exist and has the expected type.

"},{"location":"sophia_stdlib/#includable-namespaces","title":"Includable namespaces","text":"

These need to be explicitly included (with .aes suffix)

"},{"location":"sophia_stdlib/#bitwise","title":"Bitwise","text":"

Bitwise operations on arbitrary precision integers.

"},{"location":"sophia_stdlib/#bsr","title":"bsr","text":"
Bitwise.bsr(n : int, x : int) : int\n

Logical bit shift x right n positions.

"},{"location":"sophia_stdlib/#bsl","title":"bsl","text":"
Bitwise.bsl(n : int, x : int) : int\n

Logical bit shift x left n positions.

"},{"location":"sophia_stdlib/#bsli","title":"bsli","text":"
Bitwise.bsli(n : int, x : int, lim : int) : int\n

Logical bit shift x left n positions, limit to lim bits.

"},{"location":"sophia_stdlib/#band","title":"band","text":"
Bitwise.band(x : int, y : int) : int\n

Bitwise and of x and y.

"},{"location":"sophia_stdlib/#bor","title":"bor","text":"
Bitwise.bor(x : int, y : int) : int\n

Bitwise or of x and y.

"},{"location":"sophia_stdlib/#bxor","title":"bxor","text":"
Bitwise.bxor(x : int, y : int) : int\n

Bitwise xor of x and y.

"},{"location":"sophia_stdlib/#bnot","title":"bnot","text":"
Bitwise.bnot(x : int) : int\n

Bitwise not of x. Defined and implemented as bnot(x) = bxor(x, -1).

"},{"location":"sophia_stdlib/#uband","title":"uband","text":"
Bitwise.uband(x : int, y : int) : int\n

Bitwise and of non-negative numbers x and y.

"},{"location":"sophia_stdlib/#ubor","title":"ubor","text":"
Bitwise.ubor(x : int, y : int) : int\n

Bitwise or of non-negative x and y.

"},{"location":"sophia_stdlib/#ubxor","title":"ubxor","text":"
Bitwise.ubxor(x : int, y : int) : int\n

Bitwise xor of non-negative x and y.

"},{"location":"sophia_stdlib/#bls12_381","title":"BLS12_381","text":""},{"location":"sophia_stdlib/#types_2","title":"Types","text":""},{"location":"sophia_stdlib/#fr","title":"fr","text":"

Built-in (Montgomery) integer representation 32 bytes

"},{"location":"sophia_stdlib/#fp","title":"fp","text":"

Built-in (Montgomery) integer representation 48 bytes

"},{"location":"sophia_stdlib/#fp2","title":"fp2","text":"
record fp2 = { x1 : fp, x2 : fp }`\n
"},{"location":"sophia_stdlib/#g1","title":"g1","text":"
record g1  = { x : fp, y : fp, z : fp }\n
"},{"location":"sophia_stdlib/#g2","title":"g2","text":"
record g2  = { x : fp2, y : fp2, z : fp2 }\n
"},{"location":"sophia_stdlib/#gt","title":"gt","text":"
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 }\n
"},{"location":"sophia_stdlib/#functions_2","title":"Functions","text":""},{"location":"sophia_stdlib/#pairing_check","title":"pairing_check","text":"
BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool\n

Pairing check of a list of points, xs and ys should be of equal length.

"},{"location":"sophia_stdlib/#int_to_fr","title":"int_to_fr","text":"
BLS12_381.int_to_fr(x : int) : fr\n

Convert an integer to an fr - a 32 bytes internal (Montgomery) integer representation.

"},{"location":"sophia_stdlib/#int_to_fp","title":"int_to_fp","text":"
BLS12_381.int_to_fp(x : int) : fp\n

Convert an integer to an fp - a 48 bytes internal (Montgomery) integer representation.

"},{"location":"sophia_stdlib/#fr_to_int","title":"fr_to_int","text":"
BLS12_381.fr_to_int(x : fr)  : int\n

Convert a fr value into an integer.

"},{"location":"sophia_stdlib/#fp_to_int","title":"fp_to_int","text":"
BLS12_381.fp_to_int(x : fp)  : int\n

Convert a fp value into an integer.

"},{"location":"sophia_stdlib/#mk_g1","title":"mk_g1","text":"
BLS12_381.mk_g1(x : int, y : int, z : int) : g1\n

Construct a g1 point from three integers.

"},{"location":"sophia_stdlib/#mk_g2","title":"mk_g2","text":"
BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2\n

Construct a g2 point from six integers.

"},{"location":"sophia_stdlib/#g1_neg","title":"g1_neg","text":"
BLS12_381.g1_neg(p : g1) : g1\n

Negate a g1 value.

"},{"location":"sophia_stdlib/#g1_norm","title":"g1_norm","text":"
BLS12_381.g1_norm(p : g1) : g1\n

Normalize a g1 value.

"},{"location":"sophia_stdlib/#g1_valid","title":"g1_valid","text":"
BLS12_381.g1_valid(p : g1) : bool\n

Check that a g1 value is a group member.

"},{"location":"sophia_stdlib/#g1_is_zero","title":"g1_is_zero","text":"
BLS12_381.g1_is_zero(p : g1) : bool\n

Check if a g1 value corresponds to the zero value of the group.

"},{"location":"sophia_stdlib/#g1_add","title":"g1_add","text":"
BLS12_381.g1_add(p : g1, q : g1) : g1\n

Add two g1 values.

"},{"location":"sophia_stdlib/#g1_mul","title":"g1_mul","text":"
BLS12_381.g1_mul(k : fr, p : g1) : g1\n

Scalar multiplication for g1.

"},{"location":"sophia_stdlib/#g2_neg","title":"g2_neg","text":"
BLS12_381.g2_neg(p : g2) : g2\n

Negate a g2 value.

"},{"location":"sophia_stdlib/#g2_norm","title":"g2_norm","text":"
BLS12_381.g2_norm(p : g2) : g2\n

Normalize a g2 value.

"},{"location":"sophia_stdlib/#g2_valid","title":"g2_valid","text":"
BLS12_381.g2_valid(p : g2) : bool\n

Check that a g2 value is a group member.

"},{"location":"sophia_stdlib/#g2_is_zero","title":"g2_is_zero","text":"
BLS12_381.g2_is_zero(p : g2) : bool\n

Check if a g2 value corresponds to the zero value of the group.

"},{"location":"sophia_stdlib/#g2_add","title":"g2_add","text":"
BLS12_381.g2_add(p : g2, q : g2) : g2\n

Add two g2 values.

"},{"location":"sophia_stdlib/#g2_mul","title":"g2_mul","text":"
BLS12_381.g2_mul(k : fr, p : g2) : g2\n

Scalar multiplication for g2.

"},{"location":"sophia_stdlib/#gt_inv","title":"gt_inv","text":"
BLS12_381.gt_inv(p : gt) : gt\n

Invert a gt value.

"},{"location":"sophia_stdlib/#gt_add","title":"gt_add","text":"
BLS12_381.gt_add(p : gt, q : gt) : gt\n

Add two gt values.

"},{"location":"sophia_stdlib/#gt_mul","title":"gt_mul","text":"
BLS12_381.gt_mul(p : gt, q : gt) : gt\n

Multiply two gt values.

"},{"location":"sophia_stdlib/#gt_pow","title":"gt_pow","text":"
BLS12_381.gt_pow(p : gt, k : fr) : gt\n

Calculate exponentiation p ^ k.

"},{"location":"sophia_stdlib/#gt_is_one","title":"gt_is_one","text":"
BLS12_381.gt_is_one(p : gt) : bool\n

Compare a gt value to the unit value of the Gt group.

"},{"location":"sophia_stdlib/#pairing","title":"pairing","text":"
BLS12_381.pairing(p : g1, q : g2) : gt\n

Compute the pairing of a g1 value and a g2 value.

"},{"location":"sophia_stdlib/#miller_loop","title":"miller_loop","text":"
BLS12_381.miller_loop(p : g1, q : g2) : gt\n

Do the Miller loop stage of pairing for g1 and g2.

"},{"location":"sophia_stdlib/#final_exp","title":"final_exp","text":"
BLS12_381.final_exp(p : gt) : gt\n

Perform the final exponentiation step of pairing for a gt value.

"},{"location":"sophia_stdlib/#func","title":"Func","text":"

Functional combinators.

"},{"location":"sophia_stdlib/#id","title":"id","text":"
Func.id(x : 'a) : 'a\n

Identity function. Returns its argument.

"},{"location":"sophia_stdlib/#const","title":"const","text":"
Func.const(x : 'a) : 'b => 'a = (y) => x\n

Constant function constructor. Given x returns a function that returns x regardless of its argument.

"},{"location":"sophia_stdlib/#flip","title":"flip","text":"
Func.flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c\n

Switches order of arguments of arity 2 function.

"},{"location":"sophia_stdlib/#comp","title":"comp","text":"
Func.comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c\n

Function composition. comp(f, g)(x) == f(g(x)).

"},{"location":"sophia_stdlib/#pipe","title":"pipe","text":"
Func.pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c\n

Flipped function composition. pipe(f, g)(x) == g(f(x)).

"},{"location":"sophia_stdlib/#rapply","title":"rapply","text":"
Func.rapply(x : 'a, f : 'a => 'b) : 'b\n

Reverse application. rapply(x, f) == f(x).

"},{"location":"sophia_stdlib/#recur","title":"recur","text":"
Func.recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res\n

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))\n

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\nlet factorial_t(n, step) =\n  let fac(rec, args) =\n    let (n, step) = args\n    if(n < 2) 1 else n * rec((n - step, step))\n  recur(fac)((n, step))\n\n// curried version\nlet factorial_c(n, step) =\n  let fac(rec, n) = (step) =>\n    if(n < 2) 1 else n * rec(n - 1)(step)\n  recur(fac)(n)(step)\n
"},{"location":"sophia_stdlib/#iter","title":"iter","text":"
Func.iter(n : int, f : 'a => 'a) : 'a => 'a\n

nth composition of f with itself, for instance iter(3, f) is equivalent to (x) => f(f(f(x))).

"},{"location":"sophia_stdlib/#curry","title":"curry","text":"
Func.curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c)\nFunc.curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd))\n

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.

"},{"location":"sophia_stdlib/#uncurry","title":"uncurry","text":"
Func.uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c\nFunc.uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd\n

Opposite to curry.

"},{"location":"sophia_stdlib/#tuplify","title":"tuplify","text":"
Func.tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c\nFunc.tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd\n

Turns a function that takes n arguments into a function that takes an n-tuple.

"},{"location":"sophia_stdlib/#untuplify","title":"untuplify","text":"
Func.untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c\nFunc.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd\n

Opposite to tuplify.

"},{"location":"sophia_stdlib/#frac","title":"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.

"},{"location":"sophia_stdlib/#types_3","title":"Types","text":""},{"location":"sophia_stdlib/#frac_1","title":"frac","text":"
datatype frac = Pos(int, int) | Zero | Neg(int, int)\n

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.

"},{"location":"sophia_stdlib/#functions_3","title":"Functions","text":""},{"location":"sophia_stdlib/#make_frac","title":"make_frac","text":"

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.

"},{"location":"sophia_stdlib/#num","title":"num","text":"

Frac.num(f : frac) : int

Returns the numerator of a fraction.

"},{"location":"sophia_stdlib/#den","title":"den","text":"

Frac.den(f : frac) : int

Returns the denominator of a fraction.

"},{"location":"sophia_stdlib/#to_pair","title":"to_pair","text":"

Frac.to_pair(f : frac) : int * int

Turns a fraction into a pair of numerator and denominator.

"},{"location":"sophia_stdlib/#sign","title":"sign","text":"

Frac.sign(f : frac) : int

Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively.

"},{"location":"sophia_stdlib/#to_str_3","title":"to_str","text":"

Frac.to_str(f : frac) : string

Conversion to string. Does not display division by 1 or denominator if equals zero.

"},{"location":"sophia_stdlib/#simplify","title":"simplify","text":"

Frac.simplify(f : frac) : frac

Reduces fraction to normal form if for some reason it is not in it.

"},{"location":"sophia_stdlib/#eq","title":"eq","text":"

Frac.eq(a : frac, b : frac) : bool

Checks if a is equal to b.

"},{"location":"sophia_stdlib/#neq","title":"neq","text":"

Frac.neq(a : frac, b : frac) : bool

Checks if a is not equal to b.

"},{"location":"sophia_stdlib/#geq","title":"geq","text":"

Frac.geq(a : frac, b : frac) : bool

Checks if a is greater or equal to b.

"},{"location":"sophia_stdlib/#leq","title":"leq","text":"

Frac.leq(a : frac, b : frac) : bool

Checks if a is lesser or equal to b.

"},{"location":"sophia_stdlib/#gt_1","title":"gt","text":"

Frac.gt(a : frac, b : frac) : bool

Checks if a is greater than b.

"},{"location":"sophia_stdlib/#lt","title":"lt","text":"

Frac.lt(a : frac, b : frac) : bool

Checks if a is lesser than b.

"},{"location":"sophia_stdlib/#min","title":"min","text":"

Frac.min(a : frac, b : frac) : frac

Chooses lesser of the two fractions.

"},{"location":"sophia_stdlib/#max","title":"max","text":"

Frac.max(a : frac, b : frac) : frac

Chooses greater of the two fractions.

"},{"location":"sophia_stdlib/#abs","title":"abs","text":"

Frac.abs(f : frac) : frac

Absolute value.

"},{"location":"sophia_stdlib/#from_int","title":"from_int","text":"

Frac.from_int(n : int) : frac

From integer conversion. Effectively make_frac(n, 1).

"},{"location":"sophia_stdlib/#floor","title":"floor","text":"

Frac.floor(f : frac) : int

Rounds a fraction to the nearest lesser or equal integer.

"},{"location":"sophia_stdlib/#ceil","title":"ceil","text":"

Frac.ceil(f : frac) : int

Rounds a fraction to the nearest greater or equal integer.

"},{"location":"sophia_stdlib/#round_to_zero","title":"round_to_zero","text":"

Frac.round_to_zero(f : frac) : int

Rounds a fraction towards zero. Effectively ceil if lesser than zero and floor if greater.

"},{"location":"sophia_stdlib/#round_from_zero","title":"round_from_zero","text":"

Frac.round_from_zero(f : frac) : int

Rounds a fraction from zero. Effectively ceil if greater than zero and floor if lesser.

"},{"location":"sophia_stdlib/#round","title":"round","text":"

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.

"},{"location":"sophia_stdlib/#add","title":"add","text":"

Frac.add(a : frac, b : frac) : frac

Sum of the fractions.

"},{"location":"sophia_stdlib/#neg","title":"neg","text":"

Frac.neg(a : frac) : frac

Negation of the fraction.

"},{"location":"sophia_stdlib/#sub","title":"sub","text":"

Frac.sub(a : frac, b : frac) : frac

Subtraction of two fractions.

"},{"location":"sophia_stdlib/#inv","title":"inv","text":"

Frac.inv(a : frac) : frac

Inverts a fraction. Throws error if a is zero.

"},{"location":"sophia_stdlib/#mul","title":"mul","text":"

Frac.mul(a : frac, b : frac) : frac

Multiplication of two fractions.

"},{"location":"sophia_stdlib/#div","title":"div","text":"

Frac.div(a : frac, b : frac) : frac

Division of two fractions.

"},{"location":"sophia_stdlib/#int_exp","title":"int_exp","text":"

Frac.int_exp(b : frac, e : int) : frac

Takes b to the power of e. The exponent can be a negative value.

"},{"location":"sophia_stdlib/#optimize","title":"optimize","text":"

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.

"},{"location":"sophia_stdlib/#is_sane","title":"is_sane","text":"

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 fracs from the surface you should report it as a bug

If you expect getting calls with malformed fracs in your contract, you should use this function to verify the input.

"},{"location":"sophia_stdlib/#list","title":"List","text":"

This module contains common operations on lists like constructing, querying, traversing etc.

"},{"location":"sophia_stdlib/#is_empty","title":"is_empty","text":"
List.is_empty(l : list('a)) : bool\n

Returns true iff the list is equal to [].

"},{"location":"sophia_stdlib/#first","title":"first","text":"
List.first(l : list('a)) : option('a)\n

Returns Some of the first element of a list or None if the list is empty.

"},{"location":"sophia_stdlib/#tail","title":"tail","text":"
List.tail(l : list('a)) : option(list('a))\n

Returns Some of a list without its first element or None if the list is empty.

"},{"location":"sophia_stdlib/#last","title":"last","text":"
List.last(l : list('a)) : option('a)\n

Returns Some of the last element of a list or None if the list is empty.

"},{"location":"sophia_stdlib/#contains","title":"contains","text":"

List.contains(e : 'a, l : list('a)) : bool\n
Checks if list l contains element e. Equivalent to List.find(x => x == e, l) != None.

"},{"location":"sophia_stdlib/#find","title":"find","text":"
List.find(p : 'a => bool, l : list('a)) : option('a)\n

Finds first element of l fulfilling predicate p as Some or None if no such element exists.

"},{"location":"sophia_stdlib/#find_indices","title":"find_indices","text":"
List.find_indices(p : 'a => bool, l : list('a)) : list(int)\n

Returns list of all indices of elements from l that fulfill the predicate p.

"},{"location":"sophia_stdlib/#nth","title":"nth","text":"
List.nth(n : int, l : list('a)) : option('a)\n

Gets nth element of l as Some or None if l is shorter than n + 1 or n is negative.

"},{"location":"sophia_stdlib/#get","title":"get","text":"
List.get(n : int, l : list('a)) : 'a\n

Gets nth element of l forcefully, throwing and error if l is shorter than n + 1 or n is negative.

"},{"location":"sophia_stdlib/#length","title":"length","text":"
List.length(l : list('a)) : int\n

Returns length of a list.

"},{"location":"sophia_stdlib/#from_to","title":"from_to","text":"
List.from_to(a : int, b : int) : list(int)\n

Creates an ascending sequence of all integer numbers between a and b (including a and b).

"},{"location":"sophia_stdlib/#from_to_step","title":"from_to_step","text":"
List.from_to_step(a : int, b : int, step : int) : list(int)\n

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.

"},{"location":"sophia_stdlib/#replace_at","title":"replace_at","text":"
List.replace_at(n : int, e : 'a, l : list('a)) : list('a)\n

Replaces nth element of l with e. Throws an error if n is negative or would cause an overflow.

"},{"location":"sophia_stdlib/#insert_at","title":"insert_at","text":"
List.insert_at(n : int, e : 'a, l : list('a)) : list('a)\n

Inserts e into l to be on position n by shifting following elements further. For instance,

insert_at(2, 9, [1,2,3,4])\n
will yield [1,2,9,3,4].

"},{"location":"sophia_stdlib/#insert_by","title":"insert_by","text":"
List.insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a)\n

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])\n
will yield [1,2,3,4,5,6,7]

"},{"location":"sophia_stdlib/#foldr","title":"foldr","text":"
List.foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b\n

Right fold of a list. Assuming l = [x, y, z] will return f(x, f(y, f(z, nil))). Not tail recursive.

"},{"location":"sophia_stdlib/#foldl","title":"foldl","text":"
List.foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b\n

Left fold of a list. Assuming l = [x, y, z] will return f(f(f(acc, x), y), z). Tail recursive.

"},{"location":"sophia_stdlib/#foreach","title":"foreach","text":"
List.foreach(l : list('a), f : 'a => unit) : unit\n

Evaluates f on each element of a list.

"},{"location":"sophia_stdlib/#reverse","title":"reverse","text":"
List.reverse(l : list('a)) : list('a)\n

Returns a copy of l with reversed order of elements.

"},{"location":"sophia_stdlib/#map_1","title":"map","text":"
List.map(f : 'a => 'b, l : list('a)) : list('b)\n

Maps function f over a list. For instance

map((x) => x == 0, [1, 2, 0, 3, 0])\n
will yield [false, false, true, false, true]

"},{"location":"sophia_stdlib/#flat_map","title":"flat_map","text":"
List.flat_map(f : 'a => list('b), l : list('a)) : list('b)\n

Maps f over a list and then flattens it. For instance

flat_map((x) => [x, x * 10], [1, 2, 3])\n
will yield [1, 10, 2, 20, 3, 30]

"},{"location":"sophia_stdlib/#filter","title":"filter","text":"
List.filter(p : 'a => bool, l : list('a)) : list('a)\n

Filters out elements of l that fulfill predicate p. For instance

filter((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3])\n
will yield [1, 1, 2]

"},{"location":"sophia_stdlib/#take","title":"take","text":"
List.take(n : int, l : list('a)) : list('a)\n

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.

"},{"location":"sophia_stdlib/#drop","title":"drop","text":"
List.drop(n : int, l : list('a)) : list('a)\n

Removes n first elements of l. Fails if n is negative. If n is greater than length of a list it will return [].

"},{"location":"sophia_stdlib/#take_while","title":"take_while","text":"
List.take_while(p : 'a => bool, l : list('a)) : list('a)\n

Returns longest prefix of l in which all elements fulfill p.

"},{"location":"sophia_stdlib/#drop_while","title":"drop_while","text":"
List.drop_while(p : 'a => bool, l : list('a)) : list('a)\n

Removes longest prefix from l in which all elements fulfill p.

"},{"location":"sophia_stdlib/#partition","title":"partition","text":"
List.partition(p : 'a => bool, l : list('a)) : (list('a) * list('a))\n

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])\n
will yield ([1, 1, 2], [-1, -2, 0, -3])

"},{"location":"sophia_stdlib/#flatten","title":"flatten","text":"
List.flatten(ll : list(list('a))) : list('a)\n

Flattens a list of lists into a one list.

"},{"location":"sophia_stdlib/#all_1","title":"all","text":"
List.all(p : 'a => bool, l : list('a)) : bool\n

Checks if all elements of a list fulfill predicate p.

"},{"location":"sophia_stdlib/#any","title":"any","text":"
List.any(p : 'a => bool, l : list('a)) : bool\n

Checks if any element of a list fulfills predicate p.

"},{"location":"sophia_stdlib/#sum_1","title":"sum","text":"
List.sum(l : list(int)) : int\n

Sums elements of a list. Returns 0 if the list is empty.

"},{"location":"sophia_stdlib/#product","title":"product","text":"
List.product(l : list(int)) : int\n

Multiplies elements of a list. Returns 1 if the list is empty.

"},{"location":"sophia_stdlib/#zip_with","title":"zip_with","text":"
List.zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c)\n

\"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])\n
will yield [2,4]

"},{"location":"sophia_stdlib/#zip","title":"zip","text":"
List.zip(l1 : list('a), l2 : list('b)) : list('a * 'b)\n

Special case of zip_with where the zipping function is (a, b) => (a, b).

"},{"location":"sophia_stdlib/#unzip","title":"unzip","text":"
List.unzip(l : list('a * 'b)) : list('a) * list('b)\n

Opposite to the zip operation. Takes a list of pairs and returns pair of lists with respective elements on same indices.

"},{"location":"sophia_stdlib/#merge","title":"merge","text":"
List.merge(lesser_cmp : ('a, 'a) => bool, l1 : list('a), l2 : list('a)) : list('a)\n

Merges two sorted lists into a single sorted list. O(length(l1) + length(l2))

"},{"location":"sophia_stdlib/#sort","title":"sort","text":"
List.sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a)\n

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))).

"},{"location":"sophia_stdlib/#intersperse","title":"intersperse","text":"
List.intersperse(delim : 'a, l : list('a)) : list('a)\n

Intersperses elements of l with delim. Does nothing on empty lists and singletons. For instance

intersperse(0, [1, 2, 3, 4])\n
will yield [1, 0, 2, 0, 3, 0, 4]

"},{"location":"sophia_stdlib/#enumerate","title":"enumerate","text":"
List.enumerate(l : list('a)) : list(int * 'a)\n

Equivalent to zip with [0..length(l)], but slightly faster.

"},{"location":"sophia_stdlib/#option","title":"Option","text":"

Common operations on option types and lists of options.

"},{"location":"sophia_stdlib/#is_none","title":"is_none","text":"
Option.is_none(o : option('a)) : bool\n

Returns true iff o == None

"},{"location":"sophia_stdlib/#is_some","title":"is_some","text":"
Option.is_some(o : option('a)) : bool\n

Returns true iff o is not None.

"},{"location":"sophia_stdlib/#match","title":"match","text":"
Option.match(n : 'b, s : 'a => 'b, o : option('a)) : 'b\n

Behaves like pattern matching on option using two case functions.

"},{"location":"sophia_stdlib/#default","title":"default","text":"
Option.default(def : 'a, o : option('a)) : 'a\n

Escapes option wrapping by providing default value for None.

"},{"location":"sophia_stdlib/#force","title":"force","text":"
Option.force(o : option('a)) : 'a\n

Forcefully escapes the option wrapping assuming it is Some. Aborts on None.

"},{"location":"sophia_stdlib/#force_msg","title":"force_msg","text":"
Option.force_msg(o : option('a), err : string) : 'a\n

Forcefully escapes the option wrapping assuming it is Some. Aborts with err error message on None.

"},{"location":"sophia_stdlib/#contains_1","title":"contains","text":"

Option.contains(e : 'a, o : option('a)) : bool\n
Returns true if and only if o contains element equal to e. Equivalent to Option.match(false, x => x == e, o).

"},{"location":"sophia_stdlib/#on_elem","title":"on_elem","text":"
Option.on_elem(o : option('a), f : 'a => unit) : unit\n

Evaluates f on element under Some. Does nothing on None.

"},{"location":"sophia_stdlib/#map_2","title":"map","text":"
Option.map(f : 'a => 'b, o : option('a)) : option('b)\n

Maps element under Some. Leaves None unchanged.

"},{"location":"sophia_stdlib/#map2","title":"map2","text":"
Option.map2(f : ('a, 'b) => 'c, o1 : option('a), o2 : option('b)) : option('c)\n

Applies arity 2 function over two options' 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))\n
will yield Some(3) and
map2((a, b) => a + b, Some(1), None)\n
will yield None.

"},{"location":"sophia_stdlib/#map3","title":"map3","text":"
Option.map3(f : ('a, 'b, 'c) => 'd, o1 : option('a), o2 : option('b), o3 : option('c)) : option('d)\n

Same as map2 but with arity 3 function.

"},{"location":"sophia_stdlib/#app_over","title":"app_over","text":"
Option.app_over(f : option ('a => 'b), o : option('a)) : option('b)\n

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))\n
will yield Some(2) and
app_over(Some((x) => x + 1), None)\n
will yield None.

"},{"location":"sophia_stdlib/#flat_map_1","title":"flat_map","text":"
Option.flat_map(f : 'a => option('b), o : option('a)) : option('b)\n

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))\n
will yield Some(2) and
flat_map((x) => Some(x + 1), None)\n
will yield None.

"},{"location":"sophia_stdlib/#to_list_1","title":"to_list","text":"
Option.to_list(o : option('a)) : list('a)\n

Turns o into an empty (if None) or singleton (if Some) list.

"},{"location":"sophia_stdlib/#filter_options","title":"filter_options","text":"
Option.filter_options(l : list(option('a))) : list('a)\n

Removes Nones from list and unpacks all remaining Somes. For instance

filter_options([Some(1), None, Some(2)])\n
will yield [1, 2].

"},{"location":"sophia_stdlib/#seq_options","title":"seq_options","text":"
Option.seq_options(l : list (option('a))) : option (list('a))\n

Tries to unpack all elements of a list from Somes. Returns None if at least element of l is None. For instance

seq_options([Some(1), Some(2)])\n
will yield Some([1, 2]), but
seq_options([Some(1), Some(2), None])\n
will yield None.

"},{"location":"sophia_stdlib/#choose","title":"choose","text":"
Option.choose(o1 : option('a), o2 : option('a)) : option('a)\n

Out of two options choose the one that is Some, or None if both are Nones.

"},{"location":"sophia_stdlib/#choose_first","title":"choose_first","text":"
Option.choose_first(l : list(option('a))) : option('a)\n

Same as choose, but chooses from a list insted of two arguments.

"},{"location":"sophia_stdlib/#pair","title":"Pair","text":"

Common operations on 2-tuples.

"},{"location":"sophia_stdlib/#fst","title":"fst","text":"
Pair.fst(t : ('a * 'b)) : 'a\n

First element projection.

"},{"location":"sophia_stdlib/#snd","title":"snd","text":"
Pair.snd(t : ('a * 'b)) : 'b\n

Second element projection.

"},{"location":"sophia_stdlib/#map1","title":"map1","text":"
Pair.map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b)\n

Applies function over first element.

"},{"location":"sophia_stdlib/#map2_1","title":"map2","text":"
Pair.map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c)\n

Applies function over second element.

"},{"location":"sophia_stdlib/#bimap","title":"bimap","text":"
Pair.bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd)\n

Applies functions over respective elements.

"},{"location":"sophia_stdlib/#swap","title":"swap","text":"
Pair.swap(t : ('a * 'b)) : ('b * 'a)\n

Swaps elements.

"},{"location":"sophia_stdlib/#set_1","title":"Set","text":""},{"location":"sophia_stdlib/#types_4","title":"Types","text":"
record set('a) = { to_map : map('a, unit) }\n
"},{"location":"sophia_stdlib/#functions_4","title":"Functions","text":""},{"location":"sophia_stdlib/#new","title":"new","text":"
Set.new() : set('a)\n

Returns an empty set

"},{"location":"sophia_stdlib/#member_1","title":"member","text":"
member(e : 'a, s : set('a)) : bool\n

Checks if the element e is present in the set s

"},{"location":"sophia_stdlib/#insert","title":"insert","text":"
insert(e : 'a, s : set('a)) : set('a)\n

Inserts the element e in the set s

"},{"location":"sophia_stdlib/#delete_1","title":"delete","text":"
Set.delete(e : 'a, s : set('a)) : set('a)\n

Removes the element e from the set s

"},{"location":"sophia_stdlib/#size_1","title":"size","text":"
size(s : set('a)) : int\n

Returns the number of elements in the set s

"},{"location":"sophia_stdlib/#to_list_2","title":"to_list","text":"
Set.to_list(s : set('a)) : list('a)\n

Returns a list containing the elements of the set s

"},{"location":"sophia_stdlib/#from_list_1","title":"from_list","text":"
Set.from_list(l : list('a)) : set('a)\n

Turns the list l into a set

"},{"location":"sophia_stdlib/#filter_1","title":"filter","text":"
Set.filter(p : 'a => bool, s : set('a)) : set('a)\n

Filters out elements of s that fulfill predicate p

"},{"location":"sophia_stdlib/#fold","title":"fold","text":"
Set.fold(f : ('a, 'b) => 'b, acc : 'b, s : set('a)) : 'b\n

Folds the function f over every element in the set s and returns the final value of the accumulator acc.

"},{"location":"sophia_stdlib/#subtract","title":"subtract","text":"
Set.subtract(s1 : set('a), s2 : set('a)) : set('a)\n

Returns the elements of s1 that are not members of s2

"},{"location":"sophia_stdlib/#intersection_1","title":"intersection","text":"
Set.intersection(s1 : set('a), s2 : set('a)) : set('a)\n

Returns the intersection of the two sets s1 and s2

"},{"location":"sophia_stdlib/#intersection_list","title":"intersection_list","text":"
Set.intersection_list(sets : list(set('a))) : set('a)\n

Returns the intersection of all the sets in the given list

"},{"location":"sophia_stdlib/#union_1","title":"union","text":"
Set.union(s1 : set('a), s2 : set('a)) : set('a)\n

Returns the union of the two sets s1 and s2

"},{"location":"sophia_stdlib/#union_list","title":"union_list","text":"
Set.union_list(sets : list(set('a))) : set('a)\n

Returns the union of all the sets in the given list

"},{"location":"sophia_stdlib/#string","title":"String","text":"

Operations on the string type. A string is a UTF-8 encoded byte array.

"},{"location":"sophia_stdlib/#length_1","title":"length","text":"

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))

"},{"location":"sophia_stdlib/#concat_1","title":"concat","text":"
concat(s1 : string, s2 : string) : string\n

Concatenates s1 and s2.

"},{"location":"sophia_stdlib/#concats","title":"concats","text":"
concats(ss : list(string)) : string\n

Concatenates a list of strings.

"},{"location":"sophia_stdlib/#to_list_3","title":"to_list","text":"
to_list(s : string) : list(char)\n

Converts a string to a list of char - the code points are normalized, but composite characters are possibly converted to multiple chars. 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].

"},{"location":"sophia_stdlib/#from_list_2","title":"from_list","text":"
from_list(cs : list(char)) : string\n

Converts a list of characters into a normalized UTF-8 string.

"},{"location":"sophia_stdlib/#to_lower","title":"to_lower","text":"
to_lower(s : string) : string\n

Converts a string to lowercase.

"},{"location":"sophia_stdlib/#to_upper","title":"to_upper","text":"
to_upper(s : string) : string\n

Converts a string to uppercase.

"},{"location":"sophia_stdlib/#at","title":"at","text":"
at(ix : int, s : string) : option(char)\n

Returns the character/codepoint at (zero-based) index ix. Basically the equivalent to List.nth(ix, String.to_list(s)).

"},{"location":"sophia_stdlib/#split_1","title":"split","text":"
split(ix : int, s:string) : string * string\n

Splits a string at (zero-based) index ix.

"},{"location":"sophia_stdlib/#contains_2","title":"contains","text":"
contains(str : string, pat : string) : option(int)\n

Searches for pat in str, returning Some(ix) if pat is a substring of str starting at position ix, otherwise returns None.

"},{"location":"sophia_stdlib/#tokens","title":"tokens","text":"
tokens(str : string, pat : string) : list(string)\n

Splits str into tokens, pat is the divider of tokens.

"},{"location":"sophia_stdlib/#to_int_2","title":"to_int","text":"
to_int(s : string) : option(int)\n

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.

"},{"location":"sophia_stdlib/#sha3_1","title":"sha3","text":"
sha3(s : string) : hash\n

Computes the SHA3/Keccak hash of the string.

"},{"location":"sophia_stdlib/#sha256_1","title":"sha256","text":"
sha256(s : string) : hash\n

Computes the SHA256 hash of the string.

"},{"location":"sophia_stdlib/#blake2b_1","title":"blake2b","text":"
blake2b(s : string) : hash\n

Computes the Blake2B hash of the string.

"},{"location":"sophia_stdlib/#triple","title":"Triple","text":""},{"location":"sophia_stdlib/#fst_1","title":"fst","text":"
Triple.fst(t : ('a * 'b * 'c)) : 'a\n

First element projection.

"},{"location":"sophia_stdlib/#snd_1","title":"snd","text":"
Triple.snd(t : ('a * 'b * 'c)) : 'b\n

Second element projection.

"},{"location":"sophia_stdlib/#thd","title":"thd","text":"
Triple.thd(t : ('a * 'b * 'c)) : 'c\n

Third element projection.

"},{"location":"sophia_stdlib/#map1_1","title":"map1","text":"
Triple.map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c)\n

Applies function over first element.

"},{"location":"sophia_stdlib/#map2_2","title":"map2","text":"
Triple.map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c)\n

Applies function over second element.

"},{"location":"sophia_stdlib/#map3_1","title":"map3","text":"
Triple.map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm)\n

Applies function over third element.

"},{"location":"sophia_stdlib/#trimap","title":"trimap","text":"
Triple.trimap(f : 'a => 'x, g : 'b => 'y, h : 'c => 'z, t : ('a * 'b * 'c)) : ('x * 'y * 'z)\n

Applies functions over respective elements.

"},{"location":"sophia_stdlib/#swap_1","title":"swap","text":"
Triple.swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a)\n

Swaps first and third element.

"},{"location":"sophia_stdlib/#rotr","title":"rotr","text":"
Triple.rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b)\n

Cyclic rotation of the elements to the right.

"},{"location":"sophia_stdlib/#rotl","title":"rotl","text":"
Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a)\n

Cyclic rotation of the elements to the left.

"},{"location":"sophia_syntax/","title":"Syntax","text":""},{"location":"sophia_syntax/#lexical-syntax","title":"Lexical syntax","text":""},{"location":"sophia_syntax/#comments","title":"Comments","text":"

Single line comments start with // and block comments are enclosed in /* and */ and can be nested.

"},{"location":"sophia_syntax/#keywords","title":"Keywords","text":"
contract include let switch type record datatype if elif else function\nstateful payable true false mod public entrypoint private indexed namespace\ninterface main using as for hiding\n
"},{"location":"sophia_syntax/#tokens","title":"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.

"},{"location":"sophia_syntax/#layout-blocks","title":"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 =\nfunction foo() = 0  // no layout\nfunction bar() =    // layout block starts on next line\nlet x = foo()     // indented more than 2 spaces\nx\n+ 1              // the '+' is indented more than the 'x'\n
"},{"location":"sophia_syntax/#notation","title":"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 Xs.
  • Sep(X, S) is short for [X (S X)*], i.e. a possibly empty sequence of Xs separated by Ss.
  • Sep1(X, S) is short for X (S X)*, i.e. same as Sep, but must not be empty.
"},{"location":"sophia_syntax/#declarations","title":"Declarations","text":"

A Sophia file consists of a sequence of declarations in a layout block.

File ::= Block(TopDecl)\n\nTopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)\n| 'contract' 'interface' Con [Implement] '=' Block(Decl)\n| 'namespace' Con '=' Block(Decl)\n| '@compiler' PragmaOp Version\n| 'include' String\n| Using\n\nImplement ::= ':' Sep1(Con, ',')\n\nDecl ::= 'type'     Id ['(' TVar* ')'] '=' TypeAlias\n| 'record'   Id ['(' TVar* ')'] '=' RecordType\n| 'datatype' Id ['(' TVar* ')'] '=' DataType\n| 'let'      Id [':' Type]      '=' Expr\n| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)\n| Using\n\nFunDecl ::= Id ':' Type                             // Type signature\n| Id Args [':' Type] '=' Block(Stmt)      // Definition\n| Id Args [':' Type] Block(GuardedDef)    // Guarded definitions\n\nGuardedDef ::= '|' Sep1(Expr, ',') '=' Block(Stmt)\n\nUsing ::= 'using' Con ['as' Con] [UsingParts]\nUsingParts ::= 'for' '[' Sep1(Id, ',') ']'\n| 'hiding' '[' Sep1(Id, ',') ']'\n\nPragmaOp ::= '<' | '=<' | '==' | '>=' | '>'\nVersion  ::= Sep1(Int, '.')\n\nEModifier ::= 'payable' | 'stateful'\nFModifier ::= 'stateful' | 'private'\n\nArgs ::= '(' Sep(Pattern, ',') ')'\n

Contract declarations must appear at the top-level.

For example,

contract Test =\ntype t = int\nentrypoint add (x : t, y : t) = x + y\n

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\nRecordType ::= '{' Sep(FieldType, ',') '}'\nDataType   ::= Sep1(ConDecl, '|')\n\nFieldType  ::= Id ':' Type\nConDecl    ::= Con ['(' Sep1(Type, ',') ')']\n

For example,

record   point('a) = {x : 'a, y : 'a}\ndatatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))\ntype     int_shape = shape(int)\n

"},{"location":"sophia_syntax/#types","title":"Types","text":"
Type ::= Domain '=>' Type             // Function type\n| Type '(' Sep(Type, ',') ')'  // Type application\n| '(' Type ')'                 // Parens\n| 'unit' | Sep(Type, '*')      // Tuples\n| Id | QId | TVar\n\nDomain ::= Type                       // Single argument\n| '(' Sep(Type, ',') ')'     // Multiple arguments\n

The function type arrow associates to the right.

Example,

'a => list('a) => (int * list('a))\n

"},{"location":"sophia_syntax/#statements","title":"Statements","text":"

Function bodies are blocks of statements, where a statement is one of the following

Stmt ::= 'switch' '(' Expr ')' Block(Case)\n| 'if' '(' Expr ')' Block(Stmt)\n| 'elif' '(' Expr ')' Block(Stmt)\n| 'else' Block(Stmt)\n| 'let' LetDef\n| Using\n| Expr\n\nLetDef ::= Id Args [':' Type] '=' Block(Stmt)   // Function definition\n| Pattern '=' Block(Stmt)              // Value definition\n\nCase    ::= Pattern '=>' Block(Stmt)\n| Pattern Block(GuardedCase)\n\nGuardedCase ::= '|' Sep1(Expr, ',') '=>' Block(Stmt)\n\nPattern ::= Expr\n

if statements can be followed by zero or more elif statements and an optional final else statement. For example,

let x : int = 4\nswitch(f(x))\nNone => 0\nSome(y) =>\nif(y > 10)\n\"too big\"\nelif(y < 3)\n\"too small\"\nelse\n\"just right\"\n
"},{"location":"sophia_syntax/#expressions","title":"Expressions","text":"
Expr ::= '(' LamArgs ')' '=>' Block(Stmt)   // Anonymous function    (x) => x + 1\n| '(' BinOp ')'                      // Operator lambda       (+)\n| 'if' '(' Expr ')' Expr 'else' Expr // If expression         if(x < y) y else x\n| Expr ':' Type                      // Type annotation       5 : int\n| Expr BinOp Expr                    // Binary operator       x + y\n| UnOp Expr                          // Unary operator        ! b\n| Expr '(' Sep(Expr, ',') ')'        // Application           f(x, y)\n| Expr '.' Id                        // Projection            state.x\n| Expr '[' Expr ']'                  // Map lookup            map[key]\n| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update  r{ fld[key].x = y }\n| '[' Sep(Expr, ',') ']'             // List                  [1, 2, 3]\n| '[' Expr '|' Sep(Generator, ',') ']'\n// List comprehension    [k | x <- [1], if (f(x)), let k = x+1]\n| '[' Expr '..' Expr ']'             // List range            [1..n]\n| '{' Sep(FieldUpdate, ',') '}'      // Record or map value   {x = 0, y = 1}, {[key] = val}\n| '(' Expr ')'                       // Parens                (1 + 2) * 3\n| '(' Expr '=' Expr ')'              // Assign pattern        (y = x::_)\n| Id | Con | QId | QCon              // Identifiers           x, None, Map.member, AELib.Token\n| Int | Bytes | String | Char        // Literals              123, 0xff, #00abc123, \"foo\", '%'\n| AccountAddress | ContractAddress   // Chain identifiers\n| OracleAddress | OracleQueryId      // Chain identifiers\n| '???'                              // Hole expression       1 + ???\n\nGenerator ::= Pattern '<-' Expr   // Generator\n| 'if' '(' Expr ')'   // Guard\n| LetDef              // Definition\n\nLamArgs ::= '(' Sep(LamArg, ',') ')'\nLamArg  ::= Id [':' Type]\n\nFieldUpdate ::= Path '=' Expr\nPath ::= Id                 // Record field\n| '[' Expr ']'       // Map key\n| Path '.' Id        // Nested record field\n| Path '[' Expr ']'  // Nested map key\n\nBinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='\n| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'\n| '|>'\nUnOp  ::= '-' | '!'\n
"},{"location":"sophia_syntax/#operators-types","title":"Operators types","text":"Operators Type - + * / mod ^ arithmetic operators ! && || logical operators == != < > =< >= comparison operators :: ++ list operators |> functional operators"},{"location":"sophia_syntax/#operator-precedence","title":"Operator precedence","text":"

In order of highest to lowest precedence.

Operators Associativity ! right ^ left * / mod left - (unary) right + - left :: ++ right < > =< >= == != none && right || right |> left"}]} \ No newline at end of file diff --git a/v7.4.0/sitemap.xml b/v7.4.0/sitemap.xml new file mode 100644 index 0000000..43df81a --- /dev/null +++ b/v7.4.0/sitemap.xml @@ -0,0 +1,48 @@ + + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + + None + 2023-09-05 + daily + + \ No newline at end of file diff --git a/v7.4.0/sitemap.xml.gz b/v7.4.0/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..e1ce3191b44832c1619a2c5575882588dcaa349a GIT binary patch literal 203 zcmV;+05ty}iwFq%;r3(#|8r?{Wo=<_E_iKh0PU1b4#FT1h4(!LVPC)+6Kg2lxpbum zAQW4Y7Ak{QZ!i6c=^0FCGjHC!%(oj}zPlE^v&qq^)w5v2}rO*K>NrrAYYG zx1a-xVG9LrLg-JF4#Pme1l`FVi1RfC)x-hlVp2wOED{uJJTR-Nc + + + + + + + + + + + + + + + + Sophia - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

This file has been moved here

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/sophia_examples/index.html b/v7.4.0/sophia_examples/index.html new file mode 100644 index 0000000..66a02c8 --- /dev/null +++ b/v7.4.0/sophia_examples/index.html @@ -0,0 +1,564 @@ + + + + + + + + + + + + + + + + + + + + + + Contract examples - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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.
    • +
    +
  • +
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/sophia_features/index.html b/v7.4.0/sophia_features/index.html new file mode 100644 index 0000000..aecae46 --- /dev/null +++ b/v7.4.0/sophia_features/index.html @@ -0,0 +1,2142 @@ + + + + + + + + + + + + + + + + + + + + + + Features - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

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:

+ +

These functions take variable number of arguments that must match the created +contract's init function. Beside that they take some additional named +arguments – please refer to their documentation for 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:

+
    +
  1. A payable contract or interface can implement a payable interface or a non-payable interface.
  2. +
  3. A non-payable contract or interface can only implement a non-payable interface, and cannot implement a payable interface.
  4. +
  5. A payable entrypoint can implement a payable entrypoint or a non-payable entrypoint.
  6. +
  7. A non-payable entrypoint can only implement a non-payable entrypoint, and cannot implement a payable entrypoint.
  8. +
  9. A non-stateful entrypoint can implement a stateful entrypoint or a non-stateful entrypoint.
  10. +
  11. A stateful entrypoint can only implement a stateful entrypoint, and cannot implement a non-stateful entrypoint.
  12. +
+

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, datatypes 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:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescriptionExample
intA 2-complement integer-1
addressæternity address, 32 bytesCall.origin
boolA Booleantrue
bitsA bit fieldBits.none
bytes(n)A byte array with n bytes#fedcba9876543210
stringAn array of bytes"Foo"
listA homogeneous immutable singly linked list.[1, 2, 3]
('a, 'b) => 'cA function. Parentheses can be skipped if there is only one argument(x : int, y : int) => x + y
tupleAn ordered heterogeneous array(42, "Foo", true)
recordAn immutable key value store with fixed key names and typed valuesrecord balance = { owner: address, value: int }
mapAn immutable key value store with dynamic mapping of keys of one type to values of one typetype accounts = map(string, address)
option('a)An optional value either None or Some('a)Some(42)
stateA user defined type holding the contract staterecord state = { owner: address, magic_key: bytes(4) }
eventAn append only list of blockchain events (or log entries)datatype event = EventX(indexed int, string)
hashA 32-byte hash - equivalent to bytes(32)
signatureA signature - equivalent to bytes(64)
Chain.ttlTime-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 'bOracle.register(acct, qfee, ttl)
oracle_query('a, 'b)A specific oracle queryOracle.query(o, q, qfee, qttl, rttl)
contractA user defined, typed, contract addressfunction call_remote(r : RemoteContract) = r.fun()
+

Literals

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeConstant/Literal example(s)
int-1, 2425, 4598275923475723498573485768
addressak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
booltrue, false
bitsBits.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
statestate{ owner = Call.origin, magic_key = #a298105f }
eventEventX(0, "Hello")
hash#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
signature#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
Chain.ttlFixedTTL(1050), RelativeTTL(50)
oracle('a, 'b)ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
oracle_query('a, 'b)oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
contractct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
+

Hole expression

+

Hole expressions, written as ???, are expressions that are used as a placeholder. During compilation, the compiler will generate a type error indication the type of the hole expression.

+
include "List.aes"
+contract C =
+    entrypoint f() =
+        List.sum(List.map(???, [1,2,3]))
+
+

A hole expression found in the example above will generate the error Found a hole of type `(int) => int`. This says that the compiler expects a function from int to int in place of the ??? placeholder.

+

Constants

+

Constants in Sophia are contract-level bindings that can be used in either contracts or namespaces. The value of a constant can be a literal, another constant, or arithmetic operations applied to other constants. Lists, tuples, maps, and records can also be used to define a constant as long as their elements are also constants.

+

The following visibility rules apply to constants: +* Constants defined inside a contract are private in that contract. Thus, cannot be accessed through instances of their defining contract. +* Constants defined inside a namespace are public. Thus, can be used in other contracts or namespaces. +* Constants cannot be defined inside a contract interface.

+

When a constant is shadowed, it can be accessed using its qualified name:

+
contract C =
+  let c = 1
+  entrypoint f() =
+    let c = 2
+    c + C.c  // the result is 3
+
+

The name of the constant must be an id; therefore, no pattern matching is allowed when defining a constant:

+
contract C
+  let x::y::_ = [1,2,3]  // this will result in an error
+
+

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 +æternity 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 ≤ 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 æternity 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 — while abort returns unused gas, a call to +exit burns it all.

+

Delegation signature

+

Some chain operations (Oracle.<operation> and AENS.<operation>) 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 æternity mainnet, etc.).

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/sophia_stdlib/index.html b/v7.4.0/sophia_stdlib/index.html new file mode 100644 index 0000000..4115b47 --- /dev/null +++ b/v7.4.0/sophia_stdlib/index.html @@ -0,0 +1,2258 @@ + + + + + + + + + + + + + + + + + + + + + + Standard library - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

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:

+ +

The following ones need to be included as regular files with .aes suffix, for example +

include "List.aes"
+

+ +

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 æternity +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, <signature : signature>) : 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, <signature : signature>) : 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, <signature : signature>) : 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, <signature : signature>) : 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 : option(map(string, AENS.pointee)), <signature : signature>) : 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).

+
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 – 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 – 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.

+
coinbase
+
Chain.coinbase : address
+
+

The address of the account that mined the current block.

+
difficulty
+
Chain.difficulty : int
+
+

The difficulty of the current block.

+
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.

+
gas_limit
+
Chain.gas_limit : int
+
+

The gas limit of the current block.

+
spend
+
Chain.spend(to : address, amount : int) : unit
+
+

Spend amount tokens to to. Will fail (and abort the contract) if contract +doesn't have amount tokens to transfer, or, if to is not payable.

+
timestamp
+
Chain.timestamp : int
+
+

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

+

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(<signature : bytes(64)>, 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(<signature : bytes(64)>, 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(<signature : bytes(64)>, 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 – 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
+
+

nth 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 – +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 – 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 – +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 fracs from the surface you should report it as a +bug

+

If you expect getting calls with malformed fracs 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 nth 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 nth 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 nth 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 options.

+

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 options' 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 Nones from list and unpacks all remaining Somes. 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 Somes. 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 options choose the one that is Some, or None if both are Nones.

+

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 chars. For example the +string "😜i̇" 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.

+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/v7.4.0/sophia_syntax/index.html b/v7.4.0/sophia_syntax/index.html new file mode 100644 index 0000000..44d5dd0 --- /dev/null +++ b/v7.4.0/sophia_syntax/index.html @@ -0,0 +1,983 @@ + + + + + + + + + + + + + + + + + + + + + + Syntax - æternity Sophia Language + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EscapeASCII
\b8
\t9
\n10
\v11
\f12
\r13
\e27
\xHexDigitsHexDigits
+

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 Xs.
  • +
  • Sep(X, S) is short for [X (S X)*], i.e. a possibly empty sequence of Xs + separated by Ss.
  • +
  • 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
+       | 'let'      Id [':' Type]      '=' Expr
+       | (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
+       | '???'                              // Hole expression       1 + ???
+
+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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorsType
- + * / mod ^arithmetic operators
! && ||logical operators
== != < > =< >=comparison operators
:: ++list operators
|>functional operators
+

Operator precedence

+

In order of highest to lowest precedence.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorsAssociativity
!right
^left
* / modleft
- (unary)right
+ -left
:: ++right
< > =< >= == !=none
&&right
||right
|>left
+ + + + + + +
+
+ + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/versions.json b/versions.json index 7e9c111..d6dcb15 100644 --- a/versions.json +++ b/versions.json @@ -1 +1 @@ -[{"version": "v7.3.0", "title": "v7.3.0", "aliases": ["latest"]}, {"version": "v7.2.1", "title": "v7.2.1", "aliases": []}, {"version": "v7.2.0", "title": "v7.2.0", "aliases": []}, {"version": "v7.1.0", "title": "v7.1.0", "aliases": []}, {"version": "v7.0.1", "title": "v7.0.1", "aliases": []}, {"version": "v7.0.0", "title": "v7.0.0", "aliases": []}, {"version": "v6.1.0", "title": "v6.1.0", "aliases": []}, {"version": "master", "title": "master", "aliases": []}] \ No newline at end of file +[{"version": "v7.4.0", "title": "v7.4.0", "aliases": ["latest"]}, {"version": "v7.3.0", "title": "v7.3.0", "aliases": []}, {"version": "v7.2.1", "title": "v7.2.1", "aliases": []}, {"version": "v7.2.0", "title": "v7.2.0", "aliases": []}, {"version": "v7.1.0", "title": "v7.1.0", "aliases": []}, {"version": "v7.0.1", "title": "v7.0.1", "aliases": []}, {"version": "v7.0.0", "title": "v7.0.0", "aliases": []}, {"version": "v6.1.0", "title": "v6.1.0", "aliases": []}, {"version": "master", "title": "master", "aliases": []}] \ No newline at end of file