TON DocsTON Docs
OnboardingNodesApplicationsAPIsContractsSmart contractsTolkTolk languageTVMTON Virtual MachineFoundationsBlockchain foundations

WalletTg messages, opcodes, and error codes

WalletTg prepends a 512-bit Ed25519 signature to each serialized request.

Signed request

FieldWidthDescription
signature512 bitsEd25519 signature over the 256-bit hash of the request cell that follows
requestVariableOpcode, header, and operation fields

The contract verifies the signature against the current publicKey. The signature covers the hash of the remaining request slice, including the opcode and referenced cells.

After its 32-bit opcode, each signed inbound request contains SeqnoHeader:

FieldWidthValidation
subwalletId32 bitsMust equal the stored subwalletId
validUntil32 bitsUnix time in seconds; must be greater than blockchain.now()
seqno32 bitsMust equal the stored seqno

Internal and external requests use different opcodes, so a signature cannot be reused across channels. The request signature does not cover the internal message envelope's sender or attached value. The contract authenticates the request body and header. Any party can relay a valid request.

Relayers must attach enough Gram for compute gas. The wallet's existing balance can fund outgoing values and fees. Emulate the request with the current balance and intended attached value; insufficient gas can fail it without advancing seqno.

Wallet v5 uses a different signature position and action encoding; see the wallet v5 interface.

Request opcodes

Opcode values are 32-bit constructor tags serialized before request fields.

OperationChannelOpcode
Send one messageInternal0x63896E74
Send one messageExternal0x63896E75
Send a batchInternal0x73896E74
Send a batchExternal0x73896E75
Change signing keyInternal0xFBBA99C7
Change signing keyExternal0xFBBA99C8
Key-changed eventExternal-out0xEBA19948

Send one message

SendOneMessageRequestI and SendOneMessageRequestE contain a header and one MessageToSend:

FieldWidthDescription
sendMode8 bitsPassed to the outgoing SENDRAWMSG action
messageCellReferenceSerialized outgoing message cell

The contract builds the outgoing action list (c5) from these fields. Clients do not supply a raw action list.

Send a batch

SendBulkMessagesRequestI and SendBulkMessagesRequestE contain the header and a standard Tolk array<MessageToSend>. The array stores len:uint8 and a maybe-reference to its first chunk. Each chunk starts with a maybe-reference to the next chunk.

Entries store sendMode:uint8 inline and messageCell by reference. A chunk with a next reference holds at most 3 entries. The final chunk holds at most 4.

Two-message and ten-message Tolk arrays: each array has a length and head reference, and linked chunks hold send modes and message-cell references.

Valid arrays contain 1–255 entries. len must equal the parsed count.

Send modes

The contract passes each 8-bit mode to SENDRAWMSG. Every message in an external send request must include SEND_MODE_IGNORE_ERRORS (+2). This skips eligible send-action errors, not request parsing errors or all action-phase errors.

Change the signing key

The outer request uses the current key. rotationSignature proves possession of newPublicKey.

ChangePublicKeyRequestI and ChangePublicKeyRequestE

FieldWidth or encodingValidation
headerSeqnoHeaderMust match current wallet state
newPublicKey256 bitsMust differ from the current stored key
rotationSignatureReference to a 512-bit signatureVerifies the rotation payload below with newPublicKey
encryptedOldPrivateKeyReference to exactly 256 bitsEmitted unchanged in the key-changed event

Rotation proof

The new key signs the hash of this payload, serialized in field order:

FieldWidthValue
tag96 bits0x4B45595F524F544154494F4E (KEY_ROTATION)
walletWorkchain8-bit signed integerWorkchain of the wallet address
walletAddrHash256 bitsAccount ID hash of the wallet address

The address fields bind the proof to this wallet.

OnKeyHasBeenChangedEvent

The contract emits this external-out event to addr_none. Its body is opcode 0xEBA19948 followed by the 256-bit encrypted key. The contract treats the value as opaque.

Failure and sequence-number behavior

The trampoline returns for internal message bodies shorter than 512 bits before loading configuration. Other internal and external messages reach WalletTg. External requests call ACCEPT after validation. Processing is charged to the WalletTg balance.

Successful send and key-change requests increment seqno once, regardless of the outgoing message count.

External send handlers save the incremented seqno and call COMMIT before parsing outgoing messages. COMMIT preserves the saved data and actions across a later compute failure. Internal send and key-rotation handlers omit COMMIT. The table distinguishes compute failures from action-phase failures:

ConditionInternal requestExternal request
Invalid signatureReturns; seqno is unchanged. A future migration may still persist.Throws before ACCEPT; seqno is unchanged.
Header mismatch or expiryFails; seqno is unchanged.Fails before ACCEPT; seqno is unchanged.
Unknown or channel-mismatched opcodeFails without updating seqno.Fails before ACCEPT; seqno is unchanged.
Malformed send arrayCompute failure rolls back seqno.Can fail after COMMIT; seqno remains incremented and no messages are emitted.
External send omits +2+2 is not required.Fails after COMMIT; seqno remains incremented and no messages are emitted.
Eligible action error with +2Action is skipped; seqno persists if the action phase succeeds.Action is skipped; seqno persists if the action phase succeeds.
Non-skippable send-action errorAction phase fails; seqno rolls back.Action phase fails; seqno rolls back despite compute-phase COMMIT.
Key-change event action failsKey rotation and seqno roll back; the request path has no COMMIT.Key rotation and seqno roll back; the request path has no COMMIT.

Error codes

ErrorCodeCondition
BeginParseOfNull7A required cell or reference is absent; an empty batch has no head reference and fails here
InvalidSeqno133Header sequence number differs from storage
InvalidSubwalletId134Header subwallet ID differs from storage
InvalidSignature135External signature is invalid; invalid internal signatures return without throwing
Expired136validUntil is not greater than current Unix time
ExternalSendMessageMustHaveIgnoreErrorsSendMode137An external send request contains a message without the +2 flag
InvalidOutArrayLen147Parsed count is zero or differs from len; an empty batch fails earlier with BeginParseOfNull
NewKeyMustDiffer148Rotation supplies the current public key
NewKeySignatureIsInvalid149New-key proof does not verify for this wallet address
InvalidOpcode65535 (0xFFFF)Request opcode is not allowed for the selected channel

Source files

On this page