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
+15-15Lines changed: 15 additions & 15 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:
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).
150
+
The user sends an XRPL `Payment` to the FAssets [directminting 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).
151
151
The bytes never appear on the XRPL ledger.
152
152
153
-
### Step 2: executor side
153
+
### Step 2: Executor Side
154
154
155
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)):
156
156
@@ -164,28 +164,28 @@ function executeDirectMintingWithData(
164
164
-`_payment` is the FDC proof of the XRPL `Payment`.
165
165
-`_data` is the ABI-encoded `PackedUserOperation` that was delivered out-of-band.
166
166
-`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.
168
168
169
169
The `executeDirectMintingWithData` function is **only valid for smart-account targets** - calling it for a non-smart-account direct mint reverts.
170
170
171
-
### Step 3: confirmation
171
+
### Step 3: Confirmation
172
172
173
173
The `MasterAccountController` verifies on-chain that `keccak256(_data) == userOpHash` from the memo.
174
174
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
175
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
176
177
-
## Hash mismatch
177
+
## Hash Mismatch
178
178
179
179
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)).
181
181
182
-
## Call value accounting
182
+
## Call Value Accounting
183
183
184
184
Whatever native value the executor attaches to `executeDirectMintingWithData` is forwarded all the way to `executeUserOp` (`AssetManagerFXRP -> MasterAccountController.handleMintedFAssets -> PersonalAccount.executeUserOp`).
185
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.
186
186
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
187
188
-
## Replay protection
188
+
## Replay Protection
189
189
190
190
Two replay-protection layers gate every custom instruction:
@@ -31,7 +31,7 @@ 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,9 +27,9 @@ 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.
@@ -64,7 +64,7 @@ The 0xFE memo is a constant 42 bytes regardless of the user operation size, whic
64
64
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
65
The raw custom instruction trades memo bandwidth for end-to-end simplicity.
Copy file name to clipboardExpand all lines: docs/smart-accounts/guides/typescript-viem/03-raw-custom-instruction.mdx
+9-9Lines changed: 9 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,16 +31,16 @@ Use the raw flow when you do not want to operate or coordinate with an executor
31
31
32
32
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.
33
33
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.
35
35
36
36
A prerequisite for the user operation to be executed is that the personal account holds enough native tokens to cover the call values.
37
37
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.
38
38
39
39
The full code is available on [GitHub](https://github.com/flare-foundation/flare-viem-starter/blob/main/src/custom-instructions-raw.ts).
40
40
41
-
## Splitting the call array across two payments
41
+
## Splitting the Call Array Across Two Payments
42
42
43
-
XRPL caps the memo at roughly `1024` bytes.
43
+
XRPL caps the memo at `1024` bytes.
44
44
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:
The `Call` struct fields are the same as for the [custom instruction](/smart-accounts/guides/typescript-viem/custom-instruction-ts#building-the-call-array).
82
82
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.
83
83
84
-
## Encoding the user operation
84
+
## Encoding the User Operation
85
85
86
86
The XRPL memo carries a 10-byte header followed by an ABI-encoded [`PackedUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#useroperation):
87
87
@@ -142,9 +142,9 @@ export function encodeExecuteUserOpMemo({
142
142
143
143
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.
144
144
145
-
## Sending the user operation
145
+
## Sending the User Operation
146
146
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.
148
148
The encoded user operation is attached as a memo, with the `0x` prefix removed.
149
149
150
150
:::warning No destination tags
@@ -196,7 +196,7 @@ export async function sendMemoFieldInstruction({
196
196
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.
197
197
The `xrplClient` and `xrplWallet` are the `Client` and `Wallet` classes from the `xrpl` library, initialized from the `.env` file.
198
198
199
-
## Waiting for execution
199
+
## Waiting for Execution
200
200
201
201
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:
202
202
@@ -241,7 +241,7 @@ The bridging is handled by the [Flare Data Connector](/fdc/overview), which caps
241
241
If any inner call reverts, the whole user operation reverts with [`CallFailed`](/smart-accounts/reference/IPersonalAccount#callfailed) and the nonce does not increment.
242
242
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).
243
243
244
-
## Full script
244
+
## Full Script
245
245
246
246
The repository with the example is available on [GitHub](https://github.com/flare-foundation/flare-viem-starter).
247
247
Helper functions live in the `src/utils` directory.
@@ -250,7 +250,7 @@ Helper functions live in the `src/utils` directory.
250
250
{RawCustomInstructionsScript}
251
251
</CodeBlock>
252
252
253
-
## Expected output
253
+
## Expected Output
254
254
255
255
```bash
256
256
Personal account address: 0xFd2f0eb6b9fA4FE5bb1F7B26fEE3c647ed103d9F
0 commit comments