Skip to main content

Global Variables

Globally available units​

An integer literal can take a suffix of either monetary or temporary units to add semantic value to these integers and to simplify arithmetic. When these units are used, the underlying integer is automatically multiplied by the value of the unit. The units sats, nexa, mnexa and mex are used to denote monetary value, while the units seconds, minutes, hours, days and weeks are used to denote time.

caution

Be careful when using these units in precise calendar calculations though, because not every year equals 365 days and not even every minute has 60 seconds because of leap seconds.

Example​

require(1 sats == 1);
require(1 nexa == 100);
require(1 mnexa == 100000000);
require(1 mex == 100000000);

require(1 seconds == 1);
require(1 minutes == 60 seconds);
require(1 hours == 60 minutes);
require(1 days == 24 hours);
require(1 weeks == 7 days);

Global time lock variables​

Nexa has support for different kinds of time locks, which can be used to specify time-based conditions inside Nexa contracts. These time locks can be separated by three main attributes: location, targeting and metric. The location can be the transaction level or the contract level, but contract level locks also require you to use a corresponding transaction level lock. The targeting can be relative (e.g. at least 4 hours have passed) or absolute (e.g. the block number is at least 600,000). The metric can be blocks or seconds.

It can be difficult to fully grasp the intricacies of time locks, so if you're starting out it is recommended to start off with the simplest version: absolute block-based time locks. If you do want to dive into the more advanced uses of time locks, James Prestwich wrote the best article explaining time locks in Bitcoin that also fully applies to Nexa.

tx.time​

tx.time is used to create absolute time locks. The value of tx.time can either represent the block number of the spending transaction or its timestamp. When comparing it with values below 500,000,000, it is treated as a blocknumber, while higher values are treated as a timestamp.

Due to limitations in the underlying Script, tx.time can only be used in the following way:

require(tx.time >= <expression>);

Because of the way time locks work, a corresponding time lock needs to be added to the transaction. The NexScript SDK automatically sets this transaction level time lock to the most recent block number, as this is the most common use case. If you need to use a different block number or timestamp, this should be passed into the NexScript SDK using the withTime() function. If the default matches your use case, no additional actions are required.

note

tx.time corresponds to the nLocktime field of the current transaction and the OP_CHECKLOCKTIMEVERIFY opcode.

tx.age​

tx.age is used to create relative time locks. The value of tx.age can either represent a number of blocks, or a number of chunks, which are 512 seconds. The corresponding transaction level time lock determines which of the two options is used.

Due to limitations in the underlying Script, tx.age can only be used in the following way:

require(tx.age >= <expression>);

Because of the way time locks work, a corresponding time lock needs to be added to the transaction. This can be done in the NexScript SDK using the withAge() function. However, the value passed into this function will always be treated as a number of blocks, so it is currently not supported to use tx.age as a number of second chunks.

note

tx.age corresponds to the nSequence field of the current UTXO and the OP_CHECKSEQUENCEVERIFY opcode.

Introspection variables​

Introspection functionality is used to create covenant contracts. Covenants are a technique used to put constraints on spending the money inside a smart contract. The main use case of this is limiting the addresses where money can be sent and the amount sent. To explore the possible uses of covenants inside smart contracts, read the NexScript Covenants Guide.

this.activeInputIndex​

int this.activeInputIndex

During the validation of a Nexa transaction, every transaction input is evaluated in order, and the contract's code is evaluated in the context of the different inputs. this.activeInputIndex represents the index of the input that is currently being evaluated. This can be used in conjunction with the properties under tx.inputs.

this.activeBytecode​

bytes this.activeBytecode

During the validation of a Nexa transaction, every transaction input is evaluated in order, and the contract's code is evaluated in the context of the different inputs. this.activeBytecode represents the contract bytecode of the input that is currently being evaluated.

tx.version​

int tx.version

Represents the version of the current transaction. Different transaction versions can have differences in functionality. Currently only version 1 and 2 exist, where only version 2 has support for BIP68.

tx.locktime​

int tx.locktime

Represents the nLocktime field of the transaction.

note

tx.locktime is similar to the tx.time global variable. It is recommended to only use tx.locktime for adding nLocktime to simulated state and tx.time in all other cases.

tx.inputs​

Represents the list of inputs of the evaluated transaction. This is an array, and cannot be used on itself. You need to access an input with a specific index and specify the properties you want to access.

tx.inputs.length​

int tx.inputs.length

Represents the number of inputs in the transaction.

tx.inputs[i].value​

int tx.inputs[i].value

Represents the value of a specific input (in satoshis).

tx.inputs[i].lockingBytecode​

bytes tx.inputs[i].lockingBytecode

Represents the locking bytecode (scriptPubKey) of a specific input.

