This document is a reference for the Pact smart-contract language, designed for correct, transactional execution on a high-performance blockchain. For more background, please see the white paper.

Copyright (c) 2016, Stuart Popejoy. All Rights Reserved.

Concepts

Execution Modes

Pact is designed to be used in distinct execution modes to address the performance requirements of rapid linear execution on a blockchain. These are:

  1. Contract definition.
  2. Transaction execution.
  3. Queries and local execution.

Contract Definition

In this mode, a large amount of code is sent into the blockchain to establish the smart contract, as comprised of code (modules), tables (data), and keysets (authorization). This can also include “transactional” (database-modifying) code, for instance to initialize data.

For a given smart contract, these should all be sent as a single message into the blockchain, so that any error will rollback the entire smart contract as a unit.

Keyset definition

Keysets are customarily defined first, as they are used to specify admin authorization schemes for modules and tables.

Module declaration

Modules define the API for smart contracts, comprised of function and “pact” definitions. When a module is declared, all references to native functions or functions from other modules are resolved. Resolution failure results in transaction rollback.

Modules can be re-defined as controlled by their admin keyset. Module versioning is not supported, except by including a version sigil in the module name (e.g., “accounts-v1”).

Table Creation

Tables are created at the same time as modules, due to the tight relationship of database tables to code modules, as described in Table Guards.

There is no restriction on how many tables may be created. However, table names are not namespaced, so care must be taken to ensure unique names.

Tables are schema-less, so there is no need to drop or redefine tables. Table data can be migrated for new module code by simply executing the necessary data modification.

Transaction Execution

“Transactions” refer to business events enacted on the blockchain, like a payment, a sale, or a workflow step of a complex contractual agreement. A transaction is generally a single call to a module function. However there is no limit on how many statements can be executed. Indeed, the difference between “transactions” and “smart contract definition” is simply the kind of code executed, not any actual difference in the code evaluation.

Queries and Local Execution

Querying data is generally not a business event, and can involve data payloads that could impact performance, so querying is carried out as a local execution on the node receiving the message. Historical queries use a transaction ID as a point of reference, to avoid any race conditions and allow asynchronous query execution.

Transactional vs local execution is accomplished by targeting different API endpoints; pact code has no ability to distinguish between transactional and local execution.

Database Interaction

Pact presents a database metaphor reflecting the unique requirements of blockchain execution, which can be adapted to run on different back-ends.

Atomic execution

A single message sent into the blockchain to be evaluated by Pact is atomic: the transaction succeeds as a unit, or does not succeed at all, known as “transactions” in database literature. There is no explicit support for rollback handling, except in multi-step transactions.

Key-Row Model

Blockchain execution can be likened to OLTP (online transaction processing) database workloads, which favor denormalized data written to a single table. Pact’s data-access API reflects this by presenting a key-row model, where a row of column values is accessed by a single key.

As a result, Pact does not support joining tables, which is more suited for an OLAP (online analytical processing) database, populated from exports from the Pact database. This does not mean Pact cannot record transactions using relational techniques – for example, a Customer table whose keys are used in a Sales table would involve the code looking up the Customer record before writing to the Sales table.

Schema-less

Pact tables do not declare a schema. Modules are expected to enforce data access to ensure type safety (through the use of functions like is-string, is-decimal etc).

No Nulls

Pact has no concept of a NULL value in its database metaphor. The main function for computing on database results, with-read, will error if any column value is not found. Authors must ensure that values are present for any transactional read. This is a safety feature to ensure totality and avoid needless, unsafe control-flow surrounding null values.

Versioned History

The key-row model is augmented by every change to column values being versioned by transaction ID. For example, a table with three columns “name”, “age”, and “role” might update “name” in transaction #1, and “age” and “role” in transaction #2. Retreiving historical data will return just the change to “name” under transaction 1, and the change to “age” and “role” in transaction #2.

Back-ends

Pact guarantees identical, correct execution at the smart-contract layer within the blockchain. As a result, the backing store need not be identical on different consensus nodes. Pact’s implementation allows for integration of industrial RDBMSs, to assist large migrations onto a blockchain-based system, by facilitating bulk replication of data to downstream systems.

Keysets and Authorization

Pact is inspired by Bitcoin scripts to incorporate public-key authorization directly into smart contract execution and administration.

Keyset definition

