Assets
Native programmable assets are a core concept in MOI. Asset rules (minting, burning, transfers, approvals) are enforced at the protocol level through asset logic written in Coco.
Asset Logic
Declare an asset logic with the asset keyword:
coco asset MyToken
Asset logic has privileged access to the asset engine via the asset global object.
Asset Engine Methods
Lifecycle
| Method | Description |
|---|---|
asset.Define(symbol, decimals, manager, creator, max_supply, enable_events) | Define asset properties (call in deploy) |
Transfers & Ownership
| Method | Description |
|---|---|
asset.Transfer(token_id, beneficiary, amount) | Transfer from Sender to beneficiary |
asset.TransferFrom(token_id, benefactor, beneficiary, amount) | Transfer from benefactor (requires approval) |
asset.Mint(token_id, beneficiary, amount) | Mint new tokens |
asset.MintWithMetadata(token_id, beneficiary, amount, static_metadata) | Mint with metadata |
asset.Burn(token_id, amount) | Burn tokens from Sender |
asset.Lockup(token_id, beneficiary, amount) | Lock tokens |
asset.Release(token_id, benefactor, beneficiary, amount) | Release locked tokens |
asset.Approve(token_id, beneficiary, amount, expires_at) | Approve spending |
asset.Revoke(token_id, beneficiary) | Revoke approval |
Queries
| Method | Returns | Description |
|---|---|---|
asset.BalanceOf(token_id, address) | U256 | Balance of address |
asset.Symbol() | String | Asset symbol |
asset.Creator() | Identifier | Creator address |
asset.Manager() | Identifier | Manager address |
asset.Decimals() | U64 | Decimal places |
asset.MaxSupply() | U256 | Maximum supply |
asset.CirculatingSupply() | U256 | Current supply |
asset.EnableEvents() | Bool | Whether events enabled |
Metadata
| Method | Description |
|---|---|
asset.SetStaticMetadata(key, value) | Set asset-level static metadata |
asset.SetDynamicMetadata(key, value) | Set asset-level dynamic metadata |
asset.GetStaticMetadata(key) | Read static metadata → Bytes |
asset.GetDynamicMetadata(key) | Read dynamic metadata → Bytes |
asset.SetStaticTokenMetadata(token_id, key, value) | Per-token static metadata |
asset.SetDynamicTokenMetadata(token_id, key, value) | Per-token dynamic metadata |
asset.GetStaticTokenMetadata(token_id, key) | Read per-token static → Bytes |
asset.GetDynamicTokenMetadata(token_id, key) | Read per-token dynamic → Bytes |
Example
my_token.coco
coco asset MyToken
event TransferEvent:
topic from Identifier
topic to Identifier
field amount U256
endpoint Transfer(to Identifier, amount U256):
if asset.EnableEvents():
emit TransferEvent{from: Sender, to: to, amount: amount}
asset.Transfer(token_id: 0, beneficiary: to, amount: amount)
endpoint Mint(to Identifier, amount U256):
if Sender != asset.Manager():
throw "Unauthorized"
asset.Mint(token_id: 0, beneficiary: to, amount: amount)
Calling Asset Logic
Regular logic cannot access the asset engine directly. It must call asset logic through an interface.
payment.coco
coco Payment
interface Token:
asset:
Transfer(to Identifier, amount U256)
endpoint Pay(token_id Identifier, recipient Identifier, amount U256):
memory token = Token(token_id)
token.Transfer(to: recipient, amount: amount)
Creating Assets
Assets are created via MOI interactions, not directly in Coco. In Cocolab:
compile MyToken from manifest(mytoken.yaml)
create MyToken(symbol: "MTK", decimals: 18, manager: user1, max_supply: 1000000)
Key Points
- Only asset logic can access the
assetglobal object - Regular logic must use interfaces to interact with assets
- This separation ensures security and programmability