From ed375aab7557bfc47055d53c8fd6825e254d0cd6 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 3 Jul 2025 14:22:23 +0200 Subject: [PATCH 1/3] feat: revamp getEarnings() --- src/functions/lend/getEarnings.ts | 172 +++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 38 deletions(-) diff --git a/src/functions/lend/getEarnings.ts b/src/functions/lend/getEarnings.ts index ba6caf6..665d46f 100644 --- a/src/functions/lend/getEarnings.ts +++ b/src/functions/lend/getEarnings.ts @@ -2,9 +2,10 @@ import { AoUtils, connectToAO } from "../../ao/utils/connect"; import { getTags } from "../../arweave/getTags"; import { getTransactions, + GetTransactionsRes, Transaction, } from "../getTransactions/getTransactions"; -import { GQLTransactionsResultInterface } from "ar-gql/dist/faces"; +import { GQLEdgeInterface, GQLTransactionsResultInterface } from "ar-gql/dist/faces"; export interface GetEarnings { walletAddress: string; @@ -18,6 +19,11 @@ export interface GetEarningsRes { startDate?: number; } +interface Action { + action: "lend" | "unlend"; + qty: bigint; +} + export async function getEarnings( aoUtilsInput: Pick, { walletAddress, token, collateralization }: GetEarnings, @@ -43,40 +49,130 @@ export async function getEarnings( }; } - // 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: "", - }), - ]); + let actions: Action[] = []; + + let lendCursor: string | undefined; + let redeemCursor: string | undefined; + let hasNextPage = true; + // let cursor = ""; + + // while (hasNextPage) { + // const res = await getTags({ + // aoUtils, + // tags: [ + // { name: "Action", values: ["Transfer", "Redeem"] }, + // { name: "" } + // ], + // cursor, + // owner: walletAddress + // }); + + + // } + + 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: Action[] = []; + 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({ + action: "lend", + qty: BigInt(tx.tags.Quantity || "0") + }); + } + } else { + const tx = unlendRequests.transactions[j++]; + + if (hasConfirmationPredicate(tx.id, unlendConfirmations)) { + newActions.push({ + action: "unlend", + qty: BigInt(tx.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); + + for (let i = actions.length - 1; i >= 0; i--) { + const action = actions[i]; + + if (action.action === "lend") { + userPrincipal += action.qty; + } else { + if (action.qty <= userPrincipal) { + userPrincipal -= action.qty; + } else { + withdrawnInterest += action.qty - userPrincipal; + userPrincipal = BigInt(0); + } + } + } + + const profit = collateralization - userPrincipal; + const totalEarnedInterest = profit + withdrawnInterest; + + console.log("user principal", userPrincipal) + console.log("profit", profit) + console.log("total earned interest", totalEarnedInterest) + +/* // 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 @@ -118,11 +214,11 @@ export async function getEarnings( // the first mint date const startDate = lendConfirmations?.edges?.[lendConfirmations?.edges?.length - 1 || 0]?.node - ?.block?.timestamp; + ?.block?.timestamp;*/ return { - base, - profit: collateralization - base, - startDate, + base: BigInt(0), + profit: BigInt(0), + startDate: 0, }; } From ee74ce8bf8dd983bec9b78b696410985b2a0befc Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 4 Jul 2025 12:43:47 +0200 Subject: [PATCH 2/3] feat: finish getEarnings --- src/arweave/getTags.ts | 2 +- src/functions/lend/getEarnings.ts | 96 +++++++------------------------ 2 files changed, 21 insertions(+), 77 deletions(-) diff --git a/src/arweave/getTags.ts b/src/arweave/getTags.ts index c4b365f..ad16fc3 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 665d46f..1d348c6 100644 --- a/src/functions/lend/getEarnings.ts +++ b/src/functions/lend/getEarnings.ts @@ -3,9 +3,8 @@ import { getTags } from "../../arweave/getTags"; import { getTransactions, GetTransactionsRes, - Transaction, } from "../getTransactions/getTransactions"; -import { GQLEdgeInterface, GQLTransactionsResultInterface } from "ar-gql/dist/faces"; +import { GQLTransactionsResultInterface } from "ar-gql/dist/faces"; export interface GetEarnings { walletAddress: string; @@ -16,12 +15,14 @@ export interface GetEarnings { export interface GetEarningsRes { base: bigint; profit: bigint; + totalEarnedInterest: bigint; startDate?: number; } -interface Action { - action: "lend" | "unlend"; +interface Event { + type: "lend" | "unlend"; qty: bigint; + date: number; } export async function getEarnings( @@ -46,29 +47,15 @@ export async function getEarnings( return { base: BigInt(0), profit: BigInt(0), + totalEarnedInterest: BigInt(0), }; } - let actions: Action[] = []; + let actions: Event[] = []; let lendCursor: string | undefined; let redeemCursor: string | undefined; let hasNextPage = true; - // let cursor = ""; - - // while (hasNextPage) { - // const res = await getTags({ - // aoUtils, - // tags: [ - // { name: "Action", values: ["Transfer", "Redeem"] }, - // { name: "" } - // ], - // cursor, - // owner: walletAddress - // }); - - - // } while (hasNextPage) { // get lends and unlends @@ -110,7 +97,7 @@ export async function getEarnings( const hasConfirmationPredicate = (id: string, confirmations: GQLTransactionsResultInterface) => !!confirmations.edges.find(tx => tx.node.tags.find((t) => t.name === "Pushed-For")?.value === id); - const newActions: Action[] = []; + const newActions: Event[] = []; let i = 0, j = 0; while (i < lendRequests.transactions.length && j < unlendRequests.transactions.length) { @@ -119,8 +106,9 @@ export async function getEarnings( if (hasConfirmationPredicate(tx.id, lendConfirmations)) { newActions.push({ - action: "lend", - qty: BigInt(tx.tags.Quantity || "0") + type: "lend", + qty: BigInt(tx.tags.Quantity || "0"), + date: tx.block.timestamp }); } } else { @@ -128,8 +116,9 @@ export async function getEarnings( if (hasConfirmationPredicate(tx.id, unlendConfirmations)) { newActions.push({ - action: "unlend", - qty: BigInt(tx.tags.Quantity || "0") + type: "unlend", + qty: BigInt(tx.tags.Quantity || "0"), + date: tx.block.timestamp }); } } @@ -152,7 +141,7 @@ export async function getEarnings( for (let i = actions.length - 1; i >= 0; i--) { const action = actions[i]; - if (action.action === "lend") { + if (action.type === "lend") { userPrincipal += action.qty; } else { if (action.qty <= userPrincipal) { @@ -167,58 +156,13 @@ export async function getEarnings( const profit = collateralization - userPrincipal; const totalEarnedInterest = profit + withdrawnInterest; - console.log("user principal", userPrincipal) - console.log("profit", profit) - console.log("total earned interest", totalEarnedInterest) - - -/* - // 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; - } - - // add together - return prev + BigInt(curr.tags.Quantity || 0); - }; - }; - - // deposited tokens (sum of successfull lends) - const sumDeposited = lends.transactions.reduce( - sumIfSuccessfull(lendConfirmations), - BigInt(0), - ); - - // withdrawn tokens (sum of successfull burns) - const sumWithdrawn = unlends.transactions.reduce( - sumIfSuccessfull(unlendConfirmations), - 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; - // 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: BigInt(0), - profit: BigInt(0), - startDate: 0, + base: userPrincipal, + profit, + totalEarnedInterest, + startDate, }; } From 332ffcaff0134fb82312e76455fb31f4fa3ea047 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 4 Jul 2025 12:48:37 +0200 Subject: [PATCH 3/3] chore: update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf6801a..0409696 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "liquidops", - "version": "1.2.8", + "version": "1.2.10", "author": "Lorimer Jenkins , Marton Lederer ", "repository": { "type": "git",