MANTLE-TRANSACTION-ENCODING
| Field | Value |
|---|---|
| Name | Mantle Transaction Encoding |
| Slug | 202 |
| Status | raw |
| Category | Standards Track |
| Editor | David Rusu [email protected] |
| Contributors | Filip Dimitrijevic [email protected] |
Timeline
- 2026-10-14 —
949faa9— [RFC] Mantle: Bound operation data payloads (#467) - 2026-10-01 —
4deef61— docs(blockchain): swap index and sig in multi sig (#469) - 2026-09-11 —
b7301a6— [RFC] Mantle: Enable basic EmPoWering machinery (#400) - 2026-09-09 —
4b9d1ca— docs(blockchain): make every Operation carry a proof and gas independent of it (#431) - 2026-08-31 —
3cac48f— docs(blockchain): rename locked notes to service notes (#423) - 2026-08-27 —
48fbc82— docs(blockchain): order channel configurations and prevent their replay (#396) - 2026-08-24 —
8b6f4f4— docs(blockchain): use one canonical encoding for ServiceType and Locator (#402) - 2026-07-20 —
5b20a2d— docs(blockchain) Channel Participation in PoS (#364) - 2026-07-03 —
709cf7f— Bedrock-RFC: Remove Concept of a Session (#365) - 2026-05-29 —
67e498e— chore: fix math issues (#350) - 2026-05-28 —
d45eed2— Chore: mirror blochain specs into github/mdbook (#347)
Revisions History
| Version | Changes | Date |
|---|---|---|
| 1.0.0 | Initial revision. | 2026-12-01 |
| 1.1.0 | [RFC] Make Ledger Transaction an Operation | 2026-03-25 |
| 1.2.0 | [RFC] Add Deposit/Withdraw to Tx Encoding | 2026-04-02 |
| 1.3.0 | [RFC] Enforce NoteId uniqueness | 2026-04-24 |
| 1.4.0 | [RFC] Simplify Mantle Transaction and Refactor Ledger Operations | 2026-05-06 |
| 1.4.1 | Removed mention of DA. Updated KeyCount from Byte to UINT16 to follow Mantle. | 2026-05-21 |
| 1.5.0 | Introduce the new Operation CHANNEL_STAKE_ASSIGNATION and update of the channel operations to reflect changes in Mantle | 2026-06-24 |
| 1.5.1 | [RFC] One canonical encoding for ServiceType and Locator: pin Locator bytes to the multiaddr binary form | 2026-08-14 |
| 1.6.0 | Added the Parent of the ChannelConfig to follow Mantle | 2026-08-27 |
| 1.6.1 | Renamed the LockedNoteId production of the SDP Operations into ServiceNoteId | 2026-08-27 |
| 1.7.0 | Added the ChannelConfigOpProof and ChannelTransferOpProof variants and factored the three channel threshold proofs into ChannelMultiSigProof, carrying the index of the signing key alongside each signature | 2026-08-31 |
| 1.8.0 | Added the ClaimPowReward Operation payload; its proof is a ZkSigProof | 2026-09-08 |
| 1.9.0 | Swap Ed25519Signature and SignerIndex order in IndexedSignature | 2026-10-01 |
| 1.10.0 | Introduced a shared maximum operation data payload size for channel inscriptions and channel deposit metadata, retaining their UINT32 length prefixes; corrected SDP activity metadata to use its tagged variant encoding without a generic length prefix | 2026-10-06 |
Introduction
This document specifies the canonical encoding of Mantle transactions (see Mantle - Mantle Transaction) and its sub-components. Transactions sent through the mempool and included in blocks use this encoding.
Overview
The transaction encoding is specified in ABNF form to remove any ambiguity and guarantee a canonical encoding. The high level encoding choices which were not immediately derivable from the Mantle specification are listed here:
- All multi-byte integers use little-endian encoding
- Any lists are length-prefixed with fixed width uints
- We derive number of proofs and type of proof from the Ops list parsed earlier
Specification
Signed Mantle Tx
SignedMantleTx = MantleTx OpsProofs
Mantle Tx
MantleTx = OpCount *Op
OpCount = Byte
Operations
Op = Opcode OpPayload
Opcode = Byte
OpPayload = Transfer /
ChannelInscribe /
ChannelConfig /
ChannelDeposit /
ChannelWithdraw /
ChannelTransfer /
SDPDeclare /
SDPWithdraw /
SDPActive /
LeaderClaim /
ClaimPowReward
Channel Operations
ChannelInscribe = ChannelId Inscription Parent Signer
Inscription = UINT32 *BYTE ; Max MAX_OPERATION_DATA_SIZE bytes
ChannelConfig = ChannelId Parent KeyCount *Signer PostingTimeframe PostingTimeout ConfigThreshold TransferThreshold
KeyCount = UINT16
PostingTimeframe = UINT32
PostingTimeout = UINT32
ConfigThreshold = UINT16
TransferThreshold = UINT16
ChannelDeposit = ChannelId Inputs DepositMetadata
Inputs = InputCount *NoteId
InputCount = Byte
DepositMetadata = UINT32 *BYTE ; Max MAX_OPERATION_DATA_SIZE bytes
ChannelTransfer = ChannelId Inputs Outputs
ChannelWithdraw = ChannelId Inputs
ChannelId = Hash32
Parent = Hash32
Signer = Ed25519PublicKey
Outputs = OutputCount *Note
OutputCount = Byte
Inputs = InputCount *NoteId
MAX_OPERATION_DATA_SIZE MUST be exactly 1,835,008 bytes. The payloads of ChannelInscribe.Inscription and ChannelDeposit.Metadata MUST each be at most MAX_OPERATION_DATA_SIZE bytes, excluding the outer UINT32 length prefix. A decoder MUST reject either field when its declared or decoded payload length exceeds the limit.
SDP Operations
SDPDeclare = ServiceType Locators ProviderId ZkId ServiceNoteId
ServiceType = Byte ; 0 = BN
Locators = LocatorCount *Locator
LocatorCount = Byte ; Max 8
Locator = 2Byte *BYTE ; Max 329 bytes, multiaddr binary form
ProviderId = Ed25519PublicKey
ZkId = ZkPublicKey
ServiceNoteId = NoteId
SDPWithdraw = DeclarationId Nonce ServiceNoteId
DeclarationId = Hash32
Nonce = UINT64
SDPActive = DeclarationId Nonce ActivityMetadata
ActivityMetadata = ActivityMetadataType ActivityMetadataPayload
ActivityMetadataType = Byte ; 0x01 = Blend
ActivityMetadataPayload = BlendActivityProof ; Selected by ActivityMetadataType
ActivityMetadataType is a one-byte discriminant. The only currently valid value is 0x01, which selects BlendActivityProof; a decoder MUST reject any other value. BlendActivityProof is the 228-byte concatenation of epoch_number, signing_key, proof_of_quota, and proof_of_selection defined in Blend Protocol — Active Message, excluding that section's metadata_type byte, which is encoded here as ActivityMetadataType.
The current Blend variant occupies 229 bytes including its discriminant. Its encoding and validation follow the selected variant's rules; MAX_OPERATION_DATA_SIZE does not apply to SDPActive.Metadata.
Leader operations
LeaderClaim = RewardsRoot VoucherNullifier PublicKey
RewardsRoot = FieldElement ; Merkle root for voucher membership proof
VoucherNullifier = FieldElement
PublicKey = ZkPublicKey
Proof of work operations
ClaimPowReward = EpochNonce BlockHash PublicKey
EpochNonce = FieldElement ; the epoch nonce the solution was found against
BlockHash = Hash32 ; recent canonical block the solution is anchored to
Transfer Operations
Transfer = Inputs Outputs
Inputs = InputCount *NoteId
InputCount = Byte
Outputs = OutputCount *Note
OutputCount = Byte
Ledger
Note = Value ZkPublicKey
Value = UINT64
NoteId = FieldElement
Op Proofs
OpsProofs = *OpProof ; 1. Lenth must equal OpCount
; 2. OpProof variant is derived from the corresponding Op.
; That is, type(OpProofs[i]) == ProofFor(Op[i])
OpProof = Ed25519SigProof /
ZkSigProof /
ZkAndEd25519SigsProof /
ChannelConfigOpProof /
ChannelWithdrawOpProof /
ChannelTransferOpProof /
ProofOfClaimProof
Ed25519SigProof = Ed25519Signature
ZkSigProof = ZkSignature
ZkAndEd25519SigsProof = ZkSignature Ed25519Signature
ChannelConfigOpProof = ChannelMultiSigProof
ChannelWithdrawOpProof = ChannelMultiSigProof
ChannelTransferOpProof = ChannelMultiSigProof
ProofOfClaimProof = Groth16
ChannelMultiSigProof = SignatureCount *IndexedSignature
IndexedSignature = SignerIndex Ed25519Signature
SignatureCount = UINT16
SignerIndex = UINT16
Common Structures
; Zero-knowledge signature
ZkSignature = Groth16
; Cryptographic primitives
Groth16 = 128BYTE ; pi_a (32) + pi_b (64) + pi_c (32)
ZkPublicKey = FieldElement
Ed25519PublicKey = 32BYTE
Ed25519Signature = 64BYTE
FieldElement = 32BYTE ; BN254 field element (little-endian)
Hash32 = 32BYTE
; Primitive types
UINT64 = 8BYTE ; 64-bit unsigned integer, little-endian
UINT32 = 4BYTE ; 32-bit unsigned integer, little-endian
UINT16 = 2BYTE ; 16-bit unsigned integer, little-endian
Byte = OCTET