Skip to content

Commit 09bea9b

Browse files
committed
feat(docs): add guides for minting with executor
1 parent 4f0957b commit 09bea9b

File tree

4 files changed

+315
-2
lines changed

4 files changed

+315
-2
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
title: Mint FAssets using the Executor
3+
tags: [intermediate, fassets]
4+
slug: fassets-mint-executor
5+
description: Learn how to mint FAssets using the executor
6+
keywords: [fassets, flare-network]
7+
sidebar_position: 6
8+
---
9+
10+
import CodeBlock from "@theme/CodeBlock";
11+
import Remix from "@site/src/components/remix";
12+
import FAssetsReserveCollateralWithExecutor from "!!raw-loader!/examples/developer-hub-javascript/fassetsReserveCollateralWithExecutor.ts";
13+
import FAssetsCreateXrpPayment from "!!raw-loader!/examples/developer-hub-javascript/fassetsCreateXrpPayment.ts";
14+
import FAssetsExecuteMinting from "!!raw-loader!/examples/developer-hub-javascript/fassetsExecuteMinting.ts";
15+
16+
## Overview
17+
18+
This guide walks you through the complete process of minting FAssets (e.g., FXRP) on the Flare network using the [executor](/fassets/minting#executor-role).
19+
20+
Minting FAssets is the process of wrapping, for instance, XRP from the XRP Ledger into an FAsset, enabling it to be used within the Flare blockchain ecosystem.
21+
22+
The minting process can be executed by an external actor that monitors pending requests.
23+
Executors are incentivized but have no special permissions.
24+
If they fail to execute in time, the request expires, and minting must be restarted.
25+
26+
See the [Minting](/fassets/minting) overview for more details.
27+
28+
## Prerequisites
29+
30+
- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit)
31+
- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts)
32+
33+
## Minting Process Steps
34+
35+
The minting process is a multi-step process that involves the following steps:
36+
37+
1. Reserve collateral from a suitable agent and nominate an executor.
38+
2. Send the underlying asset (e.g., XRP) to the agent.
39+
3. Use [Flare Data Connector (FDC)](/fdc/overview) to generate proof of payment.
40+
4. Execute minting with the proof to receive FAssets.
41+
42+
## Reserve Collateral
43+
44+
The following code demonstrates how to reserve collateral by calling the [`reserveCollateral()`](/fassets/reference/IAssetManager#reservecollateral) function on the AssetManager contract.
45+
46+
<CodeBlock language="typescript" title="scripts/fassets/reserveCollateral.ts">
47+
{FAssetsReserveCollateralWithExecutor}
48+
</CodeBlock>
49+
50+
### Collateral Reservation Script Breakdown
51+
52+
1. Define constants
53+
54+
- `ASSET_MANAGER_ADDRESS`: FXRP AssetManager address on Songbird Testnet (Coston).
55+
- `LOTS_TO_MINT`: Number of FAssets lots to reserve.
56+
- `UNDERLYING_ADDRESS`: Target XRP Ledger address for the minted asset.
57+
- `EXECUTOR_ADDRESS`: Executor address that will execute the minting process and provide the proof of underlying asset payment.
58+
59+
2. Retrieve and filter agents with enough free collateral and select the agent with the lowest fee and normal status.
60+
3. Parse [`CollateralReserved`](/fassets/reference/IAssetManagerEvents#collateralreserved) event.
61+
4. Start the minting reservation process at the script's entry point.
62+
5. Call `findBestAgent()` with the required number of lots.
63+
6. Fetch agent metadata from [`getAgentInfo()`](/fassets/reference/IAssetManager#getagentinfo) to get the agent's `feeBIPS`, which is used to calculate the collateral reservation fee.
64+
7. Calculate the collateral reservation fee by calling [`collateralReservationFee()`](/fassets/reference/IAssetManager#collateralreservationfee).
65+
8. Set the executor fee the same as the collateral reservation fee to make this example simpler.
66+
9. Reserve collateral from agent by calling [`reserveCollateral()`](/fassets/reference/IAssetManager#reservecollateral) with the executor address.
67+
The executor fee is on top of the collateral reservation fee in native tokens.
68+
The fee is agreed between the minter and the executor.
69+
10. Call `assetMintingDecimals()` to determine the XRP token's decimal precision.
70+
11. Parse the [`CollateralReserved`](/fassets/reference/IAssetManagerEvents#collateralreserved) event to get the collateral reservation ID and other important collateral reservation details.
71+
12. Calculate the total XRP value required for payment.
72+
73+
## Send Payment on XRP Ledger
74+
75+
The next step is to send the XRP Ledger payment to the agent, and you can use this script to do that.
76+
77+
<CodeBlock language="typescript" title="scripts/fassets/xrpPayment.ts">
78+
{FAssetsCreateXrpPayment}
79+
</CodeBlock>
80+
81+
### XRP Payment Script Breakdown
82+
83+
1. Install the `xrpl` package — it's not included in the Flare Hardhat Starter Kit by default.
84+
2. Specify the correct constants from the reserve collateral script:
85+
- `AGENT_ADDRESS` - Agent's XRP Ledger address.
86+
- `AMOUNT_XRP` - XRP amount to send.
87+
- `PAYMENT_REFERENCE` - Payment reference from the the reserve collateral script.
88+
3. Create a client to connect to the XRP Ledger Testnet.
89+
4. Load the sender's wallet.
90+
5. Construct the payment transaction.
91+
6. Sign and submit the transaction.
92+
93+
## Generate Proof with Flare Data Connector
94+
95+
The executor will use the [Flare Data Connector Payment](/fdc/guides/hardhat/payment) to validate the XRP payment and generate a Merkle proof.
96+
97+
## Execute Minting
98+
99+
Once the XRP payment is validated, executor can retrieve the FDC proof from the [Data Availability Layer](/fdc/overview#data-availability-layer) and call the [`executeMinting()`](/fassets/reference/IAssetManager#executeminting) function on the AssetManager contract.
100+
Once the minting is executed, the executor will receive the executor fee in native tokens.
101+
If the executor fails to execute in time, the request expires, and minting must be restarted.
102+
103+
This script demonstrates how to retrieve the FDC proof and execute minting.
104+
105+
<CodeBlock language="typescript" title="scripts/fassets/executeMinting.ts">
106+
{FAssetsExecuteMinting}
107+
</CodeBlock>
108+
109+
### Execute Minting Script Breakdown
110+
111+
1. Get environment variables.
112+
2. Declare the constant `ASSET_MANAGER_ADDRESS` pointing to the FXRP AssetManager on the Songbird Testnet (Coston network).
113+
114+
3. Set the collateral reservation ID to the previously reserved minting request.
115+
4. Set the Flare Data Connector (FDC) round ID to retrieve the proof.
116+
5. Prepare the FDC request payload data.
117+
6. Create a function to get the proof from the FDC.
118+
It sends a POST request to the [Flare Data Availability Layer](/fdc/overview#data-availability-layer) and returns a Merkle proof and attestation response from FDC.
119+
7. Retrieve the FDC proof from the Data Availability Layer.
120+
8. Call the [`executeMinting()`](/fassets/reference/IAssetManager#executeminting) function on the AssetManager contract and send a transaction to the Flare network to convert the attested XRP payment into FXRP (minting).
121+
9. On a successful transaction call `parseExecutemintingEvents()` to extract and log events [`RedemptionTicketCreated`](/fassets/reference/IAssetManagerEvents#redemptionticketcreated) and [`MintingExecuted`](/fassets/reference/IAssetManagerEvents#mintingexecuted).
122+
123+
## Next Steps
124+
125+
Now that you have successfully minted FAssets using the executor, you can use them in Flare dApps or transfer them to other users or smart contracts within the Flare ecosystem.
126+
127+
:::tip Redeem
128+
To convert your FAssets (e.g., FXRP) back into the original asset (e.g., XRP), follow the steps in the [FAssets Redemption Guide](/fassets/developer-guides/fassets-redeem).
129+
:::

docs/fassets/developer-guides/6-fassets-redeem.mdx renamed to docs/fassets/developer-guides/7-fassets-redeem.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tags: [intermediate, fassets]
44
slug: fassets-redeem
55
description: Learn how to redeem FAssets
66
keywords: [fassets, flare-network]
7-
sidebar_position: 6
7+
sidebar_position: 7
88
---
99

1010
import CodeBlock from "@theme/CodeBlock";

docs/fassets/developer-guides/7-fassets-swap-redeem.mdx renamed to docs/fassets/developer-guides/8-fassets-swap-redeem.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tags: [intermediate, fassets]
44
slug: fassets-swap-redeem
55
description: Learn how to swap and redeem FAssets
66
keywords: [fassets, flare-network]
7-
sidebar_position: 7
7+
sidebar_position: 8
88
---
99

1010
import CodeBlock from "@theme/CodeBlock";
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { ethers } from "hardhat";
2+
3+
import {
4+
IAssetManagerInstance,
5+
IAssetManagerContract,
6+
} from "../../typechain-types";
7+
8+
// 1. Define constants
9+
10+
// AssetManager address on Flare Testnet Coston2 network
11+
const ASSET_MANAGER_ADDRESS = "0xDeD50DA9C3492Bee44560a4B35cFe0e778F41eC5";
12+
// Number of lots to reserve
13+
const LOTS_TO_MINT = 1;
14+
// XRP Ledger address
15+
const UNDERLYING_ADDRESS = "rSHYuiEvsYsKR8uUHhBTuGP5zjRcGt4nm";
16+
17+
// Executor address
18+
const EXECUTOR_ADDRESS = "0xb292348a4Cb9f5F008589B3596405FBba6986c55";
19+
20+
// 2. Function to find the best agent with enough free collateral lots
21+
22+
// Function from FAssets Bot repository
23+
// https://github.com/flare-foundation/fasset-bots/blob/main/packages/fasset-bots-core/src/commands/InfoBotCommands.ts#L83
24+
async function findBestAgent(
25+
assetManager: IAssetManagerInstance,
26+
minAvailableLots = 1,
27+
) {
28+
// get max 100 agents
29+
const agents = (await assetManager.getAvailableAgentsDetailedList(0, 100))
30+
._agents;
31+
32+
// filter agents with enough free collateral lots
33+
let agentsWithLots = agents.filter(
34+
(agent) => agent.freeCollateralLots > minAvailableLots,
35+
);
36+
37+
if (agentsWithLots.length === 0) {
38+
return undefined;
39+
}
40+
41+
// sort by lowest fee
42+
agentsWithLots.sort((a, b) => a.feeBIPS - b.feeBIPS);
43+
44+
while (agentsWithLots.length > 0) {
45+
const lowestFee = agentsWithLots[0].feeBIPS;
46+
47+
// get all agents with the lowest fee
48+
let optimal = agentsWithLots.filter((a) => a.feeBIPS == lowestFee);
49+
50+
while (optimal.length > 0) {
51+
// const agentVault = (requireNotNull(randomChoice(optimal)) as any).agentVault; // list must be nonempty
52+
53+
// get a random agent from the list
54+
const agentVault =
55+
optimal[Math.floor(Math.random() * optimal.length)].agentVault;
56+
// const agentVault = (randomChoice(optimal) as any).agentVault;
57+
58+
const info = await assetManager.getAgentInfo(agentVault);
59+
// 0 = NORMAL
60+
if (Number(info.status) === 0) {
61+
return agentVault;
62+
}
63+
// If not found remote this agent and do another round
64+
optimal = optimal.filter((a) => a.agentVault !== agentVault);
65+
agentsWithLots = agentsWithLots.filter(
66+
(a) => a.agentVault !== agentVault,
67+
);
68+
}
69+
}
70+
}
71+
72+
// 3. Function to parse the CollateralReserved event
73+
74+
async function parseCollateralReservedEvent(transactionReceipt) {
75+
console.log("\nParsing events...", transactionReceipt.rawLogs);
76+
77+
const assetManager = (await ethers.getContractAt(
78+
"IAssetManager",
79+
ASSET_MANAGER_ADDRESS,
80+
)) as IAssetManagerContract;
81+
82+
for (const log of transactionReceipt.rawLogs) {
83+
try {
84+
const parsedLog = assetManager.interface.parseLog({
85+
topics: log.topics,
86+
data: log.data,
87+
});
88+
89+
if (!parsedLog) continue;
90+
91+
const collateralReservedEvents = ["CollateralReserved"];
92+
if (!collateralReservedEvents.includes(parsedLog.name)) continue;
93+
94+
console.log(`\nEvent: ${parsedLog.name}`);
95+
console.log("Arguments:", parsedLog.args);
96+
const collateralReservedEvent = parsedLog.args;
97+
98+
return collateralReservedEvent;
99+
} catch (e) {
100+
console.log("Error parsing event:", e);
101+
}
102+
}
103+
}
104+
105+
// 4. Main function
106+
107+
async function main() {
108+
// Initialize the FAssets FXRP AssetManager contract
109+
const AssetManager = artifacts.require("IAssetManager");
110+
const assetManager: IAssetManagerInstance = await AssetManager.at(
111+
ASSET_MANAGER_ADDRESS,
112+
);
113+
114+
// 5. Find the best agent with enough free collateral lots
115+
const agentVaultAddress = await findBestAgent(assetManager, LOTS_TO_MINT);
116+
if (!agentVaultAddress) {
117+
throw new Error("No suitable agent found with enough free collateral lots");
118+
}
119+
console.log(agentVaultAddress);
120+
121+
// 6. Get the agent info
122+
const agentInfo = await assetManager.getAgentInfo(agentVaultAddress);
123+
console.log("Agent info:", agentInfo);
124+
125+
// 7. Get the collateral reservation fee according to the number of lots to reserve
126+
// https://dev.flare.network/fassets/minting/#collateral-reservation-fee
127+
const collateralReservationFee =
128+
await assetManager.collateralReservationFee(LOTS_TO_MINT);
129+
console.log(
130+
"Collateral reservation fee:",
131+
collateralReservationFee.toString(),
132+
);
133+
134+
const IAssetManager = artifacts.require("IAssetManager");
135+
const iAssetManager: IAssetManagerInstance = await IAssetManager.at(
136+
ASSET_MANAGER_ADDRESS,
137+
);
138+
139+
// 8. To make this example simpler we're using the same fee for the executor and the agent
140+
const executorFee = collateralReservationFee;
141+
const totalFee = collateralReservationFee.add(executorFee);
142+
143+
console.log("Total reservation fee:", totalFee.toString());
144+
145+
// 9. Reserve collateral
146+
// https://dev.flare.network/fassets/reference/IAssetManager#reservecollateral
147+
const tx = await iAssetManager.reserveCollateral(
148+
agentVaultAddress,
149+
LOTS_TO_MINT,
150+
agentInfo.feeBIPS,
151+
// Not using the executor
152+
EXECUTOR_ADDRESS,
153+
[UNDERLYING_ADDRESS],
154+
// Sending the collateral reservation fee as native tokens
155+
{ value: totalFee },
156+
);
157+
158+
console.log("Collateral reservation successful:", tx);
159+
160+
// 10. Get the asset decimals
161+
const decimals = await assetManager.assetMintingDecimals();
162+
163+
// 11. Parse the CollateralReserved event
164+
const collateralReservedEvent = await parseCollateralReservedEvent(
165+
tx.receipt,
166+
);
167+
168+
const collateralReservationInfo =
169+
await assetManager.collateralReservationInfo(
170+
collateralReservedEvent.collateralReservationId,
171+
);
172+
console.log("Collateral reservation info:", collateralReservationInfo);
173+
174+
// 11. Calculate the total amount of XRP to pay
175+
const totalUBA =
176+
collateralReservedEvent.valueUBA + collateralReservedEvent.feeUBA;
177+
const totalXRP = Number(totalUBA) / 10 ** decimals;
178+
console.log(`You need to pay ${totalXRP} XRP`);
179+
}
180+
181+
main().catch((error) => {
182+
console.error(error);
183+
process.exitCode = 1;
184+
});

0 commit comments

Comments
 (0)