MANTLE-TRANSACTION-ENCODING

FieldValue
NameMantle Transaction Encoding
Slug202
Statusraw
CategoryStandards Track
EditorDavid Rusu [email protected]
ContributorsFilip 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

VersionChangesDate
1.0.0Initial revision.2026-12-01
1.1.0[RFC] Make Ledger Transaction an Operation2026-03-25
1.2.0[RFC] Add Deposit/Withdraw to Tx Encoding2026-04-02
1.3.0[RFC] Enforce NoteId uniqueness2026-04-24
1.4.0[RFC] Simplify Mantle Transaction and Refactor Ledger Operations2026-05-06
1.4.1Removed mention of DA. Updated KeyCount from Byte to UINT16 to follow Mantle.2026-05-21
1.5.0Introduce the new Operation CHANNEL_STAKE_ASSIGNATION and update of the channel operations to reflect changes in Mantle2026-06-24
1.5.1[RFC] One canonical encoding for ServiceType and Locator: pin Locator bytes to the multiaddr binary form2026-08-14
1.6.0Added the Parent of the ChannelConfig to follow Mantle2026-08-27
1.6.1Renamed the LockedNoteId production of the SDP Operations into ServiceNoteId2026-08-27
1.7.0Added the ChannelConfigOpProof and ChannelTransferOpProof variants and factored the three channel threshold proofs into ChannelMultiSigProof, carrying the index of the signing key alongside each signature2026-08-31
1.8.0Added the ClaimPowReward Operation payload; its proof is a ZkSigProof2026-09-08
1.9.0Swap Ed25519Signature and SignerIndex order in IndexedSignature2026-10-01
1.10.0Introduced 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 prefix2026-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:

  1. All multi-byte integers use little-endian encoding
  2. Any lists are length-prefixed with fixed width uints
  3. 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