Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Centrifuge App: Liquidity pools investments #1542

Merged
merged 69 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
d91787f
lp module
onnovisser Aug 18, 2023
3a029c7
collect
onnovisser Aug 18, 2023
02f22fb
invest redeem provider
onnovisser Aug 22, 2023
c7b09ad
add domain deploy table
onnovisser Aug 22, 2023
2f5cb43
mint
onnovisser Aug 23, 2023
5c0bf82
redeem
onnovisser Aug 24, 2023
da457b8
reduce abi
onnovisser Aug 24, 2023
725c194
Merge commit 'e5beac5a96e37fc649d00b3ac09f681e6199fddb' into liquidit…
onnovisser Aug 25, 2023
fa41c78
fix dialog networks
onnovisser Aug 25, 2023
07ca9eb
cancel order
onnovisser Aug 25, 2023
b2d9bd4
remove logs
onnovisser Aug 25, 2023
8711ca4
cleanup
onnovisser Aug 25, 2023
f6ff35e
fix allowance
onnovisser Aug 29, 2023
148b004
permit
onnovisser Aug 29, 2023
a0991ce
abi
onnovisser Aug 29, 2023
1a23c4c
fix permit tx
onnovisser Aug 30, 2023
6aee9d7
tranche token permit
onnovisser Aug 30, 2023
fa17027
deposit events
onnovisser Aug 31, 2023
83e503a
get liquidity pools refactor
onnovisser Aug 31, 2023
17cdf52
add allow currency to batch
onnovisser Sep 4, 2023
9fa64a8
fix select
onnovisser Sep 7, 2023
a7a0c9c
Merge branch 'main' into liquidity-pools-investments
onnovisser Sep 7, 2023
61b648f
account picker
onnovisser Sep 7, 2023
8995946
foreign asset fix
onnovisser Sep 12, 2023
e705612
merge main
onnovisser Sep 12, 2023
6799e43
picker fix
onnovisser Sep 12, 2023
198b9e6
swap orders
onnovisser Sep 12, 2023
d4b8091
foreign asset fix
onnovisser Sep 12, 2023
d30957d
swap page
onnovisser Sep 12, 2023
0c13ef9
currency meta
onnovisser Sep 12, 2023
2adde1d
fix
onnovisser Sep 12, 2023
a955e44
api changes
onnovisser Sep 14, 2023
629f7ef
merge main
onnovisser Sep 15, 2023
8c67bd9
merge main
onnovisser Sep 15, 2023
39d711c
get currency ids
onnovisser Sep 16, 2023
5be33b4
remove mocked data
onnovisser Sep 16, 2023
59cc554
fix evm conversion
onnovisser Sep 18, 2023
33ed728
fix loading
onnovisser Sep 18, 2023
71781df
fix validation
onnovisser Sep 18, 2023
f82315c
pending order
onnovisser Sep 18, 2023
8a02615
disable cancel
onnovisser Sep 18, 2023
697e68f
point to moonbase
onnovisser Sep 18, 2023
34437d9
update base logo
onnovisser Sep 18, 2023
4ddcc25
fix empty pool loans
onnovisser Sep 18, 2023
77d5adc
withdraw button
onnovisser Sep 18, 2023
1a004a2
add arbitrum
onnovisser Sep 18, 2023
9d40002
arbitrum debug flag
onnovisser Sep 18, 2023
703adbb
remove withdraw
onnovisser Sep 18, 2023
fae38b3
merge swaps
onnovisser Sep 18, 2023
b5cf966
fix chain id
onnovisser Sep 18, 2023
a754421
small fixes
onnovisser Sep 18, 2023
36e684b
account conversion
onnovisser Sep 21, 2023
66d6f5d
typo
onnovisser Sep 21, 2023
abce46e
dialog
onnovisser Sep 21, 2023
7988cd9
update domain member
onnovisser Sep 26, 2023
fc418f7
fix update member
onnovisser Sep 26, 2023
3a2ce95
fix update member
onnovisser Sep 26, 2023
f857b6c
deploy pools
onnovisser Sep 29, 2023
8884a1b
deploy tranche/lps
onnovisser Sep 29, 2023
4fdef08
add currency
onnovisser Oct 2, 2023
e396c36
merge main
onnovisser Oct 2, 2023
344b1fe
merge main
onnovisser Oct 4, 2023
5b611a0
Merge commit 'f19d396d1078ae1965bc309181fd0f565679b962' into liquidit…
onnovisser Oct 25, 2023
95df0e2
remove debug
onnovisser Oct 25, 2023
9c8bc0b
contract changes
onnovisser Oct 25, 2023
a94748e
contract changes
onnovisser Oct 27, 2023
1d545b2
permit amount
onnovisser Oct 27, 2023
702f28e
cancel order
onnovisser Oct 30, 2023
151a886
Merge branch 'main' into liquidity-pools-investments
onnovisser Oct 30, 2023
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
289 changes: 144 additions & 145 deletions centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function InvestRedeemCentrifugeProvider({ 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 { state: liquidityState } = useLiquidityRewards()
const liquidityState = useLiquidityRewards().state

if (!tranche) throw new Error(`Token not found. Pool id: ${poolId}, token id: ${trancheId}`)

Expand Down Expand Up @@ -95,7 +95,7 @@ export function InvestRedeemCentrifugeProvider({ poolId, trancheId, children }:
).toDecimal(),
nativeBalance: balances?.native.balance.toDecimal() ?? Dec(0),
poolCurrencyBalance: poolCurBalance,
poolCUrrencyBalanceWithPending: poolCurBalanceCombined,
poolCurrencyBalanceWithPending: poolCurBalanceCombined,
trancheBalance,
trancheBalanceWithPending: combinedBalance,
investmentValue,
Expand All @@ -115,6 +115,7 @@ export function InvestRedeemCentrifugeProvider({ poolId, trancheId, children }:
needsToCollectBeforeOrder: false,
needsPoolCurrencyApproval: false,
needsTrancheTokenApproval: false,
canChangeOrder: true,
pendingAction,
pendingTransaction: pendingAction && txActions[pendingAction]?.lastCreatedTransaction,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { CurrencyBalance, Pool } from '@centrifuge/centrifuge-js'
import { useEvmNativeBalance, useEvmNativeCurrency } from '@centrifuge/centrifuge-react'
import { TransactionRequest } from '@ethersproject/providers'
import BN from 'bn.js'
import * as React from 'react'
import { Dec } from '../../utils/Decimal'
import { useEvmTransaction } from '../../utils/tinlake/useEvmTransaction'
import { useAddress } from '../../utils/useAddress'
import { useLiquidityPoolInvestment, useLiquidityPools } from '../../utils/useLiquidityPools'
import { usePendingCollect, usePool, usePoolMetadata } from '../../utils/usePools'
import { InvestRedeemContext } from './InvestRedeemProvider'
import { InvestRedeemAction, InvestRedeemActions, InvestRedeemProviderProps as Props, InvestRedeemState } from './types'

export function InvestRedeemLiquidityPoolsProvider({ poolId, trancheId, children }: Props) {
const centAddress = useAddress('substrate')
const evmAddress = useAddress('evm')
const { data: evmNativeBalance } = useEvmNativeBalance(evmAddress)
const evmNativeCurrency = useEvmNativeCurrency()
const order = usePendingCollect(poolId, trancheId, centAddress)
const pool = usePool(poolId) as Pool
const [pendingAction, setPendingAction] = React.useState<InvestRedeemAction>()
const { isLoading: isLpsLoading } = useLiquidityPools(poolId, trancheId)
const {
data: lpInvest,
refetch: refetchInvest,
isLoading: isInvestmentLoading,
} = useLiquidityPoolInvestment(poolId, trancheId)
const isAllowedToInvest = lpInvest?.isAllowedToInvest
const tranche = pool.tranches.find((t) => t.id === trancheId)
const { data: metadata, isLoading: isMetadataLoading } = usePoolMetadata(pool)
const trancheMeta = metadata?.tranches?.[trancheId]

if (!tranche) throw new Error(`Token not found. Pool id: ${poolId}, token id: ${trancheId}`)

const trancheBalance = lpInvest?.tokenBalance?.toDecimal() ?? Dec(0)

const price = lpInvest?.tokenPrice?.toDecimal() ?? Dec(1)
const investToCollect = lpInvest?.maxMint.toDecimal() ?? Dec(0)
const currencyToCollect = lpInvest?.maxWithdraw.toDecimal() ?? Dec(0)
const pendingRedeem = order?.remainingRedeemToken.toDecimal() ?? Dec(0)
const combinedTrancheBalance = trancheBalance.add(investToCollect).add(pendingRedeem)
const investmentValue = combinedTrancheBalance.mul(price)
const poolCurBalance = lpInvest?.currencyBalance.toDecimal() ?? Dec(0)
const poolCurBalanceCombined = poolCurBalance
.add(currencyToCollect)
.add(order?.remainingInvestCurrency.toDecimal() ?? 0)

const isCalculatingOrders = pool.epoch.status !== 'ongoing'

const collectType = investToCollect.gt(0) ? 'invest' : currencyToCollect.gt(0) ? 'redeem' : null

const invest = useEvmTransaction('Invest', (cent) => cent.liquidityPools.updateInvestOrder)
const redeem = useEvmTransaction('Redeem', (cent) => cent.liquidityPools.updateRedeemOrder)
const collectInvest = useEvmTransaction('Collect', (cent) => cent.liquidityPools.mint)
const collectRedeem = useEvmTransaction('Collect', (cent) => cent.liquidityPools.withdraw)
const approve = useEvmTransaction('Approve', (cent) => cent.liquidityPools.approveManagerForCurrency)
const cancelInvest = useEvmTransaction('Cancel order', (cent) => cent.liquidityPools.updateInvestOrder)
const cancelRedeem = useEvmTransaction('Cancel order', (cent) => cent.liquidityPools.updateRedeemOrder)

const txActions = {
invest,
redeem,
collect: collectType === 'invest' ? collectInvest : collectRedeem,
approvePoolCurrency: approve,
approveTrancheToken: approve,
cancelInvest,
cancelRedeem,
}
const pendingTransaction = pendingAction && txActions[pendingAction]?.lastCreatedTransaction

function doAction<T = any>(
name: InvestRedeemAction,
fn: (arg: T) => any[],
opt?: TransactionRequest
): (args?: T) => void {
return (args) => {
txActions[name]?.execute(fn(args!) as any, opt)
setPendingAction(name)
}
}

React.useEffect(() => {
if (pendingAction && pendingTransaction?.status === 'succeeded') {
refetchInvest()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pendingTransaction?.status])

function useActionSucceeded(cb: (action: InvestRedeemAction) => void) {
React.useEffect(() => {
if (pendingAction && pendingTransaction?.status === 'succeeded') {
cb(pendingAction)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pendingTransaction?.status])
}

const state: InvestRedeemState = {
poolId,
trancheId,
isDataLoading: isLpsLoading || isInvestmentLoading || isMetadataLoading,
isAllowedToInvest,
isPoolBusy: isCalculatingOrders,
isFirstInvestment: order?.submittedAt === 0 && order.investCurrency.isZero(),
nativeCurrency: evmNativeCurrency,
trancheCurrency: tranche.currency,
poolCurrency: lpInvest && {
decimals: lpInvest.currencyDecimals,
symbol: lpInvest.currencySymbol,
},
capacity: tranche.capacity.toDecimal(),
minInitialInvestment: new CurrencyBalance(
trancheMeta?.minInitialInvestment ?? 0,
pool.currency.decimals
).toDecimal(),
nativeBalance: evmNativeBalance?.toDecimal() ?? Dec(0),
poolCurrencyBalance: poolCurBalance,
poolCurrencyBalanceWithPending: poolCurBalanceCombined,
trancheBalance,
trancheBalanceWithPending: combinedTrancheBalance,
investmentValue,
tokenPrice: price,
order: order
? {
investCurrency: order.investCurrency.toDecimal(),
redeemToken: order.redeemToken.toDecimal(),
payoutCurrencyAmount: order.payoutCurrencyAmount.toDecimal(),
payoutTokenAmount: order.payoutTokenAmount.toDecimal(),
remainingInvestCurrency: order.remainingInvestCurrency.toDecimal(),
remainingRedeemToken: order.remainingRedeemToken.toDecimal(),
}
: null,
collectAmount: investToCollect.gt(0) ? investToCollect : currencyToCollect,
collectType,
needsToCollectBeforeOrder: investToCollect.gt(0) || currencyToCollect.gt(0),
needsPoolCurrencyApproval: lpInvest?.managerCurrencyAllowance.isZero() ?? false,
needsTrancheTokenApproval: lpInvest?.managerTrancheTokenAllowance.isZero() ?? false,
canChangeOrder: false,
pendingAction,
pendingTransaction: pendingAction && txActions[pendingAction]?.lastCreatedTransaction,
}

const actions: InvestRedeemActions = {
invest: doAction('invest', (newOrder: BN) => [lpInvest?.lpAddress, newOrder]),
redeem: doAction('redeem', (newOrder: BN) => [lpInvest?.lpAddress, newOrder]),
collect: doAction('collect', () =>
collectType === 'invest' ? [lpInvest?.lpAddress, lpInvest?.maxMint] : [lpInvest?.lpAddress, lpInvest?.maxWithdraw]
),
approvePoolCurrency: doAction('approvePoolCurrency', () => [lpInvest?.managerAddress, lpInvest?.currencyAddress]),
approveTrancheToken: doAction('approveTrancheToken', () => [lpInvest?.managerAddress, lpInvest?.lpAddress]),
cancelInvest: doAction('cancelInvest', () => [lpInvest?.lpAddress, new BN(0)]),
cancelRedeem: doAction('cancelRedeem', () => [lpInvest?.lpAddress, new BN(0)]),
}

const hooks = {
useActionSucceeded,
}

return <InvestRedeemContext.Provider value={{ state, actions, hooks }}>{children}</InvestRedeemContext.Provider>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useWallet } from '@centrifuge/centrifuge-react'
import * as React from 'react'
import { InvestRedeemCentrifugeProvider } from './InvestRedeemCentrifugeProvider'
import { InvestRedeemLiquidityPoolsProvider } from './InvestRedeemLiquidityPoolsProvider'
import { InvestRedeemTinlakeProvider } from './InvestRedeemTinlakeProvider'
import { InvestRedeemContext as InvestRedeemContextType, InvestRedeemProviderProps as Props } from './types'

Expand All @@ -14,12 +15,12 @@ export function useInvestRedeem() {

export function InvestRedeemProvider(props: Props) {
const isTinlakePool = props.poolId.startsWith('0x')
const { connectedNetwork } = useWallet()
if (connectedNetwork && [1, 5, 8453, 84531].includes(connectedNetwork as any)) {
return null
}

const Comp = isTinlakePool ? InvestRedeemTinlakeProvider : InvestRedeemCentrifugeProvider
const { connectedType, isEvmOnSubstrate } = useWallet()
const Comp = isTinlakePool
? InvestRedeemTinlakeProvider
: connectedType === 'evm' && !isEvmOnSubstrate
? InvestRedeemLiquidityPoolsProvider
: InvestRedeemCentrifugeProvider

return <Comp {...props} />
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,18 @@ export function InvestRedeemTinlakeProvider({ poolId, trancheId, children }: Pro
setPendingAction(name)
}
}
React.useEffect(() => {
if (pendingAction && pendingTransaction?.status === 'succeeded') {
refetchInvestment()
refetchBalance()
refetchBalances()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pendingTransaction?.status])

function useActionSucceeded(cb: (action: InvestRedeemAction) => void) {
React.useEffect(() => {
if (pendingAction && pendingTransaction?.status === 'succeeded') {
refetchInvestment()
refetchBalance()
refetchBalances()
cb(pendingAction)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -102,7 +107,7 @@ export function InvestRedeemTinlakeProvider({ poolId, trancheId, children }: Pro
).toDecimal(),
nativeBalance: nativeBalance?.toDecimal() || Dec(0),
poolCurrencyBalance: poolCurrencyBalance,
poolCUrrencyBalanceWithPending: poolCurrencyBalance.add(disburse?.remainingInvestCurrency || 0),
poolCurrencyBalanceWithPending: poolCurrencyBalance.add(disburse?.remainingInvestCurrency || 0),
trancheBalance,
trancheBalanceWithPending: combinedBalance,
investmentValue,
Expand All @@ -120,6 +125,7 @@ export function InvestRedeemTinlakeProvider({ poolId, trancheId, children }: Pro
needsToCollectBeforeOrder: !collectAmount.isZero(),
needsPoolCurrencyApproval: !!trancheInvestment?.poolCurrencyAllowance.isZero(),
needsTrancheTokenApproval: !!trancheInvestment?.tokenAllowance.isZero(),
canChangeOrder: true,
pendingAction,
pendingTransaction: pendingAction && txActions[pendingAction]?.lastCreatedTransaction,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import css from '@styled-system/css'
import styled from 'styled-components'

export const LightButton = styled.button<{ $left?: boolean; $right?: boolean }>(
export const LightButton = styled.button(
{
display: 'flex',
justifyContent: 'center',
Expand All @@ -14,14 +14,18 @@ export const LightButton = styled.button<{ $left?: boolean; $right?: boolean }>(
(props) =>
css({
color: 'textPrimary',
borderBottomLeftRadius: props.$left ? 'card' : undefined,
borderBottomRightRadius: props.$right ? 'card' : undefined,
backgroundColor: 'secondarySelectedBackground',
'&:hover, &:focus-visible': {
color: 'textSelected',
},
'&:disabled': {
cursor: 'not-allowed',
},
'&:first-child': {
borderBottomLeftRadius: 'card',
},
'&:last-child': {
borderBottomRightRadius: 'card',
},
})
)
3 changes: 2 additions & 1 deletion centrifuge-app/src/components/InvestRedeem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type InvestRedeemState = {
minInitialInvestment: Decimal
nativeBalance: Decimal
poolCurrencyBalance: Decimal
poolCUrrencyBalanceWithPending: Decimal
poolCurrencyBalanceWithPending: Decimal
trancheBalance: Decimal
trancheBalanceWithPending: Decimal
investmentValue: Decimal
Expand All @@ -47,6 +47,7 @@ export type InvestRedeemState = {
needsToCollectBeforeOrder: boolean
needsPoolCurrencyApproval: boolean
needsTrancheTokenApproval: boolean
canChangeOrder: boolean
pendingAction?: InvestRedeemAction | null
pendingTransaction?: Transaction | null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { LiquidityRewardsContext } from './LiquidityRewardsContext'
import { LiquidityRewardsActions, LiquidityRewardsProviderProps, LiquidityRewardsState } from './types'

export function LiquidityRewardsProvider(props: LiquidityRewardsProviderProps) {
// const { connectedType, isEvmOnSubstrate } = useWallet()
const isTinlakePool = props.poolId.startsWith('0x')
return !isTinlakePool ? <Provider {...props} /> : <>{props.children}</>
}

function Provider({ poolId, trancheId, children }: LiquidityRewardsProviderProps) {
const pool = usePool(poolId) as Pool
const consts = useCentrifugeConsts()
const address = useAddress()
const address = useAddress('substrate')
const order = usePendingCollect(poolId, trancheId, address)
const stakes = useAccountStakes(address, poolId, trancheId)
const rewards = useComputeLiquidityRewards(address, poolId, trancheId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Box, Divider, Grid, IconCheckInCircle, Shelf, Stack, Text } from '@cent
import * as React from 'react'
import { millisecondsToDays } from '../../utils/date'
import { formatBalance } from '../../utils/formatting'
import { LightButton } from '../InvestRedeem/LightButton'
import { useActiveEpochData } from './hooks'
import { LightButton } from './LightButton'
import { useLiquidityRewards } from './LiquidityRewardsContext'

export function LiquidityRewardsStaker() {
Expand Down Expand Up @@ -69,19 +69,14 @@ export function LiquidityRewardsStaker() {

<Grid mt="1px" gap="1px" columns={canStake && canUnstake ? 2 : 1}>
{canStake && (
<LightButton onClick={stake} $left $right={!canUnstake} disabled={isLoading.unstake || isLoading.stake}>
<LightButton onClick={stake} disabled={isLoading.unstake || isLoading.stake}>
<Text variant="body2" color="inherit">
Stake
</Text>
</LightButton>
)}
{canUnstake && (
<LightButton
onClick={() => unstake()}
$left={!canStake}
$right
disabled={isLoading.unstake || isLoading.stake}
>
<LightButton onClick={() => unstake()} disabled={isLoading.unstake || isLoading.stake}>
<Text variant="body2" color="inherit">
Unstake
</Text>
Expand Down
1 change: 0 additions & 1 deletion centrifuge-app/src/components/LiquidityRewards/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export function useAccountStakes(address?: string, poolId?: string, trancheId?:
['stakes', address, poolId, trancheId],
(cent) => cent.rewards.getAccountStakes([address!, poolId!, trancheId!]),
{
suspense: true,
enabled: !!address && !!poolId && !!trancheId,
}
)
Expand Down
Loading