Skip to content

Commit

Permalink
Merge pull request #79 from Hipo/feat/folks-lending-pools
Browse files Browse the repository at this point in the history
Create folks-lending-pool util function
  • Loading branch information
fergalwalsh authored Jan 11, 2024
2 parents e7b968b + 84080f8 commit 35c2ad6
Show file tree
Hide file tree
Showing 17 changed files with 537 additions and 1 deletion.
14 changes: 14 additions & 0 deletions dist/folks-lending-pools/add-liquidity/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Algodv2 } from "algosdk";
import { SignerTransaction, SupportedNetwork } from "../../util/commonTypes";
import { FolksLendingAssetInfo } from "../types";
export declare function generateTxns({ client, network, poolAddress, poolTokenId, lendingManagerId, asset1In, asset2In, initiatorAddr }: {
client: Algodv2;
network: SupportedNetwork;
poolAddress: string;
poolTokenId: number;
lendingManagerId: number;
asset1In: FolksLendingAssetInfo;
asset2In: FolksLendingAssetInfo;
initiatorAddr: string;
}): Promise<SignerTransaction[]>;
export declare function getAddLiquidityTotalFee(wrapperAppOptInRequiredAssetIdCount?: number): number;
7 changes: 7 additions & 0 deletions dist/folks-lending-pools/add-liquidity/utils.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Algodv2 } from "algosdk";
import { SupportedNetwork } from "../../util/commonTypes";
export declare function getFolksWrapperAppOptInRequiredAssetIDs({ client, network, assetIDs }: {
client: Algodv2;
network: SupportedNetwork;
assetIDs: number[];
}): Promise<number[]>;
7 changes: 7 additions & 0 deletions dist/folks-lending-pools/constants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SupportedNetwork } from "../util/commonTypes";
declare const SECONDS_IN_YEAR: bigint;
declare const ONE_14_DP: bigint;
declare const ONE_16_DP: bigint;
declare const FOLKS_WRAPPER_APP_ID: Record<SupportedNetwork, number>;
declare const FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT = 14;
export { SECONDS_IN_YEAR, ONE_14_DP, ONE_16_DP, FOLKS_WRAPPER_APP_ID, FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT };
25 changes: 25 additions & 0 deletions dist/folks-lending-pools/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import algosdk from "algosdk";
import * as AddLiquidity from "./add-liquidity";
import * as RemoveLiquidity from "./remove-liquidity";
import { getFolksWrapperAppOptInRequiredAssetIDs } from "./add-liquidity/utils";
import { FolksLendingPool } from "./types";
/**
* Calculates the amount fAsset received when adding liquidity with original asset.
*/
declare function calculateDepositReturn(depositAmount: number, depositInterestIndex: bigint, depositInterestRate: bigint, lastUpdate?: number): bigint;
/**
* Calculates the amount original asset received when removing liquidity from fAsset pool.
*/
declare function calculateWithdrawReturn(withdrawAmount: number, depositInterestIndex: bigint, depositInterestRate: bigint, lastUpdate?: number): bigint;
/**
* Fetches Folks lending pool application info from the algod, parses the global state and builds FolksLendingPool object.
*/
export declare function fetchFolksLendingPool(algod: algosdk.Algodv2, appId: number): Promise<FolksLendingPool>;
export declare const LendingPool: {
AddLiquidity: typeof AddLiquidity;
RemoveLiquidity: typeof RemoveLiquidity;
calculateWithdrawReturn: typeof calculateWithdrawReturn;
calculateDepositReturn: typeof calculateDepositReturn;
getFolksWrapperAppOptInRequiredAssetIDs: typeof getFolksWrapperAppOptInRequiredAssetIDs;
};
export {};
15 changes: 15 additions & 0 deletions dist/folks-lending-pools/remove-liquidity/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Algodv2 } from "algosdk";
import { V2PoolInfo } from "../../util/pool/poolTypes";
import { SignerTransaction, SupportedNetwork } from "../../util/commonTypes";
import { FolksLendingAssetInfo } from "../types";
export declare function generateTxns({ client, pool, poolTokenIn, initiatorAddr, asset1Out, asset2Out, lendingManagerId, network }: {
client: Algodv2;
pool: Pick<V2PoolInfo, "account" | "poolTokenID">;
poolTokenIn: number | bigint;
initiatorAddr: string;
asset1Out: Omit<FolksLendingAssetInfo, "amount">;
asset2Out: Omit<FolksLendingAssetInfo, "amount">;
lendingManagerId: number;
network: SupportedNetwork;
}): Promise<SignerTransaction[]>;
export declare function getRemoveLiquidityTotalFee(): number;
12 changes: 12 additions & 0 deletions dist/folks-lending-pools/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AssetWithIdAndAmount } from "../util/asset/assetModels";
export type FolksLendingAssetInfo = AssetWithIdAndAmount & {
fAssetId: number;
lendingAppId: number;
};
export interface FolksLendingPool {
appId: number;
managerAppId: number;
depositInterestRate: bigint;
depositInterestIndex: bigint;
lastUpdate: number;
}
13 changes: 13 additions & 0 deletions dist/folks-lending-pools/utils.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Utility function for converting the Algorand key-value schema into a plain object.
*
* Algorand store keys in base64 encoding and store values as either bytes or unsigned integers depending
* on the type. This function decodes this information into a more human friendly structure.
*
* @param kv Algorand key-value data structure to parse.
*
* @returns Key value dictionary parsed from the argument.
*/
export declare function parseState(kv: any): any;
export declare function mulScale(n1: bigint, n2: bigint, scale: bigint): bigint;
export declare function divScale(n1: bigint, n2: bigint, scale: bigint): bigint;
2 changes: 2 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export { SwapQuoteType } from "./swap/types";
export { SwapQuoteError };
export { SwapQuoteErrorType } from "./util/error/SwapQuoteError";
export { redeemExcessAsset, redeemAllExcessAsset, generateRedeemTxns, REDEEM_PROCESS_TXN_COUNT } from "./redeem";
export { fetchFolksLendingPool, LendingPool } from "./folks-lending-pools";
export type { FolksLendingPool } from "./folks-lending-pools/types";
export { prepareCommitTransactions, getStakingAppID } from "./stake";
export { tinymanJSSDKConfig } from "./config";
export { combineAndRegroupSignerTxns } from "./util/transaction/transactionUtils";
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions src/folks-lending-pools/add-liquidity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import algosdk, {
ALGORAND_MIN_TX_FEE,
Algodv2,
decodeAddress,
encodeUint64
} from "algosdk";

