Skip to content

Commit ef39bd7

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

5 files changed

Lines changed: 35 additions & 35 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: 15 additions & 15 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

@@ -143,14 +143,14 @@ 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).
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 **out-of-band** (e.g. over an authenticated HTTP API).
151151
The bytes never appear on the XRPL ledger.
152152

153-
### Step 2: executor side
153+
### Step 2: Executor Side
154154

155155
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)):
156156

@@ -164,28 +164,28 @@ function executeDirectMintingWithData(
164164
- `_payment` is the FDC proof of the XRPL `Payment`.
165165
- `_data` is the ABI-encoded `PackedUserOperation` that was delivered out-of-band.
166166
- `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.
167+
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.
168168

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

171-
### Step 3: confirmation
171+
### Step 3: Confirmation
172172

173173
The `MasterAccountController` verifies on-chain that `keccak256(_data) == userOpHash` from the memo.
174174
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**.
175175
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.
176176

177-
## Hash mismatch
177+
## Hash Mismatch
178178

179179
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)).
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 new user operation (see [Failure Handling](#failure-handling)).
181181

182-
## Call value accounting
182+
## Call Value Accounting
183183

184184
Whatever native value the executor attaches to `executeDirectMintingWithData` is forwarded all the way to `executeUserOp` (`AssetManagerFXRP -> MasterAccountController.handleMintedFAssets -> PersonalAccount.executeUserOp`).
185185
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.
186186
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.
187187

188-
## Replay protection
188+
## Replay Protection
189189

190190
Two replay-protection layers gate every custom instruction:
191191

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

Lines changed: 5 additions & 5 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
[
@@ -31,7 +31,7 @@ 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: 4 additions & 4 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,9 +27,9 @@ 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 Table
3333

3434
| Dimension | Custom Instruction (`0xFE`) | Raw Custom Instruction (`0xFF`) |
3535
| ---------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
@@ -64,7 +64,7 @@ The 0xFE memo is a constant 42 bytes regardless of the user operation size, whic
6464
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.
6565
The raw custom instruction trades memo bandwidth for end-to-end simplicity.
6666

67-
## What stays the same
67+
## Similarities
6868

6969
The two flows share more than they differ:
7070

docs/smart-accounts/guides/typescript-viem/03-raw-custom-instruction.mdx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ Use the raw flow when you do not want to operate or coordinate with an executor
3131

3232
This guide hits that cap head-on: it executes the same three example calls as the [Custom Instruction TypeScript guide](/smart-accounts/guides/typescript-viem/custom-instruction-ts), but the XRPL memo field is capped at `1024` bytes, and the three calls together exceed that budget when ABI-encoded.
3333
The script therefore splits the work into two user operations sent in sequence, paying the FAssets minting fee and executor fee on each XRPL payment.
34-
The same cap is also why some calls - those whose calldata alone overruns the memo budget - cannot be expressed in the 0xFF flow without deploying a shim contract on Flare to compress them.
34+
The same cap is also why some calls - those whose calldata alone overruns the memo budget - cannot be expressed in the `0xFF` flow without deploying a shim contract on Flare to compress them.
3535

3636
A prerequisite for the user operation to be executed is that the personal account holds enough native tokens to cover the call values.
3737
The script calls forward a total of `2` C2FLR (Coston2 Flare native tokens), so fund the personal account from the [Flare faucet](https://faucet.flare.network/coston2) before running it.
3838

3939
The full code is available on [GitHub](https://github.com/flare-foundation/flare-viem-starter/blob/main/src/custom-instructions-raw.ts).
4040

41-
## Splitting the call array across two payments
41+
## Splitting the Call Array Across Two Payments
4242

43-
XRPL caps the memo at roughly `1024` bytes.
43+
XRPL caps the memo at `1024` bytes.
4444
The `pinNotice` call carries a string argument that, together with the other two calls, exceeds that limit, so the script splits the work into two user operations:
4545

4646
```typescript
@@ -81,7 +81,7 @@ const pinNoticeCalls: Call[] = [
8181
The `Call` struct fields are the same as for the [custom instruction](/smart-accounts/guides/typescript-viem/custom-instruction-ts#building-the-call-array).
8282
The split is the entire reason this guide exists: in the [custom instruction flow](/smart-accounts/guides/typescript-viem/custom-instruction-ts) the same three calls fit in a single 42-byte memo, regardless of batch size.
8383

84-
## Encoding the user operation
84+
## Encoding the User Operation
8585

8686
The XRPL memo carries a 10-byte header followed by an ABI-encoded [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation):
8787

@@ -142,9 +142,9 @@ export function encodeExecuteUserOpMemo({
142142

143143
Unlike the custom instruction, which carries only `keccak256(userOp)` in the memo, this header is followed by the full ABI-encoded `PackedUserOperation` - hence the memo cap problem above.
144144

145-
## Sending the user operation
145+
## Sending the User Operation
146146

147-
The XRPL `Payment` is sent to the FAssets **direct minting payment address** read from the `AssetManagerFXRP` contract - not to an operator wallet.
147+
The XRPL `Payment` is sent to the FAssets [direct minting payment address](/fassets/reference/IAssetManager#directmintingpaymentaddress) read from the `AssetManagerFXRP` contract - not to an operator wallet.
148148
The encoded user operation is attached as a memo, with the `0x` prefix removed.
149149

150150
:::warning No destination tags
@@ -196,7 +196,7 @@ export async function sendMemoFieldInstruction({
196196
There is no `(data, totalCallValue)` to hand off to an executor here: the entire user operation already lives in the memo, so the only handoff is the XRPL payment itself.
197197
The `xrplClient` and `xrplWallet` are the `Client` and `Wallet` classes from the `xrpl` library, initialized from the `.env` file.
198198

199-
## Waiting for execution
199+
## Waiting for Execution
200200

201201
Because the raw flow has no executor receipt to parse, the script watches for the `UserOperationExecuted` event with Viem's [`watchContractEvent`](https://viem.sh/docs/contract/watchContractEvent#watchcontractevent), filtering on the personal account and the nonce we submitted:
202202

@@ -241,7 +241,7 @@ The bridging is handled by the [Flare Data Connector](/fdc/overview), which caps
241241
If any inner call reverts, the whole user operation reverts with [`CallFailed`](/smart-accounts/reference/IPersonalAccount#callfailed) and the nonce does not increment.
242242
The FXRP transfer, however, is performed before the memo is decoded, so the mint succeeds even when the user operation reverts - see [`DirectMintingExecuted`](/smart-accounts/reference/IMasterAccountController#directmintingexecuted).
243243

244-
## Full script
244+
## Full Script
245245

246246
The repository with the example is available on [GitHub](https://github.com/flare-foundation/flare-viem-starter).
247247
Helper functions live in the `src/utils` directory.
@@ -250,7 +250,7 @@ Helper functions live in the `src/utils` directory.
250250
{RawCustomInstructionsScript}
251251
</CodeBlock>
252252

253-
## Expected output
253+
## Expected Output
254254

255255
```bash
256256
Personal account address: 0xFd2f0eb6b9fA4FE5bb1F7B26fEE3c647ed103d9F

0 commit comments

Comments
 (0)