@onflow/interaction

Flow Interaction ADT and Helpers

Usage no npm install needed!

<script type="module">
  import onflowInteraction from 'https://cdn.skypack.dev/@onflow/interaction';
</script>

README

@onflow/interaction

This module provides an ADT (Abstract Data Type) that represents the underlying data required by the send function. It provides function in which to modify the interaction.

Status

  • Last Updated: Feb 2nd 2021
  • Stable: Yes
  • Risk of Breaking Change: Medium

This is a keystone data structure in how the SDK works. We will strive for backwards compatibility in any changes to this, as it also makes our lives easier. Now that we have a stable encoding package for creating what needs to be signed in transactions, we will be bringing this data structure closer to what those functions need.

Known Upcoming Changes:

  • We will be changing some of the underlying fields and data so they are more inline with @onflow/encode

Install

npm install --save @onflow/interaction

Types of interactions

Currently the Access Node recognizes 7 different types of interactions.

  • Script executes a script, can be used to query Flow
  • Transaction executes a transaction
  • GetTransactionStatus requests the status of a supplied transaction
  • GetAccount requests a supplied account
  • GetEvents requests events of a supplied type
  • GetLatestBlock requests the latest block
  • Ping requests a pong
  • GetTransaction requests a transaction
  • GetBlockById requests a block by an ID deprecated, use GetBlock instead
  • GetBlockByHeight requests a block by a height deprecated, use GetBlock instead
  • GetBlock requests a block
  • GetBlockHeader requests a block header

Internal Properties

The interaction is a monomorphic data structure, that merges the 7 types of interactions together. Internally it has a bunch of properties, but not everything needs to be included for each of the 7 interaction types.

  • tag (all) Int -- a marker that represents the type of the interaction
  • status (all) Int -- a marker that represents the status of the interaction
  • reason (all) String -- used to supply more information/feedback when a status is bad
  • accounts (transaction, script)
    • kind (transaction, script) Int -- denotes the kind of account, ACCOUNT or PARAM or ARGUMENT
    • tempId (transaction, script) String -- denotes the internal tempId for this account
    • addr (transaction, script) String -- denotes the address of this account
    • keyId (transaction, script) Int -- denotes the keyId in question for this account
    • sequenceNum (transaction, script) Int -- denotes the sequenceNum in question for this account
    • signature (transaction, script) String -- the signature produced by the signingFunction for this account
    • signingFunction (transaction, script) Function -- the signing function for this account
    • resolve (transaction, script) Function -- the resolver for this account
    • role (transaction, script)
      • propser (transaction, script) Boolean -- denotes if this account is a propser
      • authorizer (transaction, script) Boolean -- denotes if this account is an authorizer
      • payer (transaction, script) Boolean -- denotes if this account is a payer
      • param (transaction, script) Boolean -- denotes if this account is a param
  • params (transaction, script)
    • kind (transaction, script) Int -- denotes the kind of param, ACCOUNT or PARAM or ARGUMENT
    • tempId (transaction, script) String -- the internal tempId for this param
    • key (transaction, script) String -- the key for this param
    • value (transaction, script) Any -- the value for this param
    • asParam (transaction, script) Any -- the asParam transformed value for this param
    • xform (transaction, script) Any -- the transform for this param
    • resolve (transaction, script) Function -- a resolver for this param
  • arguments (transaction, script)
    • kind (transaction, script) Int -- denotes the kind of argument, ACCOUNT or PARAM or ARGUMENT
    • tempId (transaction, script) String -- the internal tempId for this argument
    • value (transaction, script) Any -- the value for this argument
    • asArgument (transaction, script) Any -- the asArgument transformed value for this argument
    • xform (transaction, script) Any -- the transform for this argument
    • resolve (transaction, script) Function -- a resolver for this argument
  • message (script, transaction)
    • cadence (script, transaction) String -- cadence code
    • refBlock (transaction) String -- id of an existing block (used for timeout)
    • computeLimit (script) Int -- how much payer is willing to spend
    • proposer (transaction) String -- the tempId of the account proposer for a transaction
    • payer (transaction) String -- the tempId of the payer for a transaction
    • authorizations (transaction) Array<String> -- list of tempIds referencing the accounts of the authorizers for a transaction
    • params (transaction, script) Array<String> -- list of tempIds referencing the params for a transaction or script
    • arguments (transaction, script) Array<String> -- list of tempIds referencing the arguments for a transaction or script
  • proposer (transaction) String -- the tempId referencing the account of the proposer for a transaction
  • payer (transaction) String -- the tempId referencing the account of the payer for a transaction
  • authorizations (transaction) Array<String> -- list of tempIds referencing the accounts of the authorizers for a transaction
  • events (getEvents)
    • start (getEvents) Int -- events after this
    • end (getEvents) Int -- events before this
    • eventType (getEvents) String -- type of events to get
    • blockIds (getEvents) Array<String> -- array of block ids to get events from
  • block (getLatestBlock, getBlockByHeight, getBlockById)
    • isSealed (getLatestBlock) Boolean -- determines if the criteria for the latest block is sealed or not.
    • height (getBlockByHeight) Int -- sets the height for the block to get.
    • id (getBlockById) Int -- sets the id for the block to get.
  • account
    • addr _(getAccount) String -- address of the account to get
  • transaction
    • id _(getTransaction) -- id of the transaction to get
  • assigns (all) {[String]:Any} -- a pocket to hold things in while building and resolving

