Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/pages/accounts/rpc/eth_fillTransaction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ type Response = {
symbol: string
}

/** Structured error details when the fill fails. */
error?: {
/** Revert error name (e.g. "InsufficientBalance"). */
errorName: string
/** Human-readable error message. */
message: string
}

/** Funding requirement when InsufficientBalance is encountered. */
requireFunds?: {
/** Deficit amount in token units. */
amount: `0x${string}`
/** Token decimals (e.g. 6). */
decimals: number
/** Human-readable deficit (e.g. "100.000000"). */
formatted: string
/** Token address. */
token: `0x${string}`
/** Token symbol (e.g. "USDC.e"). */
symbol: string
}

/** Sponsor details, present when `sponsored` is `true`. */
sponsor?: {
/** Sponsor address. */
Expand Down Expand Up @@ -312,3 +334,75 @@ result.capabilities.fee
// symbol: 'pathUSD',
// }
```

### Error Details

When `eth_fillTransaction` fails, the relay returns structured error details in `capabilities.error` instead of throwing.

```ts twoslash
import { Provider } from 'accounts'
const provider = Provider.create()
const [account] = await provider.request({ method: 'eth_accounts' })
// ---cut---
const result = await provider.request({
method: 'eth_fillTransaction',
params: [{
from: account,
calls: [{
to: '0x20c000000000000000000000b9537d11c60e8b50', // USDC.e
data: '0xa9059cbb000000000000000000000000cafebabecafebabecafebabecafebabecafebabe0000000000000000000000000000000000000000000000000000000005f5e100',
}],
}],
})

result.capabilities.error
// {
// errorName: 'BelowMinimumOrderSize',
// message: 'Below minimum order size: 1000000.',
// }
```

### Require Funds

For `InsufficientBalance` errors, the relay also returns a `capabilities.requireFunds` object with the exact deficit amount and token metadata.

```ts twoslash
import { Provider } from 'accounts'
const provider = Provider.create()
const [account] = await provider.request({ method: 'eth_accounts' })
// ---cut---
const result = await provider.request({
method: 'eth_fillTransaction',
params: [{
from: account,
calls: [{
to: '0x20c000000000000000000000b9537d11c60e8b50', // USDC.e
// transfer 100 USDC.e (but account has 0)
data: '0xa9059cbb000000000000000000000000cafebabecafebabecafebabecafebabecafebabe0000000000000000000000000000000000000000000000000000000005f5e100',
}],
}],
})

result.capabilities.requireFunds
// {
// amount: '0x5f5e100',
// decimals: 6,
// formatted: '100.000000',
// token: '0x20c000000000000000000000b9537d11c60e8b50',
// symbol: 'USDC.e',
// }

result.capabilities.balanceDiffs
// {
// '0x1234567890abcdef1234567890abcdef12345678': [{
// address: '0x20c000000000000000000000b9537d11c60e8b50',
// decimals: 6,
// direction: 'outgoing',
// formatted: '100.000000',
// name: 'USDC.e',
// recipients: ['0xcafebabecafebabecafebabecafebabecafebabe'],
// symbol: 'USDC.e',
// value: '0x5f5e100',
// }],
// }
```
113 changes: 110 additions & 3 deletions src/pages/accounts/server/handler.relay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export const POST = handler.fetch // Next.js
title="Fee Derivation"
to="#fee-derivation"
/>
<Card
description="Exact deficit amount and token metadata when funds are insufficient"
icon="lucide:wallet"
title="Require Funds"
to="#require-funds"
/>
</Cards>

### Sponsorship
Expand Down Expand Up @@ -124,7 +130,7 @@ result.capabilities.sponsor

### Auto Swap

When a user has insufficient balance of a required token, the relay automatically injects swap calls (approve + buy) via the [Stablecoin DEX](/guide/stablecoin-dex). Swap details are reported in `capabilities.autoSwap`, and swap-related balance diffs are excluded from `capabilities.balanceDiffs`. Configurable via [`autoSwap`](#autoswap).
When a user has insufficient balance of a required token, the relay automatically injects swap calls (approve + buy) via the [Stablecoin DEX](/guide/stablecoin-dex). Swap details are reported in `capabilities.autoSwap`, and swap-related balance diffs are excluded from `capabilities.balanceDiffs`. Enabled by [`features: 'all'`](#features), configurable via [`autoSwap`](#autoswap).

:::code-group

Expand Down Expand Up @@ -299,14 +305,73 @@ const handler = Handler.relay()

:::

### Require Funds

For insufficient balance errors, the relay also returns a `capabilities.requireFunds` object with the exact deficit amount and token metadata — enabling UIs to prompt the user to fund their account.

```ts twoslash
import { Provider } from 'accounts'
const provider = Provider.create()
const [account] = await provider.request({ method: 'eth_accounts' })
// ---cut---
const result = await provider.request({
method: 'eth_fillTransaction',
params: [{
from: account,
calls: [{
to: '0x20c000000000000000000000b9537d11c60e8b50', // USDC.e
// transfer 100 USDC.e (but account has 40)
data: '0xa9059cbb000000000000000000000000cafebabecafebabecafebabecafebabecafebabe0000000000000000000000000000000000000000000000000000000005f5e100',
}],
}],
})

result.capabilities.requireFunds
// {
// amount: '0x3938700',
// decimals: 6,
// formatted: '60.000000',
// token: '0x20c000000000000000000000b9537d11c60e8b50',
// symbol: 'USDC.e',
// }

result.capabilities.balanceDiffs
// {
// '0x1234567890abcdef1234567890abcdef12345678': [{
// address: '0x20c000000000000000000000b9537d11c60e8b50',
// decimals: 6,
// direction: 'outgoing',
// formatted: '100.000000',
// name: 'USDC.e',
// recipients: ['0xcafebabecafebabecafebabecafebabecafebabe'],
// symbol: 'USDC.e',
// value: '0x5f5e100',
// }],
// }
```

### Enabling Features

By default, only a minimum set of features are enabled, given on what options you pass to `Handler.relay(){:js}` (e.g. `feePayer`, `autoSwap`, etc).

Set [`features: 'all'`](#features) to enable all features by default such as: **fee token resolution**, **auto-swap**, and **simulation** (balance diffs + fee breakdown). This will come at the cost of slightly increased network latency.

```ts twoslash
import { privateKeyToAccount } from 'viem/accounts'
import { Handler } from 'accounts/server'

const handler = Handler.relay({
features: 'all', // [!code focus]
})
```

## Parameters

### autoSwap

- **Type:** `false | { slippage?: number }`
- **Default:** `{}`

AMM swap options for automatic insufficient balance resolution. When a user doesn't hold enough of a token, the relay auto-swaps from their fee token via the [Stablecoin DEX](/guide/stablecoin-dex). Set to `false` to disable.
AMM swap options for automatic insufficient balance resolution. When a user doesn't hold enough of a token, the relay auto-swaps from their fee token via the [Stablecoin DEX](/guide/stablecoin-dex). Set to `false` to disable even when `features: 'all'` is set.

```ts twoslash
import { Handler } from 'accounts/server'
Expand Down Expand Up @@ -339,6 +404,24 @@ const handler = Handler.relay({
})
```

### features

- **Type:** `'all'`
- **Optional**

Controls which relay features are enabled. By default, only fee payer sponsorship is active.

- `'all'`: enables all features (fee token resolution, auto-swap, balance diffs, fee breakdown, etc).
- `undefined` (default): enables only features that are configured via options (e.g. `feePayer`, `autoSwap`, etc).

```ts twoslash
import { Handler } from 'accounts/server'

const handler = Handler.relay({
features: 'all', // [!code focus]
})
```

### feePayer

- **Type:** `object`
Expand Down Expand Up @@ -500,6 +583,30 @@ type Response = {
/** Slippage tolerance (e.g. 0.05 = 5%). */
slippage: number
}
/** Structured error details when the fill fails (e.g. InsufficientBalance). */
error?: {
/** ABI item that caused the error. */
abiItem: AbiItem
/** Data that caused the error. */
data: Hex
/** Revert error name (e.g. "InsufficientBalance"). */
errorName: string
/** Human-readable error message. */
message: string
}
/** Funding requirement when InsufficientBalance is encountered. */
requireFunds?: {
/** Deficit amount in token units (hex-encoded). */
amount: Hex
/** Token decimals (e.g. 6). */
decimals: number
/** Human-readable deficit (e.g. "100.000000"). */
formatted: string
/** Token address. */
token: Address
/** Token symbol (e.g. "USDC.e"). */
symbol: string
}
/** Sponsor details (when sponsored). */
sponsor?: { address: Address; name: string; url: string }
/** Whether the transaction is sponsored by a fee payer. */
Expand Down
Loading