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.
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.
| Capability | Opcodes | What it lets a script say |
|---|---|---|
| Commit to a template | OP_CHECKTEMPLATEVERIFY | "This output can only be spent by exactly this transaction shape" |
| Commit to selected fields | OP_TXHASH with a bitmask | "The outputs must be these, the inputs may be anything" |
| Verify external data | OP_CHECKSIGFROMSTACK, ECDSA or ML-DSA-44 | "Only if an oracle signed this price" |
| Inspect outputs | OP_OUTPUTVALUE, OP_OUTPUTSCRIPT, OP_OUTPUTASSETFIELD, OP_OUTPUTAUTHCOMMITMENT | "Output 0 pays the seller at least X", "output 2 carries this same covenant" |
| Inspect inputs | OP_INPUTVALUE, OP_INPUTASSETFIELD, OP_TXFIELD, counts | "How many tokens are being spent from me" |
| Read state without spending it | OP_REFINPUTFIELD, OP_REFINPUTASSETFIELD on transaction v3 | "Check the current value of that oracle UTXO" |
| Know where we are | OP_CHAINCONTEXT | "Valid only before height H or before this time" |
| Build and compare data | OP_CAT, OP_SPLIT, OP_REVERSEBYTES, 64-bit OP_MUL OP_DIV OP_MOD | Prices, remainders, serialized structures |
| Modern hashes and signatures | Keccak, BLAKE2b, BLAKE3, SHA3, Poseidon, Ed25519, Merkle inclusion, OP_CHECKSIGADD | Bridges 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 type | Key check | Use |
|---|---|---|
0x00 NoAuth | None | Pure covenants: the script alone decides |
0x01 PQ | ML-DSA-44 signature | Post-quantum protected outputs with optional script rules |
0x02 Legacy | secp256k1 signature | Classical 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.
How one fill is enforced
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 × unitPriceand its script equals the seller's payment script. - Delivery: output 1 is a transfer of exactly
Nunits of the right asset. - Continuity: output 2 carries the same AuthScript commitment as the input being spent, the same asset name, and
inputAmount − Nunits. Reading the input amount withOP_INPUTASSETFIELDis what lets one script serve the whole life of the order as the lot drains. - Deadline, optionally:
OP_CHAINCONTEXTmust 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
| Contract | Built from | Status |
|---|---|---|
| Partial-fill sell order | Output and input introspection, OP_CHAINCONTEXT, OP_OUTPUTAUTHCOMMITMENT | Implemented in neurai-scripts |
| Vault with delayed withdrawal | OP_CHECKTEMPLATEVERIFY committing to a timelocked second stage | Feasible today on testnet |
| Batched payouts | OP_CHECKTEMPLATEVERIFY over a fixed set of outputs | Feasible today on testnet |
| Oracle-gated release | OP_CHECKSIGFROMSTACK over a signed price or event | Feasible today on testnet |
| Cross-chain atomic swap with proof | OP_CAT, OP_SPLIT, Keccak or SHA3, OP_CHECKMERKLEINCLUSION | Feasible today on testnet |
| Post-quantum threshold custody | OP_CHECKSIGADD with ML-DSA-44 keys | Feasible today on testnet |
| Shared state read by many contracts | Reference inputs on transaction v3 | Feasible today on testnet |
| Automated market maker | Introspection plus 64-bit arithmetic | Planned 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
getblockchaininfofor 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_CHECKSIGFROMSTACKis 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
- Covenants and opcodes: the opcode catalogue and AuthScript.
- Transactions and consensus: transaction v3 and reference inputs.
neurai-scriptson GitHub: source, tests and the guide for adding new covenants.- Node repository,
doc/covenants.md: full specification of every opcode with stack diagrams.