Skip to main content

Redeem FAssets

Overview

In this guide, you will learn how to redeem FAssets using the Asset Manager smart contract. Redemption is the process of burning FAssets (e.g., FXRP) on Flare in exchange for their equivalent value on the original chain (e.g., XRP on XRPL).

See the Redemption overview for more details.

Prerequisites

Redeem FAssets Smart Contract

The following example demonstrates how to redeem FAssets using the AssetManager smart contract.

contracts/FAssetsRedeem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAssetManager} from "@flarenetwork/flare-periphery-contracts/coston/IAssetManager.sol";
import {AssetManagerSettings} from "@flarenetwork/flare-periphery-contracts/coston/userInterfaces/data/AssetManagerSettings.sol";

contract FAssetsRedeem {
// 1. Use the AssetManager contract
IAssetManager public immutable assetManager;

constructor(address _assetManager) {
assetManager = IAssetManager(_assetManager);
}

// 2. Get the AssetManager settings
function getSettings()
public
view
returns (uint256 lotSizeAMG, uint256 assetDecimals)
{
AssetManagerSettings.Data memory settings = assetManager.getSettings();
lotSizeAMG = settings.lotSizeAMG;
assetDecimals = settings.assetDecimals;

return (lotSizeAMG, assetDecimals);
}

// 3. Redeem FAssets
function redeem(
uint256 _lots,
string memory _redeemerUnderlyingAddressString
) public returns (uint256) {
uint256 redeemedAmountUBA = assetManager.redeem(
_lots,
_redeemerUnderlyingAddressString,
payable(address(0))
);

return redeemedAmountUBA;
}
}

Code Breakdown

  1. Define the asset manager contract address to interact with it.
  2. Get the asset manager settings to calculate the redeemed amount; for that, you need to get the lotSizeAMG and assetDecimals from the asset manager settings.
  3. Redeem the FAssets using the redeem function by specifying the number of lots to redeem and the underlying chain address.
info

In this example, you are not using the executor vault address, but you can use it to redeem FAssets on behalf of another address.

Deploy and Interact with the Smart Contract

To deploy the contract and redeem FAssets, you can use the Flare Hardhat Starter Kit.

Create a new file, for example scripts/fassets/redeem.ts and add the following code:

Import the Required Contracts

At first, you need to import the required dependencies and smart contract TypeScript types:

import { ethers, run } from "hardhat";
import { formatUnits } from "ethers";

import {
FAssetsRedeemInstance,
IAssetManagerContract,
ERC20Instance,
} from "../../typechain-types";

Define the Constants

Define the constants:

  • ASSET_MANAGER_ADDRESS - asset manager address;
  • LOTS_TO_REDEEM - the number of lots to redeem;
  • UNDERLYING_ADDRESS - underlying chain address where the redeemed asset will be sent;
  • FXRP_TOKEN_ADDRESS - FXRP token address.
// AssetManager address on Songbird Testnet Coston network
const ASSET_MANAGER_ADDRESS = "0x56728e46908fB6FcC5BCD2cc0c0F9BB91C3e4D34";
const LOTS_TO_REDEEM = 1;
const UNDERLYING_ADDRESS = "rSHYuiEvsYsKR8uUHhBTuGP5zjRcGt4nm";
const FXRP_TOKEN_ADDRESS = "0x36be8f2e1CC3339Cf6702CEfA69626271C36E2fd";

Deploy and Verify the Redeem Contract

Deploy and verify the smart contract providing the asset manager address as a constructor argument:

async function deployAndVerifyContract() {
const FAssetsRedeem = artifacts.require("FAssetsRedeem");
const args = [ASSET_MANAGER_ADDRESS];
const fAssetsRedeem: FAssetsRedeemInstance = await FAssetsRedeem.new(...args);

const fAssetsRedeemAddress = await fAssetsRedeem.address;

try {
await run("verify:verify", {
address: fAssetsRedeemAddress,
constructorArguments: args,
});
} catch (e: any) {
console.log(e);
}

console.log("FAssetsRedeem deployed to:", fAssetsRedeemAddress);

return fAssetsRedeem;
}

Transfer FXRP to the Redeem Contract

To redeem FAssets, you must transfer a sufficient amount of FXRP to the FAssetsRedeem contract address after it is deployed, as it acts as the invoker during the redemption process.

async function transferFXRP(
fAssetsRedeemAddress: string,
amountToRedeem: number,
) {
// Get FXRP token contract
const fxrp = (await ethers.getContractAt(
"IERC20",
FXRP_TOKEN_ADDRESS,
)) as ERC20Instance;

// Transfer FXRP to the deployed contract
console.log("Transferring FXRP to contract...");
const transferTx = await fxrp.transfer(fAssetsRedeemAddress, amountToRedeem);
await transferTx.wait();
console.log("FXRP transfer completed");
}
warning

In a production environment, you should use a more secure method to transfer FXRP to a smart contract.

Parse the Redemption Events

