Skip to main content

Asset model

Neurai uses a UTXO model. Asset balances are derived from spendable outputs that embed asset payloads in scriptPubKey. There is no global asset state outside the chain.

Asset types

Neurai supports 11 asset types:

TypeNamePrefix/SuffixCost (XNA)Purpose
0ROOT-1000Top-level namespace
1SUB/ separator200Sub-asset under ROOT
2UNIQUE# separator10Unique/NFT-style asset
3MSGCHANNEL~ separator200Messaging channel (legacy)
4QUALIFIER# prefix2000KYC/identity tag
5SUB_QUALIFIER#/#200Sub-qualifier tag
6RESTRICTED$ prefix3000Restricted/securities asset
7VOTEreserved0Voting asset (future)
8REISSUE-200Reissue operation
9OWNER! suffix0Ownership token
10NULL_ADD_QUALIFIER-0.2Add qualifier to address

Naming rules (examples)

  • ROOT: MYTOKEN, PROJECT_X, GAME.TOKEN
  • SUB: GAME/GOLD, PROJECT/VIP
  • UNIQUE: ART#PIECE_001, TICKETS#ROW_A_SEAT_12
  • RESTRICTED: $COMPANY
  • QUALIFIER: #KYC, #ACCREDITED
  • OWNER: ASSET! (auto-created on issuance)

Script encoding

Assets are embedded in the output script using OP_XNA_ASSET:

<base script> OP_XNA_ASSET <pushdata: payload> OP_DROP

OP_XNA_ASSET is defined as 0xc0. The payload begins with a 4-byte prefix:

  • rvnq: new asset (issue)
  • rvno: owner token output
  • rvnr: reissue
  • rvnt: transfer

The remainder is the serialized structure (via CDataStream), depending on the prefix.

Implementation note: CScript::IsAssetScript expects OP_XNA_ASSET after the base P2PKH script (index 25 in the standard layout).

Base P2PKH example

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

Issue/transfer/reissue templates

OP_DUP OP_HASH160 <20B_HASH160> OP_EQUALVERIFY OP_CHECKSIG
OP_XNA_ASSET <PUSHDATA(payload)> OP_DROP

Payloads:

  • Issue: rvnq + CNewAsset
  • Transfer: rvnt + CAssetTransfer
  • Reissue: rvnr + CReissueAsset

Null-data scripts (qualifier/restricted)

Certain qualifier and restricted operations use non-spendable scripts:

  1. Address tag / restriction:
OP_XNA_ASSET <hash160(dest)> <PUSHDATA(CNullAssetTxData)>
  1. Verifier string for restricted assets:
OP_XNA_ASSET OP_RESERVED <PUSHDATA(CNullAssetTxVerifierString)>
  1. Global restriction:
OP_XNA_ASSET OP_RESERVED OP_RESERVED <PUSHDATA(CNullAssetTxData)>

These scripts are detected by their prefixes in src/script/script.cpp and are not spendable outputs.

Issuance transaction layout

A standard issuance has at least three outputs:

  1. Burn output: XNA sent to a type-specific burn address.
  2. Owner output: ASSET! owner token (rvno).
  3. Issue output: asset issuance (rvnq).

Burn amount and burn address are type-specific and enforced in assets.cpp (see GetBurnAmount / GetBurnAddress).

Type-specific burn addresses (examples)

  • ROOT: NbURNXXXXXXXXXXXXXXXXXXXXXXXT65Gdr
  • SUB: NXissueSubAssetXXXXXXXXXXXXXX6B2JF
  • UNIQUE: NXissueUniqueAssetXXXXXXXXXXUBzP4Z

Special cases

  • UNIQUE: multiple rvnq outputs (one per unique), plus the owner output and per-unique burn.
  • RESTRICTED: includes a verifier string output (CNullAssetTxVerifierString).
  • QUALIFIER: uses CNullAssetTxData for address tagging, may require extra burn.

Transfer transaction layout

Transfers embed a CAssetTransfer payload:

<base script> OP_XNA_ASSET <rvnt + CAssetTransfer> OP_DROP

CAssetTransfer fields:

  • asset name
  • amount
  • optional message (text or IPFS/txid)
  • optional expireTime (only serialized when message exists)

The message field is the primary place to attach descriptive metadata to a transfer.

Diagram: asset payload in scriptPubKey

P2PKH base script                              Asset payload
----------------- -------------
OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_XNA_ASSET <pushdata> OP_DROP
OP_CHECKSIG

<pushdata> = prefix + serialized struct
prefix = rvnq | rvno | rvnr | rvnt

Reissue transaction layout

Reissue outputs embed rvnr + CReissueAsset. Reissue is controlled by the owner token (ASSET!) and can change:

  • total supply (additional amount)
  • decimal units (if allowed)
  • reissuable flag
  • IPFS/txid metadata

End-to-end flow (issue → transfer → reissue)

  1. Issue (ROOT):
neurai-cli issue "ACME" 1000 "Ndst..." "Nchange..." 2 true false

Outputs: burn, owner (ACME!), issue (ACME).

  1. Transfer:
neurai-cli transfer "ACME" 25.50 "Ndest..." "Invoice 2024-001" 0 "Nchange..." "Nassetchange..."
  1. Reissue:
neurai-cli reissue "ACME" 500 "Ndest..." 2 true "QmMetadata..."

Implementation notes

  • Asset scripts are detected by CScript::IsAssetScript (expects OP_XNA_ASSET after the base P2PKH script).
  • AssetFromScript, TransferAssetFromScript, ReissueAssetFromScript parse the payload.
  • Burn amounts and addresses are in chainparams and validated during issuance.

Diagram: issuance flow (ROOT)

Client/RPC                      Transaction outputs
--------- --------------------
issue ROOT
|
+-- burn output (XNA -> burn address)
+-- owner output (ASSET! with rvno)
+-- issue output (ASSET with rvnq)

Diagram: transfer flow

Client/RPC                      Transaction outputs
--------- --------------------
transfer ASSET amount
|
+-- destination output
P2PKH + OP_XNA_ASSET + rvnt + CAssetTransfer
(message/expireTime optional)

Diagram: reissue flow

Client/RPC                      Transaction outputs
--------- --------------------
reissue ASSET amount
|
+-- burn output (XNA -> burn address)
+-- reissue output (ASSET with rvnr + CReissueAsset)
+-- optional metadata update (IPFS/txid)