-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from minswap/tlinh
feat: minswap tokens
- Loading branch information
Showing
576 changed files
with
13,272 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
BLOCKFROST_PROJECT_ID= |
17 changes: 14 additions & 3 deletions
17
.github/workflows/ci.yaml → .github/workflows/validate-tokens.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,37 @@ | ||
name: CI | ||
name: validate-tokens | ||
on: | ||
pull_request: | ||
branches: | ||
- "*" | ||
|
||
jobs: | ||
validate-data: | ||
validate-tokens: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
node-version: [20] | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Install pnpm | ||
uses: pnpm/action-setup@v4 | ||
with: | ||
version: 9.5.0 | ||
|
||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
cache: 'pnpm' | ||
|
||
- name: Install dependencies | ||
run: pnpm install | ||
- name: Run validate data | ||
|
||
- name: Run biome check | ||
run: pnpm run lint:ci | ||
|
||
- name: Run validate token data | ||
run: pnpm run check-format | ||
|
||
- name: Run test | ||
run: pnpm run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,6 @@ | |
node_modules | ||
.idea | ||
.vscode | ||
.env | ||
act | ||
build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,49 @@ | ||
# Minswap tokens | ||
The merge of deprecated verified-tokens and market cap repositories, which contains a list of tokens, exposes APIs for transparent access to circulating supply and total supply. | ||
|
||
## Requirements | ||
For tokens to be verified, ensure your token has a pool with at least **1000 ADA TVL** and follow the structures stated in the instructions below. Any token that has been verified does not meet the requirements in the future would still be unverified. | ||
|
||
## How to add my token | ||
Create a pull request adding yaml file according to the following structure in the `src/tokens`: | ||
```yaml | ||
# 1 token = 1 yaml file | ||
# filename: policyId + tokenName (like cardano-token-registry) | ||
# merge verified-tokens and market-cap into 1 new repo, then archive those 2 old repos (to avoid breaking changes with integrators) | ||
# filename/assetId: policyId + hex-coded token name | ||
|
||
projectName: Minswap | ||
project: Minswap | ||
# among DeFi, RealFi, GameFi, Meme, Bridge, Metaverse, Wallet, NFT, Oracle, AI, Launchpad, DAO, Stablecoin, Social, Media, Risk Ratings, Index Vaults, DePIN, Other | ||
categories: | ||
- DeFi | ||
- DAO | ||
- DeFi | ||
- DAO | ||
|
||
decimals: 0 | ||
# not required, among website, twitter, discord, telegram, coinMarketCap, coinGecko | ||
socialLinks: | ||
website: https:// | ||
discord: ... | ||
|
||
unverified: true # default false, if a token violate verification policy then turn on | ||
verified: true # default true, if a token violate verification policy then switch to false | ||
|
||
maxSupply: 500000000 | ||
# the following fields are not required | ||
maxSupply: 500000000 # either number or string | ||
# or | ||
maxSupply: https://... | ||
|
||
treasuryWallets: | ||
- addr... | ||
- addr... | ||
- https://... | ||
|
||
burnWallets: | ||
- addr... | ||
- https://... | ||
|
||
# total = max - burn | ||
# circulating = max - burn - treasury | ||
treasury: | ||
- addr... | ||
- stake... | ||
- https://... | ||
- assetId | ||
|
||
burn: | ||
- addr... | ||
- stake... | ||
- https://... | ||
- assetId | ||
|
||
circulatingOnChain: | ||
- addr... | ||
- stake... | ||
- https://... | ||
- assetId | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
declare global { | ||
namespace NodeJS { | ||
interface ProcessEnv { | ||
BLOCKFROST_PROJECT_ID: string; | ||
} | ||
} | ||
} | ||
|
||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import { BlockFrostAPI } from "@blockfrost/blockfrost-js"; | ||
import * as SDK from "@minswap/sdk"; | ||
import { dump, load } from "js-yaml"; | ||
|
||
import type { TokenMetadata } from "@/types"; | ||
|
||
const MINIMUM_TVL = 1000_000000n; // 1000 ADA | ||
const LIMIT_PAGINATION = 100; | ||
const __dirname = import.meta.dirname; | ||
const TOKEN_DIR = path.join(__dirname, "../src/tokens"); | ||
|
||
const STABLE_COINS = [ | ||
"8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344", // DJED | ||
"f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344", // iUSD | ||
"25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534443", // USDC | ||
"c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.5553444d", // USDM | ||
"92776616f1f32c65a173392e4410a3d8c39dcf6ef768c73af164779c.4d79555344", // MyUSD | ||
]; | ||
|
||
const blockfrostAPI = new BlockFrostAPI({ | ||
projectId: process.env["BLOCKFROST_PROJECT_ID"], | ||
network: "mainnet", | ||
}); | ||
|
||
const blockfrostAdapter = new SDK.BlockfrostAdapter({ | ||
networkId: SDK.NetworkId.MAINNET, | ||
blockFrost: blockfrostAPI, | ||
}); | ||
|
||
async function verifyTVL() { | ||
const [v1Pools, { pools: v2Pools }] = await Promise.all([getAllV1Pools(), blockfrostAdapter.getAllV2Pools()]); | ||
|
||
fs.readdir(TOKEN_DIR, async function (error, files) { | ||
if (error) { | ||
throw error; | ||
} | ||
for (const file of files) { | ||
const filePath = path.join(TOKEN_DIR, file); | ||
const tokenData = <TokenMetadata>load(fs.readFileSync(filePath, "utf8")); | ||
const tokenId = file.split(".")[0]; | ||
const newVerified = await checkTVL(v1Pools, v2Pools, tokenId); | ||
if (newVerified === tokenData.verified) { | ||
continue; | ||
} | ||
|
||
const tokenInfo = { | ||
...tokenData, | ||
verified: newVerified, | ||
}; | ||
|
||
let yamlString = ""; | ||
for (const [key, value] of Object.entries(tokenInfo)) { | ||
yamlString += `${dump({ [key]: value }, { lineWidth: -1 })}\n`; | ||
} | ||
fs.writeFileSync(filePath, yamlString, "utf8"); | ||
} | ||
}); | ||
} | ||
|
||
async function checkTVL(v1Pools: SDK.PoolV1.State[], v2Pools: SDK.PoolV2.State[], tokenId: string): Promise<boolean> { | ||
if (STABLE_COINS.includes(tokenId)) { | ||
return true; | ||
} | ||
|
||
let maxTVL = 0n; | ||
|
||
const poolV1 = v1Pools.find((pool) => pool.assetA === SDK.Asset.toString(SDK.ADA) && pool.assetB === tokenId); | ||
|
||
maxTVL = (poolV1?.reserveA ?? 0n) * 2n; | ||
|
||
const poolV2 = v2Pools.find((pool) => pool.assetA === SDK.Asset.toString(SDK.ADA) && pool.assetB === tokenId); | ||
|
||
const tvlV2 = (poolV2?.reserveA ?? 0n) * 2n; | ||
if (maxTVL < tvlV2) { | ||
maxTVL = tvlV2; | ||
} | ||
|
||
return maxTVL >= MINIMUM_TVL; | ||
} | ||
|
||
async function getAllV1Pools() { | ||
const v1Pools: SDK.PoolV1.State[] = []; | ||
|
||
let page = 1; | ||
while (true) { | ||
const paginatedPools = await blockfrostAdapter.getV1Pools({ | ||
page, | ||
count: LIMIT_PAGINATION, | ||
}); | ||
if (paginatedPools.length === 0) { | ||
break; | ||
} | ||
v1Pools.push(...paginatedPools); | ||
page++; | ||
} | ||
return v1Pools; | ||
} | ||
|
||
verifyTVL(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import * as fs from "node:fs"; | ||
import path from "node:path"; | ||
import Ajv from "ajv"; | ||
import { load } from "js-yaml"; | ||
|
||
import { DEFAULT_TOKEN_DIR } from "@/const"; | ||
import { tokenSchema } from "@/tokenSchema"; | ||
import type { TokenMetadata } from "@/types"; | ||
|
||
const ajv = new Ajv(); | ||
const __dirname = import.meta.dirname; | ||
const TOKEN_DIR = path.join(__dirname, `../src/${DEFAULT_TOKEN_DIR}`); | ||
|
||
async function validateTokenFiles() { | ||
fs.readdir(TOKEN_DIR, (error, files) => { | ||
if (error) { | ||
console.error(error); | ||
throw error; | ||
} | ||
for (const file of files) { | ||
const filePath = path.join(TOKEN_DIR, `${file}`); | ||
const tokenFileData = fs.readFileSync(filePath, "utf-8"); | ||
const tokenData: TokenMetadata = { | ||
tokenId: file.split(".")[0], | ||
...(load(tokenFileData) as Omit<TokenMetadata, "tokenId">), | ||
}; | ||
const validate = ajv.validate(tokenSchema, tokenData); | ||
if (!validate) { | ||
throw new Error(`Error validating token, token file: ${file}`); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
validateTokenFiles(); |
Oops, something went wrong.