During the redemption process, the AssetManager emits an events:

  • RedemptionRequested - holds the agent vault address, redemption request id, the amount of FAssets to redeem, and other important information.
  • RedemptionTicketCreated - holds the redemption ticket information that is updated during the redemption process.

To parse the redemption events, you can use the following function:

async function parseRedemptionEvents(
transactionReceipt: any,
fAssetsRedeem: FAssetsRedeemInstance,
) {
console.log("\nParsing events...", transactionReceipt.rawLogs);

// Get AssetManager contract interface
const assetManager = (await ethers.getContractAt(
"IAssetManager",
ASSET_MANAGER_ADDRESS,
)) as IAssetManagerContract;

for (const log of transactionReceipt.rawLogs) {
try {
// Try to parse the log using the AssetManager interface
const parsedLog = assetManager.interface.parseLog({
topics: log.topics,
data: log.data,
});

if (parsedLog) {
const redemptionEvents = [
"RedemptionRequested",
"RedemptionTicketUpdated",
];
if (redemptionEvents.includes(parsedLog.name)) {
console.log(`\nEvent: ${parsedLog.name}`);
console.log("Arguments:", parsedLog.args);
}
}
} catch (e) {
console.log("Error parsing event:", e);
}
}
}

Redeeming FAssets

To put it all together you can use the following function to deploy the contract, transfer FXRP to it, redeem the FAssets, and parse the redemption events:

async function main() {
// Deploy and verify the contract
const fAssetsRedeem: FAssetsRedeemInstance = await deployAndVerifyContract();

// Get the lot size and decimals to calculate the amount to redeem
const settings = await fAssetsRedeem.getSettings();
const lotSize = settings[0];
const decimals = settings[1];
console.log("Lot size:", lotSize.toString());
console.log("Asset decimals:", decimals.toString());

// Calculate the amount to redeem according to the lot size and the number of lots to redeem
const amountToRedeem = Number(lotSize) * Number(LOTS_TO_REDEEM);
console.log(
`Required FXRP amount ${formatUnits(amountToRedeem, Number(decimals))} FXRP`,
);
console.log(`Required amount in base units: ${amountToRedeem.toString()}`);

// Transfer FXRP to the contract
await transferFXRP(fAssetsRedeem.address, amountToRedeem);

// Call redeem function and wait for transaction
const tx = await fAssetsRedeem.redeem(LOTS_TO_REDEEM, UNDERLYING_ADDRESS);
console.log("TX receipt", tx.receipt);

// Parse events from the transaction
await parseRedemptionEvents(tx.receipt, fAssetsRedeem);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Run the Script

To run the script, use the Flare Hardhat Starter Kit with the following command:

npx hardhat run scripts/fassets/getLotSize.ts --network coston

Once the script is executed, two events hold the important information:

RedemptionRequested

The RedemptionRequested event contains the agent vault address, redeemer address, underlying chain address, amount of FAssets to redeem, redemption fee, and other important information.

Event: RedemptionRequested
Arguments: Result(12) [
'0x3c831Fe4417bEFFAc721d24996985eE2dd627053',
'0xCA7C9fBbA56E44C508bcb4872775c5fEd169cDb3',
1898730n,
'rSHYuiEvsYsKR8uUHhBTuGP5zjRcGt4nm',
20000000n,
20000n,
6386165n,
6386708n,
1744293249n,
'0x46425052664100020000000000000000000000000000000000000000001cf8ea',
'0x0000000000000000000000000000000000000000',
0n
]

When decoding an event, the most important data from the event is:

  • agent vault address that will redeem the FAssets is 0x3c831Fe4417bEFFAc721d24996985eE2dd627053;
  • redeemer address is 0xCA7C9fBbA56E44C508bcb4872775c5fEd169cDb3;
  • redemption ticket id is 1898730;
  • underlying chain address is rSHYuiEvsYsKR8uUHhBTuGP5zjRcGt4nm;
  • amount of FAssets to redeem is 20000000;
  • redemption fee is 20000;

You can see the full event description here section.

RedemptionTicketUpdated

The event RedemptionTicketUpdated holds the redemption ticket information like agent vault address, redemption ticket ID and the value of the redemption ticket in underlying chain currency.

For every minting, a redemption ticket is created, and during the redemption process, the redemption ticket is updated with the new redemption status.

Event: RedemptionTicketUpdated
Arguments: Result(3) [
'0x3c831Fe4417bEFFAc721d24996985eE2dd627053',
870n,
3440000000n
]

Once decoding the most important data from the event is:

  • agent vault address that will redeem the FAssets is 0x3c831Fe4417bEFFAc721d24996985eE2dd627053;
  • redemption ticket id is 870;
  • value of the redemption ticket in underlying chain currency is 3440000000 (partially redeemed).

You can read the full event description here.

Next Steps

tip

This is only the first step of the redemption process. The redeemer or agent completes the redemption process when proof of payment is presented to the FAssets system. Read more about the redemption process in the here.