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 the following 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 |
| 12 | DEPIN | & prefix | 10 | Soulbound identity/device asset (POSITRONIC) |
(Type 11 is INVALID, an internal sentinel and not an issuable asset.)
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) - DEPIN:
&DEVICE,&FLEET/UNIT001(sub-DePIN with/)
DePIN assets (soulbound)
AssetType::DEPIN = 12 is a new asset class introduced by the POSITRONIC upgrade for device identity, memberships and bound credentials. It is soulbound (creator-gated transfers) and doubles as the access key for the encrypted DePIN chat and MCP channels. It is currently enabled on testnet and regtest only.
Naming and parameters
- Prefix
&: names match&[A-Z0-9._]{3,}(e.g.&DEVICE). Sub-DePIN names use/for fleet hierarchies, e.g.&FLEET/UNIT001. units = 0: there is no fractional supply. Issuance and reissue both reject any other value (DEPIN_ASSET_UNITS); a transfer must move a positive integer amount.- Burn cost: mirrors the UNIQUE asset issuance fee — 10 XNA — and uses the same burn address as unique assets (
GetIssueUniqueAssetBurnAmount/IssueUniqueAssetBurnAddress). - Owner token: issuance creates the matching
&NAME!owner token, exactly as for ROOT/UNIQUE assets.
Soulbound transfers
Every DePIN transfer is co-signed by the creator. The wallet auto-attaches the asset's owner token to the transaction, so a holder cannot move the asset alone (AddressHasDEPINOwnerToken). If the owner token is missing, validation fails with bad-txns-...doesn't have owner token for DEPIN asset.
Revocation flags
DePIN borrows the restricted-asset null-data machinery (CNullAssetTxData) to manage holder validity, persisted in the restricted DB:
- Owner revocation (
R): the creator can freeze any holder's address, immediately invalidating that holder's DePIN asset. This is validated byVerifyDEPINOwnerChange. The owner token's own address is exempt and can never be frozen. - Holder self-revocation (
S): a holder can mark their own asset invalid (SELF_RESTRICTED_FLAG = 'S', validated byVerifySelfRestrictionChange). Once self-revoked, the asset stops being valid for that address; only the creator can re-activate it via freeze/unfreeze withflag = 0.
Reissue and sub-issuance
Reissue requires the owner token to be present at the creator's address and must keep units = 0. Sub-DePIN assets are issued under a parent (&FLEET/UNIT001) for fleet-style structures without polluting the root namespace.
Script encoding
DePIN assets use the same OP_XNA_ASSET payload framing as other assets (issue rvnq, owner rvno, reissue rvnr, transfer rvnt); the soulbound behaviour and revocation come from the owner-token co-signing rule and the R / S null-data flags above, not from a new script prefix. Detection is via IsAssetNameADEPIN.
Typical uses
- Per-unit device identity for IoT / DePIN hardware (one asset per unit, owner-gated).
- Memberships and access passes the issuer can revoke without burning supply.
- Soulbound credentials: badges, certifications, reputation tags.
- Gating the encrypted DePIN chat and MCP-agent channels to the holder set.
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)