Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "liquidops",
"version": "1.2.9",
"version": "1.2.10",
"author": "Lorimer Jenkins <lorimer@liquidops.io>, Marton Lederer <marton@liquidops.io>",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion src/arweave/getTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function getTags({

const query = `
query GetTransactions(
$tags: [TagFilter!],
$tags: [TagFilter!],
$cursor: String
${owner ? ", $owner: String!" : ""}
${recipientValue ? ", $recipients: [String!]" : ""}
Expand Down
184 changes: 112 additions & 72 deletions src/functions/lend/getEarnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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<AoUtils, "signer" | "configs">,
{ walletAddress, token, collateralization }: GetEarnings,
Expand All @@ -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,
};
}
Loading