Exposed Constants

Tags

Label asString
UNKNOWN UNKNOWN
SCRIPT SCRIPT
TRANSACTION TRANSACTION
GET_TRANSACTION_STATUS GET_TRANSACTION_STATUS
GET_ACCOUNT GET_ACCOUNT
GET_EVENTS GET_EVENTS
GET_LATEST_BLOCK GET_LATEST_BLOCK
PING PING
PING PING
GET_TRANSACTION GET_TRANSACTION
GET_BLOCK_BY_ID GET_BLOCK_BY_ID
GET_BLOCK GET_BLOCK
GET_BLOCK_HEADER GET_BLOCK_HEADER

Status

Label asString
BAD BAD
OK OK

Exposed Functions

interaction/0

Constructs an empty interaction.

import {interaction} from "@onflow/interaction"

const emptyInteraction = interaction()

isInteraction/1

returns true if the value passed to it is an interaction

import {interaction, isInteraction} from "@onflow/interaction"

const ix = interaction()

isInteraction(ix) // true
isInteraction("i am a string") // false

Ok/1 and isOk/1

Sets the status of an interaction to OK

import {interaction, Ok, isOk} from "@onflow/interaction"

isOk(Ok(interaction())) // true

Bad/2, isBad/1 and why/1

Sets the status of an interaction to BAD, can also add a reason as to why its bad.

import {interaction, Bad, why} from "@onflow/interaction"

const ix = Bad(interaction, "You were supposed to do the thing")
isBad(ix) // true
why(ix) // "You were supposed to do the thing"

makeUnknown/1 and isUnknown/1

tags an interaction as Unknown

import {interaction, makeUnknown, isUnknown} from "@onflow/interaction"

isUnknown(makeUnknown(interaction())) // true

makeScript/1 and isScript/1

tags an interaction as a Script interaction

import {interaction, makeScript, isScript} from "@onflow/interaction"

isScript(makeScript(interaction())) // true

makeTransaction/1 and isTransaction/1

tags an interaction as a Transaction interaction

import {interaction, makeTransaction, isTransaction} from "@onflow/interaction"

isTransaction(makeTransaction(interaction())) // true

makeGetTransaction/1 and isGetTransaction/1

tags an interaction as a GetTransactionStatus interaction

import {
  interaction,
  makeGetTransactionStatus,
  isGetTransactionStatus,
} from "@onflow/interaction"

isGetTransactionStatus(makeGetTransactionStatus(interaction())) // true

makeGetAccount/1 and isGetAccount/1

tags an interaction as a GetAccount interaction

import {interaction, makeGetAccount, isGetAccount} from "@onflow/interaction"

isGetAccount(makeGetAccount(interaction())) // true

makeGetEvents/1 and isGetEvents/1

tags an interaction as a GetEvents interaction

import {interaction, makeGetEvents, isGetEvents} from "@onflow/interaction"

isGetEvents(makeGetEvents(interaction())) // true

makeGetLatestBlock/1 and isGetLatestBlock/1

tags an interaction as a GetLatestBlock interaction

import {
  interaction,
  makeGetLatestBlock,
  isGetLatestBlock,
} from "@onflow/interaction"

isGetLatestBlock(makeGetLatestBlock(interaction())) // true

makePing/1 and isPing/1

tags an interaction as a Ping interaction

