Skip to content

Commit

Permalink
Merge pull request #2 from minswap/tlinh
Browse files Browse the repository at this point in the history
feat: minswap tokens
  • Loading branch information
0xj4m35 authored Nov 27, 2024
2 parents bebc939 + 34c1304 commit 76c1f56
Show file tree
Hide file tree
Showing 576 changed files with 13,272 additions and 147 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BLOCKFROST_PROJECT_ID=
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
node_modules
.idea
.vscode
.env
act
build
49 changes: 31 additions & 18 deletions README.md
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
```
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"files": {
"ignoreUnknown": false,
"ignore": ["node_modules/**", ".vscode/**"]
"ignore": ["node_modules/**", ".vscode/**", "build", "src/tokens", "*.config.js"]
},
"formatter": {
"bracketSpacing": true,
Expand Down
9 changes: 9 additions & 0 deletions environment.d.ts
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 {};
101 changes: 101 additions & 0 deletions internal/checkTVL.ts
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();
35 changes: 35 additions & 0 deletions internal/validateTokenFiles.ts
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();
Loading

0 comments on commit 76c1f56

Please sign in to comment.