Skip to main content

Smart Contracts

Neurai has no virtual machine and no contract accounts. What it has, with POSITRONIC, is a Script engine that can inspect the transaction that spends an output and refuse it unless it matches the rules written in the script. Contracts built this way are called covenants. They run inside AuthScript outputs, are enforced by every node as part of consensus, and cost nothing beyond the transaction fee.

This page explains what the opcodes let you express, shows one contract that is already implemented and published, the partial-fill sell order, and lists what else can be built with the same pieces.

Status

The covenant opcodes are active on testnet and regtest and inactive on mainnet until POSITRONIC activates. The @neuraiproject/neurai-scripts package targets testnet today.

What a covenant can look at

A classical script can only check signatures and a few locks. A covenant script on Neurai can read almost everything about the spending transaction and the chain, and compute on it.

witnessScriptruns inside an AuthScript outputevery rule enforced by consensusTransactionOP_TXHASH OP_TXFIELDOP_TXLOCKTIME OP_INPUTCOUNTOP_OUTPUTCOUNTOutputsOP_OUTPUTVALUE OP_OUTPUTSCRIPTOP_OUTPUTASSETFIELDOP_OUTPUTAUTHCOMMITMENTInputs and their prevoutsOP_INPUTVALUEOP_INPUTASSETFIELDOP_TXFIELD (spent output)Reference inputs (tx v3)OP_REFINPUTCOUNTOP_REFINPUTFIELDOP_REFINPUTASSETFIELDChain and external dataOP_CHAINCONTEXT height · MTPOP_CHECKSIGFROMSTACK oracleOP_CHECKTEMPLATEVERIFYBytes, math and hashesOP_CAT OP_SPLIT OP_REVERSEBYTESOP_MUL OP_DIV OP_MOD 64-bitSHA3 · BLAKE3 · Keccak · Poseidon
CapabilityOpcodesWhat it lets a script say
Commit to a templateOP_CHECKTEMPLATEVERIFY"This output can only be spent by exactly this transaction shape"
Commit to selected fieldsOP_TXHASH with a bitmask"The outputs must be these, the inputs may be anything"
Verify external dataOP_CHECKSIGFROMSTACK, ECDSA or ML-DSA-44"Only if an oracle signed this price"
Inspect outputsOP_OUTPUTVALUE, OP_OUTPUTSCRIPT, OP_OUTPUTASSETFIELD, OP_OUTPUTAUTHCOMMITMENT"Output 0 pays the seller at least X", "output 2 carries this same covenant"
Inspect inputsOP_INPUTVALUE, OP_INPUTASSETFIELD, OP_TXFIELD, counts"How many tokens are being spent from me"
Read state without spending itOP_REFINPUTFIELD, OP_REFINPUTASSETFIELD on transaction v3"Check the current value of that oracle UTXO"
Know where we areOP_CHAINCONTEXT"Valid only before height H or before this time"
Build and compare dataOP_CAT, OP_SPLIT, OP_REVERSEBYTES, 64-bit OP_MUL OP_DIV OP_MODPrices, remainders, serialized structures
Modern hashes and signaturesKeccak, BLAKE2b, BLAKE3, SHA3, Poseidon, Ed25519, Merkle inclusion, OP_CHECKSIGADDBridges to other chains, proofs, threshold schemes

The full catalogue with bytes and activation rules is in Covenants and opcodes.

Where contracts live: AuthScript

An AuthScript output is OP_1 <32-byte commitment>, where the commitment hashes an auth type, an optional public key and the witness script. Spending reveals them; the node checks the key if there is one and then runs the script.

Auth typeKey checkUse
0x00 NoAuthNonePure covenants: the script alone decides
0x01 PQML-DSA-44 signaturePost-quantum protected outputs with optional script rules
0x02 Legacysecp256k1 signatureClassical key plus covenant logic

Because authentication is separate from the script, a covenant is written once and works with any key type.

A working contract: the partial-fill sell order

@neuraiproject/neurai-scripts ships a complete decentralized sell order. A seller locks a lot of tokens at a fixed price. Any number of buyers take any fraction of it, paying the seller in the same transaction, without the seller being online and without a matching server or an escrow. The covenant is the order book entry.

