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)