Skip to content

Commit dddcfaf

Browse files
fix(docs): apply suggested changes
Co-authored-by: Kristaps Grinbergs <fassko@gmail.com>
1 parent 97970aa commit dddcfaf

6 files changed

Lines changed: 59 additions & 60 deletions

File tree

docs/smart-accounts/1-overview.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,13 @@ The contract then resolves the XRPL user's `PersonalAccount` from the address ma
174174
### Direct-minting flow
175175

176176
When the user mints FXRP directly to their smart account via [FAssets direct minting](/fassets/direct-minting), the FAssets `AssetManager` calls back into [`handleMintedFAssets`](/smart-accounts/reference/IMasterAccountController#handlemintedfassets) on `MemoInstructionsFacet`.
177-
The facet enforces that the caller is the `AssetManager`, resolves (or deploys) the user's `PersonalAccount`, pays an executor fee out of the minted FAssets, forwards the remainder to the personal account, and dispatches any memo instruction (`0xFF`, `0xFE`, `0xE0`, `0xE1`, `0xE2`, `0xD0`, `0xD1`).
177+
It enforces that the caller is the `AssetManager`, resolves (or deploys) the user's `PersonalAccount`, pays an executor fee out of the minted FAssets, forwards the remainder to the personal account, and dispatches any memo instruction (`0xFF`, `0xFE`, `0xE0`, `0xE1`, `0xE2`, `0xD0`, `0xD1`).
178178

179179
## Actions on Flare
180180

181181
The XRPL user's smart account performs the actions in the instructions.
182182
This can be any of the instructions listed above, reserving collateral for minting FXRP, transferring FXRP to another address, redeeming FXRP, depositing it into a vault ...
183-
Furthermore, custom instructions can be executed - arbitrary function calls on Flare, encoded as an EIP-4337 `PackedUserOperation` and replayed on-chain by the personal account.
183+
Furthermore, custom instructions can be executed - arbitrary function calls on Flare, encoded as an [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) `PackedUserOperation` and replayed on-chain by the personal account.
184184
The user operation can be committed to as a 32-byte hash with the bytes delivered to Flare by an off-chain executor (opcode `0xFE`, see the [Custom Instruction guide](/smart-accounts/custom-instruction)), or carried in the XRPL memo in full (opcode `0xFF`, see the [Raw Custom Instruction guide](/smart-accounts/raw-custom-instruction)).
185185
Authorization comes from the XRPL `Payment` signature itself; the on-chain check only validates the `sender` and `nonce` fields of the `PackedUserOperation`.
186186
The [Custom Instruction Comparison](/smart-accounts/custom-instruction-comparison) covers when to pick each.

docs/smart-accounts/3-custom-instruction.mdx

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ XRPL transactions targeting smart accounts must not use a destination tag.
3333
A destination tag forces [FAssets direct minting](/fassets/direct-minting) to credit the tag-holder, which would let an unrelated party front-run the user operation.
3434
:::
3535

36-
## User operation payload
36+
## User Operation Payload
3737

38-
A custom instruction has two layers: the outer EIP-4337 [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation) that the XRPL memo commits to, and the inner [`executeUserOp(Call[])`](/smart-accounts/reference/IPersonalAccount#executeuserop) that the personal account runs once the controller dispatches it.
38+
A custom instruction has two layers: the outer [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation) that the XRPL memo commits to, and the inner [`executeUserOp(Call[])`](/smart-accounts/reference/IPersonalAccount#executeuserop) that the personal account runs once the controller dispatches it.
3939

4040
Only three fields from the [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation) struct are required for Flare Smart Accounts:
4141

4242
- `sender` **must** equal the address of the personal account derived from the XRPL sender.
4343
Use [`getPersonalAccount`](/smart-accounts/reference/IMasterAccountController#getpersonalaccount) to look it up - the address is deterministic, so you can fetch it before the account is even deployed.
4444
- `nonce` **must** equal the personal account's current nonce returned by [`getNonce`](/smart-accounts/reference/IMasterAccountController#getnonce).
4545
The nonce auto-increments on every successful execution to prevent replay.
46-
- `callData` is the calldata that the controller invokes on the personal account.
46+
- `callData` is the data that the controller invokes on the personal account.
4747
In practice, this is `abi.encodeCall(IPersonalAccount.executeUserOp, (calls))` - anything else either reverts or is rejected by the personal account's `onlyController` modifier.
4848

4949
The remaining fields are not validated on-chain and can be left empty.
@@ -98,7 +98,7 @@ const callData = encodeFunctionData({
9898

9999
The encoded `callData` becomes the `callData` field of the `PackedUserOperation` that the XRPL memo commits to.
100100

101-
## Memo layout
101+
## Memo Layout
102102

103103
The custom instruction memo is a constant 42 bytes:
104104

@@ -115,9 +115,9 @@ This is the main practical advantage over the [Raw Custom Instruction](/smart-ac
115115
The off-chain delivery also makes the call payload **private on XRPL**.
116116
Only the 32-byte commitment is published; the inner `target`, `value`, and `data` of each call only become visible when the executor submits the user operation to Flare.
117117

118-
## Three-step protocol
118+
## Three-step Protocol
119119

120-
The 0xFE flow runs three steps that map onto two independent actors in production.
120+
The `0xFE` flow runs three steps that map onto two independent actors.
121121
A demo script can run all three from the same process, but the on-chain checks are designed around the two-actor split.
122122
The executor bridges the XRPL payment to Flare with a proof from the [Flare Data Connector (FDC)](/fdc/overview), the same attestation system used by the proof-based flow:
123123

@@ -133,7 +133,7 @@ sequenceDiagram
133133
User->>User: 1. encode UserOp
134134
User->>User: compute keccak256(userOp)
135135
User->>XRPL: 2. Payment to direct-minting<br/>address with 42-byte memo<br/>[0xFE][walletId][fee][hash]
136-
Note over Executor: receives userOp bytes<br/>out-of-band
136+
Note over Executor: receives userOp bytes<br/>off-chain
137137
138138
Executor->>Executor: 3. fetch FDC XRPPayment proof
139139
Executor->>AM: 4. executeDirectMintingWithData(proof, data)<br/>{value: sum(call.value)}
@@ -143,16 +143,15 @@ sequenceDiagram
143143
MasterAccountController-->>User: UserOperationExecuted event
144144
```
145145

146-
### Step 1: user side
146+
### Step 1: User Side
147147

148148
The user constructs the `PackedUserOperation` as described in [User operation payload](#user-operation-payload), computes `keccak256` over the ABI encoding, and packs the 42-byte memo from [Memo layout](#memo-layout).
149149

150-
The user sends an XRPL `Payment` to the FAssets direct-minting address with this memo, and delivers the full `PackedUserOperation` bytes to the executor **out-of-band** (e.g. over an authenticated HTTP API).
151-
The bytes never appear on the XRPL ledger.
150+
The user sends an XRPL `Payment` to the FAssets [direct minting address](/fassets/reference/IAssetManager#directmintingpaymentaddress) with this memo, and delivers the full `PackedUserOperation` bytes to the executor **off-chain** (e.g. over an authenticated HTTP API).
152151

153-
### Step 2: executor side
152+
### Step 2: Executor Side
154153

155-
The executor takes the XRPL transaction hash, requests an [`IXRPPayment` attestation](/fdc/attestation-types/payment) from the [Flare Data Connector](/fdc/overview), and calls `executeDirectMintingWithData` on `AssetManagerFXRP` (see the [FAssets direct minting page](/fassets/direct-minting)):
154+
The executor takes the XRPL transaction hash, requests an [`XRPPayment` attestation](/fdc/attestation-types/xrp-payment) from the [Flare Data Connector](/fdc/overview), and calls `executeDirectMintingWithData` on `AssetManagerFXRP` (see the [FAssets direct minting page](/fassets/direct-minting)):
156155

157156
```solidity
158157
function executeDirectMintingWithData(
@@ -162,30 +161,30 @@ function executeDirectMintingWithData(
162161
```
163162

164163
- `_payment` is the FDC proof of the XRPL `Payment`.
165-
- `_data` is the ABI-encoded `PackedUserOperation` that was delivered out-of-band.
164+
- `_data` is the ABI-encoded `PackedUserOperation` that was delivered off-chain.
166165
- `msg.value` **must equal the sum of `call.value` across the user operation**.
167-
`AssetManagerFXRP` forwards this value into `MasterAccountController.handleMintedFAssets`, which forwards it again into the personal account's `executeUserOp` so the inner calls can attach native value.
166+
The `AssetManagerFXRP` forwards this value to the `MasterAccountController` function `handleMintedFAssets`, which forwards it to the personal account's `executeUserOp` function so that the inner calls can attach the native value.
168167

169168
The `executeDirectMintingWithData` function is **only valid for smart-account targets** - calling it for a non-smart-account direct mint reverts.
170169

171-
### Step 3: confirmation
170+
### Step 3: Confirmation
172171

173172
The `MasterAccountController` verifies on-chain that `keccak256(_data) == userOpHash` from the memo.
174173
If it matches, it decodes `_data` as a `PackedUserOperation`, validates `sender` and `nonce`, executes `executeUserOp` on the personal account, and emits [`UserOperationExecuted`](/smart-accounts/reference/IMasterAccountController#useroperationexecuted) - **all inside the executor's transaction**.
175174
This is the key difference from the proof-based dispatch: there is no separate cross-chain wait, because the executor's call already executed the user operation by the time it returns.
176175

177-
## Hash mismatch
176+
## Hash Mismatch
178177

179178
If the bytes the executor submits do not hash to the commitment in the memo, `handleMintedFAssets` reverts with `CustomInstructionHashMismatch(expected, actual)`.
180-
The FAsset transfer is performed before the memo is decoded, however, so even on a mismatch the FXRP credited by direct minting remains in the personal account, and the user can recover by issuing a fresh user operation (see [Failure Handling](#failure-handling)).
179+
The FAsset transfer is performed before the memo is decoded, however, so even on a mismatch the FXRP credited by direct minting remains in the personal account, and the user can recover by issuing a new user operation (see [Failure Handling](#failure-handling)).
181180

182-
## Call value accounting
181+
## Call Value Accounting
183182

184183
Whatever native value the executor attaches to `executeDirectMintingWithData` is forwarded all the way to `executeUserOp` (`AssetManagerFXRP -> MasterAccountController.handleMintedFAssets -> PersonalAccount.executeUserOp`).
185-
The executor must therefore compute the total native value to attach as the sum of `call.value` across the user operation it received out-of-band.
184+
The executor must therefore compute the total native value to attach as the sum of `call.value` across the user operation it received off-chain.
186185
The user-side helper in the [TypeScript guide](/smart-accounts/guides/typescript-viem/custom-instruction-ts) returns this value alongside the XRPL transaction hash, so the executor does not have to recompute it from scratch.
187186

188-
## Replay protection
187+
## Replay Protection
189188

190189
Two replay-protection layers gate every custom instruction:
191190

docs/smart-accounts/4-raw-custom-instruction.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sidebar_position: 1
33
slug: raw-custom-instruction
44
title: Raw Custom Instruction
55
authors: [nikerzetic]
6-
description: Performing custom function calls in the Flare Smart Accounts by carrying the full PackedUserOperation in the XRPL memo (0xFF).
6+
description: Performing custom function calls in the Flare Smart Accounts by carrying the full PackedUserOperation in the XRPL memo.
77
tags: [intermediate, ethereum, flare-smart-accounts]
88
keywords:
99
[
@@ -24,14 +24,14 @@ Reach for the raw variant when you do not want to operate or coordinate with an
2424
The [comparison guide](/smart-accounts/custom-instruction-comparison) breaks down when each is appropriate.
2525

2626
This page covers only what differs from the [Custom Instruction](/smart-accounts/custom-instruction).
27-
For the [`PackedUserOperation` construction](/smart-accounts/custom-instruction#user-operation-payload) (`sender`, `nonce`, `callData`), the [`Call` struct](/smart-accounts/custom-instruction#executeuserop-and-the-call-struct), and how to [build `callData` in TypeScript](/smart-accounts/custom-instruction#building-calldata-in-typescript), refer to the Custom Instruction page.
27+
For the [`PackedUserOperation` construction](/smart-accounts/custom-instruction#user-operation-payload) (`sender`, `nonce`, `callData`), the [`Call` struct](/smart-accounts/custom-instruction#executeuserop-and-the-call-struct), and how to [build `callData` in TypeScript](/smart-accounts/custom-instruction#building-calldata-in-typescript), refer to the [Custom Instruction page](/smart-accounts/custom-instruction).
2828

2929
:::warning No destination tags
3030
XRPL transactions targeting smart accounts must not use a destination tag.
3131
A destination tag forces [FAssets direct minting](/fassets/direct-minting) to credit the tag-holder, which would let an unrelated party front-run the user operation.
3232
:::
3333

34-
## Memo layout
34+
## Memo Layout
3535

3636
The XRPL memo carries a 10-byte instruction header followed by the ABI-encoded `PackedUserOperation`:
3737

@@ -45,12 +45,12 @@ The XRPL memo carries a 10-byte instruction header followed by the ABI-encoded `
4545
The total memo length is `10 + len(abi.encode(userOp))` bytes.
4646
The XRPL caps each memo at `1024` bytes, so large or repetitive call batches must be split across multiple XRPL payments - each of which pays its own FAssets minting fee and executor fee - and a single call whose ABI-encoded calldata alone exceeds the budget cannot be expressed at all without deploying a shim contract on Flare to compress it.
4747

48-
## Dispatch flow
48+
## Dispatch Flow
4949

5050
A single XRPL payment drives the whole flow: the FAssets `AssetManager` mints FXRP into the smart account, calls `MasterAccountController.handleMintedFAssets`, and the controller decodes the `PackedUserOperation` directly from the memo bytes (no `_data` parameter, no hash check) before dispatching `executeUserOp` on the personal account.
5151
There is no executor split: any indexer that observes the XRPL payment can submit the FDC proof through `executeDirectMinting(proof)`, and the `MasterAccountController` validates the same `sender`, `nonce`, and `callData` fields as for the [custom instruction](/smart-accounts/custom-instruction#user-operation-payload).
5252

53-
## Replay protection
53+
## Replay Protection
5454

5555
Each personal account maintains a monotonically increasing nonce, accessible via [`getNonce`](/smart-accounts/reference/IMasterAccountController#getnonce).
5656
A successful `executeUserOp` increments the nonce and emits [`UserOperationExecuted`](/smart-accounts/reference/IMasterAccountController#useroperationexecuted), so the same `PackedUserOperation` cannot be re-executed.
@@ -68,7 +68,7 @@ The whole pipeline is atomic with respect to the user operation:
6868
Because the FXRP transfer is performed before the memo is decoded, **the mint succeeds even if the user operation reverts** - see [`DirectMintingExecuted`](/smart-accounts/reference/IMasterAccountController#directmintingexecuted).
6969
The freshly minted FXRP remains in the personal account, and the user can either re-submit a fixed user operation or transfer the FXRP to another address via standard [FAssets instructions](/smart-accounts/fasset-instructions).
7070

71-
## Next steps
71+
## Next Steps
7272

7373
- Walk through a Viem implementation in the [Raw Custom Instruction TypeScript guide](/smart-accounts/guides/typescript-viem/raw-custom-instruction-ts).
7474
- Read the [Custom Instruction](/smart-accounts/custom-instruction) for the recommended hash-commitment variant.

docs/smart-accounts/5-custom-instruction-comparison.mdx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ keywords:
1616
]
1717
---
1818

19-
Flare Smart Accounts expose two custom instruction memo opcodes that ultimately execute the same `PackedUserOperation` against a personal account:
19+
Flare Smart Accounts expose two custom instruction memo opcodes that ultimately execute the same `PackedUserOperation` in the personal account scope:
2020

2121
- [**Custom Instruction**](/smart-accounts/custom-instruction) - opcode `0xFE`.
2222
The XRPL memo carries only `keccak256(userOp)` in fixed 42 bytes; an off-chain executor delivers the ABI-encoded custom instruction (`userOp`) via `executeDirectMintingWithData`.
@@ -27,11 +27,11 @@ Both flows are validated on-chain against the same `(sender, nonce)` rules and e
2727
The difference is purely in how the user-operation bytes travel to Flare, and which actor performs which step.
2828

2929
The hash-based flow was added because the XRPL `Payment` memo is capped at `1024` bytes - small enough that non-trivial user operations either had to be split across multiple XRPL payments (each paying its own minting and executor fees) or routed through purpose-built shim contracts on Flare that compressed the call into something memo-sized.
30-
The 0xFE memo is a constant 42 bytes regardless of the user operation size, which removes the need for both workarounds.
30+
The `0xFE` memo is a constant 42 bytes regardless of the user's operation size, removing the need for both workarounds.
3131

32-
## Side-by-side
32+
## Comparison
3333

34-
| Dimension | Custom Instruction (`0xFE`) | Raw Custom Instruction (`0xFF`) |
34+
| Dimension | [Custom Instruction](/smart-accounts/custom-instruction) (`0xFE`) | [Raw Custom Instruction](/smart-accounts/raw-custom-instruction) (`0xFF`) |
3535
| ---------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
3636
| XRPL memo payload | 10-byte header + `keccak256(userOp)` (fixed 42 bytes) | 10-byte header + `abi.encode(PackedUserOperation)` |
3737
| XRPL memo size | Constant 42 bytes regardless of batch size | Grows linearly with batch and argument sizes; capped at ~1024 bytes |
@@ -44,8 +44,6 @@ The 0xFE memo is a constant 42 bytes regardless of the user operation size, whic
4444
| Replay protection | Same as raw - plus the on-chain hash check pins `_data` to the memo's commitment | Personal account nonce + `usedTransactionIds` |
4545
| Failure modes specific to the flow | `CustomInstructionHashMismatch` if `keccak256(_data)` differs from the commitment | None beyond shared ones |
4646

47-
## When to use which
48-
4947
**Use the custom instruction (`0xFE`) when**
5048

5149
- The user operation's ABI encoding exceeds the XRPL memo cap (long `bytes` arguments, large call batches, or deeply nested structs).
@@ -64,7 +62,7 @@ The 0xFE memo is a constant 42 bytes regardless of the user operation size, whic
6462
In short, the custom instruction trades a small amount of off-chain coordination (and one extra on-chain proof-binding check) for a fixed memo size and private payloads.
6563
The raw custom instruction trades memo bandwidth for end-to-end simplicity.
6664

67-
## What stays the same
65+
## Similarities
6866

6967
The two flows share more than they differ:
7068

0 commit comments

Comments
 (0)