Keysets are defined by reading definitions from the message payload. Keysets consist of a list of public keys and a keyset predicate.

Keyset Predicates

A keyset predicate references a function by name which will compare the public keys in the keyset to the key or keys used to sign the blockchain message. The function accepts two arguments, “count” and “matched”, where “count” is the number of keys in the keyset and “matched” is how many keys on the message signature matched a keyset key.

Support for multiple signatures is the responsibility of the blockchain layer, and is a powerful feature for Bitcoin-style “multisig” contracts (ie requiring at least two signatures to release funds).

Pact comes with built-in keyset predicates: keys-all, keys-any, keys-2. Module authors are free to define additional predicates.

Key rotation

Keysets can be rotated, but only by messages authorized against the current keyset definition and predicate. Once authorized, the keyset can be easily redefined.

Module Table Guards

When creating a table, a module name must also be specified. By this mechanism, tables are “guarded” or “encapsulated” by the module, such that direct access to the table via data-access functions is authorized by the module’s admin keyset. However, within module functions, table access is unconstrained. This gives contract authors great flexibility in designing data access, and is intended to enshrine the module as the main “user” data access API.

Row-level keysets

Keysets can be stored as a column value in a row, allowing for row-level authorization. The following code indicates how this might be achieved:

(defun create-account (id)
  (insert 'accounts id { "balance": 0.0, "keyset": (read-keyset "owner-keyset") }))

(defun read-balance (id)
  (with-read { "balance":= bal, "keyset":= ks }
    (with-keyset ks
      (format "Your balance is {}" bal))))

In the example, create-account reads a keyset definition from the message payload using read-keyset to store as “keyset” in the table. read-balance only allows that owner’s keyset to read the balance, by first enforcing the keyset using with-keyset.

Computational Model

Here we cover various aspects of Pact’s approach to computation.

Turing-Incomplete

Pact is turing-incomplete, in that there is no recursion (recursion is detected before execution and results in an error) and no ability to loop indefinitely. Pact does support operation on list structures via map, fold and filter, but since there is no ability to define infinite lists, these are necessarily bounded.

Turing-incompleteness allows Pact module loading to resolve all references in advance, meaning that instead of addressing functions in a lookup table, the function definition is directly injected (or “inlined”) into the callsite. This is an example of the performance advantages of a Turing-incomplete language.

Variables

Pact allows variable declarations in let expressions and bindings. Variables are immutable: they cannot be re-assigned or modified in-place. The most common variable declaration occurs in the with-read function, assigning variables to column values by name.

Module-global constant values can be declared with defconst.

Data Types

Pact is not a explicitly-typed language, but does use fixed type representations “under the hood” and does no coercion of types, so is strongly-typed nonetheless. Authors can employ functions like is-string, is-decimal to enforce type safety.

Pact’s supported types are:

Functions may take no arguments and return bare values, which can function as constants.

Performance

Pact is designed to maximize the performance of transaction execution, penalizing queries and module definition in favor of fast recording of business events on the blockchain. Some tips for fast execution are:

Single-function transactions

Design transactions so they can be executed with a single function call.

Call with references instead of use

When calling module functions in transactions, use reference syntax instead of importing the module with use. When defining modules that reference other module functions, use is fine, as those references will be inlined at module definition time.

Hardcoded arguments vs. message values

A transaction can encode values directly into the transactional code:

(accounts.transfer "Acct1" "Acct2" 100.00)

or it can read values from the message JSON payload:

(defun transfer-msg ()
  (transfer (read-msg "from") (read-msg "to")
            (read-decimal "amount")))
...
(accounts.transfer-msg)

The latter will execute slightly faster, as there is less code to interpret at transaction time.

Control Flow

Pact supports conditionals via if, bounded looping, and of course function application.

“If” considered harmful

Consider avoiding if wherever possible: every branch makes code harder to understand and more prone to bugs. The best practice is to put “what am I doing” code in the front-end, and “validate this transaction which I intend to succeed” code in the smart contract.

Pact’s original design left out if altogether (and looping), but it was decided that users should be able to judiciously use these features as necessary.

Use enforce

“If” should never be used to enforce business logic invariants: instead, enforce is the right choice, which will fail the transaction.

Indeed, failure is the only non-local exit allowed by Pact. This reflects Pact’s emphasis on totality.

Functional Concepts

Pact includes the functional-programming “greatest hits”: map, fold and filter. These all employ partial application, where the list item is appended onto the application arguments in order to serially execute the function.

(map (+ 2) [1 2 3])
(fold (+) ["Concatenate" " " "me"]

Pact also has compose, which allows “chaining” applications in a functional style.

LISP

Pact’s use of LISP syntax is intended to make the code reflect its runtime representation directly, allowing contract authors focus directly on program execution. Pact code is stored in human-readable form on the ledger, such that the code can be directly verified, but the use of LISP-style s-expression syntax allows this code to execute quickly.

Message Data

Pact expects code to arrive in a message with a JSON payload and signatures. Message data is read using read-msg and related functions, while signatures are not directly readable or writable – they are evaluated as part of keyset predicate enforcement.

JSON support

Values returned from Pact transactions are expected to be directly represented as JSON values.

When reading values from a message via read-msg, Pact coerces JSON types as follows:

  • String -> String
  • Number -> Integer (rounded)
  • Boolean -> Boolean
  • Object -> JSON Value
  • Array -> JSON Value
  • Null -> JSON Value

Decimal values are represented as Strings and read using read-decimal.

JSON Objects, Arrays, and Nulls are not coerced, intended for direct storage and retreival as opaque payloads in the database.

Confidentiality

Pact is designed to be used in a confidentiality-preserving environment, where messages are only visible to a subset of participants. This has significant implications for smart contract execution.

Entities

An entity is a business participant that is able or not able to see a confidential message. An entity might be a company, a group within a company, or an individual.

Disjoint Databases

Pact smart contracts operate on messages organized by a blockchain, and serve to produce a database of record, containing results of transactional executions. In a confidential environment, different entities execute different transactions, meaning the resulting databases are now disjoint.

This does not affect Pact execution; however, database data can no longer enact a “two-sided transaction”, meaning we need a new concept to handle enacting a single transaction over multiple disjoint datasets.

Pacts

Pacts are multi-step sequential transactions that are defined as a single body of code called a pact. With a pact, participants ensure they are executing an identical code path, even as they execute distinct “steps” in that path.

The concept of pacts reflect coroutines in software engineering: functions that can yield and resume computation “in the middle of” their body. A step in a pact designates a target entity to execute it, after which the pact “yields” execution, completing the transaction and initiating a signed “Resume” message into the blockchain.

The counterparty entity sees this “Resume” message and drops back into the pact body to find if the next step is targetted for it, if so executing it.

Since any step can fail, steps can be designed with rollbacks to undo changes if a subsequent step fails.

Syntax

Literals

Strings

String literals are created with double-ticks:

pact> "a string"
"a string"

Strings also support multiline by putting a backslash before and after whitespace (not interactively).

(defun id (a)
  "Identity function. \
  \Argument is returned."
  a)

Symbols

Symbols are string literals representing some unique item in the runtime, like a function or a table name. Their representation internally is simply a string literal so their usage is idiomatic.

Symbols are created with a preceding tick, thus they do no support whitespace or multiline.

pact> 'a-symbol
"a-symbol"

Integers

Integer literals are unbounded positive naturals. For negative numbers use the unary - function.

pact> 12345
12345

Decimals

Decimal literals are positive decimals to exact expressed precision.

pact> 100.25
100.25
pact> 356452.23451872
356452.23451872

Booleans

Booleans are represented by true and false literals.

pact> (and true false)
false

Lists

List literals are created with brackets. This is actually a special form, which evaluates the list function.

pact> [1 2 3]
[1 2 3]
pact> (= [1 2 3] (list 1 2 3))
true

Objects

Objects are dictionaries, created with curly-braces specifying key-value pairs using a colon :. For certain applications (database updates), keys must be strings.

pact> { "foo": (+ 1 2), "bar": "baz" }
(TObject [("foo",3),("bar","baz")])

Bindings

Bindings are dictionary-like forms, also created with curly braces, to bind database results to variables using the := operator. They are used in with-read, with-default-read, and bind to assign variables to named columns in a row, or values in an object.

(defun check-balance (id)
  (with-read 'accounts id { "balance" := bal }
    (enforce (> bal 0) (format "Account in overdraft: {}" bal))))

Special forms

defun

(defun NAME ARGLIST [DOCSTRING] BODY...)

Define NAME as a function, accepting ARGLIST arguments, with optional DOCSTRING. Arguments are in scope for BODY, one or more expressions.

(defun add3 (a b c) (+ a (+ b c)))

(defun scale3 (a b c s) "multiply sum of A B C times s"
  (* s (add3 a b c)))

defconst

(defun NAME VALUE [DOCSTRING])

Define NAME as VALUE, with option DOCSTRING.

(defconst COLOR_RED="#FF0000" "Red in hex")
(defconst COLOR_GRN="#00FF00" "Green in hex")
(defconst PI 3.14159265 "Pi to 8 decimals")

defpact

(defpact NAME ARGLIST [DOCSTRING] STEPS...)

Define NAME as a pact, a multistep computation intended for private transactions. Identical to defun except body must be comprised of steps.

(defpact payment (payer payer-entity payee
                  payee-entity amount)
  (step-with-rollback payer-entity
    (debit payer amount)
    (credit payer amount))
  (step payee-entity
    (credit payee amount)))

let

(let (BINDPAIR [BINDPAIR [...]]) BODY)

Bind variables in BINDPAIRs to be in scope over BODY. Variables within BINDPAIRs cannot refer to previously-declared variables in the same let binding; for this use (let*){#letstar}.

(let ((x 2)
      (y 5))
  (* x y))
> 10

let*

(let\* (BINDPAIR [BINDPAIR [...]]) BODY)

Bind variables in BINDPAIRs to be in scope over BODY. Variables can reference previously declared BINDPAIRS in the same let. let\* is expanded at compile-time to nested let calls for each BINDPAIR; thus let is preferred where possible.

(let* ((x 2)
       (y (* x 10)))
  (+ x y))
> 22

step

(step ENTITY EXPR)

Define a step within a pact, which can only be executed by nodes representing ENTITY, in order of execution specified in containing defpact.

step-with-rollback

(step-with-rollback ENTITY EXPR ROLLBACK-EXPR)

Define a step within a pact, which can only be executed by nodes representing ENTITY, in order of execution specified in containing defpact. If any subsequent steps fail, ROLLBACK-EXPR will be executed.

use

(use MODULE-SYMBOL)

Import an existing module into namespace.

(use 'accounts)
(transfer "123" "456" 5 (time "2016-07-22T11:26:35Z"))
"Write succeeded"

module

(module NAME KEYSET [DOCSTRING] DEFS...)

Define and install module NAME, guarded by keyset KEYSET, with optional DOCSTRING. DEFS must be defun or defpact expressions only.

(module accounts 'accounts-admin
  "Module for interacting with accounts"

  (defun create-account (id bal)
   "Create account ID with initial balance BAL"
   (insert 'accounts id { "balance": bal }))

  (defun transfer (from to amount)
   "Transfer AMOUNT from FROM to TO"
   (with-read 'accounts from { "balance": fbal }
    (enforce (<= amount fbal) "Insufficient funds")
     (with-read 'accounts to { "balance": tbal }
      (update 'accounts from { "balance": (- fbal amount) })
      (update 'accounts to { "balance": (+ tbal amount) }))))
)

Expressions

Expressions may be literals, atoms, s-expressions, or references.

Atoms

Atoms are non-reserved barewords starting with a letter or allowed symbol, and containing letters, digits and allowed symbols. Allowed symbols are %#+-_&$@<>=?*!|/. Atoms must resolve to a variable bound by a defun, defpact, binding form, or to symbols imported into the namespace with use.

S-expressions

S-expressions are formed with parentheses, with the first atom determining if the expression is a special form or a function application, in which case the first atom must refer to a definition.

Partial application

An application with less than the required arguments is in some contexts a valid partial application of the function. However, this is only supported in Pact’s functional-style functions; anywhere else this will result in a runtime error.

References

References are two atoms joined by a dot . to directly resolve to module definitions.

pact> accounts.transfer
"(defun accounts.transfer (src,dest,amount,date) \"transfer AMOUNT from
SRC to DEST\")"
pact> transfer
Eval failure:
transfer<EOF>: Cannot resolve transfer
pact> (use 'accounts)
"Using \"accounts\""
pact> transfer
"(defun accounts.transfer (src,dest,amount,date) \"transfer AMOUNT from
SRC to DEST\")"

References are preferred to use for transactions, as references resolve faster. However in module definition, use is preferred for legibility.

Built-in Functions

General

at

Args: idx a

Index list A at IDX, or get value with key IDX from object A.

pact> (at 1 [1 2 3])
2
pact> (at "bar" { "foo": 1, "bar": 2 })
2

bind

Args: src bindings body

Evaluate SRC which must return an object, using BINDINGS to bind variables to values in the result.

pact> (bind { "a": 1, "b": 2 } { "a" := a-value } a-value)
1

compose

Args: apps value

Compose APPS left-to-right, such that element 1 operates on VALUE, 2 on the result, etc.

pact> (filter (compose (length) (< 2)) ["my" "dog" "has" "fleas"])
["dog" "has" "fleas"]

drop

Args: count list

Drop COUNT values from LIST (or string). If negative, drop from end.

pact> (drop 2 "vwxyz")
"xyz"
pact> (drop (- 2) [1 2 3 4 5])
[1 2 3]

enforce

Args: test msg

Fail transaction with MSG if TEST fails, or returns true.

pact> (enforce (!= (+ 2 2) 4) "Chaos reigns")
<interactive>:1:0:Failure: Chaos reigns

filter

Args: app list

Filter LIST by applying APP to each element to get a boolean determining inclusion.

pact> (filter (compose (length) (< 2)) ["my" "dog" "has" "fleas"])
["dog" "has" "fleas"]

fold

Args: app init list

Iteratively reduce LIST by applying APP to last result and element, starting with INIT.

pact> (fold (+) 0 [100 10 5])
115

format

Args: template vars

Interpolate VARS into TEMPLATE using {}.

pact> (format "My {} has {}" "dog" "fleas")
"My dog has fleas"

if

Args: cond then else

Test COND, if true evaluate THEN, otherwise evaluate ELSE.

pact> (if (= (+ 2 2) 4) "Sanity prevails" "Chaos reigns")
"Sanity prevails"

is-bool

Args: val

Return VAL, enforcing boolean type.

pact> (is-bool true)
true

is-decimal

Args: val

Return VAL, enforcing decimal type.

pact> (is-decimal 123.45)
123.45

is-integer

Args: val

Return VAL, enforcing integer type

pact> (is-integer 123)
123
pact> (is-integer "abc")
<interactive>:0:0:Not integer: "abc"

is-string

Args: val

Return VAL, enforcing string type.

pact> (is-string 123)
<interactive>:0:0:Not string: 123
pact> (is-string "abc")
"abc"

is-time

Args: val

Return VAL, enforcing time type.

pact> (is-time (time "2016-07-22T11:26:35Z"))
"2016-07-22T11:26:35Z"

length

Args: a

Compute length of A, which can be a list, a string, or an object.

pact> (length [1 2 3])
3
pact> (length "abcdefgh")
8
pact> (length { "a": 1, "b": 2 })
2

list

Args: elems

Create list from ELEMS.

pact> (list 1 2 3)
[1 2 3]

map

Args: app list

Apply elements in LIST as last arg to APP, returning list of results.

pact> (map (+ 1) [1 2 3])
[2 3 4]

pact-txid

Return reference tx id for pact execution.

read-decimal

Args: key

Parse KEY string value from message data body as decimal.

(defun exec ()
   (transfer (read-msg "from") (read-msg "to") (read-decimal "amount")))

read-integer

Args: key

Parse KEY string value from message data body as integer.

(read-integer "age")

read-msg

Args: key

Read KEY from message data body. Will recognize JSON types as corresponding Pact type.

(defun exec ()
   (transfer (read-msg "from") (read-msg "to") (read-decimal "amount")))

remove

Args: key object

Remove entry for KEY from OBJECT.

pact> (remove "bar" { "foo": 1, "bar": 2 })
{"foo": 1}

take

Args: count list

Take COUNT values from LIST (or string). If negative, take from end.

pact> (take 2 "abcd")
"ab"
pact> (take (- 3) [1 2 3 4 5])
[3 4 5]

typeof

Args: a

Returns type of A as string.

pact> (typeof "hello")
"string"

Database

create-table

Args: table module

Create table TABLE guarded by module MODULE.

(create-table 'accounts 'accounts-admin)

describe-keyset

Args: keyset

Get metadata for KEYSET

describe-module

Args: module

Get metadata for MODULE

describe-table

Args: table

Get metadata for TABLE

insert

Args: table key object

Write entry in TABLE for KEY of OBJECT column data, failing if data already exists for KEY.

(insert 'accounts { "balance": 0.0, "note": "Created account." })

keys

Args: table

Return all keys in TABLE.

(keys 'accounts)

read

Args: table key colnames...

Read row from TABLE for KEY returning object of COLNAMES mapped to values, or entire record if not specified.

(read 'accounts id 'balance 'ccy)

txids

Args: table txid

Return all txid values greater than or equal to TXID in TABLE.

(txids 'accounts 123849535)

txlog

Args: table txid

Return all updates to TABLE performed in transaction TXID.

(txlog 'accounts 123485945)

update

Args: table key object

Write entry in TABLE for KEY of OBJECT column data, failing if data does not exist for KEY.

(update 'accounts { "balance": (+ bal amount), "change": amount, "note": "credit" })

with-default-read

Args: table key defaults bindings body

Read row from TABLE for KEY and bind columns per BINDINGS over BODY. If row not found, read columns from DEFAULTS, an object with matching key names.

(with-default-read 'accounts id { "balance": 0, "ccy": "USD" } { "balance":= bal, "ccy":= ccy }
   (format "Balance for {} is {} {}" id bal ccy))

with-read

Args: table key bindings body

Read row from TABLE for KEY and bind columns per BINDINGS over BODY.

(with-read 'accounts id { "balance":= bal, "ccy":= ccy }
   (format "Balance for {} is {} {}" id bal ccy))

write

Args: table key object

Write entry in TABLE for KEY of OBJECT column data.

(write 'accounts { "balance": 100.0 })

Time

add-time

Args: time seconds

Add SECONDS to TIME; SECONDS can be integer or decimal.

pact> (add-time (time "2016-07-22T12:00:00Z") 15)
"2016-07-22T12:00:15Z"

days

Args: n

N days, for use with ‘add-time’

pact> (add-time (time "2016-07-22T12:00:00Z") (days 1))
"2016-07-23T12:00:00Z"

diff-time

Args: time1 time2

Compute difference between TIME1 and TIME2 in seconds.

pact> (diff-time (parse-time "%T" "16:00:00") (parse-time "%T" "09:30:00"))
23400

hours

Args: n

N hours, for use with ‘add-time’

pact> (add-time (time "2016-07-22T12:00:00Z") (hours 1))
"2016-07-22T13:00:00Z"

minutes

Args: n

N minutes, for use with ‘add-time’.

pact> (add-time (time "2016-07-22T12:00:00Z") (minutes 1))
"2016-07-22T12:01:00Z"

parse-time

Args: format utcval

Construct time from UTCVAL using FORMAT. See strftime docs for format info.

pact> (parse-time "%F" "2016-09-12")
"2016-09-12T00:00:00Z"

time

Args: utcval

Construct time from UTCVAL using ISO8601 format (%Y-%m-%dT%H:%M:%SZ).

pact> (time "2016-07-22T11:26:35Z")
"2016-07-22T11:26:35Z"

Operators

!=

Args: a b

True if a does not equal b.

pact> (!= "hello" "goodbye")
true

*

Args: a b

Multiply A by B.

pact> (* 0.5 10.0)
5
pact> (* 3 5)
15

+

Args: a b

Add numbers, concatenate strings/lists, or merge objects.

pact> (+ 1 2)
3
pact> (+ 5.0 0.5)
5.5
pact> (+ "every" "body")
"everybody"
pact> (+ [1 2] [3 4])
[1 2 3 4]
pact> (+ { "foo": 100 } { "foo": 1, "bar": 2 })
{"bar": 2, "foo": 100}

-

Args: a b

Negate A, or subtract A from B.

pact> (- 1.0)
-1.0
pact> (- 3 2)
1

/

Args: a b

Divide A by B.

pact> (/ 10.0 2.0)
5
pact> (/ 8 3)
2

<

Args: a b

True if A < B.

pact> (< 1 3)
true
pact> (< 5.24 2.52)
false
pact> (< "abc" "def")
true

<=

Args: a b

True if A <= B.

pact> (<= 1 3)
true
pact> (<= 5.24 2.52)
false
pact> (<= "abc" "def")
true

=

Args: a b

True if a equals b.

pact> (= [1 2 3] [1 2 3])
true
pact> (= 'foo "foo")
true
pact> (= { 1: 2 } { 1: 2})
true

>

Args: a b

True if A > B.

pact> (> 1 3)
false
pact> (> 5.24 2.52)
true
pact> (> "abc" "def")
false

>=

Args: a b

True if A >= B.

pact> (>= 1 3)
false
pact> (>= 5.24 2.52)
true
pact> (>= "abc" "def")
false

^

Args: a b

Raise A to B power.

pact> (^ 2 3)
8

abs

Args: a

Absolute value of A.

pact> (abs (- 10 23))
13

and

Args: a b

Boolean logic.

pact> (and true false)
false

ceiling

Args: a prec

Rounds up value of decimal A as integer, or to PREC precision as decimal.

pact> (ceiling 3.5)
4
pact> (ceiling 100.15234 2)
100.16

exp

Args: a

Exp of A

pact> (round (exp 3) 6)
20.085537

floor

Args: a prec

Rounds down value of decimal A as integer, or to PREC precision as decimal.

pact> (floor 3.5)
3
pact> (floor 100.15234 2)
100.15

ln

Args: a

Natural log of A.

pact> (round (ln 60) 6)
4.094345

log

Args: a b

Log of B base A.

pact> (log 2 256)
8

mod

Args: a b

A modulo B.

pact> (mod 13 8)
5

not

Args: a

Boolean logic.

pact> (not (> 1 2))
true

or

Args: a b

Boolean logic.

pact> (or true false)
true

round

Args: a prec

Performs Banker’s rounding value of decimal A as integer, or to PREC precision as decimal.

pact> (round 3.5)
4
pact> (round 100.15234 2)
100.15

sqrt

Args: a

Square root of A.

pact> (sqrt 25)
5

KeySets

define-keyset

Args: name keyset

Define keyset as NAME with KEYSET. If keyset NAME already exists, keyset will be enforced before updating to new value.

(define-keyset 'admin-keyset (read-keyset "keyset"))

keys-2

Args: count matched

Keyset predicate function to match at least 2 keys in keyset.

pact> (keys-2 3 1)
false

keys-all

Args: count matched

Keyset predicate function to match all keys in keyset.

pact> (keys-all 3 3)
true

keys-any

Args: count matched

Keyset predicate function to match all keys in keyset.

pact> (keys-any 10 1)
true

read-keyset

Args: key

Read KEY from message data body as keyset ({ “keys”: KEYLIST, “pred”: PREDFUN }). PREDFUN should resolve to a keys predicate.

(read-keyset "admin-keyset")

with-keyset

Args: keyset-or-name body

Enforce KEYSET-OR-NAME against message keys to run BODY. KEYSET-OR-NAME can be a symbol of a keyset name or a keyset object.

(with-keyset 'admin-keyset ...)
(with-keyset (read-keyset "keyset") ...)

REPL-only functions

The following functions are loaded magically in the interactive REPL, or in script files with a .repl extension. They are not available for blockchain-based execution.

begin-tx

Args: name

Begin transaction with optional NAME.

(begin-tx "load module")

bench

Args: exprs

Benchmark execution of EXPRS.

(bench (+ 1 2))

commit-tx

Commit transaction.

(commit-tx)

env-data

Args: json

Set transaction JSON data, either as encoded string, or as pact types coerced to JSON.

pact> (env-data { "keyset": { "keys": ["my-key" "admin-key"], "pred": "keys-any" } })
"Setting transaction data"

env-entity

Args: entity

Set environment confidential ENTITY id.

(env-entity "my-org")

env-keys

Args: keys...

Set transaction signature KEYS.

pact> (env-keys "my-key" "admin-key")
"Setting transaction keys"

env-step

Args: step-idx rollback

Modify pact step state. With no arguments, unset step. STEP-IDX sets step index for current pact execution, ROLLBACK defaults to false.

(env-step 1)
(env-step 0 true)

expect

Args: doc expected actual

Evaluate ACTUAL and verify that it equals EXPECTED.

pact> (expect "Sanity prevails." 4 (+ 2 2))
"Expect: success: Sanity prevails."

expect-failure

Args: doc actual

Evaluate ACTUAL and succeed only if it throws an error.

pact> (expect-failure "Enforce fails on false" (enforce false "Expected error"))
"Expect failure: success: Enforce fails on false"

load

Args: file

Load and evaluate FILE.

(load "accounts.repl")

rollback-tx

Rollback transaction.

(rollback-tx)