diff --git a/docs/fxrp/overview.mdx b/docs/fxrp/overview.mdx
index e76cc8b9..da8813b5 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.
@@ -123,11 +124,4 @@ FXRP is deployed as an [Omnichain Fungible Token (OFT)](/fxrp/oft) using LayerZe
## Upshift Vaults
-[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..913fb31a
--- /dev/null
+++ b/docs/fxrp/upshift/_upshift.mdx
@@ -0,0 +1,53 @@
+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.
+
+[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).
+
+
+
+:::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..f3659926
--- /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 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
+
+// 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: { toString(): string },
+ 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: { toString(): string },
+ assetsAfterFee: { toString(): string },
+ 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..a9ffab2a
--- /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 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";
+
+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: IERC20Instance,
+ 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: IERC20Instance,
+ 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: { toString(): string },
+ 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..bd2e483f
--- /dev/null
+++ b/examples/developer-hub-javascript/upshift-instant-redeem.ts
@@ -0,0 +1,207 @@
+/**
+ * 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 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";
+
+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: { toString(): string },
+ 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: IERC20Instance,
+ 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: IERC20Instance,
+ userAddress: string,
+ lpBalanceBefore: { toString(): string },
+ assetBalanceBefore: { toString(): string },
+ 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..6ef8508a
--- /dev/null
+++ b/examples/developer-hub-javascript/upshift-request-redeem.ts
@@ -0,0 +1,249 @@
+/**
+ * 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 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";
+
+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: { toString(): string },
+ 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: { toString(): string },
+ 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..bd90a7cc
--- /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 type { ITokenizedVaultInstance } from "../../typechain-types/contracts/upshift/ITokenizedVault";
+import type { 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: IERC20Instance,
+ 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: IERC20Instance,
+ 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/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',
]
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",
+ ],
+ },
],
},
{