Alice publishes100 CAT locked1 XNA per CATBob fills 5pays 5 XNA to Aliceremainder 95 CATCarol fills 5pays 5 XNA to Aliceremainder 90 CATAlice cancelssigns the cancel branchrecovers 90 CATthe covenant UTXO is the order book entry: no matching server, no escrow, no second signature from the sellerfills can chain in the mempool before confirmation · an optional deadline by height or median time past stops new fills

How one fill is enforced

inputsvin[0] covenant UTXOunlock: <N> <0> <0> · 95 CATvin[1..] buyer's XNAordinary P2PKH, SIGHASH_ALLoutputs, order enforcedvout[0] N × price XNA → sellerOP_OUTPUTVALUE · OP_OUTPUTSCRIPTvout[1] N CAT → buyerOP_OUTPUTASSETFIELD name and amountvout[2] 95 − N CAT → same covenantOP_OUTPUTAUTHCOMMITMENT == OP_TXFIELDvout[3..] buyer changeunconstrainedwitnessScriptpartial-fill branchreads N from the stack,checks every output

The script reads the fill amount N from the unlock stack and then checks, against parameters hardcoded when the order was created:

  • Payment: output 0 has value at least N × unitPrice and its script equals the seller's payment script.
  • Delivery: output 1 is a transfer of exactly N units of the right asset.
  • Continuity: output 2 carries the same AuthScript commitment as the input being spent, the same asset name, and inputAmount − N units. Reading the input amount with OP_INPUTASSETFIELD is what lets one script serve the whole life of the order as the lot drains.
  • Deadline, optionally: OP_CHAINCONTEXT must still be below the configured height or median time past.

Three branches are selected by the top of the unlock stack: cancel (the seller signs and recovers the remainder), full fill (a buyer drains the lot, no continuation output because a zero-amount asset transfer is invalid) and partial fill. Fills can chain in the mempool on top of each other before confirmation.

Using it

import {
buildPartialFillScriptHex,
buildFillScriptSigHex,
buildCancelScriptSigHex,
parsePartialFillScript
} from '@neuraiproject/neurai-scripts';

// 1) Alice publishes an order: 100 CAT at 1 XNA per CAT, valid until height 1,250,000.
const scriptPubKeyHex = buildPartialFillScriptHex({
sellerAddress: aliceP2PKHAddress, // "t..." on testnet
tokenId: 'CAT',
unitPriceSats: 100_000_000n,
expiration: { mode: 'height', value: 1_250_000n }
});

// 2) Bob takes 5 CAT. The fill transaction is assembled with neurai-create-transaction:
// vin[0] = covenant UTXO with this scriptSig, vin[1..] = Bob's XNA,
// vout[0] = 5 XNA to Alice, vout[1] = 5 CAT to Bob, vout[2] = 95 CAT to the same covenant.
const bobScriptSigHex = buildFillScriptSigHex(5n);

// 3) Alice cancels on the latest remainder UTXO with a normal SIGHASH_ALL signature.
const cancelScriptSigHex = buildCancelScriptSigHex(aliceCancelSig, alicePubKey);

// 4) An indexer finds live orders by parsing scriptPubKeys.
const order = parsePartialFillScript(utxo.scriptPubKeyHex, 'xna-test');
// order.tokenId, order.unitPriceSats, order.sellerPubKeyHash, order.expiration

A post-quantum variant, buildPartialFillScriptPQHex, replaces the ECDSA cancel branch with OP_CHECKSIGFROMSTACK over an ML-DSA-44 signature of OP_TXHASH, and accepts a bech32m payment destination. NIP-018 raises the script element cap to 3072 bytes so the signature fits.

