Skip to content

Commit 76c1f56

Browse files
authored
Merge pull request #2 from minswap/tlinh
feat: minswap tokens
2 parents bebc939 + 34c1304 commit 76c1f56

File tree

576 files changed

+13272
-147
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

576 files changed

+13272
-147
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BLOCKFROST_PROJECT_ID=
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
name: CI
1+
name: validate-tokens
22
on:
33
pull_request:
44
branches:
55
- "*"
6+
67
jobs:
7-
validate-data:
8+
validate-tokens:
89
runs-on: ubuntu-latest
910
strategy:
1011
matrix:
1112
node-version: [20]
1213
steps:
1314
- uses: actions/checkout@v4
15+
1416
- name: Install pnpm
1517
uses: pnpm/action-setup@v4
1618
with:
1719
version: 9.5.0
20+
1821
- name: Use Node.js ${{ matrix.node-version }}
1922
uses: actions/setup-node@v4
2023
with:
2124
node-version: ${{ matrix.node-version }}
2225
cache: 'pnpm'
26+
2327
- name: Install dependencies
2428
run: pnpm install
25-
- name: Run validate data
29+
30+
- name: Run biome check
31+
run: pnpm run lint:ci
32+
33+
- name: Run validate token data
34+
run: pnpm run check-format
35+
36+
- name: Run test
2637
run: pnpm run test

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
node_modules
33
.idea
44
.vscode
5+
.env
6+
act
57
build

README.md

+31-18
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,49 @@
11
# Minswap tokens
2+
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.
23

34
## Requirements
5+
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.
46

7+
## How to add my token
8+
Create a pull request adding yaml file according to the following structure in the `src/tokens`:
59
```yaml
610
# 1 token = 1 yaml file
7-
# filename: policyId + tokenName (like cardano-token-registry)
8-
# merge verified-tokens and market-cap into 1 new repo, then archive those 2 old repos (to avoid breaking changes with integrators)
11+
# filename/assetId: policyId + hex-coded token name
912

10-
projectName: Minswap
13+
project: Minswap
14+
# among DeFi, RealFi, GameFi, Meme, Bridge, Metaverse, Wallet, NFT, Oracle, AI, Launchpad, DAO, Stablecoin, Social, Media, Risk Ratings, Index Vaults, DePIN, Other
1115
categories:
12-
- DeFi
13-
- DAO
16+
- DeFi
17+
- DAO
1418

19+
decimals: 0
20+
# not required, among website, twitter, discord, telegram, coinMarketCap, coinGecko
1521
socialLinks:
1622
website: https://
1723
discord: ...
1824

19-
unverified: true # default false, if a token violate verification policy then turn on
25+
verified: true # default true, if a token violate verification policy then switch to false
2026

21-
maxSupply: 500000000
27+
# the following fields are not required
28+
maxSupply: 500000000 # either number or string
2229
# or
2330
maxSupply: https://...
2431

25-
treasuryWallets:
26-
- addr...
27-
- addr...
28-
- https://...
29-
30-
burnWallets:
31-
- addr...
32-
- https://...
33-
34-
# total = max - burn
35-
# circulating = max - burn - treasury
32+
treasury:
33+
- addr...
34+
- stake...
35+
- https://...
36+
- assetId
37+
38+
burn:
39+
- addr...
40+
- stake...
41+
- https://...
42+
- assetId
43+
44+
circulatingOnChain:
45+
- addr...
46+
- stake...
47+
- https://...
48+
- assetId
3649
```

