Skip to content

Commit

Permalink
epoch changes
Browse files Browse the repository at this point in the history
  • Loading branch information
onnovisser committed Oct 6, 2023
1 parent a4b2652 commit ccca53d
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 156 deletions.
18 changes: 8 additions & 10 deletions centrifuge-app/src/components/PoolChangesBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,23 @@ import { RouterTextLink } from './TextLink'
export type PoolChangesBannerProps = {
poolId: string
}
const STORAGE_KEY = 'poolChangesBannerDismissedAt'
const STORAGE_KEY = 'poolChangesBannerDismissed'

export function PoolChangesBanner({ poolId }: PoolChangesBannerProps) {
const changes = usePoolChanges(poolId)
const loanChanges = useLoanChanges(poolId)
const poolChanges = usePoolChanges(poolId)
const { policyChanges } = useLoanChanges(poolId)
const [isOpen, setIsOpen] = React.useState(false)

React.useEffect(() => {
const dismissedAt = new Date(localStorage.getItem(STORAGE_KEY) ?? 0)
if (
(changes && new Date(changes.submittedAt) > dismissedAt) ||
(loanChanges?.length && new Date(loanChanges.at(-1)!.submittedAt) > dismissedAt)
) {
const dismissed = !!sessionStorage.getItem(STORAGE_KEY)
const hasReady = policyChanges?.some((change) => change.status === 'ready') || poolChanges?.status === 'ready'
if (!dismissed && hasReady) {
setIsOpen(true)
}
}, [changes, loanChanges])
}, [poolChanges, policyChanges])

function onClose() {
localStorage.setItem(STORAGE_KEY, new Date(Date.now()).toISOString())
sessionStorage.setItem(STORAGE_KEY, '1')
setIsOpen(false)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CurrencyBalance, Perquintill, PoolMetadata, PoolMetadataInput, Rate } from '@centrifuge/centrifuge-js'
import { useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Button, Grid, NumberInput, Shelf, Stack, Text, Thumbnail } from '@centrifuge/fabric'
import { useCentrifugeConsts, useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Button, Grid, NumberInput, Shelf, Stack, StatusChip, Text, Thumbnail } from '@centrifuge/fabric'
import { Form, FormikProvider, useFormik } from 'formik'
import * as React from 'react'
import { useParams } from 'react-router'
Expand All @@ -12,7 +12,7 @@ import { LabelValueStack } from '../../../components/LabelValueStack'
import { PageSection } from '../../../components/PageSection'
import { formatBalance, formatPercentage } from '../../../utils/formatting'
import { useSuitableAccounts } from '../../../utils/usePermissions'
import { useConstants, usePool, usePoolChanges, usePoolMetadata } from '../../../utils/usePools'
import { usePool, usePoolChanges, usePoolMetadata } from '../../../utils/usePools'
import { TrancheInput } from '../../IssuerCreatePool/TrancheInput'
import { validate } from '../../IssuerCreatePool/validate'

