├── .changeset
├── README.md
└── config.json
├── .github
├── actions
│ └── setup
│ │ └── action.yml
└── workflows
│ ├── gen-docs.yml
│ ├── lint-and-typecheck.yml
│ ├── publish-preview.yml
│ └── release.yml
├── .gitignore
├── .husky
└── pre-commit
├── .nvmrc
├── .prettierignore
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── eslint.config.mjs
├── examples
├── config.ts
├── lend
│ ├── complex-flash-loan.ts
│ ├── liquidate.ts
│ └── simple-flash-loan.ts
└── xalgo
│ └── stake.ts
├── media
└── repo-header.jpg
├── package.json
├── pnpm-lock.yaml
├── prettier.config.mjs
├── src
├── algo-liquid-governance
│ ├── common
│ │ ├── governance.ts
│ │ ├── index.ts
│ │ ├── mainnet-constants.ts
│ │ └── types.ts
│ ├── v1
│ │ ├── constants
│ │ │ ├── abi-contracts.ts
│ │ │ └── mainnet-constants.ts
│ │ ├── governance.ts
│ │ ├── index.ts
│ │ └── types.ts
│ └── v2
│ │ ├── constants
│ │ ├── abi-contracts.ts
│ │ └── mainnet-constants.ts
│ │ ├── governance.ts
│ │ ├── index.ts
│ │ └── types.ts
├── index.ts
├── lend
│ ├── abi-contracts
│ │ ├── deposit-staking.json
│ │ ├── deposits.json
│ │ ├── index.ts
│ │ ├── loan.json
│ │ ├── lp-token-oracle.json
│ │ ├── oracle-adapter.json
│ │ └── pool.json
│ ├── amm.ts
│ ├── constants
│ │ ├── mainnet-constants.ts
│ │ └── testnet-constants.ts
│ ├── deposit-staking.ts
│ ├── deposit.ts
│ ├── formulae.ts
│ ├── index.ts
│ ├── loan.ts
│ ├── opup.ts
│ ├── oracle.ts
│ ├── types.ts
│ └── utils.ts
├── math-lib.ts
├── utils.ts
└── xalgo
│ ├── abi-contracts
│ ├── index.ts
│ ├── stake_and_deposit.json
│ └── xalgo.json
│ ├── consensus.ts
│ ├── constants
│ ├── mainnet-constants.ts
│ └── testnet-constants.ts
│ ├── formulae.ts
│ ├── index.ts
│ └── types.ts
├── tsconfig.json
└── typedoc.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
3 | "changelog": ["@changesets/changelog-github", { "repo": "Folks-Finance/algorand-js-sdk" }],
4 | "commit": false,
5 | "access": "public",
6 | "baseBranch": "main",
7 | "updateInternalDependencies": "patch"
8 | }
9 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: "Setup"
2 | description: "Setup Node.js and pnpm and install dependencies"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Setup pnpm # by default it uses the version set in the package.json as packageManager
8 | uses: pnpm/action-setup@v4
9 |
10 | - name: Setup Node.js
11 | uses: actions/setup-node@v4
12 | with:
13 | node-version-file: ".nvmrc"
14 | cache: "pnpm"
15 |
16 | - name: Install dependencies
17 | shell: bash
18 | env:
19 | HUSKY: "0"
20 | run: pnpm install --frozen-lockfile
21 |
--------------------------------------------------------------------------------
/.github/workflows/gen-docs.yml:
--------------------------------------------------------------------------------
1 | name: Generate docs and deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | workflow_dispatch:
8 |
9 | permissions:
10 | contents: read
11 | pages: write
12 | id-token: write
13 |
14 | concurrency:
15 | group: "pages"
16 | cancel-in-progress: false
17 |
18 | jobs:
19 | deploy:
20 | environment:
21 | name: github-pages
22 | url: ${{ steps.deployment.outputs.page_url }}
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@v4
27 |
28 | - name: Setup
29 | uses: ./.github/actions/setup
30 |
31 | - name: Generate documentation
32 | run: pnpm run gen-docs
33 |
34 | - name: Setup Pages
35 | uses: actions/configure-pages@v5
36 |
37 | - name: Upload artifact
38 | uses: actions/upload-pages-artifact@v3
39 | with:
40 | path: "docs"
41 |
42 | - name: Deploy to GitHub Pages
43 | id: deployment
44 | uses: actions/deploy-pages@v4
45 |
--------------------------------------------------------------------------------
/.github/workflows/lint-and-typecheck.yml:
--------------------------------------------------------------------------------
1 | name: Lint and Typecheck
2 |
3 | concurrency:
4 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
5 | cancel-in-progress: true
6 |
7 | on:
8 | push:
9 | branches:
10 | - main
11 | pull_request:
12 | branches:
13 | - main
14 |
15 | jobs:
16 | lint:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Checkout repository
20 | uses: actions/checkout@v4
21 |
22 | - name: Setup
23 | uses: ./.github/actions/setup
24 |
25 | - name: Lint
26 | run: pnpm lint
27 |
28 | typecheck:
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout repository
32 | uses: actions/checkout@v4
33 |
34 | - name: Setup
35 | uses: ./.github/actions/setup
36 |
37 | - name: Typecheck
38 | run: pnpm typecheck
39 |
--------------------------------------------------------------------------------
/.github/workflows/publish-preview.yml:
--------------------------------------------------------------------------------
1 | name: Publish Any Commit
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 |
8 | steps:
9 | - name: Checkout repository
10 | uses: actions/checkout@v4
11 |
12 | - name: Setup
13 | uses: ./.github/actions/setup
14 |
15 | - name: Build
16 | run: pnpm build
17 |
18 | - run: pnpx pkg-pr-new publish
19 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v4
17 |
18 | - name: Setup
19 | uses: ./.github/actions/setup
20 |
21 | - name: Create Release Pull Request or Publish
22 | id: changesets
23 | uses: changesets/action@v1
24 | with:
25 | publish: pnpm run release
26 | title: "chore(release): version packages"
27 | commit: "chore(release): version packages"
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # build
5 | /dist
6 | /docs
7 |
8 | # debug
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | .pnpm-debug.log*
13 |
14 | # typescript
15 | *.tsbuildinfo
16 |
17 | # eslint
18 | .eslintcache
19 |
20 | # macos
21 | .DS_Store
22 |
23 | # local env files
24 | .env
25 | .env*.local
26 |
27 | # intellij
28 | .idea
29 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | pnpm exec lint-staged
2 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v22.12.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | pnpm-lock.yaml
2 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // Formatting using Prettier by default for all languages
3 | "prettier.configPath": "prettier.config.mjs",
4 | "editor.defaultFormatter": "esbenp.prettier-vscode",
5 | "editor.formatOnSave": true,
6 |
7 | // Formatting using Prettier for JavaScript and TypeScript, overrides VSCode default.
8 | "[javascript]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | },
11 | "[typescript]": {
12 | "editor.defaultFormatter": "esbenp.prettier-vscode"
13 | },
14 |
15 | // Linting using ESLint.
16 | "eslint.validate": ["javascript", "typescript"],
17 | "eslint.useFlatConfig": true,
18 | "editor.codeActionsOnSave": {
19 | "source.fixAll.eslint": "explicit"
20 | },
21 | "eslint.workingDirectories": [
22 | {
23 | "mode": "auto"
24 | }
25 | ],
26 |
27 | "typescript.tsdk": "node_modules/typescript/lib"
28 | }
29 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @folks-finance/algorand-sdk
2 |
3 | ## 0.1.6
4 |
5 | ### Patch Changes
6 |
7 | - [#84](https://github.com/Folks-Finance/algorand-js-sdk/pull/84) [`f515b0b`](https://github.com/Folks-Finance/algorand-js-sdk/commit/f515b0bac216e43020ac1e7e44eee7717f04ea23) Thanks [@gidonkatten](https://github.com/gidonkatten)! - add ability to close out governance escrow
8 |
9 | ## 0.1.5
10 |
11 | ### Patch Changes
12 |
13 | - [#81](https://github.com/Folks-Finance/algorand-js-sdk/pull/81) [`e23f100`](https://github.com/Folks-Finance/algorand-js-sdk/commit/e23f100103f38d3fb78be44fd345fad92f90cf03) Thanks [@gidonkatten](https://github.com/gidonkatten)! - support for staking apr in lending pool info
14 |
15 | ## 0.1.4
16 |
17 | ### Patch Changes
18 |
19 | - [#78](https://github.com/Folks-Finance/algorand-js-sdk/pull/78) [`113c7cc`](https://github.com/Folks-Finance/algorand-js-sdk/commit/113c7cc2b407cfa8bbd7484dea4897fc41e2472a) Thanks [@gidonkatten](https://github.com/gidonkatten)! - transaction to claim x algo fees
20 |
21 | ## 0.1.3
22 |
23 | ### Patch Changes
24 |
25 | - [#76](https://github.com/Folks-Finance/algorand-js-sdk/pull/76) [`03313b7`](https://github.com/Folks-Finance/algorand-js-sdk/commit/03313b7237c32f366a7a1fd2a177d6ecdefab400) Thanks [@gidonkatten](https://github.com/gidonkatten)! - option to include additional interest in net rate and yield calculations
26 |
27 | ## 0.1.2
28 |
29 | ### Patch Changes
30 |
31 | - [#74](https://github.com/Folks-Finance/algorand-js-sdk/pull/74) [`dd291d6`](https://github.com/Folks-Finance/algorand-js-sdk/commit/dd291d6754ac0ec2d8c37d041e4a0dc3e4a5b0e3) Thanks [@gidonkatten](https://github.com/gidonkatten)! - fix unexpected register fee amount
32 |
33 | ## 0.1.1
34 |
35 | ### Patch Changes
36 |
37 | - [#72](https://github.com/Folks-Finance/algorand-js-sdk/pull/72) [`a724ede`](https://github.com/Folks-Finance/algorand-js-sdk/commit/a724edee55b4d9337ff7713b1b2a9dfc0b584543) Thanks [@gidonkatten](https://github.com/gidonkatten)! - new xalgo usdc lending pools
38 |
39 | ## 0.1.0
40 |
41 | ### Minor Changes
42 |
43 | - [#56](https://github.com/Folks-Finance/algorand-js-sdk/pull/56) [`592c0fa`](https://github.com/Folks-Finance/algorand-js-sdk/commit/592c0faa187cdd4542c8a26bae0a3310207ca8e1) Thanks [@gidonkatten](https://github.com/gidonkatten)! - updated transactions for new xalgo version
44 |
45 | ### Patch Changes
46 |
47 | - [#65](https://github.com/Folks-Finance/algorand-js-sdk/pull/65) [`1829e69`](https://github.com/Folks-Finance/algorand-js-sdk/commit/1829e696f76cf5ec3ae46ff884788435f7dddb36) Thanks [@gidonkatten](https://github.com/gidonkatten)! - add xalgo pool
48 |
49 | - [#56](https://github.com/Folks-Finance/algorand-js-sdk/pull/56) [`592c0fa`](https://github.com/Folks-Finance/algorand-js-sdk/commit/592c0faa187cdd4542c8a26bae0a3310207ca8e1) Thanks [@gidonkatten](https://github.com/gidonkatten)! - stake and deposit util
50 |
51 | ## 0.0.7
52 |
53 | ### Patch Changes
54 |
55 | - [#69](https://github.com/Folks-Finance/algorand-js-sdk/pull/69) [`30119c7`](https://github.com/Folks-Finance/algorand-js-sdk/commit/30119c7857855fcfcacb52e2afa36c8bc85af442) Thanks [@gidonkatten](https://github.com/gidonkatten)! - return group transaction for register online
56 |
57 | ## 0.0.6
58 |
59 | ### Patch Changes
60 |
61 | - [#67](https://github.com/Folks-Finance/algorand-js-sdk/pull/67) [`889fc32`](https://github.com/Folks-Finance/algorand-js-sdk/commit/889fc3231d327ed851607c1afed355b5caea54ac) Thanks [@gidonkatten](https://github.com/gidonkatten)! - fix register online abi for new distributor
62 |
63 | ## 0.0.5
64 |
65 | ### Patch Changes
66 |
67 | - [#64](https://github.com/Folks-Finance/algorand-js-sdk/pull/64) [`2f0e705`](https://github.com/Folks-Finance/algorand-js-sdk/commit/2f0e705f71e74eb1ba983d6059ae7e94459b464c) Thanks [@gidonkatten](https://github.com/gidonkatten)! - new galgo distributor
68 |
69 | ## 0.0.4
70 |
71 | ### Patch Changes
72 |
73 | - [#61](https://github.com/Folks-Finance/algorand-js-sdk/pull/61) [`c149f70`](https://github.com/Folks-Finance/algorand-js-sdk/commit/c149f700b352631ac2c6ecf693a747189607afed) Thanks [@gidonkatten](https://github.com/gidonkatten)! - fix testnet xalgo pool app id
74 |
75 | ## 0.0.3
76 |
77 | ### Patch Changes
78 |
79 | - [#59](https://github.com/Folks-Finance/algorand-js-sdk/pull/59) [`b094b00`](https://github.com/Folks-Finance/algorand-js-sdk/commit/b094b00dd429fd06aba4bd1055adc8b4e811005b) Thanks [@gidonkatten](https://github.com/gidonkatten)! - update testnet oracle and add xalgo pool to testnet
80 |
81 | ## 0.0.2
82 |
83 | ### Patch Changes
84 |
85 | - [#57](https://github.com/Folks-Finance/algorand-js-sdk/pull/57) [`87a3cf0`](https://github.com/Folks-Finance/algorand-js-sdk/commit/87a3cf0baad5e96bb40ac374b2b2a2efd461e095) Thanks [@stefanofa](https://github.com/stefanofa)! - Fixed unstake transaction bug by initializing arrays with BigInt(0)s to prevent undefined elements during iteration.
86 |
87 | ## 0.0.1
88 |
89 | ### Patch Changes
90 |
91 | - [#53](https://github.com/Folks-Finance/algorand-js-sdk/pull/53) [`b21e6dd`](https://github.com/Folks-Finance/algorand-js-sdk/commit/b21e6ddf0c2d6a8ab3a4e5779287a40369c5e792) Thanks [@stefanofa](https://github.com/stefanofa)! - Setup eslint
92 |
93 | - [#51](https://github.com/Folks-Finance/algorand-js-sdk/pull/51) [`72f5eb7`](https://github.com/Folks-Finance/algorand-js-sdk/commit/72f5eb7407afb402c0ae71afcd89d34ddde1d024) Thanks [@stefanofa](https://github.com/stefanofa)! - Migrate package to @folks-finance/algorand-sdk
94 |
95 | - [#54](https://github.com/Folks-Finance/algorand-js-sdk/pull/54) [`7b0f156`](https://github.com/Folks-Finance/algorand-js-sdk/commit/7b0f15666902b313af55b19df7f5215bd99cd059) Thanks [@stefanofa](https://github.com/stefanofa)! - Generate docs using github actions
96 |
97 | - [`2684d99`](https://github.com/Folks-Finance/algorand-js-sdk/commit/2684d995bba22afc81f358f928b66f469cadc9fe) Thanks [@stefanofa](https://github.com/stefanofa)! - Remove code related to v1 protocol
98 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Folks Finance
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @folks-finance/algorand-sdk
2 |
3 | [![License: MIT][license-image]][license-url]
4 | [![CI][ci-image]][ci-url]
5 | [![NPM version][npm-image]][npm-url]
6 | [![Downloads][downloads-image]][npm-url]
7 |
8 | 
9 |
10 | The official JavaScript SDK for the Algorand Folks Finance protocol
11 |
12 | ## Installation
13 |
14 | ### Package manager
15 |
16 | Using npm:
17 |
18 | ```bash
19 | npm install @folks-finance/algorand-sdk
20 | ```
21 |
22 | Using yarn:
23 |
24 | ```bash
25 | yarn add @folks-finance/algorand-sdk
26 | ```
27 |
28 | Using pnpm:
29 |
30 | ```bash
31 | pnpm add @folks-finance/algorand-sdk
32 | ```
33 |
34 | Using bun:
35 |
36 | ```bash
37 | bun add @folks-finance/algorand-sdk
38 | ```
39 |
40 | No extra setup is required if you're just using the sdk as a dependency in your project.
41 |
42 | ## Documentation
43 |
44 | Documentation is generated with TypeDoc and available at .
45 |
46 | ## Running Examples (For Contributors)
47 |
48 | If you've forked or cloned this repository, you can run the included examples:
49 |
50 | 1. Update `examples/config.ts` with your configuration.
51 | 2. Run an example, for example:
52 |
53 | ```bash
54 | pnpm example examples/v2/liquidate.ts
55 | ```
56 |
57 | [license-image]: https://img.shields.io/badge/License-MIT-brightgreen.svg?style=flat-square
58 | [license-url]: https://opensource.org/licenses/MIT
59 | [ci-image]: https://img.shields.io/github/actions/workflow/status/Folks-Finance/algorand-js-sdk/lint-and-typecheck.yml?branch=main&logo=github&style=flat-square
60 | [ci-url]: https://github.com/Folks-Finance/algorand-js-sdk/actions/workflows/lint-and-typecheck.yml
61 | [npm-image]: https://img.shields.io/npm/v/@folks-finance/algorand-sdk.svg?style=flat-square
62 | [npm-url]: https://www.npmjs.com/package/@folks-finance/algorand-sdk
63 | [downloads-image]: https://img.shields.io/npm/dm/@folks-finance/algorand-sdk.svg?style=flat-square
64 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import eslint from "@eslint/js";
2 | import tsParser from "@typescript-eslint/parser";
3 | import eslintConfigPrettier from "eslint-config-prettier";
4 | import eslintPluginImportX from "eslint-plugin-import-x";
5 | import eslintPluginUnicorn from "eslint-plugin-unicorn";
6 | import globals from "globals";
7 | import tseslint from "typescript-eslint";
8 |
9 | // eslint-disable-next-line import-x/no-default-export
10 | export default tseslint.config(
11 | { ignores: ["dist/", "docs/", "examples/"] },
12 | { files: ["**/*.{js,mjs,cjs,ts}"] },
13 | {
14 | languageOptions: {
15 | parser: tsParser,
16 | parserOptions: {
17 | project: true,
18 | tsconfigRootDir: import.meta.dirname,
19 | },
20 | globals: { ...globals.browser, ...globals.node },
21 | },
22 | },
23 | eslint.configs.recommended,
24 | tseslint.configs.recommended,
25 | {
26 | plugins: {
27 | "import-x": eslintPluginImportX,
28 | unicorn: eslintPluginUnicorn,
29 | },
30 | rules: {
31 | "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
32 | "import-x/no-cycle": "warn",
33 | "import-x/no-default-export": "error",
34 | "import-x/no-duplicates": ["error"],
35 | "import-x/no-named-as-default": "off",
36 | "import-x/no-unresolved": "error",
37 | "import-x/order": [
38 | "error",
39 | {
40 | groups: ["builtin", "external", "internal", "parent", "sibling", "index", "type"],
41 | "newlines-between": "always",
42 | alphabetize: { order: "asc" },
43 | },
44 | ],
45 | "@typescript-eslint/consistent-type-imports": ["error", { prefer: "type-imports" }],
46 | "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
47 | "@typescript-eslint/restrict-template-expressions": ["error", { allowNumber: true }],
48 | "@typescript-eslint/switch-exhaustiveness-check": "error",
49 | "unicorn/better-regex": "error",
50 | "unicorn/consistent-function-scoping": "error",
51 | "unicorn/expiring-todo-comments": "error",
52 | "unicorn/filename-case": ["error", { case: "kebabCase" }],
53 | "unicorn/no-array-for-each": "error",
54 | "unicorn/no-for-loop": "error",
55 | },
56 | },
57 | {
58 | files: ["**/*?(.c|.m)js"],
59 | ...tseslint.configs.disableTypeChecked,
60 | },
61 | {
62 | settings: {
63 | "import-x/parsers": {
64 | "@typescript-eslint/parser": [".ts", ".tsx"],
65 | },
66 | "import-x/resolver": {
67 | typescript: true,
68 | node: true,
69 | },
70 | },
71 | },
72 | { linterOptions: { reportUnusedDisableDirectives: true } },
73 | eslintConfigPrettier,
74 | );
75 |
--------------------------------------------------------------------------------
/examples/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Algodv2,
3 | generateAccount,
4 | Indexer,
5 | //mnemonicToSecretKey
6 | } from "algosdk";
7 |
8 | // TODO: Replace
9 | // export const sender = mnemonicToSecretKey("");
10 | export const sender = generateAccount();
11 |
12 | export const algodClient = new Algodv2("", "https://testnet-api.algonode.cloud/", 443);
13 | export const indexerClient = new Indexer("", "https://testnet-idx.algonode.cloud/", 443);
14 |
--------------------------------------------------------------------------------
/examples/lend/complex-flash-loan.ts:
--------------------------------------------------------------------------------
1 | import { assignGroupID } from "algosdk";
2 |
3 | import {
4 | calcFlashLoanRepayment,
5 | prepareFlashLoanBegin,
6 | prepareFlashLoanEnd,
7 | TestnetPools,
8 | TestnetReserveAddress,
9 | } from "../../src";
10 | import { algodClient, sender } from "../config";
11 |
12 | import type { Transaction } from "algosdk";
13 |
14 | async function main() {
15 | const reserveAddress = TestnetReserveAddress;
16 | const pools = TestnetPools;
17 |
18 | // can put arbitrary txns inside flash loan
19 | const insideTxns: Transaction[] = [];
20 |
21 | // retrieve params
22 | const params = await algodClient.getTransactionParams().do();
23 |
24 | // there are two ways you can use flash loans
25 | // will show here the complex version where have complete flexibility and
26 | // can do multiple flash loans in single group transaction
27 |
28 | // final result will be:
29 | // - flash loan borrow of 2 ALGO
30 | // - flash loan borrow of 1 USDC
31 | // -
32 | // - flash loan repay of 2.002001 ALGO (0.1% fee + 0.000001)
33 | // - flash loan repay of 1.002001 USDC (0.1% fee + 0.000001)
34 |
35 | // flash loan of 2 ALGO, repayment will be 2.002 ALGO (0.1% + 1)
36 | const algoBorrowAmount = 2e6;
37 | const algoRepaymentAmount = calcFlashLoanRepayment(BigInt(algoBorrowAmount), BigInt(0.001e16));
38 | const algoTxnIndexForFlashLoanEnd = insideTxns.length + 3;
39 | const algoFlashLoanBegin = prepareFlashLoanBegin(
40 | pools.ALGO,
41 | sender.addr,
42 | sender.addr,
43 | algoBorrowAmount,
44 | algoTxnIndexForFlashLoanEnd,
45 | params,
46 | );
47 | const algoFlashLoanEnd = prepareFlashLoanEnd(pools.ALGO, sender.addr, reserveAddress, algoRepaymentAmount, params);
48 |
49 | // flash loan of 1 USDC, repayment will be 1.001 USDC (0.1% + 1)
50 | const usdcBorrowAmount = 1e6;
51 | const usdcRepaymentAmount = calcFlashLoanRepayment(BigInt(usdcBorrowAmount), BigInt(0.001e16));
52 | const usdcTxnIndexForFlashLoanEnd = insideTxns.length + 5;
53 | const usdcFlashLoanBegin = prepareFlashLoanBegin(
54 | pools.USDC,
55 | sender.addr,
56 | sender.addr,
57 | usdcBorrowAmount,
58 | usdcTxnIndexForFlashLoanEnd,
59 | params,
60 | );
61 | const usdcFlashLoanEnd = prepareFlashLoanEnd(pools.USDC, sender.addr, reserveAddress, usdcRepaymentAmount, params);
62 |
63 | // build
64 | const flashLoanTxns: Transaction[] = [
65 | algoFlashLoanBegin,
66 | usdcFlashLoanBegin,
67 | ...insideTxns,
68 | ...algoFlashLoanEnd,
69 | ...usdcFlashLoanEnd,
70 | ];
71 |
72 | // group, sign and submit
73 | assignGroupID(flashLoanTxns);
74 | const signedTxns = flashLoanTxns.map((txn) => txn.signTxn(sender.sk));
75 | await algodClient.sendRawTransaction(signedTxns).do();
76 | }
77 |
78 | main().catch(console.error);
79 |
--------------------------------------------------------------------------------
/examples/lend/liquidate.ts:
--------------------------------------------------------------------------------
1 | import { assignGroupID, waitForConfirmation } from "algosdk";
2 |
3 | import {
4 | getOraclePrices,
5 | prefixWithOpUp,
6 | prepareLiquidateLoan,
7 | retrieveLiquidatableLoans,
8 | retrieveLoanInfo,
9 | retrievePoolManagerInfo,
10 | TestnetLoans,
11 | TestnetOpUp,
12 | TestnetOracle,
13 | TestnetPoolManagerAppId,
14 | TestnetPools,
15 | TestnetReserveAddress
16 | } from "../../src";
17 | import { algodClient, indexerClient, sender } from "../config";
18 |
19 | import type {
20 | LPToken,
21 | LPTokenPool,
22 | Pool,
23 | UserLoanInfo} from "../../src";
24 |
25 | function sleep(ms: number) {
26 | return new Promise((resolve) => setTimeout(resolve, ms));
27 | }
28 |
29 | const isLPTokenPool = (pool: Pool): pool is LPTokenPool => {
30 | return "lpToken" in pool;
31 | };
32 |
33 | const getAssetFromAppId = (pools: Record, appId: number): LPToken | number => {
34 | const [, pool] = Object.entries(pools).find(([_, pool]) => pool.appId === appId)!;
35 | return isLPTokenPool(pool) ? (pool as LPTokenPool).lpToken : pool.assetId;
36 | };
37 |
38 | export const getUserLoanAssets = (pools: Record, userLoan: UserLoanInfo) => {
39 | const lpAssets: LPToken[] = [];
40 | const baseAssetIds: number[] = [];
41 | const loanPoolAppIds = new Set(); // use set to remove duplicates (assets which are both collateral and borrow)
42 |
43 | for (const { poolAppId } of userLoan.collaterals) loanPoolAppIds.add(poolAppId);
44 | for (const { poolAppId } of userLoan.borrows) loanPoolAppIds.add(poolAppId);
45 |
46 | // add to lp assets and base assets
47 | for (const poolAppId of loanPoolAppIds) {
48 | const asset = getAssetFromAppId(pools, poolAppId);
49 | if (Number.isNaN(asset)) {
50 | lpAssets.push(asset as LPToken);
51 | } else {
52 | baseAssetIds.push(asset as number);
53 | }
54 | }
55 |
56 | return { lpAssets, baseAssetIds };
57 | };
58 |
59 | async function main() {
60 | const poolManagerAppId = TestnetPoolManagerAppId;
61 | const loanAppId = TestnetLoans.GENERAL!;
62 | const oracle = TestnetOracle;
63 | const pools = TestnetPools;
64 | const reserveAddress = TestnetReserveAddress;
65 | const opup = TestnetOpUp;
66 |
67 | // get pool manager info, loan info and oracle prices
68 | const poolManagerInfo = await retrievePoolManagerInfo(indexerClient, poolManagerAppId);
69 | const loanInfo = await retrieveLoanInfo(indexerClient, loanAppId);
70 | const oraclePrices = await getOraclePrices(indexerClient, oracle);
71 |
72 | // retrieve params
73 | const params = await algodClient.getTransactionParams().do();
74 |
75 | // loop through loans and liquidate them if possible
76 | let nextToken = undefined;
77 | do {
78 | // sleep for 0.1 seconds to prevent hitting request limit
79 | await sleep(100);
80 |
81 | // find liquidatable loans
82 | const liquidatableLoans: { loans: UserLoanInfo[]; nextToken?: string } = await retrieveLiquidatableLoans(
83 | indexerClient,
84 | loanAppId,
85 | poolManagerInfo,
86 | loanInfo,
87 | oraclePrices,
88 | nextToken,
89 | );
90 | nextToken = liquidatableLoans.nextToken;
91 |
92 | // liquidate
93 | for (const loan of liquidatableLoans.loans) {
94 | // decide on which collateral to seize
95 | const [, collateralPool] = Object.entries(pools).find(
96 | ([_, pool]) => pool.appId === loan.collaterals[0].poolAppId,
97 | )!;
98 |
99 | // decide on which borrow to repay
100 | const [, borrowPool] = Object.entries(pools).find(([_, pool]) => pool.appId === loan.borrows[0].poolAppId)!;
101 |
102 | // decide on how much to repay
103 | const repayAmount = loan.borrows[0].borrowBalance / BigInt(2);
104 |
105 | // decide on minimum collateral willing to accept
106 | // TODO: MUST SET IF RUNNING ACTUAL LIQUIDATOR
107 | const minCollateral = 0;
108 |
109 | // get assets in user loan
110 | const { lpAssets, baseAssetIds } = getUserLoanAssets(pools, loan);
111 |
112 | // transaction
113 | let liquidateTxns = prepareLiquidateLoan(
114 | loanAppId,
115 | poolManagerAppId,
116 | sender.addr,
117 | loan.escrowAddress,
118 | reserveAddress,
119 | collateralPool,
120 | borrowPool,
121 | oracle,
122 | lpAssets,
123 | baseAssetIds,
124 | repayAmount,
125 | minCollateral,
126 | loan.borrows[0].isStable,
127 | params,
128 | );
129 |
130 | // add opup transactions to increase opcode budget TODO better estimate
131 | const budget = Math.ceil(10 + lpAssets.length + 0.5 * baseAssetIds.length);
132 | liquidateTxns = prefixWithOpUp(opup, sender.addr, liquidateTxns, budget, params);
133 |
134 | // group, sign and submit
135 | assignGroupID(liquidateTxns);
136 | const signedTxns = liquidateTxns.map((txn) => txn.signTxn(sender.sk));
137 | try {
138 | const { txId } = await algodClient.sendRawTransaction(signedTxns).do();
139 | await waitForConfirmation(algodClient, txId, 1000);
140 | console.log("Successfully liquidated: " + loan.escrowAddress);
141 | } catch (e) {
142 | console.error(e);
143 | console.log("Failed to liquidate: " + loan.escrowAddress);
144 | }
145 | }
146 | } while (nextToken !== undefined);
147 | }
148 |
149 | main().catch(console.error);
150 |
--------------------------------------------------------------------------------
/examples/lend/simple-flash-loan.ts:
--------------------------------------------------------------------------------
1 | import { assignGroupID } from "algosdk";
2 |
3 | import { TestnetPools, TestnetReserveAddress, wrapWithFlashLoan } from "../../src";
4 | import { algodClient, sender } from "../config";
5 |
6 | import type { Transaction } from "algosdk";
7 |
8 | async function main() {
9 | const reserveAddress = TestnetReserveAddress;
10 | const pools = TestnetPools;
11 |
12 | // can put arbitrary txns inside flash loan
13 | const insideTxns: Transaction[] = [];
14 |
15 | // retrieve params
16 | const params = await algodClient.getTransactionParams().do();
17 |
18 | // there are two ways you can use flash loans
19 | // will show here the simple version where just wrapping inside txns with flash loan
20 |
21 | // final result will be:
22 | // - flash loan borrow of 1 ALGO
23 | // -
24 | // - flash loan repay of 1.001001 ALGO (0.1% fee + 0.000001)
25 |
26 | // flash loan of 1 ALGO, repayment will be 1.001001 ALGO (0.1% + 0.000001)
27 | const algoBorrowAmount = 1e6;
28 | const flashLoanTxns = wrapWithFlashLoan(
29 | insideTxns,
30 | pools.ALGO,
31 | sender.addr,
32 | sender.addr,
33 | reserveAddress,
34 | algoBorrowAmount,
35 | params,
36 | );
37 |
38 | // group, sign and submit
39 | assignGroupID(flashLoanTxns);
40 | const signedTxns = flashLoanTxns.map((txn) => txn.signTxn(sender.sk));
41 | await algodClient.sendRawTransaction(signedTxns).do();
42 | }
43 |
44 | main().catch(console.error);
45 |
--------------------------------------------------------------------------------
/examples/xalgo/stake.ts:
--------------------------------------------------------------------------------
1 | import { assignGroupID } from "algosdk";
2 |
3 | import {
4 | getConsensusState,
5 | prefixWithOpUp,
6 | prepareImmediateStakeTransactions,
7 | TestnetConsensusConfig,
8 | TestnetOpUp,
9 | } from "../../src";
10 | import { algodClient, sender } from "../config";
11 |
12 | async function main() {
13 | const opup = TestnetOpUp;
14 | const consensusConfig = TestnetConsensusConfig;
15 |
16 | // retrieve params and consensus state
17 | const params = await algodClient.getTransactionParams().do();
18 | const consensusState = await getConsensusState(algodClient, consensusConfig);
19 |
20 | // stake 1 ALGO
21 | const stakeAmount = 1e6;
22 | const minReceivedAmount = 0;
23 | let stakeTransactions = prepareImmediateStakeTransactions(
24 | consensusConfig,
25 | consensusState,
26 | sender.addr,
27 | sender.addr,
28 | stakeAmount,
29 | minReceivedAmount,
30 | params,
31 | );
32 |
33 | // add additional opcode budget (if needed)
34 | stakeTransactions = prefixWithOpUp(opup, sender.addr, stakeTransactions, 0, params);
35 |
36 | // group, sign and submit
37 | assignGroupID(stakeTransactions);
38 | const signedTxns = stakeTransactions.map((txn) => txn.signTxn(sender.sk));
39 | await algodClient.sendRawTransaction(signedTxns).do();
40 | }
41 |
42 | main().catch(console.error);
43 |
--------------------------------------------------------------------------------
/media/repo-header.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Folks-Finance/algorand-js-sdk/5d48e97b05c03a744db0e1ad52fda1859827a28a/media/repo-header.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@folks-finance/algorand-sdk",
3 | "version": "0.1.6",
4 | "description": "The official JavaScript SDK for the Folks Finance Protocol",
5 | "main": "dist/index.js",
6 | "types": "dist/types/index.d.ts",
7 | "files": [
8 | "dist/**/*",
9 | "src"
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/Folks-Finance/algorand-js-sdk.git"
14 | },
15 | "scripts": {
16 | "prepare": "husky",
17 | "build": "tsc -p tsconfig.json",
18 | "typecheck": "tsc --noEmit",
19 | "lint": "eslint .",
20 | "format": "prettier --write 'src/**/*'",
21 | "gen-docs": "typedoc",
22 | "example": "pnpx tsx",
23 | "release": "pnpm build && changeset publish"
24 | },
25 | "dependencies": {
26 | "algosdk": "^2.9.0"
27 | },
28 | "devDependencies": {
29 | "@changesets/changelog-github": "^0.5.0",
30 | "@changesets/cli": "^2.27.10",
31 | "@eslint/js": "^9.16.0",
32 | "@types/node": "^22.10.1",
33 | "@typescript-eslint/parser": "^8.18.0",
34 | "@typhonjs-typedoc/typedoc-theme-dmt": "^0.3.0",
35 | "eslint": "^9.16.0",
36 | "eslint-config-prettier": "^9.1.0",
37 | "eslint-import-resolver-typescript": "^3.7.0",
38 | "eslint-plugin-import-x": "^4.5.0",
39 | "eslint-plugin-unicorn": "^56.0.1",
40 | "globals": "^15.13.0",
41 | "husky": "^9.1.7",
42 | "lint-staged": "^15.2.10",
43 | "prettier": "^3.4.2",
44 | "typedoc": "^0.27.4",
45 | "typescript": "^5.7.2",
46 | "typescript-eslint": "^8.17.0"
47 | },
48 | "author": "Folks Finance",
49 | "license": "MIT",
50 | "lint-staged": {
51 | "*.src/**/*": "prettier --write"
52 | },
53 | "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c",
54 | "keywords": [
55 | "folks-finance",
56 | "lending",
57 | "algorand",
58 | "blockchain",
59 | "defi"
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/prettier.config.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('prettier').Options}
3 | */
4 | // eslint-disable-next-line import-x/no-default-export
5 | export default {
6 | printWidth: 120,
7 | tabWidth: 2,
8 | useTabs: false,
9 | semi: true,
10 | singleQuote: false,
11 | trailingComma: "all",
12 | };
13 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/common/governance.ts:
--------------------------------------------------------------------------------
1 | import { getParsedValueFromState, parseUint64s } from "../../utils";
2 |
3 | import type { Dispenser, DispenserInfo } from "./types";
4 | import type { Indexer } from "algosdk";
5 |
6 | /**
7 | *
8 | * Returns information regarding the given liquid governance dispenser.
9 | *
10 | * @param indexerClient - Algorand indexer client to query
11 | * @param dispenser - dispenser to query about
12 | * @returns DispenserInfo[] dispenser info
13 | */
14 | export async function getDispenserInfo(indexerClient: Indexer, dispenser: Dispenser): Promise {
15 | const { appId } = dispenser;
16 | const res = await indexerClient.lookupApplications(appId).do();
17 | const state = res["application"]["params"]["global-state"];
18 |
19 | const distributorAppIds = parseUint64s(String(getParsedValueFromState(state, "distribs"))).map((appId) =>
20 | Number(appId),
21 | );
22 | const isMintingPaused = Boolean(getParsedValueFromState(state, "is_minting_paused") || 0);
23 |
24 | return {
25 | currentRound: res["current-round"],
26 | distributorAppIds,
27 | isMintingPaused,
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/common/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./mainnet-constants";
2 | export * from "./governance";
3 | export * from "./types";
4 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/common/mainnet-constants.ts:
--------------------------------------------------------------------------------
1 | import type { Dispenser } from "./types";
2 |
3 | const govDispenser: Dispenser = {
4 | appId: 793119194,
5 | gAlgoId: 793124631,
6 | };
7 |
8 | export { govDispenser };
9 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/common/types.ts:
--------------------------------------------------------------------------------
1 | interface Dispenser {
2 | appId: number;
3 | gAlgoId: number;
4 | }
5 |
6 | interface DispenserInfo {
7 | currentRound: number; // round the data was read at
8 | distributorAppIds: number[]; // list of valid distributor app ids
9 | isMintingPaused: boolean; // flag indicating if users can mint gALGO
10 | }
11 |
12 | interface Distributor {
13 | appId: number;
14 | }
15 |
16 | export { Dispenser, DispenserInfo, Distributor };
17 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v1/constants/abi-contracts.ts:
--------------------------------------------------------------------------------
1 | import { ABIContract } from "algosdk";
2 |
3 | const abiDistributor = new ABIContract({
4 | name: "algo-liquid-governance-distributor",
5 | desc: "Mints gALGO when called by verified distributor applications",
6 | methods: [
7 | {
8 | name: "mint",
9 | desc: "Mint equivalent amount of gALGO as ALGO sent (if in commitment period then also commits user)",
10 | args: [
11 | { type: "pay", name: "algo_sent", desc: "Send ALGO to the distributor application account" },
12 | { type: "asset", name: "g_algo", desc: "The gALGO asset" },
13 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
14 | ],
15 | returns: { type: "void" },
16 | },
17 | {
18 | name: "unmint_premint",
19 | desc: "Unmint in the commitment period pre-minted gALGO for equivalent amount of ALGO",
20 | args: [
21 | { type: "uint64", name: "unmint_amount", desc: "The amount of pre-minted gALGO to unmint" },
22 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
23 | ],
24 | returns: { type: "void" },
25 | },
26 | {
27 | name: "unmint",
28 | desc: "Unmint in the commitment period gALGO for equivalent amount of ALGO as gALGO sent",
29 | args: [
30 | { type: "axfer", name: "g_algo_sent", desc: "Send gALGO to the distributor application account" },
31 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
32 | ],
33 | returns: { type: "void" },
34 | },
35 | {
36 | name: "claim_premint",
37 | desc: "Claim pre-minted gALGO on behalf of yourself or another account",
38 | args: [
39 | { type: "account", name: "receiver", desc: "The user that pre-minted gALGO" },
40 | { type: "asset", name: "g_algo", desc: "The gALGO asset" },
41 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
42 | ],
43 | returns: { type: "void" },
44 | },
45 | {
46 | name: "burn",
47 | desc: "Burn after the governance period gALGO for equivalent amount of ALGO as gALGO sent",
48 | args: [
49 | { type: "axfer", name: "g_algo_sent", desc: "Send gALGO to the distributor application account" },
50 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
51 | ],
52 | returns: { type: "void" },
53 | },
54 | {
55 | name: "early_claim_rewards",
56 | desc: "Early claim governance rewards in the form of gALGO",
57 | args: [
58 | { type: "uint64", name: "amount", desc: "The amount of committed ALGO to early claim rewards on" },
59 | { type: "asset", name: "g_algo", desc: "The gALGO asset" },
60 | { type: "application", name: "dispenser", desc: "The dispenser application that mints gALGO" },
61 | ],
62 | returns: { type: "void" },
63 | },
64 | {
65 | name: "claim_rewards",
66 | desc: "Claim governance rewards in the form of ALGO after they have been distributed by the foundation",
67 | args: [],
68 | returns: { type: "void" },
69 | },
70 | ],
71 | });
72 |
73 | export { abiDistributor };
74 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v1/constants/mainnet-constants.ts:
--------------------------------------------------------------------------------
1 | import type { Distributor } from "../../common";
2 |
3 | const govDistributor4: Distributor = {
4 | appId: 793119270,
5 | };
6 |
7 | const govDistributor5a: Distributor = {
8 | appId: 887391617,
9 | };
10 |
11 | const govDistributor5b: Distributor = {
12 | appId: 902731930,
13 | };
14 |
15 | const govDistributor6: Distributor = {
16 | appId: 991196662,
17 | };
18 |
19 | export { govDistributor4, govDistributor5a, govDistributor5b, govDistributor6 };
20 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v1/governance.ts:
--------------------------------------------------------------------------------
1 | import {
2 | assignGroupID,
3 | AtomicTransactionComposer,
4 | getApplicationAddress,
5 | getMethodByName,
6 | makeApplicationOptInTxn,
7 | } from "algosdk";
8 |
9 | import { getParsedValueFromState, signer, transferAlgoOrAsset } from "../../utils";
10 |
11 | import { abiDistributor } from "./constants/abi-contracts";
12 |
13 | import type { DistributorInfo, UserCommitmentInfo } from "./types";
14 | import type { Dispenser, Distributor } from "../common";
15 | import type { Indexer, SuggestedParams, Transaction } from "algosdk";
16 |
17 | /**
18 | *
19 | * Returns information regarding the given liquid governance distributor.
20 | *
21 | * @param indexerClient - Algorand indexer client to query
22 | * @param distributor - distributor to query about
23 | * @returns DistributorInfo[] distributor info
24 | */
25 | async function getDistributorInfo(indexerClient: Indexer, distributor: Distributor): Promise {
26 | const { appId } = distributor;
27 | const res = await indexerClient.lookupApplications(appId).do();
28 | const state = res["application"]["params"]["global-state"];
29 |
30 | const dispenserAppId = Number(getParsedValueFromState(state, "dispenser_app_id") || 0);
31 | const commitEnd = BigInt(getParsedValueFromState(state, "commit_end") || 0);
32 | const periodEnd = BigInt(getParsedValueFromState(state, "period_end") || 0);
33 | const totalCommitment = BigInt(getParsedValueFromState(state, "total_commitment") || 0);
34 | const totalCommitmentClaimed = BigInt(getParsedValueFromState(state, "total_commitment_claimed") || 0);
35 | const canClaimAlgoRewards = Boolean(getParsedValueFromState(state, "can_claim_algo_rewards") || 0);
36 | const rewardsPerAlgo = BigInt(getParsedValueFromState(state, "rewards_per_algo") || 0);
37 | const totalRewardsClaimed = BigInt(getParsedValueFromState(state, "total_rewards_claimed") || 0);
38 | const isBurningPaused = Boolean(getParsedValueFromState(state, "is_burning_paused") || 0);
39 |
40 | // optional
41 | const premintEndState = getParsedValueFromState(state, "premint_end");
42 | const premintEnd = premintEndState !== undefined ? BigInt(premintEndState) : undefined;
43 |
44 | return {
45 | currentRound: res["current-round"],
46 | dispenserAppId,
47 | premintEnd,
48 | commitEnd,
49 | periodEnd,
50 | totalCommitment,
51 | totalCommitmentClaimed,
52 | canClaimAlgoRewards,
53 | rewardsPerAlgo,
54 | totalRewardsClaimed,
55 | isBurningPaused,
56 | };
57 | }
58 |
59 | /**
60 | *
61 | * Returns information regarding a user's liquid governance commitment.
62 | *
63 | * @param indexerClient - Algorand indexer client to query
64 | * @param distributor - distributor to query about
65 | * @param userAddr - user address to get governance info about
66 | * @returns UserCommitmentInfo[] user commitment info
67 | */
68 | async function getUserLiquidGovernanceInfo(
69 | indexerClient: Indexer,
70 | distributor: Distributor,
71 | userAddr: string,
72 | ): Promise {
73 | const { appId } = distributor;
74 |
75 | // get user account local state
76 | const req = indexerClient.lookupAccountAppLocalStates(userAddr).applicationID(appId);
77 | const res = await req.do();
78 |
79 | // user local state
80 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
81 | const state = res["apps-local-states"]?.find((app: any) => app.id === appId)?.["key-value"];
82 | if (state === undefined) throw new Error("Unable to find commitment for: " + userAddr + ".");
83 | const commitment = BigInt(getParsedValueFromState(state, "commitment") || 0);
84 | const commitmentClaimed = BigInt(getParsedValueFromState(state, "commitment_claimed") || 0);
85 |
86 | // optional
87 | const premintState = getParsedValueFromState(state, "premint");
88 | const premint = premintState !== undefined ? BigInt(premintState) : undefined;
89 |
90 | return {
91 | currentRound: res["current-round"],
92 | premint,
93 | commitment,
94 | commitmentClaimed,
95 | };
96 | }
97 |
98 | /**
99 | *
100 | * Returns a group transaction to mint gALGO for ALGO at a one-to-one rate.
101 | * If in the commitment period then also commits user into governance.
102 | *
103 | * @param dispenser - dispenser to mint gALGO from
104 | * @param distributor - distributor that calls dispenser and to send ALGO to
105 | * @param senderAddr - account address for the sender
106 | * @param amount - amount of ALGO to send and gALGO to mint
107 | * @param includeOptIn - whether to include an opt in transaction (must be opted in if minting in commitment period)
108 | * @param params - suggested params for the transactions with the fees overwritten
109 | * @param note - optional note to distinguish who is the minter (must pass to be eligible for revenue share)
110 | * @returns Transaction[] mint transactions
111 | */
112 | function prepareMintTransactions(
113 | dispenser: Dispenser,
114 | distributor: Distributor,
115 | senderAddr: string,
116 | amount: number | bigint,
117 | includeOptIn: boolean,
118 | params: SuggestedParams,
119 | note?: Uint8Array,
120 | ): Transaction[] {
121 | const atc = new AtomicTransactionComposer();
122 | const payment = {
123 | txn: transferAlgoOrAsset(0, senderAddr, getApplicationAddress(distributor.appId), amount, {
124 | ...params,
125 | fee: 0,
126 | flatFee: true,
127 | }),
128 | signer,
129 | };
130 | atc.addMethodCall({
131 | sender: senderAddr,
132 | signer,
133 | appID: distributor.appId,
134 | method: getMethodByName(abiDistributor.methods, "mint"),
135 | methodArgs: [payment, dispenser.gAlgoId, dispenser.appId],
136 | suggestedParams: { ...params, flatFee: true, fee: 4000 },
137 | note,
138 | });
139 |
140 | const txns = atc.buildGroup().map(({ txn }) => {
141 | txn.group = undefined;
142 | return txn;
143 | });
144 | // for ledger compatibility (max 2 app args), remove index references which are not strictly needed
145 | txns[1].appArgs = txns[1].appArgs?.slice(0, -2);
146 | // user must be opted in before they can mint in the commitment period
147 | if (includeOptIn)
148 | txns.unshift(makeApplicationOptInTxn(senderAddr, { ...params, fee: 1000, flatFee: true }, distributor.appId));
149 | return assignGroupID(txns);
150 | }
151 |
152 | /**
153 | *
154 | * Returns a transaction to unmint pre-minted gALGO for ALGO at a one-to-one rate.
155 | * Must be in commitment period. By unminting, you will lose your governance rewards.
156 | *
157 | * @param dispenser - dispenser to send gALGO to
158 | * @param distributor - distributor to receive ALGO from
159 | * @param senderAddr - account address for the sender
160 | * @param amount - amount of gALGO to unmint and ALGO to receive
161 | * @param params - suggested params for the transactions with the fees overwritten
162 | * @param note - optional note to distinguish who is the unminter (must pass to be eligible for revenue share)
163 | * @returns Transaction unmint pre-mint transaction
164 | */
165 | function prepareUnmintPremintTransaction(
166 | dispenser: Dispenser,
167 | distributor: Distributor,
168 | senderAddr: string,
169 | amount: number | bigint,
170 | params: SuggestedParams,
171 | note?: Uint8Array,
172 | ): Transaction {
173 | const atc = new AtomicTransactionComposer();
174 | atc.addMethodCall({
175 | sender: senderAddr,
176 | signer,
177 | appID: distributor.appId,
178 | method: getMethodByName(abiDistributor.methods, "unmint_premint"),
179 | methodArgs: [amount, dispenser.appId],
180 | suggestedParams: { ...params, flatFee: true, fee: 2000 },
181 | note,
182 | });
183 | const txns = atc.buildGroup().map(({ txn }) => {
184 | txn.group = undefined;
185 | return txn;
186 | });
187 | return txns[0];
188 | }
189 |
190 | /**
191 | *
192 | * Returns a group transaction to unmint gALGO for ALGO at a one-to-one rate.
193 | * Must be in commitment period. By unminting, you will lose your governance rewards.
194 | *
195 | * @param dispenser - dispenser to send gALGO to
196 | * @param distributor - distributor to receive ALGO from
197 | * @param senderAddr - account address for the sender
198 | * @param amount - amount of gALGO to send and ALGO to receive
199 | * @param params - suggested params for the transactions with the fees overwritten
200 | * @param note - optional note to distinguish who is the unminter (must pass to be eligible for revenue share)
201 | * @returns Transaction[] unmint transactions
202 | */
203 | function prepareUnmintTransactions(
204 | dispenser: Dispenser,
205 | distributor: Distributor,
206 | senderAddr: string,
207 | amount: number | bigint,
208 | params: SuggestedParams,
209 | note?: Uint8Array,
210 | ): Transaction[] {
211 | const atc = new AtomicTransactionComposer();
212 | const assetTransfer = {
213 | txn: transferAlgoOrAsset(dispenser.gAlgoId, senderAddr, getApplicationAddress(dispenser.appId), amount, {
214 | ...params,
215 | fee: 0,
216 | flatFee: true,
217 | }),
218 | signer,
219 | };
220 | atc.addMethodCall({
221 | sender: senderAddr,
222 | signer,
223 | appID: distributor.appId,
224 | method: getMethodByName(abiDistributor.methods, "unmint"),
225 | methodArgs: [assetTransfer, dispenser.appId],
226 | suggestedParams: { ...params, flatFee: true, fee: 3000 },
227 | note,
228 | });
229 |
230 | const txns = atc.buildGroup().map(({ txn }) => {
231 | txn.group = undefined;
232 | return txn;
233 | });
234 | return assignGroupID(txns);
235 | }
236 |
237 | /**
238 | *
239 | * Returns a transaction to claim pre-minted gALGO.
240 | * Can be called on behalf of yourself or another user.
241 | *
242 | * @param dispenser - dispenser to send gALGO to
243 | * @param distributor - distributor to receive ALGO from
244 | * @param senderAddr - account address for the sender
245 | * @param receiverAddr - account address for the pre-minter that will receiver the gALGO
246 | * @param params - suggested params for the transactions with the fees overwritten
247 | * @returns Transaction claim pre-mint transaction
248 | */
249 | function prepareClaimPremintTransaction(
250 | dispenser: Dispenser,
251 | distributor: Distributor,
252 | senderAddr: string,
253 | receiverAddr: string,
254 | params: SuggestedParams,
255 | ): Transaction {
256 | const atc = new AtomicTransactionComposer();
257 | atc.addMethodCall({
258 | sender: senderAddr,
259 | signer,
260 | appID: distributor.appId,
261 | method: getMethodByName(abiDistributor.methods, "claim_premint"),
262 | methodArgs: [receiverAddr, dispenser.gAlgoId, dispenser.appId],
263 | suggestedParams: { ...params, flatFee: true, fee: 3000 },
264 | });
265 | const txns = atc.buildGroup().map(({ txn }) => {
266 | txn.group = undefined;
267 | return txn;
268 | });
269 | return txns[0];
270 | }
271 |
272 | /**
273 | *
274 | * Returns a group transaction to burn gALGO for ALGO at a one-to-one rate.
275 | * Must be after period end.
276 | *
277 | * @param dispenser - dispenser to send gALGO to
278 | * @param distributor - distributor to receive ALGO from
279 | * @param senderAddr - account address for the sender
280 | * @param amount - amount of gALGO to send and ALGO to receive
281 | * @param params - suggested params for the transactions with the fees overwritten
282 | * @returns Transaction[] burn transactions
283 | */
284 | function prepareBurnTransactions(
285 | dispenser: Dispenser,
286 | distributor: Distributor,
287 | senderAddr: string,
288 | amount: number | bigint,
289 | params: SuggestedParams,
290 | ): Transaction[] {
291 | const atc = new AtomicTransactionComposer();
292 | const assetTransfer = {
293 | txn: transferAlgoOrAsset(dispenser.gAlgoId, senderAddr, getApplicationAddress(dispenser.appId), amount, {
294 | ...params,
295 | fee: 0,
296 | flatFee: true,
297 | }),
298 | signer,
299 | };
300 | atc.addMethodCall({
301 | sender: senderAddr,
302 | signer,
303 | appID: distributor.appId,
304 | method: getMethodByName(abiDistributor.methods, "burn"),
305 | methodArgs: [assetTransfer, dispenser.appId],
306 | suggestedParams: { ...params, flatFee: true, fee: 3000 },
307 | });
308 |
309 | const txns = atc.buildGroup().map(({ txn }) => {
310 | txn.group = undefined;
311 | return txn;
312 | });
313 | return assignGroupID(txns);
314 | }
315 |
316 | /**
317 | *
318 | * Returns a group transaction to early claim governance rewards for a given amount of ALGO.
319 | * Must be after commitment end.
320 | * Rewards received is in gALGO. Amount of rewards is determined by rewards_per_algo.
321 | *
322 | * @param dispenser - distributor to receive gALGO from
323 | * @param distributor - distributor which has sender's commitment
324 | * @param senderAddr - account address for the sender
325 | * @param amount - amount of ALGO to early claim rewards on
326 | * @param params - suggested params for the transactions with the fees overwritten
327 | * @returns Transaction early claim governance rewards transaction
328 | */
329 | function prepareEarlyClaimGovernanceRewardsTransaction(
330 | dispenser: Dispenser,
331 | distributor: Distributor,
332 | senderAddr: string,
333 | amount: number | bigint,
334 | params: SuggestedParams,
335 | ): Transaction {
336 | const atc = new AtomicTransactionComposer();
337 | atc.addMethodCall({
338 | sender: senderAddr,
339 | signer,
340 | appID: distributor.appId,
341 | method: getMethodByName(abiDistributor.methods, "early_claim_rewards"),
342 | methodArgs: [amount, dispenser.gAlgoId, dispenser.appId],
343 | suggestedParams: { ...params, flatFee: true, fee: 3000 },
344 | });
345 |
346 | const txns = atc.buildGroup().map(({ txn }) => {
347 | txn.group = undefined;
348 | return txn;
349 | });
350 | // for ledger compatibility (max 2 app args), remove index references which are not strictly needed
351 | txns[0].appArgs = txns[0].appArgs?.slice(0, -2);
352 | return txns[0];
353 | }
354 |
355 | /**
356 | *
357 | * Returns a group transaction to claim governance rewards for unclaimed commitment.
358 | * Must be after period end and rewards have been distributed from Algorand Foundation.
359 | * Rewards received is in ALGO. Amount of rewards is determined by rewards_per_algo.
360 | *
361 | * @param distributor - distributor that calls dispenser and to send ALGO to
362 | * @param senderAddr - account address for the sender
363 | * @param params - suggested params for the transactions with the fees overwritten
364 | * @returns Transaction claim governance rewards transaction
365 | */
366 | function prepareClaimGovernanceRewardsTransaction(
367 | distributor: Distributor,
368 | senderAddr: string,
369 | params: SuggestedParams,
370 | ): Transaction {
371 | const atc = new AtomicTransactionComposer();
372 | atc.addMethodCall({
373 | sender: senderAddr,
374 | signer,
375 | appID: distributor.appId,
376 | method: getMethodByName(abiDistributor.methods, "claim_rewards"),
377 | suggestedParams: { ...params, flatFee: true, fee: 2000 },
378 | });
379 | const txns = atc.buildGroup().map(({ txn }) => {
380 | txn.group = undefined;
381 | return txn;
382 | });
383 | return txns[0];
384 | }
385 |
386 | export {
387 | getDistributorInfo,
388 | getUserLiquidGovernanceInfo,
389 | prepareMintTransactions,
390 | prepareUnmintPremintTransaction,
391 | prepareUnmintTransactions,
392 | prepareClaimPremintTransaction,
393 | prepareBurnTransactions,
394 | prepareEarlyClaimGovernanceRewardsTransaction,
395 | prepareClaimGovernanceRewardsTransaction,
396 | };
397 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v1/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants/abi-contracts";
2 | export * from "./constants/mainnet-constants";
3 | export * from "./governance";
4 | export * from "./types";
5 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v1/types.ts:
--------------------------------------------------------------------------------
1 | interface DistributorInfo {
2 | currentRound: number; // round the data was read at
3 | dispenserAppId: number; // id of dispenser app which mints gALGO
4 | premintEnd?: bigint; // unix timestamp for the end of the pre-mint period
5 | commitEnd: bigint; // unix timestamp for end of the commitment period
6 | periodEnd: bigint; // unix timestamp for end of the governance period
7 | totalCommitment: bigint; // total amount of ALGOs committed
8 | totalCommitmentClaimed: bigint; // total amount of ALGOs committed whose rewards have already been claimed
9 | canClaimAlgoRewards: boolean; // flag to indicate if users can claim ALGO rewards (excl early claims)
10 | rewardsPerAlgo: bigint; // reward amount per ALGO committed (16 d.p.)
11 | totalRewardsClaimed: bigint; // total amount of rewards claimed
12 | isBurningPaused: boolean; // flag to indicate if users can burn their ALGO for gALGO
13 | }
14 |
15 | interface UserCommitmentInfo {
16 | currentRound: number;
17 | premint?: bigint; // amount of ALGOs the user has pre-minted and not yet claimed
18 | commitment: bigint; // amount of ALGOs the user has committed
19 | commitmentClaimed: bigint; // amount of ALGOs the user has committed whose rewards have already been claimed
20 | }
21 |
22 | export { DistributorInfo, UserCommitmentInfo };
23 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v2/constants/abi-contracts.ts:
--------------------------------------------------------------------------------
1 | import { ABIContract } from "algosdk";
2 |
3 | const abiDistributor = new ABIContract({
4 | name: "algo-liquid-governance-distributor",
5 | desc: "Mints gALGO when called by verified distributor applications",
6 | methods: [
7 | {
8 | name: "add_escrow",
9 | desc: "",
10 | args: [
11 | { type: "pay", name: "user_call", desc: "" },
12 | { type: "bool", name: "delegate", desc: "" },
13 | ],
14 | returns: { type: "void" },
15 | },
16 | {
17 | name: "mint",
18 | desc: "",
19 | args: [
20 | { type: "pay", name: "send_algo", desc: "" },
21 | { type: "account", name: "escrow", desc: "" },
22 | { type: "application", name: "dispenser", desc: "" },
23 | { type: "asset", name: "g_algo", desc: "" },
24 | { type: "bool", name: "ensure_commit", desc: "" },
25 | ],
26 | returns: { type: "void" },
27 | },
28 | {
29 | name: "unmint_premint",
30 | desc: "",
31 | args: [
32 | { type: "account", name: "escrow", desc: "" },
33 | { type: "uint64", name: "unmint_amount", desc: "" },
34 | ],
35 | returns: { type: "void" },
36 | },
37 | {
38 | name: "unmint",
39 | desc: "",
40 | args: [
41 | { type: "axfer", name: "send_galgo", desc: "" },
42 | { type: "account", name: "escrow", desc: "" },
43 | { type: "application", name: "dispenser", desc: "" },
44 | ],
45 | returns: { type: "void" },
46 | },
47 | {
48 | name: "claim_premint",
49 | desc: "",
50 | args: [
51 | { type: "account", name: "escrow", desc: "" },
52 | { type: "account", name: "receiver", desc: "" },
53 | { type: "application", name: "dispenser", desc: "" },
54 | { type: "asset", name: "g_algo", desc: "" },
55 | ],
56 | returns: { type: "void" },
57 | },
58 | {
59 | name: "register_online",
60 | desc: "",
61 | args: [
62 | { type: "pay", name: "send_algo", desc: "" },
63 | { type: "account", name: "escrow", desc: "" },
64 | { type: "address", name: "vote_key", desc: "" },
65 | { type: "address", name: "sel_key", desc: "" },
66 | { type: "byte[64]", name: "state_proof_key", desc: "" },
67 | { type: "uint64", name: "vote_first", desc: "" },
68 | { type: "uint64", name: "vote_last", desc: "" },
69 | { type: "uint64", name: "vote_key_dilution", desc: "" },
70 | ],
71 | returns: { type: "void" },
72 | },
73 | {
74 | name: "register_offline",
75 | desc: "",
76 | args: [{ type: "account", name: "escrow", desc: "" }],
77 | returns: { type: "void" },
78 | },
79 | {
80 | name: "governance",
81 | desc: "",
82 | args: [
83 | { type: "account", name: "escrow", desc: "" },
84 | { type: "account", name: "dest", desc: "" },
85 | { type: "string", name: "note", desc: "" },
86 | ],
87 | returns: { type: "void" },
88 | },
89 | {
90 | name: "remove_escrow",
91 | desc: "",
92 | args: [{ type: "account", name: "escrow", desc: "" }],
93 | returns: { type: "void" },
94 | },
95 | {
96 | name: "burn",
97 | desc: "",
98 | args: [
99 | { type: "axfer", name: "send_galgo", desc: "" },
100 | { type: "application", name: "dispenser", desc: "" },
101 | ],
102 | returns: { type: "void" },
103 | },
104 | ],
105 | });
106 |
107 | export { abiDistributor };
108 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v2/constants/mainnet-constants.ts:
--------------------------------------------------------------------------------
1 | import type { Distributor } from "../../common";
2 |
3 | const govDistributor7: Distributor = {
4 | appId: 1073098885,
5 | };
6 |
7 | const govDistributor8: Distributor = {
8 | appId: 1136393919,
9 | };
10 |
11 | const govDistributor9: Distributor = {
12 | appId: 1200551652,
13 | };
14 |
15 | const govDistributor10: Distributor = {
16 | appId: 1282254855,
17 | };
18 |
19 | const govDistributor11: Distributor = {
20 | appId: 1702641473,
21 | };
22 |
23 | const govDistributor12: Distributor = {
24 | appId: 2057814942,
25 | };
26 |
27 | const govDistributor13: Distributor = {
28 | appId: 2330032485,
29 | };
30 |
31 | const govDistributor14: Distributor = {
32 | appId: 2629511242,
33 | };
34 |
35 | export {
36 | govDistributor7,
37 | govDistributor8,
38 | govDistributor9,
39 | govDistributor10,
40 | govDistributor11,
41 | govDistributor12,
42 | govDistributor13,
43 | govDistributor14,
44 | };
45 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v2/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants/abi-contracts";
2 | export * from "./constants/mainnet-constants";
3 | export * from "./governance";
4 | export * from "./types";
5 |
--------------------------------------------------------------------------------
/src/algo-liquid-governance/v2/types.ts:
--------------------------------------------------------------------------------
1 | interface DistributorInfo {
2 | currentRound?: number; // round the data was read at
3 | dispenserAppId: number; // id of dispenser app which mints gALGO
4 | premintEnd: bigint; // unix timestamp for the end of the pre-mint period
5 | commitEnd: bigint; // unix timestamp for end of the commitment period
6 | periodEnd: bigint; // unix timestamp for end of the governance period
7 | fee: bigint; // minting fee 4 d.p.
8 | totalCommitment: bigint; // total amount of ALGOs committed
9 | isBurningPaused: boolean; // flag to indicate if users can burn their ALGO for gALGO
10 | }
11 |
12 | interface UserCommitmentInfo {
13 | currentRound?: number;
14 | userAddress: string;
15 | canDelegate: boolean; // whether voting can be delegated to admin
16 | premint: bigint; // amount of ALGOs the user has pre-minted and not yet claimed
17 | commitment: bigint; // amount of ALGOs the user has committed
18 | nonCommitment: bigint; // amount of ALGOs the user has added after the commitment period
19 | }
20 |
21 | interface EscrowGovernanceStatus {
22 | currentRound?: number;
23 | balance: bigint;
24 | isOnline: boolean;
25 | status?: {
26 | version: number;
27 | commitment: bigint;
28 | beneficiaryAddress?: string;
29 | xGovControlAddress?: string;
30 | };
31 | }
32 |
33 | export { DistributorInfo, UserCommitmentInfo, EscrowGovernanceStatus };
34 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * as algoLiquidGovernanceV1 from "./algo-liquid-governance/v1";
2 | export * from "./algo-liquid-governance/common";
3 | export * from "./algo-liquid-governance/v2";
4 |
5 | export * from "./lend";
6 |
7 | export * from "./xalgo";
8 |
9 | export * from "./math-lib";
10 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/deposit-staking.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "f_staking",
3 | "methods": [
4 | {
5 | "name": "add_f_staking_escrow",
6 | "desc": "Add f staking escrow for a user. The escrow opts in and rekeys itself to the f staking application.",
7 | "args": [
8 | {
9 | "type": "pay",
10 | "name": "user_call",
11 | "desc": "The transaction from the user to approve the f staking escrow."
12 | }
13 | ],
14 | "returns": {
15 | "type": "void"
16 | }
17 | },
18 | {
19 | "name": "opt_escrow_into_asset",
20 | "desc": "Add support for a f staking escrow to hold a pool's f asset.",
21 | "args": [
22 | {
23 | "type": "account",
24 | "name": "escrow",
25 | "desc": "The user's f staking escrow."
26 | },
27 | {
28 | "type": "application",
29 | "name": "pool",
30 | "desc": "The pool application to support."
31 | },
32 | {
33 | "type": "asset",
34 | "name": "f_asset",
35 | "desc": "The f asset of the pool."
36 | },
37 | {
38 | "type": "uint8",
39 | "name": "stake_index",
40 | "desc": "The index of the staking program in the array."
41 | }
42 | ],
43 | "returns": {
44 | "type": "void"
45 | }
46 | },
47 | {
48 | "name": "sync_stake",
49 | "desc": "Sync the stake balance of an escrow's staking program.",
50 | "args": [
51 | {
52 | "type": "account",
53 | "name": "escrow",
54 | "desc": "The user's f staking escrow."
55 | },
56 | {
57 | "type": "application",
58 | "name": "pool",
59 | "desc": "The pool of the staking program."
60 | },
61 | {
62 | "type": "asset",
63 | "name": "f_asset",
64 | "desc": "The f asset of the pool."
65 | },
66 | {
67 | "type": "uint8",
68 | "name": "stake_index",
69 | "desc": "The index of the staking program in the array."
70 | }
71 | ],
72 | "returns": {
73 | "type": "void"
74 | }
75 | },
76 | {
77 | "name": "withdraw_stake",
78 | "desc": "Withdraw a stake from f staking escrow.",
79 | "args": [
80 | {
81 | "type": "account",
82 | "name": "escrow",
83 | "desc": "The user's f staking escrow."
84 | },
85 | {
86 | "type": "account",
87 | "name": "receiver",
88 | "desc": "The account to receive the stake sent from the f staking escrow."
89 | },
90 | {
91 | "type": "application",
92 | "name": "pool_manager",
93 | "desc": "The pool manager application of the pool."
94 | },
95 | {
96 | "type": "application",
97 | "name": "pool",
98 | "desc": "The pool of the staking program to withdraw from."
99 | },
100 | {
101 | "type": "asset",
102 | "name": "asset",
103 | "desc": "The asset of the pool."
104 | },
105 | {
106 | "type": "asset",
107 | "name": "f_asset",
108 | "desc": "The f asset of the pool."
109 | },
110 | {
111 | "type": "uint64",
112 | "name": "amount",
113 | "desc": "The amount of asset / f asset to send to withdraw from escrow."
114 | },
115 | {
116 | "type": "bool",
117 | "name": "is_f_asset_amount",
118 | "desc": "Whether the amount to withdraw is expressed in terms of f asset or asset."
119 | },
120 | {
121 | "type": "bool",
122 | "name": "remain_deposited",
123 | "desc": "Whether receiver should get f asset or asset. Cannot remain deposited and use asset amount."
124 | },
125 | {
126 | "type": "uint8",
127 | "name": "stake_index",
128 | "desc": "The index of the staking program in the array."
129 | }
130 | ],
131 | "returns": {
132 | "type": "void"
133 | }
134 | },
135 | {
136 | "name": "claim_rewards",
137 | "desc": "Claim rewards for an escrow's staking program",
138 | "args": [
139 | {
140 | "type": "account",
141 | "name": "escrow",
142 | "desc": "The user's f staking escrow."
143 | },
144 | {
145 | "type": "account",
146 | "name": "receiver",
147 | "desc": "The account to receive the reward sent from the f staking application."
148 | },
149 | {
150 | "type": "uint64",
151 | "name": "stake_index",
152 | "desc": "The index of the staking program in the array."
153 | }
154 | ],
155 | "returns": {
156 | "type": "void"
157 | }
158 | },
159 | {
160 | "name": "close_out_escrow_from_asset",
161 | "desc": "Remove support for a f staking escrow to hold a pool's f asset.",
162 | "args": [
163 | {
164 | "type": "account",
165 | "name": "escrow",
166 | "desc": "The user's f staking escrow."
167 | },
168 | {
169 | "type": "account",
170 | "name": "f_asset_creator"
171 | },
172 | {
173 | "type": "asset",
174 | "name": "f_asset"
175 | }
176 | ],
177 | "returns": {
178 | "type": "void"
179 | }
180 | },
181 | {
182 | "name": "remove_f_staking_escrow",
183 | "desc": "Remove a f staking escrow for a user and return its minimum balance.",
184 | "args": [
185 | {
186 | "type": "account",
187 | "name": "escrow",
188 | "desc": "The user's f staking escrow."
189 | }
190 | ],
191 | "returns": {
192 | "type": "void"
193 | }
194 | }
195 | ],
196 | "networks": {}
197 | }
198 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/deposits.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "deposits",
3 | "desc": "Allows users to create an escrow which will hold their deposits for them.",
4 | "methods": [
5 | {
6 | "name": "add_deposit_escrow",
7 | "desc": "Add a deposit escrow for a user. The escrow opts in and rekeys itself to the deposit application.",
8 | "args": [
9 | {
10 | "type": "pay",
11 | "name": "user_call",
12 | "desc": "The transaction from the user to approve the deposit escrow."
13 | }
14 | ],
15 | "returns": {
16 | "type": "void"
17 | }
18 | },
19 | {
20 | "name": "opt_escrow_into_asset",
21 | "desc": "Add support for a deposit escrow to hold a pool's f asset.",
22 | "args": [
23 | {
24 | "type": "account",
25 | "name": "escrow",
26 | "desc": "The user's deposit escrow."
27 | },
28 | {
29 | "type": "application",
30 | "name": "pool_manager",
31 | "desc": "The pool manager application of the deposits application."
32 | },
33 | {
34 | "type": "application",
35 | "name": "pool",
36 | "desc": "The pool application to support."
37 | },
38 | {
39 | "type": "asset",
40 | "name": "f_asset",
41 | "desc": "The f asset of the pool."
42 | },
43 | {
44 | "type": "uint8",
45 | "name": "index",
46 | "desc": "The index of the pool in the pool manager array."
47 | }
48 | ],
49 | "returns": {
50 | "type": "void"
51 | }
52 | },
53 | {
54 | "name": "withdraw",
55 | "desc": "Withdraw an asset from deposit escrow.",
56 | "args": [
57 | {
58 | "type": "account",
59 | "name": "escrow",
60 | "desc": "The user's deposit escrow."
61 | },
62 | {
63 | "type": "account",
64 | "name": "receiver",
65 | "desc": "The account to receive the asset from the pool."
66 | },
67 | {
68 | "type": "application",
69 | "name": "pool_manager",
70 | "desc": "The pool manager application of the pool."
71 | },
72 | {
73 | "type": "application",
74 | "name": "pool",
75 | "desc": "The pool to withdraw from."
76 | },
77 | {
78 | "type": "asset",
79 | "name": "asset",
80 | "desc": "The asset of the pool."
81 | },
82 | {
83 | "type": "asset",
84 | "name": "f_asset",
85 | "desc": "The f asset of the pool."
86 | },
87 |
88 | {
89 | "type": "uint64",
90 | "name": "amount",
91 | "desc": "The amount of asset / f asset to send to withdraw from escrow."
92 | },
93 | {
94 | "type": "bool",
95 | "name": "is_f_asset_amount",
96 | "desc": "Whether the amount to withdraw is expressed in terms of f asset or asset."
97 | },
98 | {
99 | "type": "bool",
100 | "name": "remain_deposited",
101 | "desc": "Whether receiver should get f asset or asset. Cannot remain deposited and use asset amount."
102 | },
103 | {
104 | "type": "uint8",
105 | "name": "index",
106 | "desc": "The index of the pool in the pool manager array."
107 | }
108 | ],
109 | "returns": {
110 | "type": "void"
111 | }
112 | },
113 | {
114 | "name": "close_out_escrow_from_asset",
115 | "desc": "Remove support for a deposit escrow to hold a pool's f asset.",
116 | "args": [
117 | {
118 | "type": "account",
119 | "name": "escrow",
120 | "desc": "The user user's deposit escrow."
121 | },
122 | {
123 | "type": "account",
124 | "name": "f_asset_creator",
125 | "desc": "The f asset creator account, also known as the pool account."
126 | },
127 | {
128 | "type": "asset",
129 | "name": "f_asset",
130 | "desc": "The f asset of the pool."
131 | }
132 | ],
133 | "returns": {
134 | "type": "void"
135 | }
136 | },
137 | {
138 | "name": "remove_deposit_escrow",
139 | "desc": "Remove a deposit escrow for a user and return its minimum balance.",
140 | "args": [
141 | {
142 | "type": "account",
143 | "name": "escrow",
144 | "desc": "The user's deposit escrow."
145 | }
146 | ],
147 | "returns": {
148 | "type": "void"
149 | }
150 | }
151 | ],
152 | "networks": {}
153 | }
154 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/index.ts:
--------------------------------------------------------------------------------
1 | import { ABIContract } from "algosdk";
2 |
3 | import depositStakingABI from "./deposit-staking.json";
4 | import depositsABI from "./deposits.json";
5 | import loanABI from "./loan.json";
6 | import lpTokenOracleABI from "./lp-token-oracle.json";
7 | import oracleAdapterABI from "./oracle-adapter.json";
8 | import poolABI from "./pool.json";
9 |
10 | export const depositsABIContract = new ABIContract(depositsABI);
11 | export const depositStakingABIContract = new ABIContract(depositStakingABI);
12 | export const loanABIContract = new ABIContract(loanABI);
13 | export const lpTokenOracleABIContract = new ABIContract(lpTokenOracleABI);
14 | export const oracleAdapterABIContract = new ABIContract(oracleAdapterABI);
15 | export const poolABIContract = new ABIContract(poolABI);
16 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/loan.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Loan",
3 | "desc": "Combines multiple pools together to allows loans to be taken out where some of these pool assets are collateral and some of the pool assets are borrowed.",
4 | "methods": [
5 | {
6 | "name": "create_loan",
7 | "desc": "Add a loan escrow for a user. The escrow opts in and rekeys itself to the loan application.",
8 | "args": [
9 | {
10 | "type": "pay",
11 | "name": "user_call",
12 | "desc": "The transaction from the user to approve the loan escrow."
13 | }
14 | ],
15 | "returns": {
16 | "type": "void"
17 | }
18 | },
19 | {
20 | "name": "add_collateral",
21 | "desc": "Add support for a collateral in a loan escrow.",
22 | "args": [
23 | {
24 | "type": "account",
25 | "name": "escrow",
26 | "desc": "The user's loan escrow."
27 | },
28 | {
29 | "type": "asset",
30 | "name": "f_asset",
31 | "desc": "The f asset of the pool."
32 | },
33 | {
34 | "type": "application",
35 | "name": "pool",
36 | "desc": "The pool application to add support for."
37 | },
38 | {
39 | "type": "uint8",
40 | "name": "pool_manager_index",
41 | "desc": "The index of the pool in the pool manager array."
42 | },
43 | {
44 | "type": "uint8",
45 | "name": "loan_index",
46 | "desc": "The index of the pool in the loan array."
47 | },
48 | {
49 | "type": "application",
50 | "name": "pool_manager",
51 | "desc": "The pool manager application of the loan."
52 | }
53 | ],
54 | "returns": {
55 | "type": "void"
56 | }
57 | },
58 | {
59 | "name": "sync_collateral",
60 | "desc": "Sync the collateral balance of a loan's given collateral. Should be proceeded with transaction sending the collateral to the loan escrow in the same group transaction.",
61 | "args": [
62 | {
63 | "type": "appl",
64 | "name": "refresh_prices",
65 | "desc": "The transaction to refresh the pool's asset price."
66 | },
67 | {
68 | "type": "account",
69 | "name": "escrow",
70 | "desc": "The user's loan escrow."
71 | },
72 | {
73 | "type": "asset",
74 | "name": "f_asset",
75 | "desc": "The f asset of the pool."
76 | },
77 | {
78 | "type": "application",
79 | "name": "pool",
80 | "desc": "The pool application to sync."
81 | },
82 | {
83 | "type": "application",
84 | "name": "pool_manager",
85 | "desc": "The pool manager application of the loan."
86 | },
87 | {
88 | "type": "application",
89 | "name": "oracle_adapter",
90 | "desc": "The oracle adapter application of the loan."
91 | }
92 | ],
93 | "returns": {
94 | "type": "void"
95 | }
96 | },
97 | {
98 | "name": "reduce_collateral",
99 | "desc": "Reduce the collateral of a loan.",
100 | "args": [
101 | {
102 | "type": "appl",
103 | "name": "refresh_prices",
104 | "desc": "The transaction to refresh the loan's asset prices."
105 | },
106 | {
107 | "type": "account",
108 | "name": "escrow",
109 | "desc": "The user's escrow."
110 | },
111 | {
112 | "type": "account",
113 | "name": "receiver",
114 | "desc": "The account to receive the f asset sent from the loan escrow."
115 | },
116 | {
117 | "type": "asset",
118 | "name": "asset",
119 | "desc": "The asset of the pool."
120 | },
121 | {
122 | "type": "asset",
123 | "name": "f_asset",
124 | "desc": "The f asset of the pool."
125 | },
126 | {
127 | "type": "uint64",
128 | "name": "amount",
129 | "desc": "The amount to reduce the collateral by."
130 | },
131 | {
132 | "type": "bool",
133 | "name": "is_f_asset_amount",
134 | "desc": "Whether the amount of collateral to reduce by is expressed in terms of f asset or asset."
135 | },
136 | {
137 | "type": "application",
138 | "name": "pool",
139 | "desc": "The pool to reduce the collateral of."
140 | },
141 | {
142 | "type": "application",
143 | "name": "pool_manager",
144 | "desc": "The pool manager application of the pool."
145 | },
146 | {
147 | "type": "application",
148 | "name": "oracle_adapter",
149 | "desc": "The oracle adapter application of the loan."
150 | }
151 | ],
152 | "returns": {
153 | "type": "void"
154 | }
155 | },
156 | {
157 | "name": "swap_collateral_begin",
158 | "desc": "Withdraw collateral from a loan escrow without checking if under-collateralized. Must be groped together with swap_collateral_end method call.",
159 | "args": [
160 | {
161 | "type": "account",
162 | "name": "escrow",
163 | "desc": "The user's loan escrow."
164 | },
165 | {
166 | "type": "account",
167 | "name": "receiver",
168 | "desc": "The account to receive the f asset sent from the loan escrow."
169 | },
170 | {
171 | "type": "asset",
172 | "name": "asset",
173 | "desc": "The asset of the pool."
174 | },
175 | {
176 | "type": "asset",
177 | "name": "f_asset",
178 | "desc": "The f asset of the pool."
179 | },
180 | {
181 | "type": "uint64",
182 | "name": "amount",
183 | "desc": "The amount of collateral to swap."
184 | },
185 | {
186 | "type": "bool",
187 | "name": "is_f_asset_amount",
188 | "desc": "Whether the amount of collateral to swap is expressed in terms of f asset or asset."
189 | },
190 | {
191 | "type": "uint64",
192 | "name": "txn_index",
193 | "desc": "The transaction index in the group transaction for the swap_collateral_end method call."
194 | },
195 | {
196 | "type": "application",
197 | "name": "pool",
198 | "desc": "The pool to swap the collateral of."
199 | },
200 | {
201 | "type": "application",
202 | "name": "pool_manager",
203 | "desc": "The pool manager application of the pool."
204 | }
205 | ],
206 | "returns": {
207 | "type": "void"
208 | }
209 | },
210 | {
211 | "name": "swap_collateral_end",
212 | "desc": "Finalise a swap collateral. Must be grouped together with swap_collateral_begin method call.",
213 | "args": [
214 | {
215 | "type": "appl",
216 | "name": "refresh_prices",
217 | "desc": "The transaction to refresh the loan's asset prices."
218 | },
219 | {
220 | "type": "account",
221 | "name": "escrow",
222 | "desc": "The user's loan escrow."
223 | },
224 | {
225 | "type": "application",
226 | "name": "pool_manager",
227 | "desc": "The pool manager application of the pool."
228 | },
229 | {
230 | "type": "application",
231 | "name": "oracle_adapter",
232 | "desc": "The oracle adapter application of the loan."
233 | }
234 | ],
235 | "returns": {
236 | "type": "void"
237 | }
238 | },
239 | {
240 | "name": "remove_collateral",
241 | "desc": "Remove support for a collateral in a loan escrow.",
242 | "args": [
243 | {
244 | "type": "account",
245 | "name": "escrow",
246 | "desc": "The user's loan escrow."
247 | },
248 | {
249 | "type": "asset",
250 | "name": "f_asset",
251 | "desc": "The f asset of the pool."
252 | },
253 | {
254 | "type": "application",
255 | "name": "pool",
256 | "desc": "The pool application to remove support for."
257 | }
258 | ],
259 | "returns": {
260 | "type": "void"
261 | }
262 | },
263 | {
264 | "name": "borrow",
265 | "desc": "Borrow an asset using collateral of a loan escrow.",
266 | "args": [
267 | {
268 | "type": "appl",
269 | "name": "refresh_prices",
270 | "desc": "The transaction to refresh the loan's asset prices."
271 | },
272 | {
273 | "type": "account",
274 | "name": "escrow",
275 | "desc": "The user's loan escrow."
276 | },
277 | {
278 | "type": "account",
279 | "name": "receiver",
280 | "desc": "The account to receive the asset sent by the pool."
281 | },
282 | {
283 | "type": "asset",
284 | "name": "asset",
285 | "desc": "The asset of the pool."
286 | },
287 | {
288 | "type": "uint64",
289 | "name": "amount",
290 | "desc": "The amount to borrow."
291 | },
292 | {
293 | "type": "uint64",
294 | "name": "max_stable_rate",
295 | "desc": "The maximum stable rate of the borrow. If zero then borrow is interpreted as a variable rate borrow."
296 | },
297 | {
298 | "type": "uint8",
299 | "name": "pool_manager_index",
300 | "desc": "The index of the pool in the pool manager array."
301 | },
302 | {
303 | "type": "uint8",
304 | "name": "loan_index",
305 | "desc": "The index of the pool in the loan array."
306 | },
307 | {
308 | "type": "application",
309 | "name": "pool",
310 | "desc": "The pool to borrow from."
311 | },
312 | {
313 | "type": "application",
314 | "name": "pool_manager",
315 | "desc": "The pool manager application of the pool."
316 | },
317 | {
318 | "type": "application",
319 | "name": "oracle_adapter",
320 | "desc": "The oracle adapter application of the loan."
321 | }
322 | ],
323 | "returns": {
324 | "type": "void"
325 | }
326 | },
327 | {
328 | "name": "switch_borrow_type",
329 | "desc": "Switch the borrow type of a borrow from variable to stable or stable to variable.",
330 | "args": [
331 | {
332 | "type": "account",
333 | "name": "escrow",
334 | "desc": "The user's loan escrow."
335 | },
336 | {
337 | "type": "asset",
338 | "name": "asset",
339 | "desc": "The asset of the pool."
340 | },
341 | {
342 | "type": "uint64",
343 | "name": "max_stable_rate",
344 | "desc": "The maximum stable rate to switch the borrow to. If zero then interpreted as switching a stable rate borrow to a variable rate borrow."
345 | },
346 | {
347 | "type": "application",
348 | "name": "pool",
349 | "desc": "The pool application so switch the borrow type of."
350 | },
351 | {
352 | "type": "application",
353 | "name": "pool_manager",
354 | "desc": "The pool manager application of the pool."
355 | }
356 | ],
357 | "returns": {
358 | "type": "void"
359 | }
360 | },
361 | {
362 | "name": "repay_with_txn",
363 | "desc": "Repay a borrow using an asset transfer transaction.",
364 | "args": [
365 | {
366 | "type": "txn",
367 | "name": "send_asset_txn",
368 | "desc": "The transaction to the pool to repay the borrow."
369 | },
370 | {
371 | "type": "account",
372 | "name": "escrow",
373 | "desc": "The user's loan escrow."
374 | },
375 | {
376 | "type": "account",
377 | "name": "receiver",
378 | "desc": "The account to receive the fr asset rewards if there are any."
379 | },
380 | {
381 | "type": "account",
382 | "name": "reserve",
383 | "desc": "The account to receive the protocol revenue from the percentage of the accrued interest."
384 | },
385 | {
386 | "type": "asset",
387 | "name": "asset",
388 | "desc": "The asset of the pool."
389 | },
390 | {
391 | "type": "asset",
392 | "name": "fr_asset",
393 | "desc": "The fr asset of the pool."
394 | },
395 | {
396 | "type": "bool",
397 | "name": "is_stable",
398 | "desc": "Whether the borrow that is being repaid is a stable or variable rate borrow."
399 | },
400 | {
401 | "type": "application",
402 | "name": "pool",
403 | "desc": "The pool to repay."
404 | },
405 | {
406 | "type": "application",
407 | "name": "pool_manager",
408 | "desc": "The pool manager application of the pool."
409 | }
410 | ],
411 | "returns": {
412 | "type": "void"
413 | }
414 | },
415 | {
416 | "name": "repay_with_collateral",
417 | "desc": "Repay a borrow using existing collateral in the loan escrow.",
418 | "args": [
419 | {
420 | "type": "account",
421 | "name": "escrow",
422 | "desc": "The user's loan escrow."
423 | },
424 | {
425 | "type": "account",
426 | "name": "receiver",
427 | "desc": "The account to receive the fr asset rewards if there are any."
428 | },
429 | {
430 | "type": "account",
431 | "name": "reserve",
432 | "desc": "The account to receive the protocol revenue from the percentage of the accrued interest."
433 | },
434 | {
435 | "type": "asset",
436 | "name": "asset",
437 | "desc": "The asset of the pool."
438 | },
439 | {
440 | "type": "asset",
441 | "name": "f_asset",
442 | "desc": "The f asset of the pool."
443 | },
444 | {
445 | "type": "asset",
446 | "name": "fr_asset",
447 | "desc": "The fr asset of the pool."
448 | },
449 | {
450 | "type": "uint64",
451 | "name": "amount",
452 | "desc": "The amount to repay expressed in terms of the asset."
453 | },
454 | {
455 | "type": "bool",
456 | "name": "is_stable",
457 | "desc": "Whether the borrow that is being repaid is a stable or variable rate borrow."
458 | },
459 | {
460 | "type": "application",
461 | "name": "pool",
462 | "desc": "The pool to repay and collateral to use."
463 | },
464 | {
465 | "type": "application",
466 | "name": "pool_manager",
467 | "desc": "The pool manager application of the pool."
468 | }
469 | ],
470 | "returns": {
471 | "type": "void"
472 | }
473 | },
474 | {
475 | "name": "liquidate",
476 | "desc": "Liquidate a borrow and seize collateral from the loan escrow.",
477 | "args": [
478 | {
479 | "type": "appl",
480 | "name": "refresh_prices",
481 | "desc": "The transaction to refresh the loan's asset prices."
482 | },
483 | {
484 | "type": "txn",
485 | "name": "send_asset_txn",
486 | "desc": "The transaction to the pool to repay the borrow."
487 | },
488 | {
489 | "type": "account",
490 | "name": "escrow",
491 | "desc": "The user's loan escrow."
492 | },
493 | {
494 | "type": "account",
495 | "name": "reserve",
496 | "desc": "The account to receive the protocol revenue from the percentage of the accrued interest."
497 | },
498 | {
499 | "type": "asset",
500 | "name": "asset",
501 | "desc": "The asset of the borrow pool."
502 | },
503 | {
504 | "type": "asset",
505 | "name": "f_asset",
506 | "desc": "The f asset of the collateral pool."
507 | },
508 | {
509 | "type": "uint64",
510 | "name": "min_col_amount",
511 | "desc": "The minimum collateral amount for the liquidator to receive."
512 | },
513 | {
514 | "type": "bool",
515 | "name": "is_stable",
516 | "desc": "Whether the borrow that is being repaid is a stable or variable rate borrow."
517 | },
518 | {
519 | "type": "application",
520 | "name": "col_pool",
521 | "desc": "The pool whose collateral is seized."
522 | },
523 | {
524 | "type": "application",
525 | "name": "bor_pool",
526 | "desc": "The pool whose borrow is repaid."
527 | },
528 | {
529 | "type": "application",
530 | "name": "pool_manager",
531 | "desc": "The pool manager application of the pool."
532 | },
533 | {
534 | "type": "application",
535 | "name": "oracle_adapter",
536 | "desc": "The oracle adapter application of the loan."
537 | }
538 | ],
539 | "returns": {
540 | "type": "void"
541 | }
542 | },
543 | {
544 | "name": "rebalance_up",
545 | "desc": "Increase the stable interest rate of a borrow through rebalancing",
546 | "args": [
547 | {
548 | "type": "account",
549 | "name": "escrow",
550 | "desc": "The loan escrow whose borrow to rebalance up."
551 | },
552 | {
553 | "type": "asset",
554 | "name": "asset",
555 | "desc": "The asset of the pool."
556 | },
557 | {
558 | "type": "application",
559 | "name": "pool",
560 | "desc": "The pool whose borrow to rebalance up."
561 | },
562 | {
563 | "type": "application",
564 | "name": "pool_manager",
565 | "desc": "The pool manager application of the pool."
566 | }
567 | ],
568 | "returns": {
569 | "type": "void"
570 | }
571 | },
572 | {
573 | "name": "rebalance_down",
574 | "desc": "Decrease the stable interest rate of a borrow through rebalancing",
575 | "args": [
576 | {
577 | "type": "account",
578 | "name": "escrow",
579 | "desc": "The loan whose borrow to rebalance down escrow."
580 | },
581 | {
582 | "type": "asset",
583 | "name": "asset",
584 | "desc": "The asset of the pool."
585 | },
586 | {
587 | "type": "application",
588 | "name": "pool",
589 | "desc": "The pool whose borrow to rebalance down."
590 | },
591 | {
592 | "type": "application",
593 | "name": "pool_manager",
594 | "desc": "The pool manager application of the pool."
595 | }
596 | ],
597 | "returns": {
598 | "type": "void"
599 | }
600 | },
601 | {
602 | "name": "remove_loan",
603 | "desc": "Remove a loan escrow for a user and return its minimum balance.",
604 | "args": [
605 | {
606 | "type": "account",
607 | "name": "escrow",
608 | "desc": "The user's loan escrow."
609 | }
610 | ],
611 | "returns": {
612 | "type": "void"
613 | }
614 | }
615 | ],
616 | "networks": {}
617 | }
618 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/lp-token-oracle.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LP Token Oracle",
3 | "desc": "Contains the information necessary to calculate the price of an LP token.",
4 | "methods": [
5 | {
6 | "name": "update_lp_tokens",
7 | "desc": "Update the given LP tokens supplies such that they are valid to calculate the corresponding LP asset price. The pools are excluded from method signature but must still be passed.",
8 | "args": [
9 | {
10 | "type": "uint64[]",
11 | "name": "assets",
12 | "desc": "The asset ids to update."
13 | }
14 | ],
15 | "returns": {
16 | "type": "void"
17 | }
18 | }
19 | ],
20 | "networks": {}
21 | }
22 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/oracle-adapter.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Oracle Adapter",
3 | "desc": "Calculates and exposes given asset prices.",
4 | "methods": [
5 | {
6 | "name": "refresh_prices",
7 | "desc": "Refresh the prices of the given assets.",
8 | "args": [
9 | {
10 | "type": "uint64[]",
11 | "name": "lp_assets",
12 | "desc": "The list of LP assets to refresh the prices of."
13 | },
14 | {
15 | "type": "uint64[]",
16 | "name": "non_lp_assets",
17 | "desc": "The list of non-LP assets to refresh the prices of."
18 | },
19 | {
20 | "type": "application",
21 | "name": "oracle_0",
22 | "desc": "The first oracle price source of the oracle adapter."
23 | },
24 | {
25 | "type": "application",
26 | "name": "oracle_1",
27 | "desc": "The second price source of the oracle adapter."
28 | },
29 | {
30 | "type": "application",
31 | "name": "lp_token_oracle",
32 | "desc": "The LP Token Oracle of the oracle adapter."
33 | }
34 | ],
35 | "returns": {
36 | "type": "void"
37 | }
38 | }
39 | ],
40 | "networks": {}
41 | }
42 |
--------------------------------------------------------------------------------
/src/lend/abi-contracts/pool.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pool",
3 | "desc": "Responsible for managing the deposits and withdrawals of an asset and exposes functionality to facilitate borrowing.",
4 | "methods": [
5 | {
6 | "name": "deposit",
7 | "desc": "Deposit the asset into the pool.",
8 | "args": [
9 | {
10 | "type": "txn",
11 | "name": "send_asset_txn",
12 | "desc": "The transaction to send the asset to the pool."
13 | },
14 | {
15 | "type": "account",
16 | "name": "receiver",
17 | "desc": "The account to receive the f asset from the pool."
18 | },
19 | {
20 | "type": "asset",
21 | "name": "asset",
22 | "desc": "The asset of the pool."
23 | },
24 | {
25 | "type": "asset",
26 | "name": "f_asset",
27 | "desc": "The f asset of the pool."
28 | },
29 | {
30 | "type": "application",
31 | "name": "pool_manager",
32 | "desc": "The pool manager application of the pool."
33 | }
34 | ],
35 | "returns": {
36 | "type": "uint64",
37 | "desc": "The amount of f asset sent by pool to receiver."
38 | }
39 | },
40 | {
41 | "name": "withdraw",
42 | "desc": "Withdraw the asset from the pool.",
43 | "args": [
44 | {
45 | "type": "axfer",
46 | "name": "send_f_asset_txn",
47 | "desc": "The transaction to send the f asset to the pool."
48 | },
49 | {
50 | "type": "uint64",
51 | "name": "received_amount",
52 | "desc": "The amount of asset to receive. Any excess f asset sent will be returned to the sender. If zero then variable."
53 | },
54 | {
55 | "type": "account",
56 | "name": "receiver",
57 | "desc": "The account to receive the asset from the pool."
58 | },
59 | {
60 | "type": "asset",
61 | "name": "asset",
62 | "desc": "The asset of the pool."
63 | },
64 | {
65 | "type": "asset",
66 | "name": "f_asset",
67 | "desc": "The f asset of the pool."
68 | },
69 | {
70 | "type": "application",
71 | "name": "pool_manager",
72 | "desc": "The pool manager application of the pool."
73 | }
74 | ],
75 | "returns": {
76 | "type": "uint64",
77 | "desc": "The amount of asset sent by the pool to the receiver."
78 | }
79 | },
80 | {
81 | "name": "update_pool_interest_indexes",
82 | "desc": "Update the pool interest indexes.",
83 | "args": [
84 | {
85 | "type": "application",
86 | "name": "pool_manager",
87 | "desc": "The pool manager application of the pool."
88 | }
89 | ],
90 | "returns": {
91 | "type": "void"
92 | }
93 | },
94 | {
95 | "name": "flash_loan_begin",
96 | "desc": "Request a flash loan of the asset. Must be grouped together with flash_loan_end method call.",
97 | "args": [
98 | {
99 | "type": "uint64",
100 | "name": "amount",
101 | "desc": "The amount of the asset to borrow."
102 | },
103 | {
104 | "type": "uint64",
105 | "name": "txn_index",
106 | "desc": "The transaction index in the group transaction for the flash_loan_end method call."
107 | },
108 | {
109 | "type": "account",
110 | "name": "receiver",
111 | "desc": "The account to receive the asset from the pool."
112 | },
113 | {
114 | "type": "asset",
115 | "name": "asset",
116 | "desc": "The asset of the pool."
117 | }
118 | ],
119 | "returns": {
120 | "type": "void"
121 | }
122 | },
123 | {
124 | "name": "flash_loan_end",
125 | "desc": "Repay a requested flash loan. Must be grouped together with flash_loan_begin method call.",
126 | "args": [
127 | {
128 | "type": "txn",
129 | "name": "send_asset_txn",
130 | "desc": "The transaction to the asset to the pool."
131 | },
132 | {
133 | "type": "account",
134 | "name": "pool_admin",
135 | "desc": "The pool admin address that will receive the flash loan fee."
136 | },
137 | {
138 | "type": "asset",
139 | "name": "asset",
140 | "desc": "The asset of the pool."
141 | }
142 | ],
143 | "returns": {
144 | "type": "void"
145 | }
146 | }
147 | ],
148 | "networks": {}
149 | }
150 |
--------------------------------------------------------------------------------
/src/lend/amm.ts:
--------------------------------------------------------------------------------
1 | import { compoundEveryHour, ONE_12_DP, ONE_16_DP } from "../math-lib";
2 | import {
3 | getAccountApplicationLocalState,
4 | getApplicationGlobalState,
5 | getParsedValueFromState,
6 | parseUint64s,
7 | } from "../utils";
8 |
9 | import type {
10 | AssetsAdditionalInterest,
11 | LendingPool,
12 | PactLendingPool,
13 | PactLendingPoolInfo,
14 | PoolManagerInfo,
15 | TinymanLendingPool,
16 | TinymanLendingPoolInfo,
17 | } from "./types";
18 | import type { Algodv2, Indexer } from "algosdk";
19 |
20 | /**
21 | *
22 | * Returns information regarding the given Pact lending pool.
23 | *
24 | * @param client - Algorand client to query
25 | * @param lendingPool - Pact lending pool to query about
26 | * @param poolManagerInfo - pool manager info which is returned by retrievePoolManagerInfo function
27 | * @param additionalInterests - optional additional interest to consider
28 | * @returns Promise lending pool info
29 | */
30 | async function retrievePactLendingPoolInfo(
31 | client: Algodv2 | Indexer,
32 | lendingPool: PactLendingPool,
33 | poolManagerInfo: PoolManagerInfo,
34 | additionalInterests?: AssetsAdditionalInterest,
35 | ): Promise {
36 | const { currentRound, globalState: state } = await getApplicationGlobalState(client, lendingPool.lpPoolAppId);
37 | if (state === undefined) throw Error("Could not find lending pool");
38 | const config = parseUint64s(String(getParsedValueFromState(state, "CONFIG")));
39 | const fa0s = BigInt(getParsedValueFromState(state, "A") || 0);
40 | const fa1s = BigInt(getParsedValueFromState(state, "B") || 0);
41 | const ltcs = BigInt(getParsedValueFromState(state, "L") || 0);
42 |
43 | // pact pool swap fee interest
44 | const lpInfoRes = await fetch(`https://api.pact.fi/api/pools/${lendingPool.lpPoolAppId}`);
45 | if (!lpInfoRes.ok || lpInfoRes.status !== 200) throw Error("Failed to fetch pact swap fee from api");
46 | const pactPoolData = await lpInfoRes.json();
47 | const swapFeeInterestRate = BigInt(Math.round(Number(pactPoolData?.["apr_7d"] || 0) * 1e16));
48 | const tvlUsd = Number(pactPoolData?.["tvl_usd"] || 0);
49 |
50 | // lending pool deposit interest and additional interest
51 | const commonLendingPoolInterest = getDepositAndAdditionalInterest(lendingPool, poolManagerInfo, additionalInterests);
52 |
53 | return {
54 | ...commonLendingPoolInterest,
55 | currentRound,
56 | fAsset0Supply: fa0s,
57 | fAsset1Supply: fa1s,
58 | liquidityTokenCirculatingSupply: ltcs,
59 | fee: config[2],
60 | swapFeeInterestRate,
61 | swapFeeInterestYield: compoundEveryHour(swapFeeInterestRate, ONE_16_DP),
62 | tvlUsd,
63 | };
64 | }
65 |
66 | /**
67 | *
68 | * Returns information regarding the given Tinyman lending pool.
69 | *
70 | * @param client - Algorand client to query
71 | * @param tinymanAppId - Tinyman application id where lending pool belongs to
72 | * @param lendingPool - Pact lending pool to query about
73 | * @param poolManagerInfo - pool manager info which is returned by retrievePoolManagerInfo function
74 | * @param additionalInterests - optional additional interest to consider
75 | * @returns Promise lending pool info
76 | */
77 | async function retrieveTinymanLendingPoolInfo(
78 | client: Algodv2 | Indexer,
79 | tinymanAppId: number,
80 | lendingPool: TinymanLendingPool,
81 | poolManagerInfo: PoolManagerInfo,
82 | additionalInterests?: AssetsAdditionalInterest,
83 | ): Promise {
84 | const { currentRound, localState: state } = await getAccountApplicationLocalState(
85 | client,
86 | tinymanAppId,
87 | lendingPool.lpPoolAppAddress,
88 | );
89 | if (state === undefined) throw Error("Could not find lending pool");
90 | const fee = BigInt(getParsedValueFromState(state, "total_fee_share") || 0);
91 | const fa0s = BigInt(getParsedValueFromState(state, "asset_2_reserves") || 0);
92 | const fa1s = BigInt(getParsedValueFromState(state, "asset_1_reserves") || 0);
93 | const ltcs = BigInt(getParsedValueFromState(state, "issued_pool_tokens") || 0);
94 |
95 | // pact pool swap fee interest
96 | const res = await fetch(`https://mainnet.analytics.tinyman.org/api/v1/pools/${lendingPool.lpPoolAppAddress}`);
97 | if (!res.ok || res.status !== 200) throw Error("Failed to fetch tinyman swap fee from api");
98 | const tmPoolData = await res.json();
99 |
100 | const swapFeeInterestRate = BigInt(Math.round(Number(tmPoolData?.["annual_percentage_rate"] || 0) * 1e16));
101 | const swapFeeInterestYield = BigInt(Math.round(Number(tmPoolData?.["annual_percentage_yield"] || 0) * 1e16));
102 | const farmInterestYield = BigInt(
103 | Math.round(Number(tmPoolData?.["staking_total_annual_percentage_yield"] || 0) * 1e16),
104 | );
105 | const tvlUsd = Number(tmPoolData?.["liquidity_in_usd"] || 0);
106 |
107 | // lending pool deposit interest and additional interest
108 | const commonLendingPoolInterest = getDepositAndAdditionalInterest(lendingPool, poolManagerInfo, additionalInterests);
109 |
110 | return {
111 | ...commonLendingPoolInterest,
112 | currentRound,
113 | fAsset0Supply: fa0s,
114 | fAsset1Supply: fa1s,
115 | liquidityTokenCirculatingSupply: ltcs,
116 | fee,
117 | swapFeeInterestRate,
118 | swapFeeInterestYield,
119 | farmInterestYield,
120 | tvlUsd,
121 | };
122 | }
123 |
124 | function getDepositAndAdditionalInterest(
125 | lendingPool: LendingPool,
126 | poolManagerInfo: PoolManagerInfo,
127 | additionalInterests?: AssetsAdditionalInterest,
128 | ) {
129 | const { asset0Id, asset1Id, pool0AppId, pool1AppId } = lendingPool;
130 |
131 | // lending pool deposit interest
132 | const pool0 = poolManagerInfo.pools[pool0AppId];
133 | const pool1 = poolManagerInfo.pools[pool1AppId];
134 | if (pool0 === undefined || pool1 === undefined) throw Error("Could not find deposit pool");
135 | const asset0DepositInterestRate = pool0.depositInterestRate / BigInt(2);
136 | const asset0DepositInterestYield = pool0.depositInterestYield / BigInt(2);
137 | const asset1DepositInterestRate = pool1.depositInterestRate / BigInt(2);
138 | const asset1DepositInterestYield = pool1.depositInterestYield / BigInt(2);
139 |
140 | // add additional interests if specified
141 | let additionalInterestRate;
142 | let additionalInterestYield;
143 | if (additionalInterests) {
144 | for (const assetId of [asset0Id, asset1Id]) {
145 | if (additionalInterests[assetId]) {
146 | const { rateBps, yieldBps } = additionalInterests[assetId];
147 | // multiply by 1e12 to standardise at 16 d.p.
148 | additionalInterestRate = (additionalInterestRate || BigInt(0)) + (rateBps * ONE_12_DP) / BigInt(2);
149 | additionalInterestYield = (additionalInterestYield || BigInt(0)) + (yieldBps * ONE_12_DP) / BigInt(2);
150 | }
151 | }
152 | }
153 |
154 | return {
155 | asset0DepositInterestRate,
156 | asset0DepositInterestYield,
157 | asset1DepositInterestRate,
158 | asset1DepositInterestYield,
159 | additionalInterestRate,
160 | additionalInterestYield,
161 | };
162 | }
163 |
164 | export { retrievePactLendingPoolInfo, retrieveTinymanLendingPoolInfo };
165 |
--------------------------------------------------------------------------------
/src/lend/constants/mainnet-constants.ts:
--------------------------------------------------------------------------------
1 | import { ONE_4_DP } from "../../math-lib";
2 | import { LoanType, LPTokenProvider } from "../types";
3 |
4 | import type { LendingPool, OpUp, Oracle, Pool, ReserveAddress } from "../types";
5 |
6 | const MainnetPoolManagerAppId = 971350278;
7 |
8 | const MainnetDepositsAppId = 971353536;
9 |
10 | const MainnetDepositStakingAppId = 1093729103;
11 |
12 | type MainnetPoolKey =
13 | | "ALGO"
14 | | "gALGO"
15 | | "xALGO"
16 | | "USDC"
17 | | "USDt"
18 | | "GARD"
19 | | "EURS"
20 | | "goBTC"
21 | | "goETH"
22 | | "WBTC"
23 | | "WETH"
24 | | "WAVAX"
25 | | "WSOL"
26 | | "WLINK"
27 | | "GOLD"
28 | | "SILVER"
29 | | "OPUL"
30 | | "WMPL";
31 | const MainnetPools: Record = {
32 | ALGO: {
33 | appId: 971368268,
34 | assetId: 0,
35 | fAssetId: 971381860,
36 | frAssetId: 971381861,
37 | assetDecimals: 6,
38 | poolManagerIndex: 0,
39 | loans: {
40 | 971388781: BigInt(0),
41 | 971389489: BigInt(0),
42 | 1202382736: BigInt(1),
43 | 1202382829: BigInt(1),
44 | },
45 | },
46 | gALGO: {
47 | appId: 971370097,
48 | assetId: 793124631,
49 | fAssetId: 971383839,
50 | frAssetId: 971383840,
51 | assetDecimals: 6,
52 | poolManagerIndex: 1,
53 | loans: {
54 | 971388781: BigInt(1),
55 | 971389489: BigInt(1),
56 | },
57 | },
58 | xALGO: {
59 | appId: 2611131944,
60 | assetId: 1134696561,
61 | fAssetId: 2611138444,
62 | frAssetId: 2611138445,
63 | assetDecimals: 6,
64 | poolManagerIndex: 17,
65 | loans: {
66 | 971388781: BigInt(17),
67 | 971389489: BigInt(2),
68 | },
69 | },
70 | USDC: {
71 | appId: 971372237,
72 | assetId: 31566704,
73 | fAssetId: 971384592,
74 | frAssetId: 971384593,
75 | assetDecimals: 6,
76 | poolManagerIndex: 2,
77 | loans: {
78 | 971388781: BigInt(2),
79 | 971388977: BigInt(0),
80 | 1202382736: BigInt(0),
81 | 1202382829: BigInt(0),
82 | },
83 | },
84 | USDt: {
85 | appId: 971372700,
86 | assetId: 312769,
87 | fAssetId: 971385312,
88 | frAssetId: 971385313,
89 | assetDecimals: 6,
90 | poolManagerIndex: 3,
91 | loans: {
92 | 971388781: BigInt(3),
93 | 971388977: BigInt(1),
94 | },
95 | },
96 | GARD: {
97 | appId: 1060585819,
98 | assetId: 684649988,
99 | fAssetId: 1060587336,
100 | frAssetId: 1060587337,
101 | assetDecimals: 6,
102 | poolManagerIndex: 7,
103 | loans: {
104 | 971388781: BigInt(7),
105 | 971388977: BigInt(2),
106 | },
107 | },
108 | EURS: {
109 | appId: 1247053569,
110 | assetId: 227855942,
111 | fAssetId: 1247054501,
112 | frAssetId: 1247054502,
113 | assetDecimals: 6,
114 | poolManagerIndex: 14,
115 | loans: {
116 | 971388781: BigInt(14),
117 | 971388977: BigInt(3),
118 | },
119 | },
120 | goBTC: {
121 | appId: 971373361,
122 | assetId: 386192725,
123 | fAssetId: 971386173,
124 | frAssetId: 971386174,
125 | assetDecimals: 8,
126 | poolManagerIndex: 4,
127 | loans: {
128 | 971388781: BigInt(4),
129 | 1202382736: BigInt(2),
130 | 1202382829: BigInt(2),
131 | },
132 | },
133 | goETH: {
134 | appId: 971373611,
135 | assetId: 386195940,
136 | fAssetId: 971387073,
137 | frAssetId: 971387074,
138 | assetDecimals: 8,
139 | poolManagerIndex: 5,
140 | loans: {
141 | 971388781: BigInt(5),
142 | 1202382736: BigInt(3),
143 | 1202382829: BigInt(3),
144 | },
145 | },
146 | WBTC: {
147 | appId: 1067289273,
148 | assetId: 1058926737,
149 | fAssetId: 1067295154,
150 | frAssetId: 1067295155,
151 | assetDecimals: 8,
152 | poolManagerIndex: 8,
153 | loans: {
154 | 971388781: BigInt(8),
155 | 1202382736: BigInt(4),
156 | 1202382829: BigInt(4),
157 | },
158 | },
159 | WETH: {
160 | appId: 1067289481,
161 | assetId: 887406851,
162 | fAssetId: 1067295558,
163 | frAssetId: 1067295559,
164 | assetDecimals: 8,
165 | poolManagerIndex: 9,
166 | loans: {
167 | 971388781: BigInt(9),
168 | 1202382736: BigInt(5),
169 | 1202382829: BigInt(5),
170 | },
171 | },
172 | WAVAX: {
173 | appId: 1166977433,
174 | assetId: 893309613,
175 | fAssetId: 1166979636,
176 | frAssetId: 1166979637,
177 | assetDecimals: 8,
178 | poolManagerIndex: 10,
179 | loans: {
180 | 971388781: BigInt(10),
181 | },
182 | },
183 | WSOL: {
184 | appId: 1166980669,
185 | assetId: 887648583,
186 | fAssetId: 1166980820,
187 | frAssetId: 1166980821,
188 | assetDecimals: 8,
189 | poolManagerIndex: 11,
190 | loans: {
191 | 971388781: BigInt(11),
192 | },
193 | },
194 | WLINK: {
195 | appId: 1216434571,
196 | assetId: 1200094857,
197 | fAssetId: 1216437148,
198 | frAssetId: 1216437149,
199 | assetDecimals: 8,
200 | poolManagerIndex: 13,
201 | loans: {
202 | 971388781: BigInt(13),
203 | },
204 | },
205 | GOLD: {
206 | appId: 1258515734,
207 | assetId: 246516580,
208 | fAssetId: 1258524377,
209 | frAssetId: 1258524378,
210 | assetDecimals: 6,
211 | poolManagerIndex: 15,
212 | loans: {
213 | 971388781: BigInt(15),
214 | },
215 | },
216 | SILVER: {
217 | appId: 1258524099,
218 | assetId: 246519683,
219 | fAssetId: 1258524381,
220 | frAssetId: 1258524382,
221 | assetDecimals: 6,
222 | poolManagerIndex: 16,
223 | loans: {
224 | 971388781: BigInt(16),
225 | },
226 | },
227 | OPUL: {
228 | appId: 1044267181,
229 | assetId: 287867876,
230 | fAssetId: 1044269355,
231 | frAssetId: 1044269356,
232 | assetDecimals: 10,
233 | poolManagerIndex: 6,
234 | loans: {
235 | 971388781: BigInt(6),
236 | },
237 | },
238 | WMPL: {
239 | appId: 1166982094,
240 | assetId: 1163259470,
241 | fAssetId: 1166982296,
242 | frAssetId: 1166982297,
243 | assetDecimals: 8,
244 | poolManagerIndex: 12,
245 | loans: {
246 | 971388781: BigInt(12),
247 | },
248 | },
249 | };
250 |
251 | const MainnetLoans: Partial> = {
252 | [LoanType.GENERAL]: 971388781,
253 | [LoanType.STABLECOIN_EFFICIENCY]: 971388977,
254 | [LoanType.ALGO_EFFICIENCY]: 971389489,
255 | [LoanType.ULTRASWAP_UP]: 1202382736,
256 | [LoanType.ULTRASWAP_DOWN]: 1202382829,
257 | };
258 |
259 | const MainnetTinymanAppId = 1002541853;
260 |
261 | type MainnetLendingPoolKey =
262 | | "ALGOgALGOPLP"
263 | | "ALGOgALGOTM"
264 | | "ALGOUSDCPLP"
265 | | "ALGOUSDCTM"
266 | | "ALGOEURSPLP"
267 | | "ALGOgoBTCPLP"
268 | | "ALGOgoBTCTM"
269 | | "ALGOgoETHPLP"
270 | | "ALGOgoETHTM"
271 | | "ALGOwBTCPLP"
272 | | "ALGOwBTCTM"
273 | | "ALGOwETHPLP"
274 | | "ALGOwETHTM"
275 | | "ALGOwAVAXPLP"
276 | | "ALGOwSOLPLP"
277 | | "ALGOwLINKPLP"
278 | | "ALGOGOLDPLP"
279 | | "ALGOGOLDTM"
280 | | "ALGOSILVERPLP"
281 | | "ALGOSILVERTM"
282 | | "ALGOwMPLPLP"
283 | | "gALGOUSDCPLP"
284 | | "gALGOUSDCTM"
285 | | "xALGOUSDCPLP"
286 | | "xALGOUSDCTM"
287 | | "USDCUSDtPLP"
288 | | "USDCUSDtTM"
289 | | "USDCEURSPLP"
290 | | "USDCEURSTM"
291 | | "USDCwBTCTM"
292 | | "USDCwETHTM"
293 | | "USDCwAVAXTM"
294 | | "USDCwLINKTM"
295 | | "USDCwSOLTM"
296 | | "USDCGOLDPLP"
297 | | "USDCSILVERPLP";
298 | const MainnetLendingPools: Record = {
299 | ALGOgALGOPLP: {
300 | provider: LPTokenProvider.PACT,
301 | lpPoolAppId: 1116366345,
302 | lpAssetId: 1116366351,
303 | pool0AppId: 971368268,
304 | pool1AppId: 971370097,
305 | asset0Id: 0,
306 | asset1Id: 793124631,
307 | feeScale: ONE_4_DP,
308 | },
309 | ALGOgALGOTM: {
310 | provider: LPTokenProvider.TINYMAN,
311 | lpPoolAppAddress: "R5Y6PRR2NEOS27HB2HGQFUMKUUMPXAYUBU4BHDXY4TCEYNSWGPOKGCV66Q",
312 | lpAssetId: 1332971358,
313 | pool0AppId: 971368268,
314 | pool1AppId: 971370097,
315 | asset0Id: 0,
316 | asset1Id: 793124631,
317 | feeScale: ONE_4_DP,
318 | },
319 | ALGOUSDCPLP: {
320 | provider: LPTokenProvider.PACT,
321 | lpPoolAppId: 1116363704,
322 | lpAssetId: 1116363710,
323 | pool0AppId: 971368268,
324 | pool1AppId: 971372237,
325 | asset0Id: 0,
326 | asset1Id: 31566704,
327 | feeScale: ONE_4_DP,
328 | },
329 | ALGOUSDCTM: {
330 | provider: LPTokenProvider.TINYMAN,
331 | lpPoolAppAddress: "ZA42RCTLUMWUUB6SXEUNTI72LVSGV3TJIUTCGLNQF3KNOXFBMEPXNST3MA",
332 | lpAssetId: 1256805381,
333 | pool0AppId: 971368268,
334 | pool1AppId: 971372237,
335 | asset0Id: 0,
336 | asset1Id: 31566704,
337 | feeScale: ONE_4_DP,
338 | },
339 | ALGOEURSPLP: {
340 | provider: LPTokenProvider.PACT,
341 | lpPoolAppId: 1247810099,
342 | lpAssetId: 1247810105,
343 | pool0AppId: 971368268,
344 | pool1AppId: 1247053569,
345 | asset0Id: 0,
346 | asset1Id: 227855942,
347 | feeScale: ONE_4_DP,
348 | },
349 | ALGOgoBTCPLP: {
350 | provider: LPTokenProvider.PACT,
351 | lpPoolAppId: 2161677283,
352 | lpAssetId: 2161677289,
353 | pool0AppId: 971368268,
354 | pool1AppId: 971373361,
355 | asset0Id: 0,
356 | asset1Id: 386192725,
357 | feeScale: ONE_4_DP,
358 | },
359 | ALGOgoETHPLP: {
360 | provider: LPTokenProvider.PACT,
361 | lpPoolAppId: 2161681928,
362 | lpAssetId: 2161681934,
363 | pool0AppId: 971368268,
364 | pool1AppId: 971373611,
365 | asset0Id: 0,
366 | asset1Id: 386195940,
367 | feeScale: ONE_4_DP,
368 | },
369 | ALGOgoBTCTM: {
370 | provider: LPTokenProvider.TINYMAN,
371 | lpPoolAppAddress: "RB6SQMZINE5SEEYH6PZNSOGEUTC6BSYTOJ2YGEAHSE6MOPCLEUTCA6MNLM",
372 | lpAssetId: 2169397535,
373 | pool0AppId: 971368268,
374 | pool1AppId: 971373361,
375 | asset0Id: 0,
376 | asset1Id: 386192725,
377 | feeScale: ONE_4_DP,
378 | },
379 | ALGOgoETHTM: {
380 | provider: LPTokenProvider.TINYMAN,
381 | lpPoolAppAddress: "DU4NAE2N6FQLTYFRURJVTOC7Y7GOTZO4C7BQHHVJ2B5NZU6EDZLK7G6HKU",
382 | lpAssetId: 2169399904,
383 | pool0AppId: 971368268,
384 | pool1AppId: 971373611,
385 | asset0Id: 0,
386 | asset1Id: 386195940,
387 | feeScale: ONE_4_DP,
388 | },
389 | ALGOwBTCPLP: {
390 | provider: LPTokenProvider.PACT,
391 | lpPoolAppId: 1116367260,
392 | lpAssetId: 1116367266,
393 | pool0AppId: 971368268,
394 | pool1AppId: 1067289273,
395 | asset0Id: 0,
396 | asset1Id: 1058926737,
397 | feeScale: ONE_4_DP,
398 | },
399 | ALGOwBTCTM: {
400 | provider: LPTokenProvider.TINYMAN,
401 | lpPoolAppAddress: "IVKGUV5LF7BKJ5CAX6YXYF67743FZEZ2Z5ZFGHQQ5ZF7YJVCGAT2MQJ46Y",
402 | lpAssetId: 1385309142,
403 | pool0AppId: 971368268,
404 | pool1AppId: 1067289273,
405 | asset0Id: 0,
406 | asset1Id: 1058926737,
407 | feeScale: ONE_4_DP,
408 | },
409 | ALGOwETHPLP: {
410 | provider: LPTokenProvider.PACT,
411 | lpPoolAppId: 1116369904,
412 | lpAssetId: 1116369910,
413 | pool0AppId: 971368268,
414 | pool1AppId: 1067289481,
415 | asset0Id: 0,
416 | asset1Id: 887406851,
417 | feeScale: ONE_4_DP,
418 | },
419 | ALGOwETHTM: {
420 | provider: LPTokenProvider.TINYMAN,
421 | lpPoolAppAddress: "QHFMCKBXVLZCAXCZV36WCQL6GTVK6AGCP4ZI5GYGM7S7FUPWECBFYPNHCE",
422 | lpAssetId: 1385320489,
423 | pool0AppId: 971368268,
424 | pool1AppId: 1067289481,
425 | asset0Id: 0,
426 | asset1Id: 887406851,
427 | feeScale: ONE_4_DP,
428 | },
429 | ALGOwAVAXPLP: {
430 | provider: LPTokenProvider.PACT,
431 | lpPoolAppId: 1168319565,
432 | lpAssetId: 1168319571,
433 | pool0AppId: 971368268,
434 | pool1AppId: 1166977433,
435 | asset0Id: 0,
436 | asset1Id: 893309613,
437 | feeScale: ONE_4_DP,
438 | },
439 | ALGOwSOLPLP: {
440 | provider: LPTokenProvider.PACT,
441 | lpPoolAppId: 1168322128,
442 | lpAssetId: 1168322134,
443 | pool0AppId: 971368268,
444 | pool1AppId: 1166980669,
445 | asset0Id: 0,
446 | asset1Id: 887648583,
447 | feeScale: ONE_4_DP,
448 | },
449 | ALGOwLINKPLP: {
450 | provider: LPTokenProvider.PACT,
451 | lpPoolAppId: 1217112826,
452 | lpAssetId: 1217112832,
453 | pool0AppId: 971368268,
454 | pool1AppId: 1216434571,
455 | asset0Id: 0,
456 | asset1Id: 1200094857,
457 | feeScale: ONE_4_DP,
458 | },
459 | ALGOGOLDPLP: {
460 | provider: LPTokenProvider.PACT,
461 | lpPoolAppId: 1258807438,
462 | lpAssetId: 1258807444,
463 | pool0AppId: 971368268,
464 | pool1AppId: 1258515734,
465 | asset0Id: 0,
466 | asset1Id: 246516580,
467 | feeScale: ONE_4_DP,
468 | },
469 | ALGOGOLDTM: {
470 | provider: LPTokenProvider.TINYMAN,
471 | lpPoolAppAddress: "N44TFF4OLWLUTJS3ZA67LODV2DQR3ZBTEXKGAQ6ZIWXBDLN7J4KBLIXNIQ",
472 | lpAssetId: 2169404223,
473 | pool0AppId: 971368268,
474 | pool1AppId: 1258515734,
475 | asset0Id: 0,
476 | asset1Id: 246516580,
477 | feeScale: ONE_4_DP,
478 | },
479 | ALGOSILVERPLP: {
480 | provider: LPTokenProvider.PACT,
481 | lpPoolAppId: 1258808812,
482 | lpAssetId: 1258808818,
483 | pool0AppId: 971368268,
484 | pool1AppId: 1258524099,
485 | asset0Id: 0,
486 | asset1Id: 246519683,
487 | feeScale: ONE_4_DP,
488 | },
489 | ALGOSILVERTM: {
490 | provider: LPTokenProvider.TINYMAN,
491 | lpPoolAppAddress: "4LJMARM7FXLYLOUERA74QHUA4SIX2YMCTHGXBW7A75BOVGV6RXJYZTQSH4",
492 | lpAssetId: 2169402187,
493 | pool0AppId: 971368268,
494 | pool1AppId: 1258524099,
495 | asset0Id: 0,
496 | asset1Id: 246519683,
497 | feeScale: ONE_4_DP,
498 | },
499 | ALGOwMPLPLP: {
500 | provider: LPTokenProvider.PACT,
501 | lpPoolAppId: 1168322907,
502 | lpAssetId: 1168322913,
503 | pool0AppId: 971368268,
504 | pool1AppId: 1166982094,
505 | asset0Id: 0,
506 | asset1Id: 1163259470,
507 | feeScale: ONE_4_DP,
508 | },
509 | gALGOUSDCPLP: {
510 | provider: LPTokenProvider.PACT,
511 | lpPoolAppId: 1736210826,
512 | lpAssetId: 1736210832,
513 | pool0AppId: 971370097,
514 | pool1AppId: 971372237,
515 | asset0Id: 793124631,
516 | asset1Id: 31566704,
517 | feeScale: ONE_4_DP,
518 | },
519 | gALGOUSDCTM: {
520 | provider: LPTokenProvider.TINYMAN,
521 | lpPoolAppAddress: "XGD2PZVQLKRG5GFRSA2WG7VCHQYI7ATCN5ZF46UH2EHYBYUVXSMNFZNJYQ",
522 | lpAssetId: 1734417671,
523 | pool0AppId: 971370097,
524 | pool1AppId: 971372237,
525 | asset0Id: 793124631,
526 | asset1Id: 31566704,
527 | feeScale: ONE_4_DP,
528 | },
529 | xALGOUSDCPLP: {
530 | provider: LPTokenProvider.PACT,
531 | lpPoolAppId: 2649980181,
532 | lpAssetId: 2649980187,
533 | pool0AppId: 2611131944,
534 | pool1AppId: 971372237,
535 | asset0Id: 1134696561,
536 | asset1Id: 31566704,
537 | feeScale: ONE_4_DP,
538 | },
539 | xALGOUSDCTM: {
540 | provider: LPTokenProvider.TINYMAN,
541 | lpPoolAppAddress: "EHYUE2VAI22PB5DID4EHUD5YH7UIOE2NG5XPQZ7NZPIHFUQ334C7J5OSTY",
542 | lpAssetId: 2649938842,
543 | pool0AppId: 2611131944,
544 | pool1AppId: 971372237,
545 | asset0Id: 1134696561,
546 | asset1Id: 31566704,
547 | feeScale: ONE_4_DP,
548 | },
549 | USDCUSDtPLP: {
550 | provider: LPTokenProvider.PACT,
551 | lpPoolAppId: 1116364721,
552 | lpAssetId: 1116364727,
553 | pool0AppId: 971372237,
554 | pool1AppId: 971372700,
555 | asset0Id: 31566704,
556 | asset1Id: 312769,
557 | feeScale: ONE_4_DP,
558 | },
559 | USDCUSDtTM: {
560 | provider: LPTokenProvider.TINYMAN,
561 | lpPoolAppAddress: "JADZYEIDHPHZAFSD45M7GOQSAEMETOMTUSHPHLXIMYJPSLKPADF52Y245I",
562 | lpAssetId: 1332995647,
563 | pool0AppId: 971372237,
564 | pool1AppId: 971372700,
565 | asset0Id: 31566704,
566 | asset1Id: 312769,
567 | feeScale: ONE_4_DP,
568 | },
569 | USDCEURSPLP: {
570 | provider: LPTokenProvider.PACT,
571 | lpPoolAppId: 1247811167,
572 | lpAssetId: 1247811173,
573 | pool0AppId: 971372237,
574 | pool1AppId: 1247053569,
575 | asset0Id: 31566704,
576 | asset1Id: 227855942,
577 | feeScale: ONE_4_DP,
578 | },
579 | USDCEURSTM: {
580 | provider: LPTokenProvider.TINYMAN,
581 | lpPoolAppAddress: "MSQ46LA7UBKA2JPG5MAMSKWJ5FO35PSKIFGTCKJ6YSL5NLZMZH4HAXMPKU",
582 | lpAssetId: 1394310065,
583 | pool0AppId: 971372237,
584 | pool1AppId: 1247053569,
585 | asset0Id: 31566704,
586 | asset1Id: 227855942,
587 | feeScale: ONE_4_DP,
588 | },
589 | USDCwBTCTM: {
590 | provider: LPTokenProvider.TINYMAN,
591 | lpPoolAppAddress: "IT7H47FAYPVWHHYF23KIPNFDIOF36MIUI2T54R47WCX6MLMTVVQZ3UK5GI",
592 | lpAssetId: 1394237139,
593 | pool0AppId: 971372237,
594 | pool1AppId: 1067289273,
595 | asset0Id: 31566704,
596 | asset1Id: 1058926737,
597 | feeScale: ONE_4_DP,
598 | },
599 | USDCwETHTM: {
600 | provider: LPTokenProvider.TINYMAN,
601 | lpPoolAppAddress: "KPG7LTBPFTEZYXBT47TAR56X6QC5BFCEFAATXJH2CRHSYAGE66X5FLQKEY",
602 | lpAssetId: 1734424720,
603 | pool0AppId: 971372237,
604 | pool1AppId: 1067289481,
605 | asset0Id: 31566704,
606 | asset1Id: 887406851,
607 | feeScale: ONE_4_DP,
608 | },
609 | USDCwAVAXTM: {
610 | provider: LPTokenProvider.TINYMAN,
611 | lpPoolAppAddress: "MJN7SKRYHIJ3I5BIO3U2LVS44FAMHBWR3LDEMVYA47BPQJ4HIBXIWKEF4M",
612 | lpAssetId: 1734427105,
613 | pool0AppId: 971372237,
614 | pool1AppId: 1166977433,
615 | asset0Id: 31566704,
616 | asset1Id: 893309613,
617 | feeScale: ONE_4_DP,
618 | },
619 | USDCwSOLTM: {
620 | provider: LPTokenProvider.TINYMAN,
621 | lpPoolAppAddress: "U6XAS3L5KRCOD32OI3LEDRVCMHN42E5YCZBFGJTGOWCE35LFVPS4QKE5W4",
622 | lpAssetId: 1734429522,
623 | pool0AppId: 971372237,
624 | pool1AppId: 1166980669,
625 | asset0Id: 31566704,
626 | asset1Id: 887648583,
627 | feeScale: ONE_4_DP,
628 | },
629 | USDCwLINKTM: {
630 | provider: LPTokenProvider.TINYMAN,
631 | lpPoolAppAddress: "ULHYOPZ5DQJM3QYQ3RXWRWSGDGJ4WEBNGSFCFYI3PRXSINAL7GHV4DZO5Q",
632 | lpAssetId: 1734433423,
633 | pool0AppId: 971372237,
634 | pool1AppId: 1216434571,
635 | asset0Id: 31566704,
636 | asset1Id: 1200094857,
637 | feeScale: ONE_4_DP,
638 | },
639 | USDCGOLDPLP: {
640 | provider: LPTokenProvider.PACT,
641 | lpPoolAppId: 1736289878,
642 | lpAssetId: 1736289884,
643 | pool0AppId: 971372237,
644 | pool1AppId: 1258515734,
645 | asset0Id: 31566704,
646 | asset1Id: 246516580,
647 | feeScale: ONE_4_DP,
648 | },
649 | USDCSILVERPLP: {
650 | provider: LPTokenProvider.PACT,
651 | lpPoolAppId: 1736326581,
652 | lpAssetId: 1736326587,
653 | pool0AppId: 971372237,
654 | pool1AppId: 1258524099,
655 | asset0Id: 31566704,
656 | asset1Id: 246519683,
657 | feeScale: ONE_4_DP,
658 | },
659 | };
660 |
661 | const MainnetReserveAddress: ReserveAddress = "Q5Q5FC5PTYQIUX5PGNTEW22UJHJHVVUEMMWV2LSG6MGT33YQ54ST7FEIGA";
662 |
663 | const MainnetOracle: Oracle = {
664 | oracle0AppId: 1040271396,
665 | oracle1AppId: 971323141,
666 | oracleAdapterAppId: 971333964,
667 | decimals: 14,
668 | };
669 |
670 | const MainnetOpUp: OpUp = {
671 | callerAppId: 1167143153,
672 | baseAppId: 971335616,
673 | };
674 |
675 | export {
676 | MainnetPoolManagerAppId,
677 | MainnetDepositsAppId,
678 | MainnetDepositStakingAppId,
679 | MainnetPoolKey,
680 | MainnetPools,
681 | MainnetLoans,
682 | MainnetTinymanAppId,
683 | MainnetLendingPoolKey,
684 | MainnetLendingPools,
685 | MainnetReserveAddress,
686 | MainnetOracle,
687 | MainnetOpUp,
688 | };
689 |
--------------------------------------------------------------------------------
/src/lend/constants/testnet-constants.ts:
--------------------------------------------------------------------------------
1 | import { LoanType } from "../types";
2 |
3 | import type { OpUp, Oracle, Pool, ReserveAddress } from "../types";
4 |
5 | const TestnetPoolManagerAppId = 147157634;
6 |
7 | const TestnetDepositsAppId = 147157692;
8 |
9 | type TestnetPoolKey = "ALGO" | "gALGO" | "xALGO" | "USDC" | "USDt" | "goBTC" | "goETH";
10 | const TestnetPools: Record = {
11 | ALGO: {
12 | appId: 147169673,
13 | assetId: 0,
14 | fAssetId: 147171698,
15 | frAssetId: 147171699,
16 | assetDecimals: 6,
17 | poolManagerIndex: 0,
18 | loans: {
19 | 147173131: BigInt(0),
20 | 168153622: BigInt(0),
21 | 397181473: BigInt(1),
22 | 397181998: BigInt(1),
23 | },
24 | },
25 | gALGO: {
26 | appId: 168152517,
27 | assetId: 167184545,
28 | fAssetId: 168153084,
29 | frAssetId: 168153085,
30 | assetDecimals: 6,
31 | poolManagerIndex: 5,
32 | loans: {
33 | 147173131: BigInt(5),
34 | 168153622: BigInt(1),
35 | },
36 | },
37 | xALGO: {
38 | appId: 730786369,
39 | assetId: 730430700,
40 | fAssetId: 730786397,
41 | frAssetId: 730786398,
42 | assetDecimals: 6,
43 | poolManagerIndex: 6,
44 | loans: {
45 | 147173131: BigInt(6),
46 | 168153622: BigInt(2),
47 | },
48 | },
49 | USDC: {
50 | appId: 147170678,
51 | assetId: 67395862,
52 | fAssetId: 147171826,
53 | frAssetId: 147171827,
54 | assetDecimals: 6,
55 | poolManagerIndex: 1,
56 | loans: {
57 | 147173131: BigInt(1),
58 | 147173190: BigInt(0),
59 | 397181473: BigInt(0),
60 | 397181998: BigInt(0),
61 | },
62 | },
63 | USDt: {
64 | appId: 147171033,
65 | assetId: 67396430,
66 | fAssetId: 147172417,
67 | frAssetId: 147172418,
68 | assetDecimals: 6,
69 | poolManagerIndex: 2,
70 | loans: {
71 | 147173131: BigInt(2),
72 | 147173190: BigInt(1),
73 | },
74 | },
75 | goBTC: {
76 | appId: 147171314,
77 | assetId: 67396528,
78 | fAssetId: 147172646,
79 | frAssetId: 147172647,
80 | assetDecimals: 8,
81 | poolManagerIndex: 3,
82 | loans: {
83 | 147173131: BigInt(3),
84 | 397181473: BigInt(2),
85 | 397181998: BigInt(2),
86 | },
87 | },
88 | goETH: {
89 | appId: 147171345,
90 | assetId: 76598897,
91 | fAssetId: 147172881,
92 | frAssetId: 147172882,
93 | assetDecimals: 8,
94 | poolManagerIndex: 4,
95 | loans: {
96 | 147173131: BigInt(4),
97 | 397181473: BigInt(3),
98 | 397181998: BigInt(3),
99 | },
100 | },
101 | };
102 |
103 | const TestnetLoans: Partial> = {
104 | [LoanType.GENERAL]: 147173131,
105 | [LoanType.STABLECOIN_EFFICIENCY]: 147173190,
106 | [LoanType.ALGO_EFFICIENCY]: 168153622,
107 | [LoanType.ULTRASWAP_UP]: 397181473,
108 | [LoanType.ULTRASWAP_DOWN]: 397181998,
109 | };
110 |
111 | const TestnetReserveAddress: ReserveAddress = "KLF3MEIIHMTA7YHNPLBDVHLN2MVC27X5M7ULTDZLMEX5XO5XCUP7HGBHMQ";
112 |
113 | const TestnetOracle: Oracle = {
114 | oracle0AppId: 159512493,
115 | oracleAdapterAppId: 147153711,
116 | decimals: 14,
117 | };
118 |
119 | const TestnetOpUp: OpUp = {
120 | callerAppId: 397104542,
121 | baseAppId: 118186203,
122 | };
123 |
124 | export {
125 | TestnetPoolManagerAppId,
126 | TestnetDepositsAppId,
127 | TestnetPoolKey,
128 | TestnetPools,
129 | TestnetLoans,
130 | TestnetReserveAddress,
131 | TestnetOracle,
132 | TestnetOpUp,
133 | };
134 |
--------------------------------------------------------------------------------
/src/lend/deposit-staking.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AtomicTransactionComposer,
3 | generateAccount,
4 | getApplicationAddress,
5 | getMethodByName,
6 | makeApplicationCloseOutTxn,
7 | OnApplicationComplete,
8 | } from "algosdk";
9 |
10 | import { maximum } from "../math-lib";
11 | import {
12 | addEscrowNoteTransaction,
13 | fromIntToByteHex,
14 | getAccountApplicationLocalState,
15 | getAccountDetails,
16 | getApplicationGlobalState,
17 | getParsedValueFromState,
18 | removeEscrowNoteTransaction,
19 | signer,
20 | unixTime,
21 | } from "../utils";
22 |
23 | import { depositStakingABIContract } from "./abi-contracts";
24 | import { depositStakingLocalState, depositStakingProgramsInfo, getEscrows, userDepositStakingInfo } from "./utils";
25 |
26 | import type { DepositStakingInfo, Pool, UserDepositStakingLocalState } from "./types";
27 | import type { Account, Algodv2, Indexer, SuggestedParams, Transaction } from "algosdk";
28 |
29 | /**
30 | *
31 | * Returns information regarding the given deposit staking application.
32 | *
33 | * @param client - Algorand client to query
34 | * @param depositStakingAppId - deposit staking application to query about
35 | * @returns Promise pool info
36 | */
37 | async function retrieveDepositStakingInfo(
38 | client: Algodv2 | Indexer,
39 | depositStakingAppId: number,
40 | ): Promise {
41 | const { currentRound, globalState: state } = await getApplicationGlobalState(client, depositStakingAppId);
42 | if (state === undefined) throw Error("Could not find Deposit Staking");
43 |
44 | // initialise staking program
45 | const stakingPrograms = [];
46 | for (let i = 0; i <= 5; i++) {
47 | const prefix = "S".charCodeAt(0).toString(16);
48 | const stakeBase64Value = String(getParsedValueFromState(state, prefix + fromIntToByteHex(i), "hex"));
49 | const stakeValue = Buffer.from(stakeBase64Value, "base64").toString("hex");
50 |
51 | for (let j = 0; j <= 4; j++) {
52 | const basePos = j * 46;
53 |
54 | const rewards: {
55 | rewardAssetId: number;
56 | endTimestamp: bigint;
57 | rewardRate: bigint;
58 | rewardPerToken: bigint;
59 | }[] = [];
60 | stakingPrograms.push({
61 | poolAppId: Number("0x" + stakeValue.slice(basePos, basePos + 12)),
62 | totalStaked: BigInt("0x" + stakeValue.slice(basePos + 12, basePos + 28)),
63 | minTotalStaked: BigInt("0x" + stakeValue.slice(basePos + 28, basePos + 44)),
64 | stakeIndex: i * 5 + j,
65 | numRewards: Number("0x" + stakeValue.slice(basePos + 44, basePos + 46)),
66 | rewards,
67 | });
68 | }
69 | }
70 |
71 | // add rewards
72 | for (let i = 0; i <= 22; i++) {
73 | const prefix = "R".charCodeAt(0).toString(16);
74 | const rewardBase64Value = String(getParsedValueFromState(state, prefix + fromIntToByteHex(i), "hex"));
75 | const rewardValue = Buffer.from(rewardBase64Value, "base64").toString("hex");
76 | for (let j = 0; j <= (i !== 22 ? 3 : 1); j++) {
77 | const basePos = j * 60;
78 |
79 | const stakeIndex = Number(BigInt(i * 4 + j) / BigInt(3));
80 | const localRewardIndex = Number(BigInt(i * 4 + j) % BigInt(3));
81 | const { totalStaked, minTotalStaked, rewards, numRewards } = stakingPrograms[stakeIndex];
82 | if (localRewardIndex >= numRewards) continue;
83 |
84 | const ts = maximum(totalStaked, minTotalStaked);
85 | const endTimestamp = BigInt("0x" + rewardValue.slice(basePos + 12, basePos + 20));
86 | const lu = BigInt("0x" + rewardValue.slice(basePos + 20, basePos + 28));
87 | const rewardRate = BigInt("0x" + rewardValue.slice(basePos + 28, basePos + 44));
88 | const rpt = BigInt("0x" + rewardValue.slice(basePos + 44, basePos + 60));
89 | const currTime = BigInt(unixTime());
90 | const dt = currTime <= endTimestamp ? currTime - lu : lu <= endTimestamp ? endTimestamp - lu : BigInt(0);
91 | const rewardPerToken = rpt + (rewardRate * dt) / ts;
92 |
93 | rewards.push({
94 | rewardAssetId: Number("0x" + rewardValue.slice(basePos, basePos + 12)),
95 | endTimestamp,
96 | rewardRate,
97 | rewardPerToken,
98 | });
99 | }
100 | }
101 |
102 | // combine
103 | return { currentRound, stakingPrograms };
104 | }
105 |
106 | /**
107 | *
108 | * Returns local state regarding the deposit staking escrows of a given user.
109 | * Use for advanced use cases where optimising number of network request.
110 | *
111 | * @param indexerClient - Algorand indexer client to query
112 | * @param depositStakingAppId - deposit staking application to query about
113 | * @param userAddr - account address for the user
114 | * @returns Promise deposit staking escrows' local state
115 | */
116 | async function retrieveUserDepositStakingsLocalState(
117 | indexerClient: Indexer,
118 | depositStakingAppId: number,
119 | userAddr: string,
120 | ): Promise {
121 | const depositStakingsLocalState: UserDepositStakingLocalState[] = [];
122 |
123 | const escrows = await getEscrows(indexerClient, userAddr, depositStakingAppId, "fa ", "fr ");
124 |
125 | // get all remaining deposit stakings' local state
126 | for (const escrowAddr of escrows) {
127 | const [{ holdings }, { currentRound, localState: state }] = await Promise.all([
128 | getAccountDetails(indexerClient, escrowAddr),
129 | getAccountApplicationLocalState(indexerClient, depositStakingAppId, escrowAddr),
130 | ]);
131 |
132 | const optedIntoAssets: Set = new Set(holdings.keys());
133 | if (state === undefined)
134 | throw Error(`Could not find deposit staking ${depositStakingAppId} in escrow ${escrowAddr}`);
135 |
136 | depositStakingsLocalState.push({
137 | ...depositStakingLocalState(state, depositStakingAppId, escrowAddr),
138 | currentRound,
139 | optedIntoAssets,
140 | });
141 | }
142 |
143 | return depositStakingsLocalState;
144 | }
145 |
146 | /**
147 | *
148 | * Returns local state regarding the deposit staking escrows of a given user.
149 | * Use for advanced use cases where optimising number of network request.
150 | *
151 | * @param indexerClient - Algorand indexer client to query
152 | * @param depositStakingAppId - deposit staking application to query about
153 | * @param escrowAddr - account address for the deposit staking escrow
154 | * @returns Promise deposit staking escrows' local state
155 | */
156 | async function retrieveUserDepositStakingLocalState(
157 | indexerClient: Indexer,
158 | depositStakingAppId: number,
159 | escrowAddr: string,
160 | ): Promise {
161 | const { currentRound, localState: state } = await getAccountApplicationLocalState(
162 | indexerClient,
163 | depositStakingAppId,
164 | escrowAddr,
165 | );
166 | if (state === undefined) throw Error(`Could not find deposit staking ${depositStakingAppId} in escrow ${escrowAddr}`);
167 | return { currentRound, ...depositStakingLocalState(state, depositStakingAppId, escrowAddr) };
168 | }
169 |
170 | /**
171 | *
172 | * Returns a group transaction to deposit staking escrow.
173 | *
174 | * @param depositStakingAppId - deposit staking application to query about
175 | * @param userAddr - account address for the user
176 | * @param params - suggested params for the transactions with the fees overwritten
177 | * @returns { txns: Transaction[], escrow: Account } object containing group transaction and generated escrow account
178 | */
179 | function prepareAddDepositStakingEscrow(
180 | depositStakingAppId: number,
181 | userAddr: string,
182 | params: SuggestedParams,
183 | ): { txns: Transaction[]; escrow: Account } {
184 | const escrow = generateAccount();
185 |
186 | const userCall = addEscrowNoteTransaction(userAddr, escrow.addr, depositStakingAppId, "fa ", {
187 | ...params,
188 | flatFee: true,
189 | fee: 2000,
190 | });
191 |
192 | const atc = new AtomicTransactionComposer();
193 | atc.addMethodCall({
194 | sender: escrow.addr,
195 | signer,
196 | appID: depositStakingAppId,
197 | onComplete: OnApplicationComplete.OptInOC,
198 | method: getMethodByName(depositStakingABIContract.methods, "add_f_staking_escrow"),
199 | methodArgs: [{ txn: userCall, signer }],
200 | rekeyTo: getApplicationAddress(depositStakingAppId),
201 | suggestedParams: { ...params, flatFee: true, fee: 0 },
202 | });
203 | const txns = atc.buildGroup().map(({ txn }) => {
204 | txn.group = undefined;
205 | return txn;
206 | });
207 | return { txns, escrow };
208 | }
209 |
210 | /**
211 | *
212 | * Returns a transaction to opt deposit staking escrow into asset so that it can hold a given pool's f asset.
213 | *
214 | * @param depositStakingAppId - deposit staking application of escrow
215 | * @param poolManagerAppId - pool manager application
216 | * @param userAddr - account address for the user
217 | * @param escrowAddr - account address for the deposit staking escrow
218 | * @param pool - pool to add f asset of
219 | * @param stakeIndex - staking program index
220 | * @param params - suggested params for the transactions with the fees overwritten
221 | * @returns Transaction opt deposit staking escrow into asset transaction
222 | */
223 | function prepareOptDepositStakingEscrowIntoAsset(
224 | depositStakingAppId: number,
225 | poolManagerAppId: number,
226 | userAddr: string,
227 | escrowAddr: string,
228 | pool: Pool,
229 | stakeIndex: number,
230 | params: SuggestedParams,
231 | ): Transaction {
232 | const { appId: poolAppId, fAssetId } = pool;
233 |
234 | const atc = new AtomicTransactionComposer();
235 | atc.addMethodCall({
236 | sender: userAddr,
237 | signer,
238 | appID: depositStakingAppId,
239 | method: getMethodByName(depositStakingABIContract.methods, "opt_escrow_into_asset"),
240 | methodArgs: [escrowAddr, poolAppId, fAssetId, stakeIndex],
241 | suggestedParams: { ...params, flatFee: true, fee: 2000 },
242 | });
243 | const txns = atc.buildGroup().map(({ txn }) => {
244 | txn.group = undefined;
245 | return txn;
246 | });
247 | return txns[0];
248 | }
249 |
250 | /**
251 | *
252 | * Returns a transaction to sync stake of deposit staking escrow
253 | *
254 | * @param depositStakingAppId - deposit staking application of escrow
255 | * @param pool - pool to sync stake of
256 | * @param userAddr - account address for the user
257 | * @param escrowAddr - account address for the deposit staking escrow
258 | * @param stakeIndex - staking program index
259 | * @param params - suggested params for the transactions with the fees overwritten
260 | * @returns Transaction sync stake of deposit staking escrow transaction
261 | */
262 | function prepareSyncStakeInDepositStakingEscrow(
263 | depositStakingAppId: number,
264 | pool: Pool,
265 | userAddr: string,
266 | escrowAddr: string,
267 | stakeIndex: number,
268 | params: SuggestedParams,
269 | ): Transaction {
270 | const { appId: poolAppId, fAssetId } = pool;
271 |
272 | const atc = new AtomicTransactionComposer();
273 | atc.addMethodCall({
274 | sender: userAddr,
275 | signer,
276 | appID: depositStakingAppId,
277 | method: getMethodByName(depositStakingABIContract.methods, "sync_stake"),
278 | methodArgs: [escrowAddr, poolAppId, fAssetId, stakeIndex],
279 | suggestedParams: { ...params, flatFee: true, fee: 2000 },
280 | });
281 | const txns = atc.buildGroup().map(({ txn }) => {
282 | txn.group = undefined;
283 | return txn;
284 | });
285 | return txns[0];
286 | }
287 |
288 | /**
289 | *
290 | * Returns a transaction to claim the rewards of a deposit staking escrow
291 | *
292 | * @param depositStakingAppId - deposit staking application of escrow
293 | * @param userAddr - account address for the user
294 | * @param escrowAddr - account address for the deposit staking escrow
295 | * @param receiverAddr - account address to receive the withdrawal (typically the same as the user address)
296 | * @param stakeIndex - staking program index
297 | * @param rewardAssetIds - the asset ids of all the rewards assets claiming
298 | * @param params - suggested params for the transactions with the fees overwritten
299 | * @returns Transaction withdraw from deposit staking escrow transaction
300 | */
301 | function prepareClaimRewardsOfDepositStakingEscrow(
302 | depositStakingAppId: number,
303 | userAddr: string,
304 | escrowAddr: string,
305 | receiverAddr: string,
306 | stakeIndex: number,
307 | rewardAssetIds: number[],
308 | params: SuggestedParams,
309 | ): Transaction {
310 | const atc = new AtomicTransactionComposer();
311 | atc.addMethodCall({
312 | sender: userAddr,
313 | signer,
314 | appID: depositStakingAppId,
315 | method: getMethodByName(depositStakingABIContract.methods, "claim_rewards"),
316 | methodArgs: [escrowAddr, receiverAddr, stakeIndex],
317 | suggestedParams: { ...params, flatFee: true, fee: 4000 },
318 | });
319 | const txns = atc.buildGroup().map(({ txn }) => {
320 | txn.group = undefined;
321 | return txn;
322 | });
323 | txns[0].appForeignAssets = rewardAssetIds;
324 | return txns[0];
325 | }
326 |
327 | /**
328 | *
329 | * Returns a transaction to withdraw from a deposit staking escrow
330 | *
331 | * @param depositStakingAppId - deposit staking application of escrow
332 | * @param pool - pool to withdraw from
333 | * @param poolManagerAppId - pool manager application
334 | * @param userAddr - account address for the user
335 | * @param escrowAddr - account address for the deposit staking escrow
336 | * @param receiverAddr - account address to receive the withdrawal (typically the same as the user address)
337 | * @param amount - the amount of asset / f asset to send to withdraw from escrow.
338 | * @param isfAssetAmount - whether the amount to withdraw is expressed in terms of f asset or asset
339 | * @param remainDeposited - whether receiver should get f asset or asset (cannot remain deposited and use asset amount)
340 | * @param stakeIndex - staking program index
341 | * @param params - suggested params for the transactions with the fees overwritten
342 | * @returns Transaction withdraw from deposit staking escrow transaction
343 | */
344 | function prepareWithdrawFromDepositStakingEscrow(
345 | depositStakingAppId: number,
346 | pool: Pool,
347 | poolManagerAppId: number,
348 | userAddr: string,
349 | escrowAddr: string,
350 | receiverAddr: string,
351 | amount: number | bigint,
352 | isfAssetAmount: boolean,
353 | remainDeposited: boolean,
354 | stakeIndex: number,
355 | params: SuggestedParams,
356 | ): Transaction {
357 | const { appId: poolAppId, assetId, fAssetId } = pool;
358 |
359 | const atc = new AtomicTransactionComposer();
360 | atc.addMethodCall({
361 | sender: userAddr,
362 | signer,
363 | appID: depositStakingAppId,
364 | method: getMethodByName(depositStakingABIContract.methods, "withdraw_stake"),
365 | methodArgs: [
366 | escrowAddr,
367 | receiverAddr,
368 | poolManagerAppId,
369 | poolAppId,
370 | assetId,
371 | fAssetId,
372 | amount,
373 | isfAssetAmount,
374 | remainDeposited,
375 | stakeIndex,
376 | ],
377 | suggestedParams: { ...params, flatFee: true, fee: 6000 },
378 | });
379 | const txns = atc.buildGroup().map(({ txn }) => {
380 | txn.group = undefined;
381 | return txn;
382 | });
383 | return txns[0];
384 | }
385 |
386 | /**
387 | *
388 | * Returns a transaction to remove asset from deposit staking escrow
389 | *
390 | * @param depositStakingAppId - deposit staking application of escrow
391 | * @param userAddr - account address for the user
392 | * @param escrowAddr - account address for the deposit staking escrow
393 | * @param pool - pool to remove f asset of
394 | * @param params - suggested params for the transactions with the fees overwritten
395 | * @returns Transaction opt out deposit staking escrow from asset transaction
396 | */
397 | function prepareOptOutDepositStakingEscrowFromAsset(
398 | depositStakingAppId: number,
399 | userAddr: string,
400 | escrowAddr: string,
401 | pool: Pool,
402 | params: SuggestedParams,
403 | ): Transaction {
404 | const { appId: poolAppId, fAssetId } = pool;
405 |
406 | const atc = new AtomicTransactionComposer();
407 | atc.addMethodCall({
408 | sender: userAddr,
409 | signer,
410 | appID: depositStakingAppId,
411 | method: getMethodByName(depositStakingABIContract.methods, "close_out_escrow_from_asset"),
412 | methodArgs: [escrowAddr, getApplicationAddress(poolAppId), fAssetId],
413 | suggestedParams: { ...params, flatFee: true, fee: 3000 },
414 | });
415 | const txns = atc.buildGroup().map(({ txn }) => {
416 | txn.group = undefined;
417 | return txn;
418 | });
419 | return txns[0];
420 | }
421 |
422 | /**
423 | *
424 | * Returns a group transaction to remove a user's deposit staking escrow and return its minimum balance to the user.
425 | *
426 | * @param depositStakingAppId - deposit staking application of escrow
427 | * @param userAddr - account address for the user
428 | * @param escrowAddr - account address for the deposit staking escrow
429 | * @param params - suggested params for the transactions with the fees overwritten
430 | * @returns Transaction[] remove and close out deposit staking escrow group transaction
431 | */
432 | function prepareRemoveDepositStakingEscrow(
433 | depositStakingAppId: number,
434 | userAddr: string,
435 | escrowAddr: string,
436 | params: SuggestedParams,
437 | ) {
438 | const atc = new AtomicTransactionComposer();
439 | atc.addMethodCall({
440 | sender: userAddr,
441 | signer,
442 | appID: depositStakingAppId,
443 | method: getMethodByName(depositStakingABIContract.methods, "remove_f_staking_escrow"),
444 | methodArgs: [escrowAddr],
445 | suggestedParams: { ...params, flatFee: true, fee: 2000 },
446 | });
447 | const txns = atc.buildGroup().map(({ txn }) => {
448 | txn.group = undefined;
449 | return txn;
450 | });
451 | const optOutTx = makeApplicationCloseOutTxn(escrowAddr, params, depositStakingAppId);
452 | const closeToTx = removeEscrowNoteTransaction(escrowAddr, userAddr, "fr ", params);
453 | return [txns[0], optOutTx, closeToTx];
454 | }
455 |
456 | export {
457 | retrieveDepositStakingInfo,
458 | depositStakingProgramsInfo,
459 | retrieveUserDepositStakingsLocalState,
460 | retrieveUserDepositStakingLocalState,
461 | userDepositStakingInfo,
462 | prepareAddDepositStakingEscrow,
463 | prepareOptDepositStakingEscrowIntoAsset,
464 | prepareSyncStakeInDepositStakingEscrow,
465 | prepareClaimRewardsOfDepositStakingEscrow,
466 | prepareWithdrawFromDepositStakingEscrow,
467 | prepareOptOutDepositStakingEscrowFromAsset,
468 | prepareRemoveDepositStakingEscrow,
469 | };
470 |
--------------------------------------------------------------------------------
/src/lend/formulae.ts:
--------------------------------------------------------------------------------
1 | import {
2 | divScale,
3 | divScaleRoundUp,
4 | expBySquaring,
5 | mulScale,
6 | mulScaleRoundUp,
7 | ONE_10_DP,
8 | ONE_14_DP,
9 | ONE_16_DP,
10 | ONE_4_DP,
11 | SECONDS_IN_YEAR,
12 | sqrt,
13 | } from "../math-lib";
14 | import { unixTime } from "../utils";
15 |
16 | /**
17 | * Calculates the dollar value of a given asset amount
18 | * @param amount (0dp)
19 | * @param price (14dp)
20 | * @return value (0dp)
21 | */
22 | function calcAssetDollarValue(amount: bigint, price: bigint): bigint {
23 | return mulScaleRoundUp(amount, price, ONE_14_DP);
24 | }
25 |
26 | /**
27 | * Calculates the total debt of a pool
28 | * @param totalVarDebt (0dp)
29 | * @param totalStblDebt (0dp)
30 | * @return totalDebt (0dp)
31 | */
32 | function calcTotalDebt(totalVarDebt: bigint, totalStblDebt: bigint): bigint {
33 | return totalVarDebt + totalStblDebt;
34 | }
35 |
36 | /**
37 | * Calculates the total debt of a pool
38 | * @param totalDebt (0dp)
39 | * @param totalDeposits (0dp)
40 | * @return availableLiquidity (0dp)
41 | */
42 | function calcAvailableLiquidity(totalDebt: bigint, totalDeposits: bigint): bigint {
43 | return totalDeposits - totalDebt;
44 | }
45 |
46 | /**
47 | * Calculates the ratio of the available liquidity that is being stable borrowed
48 | * @param stblBorAmount (0dp)
49 | * @param availableLiquidity (0dp)
50 | * @return stableBorrowRatio (16dp)
51 | */
52 | function calcStableBorrowRatio(stblBorAmount: bigint, availableLiquidity: bigint): bigint {
53 | return divScale(stblBorAmount, availableLiquidity, ONE_16_DP);
54 | }
55 |
56 | /**
57 | * Calculates the maximum stable borrow amount a user can make in one go
58 | * @param availableLiquidity (0dp)
59 | * @param sbpc (0dp)
60 | * @return stableBorrowRatio (16dp)
61 | */
62 | function calcMaxSingleStableBorrow(availableLiquidity: bigint, sbpc: bigint): bigint {
63 | return mulScale(availableLiquidity, sbpc, ONE_16_DP);
64 | }
65 |
66 | /**
67 | * Calculates the utilisation ratio of a pool
68 | * @param totalDebt (0dp)
69 | * @param totalDeposits (0dp)
70 | * @return utilisationRatio (16dp)
71 | */
72 | function calcUtilisationRatio(totalDebt: bigint, totalDeposits: bigint): bigint {
73 | if (totalDeposits === BigInt(0)) return BigInt(0);
74 | return divScale(totalDebt, totalDeposits, ONE_16_DP);
75 | }
76 |
77 | /**
78 | * Calculates the stable debt to total debt ratio of a pool
79 | * @param totalStblDebt (0dp)
80 | * @param totalDebt (0dp)
81 | * @return stableDebtToTotalDebtRatio (16dp)
82 | */
83 | function calcStableDebtToTotalDebtRatio(totalStblDebt: bigint, totalDebt: bigint): bigint {
84 | if (totalDebt === BigInt(0)) return BigInt(0);
85 | return divScale(totalStblDebt, totalDebt, ONE_16_DP);
86 | }
87 |
88 | /**
89 | * Calculate the variable borrow interest rate of a pool
90 | * @param vr0 (16dp)
91 | * @param vr1 (16dp)
92 | * @param vr2 (16dp)
93 | * @param ut (16dp)
94 | * @param uopt (16dp)
95 | * @return variableBorrowInterestRate (16dp)
96 | */
97 | function calcVariableBorrowInterestRate(vr0: bigint, vr1: bigint, vr2: bigint, ut: bigint, uopt: bigint): bigint {
98 | return ut < uopt
99 | ? vr0 + divScale(mulScale(ut, vr1, ONE_16_DP), uopt, ONE_16_DP)
100 | : vr0 + vr1 + divScale(mulScale(ut - uopt, vr2, ONE_16_DP), ONE_16_DP - uopt, ONE_16_DP);
101 | }
102 |
103 | /**
104 | * Calculate the stable borrow interest rate of a pool
105 | * @param vr1 (16dp)
106 | * @param sr0 (16dp)
107 | * @param sr1 (16dp)
108 | * @param sr2 (16dp)
109 | * @param sr3 (16dp)
110 | * @param ut (16dp)
111 | * @param uopt (16dp)
112 | * @param ratiot (16dp)
113 | * @param ratioopt (16dp)
114 | * @return stableBorrowInterestRate (16dp)
115 | */
116 | function calcStableBorrowInterestRate(
117 | vr1: bigint,
118 | sr0: bigint,
119 | sr1: bigint,
120 | sr2: bigint,
121 | sr3: bigint,
122 | ut: bigint,
123 | uopt: bigint,
124 | ratiot: bigint,
125 | ratioopt: bigint,
126 | ): bigint {
127 | const base =
128 | ut <= uopt
129 | ? vr1 + sr0 + divScale(mulScale(ut, sr1, ONE_16_DP), uopt, ONE_16_DP)
130 | : vr1 + sr0 + sr1 + divScale(mulScale(ut - uopt, sr2, ONE_16_DP), ONE_16_DP - uopt, ONE_16_DP);
131 | const extra =
132 | ratiot <= ratioopt
133 | ? BigInt(0)
134 | : divScale(mulScale(sr3, ratiot - ratioopt, ONE_16_DP), ONE_16_DP - ratioopt, ONE_16_DP);
135 | return base + extra;
136 | }
137 |
138 | /**
139 | * Calculate the overall borrow interest rate of a pool
140 | * @param totalVarDebt (0dp)
141 | * @param totalDebt (0dp)
142 | * @param vbirt (16dp)
143 | * @param osbiat (16dp)
144 | * @return overallBorrowInterestRate (16dp)
145 | */
146 | function calcOverallBorrowInterestRate(totalVarDebt: bigint, totalDebt: bigint, vbirt: bigint, osbiat: bigint): bigint {
147 | if (totalDebt === BigInt(0)) return BigInt(0);
148 | return (totalVarDebt * vbirt + osbiat) / totalDebt;
149 | }
150 |
151 | /**
152 | * Calculate the deposit interest rate of a pool
153 | * @param obirt (16dp)
154 | * @param ut (16dp)
155 | * @param rr (16dp)
156 | * @return overallBorrowInterestRate (16dp)
157 | */
158 | function calcDepositInterestRate(obirt: bigint, rr: bigint, ut: bigint): bigint {
159 | return mulScale(mulScale(ut, obirt, ONE_16_DP), ONE_16_DP - rr, ONE_16_DP);
160 | }
161 |
162 | /**
163 | * Calculate the borrow interest index of pool
164 | * @param birt1 (16dp)
165 | * @param biit1 (16dp)
166 | * @param latestUpdate (0dp)
167 | * @return borrowInterestIndex (14dp)
168 | */
169 | function calcBorrowInterestIndex(birt1: bigint, biit1: bigint, latestUpdate: bigint): bigint {
170 | const dt = BigInt(unixTime()) - latestUpdate;
171 | return mulScale(biit1, expBySquaring(ONE_16_DP + birt1 / SECONDS_IN_YEAR, dt, ONE_16_DP), ONE_16_DP);
172 | }
173 |
174 | /**
175 | * Calculate the deposit interest index of pool
176 | * @param dirt1 (16dp)
177 | * @param diit1 (16dp)
178 | * @param latestUpdate (0dp)
179 | * @return depositInterestIndex (14dp)
180 | */
181 | function calcDepositInterestIndex(dirt1: bigint, diit1: bigint, latestUpdate: bigint): bigint {
182 | const dt = BigInt(unixTime()) - latestUpdate;
183 | return mulScale(diit1, ONE_16_DP + (dirt1 * dt) / SECONDS_IN_YEAR, ONE_16_DP);
184 | }
185 |
186 | /**
187 | * Calculates the fAsset received from a deposit
188 | * @param depositAmount (0dp)
189 | * @param diit (14dp)
190 | * @return depositReturn (0dp)
191 | */
192 | function calcDepositReturn(depositAmount: bigint, diit: bigint): bigint {
193 | return divScale(depositAmount, diit, ONE_14_DP);
194 | }
195 |
196 | /**
197 | * Calculates the asset received from a withdraw
198 | * @param withdrawAmount (0dp)
199 | * @param diit (14dp)
200 | * @return withdrawReturn (0dp)
201 | */
202 | function calcWithdrawReturn(withdrawAmount: bigint, diit: bigint): bigint {
203 | return mulScale(withdrawAmount, diit, ONE_14_DP);
204 | }
205 |
206 | /**
207 | * Calculates the collateral asset loan value
208 | * @param amount (0dp)
209 | * @param price (14dp)
210 | * @param factor (4dp)
211 | * @return loanValue (4dp)
212 | */
213 | function calcCollateralAssetLoanValue(amount: bigint, price: bigint, factor: bigint): bigint {
214 | return mulScale(mulScale(amount, price, ONE_10_DP), factor, ONE_4_DP);
215 | }
216 |
217 | /**
218 | * Calculates the borrow asset loan value
219 | * @param amount (0dp)
220 | * @param price (14dp)
221 | * @param factor (4dp)
222 | * @return loanValue (4dp)
223 | */
224 | function calcBorrowAssetLoanValue(amount: bigint, price: bigint, factor: bigint): bigint {
225 | return mulScaleRoundUp(mulScaleRoundUp(amount, price, ONE_10_DP), factor, ONE_4_DP);
226 | }
227 |
228 | /**
229 | * Calculates the loan's LTV ratio
230 | * @param totalBorrowBalanceValue (4dp)
231 | * @param totalCollateralBalanceValue (4dp)
232 | * @return LTVRatio (4dp)
233 | */
234 | function calcLTVRatio(totalBorrowBalanceValue: bigint, totalCollateralBalanceValue: bigint): bigint {
235 | if (totalCollateralBalanceValue === BigInt(0)) return BigInt(0);
236 | return divScale(totalBorrowBalanceValue, totalCollateralBalanceValue, ONE_4_DP);
237 | }
238 |
239 | /**
240 | * Calculates the loan's borrow utilisation ratio
241 | * @param totalEffectiveBorrowBalanceValue (4dp)
242 | * @param totalEffectiveCollateralBalanceValue (4dp)
243 | * @return borrowUtilisationRatio (4dp)
244 | */
245 | function calcBorrowUtilisationRatio(
246 | totalEffectiveBorrowBalanceValue: bigint,
247 | totalEffectiveCollateralBalanceValue: bigint,
248 | ): bigint {
249 | if (totalEffectiveCollateralBalanceValue === BigInt(0)) return BigInt(0);
250 | return divScale(totalEffectiveBorrowBalanceValue, totalEffectiveCollateralBalanceValue, ONE_4_DP);
251 | }
252 |
253 | /**
254 | * Calculates the loan's liquidation margin
255 | * @param totalEffectiveBorrowBalanceValue (4dp)
256 | * @param totalEffectiveCollateralBalanceValue (4dp)
257 | * @return liquidationMargin (4dp)
258 | */
259 | function calcLiquidationMargin(
260 | totalEffectiveBorrowBalanceValue: bigint,
261 | totalEffectiveCollateralBalanceValue: bigint,
262 | ): bigint {
263 | if (totalEffectiveCollateralBalanceValue === BigInt(0)) return BigInt(0);
264 | return divScale(
265 | totalEffectiveCollateralBalanceValue - totalEffectiveBorrowBalanceValue,
266 | totalEffectiveCollateralBalanceValue,
267 | ONE_4_DP,
268 | );
269 | }
270 |
271 | /**
272 | * Calculates the borrow balance of the loan at time t
273 | * @param bbtn1 (0dp)
274 | * @param biit (14dp)
275 | * @param biitn1 (14dp)
276 | * @return borrowBalance (0dp)
277 | */
278 | function calcBorrowBalance(bbtn1: bigint, biit: bigint, biitn1: bigint): bigint {
279 | return mulScaleRoundUp(bbtn1, divScaleRoundUp(biit, biitn1, ONE_14_DP), ONE_14_DP);
280 | }
281 |
282 | /**
283 | * Calculates the stable borrow interest rate of the loan after a borrow increase
284 | * @param bbt (0dp)
285 | * @param amount (0dp)
286 | * @param sbirtn1 (16dp)
287 | * @param sbirt1 (16dp)
288 | * @return stableInterestRate (16dp)
289 | */
290 | function calcLoanStableInterestRate(bbt: bigint, amount: bigint, sbirtn1: bigint, sbirt1: bigint): bigint {
291 | return (bbt * sbirtn1 + amount * sbirt1) / (bbt + amount);
292 | }
293 |
294 | /**
295 | * Calculates the deposit interest rate condition required to rebalance up stable borrow.
296 | * Note that there is also a second condition on the pool utilisation ratio.
297 | * @param rudir (16dp)
298 | * @param vr0 (16dp)
299 | * @param vr1 (16dp)
300 | * @param vr2 (16dp)
301 | * @return rebalanceUpThreshold (16dp)
302 | */
303 | function calcRebalanceUpThreshold(rudir: bigint, vr0: bigint, vr1: bigint, vr2: bigint): bigint {
304 | return mulScale(rudir, vr0 + vr1 + vr2, ONE_16_DP);
305 | }
306 |
307 | /**
308 | * Calculates the stable interest rate condition required to rebalance down stable borrow
309 | * @param rdd (16dp)
310 | * @param sbirt1 (16dp)
311 | * @return rebalanceDownThreshold (16dp)
312 | */
313 | function calcRebalanceDownThreshold(rdd: bigint, sbirt1: bigint): bigint {
314 | return mulScale(ONE_16_DP + rdd, sbirt1, ONE_16_DP);
315 | }
316 |
317 | /**
318 | * Calculates the flash loan repayment amount for a given borrow amount and fee
319 | * @param borrowAmount (0dp)
320 | * @param fee (16dp)
321 | * @return repaymentAmount (0dp)
322 | */
323 | function calcFlashLoanRepayment(borrowAmount: bigint, fee: bigint): bigint {
324 | return borrowAmount + mulScaleRoundUp(borrowAmount, fee, ONE_16_DP);
325 | }
326 |
327 | /**
328 | * Calculates the LP price
329 | * @param r0 pool supply of asset 0
330 | * @param r1 pool supply of asset 1
331 | * @param p0 price of asset 0
332 | * @param p1 price of asset 1
333 | * @param lts circulating supply of liquidity token
334 | * @return bigint LP price
335 | */
336 | function calcLPPrice(r0: bigint, r1: bigint, p0: bigint, p1: bigint, lts: bigint): bigint {
337 | return BigInt(2) * (sqrt(r0 * p0 * r1 * p1) / lts);
338 | }
339 |
340 | export {
341 | calcAssetDollarValue,
342 | calcTotalDebt,
343 | calcAvailableLiquidity,
344 | calcStableBorrowRatio,
345 | calcMaxSingleStableBorrow,
346 | calcUtilisationRatio,
347 | calcStableDebtToTotalDebtRatio,
348 | calcVariableBorrowInterestRate,
349 | calcStableBorrowInterestRate,
350 | calcOverallBorrowInterestRate,
351 | calcDepositInterestRate,
352 | calcBorrowInterestIndex,
353 | calcDepositInterestIndex,
354 | calcDepositReturn,
355 | calcWithdrawReturn,
356 | calcCollateralAssetLoanValue,
357 | calcBorrowAssetLoanValue,
358 | calcLTVRatio,
359 | calcBorrowUtilisationRatio,
360 | calcLiquidationMargin,
361 | calcBorrowBalance,
362 | calcLoanStableInterestRate,
363 | calcRebalanceUpThreshold,
364 | calcRebalanceDownThreshold,
365 | calcFlashLoanRepayment,
366 | calcLPPrice,
367 | };
368 |
--------------------------------------------------------------------------------
/src/lend/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./abi-contracts";
2 | export * from "./constants/mainnet-constants";
3 | export * from "./constants/testnet-constants";
4 | export * from "./amm";
5 | export * from "./deposit";
6 | export * from "./deposit-staking";
7 | export * from "./formulae";
8 | export * from "./loan";
9 | export * from "./opup";
10 | export * from "./oracle";
11 | export * from "./types";
12 |
--------------------------------------------------------------------------------
/src/lend/opup.ts:
--------------------------------------------------------------------------------
1 | import { encodeUint64, makeApplicationNoOpTxn } from "algosdk";
2 |
3 | import type { OpUp } from "./types";
4 | import type { SuggestedParams, Transaction } from "algosdk";
5 |
6 | /**
7 | *
8 | * Given a transaction or transaction group, prefixes it with appl call to increase opcode budget and returns the new
9 | * transaction group.
10 | * A lot of the lending operations require additional opcode cost so use this to increase the budget.
11 | *
12 | * @param opup - opup applications
13 | * @param userAddr - account address for the user
14 | * @param transactions - transaction(s) to prefix opup to
15 | * @param numInnerTransactions - number of inner transactions to issue (remaining opcode is 691 + 689 * num.inner.txns)
16 | * @param params - suggested params for the transactions with the fees overwritten
17 | * @returns Transaction[] transaction group with opup prefixed
18 | */
19 | function prefixWithOpUp(
20 | opup: OpUp,
21 | userAddr: string,
22 | transactions: Transaction | Transaction[],
23 | numInnerTransactions: number,
24 | params: SuggestedParams,
25 | ): Transaction[] {
26 | if (!Number.isInteger(numInnerTransactions) || numInnerTransactions > 256) {
27 | throw Error("Invalid number of inner transactions");
28 | }
29 |
30 | const { callerAppId, baseAppId } = opup;
31 | const fee = (numInnerTransactions + 1) * 1000;
32 | const prefix = makeApplicationNoOpTxn(
33 | userAddr,
34 | { ...params, flatFee: true, fee },
35 | callerAppId,
36 | [encodeUint64(numInnerTransactions)],
37 | undefined,
38 | [baseAppId],
39 | );
40 | const txns = Array.isArray(transactions) ? transactions : [transactions];
41 | return [prefix, ...txns].map((txn) => {
42 | txn.group = undefined;
43 | return txn;
44 | });
45 | }
46 |
47 | export { prefixWithOpUp };
48 |
--------------------------------------------------------------------------------
/src/lend/oracle.ts:
--------------------------------------------------------------------------------
1 | import { AtomicTransactionComposer, decodeAddress, decodeUint64, encodeAddress, getMethodByName } from "algosdk";
2 |
3 | import { minimum } from "../math-lib";
4 | import {
5 | fromIntToBytes8Hex,
6 | getAccountApplicationLocalState,
7 | getApplicationGlobalState,
8 | getParsedValueFromState,
9 | signer,
10 | } from "../utils";
11 |
12 | import { lpTokenOracleABIContract, oracleAdapterABIContract } from "./abi-contracts";
13 | import { calcLPPrice } from "./formulae";
14 | import { LPTokenProvider } from "./types";
15 |
16 | import type { LPToken, Oracle, OraclePrice, OraclePrices, PactLPToken, TinymanLPToken } from "./types";
17 | import type { Algodv2, Indexer, SuggestedParams, Transaction } from "algosdk";
18 | import type { TealKeyValue } from "algosdk/dist/types/client/v2/algod/models/types";
19 |
20 | function parseOracleValue(base64Value: string) {
21 | const value = Buffer.from(base64Value, "base64").toString("hex");
22 |
23 | // [price (uint64), latest_update (uint64), ...]
24 | const price = BigInt("0x" + value.slice(0, 16));
25 | const timestamp = BigInt("0x" + value.slice(16, 32));
26 | return { price, timestamp };
27 | }
28 |
29 | function parseLPTokenOracleValue(lpAssetId: number, base64Value: string): LPToken {
30 | const value = Buffer.from(base64Value, "base64").toString("hex");
31 |
32 | // [type (uint8), a0_id (uint64), a1_id, (uint64), _ (uint64), _ (uint64), _ (uint64), pool (address/uint64)]
33 | const provider = Number("0x" + value.slice(0, 2));
34 | const asset0Id = Number(`0x${value.slice(2, 34)}`);
35 | const asset1Id = Number(`0x${value.slice(34, 50)}`);
36 |
37 | // check if LP token is Tinyman or Pact
38 | if (provider == LPTokenProvider.TINYMAN) {
39 | const poolAddress = encodeAddress(Buffer.from(value.slice(82, 146), "hex"));
40 | return { provider, lpAssetId, asset0Id, asset1Id, lpPoolAddress: poolAddress };
41 | } else if (provider == LPTokenProvider.PACT) {
42 | const poolAppId = Number("0x" + value.slice(82, 98));
43 | return { provider, lpAssetId, asset0Id, asset1Id, lpPoolAppId: poolAppId };
44 | } else {
45 | throw Error("Unknown LP Token type");
46 | }
47 | }
48 |
49 | async function getTinymanLPPrice(
50 | client: Algodv2 | Indexer,
51 | validatorAppId: number,
52 | poolAddress: string,
53 | p0: bigint,
54 | p1: bigint,
55 | ): Promise {
56 | const { localState: state } = await getAccountApplicationLocalState(client, validatorAppId, poolAddress);
57 | if (state === undefined)
58 | throw new Error(`Unable to find Tinyman Pool: ${poolAddress} for validator app ${validatorAppId}.`);
59 |
60 | const r0 = BigInt(getParsedValueFromState(state, "s1") || 0);
61 | const r1 = BigInt(getParsedValueFromState(state, "s2") || 0);
62 | const lts = BigInt(getParsedValueFromState(state, "ilt") || 0);
63 |
64 | return calcLPPrice(r0, r1, p0, p1, lts);
65 | }
66 |
67 | async function getPactLPPrice(client: Algodv2 | Indexer, poolAppId: number, p0: bigint, p1: bigint): Promise {
68 | const { globalState: state } = await getApplicationGlobalState(client, poolAppId);
69 | if (state === undefined) throw new Error(`Unable to find Pact Pool: ${poolAppId}.`);
70 |
71 | const r0 = BigInt(getParsedValueFromState(state, "A") || 0);
72 | const r1 = BigInt(getParsedValueFromState(state, "B") || 0);
73 | const lts = BigInt(getParsedValueFromState(state, "L") || 0);
74 |
75 | return calcLPPrice(r0, r1, p0, p1, lts);
76 | }
77 |
78 | /**
79 | *
80 | * Returns oracle prices for given oracle and provided assets.
81 | *
82 | * @param client - Algorand client to query
83 | * @param oracle - oracle to query
84 | * @param assetIds - assets ids to get prices for, if undefined then returns all prices
85 | * @returns OraclePrices oracle prices
86 | */
87 | async function getOraclePrices(client: Algodv2 | Indexer, oracle: Oracle, assetIds?: number[]): Promise {
88 | const { oracle0AppId, lpTokenOracle } = oracle;
89 |
90 | const lookupApps = [getApplicationGlobalState(client, oracle0AppId)];
91 | if (lpTokenOracle) lookupApps.push(getApplicationGlobalState(client, lpTokenOracle.appId));
92 | const [oracleRes, lpTokenOracleRes] = await Promise.all(lookupApps);
93 |
94 | const { currentRound, globalState: oracleState } = oracleRes;
95 | const lpTokenOracleState = lpTokenOracleRes?.globalState;
96 |
97 | if (oracleState === undefined) throw Error("Could not find Oracle");
98 | if (lpTokenOracle && lpTokenOracleState === undefined) throw Error("Could not find LP Token Oracle");
99 |
100 | const prices: Record = {};
101 |
102 | // get the assets for which we need to retrieve their prices
103 | const allAssetIds: number[] = oracleState
104 | .concat(lpTokenOracleState || [])
105 | .filter(({ key }: TealKeyValue) => {
106 | // remove non asset ids global state
107 | key = Buffer.from(key, "base64").toString("utf8");
108 | return key !== "updater_addr" && key !== "admin" && key !== "tinyman_validator_app_id" && key !== "td";
109 | })
110 | .map(({ key }: TealKeyValue) => {
111 | // convert key to asset id
112 | return decodeUint64(Buffer.from(key, "base64"), "safe");
113 | });
114 | const assets = assetIds ? assetIds : allAssetIds;
115 |
116 | // retrieve asset prices
117 | const retrievePrices = assets.map(async (assetId) => {
118 | let assetPrice: OraclePrice;
119 | const lpTokenBase64Value = lpTokenOracle
120 | ? getParsedValueFromState(lpTokenOracleState!, fromIntToBytes8Hex(assetId), "hex")
121 | : undefined;
122 |
123 | // lpTokenBase64Value defined iff asset is lp token in given lpTokenOracle
124 | if (lpTokenBase64Value !== undefined) {
125 | const lpTokenOracleValue = parseLPTokenOracleValue(assetId, lpTokenBase64Value as string);
126 | const { price: p0, timestamp: t0 } = parseOracleValue(
127 | String(getParsedValueFromState(oracleState, fromIntToBytes8Hex(lpTokenOracleValue.asset0Id), "hex")),
128 | );
129 | const { price: p1, timestamp: t1 } = parseOracleValue(
130 | String(getParsedValueFromState(oracleState, fromIntToBytes8Hex(lpTokenOracleValue.asset1Id), "hex")),
131 | );
132 |
133 | let price: bigint;
134 | switch (lpTokenOracleValue.provider) {
135 | case LPTokenProvider.TINYMAN:
136 | price = await getTinymanLPPrice(
137 | client,
138 | lpTokenOracle!.tinymanValidatorAppId,
139 | lpTokenOracleValue.lpPoolAddress,
140 | p0,
141 | p1,
142 | );
143 | break;
144 | case LPTokenProvider.PACT:
145 | price = await getPactLPPrice(client, lpTokenOracleValue.lpPoolAppId, p0, p1);
146 | break;
147 | default:
148 | throw Error("Unknown LP Token provider");
149 | }
150 | assetPrice = { price, timestamp: minimum(t0, t1) };
151 | } else {
152 | assetPrice = parseOracleValue(String(getParsedValueFromState(oracleState, fromIntToBytes8Hex(assetId), "hex")));
153 | }
154 |
155 | prices[assetId] = assetPrice;
156 | });
157 |
158 | await Promise.all(retrievePrices);
159 | return { currentRound, prices };
160 | }
161 |
162 | /**
163 | *
164 | * Returns a group transaction to refresh the given assets.
165 | *
166 | * @param oracle - oracle applications to use
167 | * @param userAddr - account address for the user
168 | * @param lpAssets - list of lp assets
169 | * @param baseAssetIds - list of base asset ids (non-lp assets)
170 | * @param params - suggested params for the transactions with the fees overwritten
171 | * @returns Transaction[] refresh prices group transaction
172 | */
173 | function prepareRefreshPricesInOracleAdapter(
174 | oracle: Oracle,
175 | userAddr: string,
176 | lpAssets: LPToken[],
177 | baseAssetIds: number[],
178 | params: SuggestedParams,
179 | ): Transaction[] {
180 | const { oracleAdapterAppId, lpTokenOracle, oracle0AppId } = oracle;
181 |
182 | if (lpTokenOracle === undefined && lpAssets.length > 0)
183 | throw Error("Cannot refresh LP assets without LP Token Oracle");
184 |
185 | const atc = new AtomicTransactionComposer();
186 |
187 | // divide lp tokens into Tinyman and Pact
188 | const tinymanLPAssets: TinymanLPToken[] = lpAssets.filter(
189 | ({ provider }) => provider === LPTokenProvider.TINYMAN,
190 | ) as TinymanLPToken[];
191 | const pactLPAssets: PactLPToken[] = lpAssets.filter(
192 | ({ provider }) => provider === LPTokenProvider.PACT,
193 | ) as PactLPToken[];
194 |
195 | // update lp tokens
196 | const foreignAccounts: string[][] = [];
197 | const foreignApps: number[][] = [];
198 |
199 | const MAX_TINYMAN_UPDATE = 4;
200 | const MAX_PACT_UPDATE = 8;
201 | const MAX_COMBINATION_UPDATE = 7;
202 | let tinymanIndex = 0;
203 | let pactIndex = 0;
204 |
205 | while (tinymanIndex < tinymanLPAssets.length && pactIndex < pactLPAssets.length) {
206 | // retrieve which lp assets to update
207 | const tinymanLPUpdates = tinymanLPAssets.slice(tinymanIndex, tinymanIndex + MAX_TINYMAN_UPDATE);
208 | const maxPactUpdates =
209 | tinymanLPUpdates.length === 0 ? MAX_PACT_UPDATE : MAX_COMBINATION_UPDATE - tinymanLPUpdates.length;
210 | const pactLPUpdates = pactLPAssets.slice(pactIndex, pactIndex + maxPactUpdates);
211 |
212 | // prepare update lp tokens arguments
213 | const lpAssetIds = [
214 | ...tinymanLPUpdates.map(({ lpAssetId }) => lpAssetId),
215 | ...pactLPUpdates.map(({ lpAssetId }) => lpAssetId),
216 | ];
217 |
218 | // foreign arrays
219 | foreignAccounts.push(tinymanLPUpdates.map(({ lpPoolAddress }) => lpPoolAddress));
220 | const apps: number[] = [];
221 | if (tinymanLPUpdates.length > 0) apps.push(lpTokenOracle!.tinymanValidatorAppId);
222 | for (const { lpPoolAppId } of pactLPUpdates) apps.push(lpPoolAppId);
223 | foreignApps.push(apps);
224 |
225 | // update lp
226 | atc.addMethodCall({
227 | sender: userAddr,
228 | signer,
229 | appID: lpTokenOracle!.appId,
230 | method: getMethodByName(lpTokenOracleABIContract.methods, "update_lp_tokens"),
231 | methodArgs: [lpAssetIds],
232 | suggestedParams: { ...params, flatFee: true, fee: 1000 },
233 | });
234 |
235 | // increase indexes
236 | tinymanIndex += tinymanLPUpdates.length;
237 | pactIndex += pactLPUpdates.length;
238 | }
239 |
240 | // prepare refresh prices arguments
241 | const oracle1AppId = oracle.oracle1AppId || 0;
242 | const lpTokenOracleAppId = lpTokenOracle?.appId || 0;
243 | const lpAssetIds = lpAssets.map(({ lpAssetId }) => lpAssetId);
244 |
245 | // refresh prices
246 | atc.addMethodCall({
247 | sender: userAddr,
248 | signer,
249 | appID: oracleAdapterAppId,
250 | method: getMethodByName(oracleAdapterABIContract.methods, "refresh_prices"),
251 | methodArgs: [lpAssetIds, baseAssetIds, oracle0AppId, oracle1AppId, lpTokenOracleAppId],
252 | suggestedParams: { ...params, flatFee: true, fee: 1000 },
253 | });
254 |
255 | // build
256 | return atc.buildGroup().map(({ txn }, index) => {
257 | if (index < foreignAccounts.length && index < foreignApps.length) {
258 | txn.appAccounts = foreignAccounts[index].map((address) => decodeAddress(address));
259 | txn.appForeignApps = foreignApps[index];
260 | }
261 | txn.group = undefined;
262 | return txn;
263 | });
264 | }
265 |
266 | export { getOraclePrices, prepareRefreshPricesInOracleAdapter };
267 |
--------------------------------------------------------------------------------
/src/lend/types.ts:
--------------------------------------------------------------------------------
1 | enum LPTokenProvider {
2 | TINYMAN = 0,
3 | PACT = 1,
4 | }
5 |
6 | interface BaseLPToken {
7 | provider: LPTokenProvider;
8 | lpAssetId: number;
9 | asset0Id: number;
10 | asset1Id: number;
11 | }
12 |
13 | interface TinymanLPToken extends BaseLPToken {
14 | provider: LPTokenProvider.TINYMAN;
15 | lpPoolAddress: string;
16 | }
17 |
18 | interface PactLPToken extends BaseLPToken {
19 | provider: LPTokenProvider.PACT;
20 | lpPoolAppId: number;
21 | }
22 |
23 | type LPToken = TinymanLPToken | PactLPToken;
24 |
25 | interface BaseLendingPool extends BaseLPToken {
26 | pool0AppId: number;
27 | pool1AppId: number;
28 | feeScale: bigint;
29 | }
30 |
31 | interface PactLendingPool extends BaseLendingPool {
32 | provider: LPTokenProvider.PACT;
33 | lpPoolAppId: number;
34 | }
35 |
36 | interface TinymanLendingPool extends BaseLendingPool {
37 | provider: LPTokenProvider.TINYMAN;
38 | lpPoolAppAddress: string;
39 | }
40 |
41 | type LendingPool = PactLendingPool | TinymanLendingPool;
42 |
43 | interface BaseLendingPoolInfo {
44 | currentRound?: number;
45 | fAsset0Supply: bigint;
46 | fAsset1Supply: bigint;
47 | liquidityTokenCirculatingSupply: bigint;
48 | fee: bigint;
49 | swapFeeInterestRate: bigint; // 16 d.p.
50 | swapFeeInterestYield: bigint; // 16 d.p.
51 | asset0DepositInterestRate: bigint; // 16 d.p.
52 | asset0DepositInterestYield: bigint; // approximation 16 d.p.
53 | asset1DepositInterestRate: bigint; // 16 d.p.
54 | asset1DepositInterestYield: bigint; // approximation 16 d.p.
55 | additionalInterestRate?: bigint; // 16 d.p.
56 | additionalInterestYield?: bigint; // approximation 16 d.p.
57 | tvlUsd: number;
58 | }
59 |
60 | type PactLendingPoolInfo = BaseLendingPoolInfo;
61 | interface TinymanLendingPoolInfo extends BaseLendingPoolInfo {
62 | farmInterestYield: bigint; // 16 d.p.
63 | }
64 |
65 | interface PoolManagerInfo {
66 | currentRound?: number;
67 | pools: Partial<
68 | Record<
69 | number,
70 | {
71 | // poolAppId -> ...
72 | variableBorrowInterestRate: bigint; // 16 d.p.
73 | variableBorrowInterestYield: bigint; // approximation 16 d.p.
74 | variableBorrowInterestIndex: bigint; // 14 d.p.
75 | depositInterestRate: bigint; // 16 d.p.
76 | depositInterestYield: bigint; // approximation 16 d.p.
77 | depositInterestIndex: bigint; // 14 d.p.
78 | metadata: {
79 | oldVariableBorrowInterestIndex: bigint; // 14 d.p.
80 | oldDepositInterestIndex: bigint; // 14 d.p.
81 | oldTimestamp: bigint;
82 | };
83 | }
84 | >
85 | >;
86 | }
87 |
88 | interface BasePool {
89 | appId: number;
90 | assetId: number;
91 | fAssetId: number;
92 | frAssetId: number;
93 | assetDecimals: number;
94 | poolManagerIndex: number;
95 | loans: Partial>; // loanAppId -> loanIndex
96 | }
97 |
98 | interface LPTokenPool extends BasePool {
99 | lpToken: LPToken;
100 | }
101 |
102 | type Pool = BasePool | LPTokenPool;
103 |
104 | interface PoolInfo {
105 | currentRound?: number;
106 | variableBorrow: {
107 | vr0: bigint; // 16 d.p.
108 | vr1: bigint; // 16 d.p.
109 | vr2: bigint; // 16 d.p.
110 | totalVariableBorrowAmount: bigint;
111 | variableBorrowInterestRate: bigint; // 16 d.p.
112 | variableBorrowInterestYield: bigint; // approximation 16 d.p.
113 | variableBorrowInterestIndex: bigint; // 14 d.p.
114 | };
115 | stableBorrow: {
116 | sr0: bigint; // 16 d.p.
117 | sr1: bigint; // 16 d.p.
118 | sr2: bigint; // 16 d.p.
119 | sr3: bigint; // 16 d.p.
120 | optimalStableToTotalDebtRatio: bigint; // 16 d.p.
121 | rebalanceUpUtilisationRatio: bigint; // 16 d.p.
122 | rebalanceUpDepositInterestRate: bigint; // 16 d.p.
123 | rebalanceDownDelta: bigint; // 16 d.p.
124 | totalStableBorrowAmount: bigint;
125 | stableBorrowInterestRate: bigint; // 16 d.p.
126 | stableBorrowInterestYield: bigint; // approximation 16 d.p.
127 | overallStableBorrowInterestAmount: bigint; // 16 d.p.
128 | };
129 | interest: {
130 | retentionRate: bigint; // 16 d.p.
131 | flashLoanFee: bigint; // 16 d.p.
132 | optimalUtilisationRatio: bigint; // 16 d.p.
133 | totalDeposits: bigint;
134 | depositInterestRate: bigint; // 16 d.p.
135 | depositInterestYield: bigint; // approximation 16 d.p.
136 | depositInterestIndex: bigint; // 14 d.p.
137 | latestUpdate: bigint;
138 | };
139 | caps: {
140 | borrowCap: bigint; // $ value
141 | stableBorrowPercentageCap: bigint; // 16 d.p.
142 | };
143 | config: {
144 | depreciated: boolean;
145 | rewardsPaused: boolean;
146 | stableBorrowSupported: boolean;
147 | flashLoanSupported: boolean;
148 | };
149 | }
150 |
151 | type UserDepositInfo = {
152 | currentRound?: number;
153 | escrowAddress: string;
154 | holdings: {
155 | fAssetId: number;
156 | fAssetBalance: bigint;
157 | }[];
158 | };
159 |
160 | type UserDepositFullInfo = {
161 | currentRound?: number;
162 | escrowAddress: string;
163 | holdings: {
164 | fAssetId: number;
165 | fAssetBalance: bigint;
166 | poolAppId: number;
167 | assetId: number;
168 | assetPrice: bigint; // 14 d.p.
169 | assetBalance: bigint;
170 | balanceValue: bigint; // in $, 4 d.p.
171 | interestRate: bigint; // 16 d.p.
172 | interestYield: bigint; // approximation 16 d.p.
173 | }[];
174 | };
175 |
176 | interface DepositStakingInfo {
177 | currentRound?: number;
178 | stakingPrograms: {
179 | poolAppId: number;
180 | totalStaked: bigint;
181 | minTotalStaked: bigint;
182 | stakeIndex: number;
183 | rewards: {
184 | rewardAssetId: number;
185 | endTimestamp: bigint;
186 | rewardRate: bigint; // 10 d.p.
187 | rewardPerToken: bigint; // 10 d.p.
188 | }[];
189 | }[];
190 | }
191 |
192 | interface DepositStakingProgramInfo {
193 | poolAppId: number;
194 | stakeIndex: number;
195 | fAssetId: number;
196 | fAssetTotalStakedAmount: bigint;
197 | assetId: number;
198 | assetPrice: bigint; // 14 d.p.
199 | assetTotalStakedAmount: bigint;
200 | totalStakedAmountValue: bigint; // in $, 4 d.p.
201 | depositInterestRate: bigint; // 16 d.p.
202 | depositInterestYield: bigint; // approximation 16 d.p.
203 | rewards: {
204 | rewardAssetId: number;
205 | endTimestamp: bigint;
206 | rewardRate: bigint; // 10 d.p.
207 | rewardPerToken: bigint; // 10 d.p.
208 | rewardAssetPrice: bigint; // 14 d.p.
209 | rewardInterestRate: bigint; // 0 if past reward end timestamp, 16 d.p.
210 | }[];
211 | }
212 |
213 | interface UserDepositStakingLocalState {
214 | currentRound?: number;
215 | userAddress: string;
216 | escrowAddress: string;
217 | optedIntoAssets: Set;
218 | stakedAmounts: bigint[];
219 | rewardPerTokens: bigint[]; // 10 d.p.
220 | unclaimedRewards: bigint[];
221 | }
222 |
223 | interface UserDepositStakingProgramInfo {
224 | poolAppId: number;
225 | fAssetId: number;
226 | fAssetStakedAmount: bigint;
227 | assetId: number;
228 | assetPrice: bigint; // 14 d.p.
229 | assetStakedAmount: bigint;
230 | stakedAmountValue: bigint; // in $, 4 d.p.
231 | depositInterestRate: bigint; // 16 d.p.
232 | depositInterestYield: bigint; // approximation 16 d.p.
233 | rewards: {
234 | rewardAssetId: number;
235 | endTimestamp: bigint;
236 | rewardAssetPrice: bigint; // 14 d.p.
237 | rewardInterestRate: bigint; // 0 if past reward end timestamp, 16 d.p.
238 | unclaimedReward: bigint;
239 | unclaimedRewardValue: bigint; // in $, 4 d.p.
240 | }[];
241 | }
242 |
243 | interface UserDepositStakingInfo {
244 | currentRound?: number;
245 | userAddress: string;
246 | escrowAddress: string;
247 | optedIntoAssets: Set;
248 | stakingPrograms: UserDepositStakingProgramInfo[];
249 | }
250 |
251 | interface PoolLoanInfo {
252 | poolAppId: number;
253 | assetId: number;
254 | collateralCap: bigint; // $ value
255 | collateralUsed: bigint;
256 | collateralFactor: bigint; // 4 d.p.
257 | borrowFactor: bigint; // 4 d.p.
258 | liquidationMax: bigint; // 4 d.p.
259 | liquidationBonus: bigint; // 4 d.p.
260 | liquidationFee: bigint; // 4 d.p.
261 | }
262 |
263 | enum LoanType {
264 | "GENERAL" = "GENERAL",
265 | "STABLECOIN_EFFICIENCY" = "STABLECOIN_EFFICIENCY",
266 | "ALGO_EFFICIENCY" = "ALGO_EFFICIENCY",
267 | "ULTRASWAP_UP" = "ULTRASWAP_UP",
268 | "ULTRASWAP_DOWN" = "ULTRASWAP_DOWN",
269 | }
270 |
271 | interface LoanInfo {
272 | currentRound?: number;
273 | canSwapCollateral: boolean;
274 | pools: Partial>; // poolAppId -> PoolLoanInfo
275 | }
276 |
277 | interface LoanLocalState {
278 | currentRound?: number;
279 | userAddress: string;
280 | escrowAddress: string;
281 | collaterals: {
282 | poolAppId: number;
283 | fAssetBalance: bigint;
284 | }[];
285 | borrows: {
286 | poolAppId: number;
287 | borrowedAmount: bigint;
288 | borrowBalance: bigint;
289 | latestBorrowInterestIndex: bigint; // 14 d.p.
290 | stableBorrowInterestRate: bigint; // 16 d.p.
291 | latestStableChange: bigint;
292 | }[];
293 | }
294 |
295 | interface UserLoanInfoCollateral {
296 | poolAppId: number;
297 | assetId: number;
298 | assetPrice: bigint; // 14 d.p.
299 | collateralFactor: bigint; // 4 d.p.
300 | depositInterestIndex: bigint; // 14 d.p.
301 | fAssetBalance: bigint;
302 | assetBalance: bigint;
303 | balanceValue: bigint; // in $, 4 d.p.
304 | effectiveBalanceValue: bigint; // in $, 4 d.p.
305 | interestRate: bigint; // 16 d.p.
306 | interestYield: bigint; // approximation 16 d.p.
307 | }
308 |
309 | interface UserLoanInfoBorrow {
310 | poolAppId: number;
311 | assetId: number;
312 | assetPrice: bigint; // 14 d.p.
313 | isStable: boolean;
314 | borrowFactor: bigint; // 4 d.p.
315 | borrowedAmount: bigint;
316 | borrowedAmountValue: bigint; // in $, 4 d.p.
317 | borrowBalance: bigint;
318 | borrowBalanceValue: bigint; // in $, 4 d.p.
319 | effectiveBorrowBalanceValue: bigint; // in $, 4 d.p.
320 | accruedInterest: bigint;
321 | accruedInterestValue: bigint; // in $, 4 d.p.
322 | interestRate: bigint; // 16 d.p.
323 | interestYield: bigint; // approximation 16 d.p.
324 | }
325 |
326 | interface UserLoanInfo {
327 | currentRound?: number;
328 | userAddress: string;
329 | escrowAddress: string;
330 | collaterals: UserLoanInfoCollateral[];
331 | borrows: UserLoanInfoBorrow[];
332 | netRate: bigint; // 16 d.p. - negative indicates losing more on borrows than gaining on collaterals
333 | netYield: bigint; // 16 d.p. - negative indicates losing more on borrows than gaining on collaterals
334 | totalCollateralBalanceValue: bigint; // in $, 4 d.p.
335 | totalBorrowedAmountValue: bigint; // in $, 4 d.p.
336 | totalBorrowBalanceValue: bigint; // in $, 4 d.p.
337 | totalEffectiveCollateralBalanceValue: bigint; // in $, 4 d.p. - used to determine if liquidatable
338 | totalEffectiveBorrowBalanceValue: bigint; // in $, 4 d.p. - used to determine if liquidatable
339 | loanToValueRatio: bigint; // 4 d.p.
340 | borrowUtilisationRatio: bigint; // 4 d.p.
341 | liquidationMargin: bigint; // 4 d.p.
342 | }
343 |
344 | interface AssetAdditionalInterest {
345 | rateBps: bigint; // 4 d.p.
346 | yieldBps: bigint; // 4 d.p.
347 | }
348 |
349 | type AssetsAdditionalInterest = Partial>; // assetId -> interest
350 |
351 | interface Oracle {
352 | oracle0AppId: number;
353 | oracle1AppId?: number;
354 | lpTokenOracle?: {
355 | appId: number;
356 | tinymanValidatorAppId: number;
357 | };
358 | oracleAdapterAppId: number;
359 | decimals: number;
360 | }
361 |
362 | interface OraclePrice {
363 | price: bigint; // price in USD for amount 1 of asset in lowest denomination
364 | timestamp: bigint;
365 | }
366 |
367 | interface OraclePrices {
368 | currentRound?: number;
369 | prices: Partial>; // assetId -> OraclePrice
370 | }
371 |
372 | interface OpUp {
373 | callerAppId: number;
374 | baseAppId: number;
375 | }
376 |
377 | type ReserveAddress = string;
378 |
379 | export {
380 | LPTokenProvider,
381 | TinymanLPToken,
382 | PactLPToken,
383 | LPToken,
384 | PactLendingPool,
385 | TinymanLendingPool,
386 | LendingPool,
387 | BaseLendingPoolInfo,
388 | PactLendingPoolInfo,
389 | TinymanLendingPoolInfo,
390 | PoolManagerInfo,
391 | BasePool,
392 | LPTokenPool,
393 | Pool,
394 | PoolInfo,
395 | UserDepositInfo,
396 | UserDepositFullInfo,
397 | DepositStakingInfo,
398 | DepositStakingProgramInfo,
399 | UserDepositStakingLocalState,
400 | UserDepositStakingProgramInfo,
401 | UserDepositStakingInfo,
402 | PoolLoanInfo,
403 | LoanType,
404 | LoanLocalState,
405 | LoanInfo,
406 | UserLoanInfoCollateral,
407 | UserLoanInfoBorrow,
408 | UserLoanInfo,
409 | AssetAdditionalInterest,
410 | AssetsAdditionalInterest,
411 | Oracle,
412 | OraclePrice,
413 | OraclePrices,
414 | OpUp,
415 | ReserveAddress,
416 | };
417 |
--------------------------------------------------------------------------------
/src/math-lib.ts:
--------------------------------------------------------------------------------
1 | const SECONDS_IN_YEAR = BigInt(365 * 24 * 60 * 60);
2 | const HOURS_IN_YEAR = BigInt(365 * 24);
3 |
4 | const ONE_2_DP = BigInt(1e2);
5 | const ONE_4_DP = BigInt(1e4);
6 | const ONE_10_DP = BigInt(1e10);
7 | const ONE_12_DP = BigInt(1e12);
8 | const ONE_14_DP = BigInt(1e14);
9 | const ONE_16_DP = BigInt(1e16);
10 |
11 | const UINT64 = BigInt(2) << BigInt(63);
12 | const UINT128 = BigInt(2) << BigInt(127);
13 |
14 | function maximum(n1: bigint, n2: bigint): bigint {
15 | return n1 > n2 ? n1 : n2;
16 | }
17 |
18 | function minimum(n1: bigint, n2: bigint): bigint {
19 | return n1 < n2 ? n1 : n2;
20 | }
21 |
22 | function mulScale(n1: bigint, n2: bigint, scale: bigint): bigint {
23 | return (n1 * n2) / scale;
24 | }
25 |
26 | function mulScaleRoundUp(n1: bigint, n2: bigint, scale: bigint): bigint {
27 | return mulScale(n1, n2, scale) + BigInt(1);
28 | }
29 |
30 | function divScale(n1: bigint, n2: bigint, scale: bigint): bigint {
31 | return (n1 * scale) / n2;
32 | }
33 |
34 | function divScaleRoundUp(n1: bigint, n2: bigint, scale: bigint): bigint {
35 | return divScale(n1, n2, scale) + BigInt(1);
36 | }
37 |
38 | function expBySquaring(x: bigint, n: bigint, scale: bigint): bigint {
39 | if (n === BigInt(0)) return scale;
40 |
41 | let y = scale;
42 | while (n > BigInt(1)) {
43 | if (n % BigInt(2)) {
44 | y = mulScale(x, y, scale);
45 | n = (n - BigInt(1)) / BigInt(2);
46 | } else {
47 | n = n / BigInt(2);
48 | }
49 | x = mulScale(x, x, scale);
50 | }
51 | return mulScale(x, y, scale);
52 | }
53 |
54 | function compound(rate: bigint, scale: bigint, period: bigint): bigint {
55 | return expBySquaring(scale + rate / period, period, scale) - scale;
56 | }
57 |
58 | function compoundEverySecond(rate: bigint, scale: bigint): bigint {
59 | return compound(rate, scale, SECONDS_IN_YEAR);
60 | }
61 |
62 | function compoundEveryHour(rate: bigint, scale: bigint): bigint {
63 | return compound(rate, scale, HOURS_IN_YEAR);
64 | }
65 |
66 | function sqrt(value: bigint): bigint {
67 | if (value < BigInt(0)) throw Error("square root of negative numbers is not supported");
68 |
69 | if (value < BigInt(2)) return value;
70 |
71 | function newtonIteration(n: bigint, x0: bigint): bigint {
72 | const x1 = (n / x0 + x0) >> BigInt(1);
73 | if (x0 === x1 || x0 === x1 - BigInt(1)) return x0;
74 | return newtonIteration(n, x1);
75 | }
76 |
77 | return newtonIteration(value, BigInt(1));
78 | }
79 |
80 | export {
81 | SECONDS_IN_YEAR,
82 | HOURS_IN_YEAR,
83 | ONE_16_DP,
84 | ONE_14_DP,
85 | ONE_12_DP,
86 | ONE_10_DP,
87 | ONE_4_DP,
88 | ONE_2_DP,
89 | UINT64,
90 | UINT128,
91 | maximum,
92 | minimum,
93 | mulScale,
94 | mulScaleRoundUp,
95 | divScale,
96 | divScaleRoundUp,
97 | expBySquaring,
98 | compoundEverySecond,
99 | compoundEveryHour,
100 | sqrt,
101 | };
102 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Algodv2,
3 | decodeAddress,
4 | getApplicationAddress,
5 | makeAssetTransferTxnWithSuggestedParamsFromObject,
6 | makePaymentTxnWithSuggestedParamsFromObject,
7 | } from "algosdk";
8 |
9 | import type { Indexer, SuggestedParams, Transaction } from "algosdk";
10 | import type { Box, TealKeyValue } from "algosdk/dist/types/client/v2/algod/models/types";
11 |
12 | const enc = new TextEncoder();
13 |
14 | /**
15 | * Transfer algo or asset. 0 assetId indicates algo transfer, else asset transfer.
16 | */
17 | function transferAlgoOrAsset(
18 | assetId: number,
19 | from: string,
20 | to: string,
21 | amount: number | bigint,
22 | params: SuggestedParams,
23 | ): Transaction {
24 | return assetId !== 0
25 | ? makeAssetTransferTxnWithSuggestedParamsFromObject({
26 | from,
27 | to,
28 | amount,
29 | suggestedParams: params,
30 | assetIndex: assetId,
31 | })
32 | : makePaymentTxnWithSuggestedParamsFromObject({ from, to, amount, suggestedParams: params });
33 | }
34 |
35 | const signer = async () => [];
36 |
37 | function unixTime(): number {
38 | return Math.floor(Date.now() / 1000);
39 | }
40 |
41 | const PAYOUTS_GO_ONLINE_FEE = BigInt(2e6);
42 |
43 | /**
44 | * Wraps a call to Algorand client (algod/indexer) and returns global state
45 | */
46 | async function getApplicationGlobalState(
47 | client: Algodv2 | Indexer,
48 | appId: number,
49 | ): Promise<{
50 | currentRound?: number;
51 | globalState?: TealKeyValue[];
52 | }> {
53 | const res = await (
54 | client instanceof Algodv2 ? client.getApplicationByID(appId) : client.lookupApplications(appId)
55 | ).do();
56 |
57 | // algod https://developer.algorand.org/docs/rest-apis/algod/#application
58 | // indexer https://developer.algorand.org/docs/rest-apis/indexer/#lookupapplicationbyid-response-200
59 | const app = client instanceof Algodv2 ? res : res["application"];
60 |
61 | return {
62 | currentRound: res["current-round"],
63 | globalState: app["params"]["global-state"],
64 | };
65 | }
66 |
67 | /**
68 | * Wraps a call to Algorand client (algod/indexer) and returns local state
69 | */
70 | async function getAccountApplicationLocalState(
71 | client: Algodv2 | Indexer,
72 | appId: number,
73 | addr: string,
74 | ): Promise<{
75 | currentRound?: number;
76 | localState?: TealKeyValue[];
77 | }> {
78 | const res = await (
79 | client instanceof Algodv2
80 | ? client.accountApplicationInformation(addr, appId)
81 | : client.lookupAccountAppLocalStates(addr).applicationID(appId)
82 | ).do();
83 |
84 | // algod https://developer.algorand.org/docs/rest-apis/algod/#accountapplicationinformation-response-200
85 | // indexer https://developer.algorand.org/docs/rest-apis/indexer/#lookupaccountapplocalstates-response-200
86 | const localState =
87 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
88 | client instanceof Algodv2 ? res["app-local-state"] : res["apps-local-states"]?.find(({ id }: any) => id === appId);
89 |
90 | return {
91 | currentRound: res["current-round"],
92 | localState: localState["key-value"],
93 | };
94 | }
95 |
96 | /**
97 | * Wraps a call to Algorand client (algod/indexer) and returns box value
98 | */
99 | async function getApplicationBox(client: Algodv2 | Indexer, appId: number, boxName: Uint8Array): Promise {
100 | return await (
101 | client instanceof Algodv2
102 | ? client.getApplicationBoxByName(appId, boxName)
103 | : client.lookupApplicationBoxByIDandName(appId, boxName)
104 | ).do();
105 | }
106 |
107 | /**
108 | * Wraps a call to Algorand client (algod/indexer) and returns account details
109 | */
110 | async function getAccountDetails(
111 | client: Algodv2 | Indexer,
112 | addr: string,
113 | ): Promise<{
114 | currentRound?: number;
115 | isOnline: boolean;
116 | holdings: Map;
117 | }> {
118 | const holdings: Map = new Map();
119 |
120 | try {
121 | const res = await (
122 | client instanceof Algodv2
123 | ? client.accountInformation(addr)
124 | : client.lookupAccountByID(addr).exclude("apps-local-state,created-apps")
125 | ).do();
126 |
127 | // algod https://developer.algorand.org/docs/rest-apis/algod/#account
128 | // indexer https://developer.algorand.org/docs/rest-apis/indexer/#lookupaccountbyid-response-200
129 | const account = client instanceof Algodv2 ? res : res["account"];
130 | const assets = account["assets"] || [];
131 |
132 | holdings.set(0, BigInt(account["amount"])); // includes min balance
133 |
134 | for (const asset of assets) {
135 | holdings.set(asset["asset-id"], BigInt(asset["amount"]));
136 | }
137 |
138 | return {
139 | currentRound: res["current-round"],
140 | isOnline: account["status"] === "Online",
141 | holdings,
142 | };
143 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
144 | } catch (e: any) {
145 | if (e.status === 404 && e.response?.text?.includes("no accounts found for address")) {
146 | holdings.set(0, BigInt(0));
147 |
148 | return {
149 | isOnline: false,
150 | holdings,
151 | };
152 | }
153 | throw e;
154 | }
155 | }
156 |
157 | /**
158 | * Convert an int to its hex representation with a fixed length of 8 bytes.
159 | */
160 | function fromIntToBytes8Hex(num: number | bigint) {
161 | return num.toString(16).padStart(16, "0");
162 | }
163 |
164 | /**
165 | * Convert an int to its hex representation with a fixed length of 1 byte.
166 | */
167 | function fromIntToByteHex(num: number | bigint) {
168 | return num.toString(16).padStart(2, "0");
169 | }
170 |
171 | function encodeToBase64(str: string, encoding: BufferEncoding = "utf8") {
172 | return Buffer.from(str, encoding).toString("base64");
173 | }
174 |
175 | function getParsedValueFromState(
176 | state: TealKeyValue[],
177 | key: string,
178 | encoding: BufferEncoding = "utf8",
179 | ): string | bigint | undefined {
180 | const encodedKey: string = encoding ? encodeToBase64(key, encoding) : key;
181 | const keyValue: TealKeyValue | undefined = state.find((entry) => entry.key === encodedKey);
182 | if (keyValue === undefined) return;
183 | const { value } = keyValue;
184 | if (value.type === 1) return value.bytes;
185 | if (value.type === 2) return BigInt(value.uint);
186 | return;
187 | }
188 |
189 | function parseUint64s(base64Value: string): bigint[] {
190 | const value = Buffer.from(base64Value, "base64").toString("hex");
191 |
192 | // uint64s are 8 bytes each
193 | const uint64s: bigint[] = [];
194 | for (let i = 0; i < value.length; i += 16) {
195 | uint64s.push(BigInt("0x" + value.slice(i, i + 16)));
196 | }
197 | return uint64s;
198 | }
199 |
200 | function parseUint8s(base64Value: string): bigint[] {
201 | const value = Buffer.from(base64Value, "base64").toString("hex");
202 | // uint8s are 1 byte each
203 | const uint8s: bigint[] = [];
204 | for (let i = 0; i < value.length; i += 2) {
205 | uint8s.push(BigInt("0x" + value.slice(i, i + 2)));
206 | }
207 | return uint8s;
208 | }
209 |
210 | function parseBitsAsBooleans(base64Value: string): boolean[] {
211 | const value = Buffer.from(base64Value, "base64").toString("hex");
212 | const bits = ("00000000" + Number("0x" + value).toString(2)).slice(-8);
213 | const bools: boolean[] = [];
214 | for (const bit of bits) {
215 | bools.push(Boolean(parseInt(bit)));
216 | }
217 | return bools;
218 | }
219 |
220 | function addEscrowNoteTransaction(
221 | userAddr: string,
222 | escrowAddr: string,
223 | appId: number,
224 | notePrefix: string,
225 | params: SuggestedParams,
226 | ): Transaction {
227 | const note = Uint8Array.from([...enc.encode(notePrefix), ...decodeAddress(escrowAddr).publicKey]);
228 | return makePaymentTxnWithSuggestedParamsFromObject({
229 | from: userAddr,
230 | to: getApplicationAddress(appId),
231 | amount: 0,
232 | note,
233 | suggestedParams: params,
234 | });
235 | }
236 |
237 | function removeEscrowNoteTransaction(
238 | escrowAddr: string,
239 | userAddr: string,
240 | notePrefix: string,
241 | params: SuggestedParams,
242 | ): Transaction {
243 | const note = Uint8Array.from([...enc.encode(notePrefix), ...decodeAddress(escrowAddr).publicKey]);
244 | return makePaymentTxnWithSuggestedParamsFromObject({
245 | from: escrowAddr,
246 | to: userAddr,
247 | amount: 0,
248 | closeRemainderTo: userAddr,
249 | note,
250 | suggestedParams: params,
251 | });
252 | }
253 |
254 | export {
255 | enc,
256 | transferAlgoOrAsset,
257 | signer,
258 | PAYOUTS_GO_ONLINE_FEE,
259 | unixTime,
260 | getApplicationGlobalState,
261 | getAccountApplicationLocalState,
262 | getApplicationBox,
263 | getAccountDetails,
264 | fromIntToBytes8Hex,
265 | fromIntToByteHex,
266 | getParsedValueFromState,
267 | parseUint64s,
268 | parseUint8s,
269 | parseBitsAsBooleans,
270 | addEscrowNoteTransaction,
271 | removeEscrowNoteTransaction,
272 | };
273 |
--------------------------------------------------------------------------------
/src/xalgo/abi-contracts/index.ts:
--------------------------------------------------------------------------------
1 | import { ABIContract } from "algosdk";
2 |
3 | import stakeAndDepositABI from "./stake_and_deposit.json";
4 | import xAlgoABI from "./xalgo.json";
5 |
6 | export const xAlgoABIContract = new ABIContract(xAlgoABI);
7 | export const stakeAndDepositABIContract = new ABIContract(stakeAndDepositABI);
8 |
--------------------------------------------------------------------------------
/src/xalgo/abi-contracts/stake_and_deposit.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "StakeAndDeposit",
3 | "methods": [
4 | {
5 | "name": "stake_and_deposit",
6 | "args": [
7 | {
8 | "type": "pay",
9 | "name": "send_algo"
10 | },
11 | {
12 | "type": "application",
13 | "name": "consensus"
14 | },
15 | {
16 | "type": "application",
17 | "name": "pool"
18 | },
19 | {
20 | "type": "application",
21 | "name": "pool_manager"
22 | },
23 | {
24 | "type": "asset",
25 | "name": "x_algo"
26 | },
27 | {
28 | "type": "asset",
29 | "name": "f_x_algo"
30 | },
31 | {
32 | "type": "account",
33 | "name": "receiver"
34 | },
35 | {
36 | "type": "uint64",
37 | "name": "min_x_algo_received"
38 | }
39 | ],
40 | "returns": {
41 | "type": "void"
42 | }
43 | }
44 | ],
45 | "networks": {}
46 | }
47 |
--------------------------------------------------------------------------------
/src/xalgo/abi-contracts/xalgo.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "XAlgo",
3 | "desc": "Allows users to participate in consensus and receive a liquid staking token",
4 | "methods": [
5 | {
6 | "name": "claim_fee",
7 | "desc": "Send the unclaimed fees to the admin.",
8 | "args": [],
9 | "returns": {
10 | "type": "void"
11 | }
12 | },
13 | {
14 | "name": "set_proposer_admin",
15 | "desc": "Privileged operation to set the proposer admin.",
16 | "args": [
17 | {
18 | "type": "uint8",
19 | "name": "proposer_index",
20 | "desc": "The index of proposer to set the admin of"
21 | },
22 | {
23 | "type": "address",
24 | "name": "new_proposer_admin",
25 | "desc": "The new proposer admin"
26 | }
27 | ],
28 | "returns": {
29 | "type": "void"
30 | }
31 | },
32 | {
33 | "name": "register_online",
34 | "desc": "Privileged operation to register a proposer online",
35 | "args": [
36 | {
37 | "type": "pay",
38 | "name": "send_algo",
39 | "desc": "Send ALGO to the proposer to pay for the register online fee"
40 | },
41 | {
42 | "type": "uint8",
43 | "name": "proposer_index",
44 | "desc": "The index of proposer to register online with"
45 | },
46 | {
47 | "type": "address",
48 | "name": "vote_key",
49 | "desc": "The root participation public key (if any) currently registered for this round"
50 | },
51 | {
52 | "type": "address",
53 | "name": "sel_key",
54 | "desc": "The selection public key (if any) currently registered for this round"
55 | },
56 | {
57 | "type": "byte[64]",
58 | "name": "state_proof_key",
59 | "desc": "The root of the state proof key (if any)"
60 | },
61 | {
62 | "type": "uint64",
63 | "name": "vote_first",
64 | "desc": "The first round for which this participation is valid"
65 | },
66 | {
67 | "type": "uint64",
68 | "name": "vote_last",
69 | "desc": "The last round for which this participation is valid"
70 | },
71 | {
72 | "type": "uint64",
73 | "name": "vote_key_dilution",
74 | "desc": "The number of subkeys in each batch of participation keys"
75 | }
76 | ],
77 | "returns": {
78 | "type": "void"
79 | }
80 | },
81 | {
82 | "name": "register_offline",
83 | "desc": "Privileged operation to register a proposer offline",
84 | "args": [
85 | {
86 | "type": "uint8",
87 | "name": "proposer_index",
88 | "desc": "The index of proposer to register offline with"
89 | }
90 | ],
91 | "returns": {
92 | "type": "void"
93 | }
94 | },
95 | {
96 | "name": "immediate_mint",
97 | "desc": "Send ALGO to the app and receive xALGO immediately",
98 | "args": [
99 | {
100 | "type": "pay",
101 | "name": "send_algo",
102 | "desc": "Send ALGO to the app to mint"
103 | },
104 | {
105 | "type": "address",
106 | "name": "receiver",
107 | "desc": "The address to receiver the xALGO at"
108 | },
109 | {
110 | "type": "uint64",
111 | "name": "min_received",
112 | "desc": "The minimum amount of xALGO to receive in return"
113 | }
114 | ],
115 | "returns": {
116 | "type": "void"
117 | }
118 | },
119 | {
120 | "name": "delayed_mint",
121 | "desc": "Send ALGO to the app and receive xALGO after 320 rounds",
122 | "args": [
123 | {
124 | "type": "pay",
125 | "name": "send_algo",
126 | "desc": "Send ALGO to the app to mint"
127 | },
128 | {
129 | "type": "address",
130 | "name": "receiver",
131 | "desc": "The address to receiver the xALGO at"
132 | },
133 | {
134 | "type": "byte[2]",
135 | "name": "nonce",
136 | "desc": "The nonce used to create the box to store the delayed mint"
137 | }
138 | ],
139 | "returns": {
140 | "type": "void"
141 | }
142 | },
143 | {
144 | "name": "claim_delayed_mint",
145 | "desc": "Claim delayed mint after 320 rounds",
146 | "args": [
147 | {
148 | "type": "address",
149 | "name": "minter",
150 | "desc": "The address which submitted the delayed mint"
151 | },
152 | {
153 | "type": "byte[2]",
154 | "name": "nonce",
155 | "desc": "The nonce used to create the box which stores the delayed mint"
156 | }
157 | ],
158 | "returns": {
159 | "type": "void"
160 | }
161 | },
162 | {
163 | "name": "burn",
164 | "desc": "Send xALGO to the app and receive ALGO",
165 | "args": [
166 | {
167 | "type": "axfer",
168 | "name": "send_xalgo",
169 | "desc": "Send xALGO to the app to burn"
170 | },
171 | {
172 | "type": "address",
173 | "name": "receiver",
174 | "desc": "The address to receiver the ALGO at"
175 | },
176 | {
177 | "type": "uint64",
178 | "name": "min_received",
179 | "desc": "The minimum amount of ALGO to receive in return"
180 | }
181 | ],
182 | "returns": {
183 | "type": "void"
184 | }
185 | },
186 | {
187 | "name": "get_xalgo_rate",
188 | "desc": "Get the conversion rate between xALGO and ALGO",
189 | "args": [],
190 | "returns": {
191 | "type": "(uint64,uint64,byte[])",
192 | "desc": "Array of [algo_balance, x_algo_circulating_supply, proposers_balances]"
193 | }
194 | },
195 | {
196 | "name": "dummy",
197 | "desc": "Dummy call to the app to bypass foreign accounts limit",
198 | "args": [],
199 | "returns": {
200 | "type": "void"
201 | }
202 | }
203 | ],
204 | "networks": {}
205 | }
206 |
--------------------------------------------------------------------------------
/src/xalgo/constants/mainnet-constants.ts:
--------------------------------------------------------------------------------
1 | import type { ConsensusConfig } from "../types";
2 |
3 | const MainnetConsensusConfig: ConsensusConfig = {
4 | consensusAppId: 1134695678,
5 | xAlgoId: 1134696561,
6 | stakeAndDepositAppId: 2633147490,
7 | };
8 |
9 | export { MainnetConsensusConfig };
10 |
--------------------------------------------------------------------------------
/src/xalgo/constants/testnet-constants.ts:
--------------------------------------------------------------------------------
1 | import type { ConsensusConfig } from "../types";
2 |
3 | const TestnetConsensusConfig: ConsensusConfig = {
4 | consensusAppId: 730430673,
5 | xAlgoId: 730430700,
6 | stakeAndDepositAppId: 731190793,
7 | };
8 |
9 | export { TestnetConsensusConfig };
10 |
--------------------------------------------------------------------------------
/src/xalgo/formulae.ts:
--------------------------------------------------------------------------------
1 | import { mulScale, ONE_16_DP } from "../math-lib";
2 |
3 | import type { ConsensusState } from "./types";
4 |
5 | function convertAlgoToXAlgoWhenImmediate(algoAmount: bigint, consensusState: ConsensusState): bigint {
6 | const { algoBalance, xAlgoCirculatingSupply, premium } = consensusState;
7 | return mulScale(mulScale(algoAmount, xAlgoCirculatingSupply, algoBalance), ONE_16_DP - premium, ONE_16_DP);
8 | }
9 |
10 | function convertAlgoToXAlgoWhenDelay(algoAmount: bigint, consensusState: ConsensusState): bigint {
11 | const { algoBalance, xAlgoCirculatingSupply } = consensusState;
12 | return mulScale(algoAmount, xAlgoCirculatingSupply, algoBalance);
13 | }
14 |
15 | function convertXAlgoToAlgo(xAlgoAmount: bigint, consensusState: ConsensusState): bigint {
16 | const { algoBalance, xAlgoCirculatingSupply } = consensusState;
17 | return mulScale(xAlgoAmount, algoBalance, xAlgoCirculatingSupply);
18 | }
19 |
20 | export { convertAlgoToXAlgoWhenImmediate, convertAlgoToXAlgoWhenDelay, convertXAlgoToAlgo };
21 |
--------------------------------------------------------------------------------
/src/xalgo/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./abi-contracts";
2 | export * from "./constants/mainnet-constants";
3 | export * from "./constants/testnet-constants";
4 | export * from "./consensus";
5 | export * from "./formulae";
6 | export * from "./types";
7 |
--------------------------------------------------------------------------------
/src/xalgo/types.ts:
--------------------------------------------------------------------------------
1 | interface ConsensusConfig {
2 | consensusAppId: number;
3 | xAlgoId: number;
4 | stakeAndDepositAppId: number;
5 | }
6 |
7 | interface ConsensusState {
8 | currentRound: number; // round the data was read at
9 | algoBalance: bigint;
10 | xAlgoCirculatingSupply: bigint;
11 | proposersBalances: {
12 | address: string;
13 | algoBalance: bigint;
14 | }[];
15 | timeDelay: bigint;
16 | numProposers: bigint;
17 | maxProposerBalance: bigint;
18 | fee: bigint; // 4 d.p.
19 | premium: bigint; // 16 d.p.
20 | lastProposersActiveBalance: bigint;
21 | totalPendingStake: bigint;
22 | totalUnclaimedFees: bigint;
23 | canImmediateStake: boolean;
24 | canDelayStake: boolean;
25 | }
26 |
27 | type ProposerAllocations = bigint[];
28 |
29 | export { ConsensusConfig, ConsensusState, ProposerAllocations };
30 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["ES6", "DOM"],
4 | "target": "ES6",
5 | "module": "CommonJS",
6 | "moduleResolution": "Node",
7 | "strict": true,
8 | "esModuleInterop": true,
9 | "resolveJsonModule": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "skipLibCheck": true,
12 | "checkJs": true,
13 | "allowJs": true,
14 | "downlevelIteration": true,
15 | "allowSyntheticDefaultImports": true,
16 | "outDir": "dist",
17 | "declaration": true,
18 | "declarationMap": true,
19 | "declarationDir": "dist/types",
20 | "sourceMap": true
21 | },
22 | "include": ["src/**/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://typedoc.org/schema.json",
3 | "entryPoints": ["src/index.ts"],
4 | "excludeExternals": true,
5 | "out": "docs",
6 | "plugin": ["@typhonjs-typedoc/typedoc-theme-dmt"],
7 | "theme": "default-modern"
8 | }
9 |
--------------------------------------------------------------------------------