Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ce76d1a
initial draft: display details pertaining to the SAV
ckeshava Jan 7, 2026
8161381
simplified the style components of VaultHeader React component
ckeshava Jan 7, 2026
a1cb949
feat: add transactions tab into the Vault webpage
ckeshava Jan 8, 2026
1f0f0b6
feat: completed a draft of the loan-brokers tab. TODO: Test the possi…
ckeshava Jan 8, 2026
d983e1e
fix errors in representation of some fields in LoanBroker section
ckeshava Jan 9, 2026
5792db5
draft: set up Vault Depositors section in the Vault webpage
ckeshava Jan 9, 2026
5ff454a
feat: add loans react component into the Vault Explorer page
ckeshava Jan 12, 2026
d29a97f
feat: introduce copy button in the LoanBrokerID field
ckeshava Jan 13, 2026
b9a51f7
fix: If TotalValueOutstanding is missing in the Loan object, consider…
ckeshava Jan 13, 2026
c7063fa
feat: Add Amount field in the VaultTransaction table
ckeshava Jan 13, 2026
5180cb3
fix: rectify minor errors in the Vault Header section
ckeshava Jan 13, 2026
9072e69
feat: add currency repr to the numeric Amount units
ckeshava Jan 13, 2026
8078192
[trivial] add messages in cases of error fetching loan-brokers and de…
ckeshava Jan 14, 2026
af7880e
feat: add transaction hash column in the VaultTransactions component
ckeshava Jan 20, 2026
4f2f291
fix: update LoanID text to white color
ckeshava Jan 20, 2026
64a1b5f
feat: add new LoanStatus Impaired for loans
ckeshava Jan 20, 2026
4c7d253
feat: Add the LoanCount to the LoanBrokerName-tab display
ckeshava Jan 20, 2026
8e5c266
feat: Add PermDomainID and LoanCounts for each of the LoanBrokers
ckeshava Jan 20, 2026
c62de36
feat: Introduced a number repr system for easier readabilioty (M for …
ckeshava Jan 20, 2026
5b83dd7
fix: Display TVL only if the asset involved is XRP or USD stablecoin
ckeshava Jan 20, 2026
7050adc
feat: add loan filter buttons
ckeshava Jan 20, 2026
eb8b7cf
feat: display warning if any loans have defaulted in a loan broker
ckeshava Jan 20, 2026
f948c71
feat: remove usages of private ripple artifactory
ckeshava Jan 21, 2026
00fc20b
feat: Add title at the top of the page
ckeshava Jan 21, 2026
248460a
feat: Unit tests for VaultHeader React component
ckeshava Jan 21, 2026
5b8d5e9
feat: Unit tests for Vault Transactions React component
ckeshava Jan 21, 2026
ddf0511
feat: Unit tests for the VaultLoans react component
ckeshava Jan 21, 2026
90db7d2
feat: Unit tests for the parent Vault react component
ckeshava Jan 21, 2026
16ff928
fix minor issues in the code -- remove TODO comments, refactor common…
ckeshava Jan 22, 2026
285f487
update the error message in the error case of VaultDepositors
ckeshava Jan 22, 2026
a6c8d9d
feat: Add currency toggle switch at the top of the page
ckeshava Jan 22, 2026
717a2db
add unit tests; update the default amount repr from -- to 0
ckeshava Jan 22, 2026
4b8cada
remove debug console.log messages
ckeshava Jan 23, 2026
d28be92
Merge remote-tracking branch 'origin/staging' into lpObjectPage-draft1
ckeshava Jan 26, 2026
aed4729
fix: recitfy linter errors
ckeshava Jan 26, 2026
47a03ad
fix flaky unit tests; fix type mis-match errors; remove duplicate tra…
ckeshava Jan 26, 2026
f974cae
[trivial] fix prettier code style
ckeshava Jan 26, 2026
83afcf6
feat: Update the behavior of the CurrencyToggle button; Fetch prices …
ckeshava Jan 26, 2026
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
26,597 changes: 5,845 additions & 20,752 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"ts-jest": "^29.4.1",
"ts-node": "^10.9.2",
"typescript": "^4.9.5",
"xrpl": "^4.4.0"
"xrpl": "^4.5.0"
},
"resolutions": {
"jest-environment-jsdom": "29.3.1",
Expand Down
50 changes: 49 additions & 1 deletion public/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"transaction_legend_toggle_show": "Show Legend",
"transactions.date_header": "Date/time (UTC)",
"no_transactions_message": "No transactions found.",
"get_vault_transactions_failed": "Unable to load vault transactions at this time.",
"retry_action": "Retry...",
"uh_oh": "UH-OH!",
"not_found_default_title": "Page Not Found",
Expand Down Expand Up @@ -635,6 +636,7 @@
"withdrawal_policy": "Withdrawal Policy",
"account_creates_vault": "<Account/> created a vault for <Asset/>",
"vault_id": "Vault ID",
"yield_pool": "Yield Pool",
"loan_broker_id": "Loan Broker ID",
"loan_id": "Loan ID",
"management_fee_rate": "Management Fee Rate",
Expand All @@ -650,6 +652,7 @@
"loan_service_fee": "Loan Service Fee",
"late_payment_fee": "Late Payment Fee",
"close_payment_fee": "Close Payment Fee",
"full_payment_fee": "Full Payment Fee",
"overpayment_fee": "Overpayment Fee",
"interest_rate": "Interest Rate",
"late_interest_rate": "Late Interest Rate",
Expand Down Expand Up @@ -803,5 +806,50 @@
"loan_fees_detail": "<LoanOriginationFee/><LoanServiceFee/>",
"loan_terms_detail": "<PaymentTotal/><PaymentInterval/><GracePeriod/>",
"no_limit": "No Limit",
"first_loss_capital": "first-loss capital"
"first_loss_capital": "first-loss capital",
"vault": "Vault",
"vault_not_found": "Vault not found",
"invalid_vault_id": "Invalid vault ID",
"check_vault_id": "Please check the vault ID",
"get_vault_failed": "Unable to load vault information at this time. Please try again later.",
"private_vault": "Private Vault",
"perm_domain_id": "Permissioned Domain ID",
"total_value_locked": "Total Value Locked (TVL)",
"shares": "Shares",
"assets_available": "Assets Available",
"unrealized_loss": "Unrealized Loss",
"other_data": "Other Data",
"max_total_supply": "Max Total Supply",
"available_to_borrow": "Available to Borrow",
"not_available": "Not available",
"first_come_first_served": "First Come First Served",
"loans": "Loans",
"loan_broker": "Loan Broker",
"total_debt": "Total Debt",
"maximum_debt": "Maximum Debt",
"management_fee": "Management Fee",
"borrower": "Borrower",
"amount_requested": "Amount Requested",
"outstanding_balance": "Outstanding Balance",
"loan_status_current": "Current",
"loan_status_default": "Default",
"all_loans": "All Loans",
"loan_status_impaired": "Impaired",
"loan_status_paid_off": "Paid Off",
"next_due_date": "Next Due Date",
"origination_date": "Origination Date",
"frequency": "Frequency",
"installments": "Installments",
"prepayment_fee": "Prepayment Fee",
"no_loans_message": "No loans found for this broker.",
"loan_default_detected": "Loan default detected in this broker.",
"no_loan_brokers_message": "No loan brokers have been set up for this vault.",
"depositors_fetch_error": "Unable to fetch depositors information from Clio node at this time.",
"no_depositors_message": "No depositors found for this vault.",
"depositors": "Depositors",
"percent_of_supply": "% of Supply",
"value": "Value",
"currency_toggle_help": "Toggle to view values in XRP or USD",
"currency_toggle_loading": "Loading USD conversion rate...",
"currency_toggle_unavailable": "USD conversion not available for this token"
}
3 changes: 3 additions & 0 deletions src/containers/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
AMENDMENTS_ROUTE,
AMENDMENT_ROUTE,
MPT_ROUTE,
VAULT_ROUTE,
NODES_ROUTE,
VALIDATORS_ROUTE,
UPGRADE_STATUS_ROUTE,
Expand All @@ -46,6 +47,7 @@ import { Validators } from '../Network/Validators'
import { UpgradeStatus } from '../Network/UpgradeStatus'
import { Tokens } from '../Tokens'
import { TokenNonMain } from '../TokenNonMain'
import { Vault } from '../Vault'