import {SignerTransaction, SupportedNetwork} from "../../util/commonTypes";
import {isAlgo} from "../../util/asset/assetUtils";
import {
FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT,
FOLKS_WRAPPER_APP_ID
} from "../constants";
import {encodeString} from "../../util/util";
import {FolksLendingAssetInfo} from "../types";
import {CONTRACT_VERSION} from "../../contract/constants";
import {getValidatorAppID} from "../../validator";
import {getFolksWrapperAppOptInRequiredAssetIDs} from "./utils";
import {MINIMUM_BALANCE_REQUIRED_PER_ASSET} from "../../util/constant";

export async function generateTxns({
client,
network,
poolAddress,
poolTokenId,
lendingManagerId,
asset1In,
asset2In,
initiatorAddr
}: {
client: Algodv2;
network: SupportedNetwork;
poolAddress: string;
poolTokenId: number;
lendingManagerId: number;
asset1In: FolksLendingAssetInfo;
asset2In: FolksLendingAssetInfo;
initiatorAddr: string;
}): Promise<SignerTransaction[]> {
const wrapperAppAddress = algosdk.getApplicationAddress(FOLKS_WRAPPER_APP_ID[network]);
const suggestedParams = await client.getTransactionParams().do();

// Make sure to sort the assets according to the fAssetIds
const [asset1, asset2] = [asset1In, asset2In].sort((a, b) => b.fAssetId - a.fAssetId);

const isAlgoPool = isAlgo(asset2.id);

const asset1InTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
from: initiatorAddr,
to: wrapperAppAddress,
assetIndex: asset1.id,
amount: asset1.amount,
suggestedParams
});

const asset2InTxn = isAlgoPool
? algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: initiatorAddr,
to: wrapperAppAddress,
amount: asset2.amount,
suggestedParams
})
: algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
from: initiatorAddr,
to: wrapperAppAddress,
assetIndex: asset2.id,
amount: asset2.amount,
suggestedParams
});

const appCallTxn1 = algosdk.makeApplicationNoOpTxnFromObject({
from: initiatorAddr,
appIndex: FOLKS_WRAPPER_APP_ID[network],
appArgs: [
encodeString("add_liquidity"),
decodeAddress(poolAddress).publicKey,
encodeUint64(asset1.lendingAppId),
encodeUint64(asset2.lendingAppId)
],
foreignAssets: [asset1.id, asset2.id, asset1.fAssetId, asset2.fAssetId],
foreignApps: [asset1.lendingAppId, asset2.lendingAppId, lendingManagerId],
accounts: [poolAddress],
suggestedParams
});