tx.inputs[i].unlockingBytecode​

bytes tx.inputs[i].unlockingBytecode

Represents the unlocking bytecode (scriptSig) of a specific input.

tx.inputs[i].outpointTransactionHash​

bytes32 tx.inputs[i].outpointTransactionHash

Represents the outpoint transaction hash where a specific input was initially locked.

tx.inputs[i].outpointIndex​

int tx.inputs[i].outpointIndex

Represents the outpoint index where a specific input was initially locked.

tx.inputs[i].sequenceNumber​

int tx.inputs[i].sequenceNumber

Represents the nSequence number of a specific input.

tx.inputs[i].type​

int tx.inputs[i].type

Represents the type of a specific input: 0 for an input that spends a UTXO, 1 for a read-only input.

tx.inputs[i].tokenGroupId​

bytes tx.inputs[i].tokenGroupId

Represents the tokenGroupId of a specific input. Returns the entire group ID including any subgroup data. Otherwise, when that specific input contains no group, returns 0x.

tx.inputs[i].tokenParentGroupId​

bytes tx.inputs[i].tokenParentGroupId

Represents the tokenParentGroupId of a specific input. Returns the entire 32 byte parent group ID. Otherwise, when that specific input contains no group returns 0x.

tx.inputs[i].subgroupData​

bytes tx.inputs[i].subgroupData

Represents the subgroup (NFT) data commitment of a specific input. Returns 0x if input has no subgroup (NFT).

tx.inputs[i].tokenAmount​

int tx.inputs[i].tokenAmount

Represents the token amount of a specific input. Returns 0 if input is ungrouped.

tx.inputs[i].tokenAuthority​

int tx.inputs[i].tokenAuthority

Represents the authority field of the UTXO spent by a specific input: the 64 bit authority flags read as a signed number, so any authority is negative (bit 63 is the AUTHORITY flag). Returns 0 if it is not an authority. This is the same value the SDK reports as the authority's token.amount, and the value new LockingBytecodeP2ST(...) takes to build one. Use hasAuthority() to check its flags. The value is read from the locking bytecode, so it does not change with Upgrade2, which changes how OP_PARSE reports authority flags.

tx.inputs[i].visibleParameters (TEMPORARILY DISABLED)​

bytes tx.inputs[i].visibleParameters

Represents the visible contract parameters of a P2ST utxo being spent as push-only script bytes data, minimally encoded. E.g. if there are two parameters OP_1 and 0xbeef, the result of this function will be 0x5102beef.

tx.outputs​

Represents the list of outputs of the evaluated transaction. This is an array, and cannot be used on itself. You need to access an output with a specific index and specify the properties you want to access.

tx.outputs.length​

int tx.outputs.length

Represents the number of outputs in the transaction.

tx.outputs[i].value​

int tx.outputs[i].value

Represents the value of a specific output (in satoshis).

tx.outputs[i].lockingBytecode​

bytes tx.outputs[i].lockingBytecode

Represents the locking bytecode (scriptPubKey) of a specific output.

tx.outputs[i].type​

int tx.outputs[i].type

Represents the type of a specific output: 1 for an output locked with a script template (such as a P2PKT or P2ST address), 0 for one locked with a plain script (such as OP_RETURN data).

tx.outputs[i].tokenGroupId​

bytes tx.outputs[i].tokenGroupId

Represents the tokenGroupId of a specific output. Returns the entire group ID including any subgroup data. Otherwise, when that specific output contains no group, returns 0x.

tx.outputs[i].tokenParentGroupId​

bytes tx.outputs[i].tokenParentGroupId

Represents the tokenParentGroupId of a specific output. Returns the entire 32 byte parent group ID. Otherwise, when that specific output contains no group returns 0x.

tx.outputs[i].subgroupData​

bytes tx.outputs[i].subgroupData

Represents the subgroup (NFT) data commitment of a specific output. Returns 0x if output has no subgroup (NFT).

tx.outputs[i].tokenAmount​

int tx.outputs[i].tokenAmount

Represents the token amount of a specific output. Returns 0 if output is ungrouped.

tx.outputs[i].tokenAuthority​

int tx.outputs[i].tokenAuthority

Represents the authority field of a specific output: the 64 bit authority flags read as a signed number, so any authority is negative (bit 63 is the AUTHORITY flag). Returns 0 if it is not an authority. This is the same value the SDK reports as the authority's token.amount, and the value new LockingBytecodeP2ST(...) takes to build one. Use hasAuthority() to check its flags. The value is read from the locking bytecode, so it does not change with Upgrade2, which changes how OP_PARSE reports authority flags.

tx.outputs[i].visibleParameters​

