Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const MyBorrowPositions: React.FC = () => {
);
const { positions: allPositions, isLoading: isLoadingAggregated } =
useUserPositions();

function getHealthFactor(contractId: string): number | null {
return (
hfPools.find((p) => p.contractId === contractId)?.healthFactor ?? null
Expand Down
129 changes: 129 additions & 0 deletions apps/web-app/src/features/borrowing/hooks/useRemoveCollateral.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"use client";

import { useState, useCallback } from "react";
import { Networks } from "@stellar/stellar-sdk";
import { useWallet } from "@/hooks/useWallet";
import { useToast } from "@/hooks/useToast";
import { removeCollateral } from "@/lib/helpers/stellar/lending";
import {
signAndSendTransaction,
type SignTransactionFn,
} from "@/lib/helpers/stellar/transaction";
import { getAvailableTokens } from "@/lib/helpers/stellar/soroswap";
import { rpcUrl } from "@/lib/constants/network";
import { extractContractErrorOrNull } from "@/lib/helpers/stellar/contractErrors";
import { TOAST_CONFIG } from "@/lib/constants/toast.config";
import type { BorrowPosition } from "./useUserBorrowPositions";

export function useRemoveCollateral() {
const { addNotification } = useToast();
const { address, signTransaction, networkPassphrase } = useWallet();
const [isLoading, setIsLoading] = useState(false);
const [selectedPosition, setSelectedPosition] =
useState<BorrowPosition | null>(null);

const openModal = useCallback((position: BorrowPosition) => {
setSelectedPosition(position);
}, []);

const closeModal = useCallback(() => {
setSelectedPosition(null);
}, []);

const showError = useCallback(
(msg: string) =>
addNotification("Something went wrong", "error", {
...TOAST_CONFIG.defaultOpts,
description: msg,
}),
[addNotification]
);

const showSuccess = useCallback(
(msg: string) =>
addNotification("Success", "success", {
...TOAST_CONFIG.defaultOpts,
description: msg,
}),
[addNotification]
);

const handleRemoveCollateral = useCallback(
async (amount: string) => {
if (!address || !selectedPosition) return;

const amountNum = parseFloat(amount);
if (!Number.isFinite(amountNum) || amountNum <= 0) {
showError("Please enter a valid amount");
return;
}

const availableTokens = getAvailableTokens();
const collateralToken =
availableTokens[selectedPosition.collateralTokenCode];

if (!collateralToken?.contract) {
showError(
`Collateral token ${selectedPosition.collateralTokenCode} not found`
);
return;
}

setIsLoading(true);
try {
const xdr = await removeCollateral(
collateralToken.contract,
amount,
7,
address,
selectedPosition.contractId
);

await signAndSendTransaction(
xdr,
signTransaction as SignTransactionFn,
{
networkPassphrase: networkPassphrase || Networks.TESTNET,
rpcUrl,
address,
waitForPending: true,
}
);

showSuccess(
`Successfully removed ${amountNum} ${selectedPosition.collateralTokenCode} from collateral`
);
closeModal();
return { success: true as const };
} catch (err) {
const friendlyError = extractContractErrorOrNull(err);
showError(
typeof friendlyError === "string"
? friendlyError
: "An unexpected error occurred. Please try again."
);
return { success: false as const, error: err };
} finally {
setIsLoading(false);
}
},
[
address,
networkPassphrase,
signTransaction,
selectedPosition,
showError,
showSuccess,
closeModal,
]
);

return {
selectedPosition,
isLoading,
isWalletConnected: Boolean(address),
openModal,
closeModal,
handleRemoveCollateral,
};
}
10 changes: 5 additions & 5 deletions apps/web-app/src/lib/constants/faucet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,31 @@ const TESTNET_FAUCET_TOKENS: {
}[] = [
{
symbol: "USTRY",
contract: "CC6SODKGOTFEDWVNPR6ESJC3GL7NC5Y4DVFKYGATZJ74F2YXHTW4RJ6D",
contract: "CCAYGJWQI5NJN7XRVNSENF47PICNSNTG4FAQHHFOJWZIRTEAC5JPMLGN",
decimals: 7,
amount: 100,
},
{
symbol: "TESOURO",
contract: "CA55OO3U556GXABJKDYP3QCGZ6AFNZPB27TROYP42AQPFFYPKU5EDOUH",
contract: "CAPFX3QEAHE7JVT6E7PYZQTFSVS5Z7AV4RE7GRJRVCPKXGQHCWSCOMTW",
decimals: 7,
amount: 100,
},
{
symbol: "CETES",
contract: "CCGWKS4GLAGPYIAOLBH6JM5RKUPMUCN47VRAEXAJWJFKXSXQ33VIRUAA",
contract: "CAJ4B2ZWU2GA7UYQZ7N7QQCTZAUSSXNKKQ326ADYVH3ALN4FFQ6LPO4U",
decimals: 7,
amount: 100,
},
{
symbol: "USDY",
contract: "CBVLFSVBZGHVAH6CV4JQYPBJSX75VFR2NJC7CQX7QKQ7KOLGQZOZAGQK",
contract: "CDRQV3D3GLWF73MWTEQWFZWMBQ47KZ3KECYPOBKBDRQBWQQ74KDH5ECT",
decimals: 7,
amount: 100,
},
{
symbol: "PYUSD",
contract: "CBCB5UDYZENTIUVVA7SHQOCVVCDXDMHEKJHHFM3OQKU2E5CAF2B62TIO",
contract: "CBNHH37BJ2G4ZT6PLWDXPOWHKLR75IGNLBRCXZNOS7YPAYS53JPEPSSS",
decimals: 7,
amount: 100,
},
Expand Down
202 changes: 201 additions & 1 deletion apps/web-app/src/lib/constants/generated/contract-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This file is automatically generated from Stellar smart contract error definitions.
* To regenerate, run: npm run generate:errors
*
* Generated at: 2026-03-08T07:15:16.526Z
* Generated at: 2026-03-08T14:16:50.215Z
*
* Source files:
* - apps/contracts/stellar-contracts/rwa-lending/src/common/error.rs
Expand Down Expand Up @@ -458,6 +458,206 @@ const _errorEntries: [number, ContractErrorInfo][] = [
contract: "rwa-oracle",
},
],
[
1,
{
code: "PositionNotFound",
message: "Position not found",
contract: "rwa-perps",
},
],
[
2,
{
code: "PositionAlreadyExists",
message: "Position already exists",
contract: "rwa-perps",
},
],
[
3,
{
code: "PositionNotLiquidatable",
message: "Position is not liquidatable",
contract: "rwa-perps",
},
],
[
10,
{
code: "MarginRatioHealthy",
message: "Margin ratio is healthy",
contract: "rwa-perps",
},
],
[
11,
{
code: "InsufficientMargin",
message: "Insufficient margin",
contract: "rwa-perps",
},
],
[
12,
{
code: "LiquidationPriceTooLow",
message: "Liquidation price is too low",
contract: "rwa-perps",
},
],
[
13,
{
code: "LiquidationPriceTooHigh",
message: "Liquidation price is too high",
contract: "rwa-perps",
},
],
[
20,
{
code: "MarketNotFound",
message: "Market not found",
contract: "rwa-perps",
},
],
[
21,
{
code: "MarketInactive",
message: "Market is inactive",
contract: "rwa-perps",
},
],
[
30,
{
code: "OraclePriceNotFound",
message: "Oracle price not found",
contract: "rwa-perps",
},
],
[
31,
{
code: "OraclePriceStale",
message: "Oracle price is stale",
contract: "rwa-perps",
},
],
[
40,
{
code: "ArithmeticError",
message: "A calculation error occurred. Please try a different amount",
contract: "rwa-perps",
},
],
[
41,
{
code: "Overflow",
message: "Arithmetic overflow occurred",
contract: "rwa-perps",
},
],
[
42,
{
code: "DivisionByZero",
message: "Division by zero",
contract: "rwa-perps",
},
],
[
50,
{
code: "Unauthorized",
message: "Unauthorized access",
contract: "rwa-perps",
},
],
[
60,
{
code: "InvalidInput",
message: "Invalid input provided",
contract: "rwa-perps",
},
],
[
61,
{
code: "NotInitialized",
message: "The lending protocol has not been initialized yet",
contract: "rwa-perps",
},
],
[
62,
{
code: "AlreadyInitialized",
message: "The lending protocol is already initialized",
contract: "rwa-perps",
},
],
[
63,
{
code: "ProtocolPaused",
message: "Protocol is paused",
contract: "rwa-perps",
},
],
[
70,
{
code: "InvalidFundingRate",
message: "Invalid funding rate",
contract: "rwa-perps",
},
],
[
71,
{
code: "FundingCalculationError",
message: "Funding calculation error",
contract: "rwa-perps",
},
],
[
72,
{
code: "MarginRatioBelowMaintenance",
message: "Margin removal would violate maintenance requirement",
contract: "rwa-perps",
},
],
[
73,
{
code: "MarginTokenNotSet",
message: "Margin token not configured",
contract: "rwa-perps",
},
],
[
80,
{
code: "ExceedsMaxLeverage",
message: "Leverage exceeds market maximum",
contract: "rwa-perps",
},
],
[
81,
{
code: "InsufficientInitialMargin",
message: "Margin below initial requirement",
contract: "rwa-perps",
},
],
[
1,
{
Expand Down
Loading
Loading