diff --git a/centrifuge-app/.env-config/.env.ff-prod b/centrifuge-app/.env-config/.env.ff-prod index dd1ee7b41..08482b5a5 100644 --- a/centrifuge-app/.env-config/.env.ff-prod +++ b/centrifuge-app/.env-config/.env.ff-prod @@ -8,7 +8,7 @@ REACT_APP_ONBOARDING_API_URL=https://europe-central2-centrifuge-production-x.clo REACT_APP_PINNING_API_URL=https://europe-central2-centrifuge-production-x.cloudfunctions.net/pinning-api-production REACT_APP_POOL_CREATION_TYPE=propose REACT_APP_RELAY_WSS_URL=wss://rpc.polkadot.io -REACT_APP_SUBQUERY_URL=https://api.subquery.network/sq/centrifuge/pools-multichain +REACT_APP_SUBQUERY_URL=https://api.centrifuge.io REACT_APP_SUBSCAN_URL=https://centrifuge.subscan.io REACT_APP_TINLAKE_NETWORK=mainnet REACT_APP_INFURA_KEY=8ed99a9a115349bbbc01dcf3a24edc96 diff --git a/centrifuge-app/.env-config/.env.production b/centrifuge-app/.env-config/.env.production index 87db55142..7dadec3d9 100644 --- a/centrifuge-app/.env-config/.env.production +++ b/centrifuge-app/.env-config/.env.production @@ -8,7 +8,7 @@ REACT_APP_ONBOARDING_API_URL=https://europe-central2-centrifuge-production-x.clo REACT_APP_PINNING_API_URL=https://europe-central2-centrifuge-production-x.cloudfunctions.net/pinning-api-production REACT_APP_POOL_CREATION_TYPE=propose REACT_APP_RELAY_WSS_URL=wss://rpc.polkadot.io -REACT_APP_SUBQUERY_URL=https://api.subquery.network/sq/centrifuge/pools-multichain +REACT_APP_SUBQUERY_URL=https://api.centrifuge.io REACT_APP_SUBSCAN_URL=https://centrifuge.subscan.io REACT_APP_TINLAKE_NETWORK=mainnet REACT_APP_INFURA_KEY=8ed99a9a115349bbbc01dcf3a24edc96 diff --git a/centrifuge-app/src/pages/Loan/ExternalFinanceForm.tsx b/centrifuge-app/src/pages/Loan/ExternalFinanceForm.tsx index db070c8f5..1339acfc8 100644 --- a/centrifuge-app/src/pages/Loan/ExternalFinanceForm.tsx +++ b/centrifuge-app/src/pages/Loan/ExternalFinanceForm.tsx @@ -109,7 +109,12 @@ export function ExternalFinanceForm({ loan, source }: { loan: ExternalLoan; sour const maxAvailable = source === 'reserve' ? pool.reserve.available.toDecimal() : sourceLoan.outstandingDebt.toDecimal() - const withdraw = useWithdraw(loan.poolId, account!, totalFinance) + const withdraw = useWithdraw(loan.poolId, account!, totalFinance, source) + + React.useEffect(() => { + financeForm.validateForm() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [source]) if (loan.status === 'Closed' || ('valuationMethod' in loan.pricing && loan.pricing.valuationMethod !== 'oracle')) { return null diff --git a/centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx b/centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx index 975e2bd86..18e8b025e 100644 --- a/centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx +++ b/centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx @@ -144,6 +144,11 @@ export function ExternalRepayForm({ loan, destination }: { loan: ExternalLoan; d validateOnMount: true, }) + React.useEffect(() => { + repayForm.validateForm() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [destination]) + const repayFormRef = React.useRef(null) useFocusInvalidInput(repayForm, repayFormRef) diff --git a/centrifuge-app/src/pages/Loan/FinanceForm.tsx b/centrifuge-app/src/pages/Loan/FinanceForm.tsx index ee8aa9854..d9f6ad982 100644 --- a/centrifuge-app/src/pages/Loan/FinanceForm.tsx +++ b/centrifuge-app/src/pages/Loan/FinanceForm.tsx @@ -161,10 +161,15 @@ function InternalFinanceForm({ loan, source }: { loan: LoanType; source: string validateOnMount: true, }) + React.useEffect(() => { + financeForm.validateForm() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [source]) + const financeFormRef = React.useRef(null) useFocusInvalidInput(financeForm, financeFormRef) - const withdraw = useWithdraw(loan.poolId, account!, Dec(financeForm.values.principal || 0)) + const withdraw = useWithdraw(loan.poolId, account!, Dec(financeForm.values.principal || 0), source) if (loan.status === 'Closed') { return null @@ -455,7 +460,7 @@ function Mux({ ) } -export function useWithdraw(poolId: string, borrower: CombinedSubstrateAccount, amount: Decimal) { +export function useWithdraw(poolId: string, borrower: CombinedSubstrateAccount, amount: Decimal, source: string) { const pool = usePool(poolId) const isLocalAsset = typeof pool.currency.key !== 'string' && 'LocalAsset' in pool.currency.key const access = usePoolAccess(poolId) @@ -467,13 +472,31 @@ export function useWithdraw(poolId: string, borrower: CombinedSubstrateAccount, const ao = access.assetOriginators.find((a) => a.address === borrower.actingAddress) const withdrawAddresses = ao?.transferAllowlist ?? [] - if (!isLocalAsset || !withdrawAddresses.length) { - if (!withdrawAddresses.length) - return { - render: () => null, - isValid: true, - getBatch: () => of([]), - } + const sortedBalances = sortBalances(muxBalances?.currencies || [], pool.currency) + const ignoredCurrencies = Object.entries(selectedAddressIndexByCurrency).flatMap(([key, index]) => { + return index === -1 ? [key] : [] + }) + const { buckets: withdrawAmounts } = muxBalances?.currencies + ? divideBetweenCurrencies(amount, sortedBalances, withdrawAddresses, ignoredCurrencies) + : { buckets: [] } + + const totalAvailable = withdrawAmounts.reduce((acc, cur) => acc.add(cur.amount), Dec(0)) + + React.useEffect(() => { + if (withdrawAddresses.length > 0 && sortedBalances.length > 0) { + const initialSelectedAddresses: Record = {} + sortedBalances.forEach((balance) => { + const currencyKey = currencyToString(balance.currency.key) + if (!(currencyKey in initialSelectedAddresses)) { + initialSelectedAddresses[currencyKey] = 0 + } + }) + setSelectedAddressIndexByCurrency(initialSelectedAddresses) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [withdrawAddresses]) + + if (!isLocalAsset) { return { render: () => , isValid: true, @@ -498,16 +521,6 @@ export function useWithdraw(poolId: string, borrower: CombinedSubstrateAccount, } } - const sortedBalances = sortBalances(muxBalances?.currencies || [], pool.currency) - const ignoredCurrencies = Object.entries(selectedAddressIndexByCurrency).flatMap(([key, index]) => { - return index === -1 ? [key] : [] - }) - const { buckets: withdrawAmounts } = muxBalances?.currencies - ? divideBetweenCurrencies(amount, sortedBalances, withdrawAddresses, ignoredCurrencies) - : { buckets: [] } - - const totalAvailable = withdrawAmounts.reduce((acc, cur) => acc.add(cur.amount), Dec(0)) - return { render: () => ( ), - isValid: amount.lte(totalAvailable), + isValid: (_: { values: Pick }) => { + const withdrawalAddresses = Object.values(selectedAddressIndexByCurrency).filter((index) => index !== -1) + return source === 'reserve' ? amount.lte(totalAvailable) && !!withdrawalAddresses.length : true + }, getBatch: () => { + const withdrawalAddresses = Object.values(selectedAddressIndexByCurrency).filter((index) => index !== -1) + if (!withdrawalAddresses.length) return of([]) return combineLatest( withdrawAmounts.flatMap((bucket) => { const index = selectedAddressIndexByCurrency[bucket.currencyKey] ?? 0 diff --git a/centrifuge-app/src/pages/Loan/RepayForm.tsx b/centrifuge-app/src/pages/Loan/RepayForm.tsx index ed38e275c..950c74896 100644 --- a/centrifuge-app/src/pages/Loan/RepayForm.tsx +++ b/centrifuge-app/src/pages/Loan/RepayForm.tsx @@ -179,6 +179,11 @@ function InternalRepayForm({ loan, destination }: { loan: ActiveLoan | CreatedLo validateOnMount: true, }) + React.useEffect(() => { + repayForm.validateForm() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [destination]) + const repayFormRef = React.useRef(null) useFocusInvalidInput(repayForm, repayFormRef) diff --git a/centrifuge-app/src/pages/NavManagement/NavManagementAssetTable.tsx b/centrifuge-app/src/pages/NavManagement/NavManagementAssetTable.tsx index 95e406211..04596cd21 100644 --- a/centrifuge-app/src/pages/NavManagement/NavManagementAssetTable.tsx +++ b/centrifuge-app/src/pages/NavManagement/NavManagementAssetTable.tsx @@ -19,7 +19,7 @@ import { switchMap } from 'rxjs' import daiLogo from '../../assets/images/dai-logo.svg' import usdcLogo from '../../assets/images/usdc-logo.svg' import { ButtonGroup } from '../../components/ButtonGroup' -import { DataCol, DataRow, DataTable } from '../../components/DataTable' +import { DataTable } from '../../components/DataTable' import { LayoutSection } from '../../components/LayoutBase/LayoutSection' import { AssetName } from '../../components/LoanList' import { RouterTextLink } from '../../components/TextLink' @@ -371,29 +371,7 @@ export function NavManagementAssetTable({ poolId }: { poolId: string }) { ) } > - - - - Total - - - - - - - {isEditing && } - - - {formatBalance(newNav, pool.currency.symbol)} - - - - } - /> + diff --git a/centrifuge-app/src/pages/NavManagement/Overview.tsx b/centrifuge-app/src/pages/NavManagement/Overview.tsx index 586a10fff..385efb4d0 100644 --- a/centrifuge-app/src/pages/NavManagement/Overview.tsx +++ b/centrifuge-app/src/pages/NavManagement/Overview.tsx @@ -110,7 +110,11 @@ export function NavOverviewCard({ poolId }: { poolId: string }) { current={pool.nav.total.toFloat()} change={changeInValuation ? new CurrencyBalance(changeInValuation, pool.currency.decimals).toFloat() : 0} pendingFees={pendingFees.toFloat()} - pendingNav={pool.nav.total.toFloat() - pendingFees.toFloat()} + pendingNav={new CurrencyBalance(changeInValuation, pool.currency.decimals) + .toDecimal() + .add(pool.nav.total.toDecimal()) + .sub(pendingFees.toDecimal()) + .toNumber()} /> ) }