bytes tx.outputs[i].visibleParameters

Represents the visible contract parameters of a P2ST output produced by this contract as push-only script bytes data, minimally encoded. E.g. if there are two parameters OP_1 and 0xbeef, the result of this function will be 0x5102beef.

Transaction state variables and functions​

In addition to transaction introspectio, Nexa introduces transaction state lookup which further greatly improves covenant contracts. See specificationspec.

tx.id​

bytes32 tx.id

Gets information about transaction id in native endianness.

tx.idem​

bytes32 tx.idem

Gets information about transaction idem in native endianness.

tx.amountIn​

int tx.amountIn

Gets information about transaction's total incoming native cryptocurrency in its finest unit (sum of all input satoshi values).

tx.amountOut​

int tx.amountOut

Gets information about transaction's total outgoing native cryptocurrency in its finest unit (sum of all output satoshi values).

tx.groupAmountIn()​

int tx.groupAmountIn(groupId)

Gets information about total incoming tokens of group indicated by 32 bytes long groupId. Limited to 8 bytes, overflows wrap.

tx.groupAmountOut()​

int tx.groupAmountOut(groupId)

Gets information about total outgoing tokens of group indicated by 32 bytes long groupId. Limited to 8 bytes, overflows wrap.

tx.groupCountIn()​

int tx.groupCountIn(groupId)

Gets information about number of inputs using a group indicated by 32 bytes long groupId.

tx.groupCountOut()​

int tx.groupCountOut(groupId)

Gets information about number of outputs using a group indicated by 32 bytes long groupId.

tx.groupNthInput()​

int tx.groupNthInput(index, groupId)

Gets information about the 0-based input index of Nth grouped input indicated by 32 bytes long groupId. Fails if inputs does not exist.

tx.groupNthOutput()​

int tx.groupNthOutput(index, groupId)

Gets information about the 0-based output index of Nth grouped output indicated by 32 bytes long groupId. Fails if outputs does not exist.

Constructing locking bytecode​

One of the main use cases of covenants is enforcing transaction outputs (where money is sent). To assist with enforcing these outputs, there is a number of LockingBytecode objects that can be instantiated. These locking bytecodes can then be compared to the locking bytecodes of transaction outputs.

Example​

bytes25 lockingBytecode = new LockingBytecodeP2PKT(pkh);
int value = 10000;
require(tx.outputs[0].lockingBytecode == lockingBytecode);
require(tx.outputs[0].value == value);

LockingBytecodeP2PKT​

new LockingBytecodeP2PKT(bytes20 pkConstraintHash): bytes23

Creates new P2PKT locking bytecode for the hash of the constraints script pkConstraintHash, because the constrain is a push script for a public key.

LockingBytecodeP2ST​

new LockingBytecodeP2ST(bytes20 templateHash, bytes constraintHash, bytes visibleParameters): bytes

Creates new P2ST locking bytecode for the template script hash templateHash, the constraints script hash constraintHash given the minimally encoded visible parameters.

new LockingBytecodeP2ST(bytes20 templateHash, bytes constraintHash, bytes visibleParameters, bytes groupId, int amount): bytes

Creates new grouped P2ST locking bytecode, i.e. the same output carrying tokens of group groupId (a 32 byte group or a longer subgroup ID). amount is a normal integer; the compiler converts it to the on-chain token quantity encoding:

  • amount >= 0 is a token quantity, encoded as an unsigned little-endian number of 2, 4 or 8 bytes (the smallest that fits).
  • amount < 0 is an authority: the value is the 64 bit authority field (flags in the top bits, AUTHORITY being bit 63), encoded as 8 bytes.

An empty groupId builds the locking bytecode of an ungrouped output, the same as the 3 argument form, and amount is ignored: ungrouped outputs have no amount field. So a covenant can forward any input, with or without tokens, from its own token fields:

// require output i to carry the tokens of input i (if any) back to this contract
int i = this.activeInputIndex;
require(tx.outputs[i].lockingBytecode == new LockingBytecodeP2ST(
templateHash, constraintHash, visibleParams, tx.inputs[i].tokenGroupId, tx.inputs[i].tokenAmount));
// require output 0 to send `amount` tokens of `groupId` back to this contract
require(tx.outputs[0].lockingBytecode
== new LockingBytecodeP2ST(templateHash, constraintHash, visibleParams, groupId, amount));
caution

The comparison is byte exact. The node also accepts non-minimal quantity encodings (e.g. an amount of 5 in 8 bytes), and such an output will not match. Wallets use the minimal encoding.

LockingBytecodeNullData​

new LockingBytecodeNullData(bytes[] chunks): bytes

Creates new OP_RETURN locking bytecode with chunks as its OP_RETURN data.