export const AppWrapper = () => {
const mode = process.env.VITE_ENVIRONMENT
Expand Down Expand Up @@ -82,6 +84,7 @@ export const AppWrapper = () => {
[NFT_ROUTE, NFT],
[AMENDMENT_ROUTE, Amendment],
[MPT_ROUTE, MPT],
[VAULT_ROUTE, Vault],
]

const redirect = legacyRedirect(basename, location)
Expand Down
6 changes: 6 additions & 0 deletions src/containers/App/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,9 @@ export const MPT_ROUTE: RouteDefinition<{
}> = {
path: '/mpt/:id',
}

export const VAULT_ROUTE: RouteDefinition<{
id: string
}> = {
path: '/vault/:id',
}
72 changes: 72 additions & 0 deletions src/containers/Vault/CurrencyToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useTranslation } from 'react-i18next'
import { useTooltip } from '../shared/components/Tooltip'
import './styles.scss'

export type DisplayCurrency = 'xrp' | 'usd'

interface Props {
nativeCurrency: string // e.g., "XRP" or "RLUSD"
selected: DisplayCurrency
onToggle: (currency: DisplayCurrency) => void
usdDisabled?: boolean // Disable USD option when no price is available
usdLoading?: boolean // Show loading state while fetching price
}

export const CurrencyToggle = ({
nativeCurrency,
selected,
onToggle,
usdDisabled = false,
usdLoading = false,
}: Props) => {
const { t } = useTranslation()
const { showTooltip, hideTooltip } = useTooltip()

const handleUsdClick = () => {
if (!usdDisabled && !usdLoading) {
onToggle('usd')
}
}

const getUsdTooltip = () => {
if (usdLoading) return t('currency_toggle_loading')
if (usdDisabled) return t('currency_toggle_unavailable')
return ''
}

return (
<div className="currency-toggle">
<button
type="button"
className={`toggle-option ${selected === 'xrp' ? 'active' : ''}`}
onClick={() => onToggle('xrp')}
>
{nativeCurrency}
</button>
<span
className="toggle-option-wrapper"
onMouseEnter={(e) => {
const tooltip = getUsdTooltip()
if (tooltip) showTooltip('text', e, tooltip)
}}
onMouseLeave={hideTooltip}
>
<button
type="button"
className={`toggle-option ${selected === 'usd' ? 'active' : ''} ${usdDisabled || usdLoading ? 'disabled' : ''}`}
onClick={handleUsdClick}
disabled={usdDisabled || usdLoading}
>
{usdLoading ? '...' : 'USD'}
</button>
</span>
<span
className="toggle-help"
onMouseEnter={(e) => showTooltip('text', e, t('currency_toggle_help'))}
onMouseLeave={hideTooltip}
>
?
</span>
</div>
)
}
112 changes: 112 additions & 0 deletions src/containers/Vault/VaultDepositors/DepositorTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useTranslation } from 'react-i18next'
import { Account } from '../../shared/components/Account'
import { useLanguage } from '../../shared/hooks'
import { useTokenToUSDRate } from '../../shared/hooks/useTokenToUSDRate'
import { localizeNumber } from '../../shared/utils'
import { DisplayCurrency } from '../CurrencyToggle'
import { formatCompactNumber } from '../utils'

