Interactions
An Interaction is the unit of state change in MOI — the equivalent of a transaction in a global-state network, but built around participants rather than a shared global tree.
This page covers the execution model behind Interactions, the structure of an Interaction payload, the full set of protocol-native operations, and the receipt the network produces once an Interaction executes.
Prerequisites: This guide assumes familiarity with MOI's core state primitives — specifically accounts, assets, and logics. If these concepts are new, start with the Accounts, Assets, and Logics documentation first.
Execution Models
An execution model defines how state changes are triggered, grouped, and executed. MOI's model is easiest to understand against the model it replaces.
Legacy Transactions (Application-Centric)
Networks with a single global state, such as Ethereum, treat a transaction as a generic trigger: an instruction to run a function in a shared smart contract. The protocol has no idea what that function actually does — minting, swapping, or nothing at all. All meaning lives in the application layer, which creates three structural problems:
Redundant boilerplate. Basic capabilities like issuing a token require deploying and auditing a contract (ERC-20 and friends) before any value can move.
A wider attack surface. Routine network operations are reimplemented as custom code, so a single flaw in a widely reused contract can compromise thousands of users.
Sequential bottlenecks. Any transaction can touch any part of global state, so the network must order transactions conservatively to avoid collisions. That rules out parallelism.
The root problem: legacy transactions push standard protocol mechanics into custom smart contracts, paying for it in security, scalability, and developer overhead.
MOI Interactions (Participant-Centric)
MOI anchors state to individual participants. An Interaction tells the protocol exactly whose state it touches and what it intends to do, which is enough context to execute the state transition inside an isolated, ephemeral cluster.
Protocol-Native Logic: creating an asset, transferring it, staking to a validator, are implemented inside the protocol as domain-specific operations (IxOps) and triggered directly through the SDKs. No boilerplate contract to deploy or audit.
Parallel Execution: Interactions explicitly declare the participant states they need to access. The network forms localized consensus clusters by locking only these specific states. Since interactions involving disjoint participants do not compete for the same state locks, they execute concurrently.
Cost-Efficient Batching: Users can pack multiple operations (IxOps) into a single interaction. Executing a batch of operations under a single localized state lock minimizes consensus overhead and consumes significantly less fuel than submitting each operation independently.
Example
Alice wants to deploy a new application logic and simultaneously transfer an existing digital asset to Bob.
Legacy model: Alice deploys the smart contract (transaction 1), waits for the network to confirm it, and then submits a separate token transfer (transaction 2). Two triggers, two full rounds of overhead, and a confirmation wait in between.
MOI: Alice submits one Interaction carrying two IxOps:
LogicDeploy(to spin up the application) andAssetAction(to transfer the asset). The protocol executes both natively in a single batch. One round of overhead, two state transitions.
Anatomy of an Interaction
| Parameter | Type | Required | Description |
|---|---|---|---|
sender | Sender | Yes | The entity initiating and authorizing the Interaction. |
ix_operations | Array<IxOperation> | Yes | The protocol-native operations to execute. |
participants | Array<Participant> | Yes | The accounts whose state the runtime is allowed to see. Anything not listed here is invisible to execution. |
fuel_price | HEX | Yes | KMOI the sender will pay per unit of execution fuel. |
fuel_limit | HEX | Yes | Ceiling on the fuel this Interaction may consume. Must be non-zero. |
payer | HEX (32 bytes) | No | A different participant who covers the fuel cost, enabling sponsored Interactions. Defaults to the sender. |
preferences | IxPreferences | No | Overrides the default compute and consensus configuration for this Interaction. |
perception | Bytes | No | Encodes the perceived value of assets at execution time (for example, 1 KMOI = 1 USD). |
Sender
Identifies who initiated the Interaction and ensures ordered execution to prevent replay attacks.
| Field | Type | Description |
|---|---|---|
| id | HEX (32 bytes) | Unique identifier of the sender account |
| sequence_id | Number | A strictly incrementing nonce. The protocol rejects any sequenceID it has already processed, preventing replays. |
| key_id | Number | Index of the sender's public key used to sign the payload. |
There is an optional field, payer which allows other participants to sponser the Interaction, by paying the Interaction cost for the sender.
Execution Fuel
Every Interaction pays for the compute it consumes, in KMOI.
fuel_priceis the rate the sender offers per unit of fuel. It must meet or exceed the network's minimum (1on Public Devnet).fuel_limitcaps total consumption, which is what stops a runaway loop from draining an account.- The account charged is the
payerif one is declared, and the sender otherwise.
Fuel is charged for work performed, so a failed Interaction still costs KMOI. How much depends on how it failed — see Operation Status Codes.
Participants and State Isolation
This is the critical distinction in MOI. On a global-state network, a transaction can read anyone's state. MOI does not allow this. The runtime executes in a strict sandbox: an account's state cannot be read or modified unless that account is declared in participants.
The sender is included implicitly. Which additional participants are required depends on the operation - an AssetAction transfer needs the beneficiary and the asset account, a LogicInvoke needs the logic account, and so on. Each operation section below shows a worked example.
Participant
| Field | Type | Description |
|---|---|---|
id | HEX (32 bytes) | Unique identifier of the participant. |
lock_type | LockType | The access the runtime is granted over this participant's state. |
notary | Boolean | Whether this participant's signature is required to finalize the Interaction. |
LockType
| Value | Name | Description |
|---|---|---|
| 0 | MutateLock | State may change — deducting funds, updating stored data. |
| 1 | ReadLock | State is visible to the runtime but cannot be altered. |
| 2 | NoLock | No lock is taken. The account is referenced but its state is unaffected. |
Network Preferences
preferences is optional. When present, it lets the initiator dictate how the ephemeral cluster should process and validate the state transition.
IxPreferences
| Field | Type | Description |
|---|---|---|
compute | Bytes | Encoded parameters defining the execution environment requirements. |
consensus | IxConsensusPreference | Localized trust and validation thresholds for the cluster. |
IxConsensusPreference
| Field | Type | Description |
|---|---|---|
TrustNodes | Array<KramaID> | Node identities the initiator explicitly trusts to participate in consensus. |
MTQ | uint | Modulated Trust Quotient — how many nodes beyond TrustNodes must also participate before the Interaction finalizes. |
Operation Reference
Each IxOperation carries a type identifier and a matching payload. MOI groups its protocol-native operations into five categories.
| Type | Name | Category | Purpose | Status |
|---|---|---|---|---|
| 1 | ParticipantCreate | Account | Registers a new participant identity and assigns its signing keys. | Active |
| 2 | AccountConfigure | Account | Adds or revokes keys on an existing account. | Active |
| 3 | AccountInherit | Account | Creates an application-specific sub-account of a primary account. | Active |
| 4 | AssetCreate | Asset | Mints a new native digital asset. | Active |
| 5 | AssetAction | Asset | Invokes a routine on an existing asset -- transfer, mint, burn, custom. | Active |
| 6 | GuardianRegister | Guardian | Registers a node as a network validator and locks its initial stake. | Not yet active |
| 7 | GuardianStake | Guardian | Adds stake to a registered guardian. | Not yet active |
| 8 | GuardianUnstake | Guardian | Begins the unbonding process for staked tokens. | Not yet active |
| 9 | GuardianWithdraw | Guardian | Releases tokens once the unbonding period has elapsed. | Not yet active |
| 10 | GuardianClaim | Guardian | Claims accumulated validator rewards. | Not yet active |
| 11 | LogicDeploy | Logic | Deploys new application logic to the network. | Active |
| 12 | LogicInvoke | Logic | Executes a routine on a deployed logic account. | Active |
| 13 | LogicEnlist | Logic | Subscribes an account to a deployed logic. | Reserved |
| 16 | LogicInteract | Logic | Cross-logic interaction. | Reserved |
| 17 | LogicUpgrade | Logic | Upgrading deployed logic instances. | Reserved |
| 14 | StorageDeposit | Storage | Converts KMOI into a prepaid storage allowance. | Active |
| 15 | StorageWithdraw | Storage | Reclaims unused storage allowance as KMOI. | Active |
| 18 | AccessCreate | Access | Writes a new Access Policy onto the sender's account. | Active |
| 19 | AccessUpdate | Access | Replaces an existing Access Policy. | Active |
| 20 | AccessDelete | Access | Removes an Access Policy. | Active |
Operations for native fuel management, governance, and voting are planned for future rollouts.
ParticipantCreate
Creates a new participant account on the network. In a single atomic step, this operation adds the new account to the network's registry of accounts (Sarga account), binds its initial signing keys, and transfers a starting KMOI balance from the sender, ensuring the participant is fully operational the moment the Interaction executes.
The sender must hold enough KMOI to cover both the execution fuel and the initial funding transfer. If it cannot cover both, the entire Interaction fails.
ParticipantCreateOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 1. |
payload | Payload | Configuration for the new account. |
result | Result | The data returned upon execution in the receipt. |
ParticipantCreatePayload
| Parameter | Type | Description |
|---|---|---|
id | HEX (32 bytes) | Globally unique identifier for the new participant. |
keys_payload | Array<KeyAddPayload> | Initial public keys granting signing authority. Weights must total ≥ 1000. |
value | AssetActionPayload | The KMOI transfer that funds the new account. |
ParticipantCreateResult
| Field | Type | Description |
|---|---|---|
| account_id | HEX (32 Bytes) | Identifier of the newly registered account. |
| error | HEX | Serialised error data if execution failed. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"id": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"keys_payload": [
{
"public_key": "0x00000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000",
"weight": 1000,
"signature_algorithm": 0
}
],
"value": {
"asset_id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000"
}
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 0,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 1,
"payload": {
"id": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"keys_payload": [
{
"public_key": "0x00000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000",
"weight": 1000,
"signature_algorithm": 0
}
],
"value": {
"asset_id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000"
}
}
}
],
"participants": [
{
"id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"lock_type": 2
}
]
}
{
"account_id": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"error": "0x"
}
AccountConfigure
Updates the collection of signing keys for an existing participant account - appending new public keys, or revoking compromised and obsolete ones.
This keeps an identity secure without migrating its assets to a new account.
A single AccountConfigure operation performs either an add or a revoke, never both. Leave the unused array empty.
AccountConfigureOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 2. |
payload | Payload | The keys to add or revoke. |
AccountConfigurePayload
| Parameter | Type | Description |
|---|---|---|
add | Array<KeyAddPayload> | New public keys to append to the authorized signers. |
revoke | Array<KeyRevokePayload> | Existing key indices to revoke immediately. |
KeyAddPayload
| Parameter | Type | Description |
|---|---|---|
public_key | Bytes | Raw byte representation of the public key. |
weight | Number | Authority weight assigned to this key, used in multisig threshold calculations (0–1000). |
signature_algorithm | Number | Identifier for the signature scheme the key uses (ECDSA, BLS, Schnorr). |
KeyRevokePayload
| Parameter | Type | Description |
|---|---|---|
key_id | Number | Index of the key to revoke in the account's key list. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"add": [
{
"public_key": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"weight": 1000,
"signature_algorithm": 0
}
],
"revoke": []
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 1,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 2,
"payload": {
"add": [
{
"public_key": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"weight": 1000,
"signature_algorithm": 0
}
],
"revoke": []
}
}
],
"participants": []
}
AccountConfigure returns no data in the IxOpResult. Success is indicated by the operation's status in the receipt.
AccountInherit
Creates a dedicated sub-account linked to a primary participant. This sub-account inherits the execution context of a specific target application while retaining the signing credentials of the primary account.
Instead of forcing a user's primary account to carry the data of every application they interact with, MOI provisions a separate sub-account for each application to keep state localized and manageable.
Inherited accounts are covered in detail in a dedicated document, coming soon.
AccountInheritOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 3. |
payload | Payload | Target account and funding for the sub-account. |
result | Result | The data returned upon execution in the receipt. |
AccountInheritPayload
| Parameter | Type | Description |
|---|---|---|
target_account | HEX (32 bytes) | The account whose execution context the sub-account inherits. |
value | AssetActionPayload | The KMOI transfer that funds the new sub-account. |
sub_account_index | Number | The sub-account's index in the primary account's sub-account list. |
AccountInheritResult
| Field | Type | Description |
|---|---|---|
sub_account | HEX (32 Bytes) | Identifier of the newly created sub-account. |
error | HEX | Serialised error data if execution failed |
- Payload
- Ix Object
- Receipt
{
"payload": {
"target_account": "0x3b7f1d9b847f8a1f2d2f48701b56e0b6ff6727e7e56a3155db9e1f2b3fbb4d33",
"value": {
"asset_id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000"
},
"sub_account_index": 0
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 2,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 3,
"payload": {
"target_account": "0x20800000a6ba9853f131679d00da0f033516a2efe9cd53c3d54e1f9a00000000",
"value": {
"asset_id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000513b40a069905a1b05bd28d8338ad4a2eff419d7972be75900000000"
},
"sub_account_index": 0
}
}
],
"participants": [
{
"id": "0x108000004cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000",
"lock_type": 2
}
]
}
{
"sub_account": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"error": "0x"
}
AssetCreate
Deploys a new digital asset natively on the network. The operation creates a dedicated asset account, deploys the asset's execution logic, registers the asset to the creator's account, and credits the initial supply to their balance.
AssetCreateOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 4. |
payload | Payload | Configuration, logic, and metadata for the asset. |
result | Result | The data returned upon execution in the receipt. |
AssetCreatePayload
| Parameter | Type | Description |
|---|---|---|
symbol | String | Identifying symbol for the asset, e.g. KOI. |
max_supply | BigInt | Maximum supply the asset can ever reach. |
standard | AssetStandard | The standard to follow: MAS0, MAS1, or MASX. MASX requires logic. |
dimension | Uint8 | Type classification of the asset, e.g. fungible or non-fungible behavior. |
decimals | Uint8 | Decimal precision, e.g. 18 for a typical fungible token. |
enable_events | Boolean | If true, the asset emits events on state changes such as transfers and mints. |
manager | String | The participant authorized to manage supply and dynamic metadata. |
logic | LogicDeployPayload | Required for MASX. The compiled manifest and initialization data. |
metadata | Map | Initial static and dynamic metadata. |
AssetCreateResult
| Field | Type | Description |
|---|---|---|
asset_id | HEX (32 Bytes) | Identifier of the newly created asset. |
error | HEX | Serialised error data if execution failed |
- Payload
- Ix Object
- Receipt
{
"payload": {
"symbol": "KOI",
"max_supply": 20000,
"standard": 0,
"dimension": 0,
"enable_events": true,
"manager": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000"
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 4,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 4,
"payload": {
"symbol": "KOI",
"max_supply": 20000,
"standard": 0,
"dimension": 0,
"enable_events": true,
"manager": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000"
}
}
],
"participants": []
}
{
"asset_id": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"error": "0x"
}
AssetAction
Executes a routine defined in an asset's compiled logic - standard transfers, mints, and burns, as well as any custom state transition the asset defines.
AssetActionOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 5. |
payload | Payload | The routine to invoke and its arguments. |
result | Result | The data returned upon execution in the receipt. |
AssetActionPayload
| Parameter | Type | Description |
|---|---|---|
asset_id | HEX (32 bytes) | Identifier of the target asset. |
callsite | String | Method to invoke - Transfer, Mint, or a custom routine such as IssueBadge. |
calldata | HEX | POLO-encoded arguments for the method. |
funds | Map | Optional. Asset IDs mapped to amounts, when the call needs to carry value with it. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"asset_id": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000ed434a2ab138e69295e134686d57d80a9aa3325dbbde9bbf00000000"
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 7,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 5,
"payload": {
"asset_id": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"callsite": "Transfer",
"calldata": "0x0d6f06659601c502616d6f756e7403138862656e65666963696172790600000000ed434a2ab138e69295e134686d57d80a9aa3325dbbde9bbf00000000"
}
}
],
"participants": [
{
"id": "0x00000000ed434a2ab138e69295e134686d57d80a9aa3325dbbde9bbf00000000",
"lock_type": 0
},
{
"id": "0x10030000d9f12d13c0347a737430cdbcca174164f0823ccc39bedbe800000000",
"lock_type": 2
}
]
}
{
"outputs": "0x0d0f",
"error": "0x"
}
GuardianRegister
Guardians are the nodes that participate in consensus and secure the network. This operation onboards one: it adds the node to the validator registry and locks an initial stake.
Registration binds three things together - the node's krama_id (its network identity), the participant account that owns it, and the consensus key it signs consensus messages with. The staked KMOI is locked from the sender's balance rather than spent: it still belongs to the owner, but cannot move while the guardian is registered.
GuardianRegisterOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 6. |
payload | Payload | Node identity, credentials, and initial stake. |
GuardianRegisterPayload
| Parameter | Type | Description |
|---|---|---|
krama_id | String | Network identity of the guardian node being registered. |
wallet_id | HEX (32 bytes) | The account that owns the guardian and receives its stake and rewards. |
consensus_key | Bytes | Public key the node uses to sign consensus messages. |
kyc_proof | Bytes | Compliance proof submitted for the guardian. |
amount | HEX | Initial KMOI to lock as stake. Must be greater than zero. |
GuardianStake
Increases the stake backing an already-registered guardian. The KMOI is locked from the sender's balance and recorded as a pending addition.
Stake does not take effect immediately. Pending additions are promoted to active stake at an epoch boundary. A guardian's active stake is what determines whether it is eligible for consensus and how much influence it carries.
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 7. |
payload | Payload | Target guardian and amount to stake. |
GuardianUnstake
Begins stake removal. The requested amount is marked pending, and at the next epoch boundary it shifts from active to inactive stake - but stays locked. This waiting period is what keeps a guardian accountable for the period it was active, rather than letting a misbehaving node exit with its funds instantly.
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 8. |
payload | Payload | Target guardian and amount to unstake. |
GuardianWithdraw
Completes the exit. Once the waiting period has elapsed and the tokens are marked inactive, this operation releases the lock and credits the KMOI back to the sender's spendable balance.
Only inactive tokens can be withdrawn. If the stake is still active or still pending, the withdrawal fails.
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 9. |
payload | Payload | Target guardian and amount to withdraw. |
GuardianClaim
Transfers accrued consensus rewards into the owner's spendable KMOI balance. Rewards are held separately from stake, so claiming them has no effect on the guardian's staked position or its participation in consensus.
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 10. |
payload | Payload | Target guardian and reward amount to claim. |
GuardianActionPayload
Shared by GuardianStake, GuardianUnstake, GuardianWithdraw, and GuardianClaim.
| Parameter | Type | Description |
|---|---|---|
krama_id | KramaID | Identity of the guardian the action applies to. |
amount | HEX | KMOI amount for the action. Must be greater than zero. |
- Ix Object
Guardian examples will be provided in a future update.
LogicDeploy
Deploys a new logic instance. The operation verifies and compiles the logic manifest, registers a new logic account, and optionally runs an initialization routine to establish starting state.
If a callsite is provided, the runtime invokes it immediately after deployment - the equivalent of a constructor.
LogicDeployOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 11. |
payload | Payload | Manifest and initialization data. |
result | Result | The data returned upon execution in the receipt. |
LogicDeployPayload
| Parameter | Type | Description |
|---|---|---|
manifest | Bytes | Serialized representation of the logic manifest. |
callsite | String | Initialization routine to invoke after successful deployment. |
calldata | HEX | POLO-encoded arguments for that routine. |
LogicDeployResult
| Field | Type | Description |
|---|---|---|
logic_id | HEX (32 bytes) | Identifier of the deployed logic. Omitted if deployment failed. |
error | HEX | Serialized error data if deployment or callsite execution failed. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"manifest": "0x0e6f031ef601ce02...",
"callsite": "Seed",
"calldata": "0x0d2f0675696e697469616c02"
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 8,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 11,
"payload": {
"manifest": "0x0e6f031ef601ce02...",
"callsite": "Seed",
"calldata": "0x0def0106458601e501f605d506a60785..."
}
}
],
"participants": []
}
{
"logic_id": "0x20000000ff572431a4f52ad972f54fee061847c682eb22ffa51c97a900000000",
"error": "0x"
}
LogicInvoke
Executes a routine within a deployed logic instance. This is the primary way participants interact with logic modules.
LogicInvokeOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 12. |
payload | Payload | Target logic and execution context. |
result | Result | The data returned upon execution in the receipt. |
LogicInvokePayload
| Parameter | Type | Description |
|---|---|---|
logic_id | HEX (32 bytes) | Identifier of the logic instance being invoked. |
callsite | String | Function to execute within the logic module. |
calldata | HEX | POLO-encoded arguments for the callsite. |
LogicInvokeResult
| Field | Type | Description |
|---|---|---|
outputs | HEX | POLO-encoded return values. Empty if the callsite returns nothing. |
error | HEX | Serialized error data if execution failed. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"logic_id": "0x0800008d898ef91ebd8a208855d6d1c98939202cfcb6a067530845993fe6975de1eb84",
"callsite": "BalanceOf",
"calldata": "0x0d2f0645616464720652fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 9,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 12,
"payload": {
"logic_id": "0x0800008d898ef91ebd8a208855d6d1c98939202cfcb6a067530845993fe6975de1eb84",
"callsite": "BalanceOf",
"calldata": "0x0d2f0645616464720652fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"
}
}
],
"participants": [
{
"id": "0x0800008d898ef91ebd8a208855d6d1c98939202cfcb6a067530845993fe6975de1eb84",
"lock_type": 1
}
]
}
{
"outputs": "0x0d2f0667616d6f756e740301312d00",
"error": "0x"
}
LogicEnlist
Subscribes a participant to a deployed logic module. Under the hood it allocates a dedicated logic storage tree inside the invoker's account state and runs a setup routine defined by the target logic. An account must enlist before it can store data tied to that logic.
This operation is currently Reserved by the protocol. The details below represent its conceptual design and are subject to change before activation.
LogicEnlistOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 13. |
payload | Payload | Target logic and enlistment parameters. |
LogicEnlistPayload
| Parameter | Type | Description |
|---|---|---|
logic_id | HEX (32 bytes) | Identifier of the deployed logic to enlist with. |
callsite | String | Registration routine defined in the logic. |
calldata | HEX | POLO-encoded arguments passed to the enlistment routine. |
StorageDeposit
When a participant invokes a logic, any state that logic writes on their behalf occupies storage on the network, and storage has to be paid for. This operation converts KMOI into a storage allowance - a prepaid quota of bytes, reserved on a target account and attributed to a specific participant.
The amount is converted at the network's storage_price_per_byte rate. Allowances are granted in whole bytes, so any remainder too small to buy a full byte is refunded to the sender immediately.
StorageDepositOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 14. |
payload | Payload | Target account, beneficiary, and amount. |
StorageDepositPayload
| Parameter | Type | Description |
|---|---|---|
target_account | HEX (32 bytes) | The logic or asset account the allowance is reserved on. |
deposit_for | HEX (32 bytes) | The participant the allowance is credited to. |
amount | Number | KMOI to convert into storage. |
deposit_for does not have to be the sender. This lets an application sponsor storage for its users, so they can interact with a logic without holding an allowance of their own.
- Payload
- Ix Object
- Receipt
{
"payload": {
"target_account": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"deposit_for": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"amount": 1000
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 1,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 14,
"payload": {
"target_account": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"deposit_for": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"amount": 1000
}
}
],
"participants": [
{
"id": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"lock_type": 0
},
{
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"lock_type": 0
}
]
}
StorageDeposit returns no data in the IxOpResult. Success is indicated by the operation's status in the receipt.
StorageWithdraw
The inverse of StorageDeposit. It releases bytes from a participant's allowance on a target account and returns the corresponding KMOI to the sender.
Only unused bytes can be reclaimed. An allowance is made up of bytes currently holding state and bytes still free; the operation fails if the requested amount exceeds what is free. Deleting state on the target account frees bytes back into the allowance, making them withdrawable.
Setting bytes_to_release to 0 withdraws the entire unused allowance. Once an allowance is fully drained, its record on the target account is cleared.
StorageWithdrawOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 15. |
payload | Payload | Target account and bytes to reclaim. |
StorageWithdrawPayload
| Parameter | Type | Description |
|---|---|---|
target_account | HEX (32 bytes) | The logic or asset account holding the allowance. |
bytes_to_release | Number | Unused bytes to reclaim. Set to 0 to withdraw the full unused allowance. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"target_account": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"bytes_to_release": 2000
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 1,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 15,
"payload": {
"target_account": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"bytes_to_release": 2000
}
}
],
"participants": [
{
"id": "0x000000004678e9f5bf2f66362ef5367fbc72efe7b419a5e7d851f57b00000000",
"lock_type": 0
},
{
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"lock_type": 0
}
]
}
StorageWithdraw returns no data in the IxOpResult. Success is indicated by the operation's status in the receipt.
AccessCreate
Writes a new Access Policy onto the sender's account, granting other accounts and logics a constrained path into state the sender owns.
The operation fails if a policy already exists for the same policy key — the pair (resource_type, resource_id). Use AccessUpdate to replace one.
AccessCreateOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 18. |
payload | Payload | The account to write the policy to, and the policy. |
AccessPayload
| Parameter | Type | Description |
|---|---|---|
target_account | HEX (32 bytes) | The account the policy is written to. Must be the sender. |
access_policy | AccessPolicy | The policy record. |
AccessPolicy
| Parameter | Type | Description |
|---|---|---|
resource_type | String | The class of resource governed. Only "storage" is currently accepted. |
resource_id | HEX (32 bytes) | The specific resource. For storage, the Logic ID of the logic that performs the write. |
action_type | Array of String | Permitted actions. Currently ["storage_mutate"]. Must be non-empty. |
caller_constraint | CallerConstraint | Constrains the immediate caller of the storing logic. |
origin_constraint | CallerConstraint | Constrains the interaction sender. |
The record also carries a scope field, which would narrow a policy to part of a resource. It is reserved: leave it empty, which means the whole resource. It is not yet enforced and is not returned by the read RPCs.
CallerConstraint
The same type is used for both caller_constraint and origin_constraint, despite the name.
| Parameter | Type | Description |
|---|---|---|
kind | Number | 0 — any identifier matches. 1 — only identifiers in set match. |
set | Array of HEX | The admitted identifiers. Required and non-empty when kind is 1; null when kind is 0. |
- Payload
- Ix Object
- Receipt
{
"payload": {
"target_account": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"access_policy": {
"resource_type": "storage",
"resource_id": "0x20000000ff572431a4f52ad972f54fee061847c682eb22ffa51c97a900000000",
"action_type": ["storage_mutate"],
"caller_constraint": {
"kind": 1,
"set": [
"0x20000000784cf54a25a23390e71563a37d7d2c0d203746876e04992b00000000"
]
},
"origin_constraint": { "kind": 0, "set": null }
}
}
}
{
"sender": {
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"sequence": 12,
"key_id": 0
},
"fuel_price": 1,
"fuel_limit": 10000,
"ix_operations": [
{
"type": 18,
"payload": {
"target_account": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"access_policy": {
"resource_type": "storage",
"resource_id": "0x20000000ff572431a4f52ad972f54fee061847c682eb22ffa51c97a900000000",
"action_type": ["storage_mutate"],
"caller_constraint": {
"kind": 1,
"set": [
"0x20000000784cf54a25a23390e71563a37d7d2c0d203746876e04992b00000000"
]
},
"origin_constraint": { "kind": 0, "set": null }
}
}
}
],
"participants": [
{
"id": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"lock_type": 0
}
]
}
AccessCreate returns no data in the IxOpResult. Success is indicated by the operation's status in the receipt.
AccessUpdate
Replaces an existing Access Policy. The policy key (resource_type, resource_id) identifies which policy is replaced; every other field is overwritten wholesale, not merged.
The operation fails if no policy exists for that key.
AccessUpdateOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 19. |
payload | Payload | The account holding the policy, and the new policy. |
The payload and the surrounding Ix object are identical in shape to AccessCreate; only type differs. Widening a constraint set charges additional storage bytes, and narrowing one credits bytes back.
- Payload
{
"payload": {
"target_account": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"access_policy": {
"resource_type": "storage",
"resource_id": "0x20000000ff572431a4f52ad972f54fee061847c682eb22ffa51c97a900000000",
"action_type": ["storage_mutate"],
"caller_constraint": {
"kind": 1,
"set": [
"0x20000000784cf54a25a23390e71563a37d7d2c0d203746876e04992b00000000"
]
},
"origin_constraint": {
"kind": 1,
"set": [
"0x00000000ed434a2ab138e69295e134686d57d80a9aa3325dbbde9bbf00000000"
]
}
}
}
}
This narrows the policy above from any origin to a single origin.
AccessDelete
Removes an Access Policy. The resource returns to being reachable only by its owner. Deletion is complete rather than a tombstone, so the same key may be created again afterwards with AccessCreate.
The operation fails if no policy exists for the given key.
AccessDeleteOperation
| Parameter | Type | Description |
|---|---|---|
type | Number | Set to 20. |
payload | Payload | Identifies the policy to remove. |
AccessDeletePayload
| Parameter | Type | Description |
|---|---|---|
target_account | HEX (32 bytes) | The account holding the policy. Must be the sender. |
resource_type | String | The resource class of the policy being removed. |
resource_id | HEX (32 bytes) | The resource ID of the policy being removed. |
- Payload
{
"payload": {
"target_account": "0x000000001ec28dabfc3e4ac4dfc2084b45785b5e9cf1287b63a4f46900000000",
"resource_type": "storage",
"resource_id": "0x20000000ff572431a4f52ad972f54fee061847c682eb22ffa51c97a900000000"
}
}
Interaction Receipt
Once an Interaction is included in a Tesseract and executed, the network records a receipt - the authoritative account of what happened: whether it succeeded, how much fuel it burned, and what each operation produced. Fetch it by interaction hash with the moi.InteractionReceipt API.
Only execution produces a receipt. An Interaction rejected during validation never enters a Tesseract, so it has none.
Receipt Structure
| Field | Type | Description |
|---|---|---|
ix_hash | HEX (32 bytes) | Hash of the Interaction this receipt belongs to. |
status | ReceiptStatus | Overall outcome. |
fuel_used | HEX | Fuel actually charged. |
ix_operations | Array<IxOpResult> | One result per operation, in submission order. |
from | HEX (32 bytes) | The sender. |
ix_index | HEX | Position of the Interaction within its Tesseract. |
ts_hash | HEX (32 bytes) | Hash of the Tesseract that included it. |
participants | Array<ParticipantState> | State of each participant account after execution. |
Receipt Status Codes
| Value | Name | Description |
|---|---|---|
| 0 | ReceiptOk | All operations succeeded and their state changes were applied. |
| 1 | ReceiptStateReverted | Execution failed and no state changes were applied. |
| 2 | ReceiptInsufficientFuel | The Interaction ran out of fuel mid-execution. |
An Interaction is all-or-nothing. Execution stops at the first operation that fails, and every state change it made - including changes from operations that had already succeeded - is discarded. Fuel is still charged, because the work was performed.
Operation Result
Each operation produces a result at the index it was submitted at.
| Field | Type | Description |
|---|---|---|
ix_type | HEX | The operation type that produced this result. |
status | IxOpStatus | Outcome of this specific operation. |
data | JSON | Operation-specific output, e.g. AccountCreationResult. |
If execution halted early, only the operations that actually ran appear in ix_operations. An array shorter than the Interaction's operation list is itself a signal of where the failure occurred.
Operation Status Codes
| Value | Name | Description |
|---|---|---|
| 0 | ResultOk | The operation completed successfully. |
| 1 | ResultExceptionRaised | The operation failed a check - insufficient balance, invalid target, failed validation. |
| 2 | ResultDefectRaised | The operation failed irrecoverably during logic execution. |
The two failure modes differ in cost. An exception charges only the fuel consumed up to the point of failure. A defect charges the Interaction's entire fuel_limit.
Participant State
One entry per account touched by the Interaction, as of after execution.
| Field | Type | Description |
|---|---|---|
id | HEX (32 bytes) | Identifier of the participant account. |
height | HEX | The account's state height after the Interaction. |
state_hash | HEX (32 bytes) | Hash of the account's state. |
transitive_link | HEX (32 bytes) | Hash of the latest tesseract that mutated the account state. |
locked_context | HEX (32 bytes) | The context the account was locked under during execution. |
context_delta | JSON | Changes made to the account's context. |
Validation Constraints
These are checked before an Interaction is accepted. Failing any of them means rejection at submission: no receipt is produced and no fuel is charged.
Interaction-level
| Constraint | Requirement |
|---|---|
AssetCreate per Interaction | At most 1 |
LogicDeploy per Interaction | At most 1 |
| Interaction size | At most 128 KB |
| Fuel | Both fuel_price and a non-zero fuel_limit must be specified |
| Fuel price | At least the network's minimum (1 on Public Devnet) |
| Sender | Must not be nil |
| Sequence | At least the sender's next expected sequence |
| Sender balance | Must cover the Interaction's total cost |
| Participants | All non-genesis participants must be registered on the network |
| Signatures | Must be valid and include one from the sender's own key |
| Combined signature weight | At least 1000 |
Key weight (ParticipantCreate) | Each registered key must weigh at most 1000 |
Access operations
| Constraint | Requirement |
|---|---|
target_account | Must equal the sender |
resource_type | Must be "storage"; asset, logic, and key are defined but rejected |
resource_id | Must not be null |
action_type | Must be non-empty |
| Constraint sets | A constraint with kind: 1 must carry a non-empty set |
Packages & SDKs
- JS MOI SDK - JavaScript/TypeScript library for the MOI Protocol: submit Interactions, deploy and invoke logics, manage participants and assets.
- JS POLO - JavaScript/TypeScript implementation of the POLO serialization scheme.
- GO POLO - Go implementation of POLO.
- Cocolang - Language and toolchain for writing, compiling, and testing Coco modules (logics).