Binding
This section presents the API of the automatically generated contract binding.
The Completium CLI command to generate a contract binding from the contract contract.tz
is:
completium-cli generate binding-ts contract.tz
When using the create project
command, binding generation is done with the following command (see here for more information):
npm run gen-binding
Deploy
The binding class provides the deploy
method to create a new instance of the contract on the configured network (see here for network configuration).
The deploy
method takes as many arguments as the number of contract parameters, plus a Parameters
object to set the originator account and optionally the initial value of contract balance.
For example, consider the following contract :
archetype example(owner : address)
variable s : string = "Hello binding"
entry set(v : string) { s := v }
The following example illustrates how to deploy it:
import { example } from `./binding/example.ts`
const alice = get_account('alice')
await example.deploy(alice.get_address(), { as : alice })
// display newly deployed contract:
console.log(await example.get_address())
The method is named originate
when binding is generated from a Michelson contract.
Storage getters
A getter method is generated for each storage element. Its name is made of prefix get_
followed by the storage element name. It does not take any argument, and the method is asynchronous (must be called with await
keyword).
It returns the storage value. Its type is the type mapped from the Archetype (or Michelson) one as presented in this table.
For example, consider the following elementary Michelson contract example.tz
whose storage is made of a string value s
and a nat value n
:
{
storage (pair (string %s) (nat %n));
parameter unit;
code { CDR;
NIL operation;
PAIR };
}
The generated contract binding class in example.ts
provides with two getter methods get_s
and get_n
. They are used for example as follow:
import { example } from `./binding/example.ts`
// ... deploy contract
// read storage
const s = await example.get_s() // s is typed string
const n = await example.get_n() // n is typed Nat
Big Map
Big map storage elements (either big_map
, iterable_big_map
or asset to big_map
) are an exception to the rule above. For each big map storage element, two functions are generated:
- a function that checks whether a big map has a given key value. It name is
has_
followed by the storage element name and_value
. Its argument is the key value. It returns a boolean value. - a function that retrieves the value associated to a key. It name is
get_
followed by the storage element name and_value
. Its argument is the key value. It returns the value associated to the key, orundefined
if not found.
For example, consider the following loan
asset declaration:
asset loan identified by id to big_map {
id : string;
subscriber : address;
principal : tez;
interest : rational = 2%;
creation : date = now;
}
The following two functions are generated:
async has_loan_value(key: string): Promise<boolean>
async get_loan_value(key: string): Promise<loan_value | undefined>
The loan_value
corresponds to the asset_value<loan>
archetype type. See below for more information.
Entry points
An asynchronous call method is generated for each entry point. Its name is the same as the contract's. It takes the same arguments as the contract's, plus a Parameters
object to set the caller account and optionally the amount of tez sent. It returns a CallResult
value.
For example, consider the following contract:
archetype example(owner : address)
variable s : string = "Hello binding"
entry set(v : string) { s := v }
The code below illustrates how to call the set
entry point:
import { example } from `./binding/example.ts`
// ... deploy ...
await example.set("Hello Documentation!", { as : alice })
Getters
A special treatment is operated for getter entry points. The difference with the call to entry point described above is the return value: it returns the callback value returned by the getter.
For example, consider the following getter entry point:
archetype example
getter getBar(s : string) : nat {
return length(s)
}
The following code calls the getBar
entry point:
const l = await example.getBar("Hello getter", { as : alice })
assert(l.equals(new Nat(12)))
Entry points' parameters
An asynchronous method is generated for each entry point to get the transaction parameter corresponding to the call to the entry point. Its name is made of the prefix get_
, followed by the entry point name and suffixed by _param
. It takes the same arguments as the contract's entry point.
The transaction parameter is then used as an argument of exec_batch
for batch execution of several entry points.
With the same example as above, consider the following contract:
archetype example(owner : address)
variable s : string = "Hello!"
entry set(v : string) { s := v }
The code below illustrates how to call the set
entry point:
import { example } from `./binding/example.ts`
await example.deploy({ as : alice })
const set_param1 = await example.get_set_param("Hello Documentation!", { as : alice })
const set_param2 = await example.get_set_param("Hello Binding!", { as : alice })
// exec batch
await exec_batch([set_param1, set_param2], { as : alice })
const s = await example.get_s()
assert(s == "Hello Binding!")
Views
An asynchronous method is generated for each contract's view. Its name is prefixed with view_
, followed by the name of the view. It takes the same arguments as the contract's view, plus a Parameters
object to set the caller account and optionally the amount of tez sent.
It returns a promise of the view's returned value.
For example, consider the following contract:
archetype example(owner : address)
variable s : string = "Hello!"
view get_s() { return s }
The code below illustrates how to call the get_s
view:
import { example } from `./binding/example.ts`
await example.deploy({ as : alice })
const s = await example.view_get_s()
assert(s == "Hello!")
Errors
The expect_to_fail
function is used to check that a call to a contract entry point fails as expected. Its second parameter is an error of Micheline
type.
Contract errors from sections or issued by divergent instructions, are generated in the binding object as the errors
field.
For example, consider the following contract:
archetype example
variable s : string = "Hello!"
entry set(v : string) {
require {
r0 : length(v) < 10
}
effect { s := v }
}
The code below illustrates how to setup a test that is expected to fail with r0
requirement:
import { example } from `./binding/example.ts`
import { expect_to_fail, get_account } from '@completium/experiment-ts'
const alice = get_account('alice')
await example.deploy({ as : alice })
async expect_to_fail(async () => {
await example.set("This is a too long message", { as : alice })
}, example.errors.r0)
Events
A class and a register method are generated for each event
declaration. The class name is the same as the event's name and it has the same fields.
The register method's is prefixed with register_
followed by the event name. It takes one argument that is a callback function called each time the event is emitted. It uses the event listener package. See here for an example.
The event listener is not available in mockup mode. See below for more information about how to work with events in mockup mode.
In mockup mode, events are available in the CallResult
object returned by the call to an entry point. The events
field is the list of events emitted by the call to the entry point.
For example, consider the following entry point:
archetype example
event HelloEvent {
msg : string
}
entry exec() {
emit<HelloEvent>({ "Hello from exec!" })
}
The following code illustrates how to test the event emission:
//...
const ref_event = new HelloEvent("Hello from exec!")
const res = await example.exec({as : alice});
assert(res.events.length == 1)
const res_event = HelloEvent.from_mich(res.events[0].payload);
assert(res_event.equals(ref_event))
Type bindings
This section presents how Archetype/Michelson types are bound to Typescript types.
Archetype
Michelson
Typescript
address
Not Available
Array
big_map
Array
bls12_381_fr
bls12_381_g1
bls12_381_g2
bool
boolean
bytes
chain_id
chest
chest_key
contract
timestamp
date
Not Available
Not Available
class
Not Available
int
Not Available
Array
key
key_hash
lambda
list
Array
map
Array
nat
option
or
Not Available
Not Available
class
sapling_state
sapling_transaction
set
Array
signature
string
string
mutez
ticket
pair
Array
unit
Tuple
An Archetype tuple (or a Michelson pair) is mapped to a Typescript tuple.
For example, consider the following tuple value in Archetype:
const t : (nat * string) = (0, "a string");
The t
value is then mapped to the following TS value:
const t : [ Nat, string ] = [ 0, new Nat("a string") ]
Map
An Archetype (Michleson) map is translated in Typescript as an array of pairs. It cannot be straightforwaldy associated to a Typescript Map
as the Map
's key type is limited to native type with natural order (such as string
or number
).
For example, consider the following Archetype map:
variable ledger : map<address, int> = []
It is translated to Typescript as:
const ledger : Array<[ Address, Int ]> = []
The following code is to retrieve the value in third position:
const v = ledger[2][1]
The lookup function to retrieve a value from a key is not provided.
Record
A class is generated for each record
declaration found in the Archetype contract. Its name is the same as the declaration's. A public class member is created for each record field, with the same name and a type mapped from the table above. The class constructor has one argument per record field, in the order of record declaration.
For example, consider the following record declaration:
record person {
first : string;
last : string;
age : nat;
}
Then a new person class is constructed with the code below:
const albert = new person("Albert", "Michelson", new Nat(170))
Fields may then be accessed like:
console.log(`Hello ${albert.first} ${albert.last}`) // "Hello Albert Michelson"
Asset
An asset collection is translated to an array of pairs of the asset key and the asset value. The asset value is translated to a class, in the same way as a record. Its name is the name of the asset suffixed by _value
.
For example, consider the following asset:
asset loan identified by id {
id : string;
subscriber : address;
principal : tez;
interest : rational = 2%;
creation : date = now;
}
The typescript type for the asset collection is:
Array<[ string, loan_value ]>
The constructor of loan_value
takes the same fields as the loan
asset value. The code below illustrates how to create an instance of loan_value
:
const lv = new loan_value(
alice.get_address(),
new Tez(10),
new Rational("3.14"),
new Date()
)
The lookup function to retrieve an asset value from a key is not provided.
Enum
A class is generated for each enum
declaration. Its name is the same as the declaration's. This class inherits from the abstract Enum
class. A class extend the enum class is generated for each enum's named label. The named label class's constructor has the same arguments as the declaration's.
For example, consider the following enum declaration:
enum float =
| Pos<nat * nat>
| Neg<nat * nat>
| Zero
Then the following classes are generated:
float
class that extendsEnum
Pos
class that extendsfloat
Neg
class that extendsfloat
Zero
class that extendsfloat
It may be used as:
const p : sign = new Pos(new Nat(3), new Nat(5))
const n : sign = new Neg(new Nat(6), new Nat(3))
const z : sign = new Zero()
Lambda
There is no dedicated type to lambda values. The default lower-level type Micheline
is then used.
The code below, extracted from the Multisig contract, illustrates the creation of a lambda as a JSON Micheline value:
const getCode = (
dest: Address,
entrypoint: string,
typ: string,
value: string): Micheline => {
const input = `{
DROP;
NIL operation;
PUSH address "${dest.toString()}";
CONTRACT %${entrypoint} ${typ};
IF_NONE
{ PUSH string "EntryNotFound";
FAILWITH }
{ };
PUSH mutez 0;
PUSH ${typ} ${value};
TRANSFER_TOKENS;
CONS;
}`;
return expr_micheline_to_json(input)
}
The expr_micheline_to_json
function is used to convert a string to a Micheline value.