You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/smart-accounts/1-overview.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -174,13 +174,13 @@ The contract then resolves the XRPL user's `PersonalAccount` from the address ma
174
174
### Direct-minting flow
175
175
176
176
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`).
178
178
179
179
## Actions on Flare
180
180
181
181
The XRPL user's smart account performs the actions in the instructions.
182
182
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.
184
184
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)).
185
185
Authorization comes from the XRPL `Payment` signature itself; the on-chain check only validates the `sender` and `nonce` fields of the `PackedUserOperation`.
186
186
The [Custom Instruction Comparison](/smart-accounts/custom-instruction-comparison) covers when to pick each.
Copy file name to clipboardExpand all lines: docs/smart-accounts/3-custom-instruction.mdx
+19-20Lines changed: 19 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,17 +33,17 @@ XRPL transactions targeting smart accounts must not use a destination tag.
33
33
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.
34
34
:::
35
35
36
-
## User operation payload
36
+
## User Operation Payload
37
37
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.
39
39
40
40
Only three fields from the [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation) struct are required for Flare Smart Accounts:
41
41
42
42
-`sender`**must** equal the address of the personal account derived from the XRPL sender.
43
43
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.
44
44
-`nonce`**must** equal the personal account's current nonce returned by [`getNonce`](/smart-accounts/reference/IMasterAccountController#getnonce).
45
45
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.
47
47
In practice, this is `abi.encodeCall(IPersonalAccount.executeUserOp, (calls))` - anything else either reverts or is rejected by the personal account's `onlyController` modifier.
48
48
49
49
The remaining fields are not validated on-chain and can be left empty.
The encoded `callData` becomes the `callData` field of the `PackedUserOperation` that the XRPL memo commits to.
100
100
101
-
## Memo layout
101
+
## Memo Layout
102
102
103
103
The custom instruction memo is a constant 42 bytes:
104
104
@@ -115,9 +115,9 @@ This is the main practical advantage over the [Raw Custom Instruction](/smart-ac
115
115
The off-chain delivery also makes the call payload **private on XRPL**.
116
116
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.
117
117
118
-
## Three-step protocol
118
+
## Three-step Protocol
119
119
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.
121
121
A demo script can run all three from the same process, but the on-chain checks are designed around the two-actor split.
122
122
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:
123
123
@@ -133,7 +133,7 @@ sequenceDiagram
133
133
User->>User: 1. encode UserOp
134
134
User->>User: compute keccak256(userOp)
135
135
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
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).
149
149
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).
152
151
153
-
### Step 2: executor side
152
+
### Step 2: Executor Side
154
153
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)):
156
155
157
156
```solidity
158
157
function executeDirectMintingWithData(
@@ -162,30 +161,30 @@ function executeDirectMintingWithData(
162
161
```
163
162
164
163
-`_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.
166
165
-`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.
168
167
169
168
The `executeDirectMintingWithData` function is **only valid for smart-account targets** - calling it for a non-smart-account direct mint reverts.
170
169
171
-
### Step 3: confirmation
170
+
### Step 3: Confirmation
172
171
173
172
The `MasterAccountController` verifies on-chain that `keccak256(_data) == userOpHash` from the memo.
174
173
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**.
175
174
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.
176
175
177
-
## Hash mismatch
176
+
## Hash Mismatch
178
177
179
178
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)).
181
180
182
-
## Call value accounting
181
+
## Call Value Accounting
183
182
184
183
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.
186
185
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.
187
186
188
-
## Replay protection
187
+
## Replay Protection
189
188
190
189
Two replay-protection layers gate every custom instruction:
@@ -24,14 +24,14 @@ Reach for the raw variant when you do not want to operate or coordinate with an
24
24
The [comparison guide](/smart-accounts/custom-instruction-comparison) breaks down when each is appropriate.
25
25
26
26
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).
28
28
29
29
:::warning No destination tags
30
30
XRPL transactions targeting smart accounts must not use a destination tag.
31
31
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.
32
32
:::
33
33
34
-
## Memo layout
34
+
## Memo Layout
35
35
36
36
The XRPL memo carries a 10-byte instruction header followed by the ABI-encoded `PackedUserOperation`:
37
37
@@ -45,12 +45,12 @@ The XRPL memo carries a 10-byte instruction header followed by the ABI-encoded `
45
45
The total memo length is `10 + len(abi.encode(userOp))` bytes.
46
46
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.
47
47
48
-
## Dispatch flow
48
+
## Dispatch Flow
49
49
50
50
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.
51
51
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).
52
52
53
-
## Replay protection
53
+
## Replay Protection
54
54
55
55
Each personal account maintains a monotonically increasing nonce, accessible via [`getNonce`](/smart-accounts/reference/IMasterAccountController#getnonce).
56
56
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:
68
68
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).
69
69
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).
70
70
71
-
## Next steps
71
+
## Next Steps
72
72
73
73
- Walk through a Viem implementation in the [Raw Custom Instruction TypeScript guide](/smart-accounts/guides/typescript-viem/raw-custom-instruction-ts).
74
74
- Read the [Custom Instruction](/smart-accounts/custom-instruction) for the recommended hash-commitment variant.
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
27
27
The difference is purely in how the user-operation bytes travel to Flare, and which actor performs which step.
28
28
29
29
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.
| 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
44
44
| Replay protection | Same as raw - plus the on-chain hash check pins `_data` to the memo's commitment | Personal account nonce + `usedTransactionIds`|
45
45
| Failure modes specific to the flow |`CustomInstructionHashMismatch` if `keccak256(_data)` differs from the commitment | None beyond shared ones |
46
46
47
-
## When to use which
48
-
49
47
**Use the custom instruction (`0xFE`) when**
50
48
51
49
- 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
64
62
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.
65
63
The raw custom instruction trades memo bandwidth for end-to-end simplicity.
0 commit comments