Skip to content

Commit

Permalink
Refactor calculating exiting and exited assets (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsudmi authored and mike-diamond committed Mar 22, 2024
1 parent f754b49 commit 17f2dfd
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 92 deletions.
1 change: 1 addition & 0 deletions src/graphql/subgraph/exitQueue/exitQueueQuery.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ query exitQueue($receiver: Bytes, $vault: String!) {
}) {
positionTicket
totalShares
totalAssets
timestamp
}
}
3 changes: 0 additions & 3 deletions src/methods/vault/requests/getExitQueuePositions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@ const getExitQueuePositions = async (input: GetExitQueuePositionsInput) => {
return mock
}

const totalShares = data.reduce((acc, { totalShares }) => acc + BigInt(totalShares), 0n)

return parseExitRequests({
options,
provider,
contracts,
userAddress,
totalShares,
vaultAddress,
exitRequests: data,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ describe('parseExitRequests function', () => {

const input: ParseExitRequestsInput = {
contracts,
totalShares: 1000n,
options: { network },
userAddress: ZeroAddress,
vaultAddress: ZeroAddress,
Expand All @@ -34,13 +33,21 @@ describe('parseExitRequests function', () => {
{
positionTicket: 'positionTicket-1',
timestamp: '123456',
totalAssets: '0',
totalShares: '100',
},
{
positionTicket: 'positionTicket-2',
timestamp: '123456',
timestamp: '123457',
totalAssets: '0',
totalShares: '200',
},
{
positionTicket: 'positionTicket-2',
timestamp: '123458',
totalAssets: '300',
totalShares: '0',
},
],
}

Expand All @@ -53,17 +60,23 @@ describe('parseExitRequests function', () => {
.mockResolvedValueOnce([
[ 1n ],
[ 2n ],
[ 3n ],
])
.mockResolvedValueOnce([
{
claimedAssets: 30n,
claimedShares: 50n,
leftShares: 10n,
exitedAssets: 30n,
exitedTickets: 50n,
leftTickets: 10n,
},
{
exitedAssets: 1n,
exitedTickets: 100n,
leftTickets: 20n,
},
{
claimedAssets: 1n,
claimedShares: 100n,
leftShares: 20n,
exitedAssets: 250n,
exitedTickets: 200n,
leftTickets: 30n,
},
])
.mockResolvedValueOnce([
Expand All @@ -79,16 +92,24 @@ describe('parseExitRequests function', () => {
{
exitQueueIndex: 1n,
timestamp: '123456',
isV1Position: true,
positionTicket: 'positionTicket-1',
},
{
exitQueueIndex: 2n,
timestamp: '123456',
timestamp: '123457',
isV1Position: true,
positionTicket: 'positionTicket-2',
},
{
exitQueueIndex: 3n,
timestamp: '123458',
isV1Position: false,
positionTicket: 'positionTicket-2',
},
],
total: 131n,
withdrawable: 31n,
total: 431n,
withdrawable: 281n,
})
})

Expand Down Expand Up @@ -138,12 +159,13 @@ describe('parseExitRequests function', () => {
.mockResolvedValueOnce([
[ -1n ],
[ 1n ],
[ -1n ],
])
.mockResolvedValueOnce([
{
leftShares: 10n,
claimedShares: 50n,
claimedAssets: 30n,
leftTickets: 10n,
exitedTickets: 50n,
exitedAssets: 30n,
},
])
.mockResolvedValueOnce([ { assets: 50n } ])
Expand All @@ -153,10 +175,11 @@ describe('parseExitRequests function', () => {
expect(result).toEqual({
positions: [ {
exitQueueIndex: 1n,
timestamp: '123456',
timestamp: '123457',
isV1Position: true,
positionTicket: 'positionTicket-2',
} ],
total: 80n,
total: 380n,
withdrawable: 30n,
})
})
Expand All @@ -166,10 +189,12 @@ describe('parseExitRequests function', () => {
.mockResolvedValueOnce([
[ 0n ],
[ 1n ],
[ 1n ],
])
.mockResolvedValueOnce([
{ leftShares: 0n, claimedShares: 0n, claimedAssets: 0n },
{ leftShares: 0n, claimedShares: 0n, claimedAssets: 0n },
{ leftTickets: 0n, exitedTickets: 100n, exitedAssets: 101n },
{ leftTickets: 0n, exitedTickets: 200n, exitedAssets: 202n },
{ leftTickets: 0n, exitedTickets: 300n, exitedAssets: 300n },
])
.mockResolvedValueOnce([ { assets: 0n } ])

Expand All @@ -180,47 +205,24 @@ describe('parseExitRequests function', () => {
{
exitQueueIndex: 0n,
timestamp: '123456',
isV1Position: true,
positionTicket: 'positionTicket-1',
},
{
exitQueueIndex: 1n,
timestamp: '123456',
timestamp: '123457',
isV1Position: true,
positionTicket: 'positionTicket-2',
},
],
total: 0n,
withdrawable: 0n,
})
})

it('should handle remainingShares being 0', async () => {
(vaultMulticall as jest.Mock)
.mockResolvedValueOnce([
[ 0n ],
[ 1n ],
])
.mockResolvedValueOnce([
{ leftShares: 10n, claimedShares: 1000n, claimedAssets: 30n },
])
.mockResolvedValueOnce([])

const result = await parseExitRequests(input)

expect(result).toEqual({
positions: [
{
exitQueueIndex: 0n,
timestamp: '123456',
positionTicket: 'positionTicket-1',
},
{
exitQueueIndex: 1n,
timestamp: '123456',
timestamp: '123458',
isV1Position: false,
positionTicket: 'positionTicket-2',
},
],
total: 30n,
withdrawable: 30n,
total: 603n,
withdrawable: 603n,
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ export type ParseExitRequestsInput = {
provider: StakeWise.Provider
options: StakeWise.Options
userAddress: string
totalShares: bigint
vaultAddress: string
exitRequests: Array<{
positionTicket: string
totalShares: string
totalAssets: string
timestamp: string
}>
}

type Position = {
exitQueueIndex: bigint
positionTicket: string
isV1Position: boolean
timestamp: string
}

Expand All @@ -28,9 +29,9 @@ type ParseExitRequestsOutput = {
}

type ExitedAssetsResponse = Array<{
leftShares: bigint
claimedShares: bigint
claimedAssets: bigint
leftTickets: bigint
exitedTickets: bigint
exitedAssets: bigint
}>

const _checkTimestamp = async (timestamp: string, provider: StakeWise.Provider) => {
Expand All @@ -46,7 +47,7 @@ const _checkTimestamp = async (timestamp: string, provider: StakeWise.Provider)
}

const parseExitRequests = async (values: ParseExitRequestsInput): Promise<ParseExitRequestsOutput> => {
const { options, contracts, provider, userAddress, vaultAddress, totalShares, exitRequests } = values
const { options, contracts, provider, userAddress, vaultAddress, exitRequests } = values

const keeperContract = contracts.base.keeper
const vaultContract = contracts.helpers.createVault(vaultAddress)
Expand Down Expand Up @@ -75,78 +76,86 @@ const parseExitRequests = async (values: ParseExitRequestsInput): Promise<ParseE
const claims: Position[] = []
const indexes = (indexesResponse || [])

let queuedShares = 0n, queuedAssets = 0n
for (let i = 0; i < indexes.length; i++) {
const { positionTicket, timestamp, totalShares, totalAssets } = exitRequests[i]
queuedShares += BigInt(totalShares)
queuedAssets += BigInt(totalAssets)

// If the index is -1 then we cannot claim anything. Otherwise, the value is >= 0.
const exitQueueIndex = indexes[i][0]
const { positionTicket, timestamp } = exitRequests[i]
if (exitQueueIndex < 0n) {
continue
}

// 24 hours must have elapsed since the position was created
const is24HoursPassed = await _checkTimestamp(timestamp, provider)

// If the index is -1 then we cannot claim anything. Otherwise, the value is >= 0.
const isValid = exitQueueIndex > -1n

if (isValid && is24HoursPassed) {
const item = { exitQueueIndex, positionTicket, timestamp }
if (is24HoursPassed) {
const isV1Position = BigInt(totalShares) > 0
const item = { exitQueueIndex, positionTicket, timestamp, isV1Position }

claims.push(item)
}
}

let exitedAssetsResponse: ExitedAssetsResponse = []

if (claims.length) {
// We need to get the data of the contract after the claim.
exitedAssetsResponse = await vaultMulticall<ExitedAssetsResponse>({
...commonMulticallParams,
request: {
params: claims.map(({ positionTicket, exitQueueIndex, timestamp }) => ({
method: 'calculateExitedAssets',
args: [ userAddress, positionTicket, timestamp, exitQueueIndex ],
})),
callStatic: true,
},
}) || []
}
else {
if (!claims.length) {
const result = await vaultMulticall<Array<{ assets: bigint }>>({
...commonMulticallParams,
request: {
params: [ { method: 'convertToAssets', args: [ totalShares ] } ],
params: [ { method: 'convertToAssets', args: [ queuedShares ] } ],
callStatic: true,
},
})
const totalV1QueuedAssets = result[0]?.assets || 0n

// If there are no positions with an index greater than 0 or their timestamp has failed the 24 hour check.
// If there are no positions with an index greater than 0 or their timestamp has failed the 24-hour check.
// Then we can use totalShares from the subgraph to show total
return {
positions: [],
withdrawable: 0n,
total: result[0]?.assets || 0n,
total: totalV1QueuedAssets + queuedAssets,
}
}

let withdrawableAssets = 0n,
totalLeftShares = 0n,
totalLeftAssets = 0n
// We need to calculate the exited assets for every position.
const exitedAssetsResponse = await vaultMulticall<ExitedAssetsResponse>({
...commonMulticallParams,
request: {
params: claims.map(({ positionTicket, exitQueueIndex, timestamp }) => ({
method: 'calculateExitedAssets',
args: [ userAddress, positionTicket, timestamp, exitQueueIndex ],
})),
callStatic: true,
},
}) || []

exitedAssetsResponse.forEach(({ leftShares, claimedAssets }) => {
totalLeftShares += leftShares
withdrawableAssets += claimedAssets
// Calculate total withdrawable assets
let withdrawableAssets = 0n
exitedAssetsResponse.forEach(({ exitedTickets, exitedAssets }, i) => {
const { isV1Position } = claims[i]
if (isV1Position) {
// in V1 exit queue exit tickets are shares
queuedShares -= exitedTickets
}
else {
queuedAssets -= exitedAssets
}
withdrawableAssets += exitedAssets
})

if (totalLeftShares > 0) {
if (queuedShares > 0) {
const result = await vaultMulticall<Array<{ assets: bigint }>>({
...commonMulticallParams,
request: {
params: [ { method: 'convertToAssets', args: [ totalLeftShares ] } ],
params: [ { method: 'convertToAssets', args: [ queuedShares ] } ],
callStatic: true,
},
})

totalLeftAssets = result[0]?.assets || 0n
queuedAssets += result[0]?.assets || 0n
}

const total = withdrawableAssets + totalLeftAssets
const total = withdrawableAssets + queuedAssets

return {
total,
Expand Down

0 comments on commit 17f2dfd

Please sign in to comment.