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:
| Type | Name | Prefix/Suffix | Cost (XNA) | Purpose |
|---|---|---|---|---|
| 0 | ROOT | - | 1000 | Top-level namespace |
| 1 | SUB | / separator | 200 | Sub-asset under ROOT |
| 2 | UNIQUE | # separator | 10 | Unique/NFT-style asset |
| 3 | MSGCHANNEL | ~ separator | 200 | Messaging channel (legacy) |
| 4 | QUALIFIER | # prefix | 2000 | KYC/identity tag |
| 5 | SUB_QUALIFIER | #/# | 200 | Sub-qualifier tag |
| 6 | RESTRICTED | $ prefix | 3000 | Restricted/securities asset |
| 7 | VOTE | reserved | 0 | Voting asset (future) |
| 8 | REISSUE | - | 200 | Reissue operation |
| 9 | OWNER | ! suffix | 0 | Ownership token |
| 10 | NULL_ADD_QUALIFIER | - | 0.2 | Add 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 outputrvnr: reissuervnt: 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:
- Address tag / restriction:
OP_XNA_ASSET <hash160(dest)> <PUSHDATA(CNullAssetTxData)>
- Verifier string for restricted assets:
OP_XNA_ASSET OP_RESERVED <PUSHDATA(CNullAssetTxVerifierString)>
- 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:
- Burn output: XNA sent to a type-specific burn address.
- Owner output:
ASSET!owner token (rvno). - 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
rvnqoutputs (one per unique), plus the owner output and per-unique burn. - RESTRICTED: includes a verifier string output (
CNullAssetTxVerifierString). - QUALIFIER: uses
CNullAssetTxDatafor 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 whenmessageexists)
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)
- Issue (ROOT):
neurai-cli issue "ACME" 1000 "Ndst..." "Nchange..." 2 true false
Outputs: burn, owner (ACME!), issue (ACME).
- Transfer:
neurai-cli transfer "ACME" 25.50 "Ndest..." "Invoice 2024-001" 0 "Nchange..." "Nassetchange..."
- Reissue:
neurai-cli reissue "ACME" 500 "Ndest..." 2 true "QmMetadata..."
Implementation notes
- Asset scripts are detected by
CScript::IsAssetScript(expectsOP_XNA_ASSETafter the base P2PKH script). AssetFromScript,TransferAssetFromScript,ReissueAssetFromScriptparse 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)