From 816826a079cdc66b1b6fd39250fc60ca5d44dff1 Mon Sep 17 00:00:00 2001 From: Kristaps Grinbergs Date: Tue, 27 Jan 2026 15:41:28 +0200 Subject: [PATCH 1/4] feat(docs): introduce Upshift vaults documentation with deposit, redeem, and claim guides --- docs/fxrp/overview.mdx | 12 +- docs/fxrp/upshift/01-status.mdx | 108 ++++++++ docs/fxrp/upshift/02-deposit.mdx | 106 +++++++ docs/fxrp/upshift/03-instant-redeem.mdx | 113 ++++++++ docs/fxrp/upshift/04-request-redeem.mdx | 131 +++++++++ docs/fxrp/upshift/05-claim.mdx | 129 +++++++++ docs/fxrp/upshift/_upshift.mdx | 44 +++ docs/fxrp/upshift/index.mdx | 9 + docs/tags.yml | 2 + .../developer-hub-javascript/upshift-claim.ts | 260 ++++++++++++++++++ .../upshift-deposit.ts | 216 +++++++++++++++ .../upshift-instant-redeem.ts | 204 ++++++++++++++ .../upshift-request-redeem.ts | 246 +++++++++++++++++ .../upshift-status.ts | 153 +++++++++++ sidebars.ts | 15 + 15 files changed, 1739 insertions(+), 9 deletions(-) create mode 100644 docs/fxrp/upshift/01-status.mdx create mode 100644 docs/fxrp/upshift/02-deposit.mdx create mode 100644 docs/fxrp/upshift/03-instant-redeem.mdx create mode 100644 docs/fxrp/upshift/04-request-redeem.mdx create mode 100644 docs/fxrp/upshift/05-claim.mdx create mode 100644 docs/fxrp/upshift/_upshift.mdx create mode 100644 docs/fxrp/upshift/index.mdx create mode 100644 examples/developer-hub-javascript/upshift-claim.ts create mode 100644 examples/developer-hub-javascript/upshift-deposit.ts create mode 100644 examples/developer-hub-javascript/upshift-instant-redeem.ts create mode 100644 examples/developer-hub-javascript/upshift-request-redeem.ts create mode 100644 examples/developer-hub-javascript/upshift-status.ts diff --git a/docs/fxrp/overview.mdx b/docs/fxrp/overview.mdx index e76cc8b9..f97d083d 100644 --- a/docs/fxrp/overview.mdx +++ b/docs/fxrp/overview.mdx @@ -6,6 +6,7 @@ keywords: [fassets, fxrp, xrp, flare-network, firelight, vault, defi] import DocCardList from "@theme/DocCardList"; import Firelight from "./firelight/_firelight.mdx"; +import Upshift from "./upshift/_upshift.mdx"; FXRP is the [FAsset](/fassets/overview) representation of XRP on the Flare network. It is an [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token that represents XRP bridged from the XRP Ledger (XRPL) to Flare. @@ -121,13 +122,6 @@ FXRP is deployed as an [Omnichain Fungible Token (OFT)](/fxrp/oft) using LayerZe -## Upshift Vaults +## Upshift -[Upshift vault](https://app.upshift.finance/pools/14/0x373D7d201C8134D4a2f7b5c63560da217e3dEA28) is part of the XRP finance stack on Flare Network. -It inherits the transparency model of the [FAssets](/fassets/overview) system: minting and redemption are proof-backed, and vault interactions are recorded on-chain. - -- **Funds are on-chain:** User deposits, vault balances, withdrawal requests, and claims are executed as smart contract state changes on Flare. - You can verify all vault holdings on [DeBank](https://debank.com/profile/0xEDb7B1e92B8D3621b46843AD024949F10438374B) portfolio tracker. -- **User ownership is verifiable:** A user's position is represented directly on-chain and can be queried from smart contracts. -- **Instruction flow is auditable:** XRPL-triggered actions are executed on Flare via [Smart Accounts](/smart-accounts/overview) with [Flare Data Connector](/fdc/overview) backed verification and onchain execution. - You can verify all smart account activity on the [Flare Systems Explorer](https://flare-systems-explorer.flare.network/smart-accounts). + diff --git a/docs/fxrp/upshift/01-status.mdx b/docs/fxrp/upshift/01-status.mdx new file mode 100644 index 00000000..286accbe --- /dev/null +++ b/docs/fxrp/upshift/01-status.mdx @@ -0,0 +1,108 @@ +--- +title: Get Upshift Vault Status +tags: [intermediate, upshift, fassets] +slug: status +description: Learn how to get Upshift vault status +keywords: [fassets, flare-network, fxrp, upshift, vault] +sidebar_position: 1 +--- + +import CodeBlock from "@theme/CodeBlock"; +import UpshiftStatus from "!!raw-loader!/examples/developer-hub-javascript/upshift-status.ts"; + +This guide demonstrates how to retrieve information about an Upshift vault, including vault configuration, LP token info, user balances, and withdrawal epoch information. + +The Upshift vault is an [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style tokenized vault compatible with [FAssets](/fassets/overview). + +## Prerequisites + +- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) +- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) +- Understanding of [FAssets](/fassets/overview) + +## Upshift Vault Status Script + +The following script retrieves and displays information about the Upshift vault: + + + {UpshiftStatus} + + +## Script Breakdown + +The `main()` function executes the following steps: + +1. **Initialize:** Gets the user account and logs the vault and user addresses. +2. **Connect to vault:** Creates an instance of the Upshift vault contract. +3. **Get reference asset info:** Retrieves the vault's reference asset (FXRP) address, symbol, and decimals. +4. **Get LP token info:** Retrieves the vault's LP token address, which represents user shares. +5. **Get vault configuration:** Fetches key vault parameters. +6. **Get withdrawal epoch info:** Retrieves the current withdrawal epoch (year, month, day) and claimable epoch. +7. **Get user balances:** Shows the user's reference asset balance and LP token balance. +8. **Check allowances:** Displays the user's token allowances to the vault for both the reference asset and LP token. + +## Running the Script + +To run the Upshift vault status script: + +1. Ensure you have the [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) set up. +2. Update the `VAULT_ADDRESS` constant with the correct vault address for your network. +3. Run the script using Hardhat: + +```bash +npx hardhat run scripts/upshift/status.ts --network coston2 +``` + +## Output + +The script outputs the following information about the Upshift vault: + +```bash +VAULT STATUS + +Vault Address: 0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81 +User Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 + +Reference Asset +Address: 0x0b6A3645c240605887a5532109323A3E12273dc7 +Symbol: FTestXRP +Decimals: 6 + +LP Token +Address: 0x1234567890abcdef1234567890abcdef12345678 + +Vault Configuration +Withdrawals Paused: false +Lag Duration: 86400 seconds +Withdrawal Fee: 0.5% +Instant Redemption Fee: 1.0% +Max Withdrawal Amount: 1000000.0 FTestXRP + +Withdrawal Epoch +Year: 2025, Month: 1, Day: 15 +Claimable Epoch: 14 + +User Balances +FTestXRP Balance: 100.0 +LP Token Balance: 95.5 + +Allowances +FTestXRP Allowance to Vault: 0.0 +LP Token Allowance to Vault: 0.0 +``` + +## Summary + +In this guide, you learned how to retrieve status information from an Upshift vault, including vault configuration, LP token details, user balances, and withdrawal epoch information. + +:::tip[What's next] + +To continue your Upshift development journey, you can: + +- Learn more about [FAssets](/fassets/overview) and how the system works. +- Learn how to [deposit assets into an Upshift vault](/fxrp/upshift/deposit). +- Learn how to [instantly redeem from an Upshift vault](/fxrp/upshift/instant-redeem) for immediate liquidity. +- Learn how to [request a redemption from an Upshift vault](/fxrp/upshift/request-redeem) for delayed liquidity. +- Learn how to [claim assets from an Upshift vault](/fxrp/upshift/claim) after your requested redemption is claimable. + +::: diff --git a/docs/fxrp/upshift/02-deposit.mdx b/docs/fxrp/upshift/02-deposit.mdx new file mode 100644 index 00000000..9967b24c --- /dev/null +++ b/docs/fxrp/upshift/02-deposit.mdx @@ -0,0 +1,106 @@ +--- +title: Deposit Assets into Upshift Vault +tags: [intermediate, upshift, fassets] +slug: deposit +description: Learn how to deposit assets into an Upshift vault +keywords: [fassets, flare-network, fxrp, upshift, vault] +sidebar_position: 2 +--- + +import CodeBlock from "@theme/CodeBlock"; +import UpshiftDeposit from "!!raw-loader!/examples/developer-hub-javascript/upshift-deposit.ts"; + +This guide demonstrates how to deposit assets into an Upshift vault. +The Upshift vault implements an [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style tokenized vault, allowing users to deposit assets (e.g., FXRP) and receive LP tokens (vault shares) in return. + +Depositing assets is the process of transferring them to the vault and receiving LP tokens that represent your proportional ownership of the vault's assets. + +## Prerequisites + +- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) +- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) +- Understanding of [FAssets](/fassets/overview) +- Sufficient asset balance (e.g., FXRP) to deposit into the vault. + +## Upshift Vault Deposit Script + +The following script demonstrates how to deposit assets into the Upshift vault: + + + {UpshiftDeposit} + + +## Script Breakdown + +The `main()` function executes the following steps: + +1. **Initialize:** Gets the user account and logs the vault and user addresses. +2. **Connect to vault:** Creates an instance of the Upshift vault contract. +3. **Get reference asset info:** Retrieves the vault's reference asset (FXRP) address, symbol, and decimals. +4. **Calculate deposit amount:** Converts the desired amount into the correct units based on decimals. +5. **Check balance:** Verifies the user has sufficient balance to cover the deposit. +6. **Approve allowance:** Approves the vault to spend the deposit amount if needed. +7. **Preview deposit:** Calls [`previewDeposit()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-7.-previewdeposit-uint256-assets) to see the expected shares and amount in reference tokens. +8. **Get LP token info:** Records the LP token balance before the deposit. +9. **Execute deposit:** Calls [`deposit()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-1.-deposit-uint256-assets-address-receiver) to transfer assets and mint LP tokens. +10. **Verify deposit:** Confirms the deposit by checking the new LP token balance and shares received. + +## Running the Script + +To run the Upshift vault deposit script: + +1. Ensure you have the [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) set up. +2. Update the `VAULT_ADDRESS` constant with the correct vault address for your network. +3. Adjust the `DEPOSIT_AMOUNT` constant to the desired number of tokens. +4. Ensure your account has sufficient asset balance (e.g., FXRP) to cover the deposit. +5. Run the script using Hardhat: + +```bash +npx hardhat run scripts/upshift/deposit.ts --network coston2 +``` + +## Output + +The script outputs the following information about the deposit: + +```bash +DEPOSIT TO VAULT + +Vault Address: 0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81 +User Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 + +Reference Asset (asset depositing): 0x0b6A3645c240605887a5532109323A3E12273dc7 + +Deposit Amount: 1 FTestXRP (1000000) +Balance: 100.0 FTestXRP +Current Allowance: 0.0 FTestXRP + +Approving vault to spend 1 FTestXRP tokens +Approval Tx: 0x87a36c1009575719fd3adb9a1bb2e3062a601bf910fc7fac3248da71891c39a4 + +Expected Shares: 0.99 +Amount in Reference Tokens: 1.0 +LP Balance Before: 0.0 + +Deposit: (tx: 0x446b7a171859d676677fc870cff81c7e8c0d618fc3588e60665792da86b94c50 , block: 12345678 ) + +Verifying deposit... +LP Balance After: 0.99 +Shares Received: 0.99 +``` + +## Summary + +In this guide, you learned how to deposit assets into an Upshift vault by specifying the amount of assets to deposit and receiving LP tokens in return. + +:::tip[What's next] + +To continue your Upshift development journey, you can: + +- Learn more about [FAssets](/fassets/overview) and how the system works. +- Learn how to [get Upshift vault status](/fxrp/upshift/status) to monitor your position. +- Learn how to [instantly redeem from an Upshift vault](/fxrp/upshift/instant-redeem) for immediate liquidity. +- Learn how to [request a redemption from an Upshift vault](/fxrp/upshift/request-redeem) for delayed liquidity. +- Learn how to [claim assets from an Upshift vault](/fxrp/upshift/claim) after your requested redemption is claimable. + +::: diff --git a/docs/fxrp/upshift/03-instant-redeem.mdx b/docs/fxrp/upshift/03-instant-redeem.mdx new file mode 100644 index 00000000..0691b2aa --- /dev/null +++ b/docs/fxrp/upshift/03-instant-redeem.mdx @@ -0,0 +1,113 @@ +--- +title: Instant Redeem from Upshift Vault +tags: [intermediate, upshift, fassets] +slug: instant-redeem +description: Learn how to instantly redeem LP tokens from an Upshift vault +keywords: [fassets, flare-network, fxrp, upshift, vault, redeem] +sidebar_position: 3 +--- + +import CodeBlock from "@theme/CodeBlock"; +import UpshiftInstantRedeem from "!!raw-loader!/examples/developer-hub-javascript/upshift-instant-redeem.ts"; + +This guide demonstrates how to perform an instant redemption from an Upshift vault. +The Upshift vault implements an [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style tokenized vault that supports instant redemptions for immediate liquidity when available. + +Instant redemption burns your LP tokens (vault shares) and immediately returns the underlying assets to your wallet, subject to an instant redemption fee. + +## Prerequisites + +- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) +- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) +- Understanding of [FAssets](/fassets/overview) +- LP tokens (vault shares) in the Upshift vault to redeem. + +## Upshift Vault Instant Redeem Script + +The following script demonstrates how to perform an instant redemption from the Upshift vault: + + + {UpshiftInstantRedeem} + + +## Script Breakdown + +The `main()` function executes the following steps: + +1. **Initialize:** Gets the user account and logs the vault and user addresses. +2. **Connect to vault:** Creates an instance of the Upshift vault contract. +3. **Get reference asset info:** Retrieves the vault's reference asset (FXRP) address, symbol, and decimals. +4. **Get LP token info:** Retrieves the LP token address and the user's current LP balance. +5. **Validate balance:** Converts the shares amount and checks if the user has sufficient LP tokens. +6. **Preview redemption:** Calls [`previewRedemption()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-8.-previewredeem-uint256-shares) to see the expected assets before and after the instant redemption fee. +7. **Get asset balance:** Records the user's asset balance before redemption. +8. **Execute instant redemption:** Calls [`instantRedeem()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-4.-instantredeem-uint256-shares-address-receiveraddr-address-holderaddr) to burn LP tokens and receive assets immediately. +9. **Verify redemption:** Confirms the redemption by checking the updated LP and asset balances. + +## Understanding Instant Redemption + +The Upshift vault supports two types of redemptions: + +- **Instant Redemption**: Immediately burns LP tokens and returns assets, but incurs an instant redemption fee. +- **Requested Redemption**: Creates a withdrawal request that is processed after a lag period, with a lower fee. + +The instant redemption fee compensates the vault for providing immediate liquidity. +The [`previewRedemption()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-8.-previewredeem-uint256-shares) function shows both the gross and net amounts you'll receive. + +## Running the Script + +To run the Upshift vault instant redeem script: + +1. Ensure you have the [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) set up. +2. Update the `VAULT_ADDRESS` constant with the correct vault address for your network. +3. Adjust the `SHARES_TO_REDEEM` constant to the desired number of shares. +4. Ensure your account has sufficient LP tokens (vault shares) to cover the redemption. +5. Run the script using Hardhat: + +```bash +npx hardhat run scripts/upshift/instantRedeem.ts --network coston2 +``` + +## Output + +The script outputs the following information about the instant redemption: + +```bash +INSTANT REDEEM FROM VAULT + +Vault Address: 0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81 +User Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 +Reference Asset (asset receiving): 0x0b6A3645c240605887a5532109323A3E12273dc7 + +LP Balance: 10.0 +Shares to Redeem: 1 (1000000) + +Instant Redemption Fee: 1.0% +Expected Assets (before fee): 1.0 FTestXRP +Expected Assets (after fee): 0.99 FTestXRP + +Asset Balance Before: 50.0 FTestXRP + +Instant Redeem: (tx: 0x3f1bd8c766852d3b835bcde79f6d8e20afeeb227d737e0ed28d057dc0e6b2ba9 , block: 12345678 ) + +Verifying redemption... +LP Balance After: 9.0 +Shares Redeemed: 1.0 +Assets Received: 0.99 FTestXRP +``` + +## Summary + +In this guide, you learned how to perform an instant redemption from an Upshift vault by specifying the number of LP tokens (shares) to redeem. +The instant redemption provides immediate liquidity but incurs a fee. + +:::tip[What's next] + +To continue your Upshift development journey, you can: + +- Learn more about [FAssets](/fassets/overview) and how the system works. +- Learn how to [deposit assets into an Upshift vault](/fxrp/upshift/deposit). +- Learn how to [request a redemption from an Upshift vault](/fxrp/upshift/request-redeem) for delayed liquidity. +- Learn how to [claim assets from an Upshift vault](/fxrp/upshift/claim) after your requested redemption is claimable. + +::: diff --git a/docs/fxrp/upshift/04-request-redeem.mdx b/docs/fxrp/upshift/04-request-redeem.mdx new file mode 100644 index 00000000..fc5f44eb --- /dev/null +++ b/docs/fxrp/upshift/04-request-redeem.mdx @@ -0,0 +1,131 @@ +--- +title: Request Redeem from Upshift Vault +tags: [intermediate, upshift, fassets] +slug: request-redeem +description: Learn how to request a delayed redemption from an Upshift vault +keywords: [fassets, flare-network, fxrp, upshift, vault, redeem, withdrawal] +sidebar_position: 4 +--- + +import CodeBlock from "@theme/CodeBlock"; +import UpshiftRequestRedeem from "!!raw-loader!/examples/developer-hub-javascript/upshift-request-redeem.ts"; + +This guide demonstrates how to request a delayed redemption from an Upshift vault. +The Upshift vault implements an [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style tokenized vault that supports requested redemptions with a lower fee than instant redemptions. + +Requesting a redemption locks your LP tokens (vault shares) and creates a withdrawal request. +After the lag duration passes, you can claim your assets using the [claim redemption script](/fxrp/upshift/claim). + +## Prerequisites + +- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) +- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) +- Understanding of [FAssets](/fassets/overview) +- LP tokens (vault shares) in the Upshift vault to redeem. + +## Upshift Vault Request Redeem Script + +The following script demonstrates how to request a delayed redemption from the Upshift vault: + + + {UpshiftRequestRedeem} + + +## Script Breakdown + +The `main()` function executes the following steps: + +1. **Initialize:** Gets the user account and logs the vault and user addresses. +2. **Connect to vault:** Creates an instance of the Upshift vault contract. +3. **Get reference asset info:** Retrieves the vault's reference asset (FXRP) address, symbol, and decimals. +4. **Get LP token info:** Retrieves the LP token address and the user's current LP balance. +5. **Validate balance:** Converts the shares amount and checks if the user has sufficient LP tokens. +6. **Approve LP allowance:** Approves the vault to spend LP tokens if the current allowance is insufficient. +7. **Check vault configuration:** Verifies withdrawals are not paused and the amount doesn't exceed maximum limits. +8. **Preview redemption:** Calls [`previewRedemption()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-8.-previewredeem-uint256-shares) to see expected assets before and after the withdrawal fee. +9. **Print withdrawal epoch:** Displays the current epoch and claimable epoch information. +10. **Execute request redeem:** Calls [`requestRedeem()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-2.-requestredeem-uint256-shares-address-receiveraddr-address-holderaddr) to lock LP tokens and create a withdrawal request. +11. **Verify request:** Confirms the request by checking the updated LP balance and showing when assets can be claimed. + +## Understanding Requested Redemption + +The Upshift vault supports two types of redemptions: + +- **Instant Redemption**: Immediately burns LP tokens and returns assets, but incurs an instant redemption fee. +- **Requested Redemption**: Creates a withdrawal request that is processed after a lag period, with a lower fee. + +The requested redemption process works as follows: + +1. **Request**: Call [`requestRedeem()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-2.-requestredeem-uint256-shares-address-receiveraddr-address-holderaddr) to lock your LP tokens and create a withdrawal request for the current epoch. +2. **Wait**: The lag duration must pass before you can claim your assets. +3. **Claim**: After the lag period execute the [claim redemption script](/fxrp/upshift/claim) to receive your assets. + +This delayed mechanism allows the vault to manage liquidity more efficiently while offering users a lower fee option. + +## Running the Script + +To run the Upshift vault request redeem script: + +1. Ensure you have the [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) set up. +2. Update the `VAULT_ADDRESS` constant with the correct vault address for your network. +3. Adjust the `SHARES_TO_REDEEM` constant to the desired number of shares. +4. Ensure your account has sufficient LP tokens (vault shares) to cover the redemption. +5. Run the script using Hardhat: + +```bash +npx hardhat run scripts/upshift/requestRedeem.ts --network coston2 +``` + +## Output + +The script outputs the following information about the request redemption: + +```bash +REQUEST REDEEM FROM VAULT + +Vault Address: 0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81 +User Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 +Reference Asset (asset receiving): 0x0b6A3645c240605887a5532109323A3E12273dc7 + +LP Balance: 10.0 +Shares to Redeem: 1 (1000000) + +Current LP Allowance: 0.0 + +Approving vault to spend 1 LP tokens... +Approval Tx: 0x87a36c1009575719fd3adb9a1bb2e3062a601bf910fc7fac3248da71891c39a4 + +Lag Duration: 86400 seconds +Withdrawal Fee: 0.5% +Withdrawals Paused: false +Max Withdrawal Amount: 1000000.0 FTestXRP + +Expected Assets (before fee): 1.0 FTestXRP +Expected Assets (after fee): 0.995 FTestXRP + +Current Epoch - Year: 2025, Month: 1, Day: 15 +Claimable Epoch: 14 + +Request Redeem: (tx: 0x3f1bd8c766852d3b835bcde79f6d8e20afeeb227d737e0ed28d057dc0e6b2ba9 , block: 12345678 ) +LP Balance After: 9.0 +Shares Locked: 1.0 + +Claim your assets after: 2025/1/16 +``` + +## Summary + +In this guide, you learned how to request a delayed redemption from an Upshift vault by specifying the number of LP tokens (shares) to redeem. +The requested redemption has a lower fee than instant redemption but requires waiting for the lag duration before claiming your assets. + +:::tip[What's next] + +To continue your Upshift development journey, you can: + +- Learn how to [get Upshift vault status](/fxrp/upshift/status) to monitor your redemption requests. +- Learn how to [claim assets from an Upshift vault](/fxrp/upshift/claim) after your requested redemption is claimable. +- Learn how to [instantly redeem from an Upshift vault](/fxrp/upshift/instant-redeem) for immediate liquidity. +- Learn how to [deposit assets into an Upshift vault](/fxrp/upshift/deposit). +- Learn more about [FAssets](/fassets/overview) and how the system works. + +::: diff --git a/docs/fxrp/upshift/05-claim.mdx b/docs/fxrp/upshift/05-claim.mdx new file mode 100644 index 00000000..6afb6a28 --- /dev/null +++ b/docs/fxrp/upshift/05-claim.mdx @@ -0,0 +1,129 @@ +--- +title: Claim Redemption from Upshift Vault +tags: [intermediate, upshift, fassets] +slug: claim +description: Learn how to claim assets from a previously requested Upshift vault redemption +keywords: [fassets, flare-network, fxrp, upshift, vault, claim, redemption] +sidebar_position: 5 +--- + +import CodeBlock from "@theme/CodeBlock"; +import UpshiftClaim from "!!raw-loader!/examples/developer-hub-javascript/upshift-claim.ts"; + +This guide demonstrates how to claim assets from a previously requested redemption in an Upshift vault. +The Upshift vault implements an [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style tokenized vault that supports requested redemptions with a lower fee than instant redemptions. + +Claiming is the final step after you have [requested a redemption](/fxrp/upshift/request-redeem). +Once the lag duration has passed, you call [`claim()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-3.-claim-uint256-year-uint256-month-uint256-day-address-receiveraddr) to receive your underlying assets (e.g., FXRP) in your wallet. + +## Prerequisites + +- [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) +- [Flare Network Periphery Contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) +- Understanding of [FAssets](/fassets/overview) +- A pending redemption request from a previous [request redeem](/fxrp/upshift/request-redeem) call. + +## Upshift Vault Claim Script + +The following script demonstrates how to claim assets from the Upshift vault: + + + {UpshiftClaim} + + +## Script Breakdown + +The `main()` function executes the following steps: + +1. **Initialize:** Gets the user account and logs the vault, user, receiver addresses, and redemption date. +2. **Connect to vault:** Creates an instance of the Upshift vault contract. +3. **Get reference asset info:** Retrieves the vault's reference asset (FXRP) address, symbol, and decimals. +4. **Check pending redemption:** Verifies that there are shares to claim for the given date and receiver. +5. **Get LP token info:** Retrieves the LP token address, decimals, and symbol. +6. **Preview redemption:** Calls [`previewRedemption()`](https://docs.upshift.finance/developer-docs/vault-contract-interface#id-8.-previewredeem-uint256-shares) to see expected assets before and after the withdrawal fee. +7. **Check if claimable:** Ensures the current block timestamp is past the claimable date (plus a small buffer) before proceeding. +8. **Get balance before:** Records the receiver's asset balance before the claim. +9. **Execute claim:** Calls [`claim()`](https://docs.upshift.finance/developer-docs/vault-contract-interface?q=getBurnableAmountByReceiver#id-3.-claim-uint256-year-uint256-month-uint256-day-address-receiveraddr) with the redemption date and receiver to transfer assets. +10. **Verify claim:** Confirms the claim by comparing the receiver's new asset balance to the expected amount. + +## Understanding the Claim Process + +The claim step completes the requested redemption flow: + +1. **Request:** You previously called [`requestRedeem()`](/fxrp/upshift/request-redeem) to lock LP tokens and create a withdrawal request for a specific epoch. +2. **Wait:** The lag duration must pass before assets can be claimed. +3. **Claim:** Once the claimable date has been reached, call `claim(year, month, day, receiver)` to receive your assets. + +The script uses the **redemption date** (year, month, day) that was output when you ran the request redeem script. +You must set `YEAR`, `MONTH`, and `DAY` in the script to match that date. + +## Running the Script + +To run the Upshift vault claim script: + +1. Ensure you have the [Flare Hardhat Starter Kit](/network/guides/hardhat-foundry-starter-kit) set up. +2. Update the `VAULT_ADDRESS` constant with the correct vault address for your network. +3. Set `YEAR`, `MONTH`, and `DAY` to the claimable date from your [request redeem](/fxrp/upshift/request-redeem) output. +4. Optionally set `RECEIVER_ADDRESS` to claim to a different address (leave empty to use the signer). +5. Ensure the claimable date has passed (the script checks and exits with a message if not). +6. Run the script using Hardhat: + +```bash +npx hardhat run scripts/upshift/claim.ts --network coston2 +``` + +## Output + +The script outputs the following information about the claim: + +```bash +CLAIM REDEMPTION + +Vault Address: 0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81 +User Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 +Receiver Address: 0x0d09ff7630588E05E2449aBD3dDD1D8d146bc5c2 +Redemption Date: 2026-01-23 + +1. Checking pending redemption... + +2. Previewing redemption... +Assets (before fee): 1.0 FTestXRP +Assets (after fee): 0.995 FTestXRP +Fee: 0.005 FTestXRP + +Shares to claim: 1.0 +LP Token Address: 0x... + +3. Checking if claimable... +Current Timestamp: 1737648000 +Claimable Epoch: 1737586800 +Ready to claim! + +4. Checking receiver balance before... +Balance: 50.0 FTestXRP + +5. Claiming... +Claim: (tx: 0x3f1bd8c766852d3b835bcde79f6d8e20afeeb227d737e0ed28d057dc0e6b2ba9 , block: 12345678 ) + +6. Verifying claim... +Balance After: 50.995 FTestXRP +Received: 0.995 FTestXRP +Claim successful! +``` + +## Summary + +In this guide, you learned how to claim assets from an Upshift vault after a requested redemption. +You use the redemption date (year, month, day) from your request redeem output, and once the lag period has passed, the claim script transfers the underlying assets to your wallet. + +:::tip[What's next] + +To continue your Upshift development journey, you can: + +- Learn how to [get Upshift vault status](/fxrp/upshift/status) to monitor your redemption requests. +- Learn how to [request a redemption](/fxrp/upshift/request-redeem) from an Upshift vault. +- Learn how to [instantly redeem](/fxrp/upshift/instant-redeem) from an Upshift vault for immediate liquidity. +- Learn how to [deposit assets](/fxrp/upshift/deposit) into an Upshift vault. +- Learn more about [FAssets](/fassets/overview) and how the system works. + +::: diff --git a/docs/fxrp/upshift/_upshift.mdx b/docs/fxrp/upshift/_upshift.mdx new file mode 100644 index 00000000..61ad0d9a --- /dev/null +++ b/docs/fxrp/upshift/_upshift.mdx @@ -0,0 +1,44 @@ +import DocCardList from "@theme/DocCardList"; + +[Upshift](https://upshift.finance) is a yield-generating protocol that supports FXRP through strategy-driven vaults on Flare. +It provides [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626)] style vaults that allow users to deposit FXRP and earn yield generated from on-chain DeFi strategies, while abstracting away strategy execution and risk management. + +Upshift vaults support both instant and requested redemptions, depending on available liquidity. +Users receive vault shares that represent their proportional ownership of the vault's assets, with yield accruing as share value increases over time. + + + +:::tip[What's next] + +- Learn more about [FAssets](/fassets/overview) and how the system works. +- Explore how to [mint FXRP](/fassets/developer-guides/fassets-mint) from XRP. +- Discover how to [redeem FXRP](/fassets/developer-guides/fassets-redeem) back to XRP. + +::: diff --git a/docs/fxrp/upshift/index.mdx b/docs/fxrp/upshift/index.mdx new file mode 100644 index 00000000..87a297ba --- /dev/null +++ b/docs/fxrp/upshift/index.mdx @@ -0,0 +1,9 @@ +--- +title: Upshift Vaults +description: Learn how to interact with Upshift vaults, ERC-4626 vaults compatible with FXRP. +keywords: [fassets, flare-network, fxrp, upshift, vault, erc-4626] +--- + +import Upshift from "./_upshift.mdx"; + + diff --git a/docs/tags.yml b/docs/tags.yml index fbe3ef48..61875fbd 100644 --- a/docs/tags.yml +++ b/docs/tags.yml @@ -14,6 +14,8 @@ fassets: label: fassets firelight: label: firelight +upshift: + label: upshift python: label: python javascript: diff --git a/examples/developer-hub-javascript/upshift-claim.ts b/examples/developer-hub-javascript/upshift-claim.ts new file mode 100644 index 00000000..28f468f6 --- /dev/null +++ b/examples/developer-hub-javascript/upshift-claim.ts @@ -0,0 +1,260 @@ +/** + * Upshift Tokenized Vault Claim Script + * + * This script claims assets from a previously requested redemption. + * Use requestRedeem.ts first to schedule a redemption. + * + */ + +import { web3 } from "hardhat"; +import { formatUnits } from "ethers"; + +import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; + +const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; +const RECEIVER_ADDRESS = ""; // Leave empty to use sender's address + +// Update these with the date from your requestRedeem call +const YEAR = 2026; +const MONTH = 1; +const DAY = 23; + +const ITokenizedVault = artifacts.require("ITokenizedVault"); +const IERC20 = artifacts.require("IERC20"); +const IERC20Metadata = artifacts.require("IERC20Metadata"); +const IFAsset = artifacts.require("IFAsset"); + +async function getReferenceAssetInfo(vault: ITokenizedVaultInstance) { + const referenceAsset = await vault.asset(); + const refAsset = await IFAsset.at(referenceAsset); + const refDecimals = Number(await refAsset.decimals()); + const refSymbol = await refAsset.symbol(); + + return { referenceAsset, refAsset, refDecimals, refSymbol }; +} + +async function checkPendingRedemption( + vault: ITokenizedVaultInstance, + year: number, + month: number, + day: number, + receiverAddr: string, +) { + console.log("\n1. Checking pending redemption..."); + const shares = await vault.getBurnableAmountByReceiver( + year, + month, + day, + receiverAddr, + ); + + if (BigInt(shares.toString()) === 0n) { + console.log("No shares found for this date and receiver address"); + return null; + } + + return shares; +} + +async function getLPTokenInfo(vault: ITokenizedVaultInstance) { + const lpTokenAddress = await vault.lpTokenAddress(); + const lpToken = await IERC20Metadata.at(lpTokenAddress); + const lpDecimals = Number(await lpToken.decimals()); + const lpSymbol = await lpToken.symbol(); + + return { lpTokenAddress, lpDecimals, lpSymbol }; +} + +async function previewRedemption( + vault: ITokenizedVaultInstance, + shares: any, + refDecimals: number, + refSymbol: string, +) { + console.log("\n2. Previewing redemption..."); + const preview = await vault.previewRedemption(shares.toString(), false); + const assetsAmount = preview[0]; + const assetsAfterFee = preview[1]; + + console.log( + `Assets (before fee): ${formatUnits(assetsAmount.toString(), refDecimals)} ${refSymbol}`, + ); + console.log( + `Assets (after fee): ${formatUnits(assetsAfterFee.toString(), refDecimals)} ${refSymbol}`, + ); + const fee = + BigInt(assetsAmount.toString()) - BigInt(assetsAfterFee.toString()); + console.log(`Fee: ${formatUnits(fee.toString(), refDecimals)} ${refSymbol}`); + + return { assetsAmount, assetsAfterFee }; +} + +async function checkIfClaimable(year: number, month: number, day: number) { + console.log("\n3. Checking if claimable..."); + const block = await web3.eth.getBlock("latest"); + const blockTimestamp = BigInt(block.timestamp); + const claimableDate = new Date(Date.UTC(year, month - 1, day, 0, 0, 0)); + const claimableEpoch = BigInt(Math.floor(claimableDate.getTime() / 1000)); + const TIMESTAMP_MANIPULATION_WINDOW = 300n; // 5 minutes + + console.log(`Current Timestamp: ${blockTimestamp.toString()}`); + console.log(`Claimable Epoch: ${claimableEpoch.toString()}`); + + const canClaim = + blockTimestamp + TIMESTAMP_MANIPULATION_WINDOW >= claimableEpoch; + + if (!canClaim) { + const timeUntil = + claimableEpoch - blockTimestamp - TIMESTAMP_MANIPULATION_WINDOW; + const hoursUntil = Number(timeUntil) / 3600; + console.log("Cannot claim yet!"); + console.log(`Wait approximately ${hoursUntil.toFixed(2)} more hours`); + console.log( + `Claimable after: ${new Date(Number(claimableEpoch) * 1000).toISOString()}`, + ); + return false; + } + console.log("Ready to claim!"); + return true; +} + +async function getBalanceBefore( + referenceAsset: string, + receiverAddr: string, + refDecimals: number, + refSymbol: string, +) { + console.log("\n4. Checking receiver balance before..."); + const refToken: IERC20Instance = await IERC20.at(referenceAsset); + const balanceBefore = await refToken.balanceOf(receiverAddr); + console.log( + `Balance: ${formatUnits(balanceBefore.toString(), refDecimals)} ${refSymbol}`, + ); + + return { refToken, balanceBefore }; +} + +async function executeClaim( + vault: ITokenizedVaultInstance, + year: number, + month: number, + day: number, + receiverAddr: string, +) { + console.log("\n5. Claiming..."); + const claimTx = await vault.claim(year, month, day, receiverAddr); + console.log( + "Claim: (tx:", + claimTx.tx, + ", block:", + claimTx.receipt.blockNumber, + ")", + ); +} + +async function verifyClaim( + refToken: IERC20Instance, + receiverAddr: string, + balanceBefore: any, + assetsAfterFee: any, + refDecimals: number, + refSymbol: string, +) { + console.log("\n6. Verifying claim..."); + const balanceAfter = await refToken.balanceOf(receiverAddr); + const received = + BigInt(balanceAfter.toString()) - BigInt(balanceBefore.toString()); + console.log( + `Balance After: ${formatUnits(balanceAfter.toString(), refDecimals)} ${refSymbol}`, + ); + console.log( + `Received: ${formatUnits(received.toString(), refDecimals)} ${refSymbol}`, + ); + + if (received === BigInt(assetsAfterFee.toString())) { + console.log("Claim successful!"); + } else { + console.log( + "Received amount differs from expected (may be due to rounding)", + ); + } +} + +async function main() { + // 1. Initialize: Get user account from Hardhat network + const accounts = await web3.eth.getAccounts(); + const userAddress = accounts[0]; + const receiverAddr = RECEIVER_ADDRESS || userAddress; + + console.log("CLAIM REDEMPTION\n"); + console.log("Vault Address:", VAULT_ADDRESS); + console.log("User Address:", userAddress); + console.log("Receiver Address:", receiverAddr); + console.log( + `Redemption Date: ${YEAR}-${String(MONTH).padStart(2, "0")}-${String(DAY).padStart(2, "0")}`, + ); + + // 2. Connect to the vault contract instance + const vault: ITokenizedVaultInstance = + await ITokenizedVault.at(VAULT_ADDRESS); + + // 3. Get reference asset info + const { referenceAsset, refDecimals, refSymbol } = + await getReferenceAssetInfo(vault); + + // 4. Check pending redemption + const shares = await checkPendingRedemption( + vault, + YEAR, + MONTH, + DAY, + receiverAddr, + ); + if (!shares) return; + + // 5. Get LP token info + const { lpTokenAddress, lpDecimals, lpSymbol } = await getLPTokenInfo(vault); + console.log( + `Shares to claim: ${formatUnits(shares.toString(), lpDecimals)} ${lpSymbol}`, + ); + console.log(`LP Token Address: ${lpTokenAddress}`); + + // 6. Preview redemption + const { assetsAfterFee } = await previewRedemption( + vault, + shares, + refDecimals, + refSymbol, + ); + + // 7. Check if claimable + const canClaim = await checkIfClaimable(YEAR, MONTH, DAY); + if (!canClaim) return; + + // 8. Get balance before + const { refToken, balanceBefore } = await getBalanceBefore( + referenceAsset, + receiverAddr, + refDecimals, + refSymbol, + ); + + // 9. Execute claim + await executeClaim(vault, YEAR, MONTH, DAY, receiverAddr); + + // 10. Verify claim + await verifyClaim( + refToken, + receiverAddr, + balanceBefore, + assetsAfterFee, + refDecimals, + refSymbol, + ); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/examples/developer-hub-javascript/upshift-deposit.ts b/examples/developer-hub-javascript/upshift-deposit.ts new file mode 100644 index 00000000..d5cf4d37 --- /dev/null +++ b/examples/developer-hub-javascript/upshift-deposit.ts @@ -0,0 +1,216 @@ +/** + * Upshift Tokenized Vault Deposit Script + * + * This script deposits assets into the Upshift Tokenized Vault. + * + */ + +import { web3 } from "hardhat"; +import { parseUnits, formatUnits } from "ethers"; + +import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; + +const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; +const DEPOSIT_AMOUNT = "1"; + +const ITokenizedVault = artifacts.require("ITokenizedVault"); +const IERC20 = artifacts.require("IERC20"); +const IFAsset = artifacts.require("IFAsset"); + +async function getReferenceAssetInfo(vault: ITokenizedVaultInstance) { + const referenceAsset = await vault.asset(); + const refAsset = await IFAsset.at(referenceAsset); + const decimals = Number(await refAsset.decimals()); + const symbol = await refAsset.symbol(); + + console.log("\nReference Asset (asset depositing):", referenceAsset); + + return { referenceAsset, refAsset, decimals, symbol }; +} + +async function checkBalance( + refAsset: any, + userAddress: string, + depositAmount: bigint, + decimals: number, + symbol: string, +) { + const balance = await refAsset.balanceOf(userAddress); + console.log( + `Balance: ${formatUnits(balance.toString(), decimals)} ${symbol}`, + ); + + if (BigInt(balance.toString()) < depositAmount) { + console.log("Insufficient balance!"); + return false; + } + return true; +} + +async function checkAndApproveAllowance( + refAsset: any, + userAddress: string, + depositAmount: bigint, + decimals: number, + symbol: string, +) { + const allowance = await refAsset.allowance(userAddress, VAULT_ADDRESS); + console.log( + `Current Allowance: ${formatUnits(allowance.toString(), decimals)} ${symbol}`, + ); + + if (BigInt(allowance.toString()) < depositAmount) { + console.log( + `\nApproving vault to spend ${DEPOSIT_AMOUNT} ${symbol} tokens`, + ); + const approveTx = await refAsset.approve( + VAULT_ADDRESS, + depositAmount.toString(), + ); + console.log("Approval Tx:", approveTx.tx); + } +} + +async function previewDeposit( + vault: ITokenizedVaultInstance, + referenceAsset: string, + depositAmount: bigint, + decimals: number, +) { + const preview = await vault.previewDeposit( + referenceAsset, + depositAmount.toString(), + ); + const expectedShares = preview[0]; + const amountInRefTokens = preview[1]; + + console.log( + `\nExpected Shares: ${formatUnits(expectedShares.toString(), decimals)}`, + ); + console.log( + `Amount in Reference Tokens: ${formatUnits(amountInRefTokens.toString(), decimals)}`, + ); + + return { expectedShares }; +} + +async function getLPTokenInfo( + vault: ITokenizedVaultInstance, + userAddress: string, + decimals: number, +) { + const lpTokenAddress = await vault.lpTokenAddress(); + const lpToken: IERC20Instance = await IERC20.at(lpTokenAddress); + const lpBalanceBefore = await lpToken.balanceOf(userAddress); + + console.log( + `LP Balance Before: ${formatUnits(lpBalanceBefore.toString(), decimals)}`, + ); + + return { lpToken, lpBalanceBefore }; +} + +async function executeDeposit( + vault: ITokenizedVaultInstance, + referenceAsset: string, + depositAmount: bigint, + userAddress: string, +) { + const depositTx = await vault.deposit( + referenceAsset, + depositAmount.toString(), + userAddress, + ); + console.log( + "\nDeposit: (tx:", + depositTx.tx, + ", block:", + depositTx.receipt.blockNumber, + ")", + ); +} + +async function verifyDeposit( + lpToken: IERC20Instance, + userAddress: string, + lpBalanceBefore: any, + decimals: number, +) { + console.log("\nVerifying deposit..."); + + const lpBalanceAfter = await lpToken.balanceOf(userAddress); + const sharesReceived = + BigInt(lpBalanceAfter.toString()) - BigInt(lpBalanceBefore.toString()); + + console.log( + `LP Balance After: ${formatUnits(lpBalanceAfter.toString(), decimals)}`, + ); + console.log( + `Shares Received: ${formatUnits(sharesReceived.toString(), decimals)}`, + ); +} + +async function main() { + // 1. Initialize: Get user account from Hardhat network + const accounts = await web3.eth.getAccounts(); + const userAddress = accounts[0]; + + console.log("DEPOSIT TO VAULT\n"); + console.log("Vault Address:", VAULT_ADDRESS); + console.log("User Address:", userAddress); + + // 2. Connect to the vault contract instance + const vault: ITokenizedVaultInstance = + await ITokenizedVault.at(VAULT_ADDRESS); + + // 3. Get reference asset info + const { referenceAsset, refAsset, decimals, symbol } = + await getReferenceAssetInfo(vault); + + // 4. Convert deposit amount from human-readable to token units + const depositAmount = parseUnits(DEPOSIT_AMOUNT, decimals); + console.log( + `\nDeposit Amount: ${DEPOSIT_AMOUNT} ${symbol} (${depositAmount.toString()})`, + ); + + // 5. Check user balance + const hasBalance = await checkBalance( + refAsset, + userAddress, + depositAmount, + decimals, + symbol, + ); + if (!hasBalance) return; + + // 6. Check and approve allowance + await checkAndApproveAllowance( + refAsset, + userAddress, + depositAmount, + decimals, + symbol, + ); + + // 7. Preview deposit + await previewDeposit(vault, referenceAsset, depositAmount, decimals); + + // 8. Get LP token info before deposit + const { lpToken, lpBalanceBefore } = await getLPTokenInfo( + vault, + userAddress, + decimals, + ); + + // 9. Execute deposit + await executeDeposit(vault, referenceAsset, depositAmount, userAddress); + + // 10. Verify deposit + await verifyDeposit(lpToken, userAddress, lpBalanceBefore, decimals); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/examples/developer-hub-javascript/upshift-instant-redeem.ts b/examples/developer-hub-javascript/upshift-instant-redeem.ts new file mode 100644 index 00000000..68504268 --- /dev/null +++ b/examples/developer-hub-javascript/upshift-instant-redeem.ts @@ -0,0 +1,204 @@ +/** + * Upshift Tokenized Vault Instant Redeem Script + * + * This script performs an instant redemption of LP shares from the Upshift Tokenized Vault. + * + */ + +import { web3 } from "hardhat"; +import { parseUnits, formatUnits } from "ethers"; + +import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; + +const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; +const SHARES_TO_REDEEM = "1"; + +const ITokenizedVault = artifacts.require("ITokenizedVault"); +const IERC20 = artifacts.require("IERC20"); +const IFAsset = artifacts.require("IFAsset"); + +async function getReferenceAssetInfo(vault: ITokenizedVaultInstance) { + const referenceAsset = await vault.asset(); + const refAsset = await IFAsset.at(referenceAsset); + const decimals = Number(await refAsset.decimals()); + const symbol = await refAsset.symbol(); + + console.log("Reference Asset (asset receiving):", referenceAsset); + + return { referenceAsset, refAsset, decimals, symbol }; +} + +async function getLPTokenInfo( + vault: ITokenizedVaultInstance, + userAddress: string, + decimals: number, +) { + const lpTokenAddress = await vault.lpTokenAddress(); + const lpToken: IERC20Instance = await IERC20.at(lpTokenAddress); + const lpBalance = await lpToken.balanceOf(userAddress); + + console.log(`\nLP Balance: ${formatUnits(lpBalance.toString(), decimals)}`); + + return { lpToken, lpBalance }; +} + +function checkLPBalance(lpBalance: any, sharesToRedeem: bigint) { + if (BigInt(lpBalance.toString()) < sharesToRedeem) { + console.log("Insufficient LP balance!"); + return false; + } + return true; +} + +async function previewRedemption( + vault: ITokenizedVaultInstance, + sharesToRedeem: bigint, + decimals: number, + symbol: string, +) { + const instantRedemptionFee = await vault.instantRedemptionFee(); + console.log( + `\nInstant Redemption Fee: ${formatUnits(instantRedemptionFee.toString(), 16)}%`, + ); + + const preview = await vault.previewRedemption( + sharesToRedeem.toString(), + true, + ); + const assetsAmount = preview[0]; + const assetsAfterFee = preview[1]; + + console.log( + `Expected Assets (before fee): ${formatUnits(assetsAmount.toString(), decimals)} ${symbol}`, + ); + console.log( + `Expected Assets (after fee): ${formatUnits(assetsAfterFee.toString(), decimals)} ${symbol}`, + ); +} + +async function getAssetBalanceBefore( + refAsset: any, + userAddress: string, + decimals: number, + symbol: string, +) { + const assetBalanceBefore = await refAsset.balanceOf(userAddress); + console.log( + `\nAsset Balance Before: ${formatUnits(assetBalanceBefore.toString(), decimals)} ${symbol}`, + ); + + return { assetBalanceBefore }; +} + +async function executeInstantRedeem( + vault: ITokenizedVaultInstance, + sharesToRedeem: bigint, + userAddress: string, +) { + const redeemTx = await vault.instantRedeem( + sharesToRedeem.toString(), + userAddress, + ); + console.log( + "\nInstant Redeem: (tx:", + redeemTx.tx, + ", block:", + redeemTx.receipt.blockNumber, + ")", + ); +} + +async function verifyRedemption( + lpToken: IERC20Instance, + refAsset: any, + userAddress: string, + lpBalanceBefore: any, + assetBalanceBefore: any, + decimals: number, + symbol: string, +) { + console.log("\nVerifying redemption..."); + + const lpBalanceAfter = await lpToken.balanceOf(userAddress); + const assetBalanceAfter = await refAsset.balanceOf(userAddress); + + const sharesRedeemed = + BigInt(lpBalanceBefore.toString()) - BigInt(lpBalanceAfter.toString()); + const assetsReceived = + BigInt(assetBalanceAfter.toString()) - + BigInt(assetBalanceBefore.toString()); + + console.log( + `LP Balance After: ${formatUnits(lpBalanceAfter.toString(), decimals)}`, + ); + console.log( + `Shares Redeemed: ${formatUnits(sharesRedeemed.toString(), decimals)}`, + ); + console.log( + `Assets Received: ${formatUnits(assetsReceived.toString(), decimals)} ${symbol}`, + ); +} + +async function main() { + // 1. Initialize: Get user account from Hardhat network + const accounts = await web3.eth.getAccounts(); + const userAddress = accounts[0]; + + console.log("INSTANT REDEEM FROM VAULT\n"); + console.log("Vault Address:", VAULT_ADDRESS); + console.log("User Address:", userAddress); + + // 2. Connect to the vault contract instance + const vault: ITokenizedVaultInstance = + await ITokenizedVault.at(VAULT_ADDRESS); + + // 3. Get reference asset info + const { refAsset, decimals, symbol } = await getReferenceAssetInfo(vault); + + // 4. Get LP token info + const { lpToken, lpBalance } = await getLPTokenInfo( + vault, + userAddress, + decimals, + ); + + // 5. Convert shares amount and validate balance + const sharesToRedeem = parseUnits(SHARES_TO_REDEEM, decimals); + console.log( + `Shares to Redeem: ${SHARES_TO_REDEEM} (${sharesToRedeem.toString()})`, + ); + + const hasBalance = checkLPBalance(lpBalance, sharesToRedeem); + if (!hasBalance) return; + + // 6. Preview redemption + await previewRedemption(vault, sharesToRedeem, decimals, symbol); + + // 7. Get asset balance before redemption + const { assetBalanceBefore } = await getAssetBalanceBefore( + refAsset, + userAddress, + decimals, + symbol, + ); + + // 8. Execute instant redemption + await executeInstantRedeem(vault, sharesToRedeem, userAddress); + + // 9. Verify redemption + await verifyRedemption( + lpToken, + refAsset, + userAddress, + lpBalance, + assetBalanceBefore, + decimals, + symbol, + ); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/examples/developer-hub-javascript/upshift-request-redeem.ts b/examples/developer-hub-javascript/upshift-request-redeem.ts new file mode 100644 index 00000000..370bdb61 --- /dev/null +++ b/examples/developer-hub-javascript/upshift-request-redeem.ts @@ -0,0 +1,246 @@ +/** + * Upshift Tokenized Vault Request Redeem Script + * + * This script requests a delayed redemption of LP shares from the Upshift Tokenized Vault. + * After the lag duration passes, use the claim script to receive assets. + * + */ + +import { web3 } from "hardhat"; +import { parseUnits, formatUnits } from "ethers"; + +import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; + +const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; +const SHARES_TO_REDEEM = "1"; + +const ITokenizedVault = artifacts.require("ITokenizedVault"); +const IERC20 = artifacts.require("IERC20"); +const IFAsset = artifacts.require("IFAsset"); + +async function getReferenceAssetInfo(vault: ITokenizedVaultInstance) { + const referenceAsset = await vault.asset(); + const refAsset = await IFAsset.at(referenceAsset); + const decimals = Number(await refAsset.decimals()); + const symbol = await refAsset.symbol(); + + console.log("Reference Asset (asset receiving):", referenceAsset); + + return { referenceAsset, refAsset, decimals, symbol }; +} + +async function getLPTokenInfo( + vault: ITokenizedVaultInstance, + userAddress: string, + decimals: number, +) { + const lpTokenAddress = await vault.lpTokenAddress(); + const lpToken: IERC20Instance = await IERC20.at(lpTokenAddress); + const lpBalance = await lpToken.balanceOf(userAddress); + + console.log(`\nLP Balance: ${formatUnits(lpBalance.toString(), decimals)}`); + + return { lpToken, lpBalance }; +} + +function checkLPBalance(lpBalance: any, sharesToRedeem: bigint) { + if (BigInt(lpBalance.toString()) < sharesToRedeem) { + console.log("Insufficient LP balance!"); + return false; + } + return true; +} + +async function checkAndApproveLPAllowance( + lpToken: IERC20Instance, + userAddress: string, + sharesToRedeem: bigint, + decimals: number, +) { + const lpAllowance = await lpToken.allowance(userAddress, VAULT_ADDRESS); + console.log( + `\nCurrent LP Allowance: ${formatUnits(lpAllowance.toString(), decimals)}`, + ); + + if (BigInt(lpAllowance.toString()) < sharesToRedeem) { + console.log(`\nApproving vault to spend ${SHARES_TO_REDEEM} LP tokens...`); + const approveTx = await lpToken.approve( + VAULT_ADDRESS, + sharesToRedeem.toString(), + ); + console.log("Approval Tx:", approveTx.tx); + } +} + +async function checkVaultConfiguration( + vault: ITokenizedVaultInstance, + sharesToRedeem: bigint, + decimals: number, + symbol: string, +) { + const lagDuration = await vault.lagDuration(); + const withdrawalFee = await vault.withdrawalFee(); + const withdrawalsPaused = await vault.withdrawalsPaused(); + const maxWithdrawalAmount = await vault.maxWithdrawalAmount(); + + console.log(`\nLag Duration: ${lagDuration.toString()} seconds`); + console.log(`Withdrawal Fee: ${formatUnits(withdrawalFee.toString(), 16)}%`); + console.log(`Withdrawals Paused: ${withdrawalsPaused}`); + console.log( + `Max Withdrawal Amount: ${formatUnits(maxWithdrawalAmount.toString(), decimals)} ${symbol}`, + ); + + if (withdrawalsPaused) { + console.log("\nError: Withdrawals are currently paused!"); + return false; + } + + if ( + BigInt(maxWithdrawalAmount.toString()) > 0n && + sharesToRedeem > BigInt(maxWithdrawalAmount.toString()) + ) { + console.log("\nError: Shares to redeem exceeds max withdrawal amount!"); + return false; + } + + return true; +} + +async function previewRedemption( + vault: ITokenizedVaultInstance, + sharesToRedeem: bigint, + decimals: number, + symbol: string, +) { + const preview = await vault.previewRedemption( + sharesToRedeem.toString(), + false, + ); + const assetsAmount = preview[0]; + const assetsAfterFee = preview[1]; + + console.log( + `\nExpected Assets (before fee): ${formatUnits(assetsAmount.toString(), decimals)} ${symbol}`, + ); + console.log( + `Expected Assets (after fee): ${formatUnits(assetsAfterFee.toString(), decimals)} ${symbol}`, + ); +} + +async function printWithdrawalEpoch(vault: ITokenizedVaultInstance) { + const epochInfo = await vault.getWithdrawalEpoch(); + console.log( + `\nCurrent Epoch - Year: ${epochInfo[0].toString()}, Month: ${epochInfo[1].toString()}, Day: ${epochInfo[2].toString()}`, + ); + console.log(`Claimable Epoch: ${epochInfo[3].toString()}`); +} + +async function executeRequestRedeem( + vault: ITokenizedVaultInstance, + sharesToRedeem: bigint, + userAddress: string, +) { + const requestTx = await vault.requestRedeem( + sharesToRedeem.toString(), + userAddress, + ); + console.log( + "\nRequest Redeem: (tx:", + requestTx.tx, + ", block:", + requestTx.receipt.blockNumber, + ")", + ); +} + +async function verifyRequest( + vault: ITokenizedVaultInstance, + lpToken: IERC20Instance, + userAddress: string, + lpBalanceBefore: any, + decimals: number, +) { + const lpBalanceAfter = await lpToken.balanceOf(userAddress); + const sharesLocked = + BigInt(lpBalanceBefore.toString()) - BigInt(lpBalanceAfter.toString()); + + console.log( + `LP Balance After: ${formatUnits(lpBalanceAfter.toString(), decimals)}`, + ); + console.log( + `Shares Locked: ${formatUnits(sharesLocked.toString(), decimals)}`, + ); + + const newEpochInfo = await vault.getWithdrawalEpoch(); + console.log( + `\nClaim your assets after: ${newEpochInfo[0].toString()}/${newEpochInfo[1].toString()}/${newEpochInfo[2].toString()}`, + ); +} + +async function main() { + // 1. Initialize: Get user account from Hardhat network + const accounts = await web3.eth.getAccounts(); + const userAddress = accounts[0]; + + console.log("REQUEST REDEEM FROM VAULT\n"); + console.log("Vault Address:", VAULT_ADDRESS); + console.log("User Address:", userAddress); + + // 2. Connect to the vault contract instance + const vault: ITokenizedVaultInstance = + await ITokenizedVault.at(VAULT_ADDRESS); + + // 3. Get reference asset info + const { decimals, symbol } = await getReferenceAssetInfo(vault); + + // 4. Get LP token info + const { lpToken, lpBalance } = await getLPTokenInfo( + vault, + userAddress, + decimals, + ); + + // 5. Convert shares amount and validate balance + const sharesToRedeem = parseUnits(SHARES_TO_REDEEM, decimals); + console.log( + `Shares to Redeem: ${SHARES_TO_REDEEM} (${sharesToRedeem.toString()})`, + ); + + const hasBalance = checkLPBalance(lpBalance, sharesToRedeem); + if (!hasBalance) return; + + // 6. Check and approve LP allowance + await checkAndApproveLPAllowance( + lpToken, + userAddress, + sharesToRedeem, + decimals, + ); + + // 7. Check vault configuration + const canProceed = await checkVaultConfiguration( + vault, + sharesToRedeem, + decimals, + symbol, + ); + if (!canProceed) return; + + // 8. Preview redemption + await previewRedemption(vault, sharesToRedeem, decimals, symbol); + + // 9. Print withdrawal epoch info + await printWithdrawalEpoch(vault); + + // 10. Execute request redeem + await executeRequestRedeem(vault, sharesToRedeem, userAddress); + + // 11. Verify request + await verifyRequest(vault, lpToken, userAddress, lpBalance, decimals); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/examples/developer-hub-javascript/upshift-status.ts b/examples/developer-hub-javascript/upshift-status.ts new file mode 100644 index 00000000..aa85a48d --- /dev/null +++ b/examples/developer-hub-javascript/upshift-status.ts @@ -0,0 +1,153 @@ +/** + * Upshift Tokenized Vault Status Script + * + * This script displays the current status of the vault and user balances. + * + */ + +import { web3 } from "hardhat"; +import { formatUnits } from "ethers"; + +import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; + +const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; + +const ITokenizedVault = artifacts.require("ITokenizedVault"); +const IERC20 = artifacts.require("IERC20"); +const IFAsset = artifacts.require("IFAsset"); + +async function printReferenceAssetInfo(vault: ITokenizedVaultInstance) { + const referenceAsset = await vault.asset(); + const refAsset = await IFAsset.at(referenceAsset); + const decimals = Number(await refAsset.decimals()); + const symbol = await refAsset.symbol(); + + console.log("\nReference Asset"); + console.log(`Address: ${referenceAsset}`); + console.log(`Symbol: ${symbol}`); + console.log(`Decimals: ${decimals}`); + + return { referenceAsset, refAsset, decimals, symbol }; +} + +async function printLPTokenInfo(vault: ITokenizedVaultInstance) { + const lpTokenAddress = await vault.lpTokenAddress(); + const lpToken: IERC20Instance = await IERC20.at(lpTokenAddress); + + console.log("\nLP Token"); + console.log(`Address: ${lpTokenAddress}`); + + return { lpTokenAddress, lpToken }; +} + +async function printVaultConfiguration( + vault: ITokenizedVaultInstance, + decimals: number, + symbol: string, +) { + console.log("\nVault Configuration"); + + const withdrawalsPaused = await vault.withdrawalsPaused(); + const lagDuration = await vault.lagDuration(); + const withdrawalFee = await vault.withdrawalFee(); + const instantRedemptionFee = await vault.instantRedemptionFee(); + const maxWithdrawalAmount = await vault.maxWithdrawalAmount(); + + console.log(`Withdrawals Paused: ${withdrawalsPaused}`); + console.log(`Lag Duration: ${lagDuration.toString()} seconds`); + console.log(`Withdrawal Fee: ${formatUnits(withdrawalFee.toString(), 16)}%`); + console.log( + `Instant Redemption Fee: ${formatUnits(instantRedemptionFee.toString(), 16)}%`, + ); + console.log( + `Max Withdrawal Amount: ${formatUnits(maxWithdrawalAmount.toString(), decimals)} ${symbol}`, + ); +} + +async function printWithdrawalEpoch(vault: ITokenizedVaultInstance) { + console.log("\nWithdrawal Epoch"); + + const epochInfo = await vault.getWithdrawalEpoch(); + console.log( + `Year: ${epochInfo[0].toString()}, Month: ${epochInfo[1].toString()}, Day: ${epochInfo[2].toString()}`, + ); + console.log(`Claimable Epoch: ${epochInfo[3].toString()}`); +} + +async function printUserBalances( + userAddress: string, + refAsset: any, + lpToken: IERC20Instance, + decimals: number, + symbol: string, +) { + console.log("\nUser Balances"); + + const refBalance = await refAsset.balanceOf(userAddress); + const lpBalance = await lpToken.balanceOf(userAddress); + + console.log( + `${symbol} Balance: ${formatUnits(refBalance.toString(), decimals)}`, + ); + console.log( + `LP Token Balance: ${formatUnits(lpBalance.toString(), decimals)}`, + ); +} + +async function printAllowances( + userAddress: string, + refAsset: any, + lpToken: IERC20Instance, + decimals: number, + symbol: string, +) { + console.log("\nAllowances"); + + const refAllowance = await refAsset.allowance(userAddress, VAULT_ADDRESS); + const lpAllowance = await lpToken.allowance(userAddress, VAULT_ADDRESS); + + console.log( + `${symbol} Allowance to Vault: ${formatUnits(refAllowance.toString(), decimals)}`, + ); + console.log( + `LP Token Allowance to Vault: ${formatUnits(lpAllowance.toString(), decimals)}`, + ); +} + +async function main() { + // 1. Initialize: Get user account from Hardhat network + const accounts = await web3.eth.getAccounts(); + const userAddress = accounts[0]; + + console.log("VAULT STATUS\n"); + console.log("Vault Address:", VAULT_ADDRESS); + console.log("User Address:", userAddress); + + // 2. Connect to the vault contract instance + const vault: ITokenizedVaultInstance = + await ITokenizedVault.at(VAULT_ADDRESS); + + // 3. Get and print reference asset info + const { refAsset, decimals, symbol } = await printReferenceAssetInfo(vault); + + // 4. Get and print LP token info + const { lpToken } = await printLPTokenInfo(vault); + + // 5. Print vault configuration + await printVaultConfiguration(vault, decimals, symbol); + + // 6. Print withdrawal epoch info + await printWithdrawalEpoch(vault); + + // 7. Print user balances + await printUserBalances(userAddress, refAsset, lpToken, decimals, symbol); + + // 8. Print allowances + await printAllowances(userAddress, refAsset, lpToken, decimals, symbol); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/sidebars.ts b/sidebars.ts index 475d921a..9d07972a 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -310,6 +310,21 @@ const sidebars: SidebarsConfig = { "fxrp/firelight/claim", ], }, + { + type: "category", + label: "Upshift Vaults", + link: { + type: "doc", + id: "fxrp/upshift/index", + }, + items: [ + "fxrp/upshift/status", + "fxrp/upshift/deposit", + "fxrp/upshift/instant-redeem", + "fxrp/upshift/request-redeem", + "fxrp/upshift/claim", + ], + }, ], }, { From 9fa908d605dfaad5a870ccad82650d62e10d92ba Mon Sep 17 00:00:00 2001 From: Kristaps Grinbergs Date: Tue, 27 Jan 2026 16:21:43 +0200 Subject: [PATCH 2/4] fix(docs): update type imports and enhance type definitions in Upshift examples --- .../developer-hub-javascript/upshift-claim.ts | 10 +++++----- .../developer-hub-javascript/upshift-deposit.ts | 10 +++++----- .../upshift-instant-redeem.ts | 17 ++++++++++------- .../upshift-request-redeem.ts | 11 +++++++---- .../developer-hub-javascript/upshift-status.ts | 8 ++++---- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/examples/developer-hub-javascript/upshift-claim.ts b/examples/developer-hub-javascript/upshift-claim.ts index 28f468f6..f3659926 100644 --- a/examples/developer-hub-javascript/upshift-claim.ts +++ b/examples/developer-hub-javascript/upshift-claim.ts @@ -9,8 +9,8 @@ import { web3 } from "hardhat"; import { formatUnits } from "ethers"; -import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; -import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; +import type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import type { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; const RECEIVER_ADDRESS = ""; // Leave empty to use sender's address @@ -68,7 +68,7 @@ async function getLPTokenInfo(vault: ITokenizedVaultInstance) { async function previewRedemption( vault: ITokenizedVaultInstance, - shares: any, + shares: { toString(): string }, refDecimals: number, refSymbol: string, ) { @@ -156,8 +156,8 @@ async function executeClaim( async function verifyClaim( refToken: IERC20Instance, receiverAddr: string, - balanceBefore: any, - assetsAfterFee: any, + balanceBefore: { toString(): string }, + assetsAfterFee: { toString(): string }, refDecimals: number, refSymbol: string, ) { diff --git a/examples/developer-hub-javascript/upshift-deposit.ts b/examples/developer-hub-javascript/upshift-deposit.ts index d5cf4d37..a9ffab2a 100644 --- a/examples/developer-hub-javascript/upshift-deposit.ts +++ b/examples/developer-hub-javascript/upshift-deposit.ts @@ -8,8 +8,8 @@ import { web3 } from "hardhat"; import { parseUnits, formatUnits } from "ethers"; -import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; -import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; +import type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import type { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; const DEPOSIT_AMOUNT = "1"; @@ -30,7 +30,7 @@ async function getReferenceAssetInfo(vault: ITokenizedVaultInstance) { } async function checkBalance( - refAsset: any, + refAsset: IERC20Instance, userAddress: string, depositAmount: bigint, decimals: number, @@ -49,7 +49,7 @@ async function checkBalance( } async function checkAndApproveAllowance( - refAsset: any, + refAsset: IERC20Instance, userAddress: string, depositAmount: bigint, decimals: number, @@ -134,7 +134,7 @@ async function executeDeposit( async function verifyDeposit( lpToken: IERC20Instance, userAddress: string, - lpBalanceBefore: any, + lpBalanceBefore: { toString(): string }, decimals: number, ) { console.log("\nVerifying deposit..."); diff --git a/examples/developer-hub-javascript/upshift-instant-redeem.ts b/examples/developer-hub-javascript/upshift-instant-redeem.ts index 68504268..bd2e483f 100644 --- a/examples/developer-hub-javascript/upshift-instant-redeem.ts +++ b/examples/developer-hub-javascript/upshift-instant-redeem.ts @@ -8,8 +8,8 @@ import { web3 } from "hardhat"; import { parseUnits, formatUnits } from "ethers"; -import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; -import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; +import type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import type { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; const SHARES_TO_REDEEM = "1"; @@ -43,7 +43,10 @@ async function getLPTokenInfo( return { lpToken, lpBalance }; } -function checkLPBalance(lpBalance: any, sharesToRedeem: bigint) { +function checkLPBalance( + lpBalance: { toString(): string }, + sharesToRedeem: bigint, +) { if (BigInt(lpBalance.toString()) < sharesToRedeem) { console.log("Insufficient LP balance!"); return false; @@ -78,7 +81,7 @@ async function previewRedemption( } async function getAssetBalanceBefore( - refAsset: any, + refAsset: IERC20Instance, userAddress: string, decimals: number, symbol: string, @@ -111,10 +114,10 @@ async function executeInstantRedeem( async function verifyRedemption( lpToken: IERC20Instance, - refAsset: any, + refAsset: IERC20Instance, userAddress: string, - lpBalanceBefore: any, - assetBalanceBefore: any, + lpBalanceBefore: { toString(): string }, + assetBalanceBefore: { toString(): string }, decimals: number, symbol: string, ) { diff --git a/examples/developer-hub-javascript/upshift-request-redeem.ts b/examples/developer-hub-javascript/upshift-request-redeem.ts index 370bdb61..6ef8508a 100644 --- a/examples/developer-hub-javascript/upshift-request-redeem.ts +++ b/examples/developer-hub-javascript/upshift-request-redeem.ts @@ -9,8 +9,8 @@ import { web3 } from "hardhat"; import { parseUnits, formatUnits } from "ethers"; -import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; -import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; +import type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import type { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; const SHARES_TO_REDEEM = "1"; @@ -44,7 +44,10 @@ async function getLPTokenInfo( return { lpToken, lpBalance }; } -function checkLPBalance(lpBalance: any, sharesToRedeem: bigint) { +function checkLPBalance( + lpBalance: { toString(): string }, + sharesToRedeem: bigint, +) { if (BigInt(lpBalance.toString()) < sharesToRedeem) { console.log("Insufficient LP balance!"); return false; @@ -158,7 +161,7 @@ async function verifyRequest( vault: ITokenizedVaultInstance, lpToken: IERC20Instance, userAddress: string, - lpBalanceBefore: any, + lpBalanceBefore: { toString(): string }, decimals: number, ) { const lpBalanceAfter = await lpToken.balanceOf(userAddress); diff --git a/examples/developer-hub-javascript/upshift-status.ts b/examples/developer-hub-javascript/upshift-status.ts index aa85a48d..bd90a7cc 100644 --- a/examples/developer-hub-javascript/upshift-status.ts +++ b/examples/developer-hub-javascript/upshift-status.ts @@ -8,8 +8,8 @@ import { web3 } from "hardhat"; import { formatUnits } from "ethers"; -import { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; -import { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; +import type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault"; +import type { IERC20Instance } from "../../typechain-types/@openzeppelin/contracts/token/ERC20/IERC20"; const VAULT_ADDRESS = "0x24c1a47cD5e8473b64EAB2a94515a196E10C7C81"; @@ -77,7 +77,7 @@ async function printWithdrawalEpoch(vault: ITokenizedVaultInstance) { async function printUserBalances( userAddress: string, - refAsset: any, + refAsset: IERC20Instance, lpToken: IERC20Instance, decimals: number, symbol: string, @@ -97,7 +97,7 @@ async function printUserBalances( async function printAllowances( userAddress: string, - refAsset: any, + refAsset: IERC20Instance, lpToken: IERC20Instance, decimals: number, symbol: string, From 348833e37680acc0b987157ef8a7e3f27801e0ee Mon Sep 17 00:00:00 2001 From: Kristaps Grinbergs Date: Tue, 27 Jan 2026 16:31:46 +0200 Subject: [PATCH 3/4] fix(toml): add exclusion for jq-verifier-test URL in configuration --- lychee.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/lychee.toml b/lychee.toml index 6142037f..dd4cacae 100644 --- a/lychee.toml +++ b/lychee.toml @@ -25,4 +25,5 @@ exclude = [ '^https://docs\.eigencloud\.xyz/', '^https://coston2\.testnet\.flarescan\.com/', '^https://jq-verifier-test\.flare\.rocks', + '^https://jq-verifier-test.flare.rocks', ] From 75dd367e6c64f26f08ddc348a09a963be8aa8bd3 Mon Sep 17 00:00:00 2001 From: Kristaps Grinbergs Date: Tue, 24 Feb 2026 17:27:59 +0200 Subject: [PATCH 4/4] refactor(docs): reorganize Upshift content --- docs/fxrp/overview.mdx | 2 +- docs/fxrp/upshift/_upshift.mdx | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/fxrp/overview.mdx b/docs/fxrp/overview.mdx index f97d083d..da8813b5 100644 --- a/docs/fxrp/overview.mdx +++ b/docs/fxrp/overview.mdx @@ -122,6 +122,6 @@ FXRP is deployed as an [Omnichain Fungible Token (OFT)](/fxrp/oft) using LayerZe -## Upshift +## Upshift Vaults diff --git a/docs/fxrp/upshift/_upshift.mdx b/docs/fxrp/upshift/_upshift.mdx index 61ad0d9a..913fb31a 100644 --- a/docs/fxrp/upshift/_upshift.mdx +++ b/docs/fxrp/upshift/_upshift.mdx @@ -1,11 +1,20 @@ import DocCardList from "@theme/DocCardList"; [Upshift](https://upshift.finance) is a yield-generating protocol that supports FXRP through strategy-driven vaults on Flare. -It provides [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626)] style vaults that allow users to deposit FXRP and earn yield generated from on-chain DeFi strategies, while abstracting away strategy execution and risk management. +It provides [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) style vaults that allow users to deposit FXRP and earn yield generated from on-chain DeFi strategies, while abstracting away strategy execution and risk management. Upshift vaults support both instant and requested redemptions, depending on available liquidity. Users receive vault shares that represent their proportional ownership of the vault's assets, with yield accruing as share value increases over time. +[Upshift vault](https://app.upshift.finance/pools/14/0x373D7d201C8134D4a2f7b5c63560da217e3dEA28) is part of the XRP finance stack on Flare Network. +It inherits the transparency model of the [FAssets](/fassets/overview) system: minting and redemption are proof-backed, and vault interactions are recorded on-chain. + +- **Funds are on-chain:** User deposits, vault balances, withdrawal requests, and claims are executed as smart contract state changes on Flare. + You can verify all vault holdings on [DeBank](https://debank.com/profile/0xEDb7B1e92B8D3621b46843AD024949F10438374B) portfolio tracker. +- **User ownership is verifiable:** A user's position is represented directly on-chain and can be queried from smart contracts. +- **Instruction flow is auditable:** XRPL-triggered actions are executed on Flare via [Smart Accounts](/smart-accounts/overview) with [Flare Data Connector](/fdc/overview) backed verification and onchain execution. + You can verify all smart account activity on the [Flare Systems Explorer](https://flare-systems-explorer.flare.network/smart-accounts). +