Tempo Transaction (EIP-2718 type 0x76) — RLP deserialization, payment call matching, and fee payer co-signing (0x78 domain).
A Tempo Transaction is an RLP-encoded envelope prefixed with 0x76:
0x76 || rlp([chain_id, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
calls, access_list, nonce_key, nonce, valid_before, valid_after,
fee_token, fee_payer_signature, aa_authorization_list,
key_authorization?, sender_signature])Each call is rlp([to, value, input]).
This module extracts the fields needed for payment verification (chain_id,
calls) and preserves the full serialized hex as raw for broadcast
passthrough. When fee payer mode is enabled, it co-signs the transaction
with a server-side key using the 0x78 domain separator.
Dependencies
Uses ExRLP (available transitively via signet → onchain) for RLP
decoding. Signing uses Signet.Signer.Curvy and Signet.Recover directly
because Tempo 0x76 is a non-standard transaction type.
Summary
Types
A single call within the transaction's batch.
A parsed Tempo Transaction with verification-relevant fields.
Functions
Co-sign a client's transaction as fee payer and return the updated hex.
Deserialize a hex-encoded Tempo Transaction (0x76 prefix).
Check if the transaction's fee_token field is empty (RLP null).
Find a matching payment call (transfer or transferWithMemo) in the transaction.
Check if the transaction has a fee payer signature placeholder (0x00).
Validate that a transaction's calls match an allowed fee-payer pattern.
Types
@type call() :: %{to: binary(), value: non_neg_integer(), input: binary()}
A single call within the transaction's batch.
@type t() :: %Onchain.Tempo.Transaction{ calls: [call()], chain_id: non_neg_integer(), fields: [term()], raw: String.t() }
A parsed Tempo Transaction with verification-relevant fields.
raw is the full serialized transaction as a hex string ("0x76...") with
0x prefix, suitable for direct JSON-RPC broadcast.
Functions
Co-sign a client's transaction as fee payer and return the updated hex.
Takes the client-signed 0x76 transaction, adds the server's fee payer signature (domain 0x78), injects the fee token, and returns a new 0x76 hex string ready for broadcast.
Parameters
tx— deserialized transaction with fee payer placeholderfee_payer_key— 32-byte binary private key for fee sponsorshipfee_token— 20-byte binary TIP-20 token address for fee payment
Returns
{:ok, updated_tx}— transaction with newrawhex for broadcast{:error, reason}— on signing or recovery failure
Deserialize a hex-encoded Tempo Transaction (0x76 prefix).
Returns {:ok, %Transaction{}} with chain_id, parsed calls, and the
original hex string as raw (for broadcast). Returns {:error, reason}
on invalid input.
Examples
iex> Onchain.Tempo.Transaction.deserialize("0x76" <> valid_rlp_hex)
{:ok, %Onchain.Tempo.Transaction{chain_id: 42431, calls: [...], raw: "0x76..."}}
iex> Onchain.Tempo.Transaction.deserialize("0x02" <> rlp_hex)
{:error, "Not a Tempo transaction: expected 0x76 type prefix"}
Check if the transaction's fee_token field is empty (RLP null).
Clients leave fee_token empty when feePayer: true, allowing the
server to choose the fee payment token.
Find a matching payment call (transfer or transferWithMemo) in the transaction.
Searches tx.calls for one targeting currency with the correct selector,
then ABI-decodes and verifies recipient, amount, and optional memo.
Options
:amount— (required) expected amount as string:recipient— (required) expected recipient as hex address:memo— (optional) bytes32 hex memo; when set, MUST match transferWithMemo
Check if the transaction has a fee payer signature placeholder (0x00).
Per spec, clients set fee_payer_signature to 0x00 when requesting
server-side fee sponsorship.
Validate that a transaction's calls match an allowed fee-payer pattern.
Fee-payer sponsored transactions are restricted to specific call sequences to prevent clients from bundling rogue calls that the server would pay gas for.
Allowed patterns (matching mppx callScopes):
[transfer][transferWithMemo][approve, swapExactAmountOut, transfer][approve, swapExactAmountOut, transferWithMemo]
When approve is present, the spender must be the stablecoin DEX.
When swapExactAmountOut is present, the call target must be the stablecoin DEX.
Returns :ok or {:error, reason}.