appCallTxn1.fee =
ALGORAND_MIN_TX_FEE * (FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT + 1);

const validatorAppID = getValidatorAppID(network, CONTRACT_VERSION.V2);
const appCallTxn2 = algosdk.makeApplicationNoOpTxnFromObject({
from: initiatorAddr,
appIndex: FOLKS_WRAPPER_APP_ID[network],
appArgs: [encodeString("noop")],
foreignAssets: [poolTokenId],
foreignApps: [validatorAppID],
accounts: [poolAddress],
suggestedParams
});

let txns = [asset1InTxn, asset2InTxn, appCallTxn1, appCallTxn2];

const optInRequiredAssetIds = await getFolksWrapperAppOptInRequiredAssetIDs({
client,
network,
assetIDs: [asset1.id, asset2.id, asset1.fAssetId, asset2.fAssetId, poolTokenId]
});

if (optInRequiredAssetIds.length) {
const wrapperAppAssetOptInTxns = [
algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: initiatorAddr,
to: wrapperAppAddress,
amount: MINIMUM_BALANCE_REQUIRED_PER_ASSET * optInRequiredAssetIds.length,
suggestedParams
}),
algosdk.makeApplicationNoOpTxnFromObject({
from: initiatorAddr,
appIndex: FOLKS_WRAPPER_APP_ID[network],
appArgs: [
encodeString("asset_optin"),
...optInRequiredAssetIds.map((assetId) => encodeUint64(assetId))
],
foreignAssets: [...optInRequiredAssetIds],
suggestedParams
})
];

wrapperAppAssetOptInTxns[1].fee =
(optInRequiredAssetIds.length + 1) * ALGORAND_MIN_TX_FEE;

txns.unshift(...wrapperAppAssetOptInTxns);
}

return algosdk.assignGroupID(txns).map((txn) => {
return {txn, signers: [initiatorAddr]};
});
}

export function getAddLiquidityTotalFee(wrapperAppOptInRequiredAssetIdCount?: number) {
// 1 asset transfer txn, 1 payment/asset transfer txn, 1 app call txn and 1 app call txn with inner txns
return (
ALGORAND_MIN_TX_FEE * (4 + FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT) +
(wrapperAppOptInRequiredAssetIdCount
? (wrapperAppOptInRequiredAssetIdCount + 1) * ALGORAND_MIN_TX_FEE +
wrapperAppOptInRequiredAssetIdCount * MINIMUM_BALANCE_REQUIRED_PER_ASSET
: 0)
);
}
26 changes: 26 additions & 0 deletions src/folks-lending-pools/add-liquidity/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Algodv2, getApplicationAddress} from "algosdk";

import {SupportedNetwork} from "../../util/commonTypes";
import {FOLKS_WRAPPER_APP_ID} from "../constants";
import {getAccountInformation} from "../../util/account/accountUtils";
import {ALGO_ASSET_ID} from "../../util/asset/assetConstants";

export async function getFolksWrapperAppOptInRequiredAssetIDs({
client,
network,
assetIDs
}: {
client: Algodv2;
network: SupportedNetwork;
assetIDs: number[];
}) {
const wrapperAppAddress = getApplicationAddress(FOLKS_WRAPPER_APP_ID[network]);
const appOptedInAssetIDs = (
await getAccountInformation(client, wrapperAppAddress)
).assets.map((asset) => asset["asset-id"]);

return assetIDs.filter(
(assetID: number) =>
assetID !== ALGO_ASSET_ID && !appOptedInAssetIDs.includes(assetID)
);
}
21 changes: 21 additions & 0 deletions src/folks-lending-pools/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {SupportedNetwork} from "../util/commonTypes";

// eslint-disable-next-line no-magic-numbers
const SECONDS_IN_YEAR = BigInt(365 * 24 * 60 * 60);
const ONE_14_DP = BigInt(1e14);
const ONE_16_DP = BigInt(1e16);

const FOLKS_WRAPPER_APP_ID: Record<SupportedNetwork, number> = {
testnet: 548587153,
mainnet: 1385499515
};

const FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT = 14;

export {
SECONDS_IN_YEAR,
ONE_14_DP,
ONE_16_DP,
FOLKS_WRAPPER_APP_ID,
FOLKS_LENDING_POOL_APP_CALL_INNER_TXN_COUNT
};
Loading

0 comments on commit 35c2ad6

Please sign in to comment.