diff --git a/package.json b/package.json index 155df2a0..0409696a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "liquidops", - "version": "1.2.9", + "version": "1.2.10", "author": "Lorimer Jenkins , Marton Lederer ", "repository": { "type": "git", diff --git a/src/arweave/getTags.ts b/src/arweave/getTags.ts index c4b365f6..ad16fc36 100644 --- a/src/arweave/getTags.ts +++ b/src/arweave/getTags.ts @@ -42,7 +42,7 @@ export async function getTags({ const query = ` query GetTransactions( - $tags: [TagFilter!], + $tags: [TagFilter!], $cursor: String ${owner ? ", $owner: String!" : ""} ${recipientValue ? ", $recipients: [String!]" : ""} diff --git a/src/functions/lend/getEarnings.ts b/src/functions/lend/getEarnings.ts index ba6caf6e..1d348c6c 100644 --- a/src/functions/lend/getEarnings.ts +++ b/src/functions/lend/getEarnings.ts @@ -2,7 +2,7 @@ import { AoUtils, connectToAO } from "../../ao/utils/connect"; import { getTags } from "../../arweave/getTags"; import { getTransactions, - Transaction, + GetTransactionsRes, } from "../getTransactions/getTransactions"; import { GQLTransactionsResultInterface } from "ar-gql/dist/faces"; @@ -15,9 +15,16 @@ export interface GetEarnings { export interface GetEarningsRes { base: bigint; profit: bigint; + totalEarnedInterest: bigint; startDate?: number; } +interface Event { + type: "lend" | "unlend"; + qty: bigint; + date: number; +} + export async function getEarnings( aoUtilsInput: Pick, { walletAddress, token, collateralization }: GetEarnings, @@ -40,89 +47,122 @@ export async function getEarnings( return { base: BigInt(0), profit: BigInt(0), + totalEarnedInterest: BigInt(0), }; } - // get lends and unlends - const [lends, unlends] = await Promise.all([ - getTransactions(aoUtils, { - token, - action: "lend", - walletAddress, - }), - getTransactions(aoUtils, { - token, - action: "unLend", - walletAddress, - }), - ]); - - // get lend and unlend confirmations - const [lendConfirmations, unlendConfirmations] = await Promise.all([ - getTags({ - aoUtils, - tags: [ - { name: "Action", values: "Mint-Confirmation" }, - { name: "Pushed-For", values: lends.transactions.map((t) => t.id) }, - ], - cursor: "", - }), - getTags({ - aoUtils, - tags: [ - { name: "Action", values: "Redeem-Confirmation" }, - { name: "Pushed-For", values: unlends.transactions.map((t) => t.id) }, - ], - cursor: "", - }), - ]); - - // reducer function for a list of lends/unlends based on a - // list of mint/redeem confirmations - // it adds together the quantities if they have a confirmation - const sumIfSuccessfull = (confirmations: GQLTransactionsResultInterface) => { - return (prev: bigint, curr: Transaction) => { - // check if it was a successfull interaction (received confirmation) - if ( - !confirmations.edges.find( - (tx) => - tx.node.tags.find((t) => t.name === "Pushed-For")?.value === - curr.id, - ) - ) { - return prev; + let actions: Event[] = []; + + let lendCursor: string | undefined; + let redeemCursor: string | undefined; + let hasNextPage = true; + + while (hasNextPage) { + // get lends and unlends + const [lendRequests, unlendRequests]: [GetTransactionsRes, GetTransactionsRes] = await Promise.all([ + getTransactions(aoUtils, { + token, + action: "lend", + walletAddress, + cursor: lendCursor + }), + getTransactions(aoUtils, { + token, + action: "unLend", + walletAddress, + cursor: redeemCursor + }), + ]); + + // get lend and unlend confirmations + const [lendConfirmations, unlendConfirmations] = await Promise.all([ + getTags({ + aoUtils, + tags: [ + { name: "Action", values: "Mint-Confirmation" }, + { name: "Pushed-For", values: lendRequests.transactions.map((t) => t.id) }, + ], + cursor: "", + }), + getTags({ + aoUtils, + tags: [ + { name: "Action", values: "Redeem-Confirmation" }, + { name: "Pushed-For", values: unlendRequests.transactions.map((t) => t.id) }, + ], + cursor: "", + }), + ]); + + const hasConfirmationPredicate = (id: string, confirmations: GQLTransactionsResultInterface) => + !!confirmations.edges.find(tx => tx.node.tags.find((t) => t.name === "Pushed-For")?.value === id); + + const newActions: Event[] = []; + let i = 0, j = 0; + + while (i < lendRequests.transactions.length && j < unlendRequests.transactions.length) { + if (lendRequests.transactions[i].block.timestamp >= unlendRequests.transactions[j].block.timestamp) { + const tx = lendRequests.transactions[i++]; + + if (hasConfirmationPredicate(tx.id, lendConfirmations)) { + newActions.push({ + type: "lend", + qty: BigInt(tx.tags.Quantity || "0"), + date: tx.block.timestamp + }); + } + } else { + const tx = unlendRequests.transactions[j++]; + + if (hasConfirmationPredicate(tx.id, unlendConfirmations)) { + newActions.push({ + type: "unlend", + qty: BigInt(tx.tags.Quantity || "0"), + date: tx.block.timestamp + }); + } } + } - // add together - return prev + BigInt(curr.tags.Quantity || 0); - }; - }; + actions = actions.concat( + newActions + ); + + lendCursor = lendRequests.pageInfo.cursor; + redeemCursor = unlendRequests.pageInfo.cursor; + hasNextPage = lendRequests.pageInfo.hasNextPage || unlendRequests.pageInfo.hasNextPage; + } + + // loop over user interactions from the very first interaction (reverse) + // and track deposits, withdraws and interest withdraws + let userPrincipal = BigInt(0); + let withdrawnInterest = BigInt(0); - // deposited tokens (sum of successfull lends) - const sumDeposited = lends.transactions.reduce( - sumIfSuccessfull(lendConfirmations), - BigInt(0), - ); + for (let i = actions.length - 1; i >= 0; i--) { + const action = actions[i]; - // withdrawn tokens (sum of successfull burns) - const sumWithdrawn = unlends.transactions.reduce( - sumIfSuccessfull(unlendConfirmations), - BigInt(0), - ); + if (action.type === "lend") { + userPrincipal += action.qty; + } else { + if (action.qty <= userPrincipal) { + userPrincipal -= action.qty; + } else { + withdrawnInterest += action.qty - userPrincipal; + userPrincipal = BigInt(0); + } + } + } - // the result of the total deposited and total withdrawn tokens - // is the amount of tokens that the user has in the pool, that - // they have deposited (without the interest!!) - const base = sumDeposited - sumWithdrawn; + const profit = collateralization - userPrincipal; + const totalEarnedInterest = profit + withdrawnInterest; // the first mint date - const startDate = - lendConfirmations?.edges?.[lendConfirmations?.edges?.length - 1 || 0]?.node - ?.block?.timestamp; + const startDate = actions[actions.length - 1]?.date; return { - base, - profit: collateralization - base, + base: userPrincipal, + profit, + totalEarnedInterest, startDate, }; }