Transaction Envelopes
Overview
A Transaction Envelope is a structure that defines the properties of a transaction, and is generally utilized to construct Transactions to be broadcast to a network.
Ox supports the core Ethereum Transaction Envelope types:
Examples
Constructing
A Transaction Envelope can be constructed using the respective .from
function.
The example below demonstrates how to construct an EIP-1559 Transaction Envelope using TransactionEnvelopeEip1559.from
. EIP-1559 Transactions are the most commonly used Transaction Envelope type.
import { TransactionEnvelopeEip1559, Value } from 'ox'
const envelope = TransactionEnvelopeEip1559.from({
chainId: 1,
maxFeePerGas: Value.fromGwei('10'),
maxPriorityFeePerGas: Value.fromGwei('1'),
nonce: 69n,
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: Value.fromEther('1.5'),
})
Signing
We can sign over a Transaction Envelope by passing the result of the Envelope's .getSignPayload
function to the respective Signer's .sign
function.
The example below demonstrates how to sign an EIP-1559 Transaction Envelope.
import { TransactionEnvelopeEip1559, Secp256k1, Value } from 'ox'
// Construct the Envelope.
const envelope = TransactionEnvelopeEip1559.from({
chainId: 1,
maxFeePerGas: Value.fromGwei('10'),
maxPriorityFeePerGas: Value.fromGwei('1'),
nonce: 69n,
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: Value.fromEther('1.5'),
})
// Sign over the Envelope.
const signature = Secp256k1.sign({
payload: TransactionEnvelopeEip1559.getSignPayload(envelope),
privateKey: '0x...',
})
// Attach the Signature to the Envelope.
const const signed: {
readonly chainId: 1;
readonly maxFeePerGas: bigint;
readonly maxPriorityFeePerGas: bigint;
readonly nonce: 69n;
readonly to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8";
readonly value: bigint;
readonly type: "eip1559";
readonly r: bigint;
readonly s: bigint;
readonly yParity: number;
}signed = TransactionEnvelopeEip1559.from(envelope, { signature })
↑ contains `r`, `s`, `yParity` signature properties.
Serializing
We can serialize a Transaction Envelope into RLP-encoded format by calling .serialize
. We can also deserialize an RLP-encoded Transaction Envelope by calling .deserialize
.
import { TransactionEnvelopeEip1559, Secp256k1, Value } from 'ox'
// Construct the Envelope.
const envelope = TransactionEnvelopeEip1559.from({
chainId: 1,
maxFeePerGas: Value.fromGwei('10'),
maxPriorityFeePerGas: Value.fromGwei('1'),
nonce: 69n,
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: Value.fromEther('1.5'),
})
// Serialize the Envelope.
const const serialized: `0x02${string}`serialized = TransactionEnvelopeEip1559.serialize(envelope)
// Deserialize the Envelope.
const const deserialized: Compute<TransactionEnvelopeEip1559>deserialized = TransactionEnvelopeEip1559.deserialize(serialized)
Sending
We can send a Transaction Envelope to the network by serializing the signed envelope with .serialize
, and then broadcasting it over JSON-RPC with eth_sendRawTransaction
.
In this example, we will use RpcTransport.fromHttp
to broadcast a eth_sendRawTransaction
request over HTTP JSON-RPC.
import { RpcTransport, TransactionEnvelopeEip1559, Secp256k1, Value } from 'ox'
// Construct the Envelope.
const envelope = TransactionEnvelopeEip1559.from({
chainId: 1,
maxFeePerGas: Value.fromGwei('10'),
maxPriorityFeePerGas: Value.fromGwei('1'),
nonce: 69n,
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: Value.fromEther('1.5'),
})
// Sign over the Envelope.
const signature = Secp256k1.sign({
payload: TransactionEnvelopeEip1559.getSignPayload(envelope),
privateKey: '0x...',
})
// Serialize the Envelope with the Signature.
const serialized = TransactionEnvelopeEip1559.serialize(envelope, {
signature
})
// Broadcast the Envelope with `eth_sendRawTransaction`.
const transport = RpcTransport.fromHttp('https://1.rpc.thirdweb.com')
const hash = await transport.request({
method: 'eth_sendRawTransaction',
params: [serialized],
})
Wallets & Signing Servers
The examples above demonstrate how to manually construct & sign a Transaction Envelope, and assumes that you will fill the Transaction with required properties (nonce, fees, signature, etc) to successfully execute the Transaction.
If you are interacting with a Wallet, or more generally an entity that is responsible for filling & signing Transactions, this means
you can skip the ceremony of manually filling the Transaction by using the eth_sendTransaction
RPC method.
The example below demonstrates how to send a Transaction Envelope with eth_sendTransaction
using an EIP-1193 Provider
to
interact with a Browser Extension Wallet. You could also use RpcTransport.fromHttp
if your backend supports signing Transactions with eth_sendTransaction
.
import 'ox/window'
import { Provider, TransactionEnvelopeEip1559, Value } from 'ox'
// Construct the Envelope.
const envelope = TransactionEnvelopeEip1559.from({
chainId: 1,
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: Value.fromEther('1.5'),
})
// Convert the Envelope to an RPC-compatible format.
const envelope_rpc = TransactionEnvelopeEip1559.toRpc(envelope)
// Broadcast the Envelope with `eth_sendTransaction`.
const provider = Provider.from(window.ethereum)
const hash = await provider.request({
method: 'eth_sendTransaction',
params: [envelope_rpc],
})
Related Modules
Module | Description |
---|---|
TransactionEnvelope | Errors & Types for working with Transaction Envelopes. |
TransactionEnvelopeEip1559 | Utility functions for working with EIP-1559 Typed Transaction Envelopes. |
TransactionEnvelopeEip2930 | Utility functions for working with EIP-2930 Typed Transaction Envelopes. |
TransactionEnvelopeEip4844 | Utility functions for working with EIP-4844 Typed Transaction Envelopes. |
TransactionEnvelopeEip7702 | Utility functions for working with EIP-7702 Typed Transaction Envelopes. |
TransactionEnvelopeLegacy | Utility functions for working with Legacy Transaction Envelopes. |