Script layout of the partial-fill branch
OP_IF // cancel: seller signs
OP_DUP OP_HASH160 <sellerPKH> OP_EQUALVERIFY OP_CHECKSIG
OP_ELSE
OP_IF // full fill: N = whole input, no vout[2]
...
OP_ELSE // partial fill, stack: [ N ]
<deadline> <HEIGHT|MTP> OP_CHAINCONTEXT OP_GREATERTHAN OP_VERIFY // optional
OP_DUP <unitPriceSats> OP_MUL
<0> OP_OUTPUTVALUE OP_SWAP OP_GREATERTHANOREQUAL OP_VERIFY // payment value
<0> OP_OUTPUTSCRIPT <sellerP2PKH> OP_EQUALVERIFY // payment destination
OP_DUP <1> <0x02> OP_OUTPUTASSETFIELD OP_EQUALVERIFY // buyer gets N
<1> <0x01> OP_OUTPUTASSETFIELD <tokenId> OP_EQUALVERIFY // right asset
<2> OP_OUTPUTAUTHCOMMITMENT <0x02> OP_TXFIELD OP_EQUALVERIFY // same covenant
<2> <0x01> OP_OUTPUTASSETFIELD <tokenId> OP_EQUALVERIFY // remainder asset
<2> <0x02> OP_OUTPUTASSETFIELD OP_OVER
<0> <0x02> OP_INPUTASSETFIELD OP_SWAP OP_SUB OP_EQUALVERIFY // remainder = in − N
OP_DROP OP_1
OP_ENDIF
OP_ENDIF

Known limits of the current version: payment only in XNA, fixed output order, no minimum or maximum fill size, and no automated market maker.

What else the same pieces build

ContractBuilt fromStatus
Partial-fill sell orderOutput and input introspection, OP_CHAINCONTEXT, OP_OUTPUTAUTHCOMMITMENTImplemented in neurai-scripts
Vault with delayed withdrawalOP_CHECKTEMPLATEVERIFY committing to a timelocked second stageFeasible today on testnet
Batched payoutsOP_CHECKTEMPLATEVERIFY over a fixed set of outputsFeasible today on testnet
Oracle-gated releaseOP_CHECKSIGFROMSTACK over a signed price or eventFeasible today on testnet
Cross-chain atomic swap with proofOP_CAT, OP_SPLIT, Keccak or SHA3, OP_CHECKMERKLEINCLUSIONFeasible today on testnet
Post-quantum threshold custodyOP_CHECKSIGADD with ML-DSA-44 keysFeasible today on testnet
Shared state read by many contractsReference inputs on transaction v3Feasible today on testnet
Automated market makerIntrospection plus 64-bit arithmeticPlanned in the NIP roadmap

Everything in the table is Script: no new trust assumptions, no execution fees beyond bytes, and the same tooling for classical and post-quantum keys.

Standard scripts and the builder

The same package covers the ordinary scripts and a builder for anything custom:

import { ScriptBuilder, opcodes, encodeMultisigRedeemScript, encodeP2SHScriptPubKey } from '@neuraiproject/neurai-scripts';

// Any script, byte by byte
const p2pkh = new ScriptBuilder()
.op(opcodes.OP_DUP, opcodes.OP_HASH160)
.pushBytes(pkh20)
.op(opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG)
.buildHex();

// 2-of-3 multisig wrapped in P2SH
const redeemScript = encodeMultisigRedeemScript({ m: 2, pubKeys: [pk1, pk2, pk3] });
const scriptPubKey = encodeP2SHScriptPubKey(hash160(redeemScript));

Also available: P2WPKH, P2WSH, AuthScript outputs with witness builders for the three auth types, and OP_RETURN payloads. Selector constants for every introspection opcode are exported, so a script never hardcodes magic bytes. Transactions are assembled with neurai-create-transaction and signed with neurai-sign-transaction. See Libraries.

Practical notes

  • Activation is per opcode. A script that uses an inactive opcode fails on mainnet today. Build and test on testnet, and check getblockchaininfo for the chain you target.
  • Asset amounts are raw. Scripts compare amounts in the smallest unit, value × 10^8, exactly as the node stores them.
  • Output order is part of the contract. The partial-fill covenant fixes vout[0..2]; put change after them.
  • Keep pushes under the caps. 520 bytes on legacy scripts, 3072 bytes inside AuthScript when OP_CHECKSIGFROMSTACK is active.
  • Index by parsing. Covenant scripts carry their parameters in the clear. An order book or an explorer reads them with the package's parsers instead of a database of its own.

Further reading