From f2e0fa42ac399ffbc498d753e995c2fd820909fb Mon Sep 17 00:00:00 2001 From: Sophia Date: Fri, 6 Sep 2024 12:47:31 -0400 Subject: [PATCH 1/4] LPv2 Centrifuge Router (#2360) * Add router ABI * Initial attempt at using router for invest * Update invest/redeem actions to use centrifuge router contract * Add invest with permit * Refactor cent router usage * Use enableLockDepositRequest instead of enable due to errors in the enable contract * Use centrifuge router address to check for approve amount * Fix cancel investment txs * Update token price in enablePoolOnDomain * Update router ABI * Add gmp status in invest/redeem behind debug flag * Double check inputs for centrifuge router * Fix update token price call * Enable permits and set a 1 hour deadline for ERC2612 Permit * Fix order of mutlicall for investing with permit * Fix return types from investments.investOrders.keys() * Replace spender with centrifuge router for investing with permit * Use alternative to enable since it's not working yet on contract side * Use cent router for requestDeposit * Increase gas limit * Clean up gmp feature * Only show gmp on correct pool and tranche * Clean up * Fix form validation in mux withdrawals * Update token prices when updating NAV and asset prices * Fix lint warnings and error * Clean up useGmp * Fix lint --- .../src/components/DebugFlags/config.ts | 5 + .../components/InvestRedeem/InvestRedeem.tsx | 3 + .../InvestRedeemLiquidityPoolsProvider.tsx | 51 ++- .../IssuerPool/Investors/LiquidityPools.tsx | 12 +- .../src/pages/Loan/ExternalFinanceForm.tsx | 1 + .../src/pages/Loan/ExternalRepayForm.tsx | 1 + centrifuge-app/src/pages/Loan/FinanceForm.tsx | 45 +- centrifuge-app/src/pages/Loan/RepayForm.tsx | 1 + .../NavManagement/NavManagementAssetTable.tsx | 23 +- .../src/utils/tinlake/useEvmTransaction.ts | 16 +- centrifuge-app/src/utils/useGmp.tsx | 126 ++++++ centrifuge-app/src/utils/useLiquidityPools.ts | 2 +- centrifuge-app/src/utils/usePools.ts | 6 +- centrifuge-js/src/modules/liquidityPools.ts | 278 +++++++++--- .../abi/CentrifugeRouter.abi.json | 418 ++++++++++++++++++ .../src/modules/liquidityPools/abi/index.ts | 3 +- 16 files changed, 871 insertions(+), 120 deletions(-) create mode 100644 centrifuge-app/src/utils/useGmp.tsx create mode 100644 centrifuge-js/src/modules/liquidityPools/abi/CentrifugeRouter.abi.json diff --git a/centrifuge-app/src/components/DebugFlags/config.ts b/centrifuge-app/src/components/DebugFlags/config.ts index 2eddcf28f3..6c365bc6d3 100644 --- a/centrifuge-app/src/components/DebugFlags/config.ts +++ b/centrifuge-app/src/components/DebugFlags/config.ts @@ -48,6 +48,7 @@ export type Key = | 'poolCreationType' | 'showTokenYields' | 'showOracleTx' + | 'showGmp' export const flagsConfig = { address: { @@ -129,6 +130,10 @@ export const flagsConfig = { default: false, type: 'checkbox', }, + showGmp: { + default: false, + type: 'checkbox', + }, } satisfies Record export const genericFlagsConfig = flagsConfig as Record diff --git a/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx b/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx index 5d92ce4e79..9412f5a770 100644 --- a/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx +++ b/centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx @@ -20,6 +20,7 @@ import { useTheme } from 'styled-components' import { ethConfig } from '../../config' import { formatBalance } from '../../utils/formatting' import { useAddress } from '../../utils/useAddress' +import { useGmp } from '../../utils/useGmp' import { useActiveDomains } from '../../utils/useLiquidityPools' import { usePool, usePoolMetadata } from '../../utils/usePools' import { LiquidityRewardsContainer } from '../LiquidityRewards/LiquidityRewardsContainer' @@ -88,6 +89,7 @@ type InputProps = { function InvestRedeemInput({ defaultView: defaultViewProp }: InputProps) { const { state } = useInvestRedeem() + const { render: renderGmp } = useGmp() const pool = usePool(state.poolId) let defaultView = defaultViewProp if (state.order && !defaultView) { @@ -101,6 +103,7 @@ function InvestRedeemInput({ defaultView: defaultViewProp }: InputProps) { return ( + {renderGmp(state.poolId, state.trancheId)} () - // | 'investWithPermit' + const [pendingActionState, setPendingAction] = React.useState() const { isLoading: isLpsLoading, data: lps } = useLiquidityPools(poolId, trancheId) const { data: lpInvest, @@ -48,6 +45,7 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children const tranche = pool.tranches.find((t) => t.id === trancheId) const { data: metadata, isLoading: isMetadataLoading } = usePoolMetadata(pool) const trancheMeta = metadata?.tranches?.[trancheId] + const chainId = provider?.network.chainId || 1 if (!tranche) throw new Error(`Token not found. Pool id: ${poolId}, token id: ${trancheId}`) @@ -77,9 +75,9 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children const minOrder = consts.orderBook.minFulfillment.toDecimal() const invest = useEvmTransaction('Invest', (cent) => cent.liquidityPools.increaseInvestOrder) - // const investWithPermit = useEvmTransaction('Invest', (cent) => cent.liquidityPools.increaseInvestOrderWithPermit) + const investWithPermit = useEvmTransaction('Invest', (cent) => cent.liquidityPools.increaseInvestOrderWithPermit) const redeem = useEvmTransaction('Redeem', (cent) => cent.liquidityPools.increaseRedeemOrder) - const collectInvest = useEvmTransaction('Collect', (cent) => cent.liquidityPools.mint) + const collectInvest = useEvmTransaction('Claim', (cent) => cent.liquidityPools.mint) const collectRedeem = useEvmTransaction('Withdraw', (cent) => cent.liquidityPools.withdraw) const approve = useEvmTransaction('Approve', (cent) => cent.liquidityPools.approveForCurrency) const cancelInvest = useEvmTransaction('Cancel order', (cent) => cent.liquidityPools.cancelInvestOrder) @@ -89,7 +87,7 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children const txActions = { invest, - // investWithPermit, + investWithPermit, redeem, collect: collectType === 'invest' @@ -118,10 +116,11 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children function doAction( name: InvestRedeemAction, fn: (arg: T) => any[] | Promise, - opt?: TransactionRequest + opt?: TransactionRequest, + gmp?: { poolId: string; trancheId: string } // enable gmp to display pending Axelar messages ): (args?: T) => void { return (args) => { - txActions[name]?.execute(fn(args!) as any, opt) + txActions[name]?.execute(fn(args!) as any, opt, gmp) setPendingAction(name) } } @@ -151,8 +150,7 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children } }, [lps]) - const supportsPermits = - !PERMITS_DISABLED && lpInvest?.currencySupportsPermit && !isSmartContractWallet && selectedWallet?.id !== 'finoa' + const supportsPermits = lpInvest?.currencySupportsPermit && !isSmartContractWallet && selectedWallet?.id !== 'finoa' const canChangeOrder = false // LP contracts don't support changing orders const state: InvestRedeemState = { @@ -202,8 +200,9 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children : Dec(0), collectType, needsToCollectBeforeOrder: true, - needsPoolCurrencyApproval: (amount) => - lpInvest ? lpInvest.lpCurrencyAllowance.toFloat() < amount && !supportsPermits : false, + needsPoolCurrencyApproval: (amount) => { + return lpInvest ? lpInvest.lpCurrencyAllowance.toFloat() < amount && !supportsPermits : false + }, needsTrancheTokenApproval: () => false, canChangeOrder, canCancelOrder: !(lpInvest?.pendingCancelDepositRequest || lpInvest?.pendingCancelRedeemRequest), @@ -229,35 +228,35 @@ export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children else if (lpInvest.lpCurrencyAllowance.lt(assets) && supportsPermits && pendingAction !== 'approvePoolCurrency') { const signer = provider!.getSigner() const connectedCent = cent.connectEvm(evmAddress!, signer) - const permit = await connectedCent.liquidityPools.signPermit([ - lpInvest.lpAddress, - lpInvest.currency.address, - assets, - ]) + const permit = await connectedCent.liquidityPools.signPermit([lpInvest.currency.address, assets, chainId]) console.log('permit', permit) - // investWithPermit.execute([lpInvest.lpAddress, assets, permit]) - // setPendingAction('investWithPermit') + investWithPermit.execute( + [lpInvest.lpAddress, assets, lpInvest?.currency.address, permit, chainId], + {}, + { poolId, trancheId } + ) + setPendingAction('investWithPermit') } else { - invest.execute([lpInvest.lpAddress, assets]) + invest.execute([lpInvest.lpAddress, assets, chainId], {}, { poolId, trancheId }) setPendingAction('invest') } }, redeem: async (newOrder: BN) => { if (!lpInvest) return - redeem.execute([lpInvest.lpAddress, newOrder]) + redeem.execute([lpInvest.lpAddress, newOrder, chainId], {}, { poolId, trancheId }) setPendingAction('redeem') }, collect: doAction('collect', () => - collectType === 'invest' ? [lpInvest?.lpAddress, lpInvest?.maxMint] : [lpInvest?.lpAddress, lpInvest?.maxWithdraw] + collectType === 'invest' ? [lpInvest?.lpAddress, chainId] : [lpInvest?.lpAddress, chainId] ), approvePoolCurrency: doAction('approvePoolCurrency', (amount) => [ - lpInvest?.lpAddress, lpInvest?.currency.address, amount.toString(), + chainId, ]), approveTrancheToken: () => {}, - cancelInvest: doAction('cancelInvest', () => [lpInvest?.lpAddress]), - cancelRedeem: doAction('cancelRedeem', () => [lpInvest?.lpAddress]), + cancelInvest: doAction('cancelInvest', () => [lpInvest?.lpAddress, chainId], undefined, { poolId, trancheId }), + cancelRedeem: doAction('cancelRedeem', () => [lpInvest?.lpAddress, chainId], undefined, { poolId, trancheId }), selectPoolCurrency(symbol) { setLpIndex(lps!.findIndex((lp) => lp.currency.symbol === symbol)) }, diff --git a/centrifuge-app/src/pages/IssuerPool/Investors/LiquidityPools.tsx b/centrifuge-app/src/pages/IssuerPool/Investors/LiquidityPools.tsx index c3bef66568..40349edf3c 100644 --- a/centrifuge-app/src/pages/IssuerPool/Investors/LiquidityPools.tsx +++ b/centrifuge-app/src/pages/IssuerPool/Investors/LiquidityPools.tsx @@ -175,7 +175,7 @@ function PoolDomain({ poolId, domain, refetch }: { poolId: string; domain: Domai ) : ( pool.tranches.map((tranche) => ( - +