Expand All @@ -28,6 +28,11 @@ export function EpochAndTranches() {
const [account] = useSuitableAccounts({ poolId, poolRole: ['PoolAdmin'], proxyType: ['Borrow'] })
const changes = usePoolChanges(poolId)

const { execute: executeApply, isLoading: isApplyLoading } = useCentrifugeTransaction(
'Apply pool update',
(cent) => cent.pools.applyPoolUpdate
)

const columns: Column[] = [
{
align: 'left',
Expand Down Expand Up @@ -86,7 +91,7 @@ export function EpochAndTranches() {
[pool, metadata]
)

const consts = useConstants()
const consts = useCentrifugeConsts()

const epochHours = Math.floor((pool?.parameters.minEpochTime ?? 0) / 3600)
const epochMinutes = Math.floor(((pool?.parameters.minEpochTime ?? 0) / 60) % 60)
Expand Down Expand Up @@ -199,22 +204,23 @@ export function EpochAndTranches() {

const hasChanges = Object.entries(form.values).some(([k, v]) => (initialValues as any)[k] !== v)

const delay = consts?.minUpdateDelay ? consts.minUpdateDelay / (60 * 60 * 24) : null
const delay = consts.poolSystem.minUpdateDelay / (60 * 60 * 24)

const trancheData = [...tranches].reverse()

return (
<FormikProvider value={form}>
<Form>
<PageSection
title="Epoch and tranches"
subtitle={
delay
? `Changes take ${
delay < 0.5 ? `${Math.ceil(delay / 24)} hours` : `${Math.round(delay)} days`
} to take effect`
: undefined
title={
<Shelf gap={1}>
Epoch and tranches{' '}
{changes && changes.status !== 'ready' && <StatusChip status="info">Pending changes</StatusChip>}
</Shelf>
}
subtitle={`Changes take ${
delay < 0.5 ? `${Math.ceil(delay / 24)} hour(s)` : `${Math.round(delay)} day(s)`
} to take effect`}
headerRight={
isEditing ? (
<ButtonGroup variant="small">
Expand All @@ -233,9 +239,22 @@ export function EpochAndTranches() {
</Button>
</ButtonGroup>
) : (
<Button variant="secondary" onClick={() => setIsEditing(true)} small key="edit">
Edit
</Button>
<ButtonGroup>
{changes?.status === 'ready' && (
<Button
small
loading={isApplyLoading}
disabled={!account}
onClick={() => executeApply([poolId], { account })}
key="apply"
>
Apply changes
</Button>
)}
<Button variant="secondary" onClick={() => setIsEditing(true)} small key="edit">
Edit
</Button>
</ButtonGroup>
)
}
>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Rate, WriteOffGroup } from '@centrifuge/centrifuge-js'
import { useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Box, Button, Stack } from '@centrifuge/fabric'
import { useCentrifugeConsts, useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Box, Button, Shelf, Stack, StatusChip } from '@centrifuge/fabric'
import { FieldArray, Form, FormikErrors, FormikProvider, setIn, useFormik } from 'formik'
import * as React from 'react'
import { useParams } from 'react-router'
Expand All @@ -9,8 +9,7 @@ import { Column, DataTable } from '../../../components/DataTable'
import { PageSection } from '../../../components/PageSection'
import { formatPercentage } from '../../../utils/formatting'
import { useSuitableAccounts } from '../../../utils/usePermissions'
import { useConstants, useWriteOffGroups } from '../../../utils/usePools'
import { PendingLoanChanges } from './PendingLoanChanges'
import { useLoanChanges, useWriteOffGroups } from '../../../utils/usePools'
import { WriteOffInput } from './WriteOffInput'

export type Row = WriteOffGroup
Expand Down Expand Up @@ -53,8 +52,15 @@ export type WriteOffGroupValues = { writeOffGroups: WriteOffGroupInput[] }
export function WriteOffGroups() {
const { pid: poolId } = useParams<{ pid: string }>()
const [isEditing, setIsEditing] = React.useState(false)
const consts = useConstants()
const consts = useCentrifugeConsts()
const [account] = useSuitableAccounts({ poolId, poolRole: ['PoolAdmin'] })
const { policyChanges } = useLoanChanges(poolId)
const latestPolicyChange = policyChanges?.at(-1)

const { execute: executeApply, isLoading: isApplyLoading } = useCentrifugeTransaction(
'Apply write-off policy',
(cent) => cent.pools.applyWriteOffPolicyUpdate
)

const savedGroups = useWriteOffGroups(poolId)
const sortedSavedGroups = [...(savedGroups ?? [])].sort((a, b) => a.overdueDays - b.overdueDays)
Expand Down Expand Up @@ -160,7 +166,7 @@ export function WriteOffGroups() {
}}
small
key="edit"
disabled={form.values.writeOffGroups.length >= (consts?.maxWriteOffPolicySize ?? 5) || !account}
disabled={form.values.writeOffGroups.length >= consts.loans.maxWriteOffPolicySize || !account}
>
Add another
</Button>
Expand All @@ -172,7 +178,14 @@ export function WriteOffGroups() {
<FormikProvider value={form}>
<Form>
<PageSection
title="Write-off policy"
title={
<Shelf gap={1}>
Write-off policy{' '}
{latestPolicyChange && latestPolicyChange.status !== 'ready' && (
<StatusChip status="info">Pending changes</StatusChip>
)}
</Shelf>
}
subtitle="At least one write-off activity is required"
headerRight={
<>
Expand All @@ -193,9 +206,22 @@ export function WriteOffGroups() {
</Button>
</ButtonGroup>
) : (
<Button variant="secondary" onClick={() => setIsEditing(true)} small>
Edit
</Button>
<ButtonGroup>
{latestPolicyChange?.status === 'ready' && (
<Button
small
loading={isApplyLoading}
disabled={!account}
onClick={() => executeApply([poolId, latestPolicyChange.hash], { account })}
key="apply"
>
Apply changes
</Button>
)}
<Button variant="secondary" onClick={() => setIsEditing(true)} small>
Edit
</Button>
</ButtonGroup>
)}
</>
}
Expand All @@ -209,7 +235,6 @@ export function WriteOffGroups() {
) : (
<DataTable data={sortedSavedGroups} columns={columns} />
)}
<PendingLoanChanges poolId={poolId} />
</Stack>
</PageSection>
</Form>
Expand Down
80 changes: 55 additions & 25 deletions centrifuge-app/src/utils/usePools.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Centrifuge, { ActiveLoan, BorrowerTransaction, Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
import { useCentrifuge, useCentrifugeQuery, useWallet } from '@centrifuge/centrifuge-react'
import { useCentrifugeConsts, useCentrifugeQuery, useWallet } from '@centrifuge/centrifuge-react'
import BN from 'bn.js'
import { useEffect } from 'react'
import { useEffect, useMemo } from 'react'
import { useQuery } from 'react-query'
import { combineLatest, map, Observable } from 'rxjs'
import { Dec } from './Decimal'
Expand Down Expand Up @@ -239,43 +239,73 @@ export function usePoolMetadata(
return typeof pool?.metadata === 'string' ? data : tinlakeData
}

export function useConstants() {
const centrifuge = useCentrifuge()
const { data } = useQuery(
['constants'],
async () => {
const api = await centrifuge.getApiPromise()
return {
minUpdateDelay: Number(api.consts.poolSystem.minUpdateDelay.toHuman()),
maxTranches: Number(api.consts.poolSystem.maxTranches.toHuman()),
challengeTime: Number(api.consts.poolSystem.challengeTime.toHuman()),
maxWriteOffPolicySize: Number(api.consts.loans.maxWriteOffPolicySize.toHuman()),
}
},
{
staleTime: Infinity,
}
)

return data
}

export function useWriteOffGroups(poolId: string) {
const [result] = useCentrifugeQuery(['writeOffGroups', poolId], (cent) => cent.pools.getWriteOffPolicy([poolId]))

return result
}

const POOL_CHANGE_DELAY = 1000 * 60 * 60 * 24 * 7 // Currently hard-coded to 1 week on chain, will probably change to a constant we can query

export function useLoanChanges(poolId: string) {
const poolOrders = usePoolOrders(poolId)

const [result] = useCentrifugeQuery(['loanChanges', poolId], (cent) => cent.pools.getProposedLoanChanges([poolId]))

return result
const policyChanges = useMemo(() => {
const hasLockedRedemptions = (poolOrders?.reduce((acc, cur) => acc + cur.activeRedeem.toFloat(), 0) ?? 0) > 0

return result
?.filter(({ change }) => !!change.loan?.policy?.length)
.map((policy) => {
const waitingPeriodDone = new Date(policy.submittedAt).getTime() + POOL_CHANGE_DELAY < Date.now()
return {
...policy,
status: !waitingPeriodDone
? ('waiting' as const)
: hasLockedRedemptions
? ('blocked' as const)
: ('ready' as const),
}
})
}, [poolOrders, result])

return { policyChanges }
}

export function usePoolChanges(poolId: string) {
const pool = usePool(poolId)
const poolOrders = usePoolOrders(poolId)
const consts = useCentrifugeConsts()
const [result] = useCentrifugeQuery(['poolChanges', poolId], (cent) => cent.pools.getProposedPoolChanges([poolId]))

return result
return useMemo(
() => {
if (!result) return result
const submittedTime = new Date(result.submittedAt).getTime()
const waitingPeriodDone = submittedTime + consts.poolSystem.minUpdateDelay * 1000 < Date.now()
const hasLockedRedemptions = (poolOrders?.reduce((acc, cur) => acc + cur.activeRedeem.toFloat(), 0) ?? 0) > 0
const isEpochOngoing = pool.epoch.status === 'ongoing'
const epochNeedsClosing = submittedTime > new Date(pool.epoch.lastClosed).getTime()
return {
...result,
status: !waitingPeriodDone
? ('waiting' as const)
: hasLockedRedemptions || !isEpochOngoing || epochNeedsClosing
? ('blocked' as const)
: ('ready' as const),
blockedBy: epochNeedsClosing
? ('epochNeedsClosing' as const)
: hasLockedRedemptions
? ('redemptions' as const)
: !isEpochOngoing
? ('epochIsClosing' as const)
: null,
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[result, poolOrders, pool]
)
}

export function usePodUrl(poolId: string) {
Expand Down
7 changes: 6 additions & 1 deletion centrifuge-js/src/modules/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export function getProxiesModule(inst: CentrifugeBase) {
const $api = inst.getApi()
const $events = inst.getEvents().pipe(
filter(({ api, events }) => {
const event = events.find(({ event }) => api.events.proxy.PureCreated.is(event))
const event = events.find(
({ event }) =>
api.events.proxy.PureCreated.is(event) ||
api.events.proxy.PureAdded.is(event) ||
api.events.proxy.PureRemoved.is(event)
)
return !!event
})
)
Expand Down
Loading

0 comments on commit ccca53d

Please sign in to comment.