biome.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
"files": {
88
"ignoreUnknown": false,
9-
"ignore": ["node_modules/**", ".vscode/**"]
9+
"ignore": ["node_modules/**", ".vscode/**", "build", "src/tokens", "*.config.js"]
1010
},
1111
"formatter": {
1212
"bracketSpacing": true,

environment.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
declare global {
2+
namespace NodeJS {
3+
interface ProcessEnv {
4+
BLOCKFROST_PROJECT_ID: string;
5+
}
6+
}
7+
}
8+
9+
export {};

internal/checkTVL.ts

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
4+
import * as SDK from "@minswap/sdk";
5+
import { dump, load } from "js-yaml";
6+
7+
import type { TokenMetadata } from "@/types";
8+
9+
const MINIMUM_TVL = 1000_000000n; // 1000 ADA
10+
const LIMIT_PAGINATION = 100;
11+
const __dirname = import.meta.dirname;
12+
const TOKEN_DIR = path.join(__dirname, "../src/tokens");
13+
14+
const STABLE_COINS = [
15+
"8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344", // DJED
16+
"f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344", // iUSD
17+
"25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534443", // USDC
18+
"c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.5553444d", // USDM
19+
"92776616f1f32c65a173392e4410a3d8c39dcf6ef768c73af164779c.4d79555344", // MyUSD
20+
];
21+
22+
const blockfrostAPI = new BlockFrostAPI({
23+
projectId: process.env["BLOCKFROST_PROJECT_ID"],
24+
network: "mainnet",
25+
});
26+
27+
const blockfrostAdapter = new SDK.BlockfrostAdapter({
28+
networkId: SDK.NetworkId.MAINNET,
29+
blockFrost: blockfrostAPI,
30+
});
31+
32+
async function verifyTVL() {
33+
const [v1Pools, { pools: v2Pools }] = await Promise.all([getAllV1Pools(), blockfrostAdapter.getAllV2Pools()]);
34+
35+
fs.readdir(TOKEN_DIR, async function (error, files) {
36+
if (error) {
37+
throw error;
38+
}
39+
for (const file of files) {
40+
const filePath = path.join(TOKEN_DIR, file);
41+
const tokenData = <TokenMetadata>load(fs.readFileSync(filePath, "utf8"));
42+
const tokenId = file.split(".")[0];
43+
const newVerified = await checkTVL(v1Pools, v2Pools, tokenId);
44+
if (newVerified === tokenData.verified) {
45+
continue;
46+
}
47+
48+
const tokenInfo = {
49+
...tokenData,
50+
verified: newVerified,
51+
};
52+
53+
let yamlString = "";
54+
for (const [key, value] of Object.entries(tokenInfo)) {
55+
yamlString += `${dump({ [key]: value }, { lineWidth: -1 })}\n`;
56+
}
57+
fs.writeFileSync(filePath, yamlString, "utf8");
58+
}
59+
});
60+
}
61+
62+
async function checkTVL(v1Pools: SDK.PoolV1.State[], v2Pools: SDK.PoolV2.State[], tokenId: string): Promise<boolean> {
63+
if (STABLE_COINS.includes(tokenId)) {
64+
return true;
65+
}
66+
67+
let maxTVL = 0n;
68+
69+
const poolV1 = v1Pools.find((pool) => pool.assetA === SDK.Asset.toString(SDK.ADA) && pool.assetB === tokenId);
70+
71+
maxTVL = (poolV1?.reserveA ?? 0n) * 2n;
72+
73+
const poolV2 = v2Pools.find((pool) => pool.assetA === SDK.Asset.toString(SDK.ADA) && pool.assetB === tokenId);
74+
75+
const tvlV2 = (poolV2?.reserveA ?? 0n) * 2n;
76+
if (maxTVL < tvlV2) {
77+
maxTVL = tvlV2;
78+
}
79+
80+
return maxTVL >= MINIMUM_TVL;
81+
}
82+
83+
async function getAllV1Pools() {
84+
const v1Pools: SDK.PoolV1.State[] = [];
85+
86+
let page = 1;
87+
while (true) {
88+
const paginatedPools = await blockfrostAdapter.getV1Pools({
89+
page,
90+
count: LIMIT_PAGINATION,
91+
});
92+
if (paginatedPools.length === 0) {
93+
break;
94+
}
95+
v1Pools.push(...paginatedPools);
96+
page++;
97+
}
98+
return v1Pools;
99+
}
100+
101+
verifyTVL();

internal/validateTokenFiles.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as fs from "node:fs";
2+
import path from "node:path";
3+
import Ajv from "ajv";
4+
import { load } from "js-yaml";
5+
6+
import { DEFAULT_TOKEN_DIR } from "@/const";
7+
import { tokenSchema } from "@/tokenSchema";
8+
import type { TokenMetadata } from "@/types";
9+
10+
const ajv = new Ajv();
11+
const __dirname = import.meta.dirname;
12+
const TOKEN_DIR = path.join(__dirname, `../src/${DEFAULT_TOKEN_DIR}`);
13+
14+
async function validateTokenFiles() {
15+
fs.readdir(TOKEN_DIR, (error, files) => {
16+
if (error) {
17+
console.error(error);
18+
throw error;
19+
}
20+
for (const file of files) {
21+
const filePath = path.join(TOKEN_DIR, `${file}`);
22+
const tokenFileData = fs.readFileSync(filePath, "utf-8");
23+
const tokenData: TokenMetadata = {
24+
tokenId: file.split(".")[0],
25+
...(load(tokenFileData) as Omit<TokenMetadata, "tokenId">),
26+
};
27+
const validate = ajv.validate(tokenSchema, tokenData);
28+
if (!validate) {
29+
throw new Error(`Error validating token, token file: ${file}`);
30+
}
31+
}
32+
});
33+
}
34+
35+
validateTokenFiles();

0 commit comments

Comments
 (0)