Skip to content

Commit 88e590f

Browse files
committed
feat(docs): integrate Mermaid theme for enhanced diagram support in gasless FXRP payments guide
1 parent 89af118 commit 88e590f

File tree

11 files changed

+1537
-341
lines changed

11 files changed

+1537
-341
lines changed

docs/fxrp/token-interactions/04-gasless-fxrp-payments.mdx

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,34 @@ Users sign payment requests off-chain with [EIP-712](https://eips.ethereum.org/E
2424
The token holder signs an authorization off-chain, and a relayer executes the transfer on-chain and pays gas.
2525

2626
This guide uses a custom [EIP-712](https://eips.ethereum.org/EIPS/eip-712) payment message and a forwarder contract instead of EIP-3009 on the token itself.
27-
The forwarder pulls FXRP from the user (after a one-time approval) and sends it to the recipient.
28-
The relayer pays gas and can charge a fee in FXRP.
27+
The forwarder pulls FXRP from the user (after a one-time approval) and sends it to the provided recipient.
28+
The relayer pays gas on behalf of the user.
29+
30+
## Architecture
31+
32+
The architecture of the gasless FXRP payments system is as follows:
33+
34+
```mermaid
35+
sequenceDiagram
36+
participant User
37+
participant Relayer
38+
participant GaslessPaymentForwarder
39+
participant FXRP
40+
41+
User->>User: 1. Sign payment (EIP-712)<br/>off-chain, no gas
42+
User->>Relayer: 2. POST signed request
43+
Relayer->>GaslessPaymentForwarder: 3. executePayment()<br/>(pays gas)
44+
GaslessPaymentForwarder->>FXRP: 4. FXRP transfer
45+
FXRP-->>GaslessPaymentForwarder: 5. Transfer confirmed
46+
GaslessPaymentForwarder-->>Relayer: 6. Execution complete
47+
Relayer-->>User: 7. Payment confirmed
48+
```
2949

3050
## Project setup
3151

32-
Create a new [Hardhat](https://hardhat.org/) project or use the [Flare Hardhat Starter](/network/guides/hardhat-foundry-starter-kit) kit.
52+
To follow this guide, create a new [Hardhat](https://hardhat.org/) project or use the [Flare Hardhat Starter](/network/guides/hardhat-foundry-starter-kit) kit.
3353

34-
Install needed dependencies:
54+
You will need to install the following dependencies:
3555

3656
```bash
3757
npm install ethers viem express cors dotenv
@@ -53,21 +73,18 @@ Save as `contracts/GaslessPaymentForwarder.sol` to create the forwarder smart co
5373

5474
**How the contract works**
5575

56-
1. Define libraries and state.
57-
2. Define events.
58-
3. Defines errors.
59-
4. Implements the contract constructor that sets the EIP-712 domain, owner, and initial relayer fee.
60-
5. Implements the `fxrp` function that returns the FXRP token from the Flare registry.
61-
6. Defines the `executePayment` function that executes a gasless payment.
62-
7. Hashes the request (type hash + from, to, amount, fee, nonce, deadline) and gets the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) digest.
63-
8. Recovers the signer from the signature with ECDSA.
64-
9. Requires the signer of the payment request.
65-
10. Requires a sufficient allowance for the total amount (amount + fee).
66-
11. Transfers the amount from the sender to the recipient.
67-
12. Transfers fee from sender to relayer (msg.sender); emits `PaymentExecuted`.
68-
13. Defines the view functions to get the nonce, domain separator, and payment request hash.
69-
14. Defines the function to set the relater authorization.
70-
15. Define the function to set the relayer fee.
76+
1. Define libraries and state (nonces, authorizedRelayers).
77+
2. Define events (PaymentExecuted, RelayerAuthorized).
78+
3. Define errors.
79+
4. Implement a constructor that sets the EIP-712 domain and owner.
80+
5. Implement `fxrp` to return the FXRP token from the Flare Contract Registry.
81+
6. Define `executePayment(from, to, amount, deadline, signature)`.
82+
7. Hash the request (type hash + from, to, amount, nonce, deadline) and get the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) digest.
83+
8. Recover the signer from the signature.
84+
9. Require signer to be the from address; increment nonce.
85+
10. Require sufficient allowance; transfer amount from sender to recipient.
86+
11. Define view functions: getNonce, getDomainSeparator, getPaymentRequestHash.
87+
12. Define setRelayerAuthorization for the relayer allowlist.
7188

7289
</details>
7390

@@ -77,9 +94,11 @@ Run the following command to compile the contract and generate the typechain-typ
7794
npx hardhat compile
7895
```
7996

97+
It will generate the `typechain-types` directory with the contract interfaces and factories.
98+
8099
### Utilities
81100

82-
Save as `utils/payment.ts` to create the utility functions that will be used to create and sign payment requests.
101+
Save as `utils/payment.ts` to create the utility functions that will be used to create and sign payment requests off-chain.
83102

84103
:::info
85104

@@ -97,17 +116,16 @@ This utility uses the `typechain-types` generated by the previous command.
97116
**Code Breakdown**
98117

99118
1. Import the necessary libraries.
100-
2. Define the needed constants and EIP-712 types.
119+
2. Define constants and EIP-712 types (PaymentRequest: from, to, amount, nonce, deadline).
101120
3. Define types for sign params, payment request, approval result, and user status.
102121
4. Parse human-readable amount to raw bigint with decimals.
103122
5. Format the raw amount to a readable string with decimals.
104123
6. Get FXRP decimals from the forwarder and token contract.
105124
7. Get the current nonce for a user from the forwarder.
106-
8. Get the minimum relayer fee from the forwarder.
107-
9. Sign the payment message with EIP-712 and return the signature.
108-
10. Create a full payment request: fetch nonce and fee, set deadline, sign, and return payload for the relayer.
109-
11. Approve the forwarder to spend FXRP.
110-
12. Check user balance, allowance, nonce, and whether approval is needed.
125+
8. Sign the payment message with EIP-712 and return the signature.
126+
9. Create a full payment request: fetch nonce, set deadline, sign, return payload for the relayer.
127+
10. Approve the forwarder to spend FXRP (one-time).
128+
11. Check user balance, allowance, nonce, and whether approval is needed.
111129

112130
</details>
113131

@@ -127,18 +145,17 @@ Save as `relayer/index.ts` to start the relayer service.
127145
**Code Breakdown**
128146

129147
1. Import necessary libraries.
130-
2. Define EIP-712 domain, payment request types, and other network configurations.
131-
3. Define `RelayerConfig`, `PaymentRequest`, and `ExecuteResult` types.
132-
4. Implement the `GaslessRelayer` class to interact with the forwarder contract.
133-
5. Execute gasless payment: normalize and validate request, recover signer with EIP-712, validate request (balance, allowance, deadline), staticCall, then recheck nonce, estimate gas with buffer, call executePayment, wait for receipt, return result.
134-
6. Validate request: check deadline against chain time, check sender FXRP balance and allowance, check fee meets minimum.
135-
7. Get nonce for an address from the forwarder contract.
136-
8. Get the minimum relayer fee from the forwarder contract.
137-
9. Get FXRP token decimals from the forwarder and token contract.
138-
10. Get the relayer FLR balance for gas.
139-
11. Start Express server.
140-
12. Main function to read environment variables, create a relayer, log FLR balance, and start the server.
141-
13. Run the main function to start the relayer service.
148+
2. Define EIP-712 domain and payment request types (from, to, amount, nonce, deadline); define NETWORKS (flare, coston2, songbird).
149+
3. Define RelayerConfig, PaymentRequest, and ExecuteResult types.
150+
4. Implement GaslessRelayer class: constructor sets provider, wallet, and forwarder.
151+
5. Execute payment: normalize and validate request, recover signer with EIP-712, validate request (balance, allowance, deadline), staticCall, recheck nonce, estimate gas, call executePayment, wait for receipt, return result.
152+
6. Validate request: check deadline against chain time, check sender balance and allowance.
153+
7. Get nonce for an address from the forwarder.
154+
8. Get FXRP token decimals from the forwarder and token contract.
155+
9. Get the relayer FLR balance for gas.
156+
10. Start Express server: GET /nonce/:addr, POST /execute.
157+
11. Main: read env (RELAYER_PRIVATE_KEY, FORWARDER_ADDRESS, RPC_URL, PORT), create relayer, log FLR balance, start server.
158+
12. Run main to start the relayer service.
142159

143160
</details>
144161

@@ -155,15 +172,12 @@ Save as `scripts/deploy.ts` to deploy the [`GaslessPaymentForwarder`](/fxrp/toke
155172

156173
**Code Breakdown**
157174

158-
1. Import necessary libraries.
159-
2. Define default relayer fee constant in FXRP base units.
160-
3. Get deployer signer and balance; log address and FLR balance.
161-
4. Deploy `GaslessPaymentForwarder` contract with the relayer fee (FXRP comes from the Flare Contract Registry) and wait for deployment, and get the contract address.
162-
5. Get FXRP token address and decimals from the forwarder.
163-
Format fee for display.
164-
Log deployment summary (network, contract, FXRP, owner, fee).
165-
6. Verify contract on block explorer.
166-
7. Run main function.
175+
1. Import Hardhat and TypeChain types.
176+
2. Get deployer signer and balance; log address and FLR balance.
177+
3. Deploy GaslessPaymentForwarder (no constructor args; FXRP from Flare Contract Registry); wait for deployment and get address.
178+
4. Get FXRP token address from the forwarder; log deployment summary (network, contract, FXRP, owner).
179+
5. Verify contract on block explorer (constructor arguments empty).
180+
6. Run main: exit 0 on success, exit 1 on error.
167181

168182
</details>
169183

@@ -181,8 +195,8 @@ Use `scripts/example-usage.ts` to run the example flow.
181195
**Code Breakdown**
182196

183197
1. Import the necessary libraries.
184-
2. Read configuration from environment.
185-
3. Validate the configuration.
198+
2. Read configuration from the environment like the RPC URL, relayer URL, forwarder address, and user private key.
199+
3. Validate the configuration to ensure that the forwarder address and user private key are set.
186200
4. Create a provider and a wallet.
187201
5. Check user FXRP balance and allowance.
188202
6. Approve the forwarder to spend FXRP.
@@ -199,7 +213,7 @@ Create a `.env` file in your project root with the following variables:
199213
| Variable | Description |
200214
| --------------------- | ----------------------------------------------- |
201215
| `PRIVATE_KEY` | Deployer private key (for deployment) |
202-
| `RELAYER_PRIVATE_KEY` | Relayer wallet (pays gas, receives FXRP fees) |
216+
| `RELAYER_PRIVATE_KEY` | Relayer wallet (pays gas) |
203217
| `USER_PRIVATE_KEY` | User wallet for testing |
204218
| `FORWARDER_ADDRESS` | Deployed contract address (set after deploy) |
205219
| `RPC_URL` | Flare network RPC (e.g. Coston2 testnet) |
@@ -241,7 +255,6 @@ npx ts-node relayer/index.ts
241255
| ------ | ----------------- | ---------------------- |
242256
| POST | `/execute` | Execute single payment |
243257
| GET | `/nonce/:address` | Get nonce for address |
244-
| GET | `/fee` | Get the relayer fee |
245258

246259
### Run the example Flow
247260

@@ -283,13 +296,11 @@ Step 2: Approving FXRP for gasless payments...
283296
Step 3: Creating payment request...
284297
To: 0x5EEeaD99dFfeB32Fe96baad6554f6E009c8B348a
285298
Amount: 0.1 FXRP
286-
Fee: 0.01 FXRP
287299

288300
Signed request created:
289301
From: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2
290302
To: 0x5EEeaD99dFfeB32Fe96baad6554f6E009c8B348a
291303
Amount: 0.1 FXRP
292-
Fee: 0.01 FXRP
293304
Deadline: 2026-02-13T14:14:20.000Z
294305
Signature: 0x0cc5532b3c67eb0ef4...
295306

docusaurus.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const config: Config = {
2020
onBrokenLinks: "throw",
2121

2222
markdown: {
23+
mermaid: true,
2324
hooks: {
2425
onBrokenMarkdownImages: "throw",
2526
onBrokenMarkdownLinks: "throw",
@@ -232,6 +233,7 @@ const config: Config = {
232233
},
233234
} satisfies Preset.ThemeConfig,
234235
themes: [
236+
"@docusaurus/theme-mermaid",
235237
[
236238
require.resolve("@easyops-cn/docusaurus-search-local"),
237239
{

examples/developer-hub-javascript/fxrp-gasless/deploy.ts

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,13 @@
66

77
// 1. Import the necessary libraries
88
import hre from "hardhat";
9-
import { erc20Abi } from "viem";
109
import type { GaslessPaymentForwarder } from "../typechain-types/contracts/GaslessPaymentForwarder";
11-
import type { IERC20Metadata } from "../typechain-types/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata";
12-
13-
// 2. Define the default relayer fee (FXRP token base units)
14-
const DEFAULT_RELAYER_FEE = 10000n;
1510

1611
async function main(): Promise<string> {
1712
const network = hre.network.name;
1813
console.log(`\nDeploying GaslessPaymentForwarder to ${network}...`);
1914

20-
// 3. Get deployer account and balance
15+
// 2. Get deployer account and balance
2116
const [deployer] = await hre.ethers.getSigners();
2217
console.log(`Deployer address: ${deployer.address}`);
2318

@@ -28,48 +23,33 @@ async function main(): Promise<string> {
2823
console.log(
2924
` ContractRegistry.getAssetManagerFXRP() -> AssetManager.fAsset()`,
3025
);
31-
console.log(`Relayer fee: ${DEFAULT_RELAYER_FEE} (base units)`);
3226

33-
// 4. Deploy the GaslessPaymentForwarder contract (FXRP fetched from registry)
27+
// 3. Deploy the GaslessPaymentForwarder contract (FXRP fetched from registry)
3428
const GaslessPaymentForwarder = await hre.ethers.getContractFactory(
3529
"GaslessPaymentForwarder",
3630
);
37-
const forwarder = (await GaslessPaymentForwarder.deploy(
38-
DEFAULT_RELAYER_FEE,
39-
)) as unknown as GaslessPaymentForwarder;
31+
const forwarder =
32+
(await GaslessPaymentForwarder.deploy()) as unknown as GaslessPaymentForwarder;
4033

4134
await forwarder.waitForDeployment();
4235
const forwarderAddress = await forwarder.getAddress();
4336

44-
// 5. Get FXRP token address from forwarder (for display)
37+
// 4. Get FXRP token address from forwarder (for display)
4538
const fxrpAddress = await forwarder.fxrp();
46-
const fxrp = new hre.ethers.Contract(
47-
fxrpAddress,
48-
erc20Abi as unknown as string[],
49-
hre.ethers.provider,
50-
) as unknown as IERC20Metadata;
51-
const decimals = await fxrp.decimals();
52-
const relayerFeeFormatted = hre.ethers.formatUnits(
53-
DEFAULT_RELAYER_FEE,
54-
decimals,
55-
);
5639

5740
console.log(`\nGaslessPaymentForwarder deployed to: ${forwarderAddress}`);
5841
console.log("\n--- Deployment Summary ---");
5942
console.log(`Network: ${network}`);
6043
console.log(`Contract: ${forwarderAddress}`);
6144
console.log(`FXRP Token: ${fxrpAddress}`);
6245
console.log(`Owner: ${deployer.address}`);
63-
console.log(
64-
`Relayer Fee: ${relayerFeeFormatted} FXRP (${DEFAULT_RELAYER_FEE} base units)`,
65-
);
6646

67-
// 6. Verify contract on block explorer
47+
// 5. Verify contract on block explorer
6848
console.log("\nVerifying contract...");
6949
try {
7050
await hre.run("verify:verify", {
7151
address: forwarderAddress,
72-
constructorArguments: [DEFAULT_RELAYER_FEE.toString()],
52+
constructorArguments: [],
7353
});
7454
console.log("Contract verified successfully.");
7555
} catch (err) {
@@ -79,15 +59,15 @@ async function main(): Promise<string> {
7959
} else {
8060
console.warn("Verification failed:", msg);
8161
console.log(
82-
`\nTo verify manually: npx hardhat verify --network ${network} ${forwarderAddress} "${DEFAULT_RELAYER_FEE}"`,
62+
`\nTo verify manually: npx hardhat verify --network ${network} ${forwarderAddress}`,
8363
);
8464
}
8565
}
8666

8767
return forwarderAddress;
8868
}
8969

90-
// 7. Main entry point for the deployment script
70+
// 6. Main entry point for the deployment script
9171
main()
9272
.then(() => process.exit(0))
9373
.catch((error) => {

examples/developer-hub-javascript/fxrp-gasless/example-usage.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,26 +88,22 @@ async function main(): Promise<void> {
8888
process.env.RECIPIENT_ADDRESS ||
8989
"0x0000000000000000000000000000000000000001";
9090
const amountFXRP = "0.1"; // 0.1 FXRP
91-
const feeFXRP = "0.01"; // 0.01 FXRP relayer fee
9291

9392
console.log(`\nStep 3: Creating payment request...`);
9493
console.log(` To: ${recipientAddress}`);
9594
console.log(` Amount: ${amountFXRP} FXRP`);
96-
console.log(` Fee: ${feeFXRP} FXRP`);
9795

9896
const paymentRequest = await createPaymentRequest(
9997
wallet,
10098
FORWARDER_ADDRESS,
10199
recipientAddress,
102100
amountFXRP,
103-
feeFXRP,
104101
);
105102

106103
console.log(`\n Signed request created:`);
107104
console.log(` From: ${paymentRequest.from}`);
108105
console.log(` To: ${paymentRequest.to}`);
109106
console.log(` Amount: ${paymentRequest.meta.amountFormatted}`);
110-
console.log(` Fee: ${paymentRequest.meta.feeFormatted}`);
111107
console.log(
112108
` Deadline: ${new Date(paymentRequest.deadline * 1000).toISOString()}`,
113109
);

0 commit comments

Comments
 (0)