interface Holder {
account: string
mpt_amount: string
}

interface AssetInfo {
currency: string
issuer?: string
mpt_issuance_id?: string
}

interface Props {
holders: Holder[]
totalSupply: string | undefined
assetsTotal: string | undefined
startRank: number
displayCurrency: DisplayCurrency
asset?: AssetInfo
}

export const DepositorTable = ({
holders,
totalSupply,
assetsTotal,
startRank,
displayCurrency,
asset,
}: Props) => {
const { t } = useTranslation()
const language = useLanguage()
const { rate: tokenToUsdRate } = useTokenToUSDRate(asset)

const totalSupplyNum = totalSupply ? Number(totalSupply) : 0

// Get the display currency label
const getDisplayCurrencyLabel = (): string =>
displayCurrency === 'usd' ? 'USD' : asset?.currency || ''

// Calculate the value of each holder's share
// Value = (holder's tokens / total supply) * total assets in vault
const calculateValue = (holderAmount: string): string => {
const amount = Number(holderAmount)
if (!totalSupplyNum || !assetsTotal) return '-'
const assetsTotalNum = Number(assetsTotal)
if (Number.isNaN(assetsTotalNum) || assetsTotalNum === 0) return '-'

// Proportional value: (holder tokens / total supply) * total assets
let value = (amount / totalSupplyNum) * assetsTotalNum

// Convert to USD if needed
if (displayCurrency === 'usd') {
if (tokenToUsdRate > 0) {
value *= tokenToUsdRate
return formatCompactNumber(value, language, {
prefix: '$',
currency: 'USD',
})
}
return '--'
}

return formatCompactNumber(value, language, {
currency: getDisplayCurrencyLabel(),
})
}

const calculatePercentOfSupply = (holderAmount: string): string => {
if (!totalSupplyNum) return '-'
const amount = Number(holderAmount)
const percent = (amount / totalSupplyNum) * 100
return `${localizeNumber(percent, language, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`
}

return (
<table className="depositor-table">
<thead>
<tr>
<th>{t('rank')}</th>
<th>{t('account')}</th>
<th>{t('lp_tokens')}</th>
<th>{t('percent_of_supply')}</th>
<th>{t('value')}</th>
</tr>
</thead>
<tbody>
{holders.map((holder, index) => (
<tr key={holder.account}>
<td className="rank-cell">{startRank + index}</td>
<td className="account-cell">
<Account account={holder.account} />
</td>
<td className="tokens-cell">
{formatCompactNumber(holder.mpt_amount, language)}
</td>
<td className="percent-cell">
{calculatePercentOfSupply(holder.mpt_amount)}
</td>
<td className="value-cell">{calculateValue(holder.mpt_amount)}</td>
</tr>
))}
</tbody>
</table>
)
}
Loading
Loading