import {interaction, makePing, isPing} from "@onflow/interaction"

isPing(makePing(interaction())) // true

makeGetTransaction/1 and isGetTransaction/1

tags an interaction as a GetTransaction interaction

import {interaction, makeGetTransaction, isGetTransaction} from "@onflow/interaction"

isGetTransaction(makeGetTransaction(interaction())) // true

makeGetBlock1 and isGetBlock/1

tags an interaction as a GetBlock interaction

import {interaction, makeGetBlock, isGetBlock} from "@onflow/interaction"

isGetBlock(makeGetBlock(interaction())) // true

makeGetBlockHeader/1 and isGetBlockHeader/1

tags an interaction as a GetBlockHeader interaction

import {interaction, makeGetBlockHeader, isGetBlockHeader} from "@onflow/interaction"

isGetBlockHeader(makeGetBlockHeader(interaction())) // true

get/3, put/2, update/2 and destory/1

crud operations for the assigns pocket inside the interaction. They are specifically designed to be used with pipe.

import {interaction, get, put, update, destory} from "@onflow/interaction"

let ix = interaction()
get(ix, "count", 0) // 0

ix = put("count", 0)(ix)
get(ix, "count", 0) // 0

ix = update("count", count => count + 1)(ix)
get(ix, "count", 0) // 1

ix = destory("count")(ix)
get(ix, "count", 0) // 0

makeAuthorizer/1

compose an Authorizer account, and registers a tempId for it in the interaction object accounts registry

import {makeAuthorizer, makeTransaction, pipe} from "@onflow/interaction"
const ix = pipe([
  makeTransaction``
  makeAuthorizer({ addr: "01", role: { authorizer: true } })
])

makePayer/1

compose a Payer account, and registers a tempId for it in the interaction object accounts registry

import {makePayer, makeTransaction, pipe} from "@onflow/interaction"
const ix = pipe([
  makeTransaction``
  makePayer({ addr: "01", role: { payer: true } })
])

makeProposer/1

compose a Proposer account, and registers a tempId for it in the interaction object accounts registry

import {makeProposer, makeTransaction, pipe} from "@onflow/interaction"
const ix = pipe([
  makeTransaction``
  makeProposer({ addr: "01", role: { proposer: true } })
])

makeParam/1

compose a Param, and registers a tempId for it in the interaction object params registry

import {makeParam, makeTransaction, pipe} from "@onflow/interaction"
const ix = pipe([
  makeTransaction``
  makeParam(...)
])

pipe/2

asynchronously composes transform functions and applys them to an interaction.

import {interaction, pipe, put, update} from "@onflow/interaction"

const ix = await pipe(interaction(), [
  put("a", 5),
  put("b", 6),
  update("sum", (_, ix) => get(ix, "a", 0) + get(ix, "b", 0)),
])

get(ix, "sum", 0) // 11

pipe/1

gets passed an array of transform functions, returning a function that takes an interaction to apply the transform functions to asynchronously.

import {interaction, pipe, put, update} from "@onflow/interaction"

const run = await pipe([
  put("a", 5),
  put("b", 6),
  update("sum", (_, ix) => get(ix, "a", 0) + get(ix, "b", 0)),
])

const ix = run(interaction())

get(ix, "sum", 0) // 11

// Pipes can also be composed

const p1 = pipe([put("a", 1), put("b", 2)])

const p2 = pipe([put("c", 3), put("d", 4)])

const calc = update("sum", (_, ix) =>
  ["a", "b", "c", "d"].reduce((acc, d) => acc + get(ix, d, 0), 0)
)

const ix = await pipe(interaction(), [p1, p2, calc])
get(ix, "sum", 0) // 10

// Pipes can be stoped

import {Bad, Ok, isBad, why} from "@onflow/interaction"

const countCantBeGreaterThan = value => ix =>
  get(ix, "count", 0) > value ? Bad(ix, `Was greater than ${value}`) : Ok(ix)

const setCount = count => put("count", count)

const incCountBy = amount => update("count", count => count + amount)

const ix = await pipe(interaction(), [
  setCount(5), // count: 5
  countCantBeGreaterThan(10), // Ok
  incCountBy(3), // count: 8
  countCantBeGreaterThan(10), // Ok
  incCountBy(5), // count: 13
  countCantBeGreaterThan(10), // Bad
  incCountBy(9), // never called
])

isBad(ix) // true
why(ix) // "Was greater than 10"