Skip to content

Commit 59ed284

Browse files
committed
add docs for new ev-reth changes
1 parent 3791c1d commit 59ed284

7 files changed

Lines changed: 357 additions & 0 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,30 @@ function sidebarHome() {
344344
// collapsed: true,
345345
// items: [{ text: "Overview", link: "/blog/overview" }],
346346
// },
347+
{
348+
text: "ev-reth",
349+
collapsed: true,
350+
items: [
351+
{ text: "Overview", link: "/ev-reth/overview" },
352+
{ text: "Configuration", link: "/ev-reth/configuration" },
353+
{ text: "Engine API", link: "/ev-reth/engine-api" },
354+
{ text: "JavaScript Client", link: "/ev-reth/js-client" },
355+
{
356+
text: "Features",
357+
collapsed: true,
358+
items: [
359+
{ text: "Base Fee Redirect", link: "/ev-reth/features/base-fee-redirect" },
360+
{ text: "Deploy Allowlist", link: "/ev-reth/features/deploy-allowlist" },
361+
{ text: "Contract Size Limits", link: "/ev-reth/features/contract-size-limits" },
362+
{ text: "Mint Precompile", link: "/ev-reth/features/mint-precompile" },
363+
{
364+
text: "Sponsored Batch Transactions",
365+
link: "/ev-reth/features/sponsored-transactions",
366+
},
367+
],
368+
},
369+
],
370+
},
347371
{
348372
text: "API Documentation",
349373
collapsed: true,

docs/concepts/fee-systems.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ By default, base fees are burned. ev-reth can redirect them to a treasury:
3434

3535
See [Base Fee Redirect](/ev-reth/features/base-fee-redirect) for details.
3636

37+
#### Sponsored Transactions
38+
39+
ev-reth also supports type `0x76` transactions where an application sponsor pays gas for the executor. The executor remains the transaction sender and still pays any value transfers inside the batch, while the sponsor pays execution gas and receives gas refunds.
40+
41+
See [Sponsored Batch Transactions](/ev-reth/features/sponsored-transactions) for the transaction model and [JavaScript Client](/ev-reth/js-client) for application integration.
42+
3743
### Cosmos SDK (ev-abci)
3844

3945
Uses standard Cosmos SDK fee model:
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Sponsored Batch Transactions
2+
3+
ev-reth supports an EIP-2718 transaction type, `0x76`, for application-sponsored gas and atomic batches of EVM operations.
4+
5+
Use this transaction type when an application wants users to submit intents without holding the native gas token, or when a product flow needs several calls to succeed or fail together.
6+
7+
## What It Adds
8+
9+
| Capability | Description |
10+
|------------|-------------|
11+
| Gas sponsorship | A sponsor account can pay gas for a transaction signed by another account |
12+
| Batch calls | One transaction can execute multiple `Call` operations |
13+
| Atomic execution | If any call reverts, the whole transaction reverts |
14+
| Standard submission | Signed transactions are submitted through `eth_sendRawTransaction` |
15+
| RPC visibility | Sponsored transactions expose a `feePayer` field in transaction and receipt responses |
16+
17+
Regular Ethereum transactions still work. Use type `0x76` only for sponsored or batched flows.
18+
19+
## Transaction Model
20+
21+
An EvNode transaction replaces the standard single `to`, `value`, and `input` fields with a list of calls:
22+
23+
```typescript
24+
type Call = {
25+
to: Address | null
26+
value: bigint
27+
data: Hex
28+
}
29+
```
30+
31+
The transaction also keeps EIP-1559-style fee fields:
32+
33+
```typescript
34+
type EvNodeTransaction = {
35+
chainId: bigint
36+
nonce: bigint
37+
maxPriorityFeePerGas: bigint
38+
maxFeePerGas: bigint
39+
gasLimit: bigint
40+
calls: Call[]
41+
accessList: AccessList
42+
feePayerSignature?: Signature
43+
}
44+
```
45+
46+
The executor is the account that signs the transaction intent. The executor remains the transaction `from` address and is visible to contracts as `tx.origin`.
47+
48+
The sponsor is optional. When a sponsor signs the transaction, ev-reth charges gas to the sponsor instead of the executor. Value transfers still come from the executor.
49+
50+
## Batch Calls
51+
52+
Each call contains:
53+
54+
- `to`: target contract or account. Use `null` for contract creation.
55+
- `value`: native token value sent with that call.
56+
- `data`: calldata or contract creation bytecode.
57+
58+
Rules:
59+
60+
- `calls` must contain at least one item.
61+
- Calls execute in order.
62+
- Only the first call may be a contract creation (`to: null`).
63+
- If any call fails, all state changes from earlier calls in the same transaction are reverted.
64+
65+
Example batch:
66+
67+
```typescript
68+
await evnode.send({
69+
calls: [
70+
{ to: token, value: 0n, data: approveData },
71+
{ to: router, value: 0n, data: swapData },
72+
],
73+
})
74+
```
75+
76+
This pattern is useful for application flows such as approve-then-call, multi-step account setup, onboarding actions, and batched admin operations.
77+
78+
## Sponsorship Flow
79+
80+
Sponsorship separates authorization from gas payment:
81+
82+
1. The executor builds the transaction with `feePayerSignature` empty.
83+
2. The executor signs the transaction intent using the `0x76` signature domain.
84+
3. The application validates the intent against its sponsorship policy.
85+
4. The sponsor signs the same intent using the `0x78` sponsor domain.
86+
5. The final signed transaction is submitted with `eth_sendRawTransaction`.
87+
88+
The sponsor signature binds to the executor address. A sponsor cannot reuse the same sponsor signature for a different executor.
89+
90+
For a user-facing application, the usual architecture is:
91+
92+
```text
93+
User wallet / app client
94+
signs executor intent
95+
|
96+
v
97+
Application sponsor service
98+
checks policy and sponsor balance
99+
adds feePayerSignature
100+
|
101+
v
102+
ev-reth JSON-RPC
103+
eth_sendRawTransaction
104+
```
105+
106+
The sponsor pays gas up to `gasLimit * maxFeePerGas`. Any gas refund goes back to the sponsor. The executor only needs enough balance for value transfers in the batch.
107+
108+
## JSON-RPC Behavior
109+
110+
Submit type `0x76` transactions with standard Ethereum JSON-RPC:
111+
112+
```json
113+
{
114+
"jsonrpc": "2.0",
115+
"id": 1,
116+
"method": "eth_sendRawTransaction",
117+
"params": ["0x76..."]
118+
}
119+
```
120+
121+
When a sponsored transaction is returned by `eth_getTransactionByHash`, `eth_getBlockByNumber`, or related transaction APIs, ev-reth includes the recovered sponsor address as `feePayer`.
122+
123+
Receipts also include `feePayer` when sponsorship was used.
124+
125+
## Validation
126+
127+
ev-reth validates type `0x76` transactions before admitting them to the txpool:
128+
129+
- The executor signature must be valid.
130+
- The sponsor signature must be valid when present.
131+
- The call list must be non-empty.
132+
- Only the first call may create a contract.
133+
- Sponsored transactions require the sponsor to have enough balance for gas.
134+
- Unsponsored transactions require the executor to have enough balance for gas and value.
135+
136+
## Client Support
137+
138+
Most Ethereum wallets and CLIs do not know how to encode transaction type `0x76`. Use the JavaScript client for application code:
139+
140+
```bash
141+
pnpm add @evstack/evnode-viem viem
142+
```
143+
144+
See [JavaScript Client](/ev-reth/js-client) for signing, batching, sponsorship, and serialization examples.

docs/ev-reth/js-client.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# JavaScript Client
2+
3+
`@evstack/evnode-viem` is a Viem-based client for EvNode transaction type `0x76`. It builds, signs, sponsors, serializes, and sends sponsored batch transactions to ev-reth.
4+
5+
Use it when your application needs:
6+
7+
- Sponsored gas, where an application account pays transaction fees.
8+
- Batched calls, where several EVM operations execute atomically.
9+
- Direct encoding and decoding of `0x76` raw transactions.
10+
11+
## Install
12+
13+
```bash
14+
pnpm add @evstack/evnode-viem viem
15+
```
16+
17+
## Create a Client
18+
19+
`createEvnodeClient` wraps a Viem client. The signer must implement `signHash(hash)`, and that function must sign the raw 32-byte digest without adding an EIP-191 prefix.
20+
21+
```typescript
22+
import { createClient, http } from 'viem'
23+
import { privateKeyToAccount, sign } from 'viem/accounts'
24+
import { createEvnodeClient } from '@evstack/evnode-viem'
25+
26+
const rpcUrl = 'http://localhost:8545'
27+
const privateKey = '0x...' as const
28+
29+
const client = createClient({
30+
transport: http(rpcUrl),
31+
})
32+
33+
const account = privateKeyToAccount(privateKey)
34+
35+
const evnode = createEvnodeClient({
36+
client,
37+
executor: {
38+
address: account.address,
39+
signHash: async (hash) => sign({ hash, privateKey }),
40+
},
41+
})
42+
```
43+
44+
## Send a Batch
45+
46+
Each call has `to`, `value`, and `data`. Use `to: null` only for the first call when deploying a contract.
47+
48+
```typescript
49+
const txHash = await evnode.send({
50+
calls: [
51+
{ to: recipient1, value: 1_000_000_000_000_000n, data: '0x' },
52+
{ to: recipient2, value: 1_000_000_000_000_000n, data: '0x' },
53+
],
54+
})
55+
```
56+
57+
If either transfer fails, ev-reth reverts the whole transaction.
58+
59+
## Sponsor a Transaction
60+
61+
Use `createIntent` when the executor signs first and a sponsor signs later.
62+
63+
```typescript
64+
import { createClient, http } from 'viem'
65+
import { privateKeyToAccount, sign } from 'viem/accounts'
66+
import { createEvnodeClient } from '@evstack/evnode-viem'
67+
68+
const client = createClient({ transport: http('http://localhost:8545') })
69+
70+
const executorKey = '0x...' as const
71+
const sponsorKey = '0x...' as const
72+
const executor = privateKeyToAccount(executorKey)
73+
const sponsor = privateKeyToAccount(sponsorKey)
74+
75+
const evnode = createEvnodeClient({
76+
client,
77+
executor: {
78+
address: executor.address,
79+
signHash: async (hash) => sign({ hash, privateKey: executorKey }),
80+
},
81+
sponsor: {
82+
address: sponsor.address,
83+
signHash: async (hash) => sign({ hash, privateKey: sponsorKey }),
84+
},
85+
})
86+
87+
const intent = await evnode.createIntent({
88+
calls: [
89+
{ to: executor.address, value: 0n, data: '0x' },
90+
],
91+
})
92+
93+
const txHash = await evnode.sponsorAndSend({ intent })
94+
```
95+
96+
The executor remains the transaction sender. The sponsor pays gas and receives gas refunds.
97+
98+
## Manual Parameters
99+
100+
If omitted, the client resolves `chainId`, `nonce`, `maxFeePerGas`, `maxPriorityFeePerGas`, `gasLimit`, and `accessList` from the RPC endpoint or local defaults.
101+
102+
Override them when your application already has fee estimates or nonce management:
103+
104+
```typescript
105+
await evnode.send({
106+
calls: [
107+
{ to: account.address, value: 0n, data: '0x' },
108+
],
109+
nonce: 12n,
110+
gasLimit: 100_000n,
111+
maxFeePerGas: 1_000_000_000n,
112+
maxPriorityFeePerGas: 0n,
113+
accessList: [],
114+
})
115+
```
116+
117+
## Application Sponsor Service
118+
119+
Applications can hide sponsor keys behind a JSON-RPC proxy. The proxy receives `eth_sendRawTransaction`, detects unsigned type `0x76` transactions, validates the app policy, adds `feePayerSignature`, and forwards the sponsored raw transaction to ev-reth.
120+
121+
The ev-reth repository includes this pattern under `bin/sponsor-service`.
122+
123+
Expected proxy behavior:
124+
125+
- Forward ordinary JSON-RPC requests unchanged.
126+
- Intercept `eth_sendRawTransaction` only when the raw transaction starts with type byte `0x76`.
127+
- Forward already-sponsored transactions unchanged.
128+
- Reject intents that fail policy checks, such as wrong chain ID, high gas limit, high max fee, or low sponsor balance.
129+
130+
Client-side code can then point Viem at the sponsor service instead of the node:
131+
132+
```typescript
133+
const client = createClient({
134+
transport: http('http://localhost:3000'),
135+
})
136+
137+
const evnode = createEvnodeClient({
138+
client,
139+
executor: {
140+
address: executor.address,
141+
signHash: async (hash) => sign({ hash, privateKey: executorKey }),
142+
},
143+
})
144+
145+
const txHash = await evnode.send({
146+
calls: [
147+
{ to, value, data },
148+
],
149+
})
150+
```
151+
152+
In this setup the executor signs the intent, while the sponsor service adds the sponsor signature before forwarding to ev-reth.
153+
154+
## Utilities
155+
156+
The package also exports lower-level helpers:
157+
158+
| Helper | Use |
159+
|--------|-----|
160+
| `encodeSignedTransaction` | Serialize an EvNode signed transaction |
161+
| `decodeEvNodeTransaction` | Decode a type `0x76` raw transaction |
162+
| `computeExecutorSigningHash` | Compute the executor digest |
163+
| `computeSponsorSigningHash` | Compute the sponsor digest |
164+
| `computeTxHash` | Compute the transaction hash |
165+
| `recoverExecutor` | Recover the executor address |
166+
| `recoverSponsor` | Recover the sponsor address |
167+
| `estimateIntrinsicGas` | Estimate the minimum intrinsic gas for calls |
168+
| `validateEvNodeTx` | Validate local call-list constraints |
169+
170+
## Related Docs
171+
172+
- [Sponsored Batch Transactions](/ev-reth/features/sponsored-transactions)
173+
- [ev-reth Overview](/ev-reth/overview)
174+
- [EVM Quickstart](/getting-started/evm/quickstart)

docs/ev-reth/overview.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ ev-reth extends reth with:
88

99
- **Engine API integration** — Driven by ev-node for block production
1010
- **Rollup-specific features** — Base fee redirect, deploy allowlist, custom precompiles
11+
- **Application gas sponsorship** — Custom transaction type for sponsored batch operations
1112
- **Configurable chain parameters** — Contract size limits, custom gas settings
1213

1314
## Architecture
@@ -47,6 +48,7 @@ ev-node drives ev-reth through the Engine API:
4748
| [Deploy Allowlist](/ev-reth/features/deploy-allowlist) | Restrict who can deploy contracts |
4849
| [Contract Size Limits](/ev-reth/features/contract-size-limits) | Increase max contract size beyond 24KB |
4950
| [Mint Precompile](/ev-reth/features/mint-precompile) | Native token minting for bridges |
51+
| [Sponsored Batch Transactions](/ev-reth/features/sponsored-transactions) | Sponsor user gas and execute multiple calls atomically |
5052

5153
## When to Use ev-reth
5254

@@ -67,3 +69,4 @@ Use ev-reth when you want:
6769
- [EVM Quickstart](/getting-started/evm/quickstart) — Get started
6870
- [Configuration](/ev-reth/configuration) — Chainspec and settings
6971
- [Engine API](/ev-reth/engine-api) — How ev-node communicates with ev-reth
72+
- [JavaScript Client](/ev-reth/js-client) — Build and sponsor type `0x76` transactions

docs/getting-started/evm/quickstart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,7 @@ forge create src/Counter.sol:Counter --rpc-url http://localhost:8545 --private-k
8787

8888
- [Configure ev-reth](/getting-started/evm/setup-ev-reth) — Customize chainspec, features
8989
- [Deploy Contracts](/getting-started/evm/deploy-contracts) — Foundry and Hardhat setup
90+
- [Sponsored Batch Transactions](/ev-reth/features/sponsored-transactions) — Sponsor user gas and batch app operations
91+
- [JavaScript Client](/ev-reth/js-client) — Build type `0x76` transactions from Viem
9092
- [Connect to Celestia](/guides/da-layers/celestia) — Production DA layer
9193
- [Run a Full Node](/guides/running-nodes/full-node) — Non-sequencer node setup

0 commit comments

Comments
 (0)