remove legacy onchain_data project (#478)

remove onchain_data project
This commit is contained in:
walker-16 2023-06-29 17:12:31 -03:00 committed by GitHub
parent 78bd0cbd75
commit 781d7ca8d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 0 additions and 14104 deletions

View File

@ -1,2 +0,0 @@
node_modules
lib

View File

@ -1,12 +0,0 @@
FROM node:16-alpine@sha256:004dbac84fed48e20f9888a23e32fa7cf83c2995e174a78d41d9a9dd1e051a20
RUN mkdir -p /app
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
npm ci
COPY . .
RUN npm run build
ENTRYPOINT node lib/main.js

View File

@ -1,20 +0,0 @@
# Onchain data for custody monitoring
For each connected wormhole chain, query the custody contract and get the updated locked value for each token.
There are two modes:
(1) query a known list of tracked tokens aka allow list
(2) query either a scan site (if available) or covalent (also if available) to grab list of known tokens locked in the custody contract
Set env variable allowlist=true if using allow list found at: onchain_data/data/allowList.json
taken from: https://github.com/wormhole-foundation/wormhole/blob/dev.v2/event_database/cloud_functions/token-allowlist-mainnet.json
Has the added benefit of also including the coin gecko ids in order to mark positions to USD
```
MONGODB_URI=mongodb://root:example@localhost:27017/ allowlist=true node lib/getCustodyData.js
```
Currently Near, Algorand, Xpla, & Aptos are not supported. Feel free to add support ;)
The aggregated custody data gets pushed to the mongodb database - "onchain_data" into the table - "custody"
There are api endpoints that the server provides which powers the wormscan website.

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
{
"dependencies": {
"@certusone/wormhole-sdk": "^0.9.0",
"@metaplex/js": "^4.12.0",
"@terra-money/terra.js": "^3.1.3",
"coingecko-api": "^1.0.10",
"mongodb": "^4.10.0",
"serve": "^11.0.0",
"typescript": "^4.5.2"
},
"scripts": {
"build": "tsc"
},
"devDependencies": {
"typescript": "^4.8.4"
}
}

View File

@ -1,224 +0,0 @@
{
"abi": [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "owner",
"type": "address"
},
{
"indexed": true,
"name": "spender",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
}

View File

@ -1,178 +0,0 @@
{
"1": {
"ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx": "star-atlas",
"NFTUkR4u7wKxy9QLaX2TGvd9oZSWoMo4jqSJqdMb7Nk": "blockasset",
"AkhdZGVbJXPuQZ53u2LrimCjkRP6ZyxG1SoM85T98eE1": "starbots",
"4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R": "raydium",
"4Te4KJgjtnZe4aE2zne8G4NPfrPjCwDmaiEx9rKnyDVZ": "solclout",
"So11111111111111111111111111111111111111112": "solana",
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "usd-coin",
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB": "tether",
"7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj": "lido-staked-sol"
},
"2": {
"0x111111111117dc0aa78b770fa6a738034120c302": "1inch",
"0x009178997aff09a67d4caccfeb897fb79d036214": "1sol",
"0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9": "aave",
"0x27702a26126e0b3702af63ee09ac4d1a084ef628": "aleph",
"0xe0cca86b254005889ac3a81e737f56a14f4a38f5": "alta-finance",
"0x9b83f827928abdf18cf1f7e67053572b9bceff3a": "artem",
"0x18aaa7115705e8be94bffebde57af9bfc265b998": "audius",
"0xbb0e17ef65f82ab018d8edd776e8dd940327b28b": "axie-infinity",
"0x0d8775f648430679a709e98d2b0cb6250d2887ef": "basic-attention-token",
"0xf17e65822b568b3903685a7c9f496cf7656cc6c2": "biconomy",
"0xef19f4e48830093ce5bc8b3ff7f903a0ae3e9fa1": "botxcoin",
"0xbba39fd2935d5769116ce38d46a71bde9cf03099": "choise",
"0xd49efa7bc0d339d74f487959c573d518ba3f8437": "shield-finance",
"0xc00e94cb662c3520282e6f5717214004a7f26888": "compound-governance-token",
"0x2ba592f78db6436527729929aaf6c908497cb200": "cream-2",
"0x6b175474e89094c44da98b954eedeac495271d0f": "dai",
"0x92d6c1e31e14520e676a687f0a93788b716beff5": "dydx",
"0x4da34f8264cb33a5c9f17081b9ef5ff6091116f4": "elyfi",
"0xfd09911130e6930bf87f2b0554c44f400bd80d3e": "ethichub",
"0x853d955acef822db058eb8505911ed77f175b99e": "frax",
"0xf8c3527cc04340b208c854e985240c02f7b7793f": "frontier-token",
"0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9": "ftx-token",
"0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0": "frax-share",
"0xc944e90c64b2c07662a292be6244bdf05cda44a7": "the-graph",
"0x4674672bcddda2ea5300f5207e1158185c944bc0": "gem-exchange-and-trading",
"0x0316eb71485b0ab14103307bf65a021042c6d380": "huobi-btc",
"0x4bd70556ae3f8a6ec6c4080a0c327b24325438f3": "hxro",
"0xe28b3b32b6c345a34ff64674606124dd5aceca30": "injective-protocol",
"0x8a9c67fee641579deba04928c4bc45f66e26343a": "jarvis-reward-token",
"0x85eee30c52b0b379b046fb0f85f4f3dc3009afec": "keep-network",
"0x5a98fcbea516cf06857215779fd812ca3bef1b32": "lido-dao",
"0x514910771af9ca656af840dff83e8264ecf986ca": "chainlink",
"0x0f5d2fb29fb7d3cfee444a200298f468908cc942": "decentraland",
"0x08d967bb0134f2d07f7cfb6e246680c53927dd30": "math",
"0xe831f96a7a1dce1aa2eb760b1e296c6a74caa9d5": "nexum",
"0xdfdb7f72c1f195c5951a234e8db9806eb0635346": "feisty-doge-nft",
"0x727f064a78dc734d33eec18d5370aef32ffd46e4": "orion-money",
"0x45804880de22913dafe09f4980848ece6ecbaf78": "pax-gold",
"0x65e6b60ea01668634d68d0513fe814679f925bad": "pixelverse",
"0xf1f955016ecbcd7321c7266bccfb96c68ea5e49b": "rally-2",
"0x3845badade8e6dff049820680d1f14bd3903a5d0": "the-sandbox",
"0x30d20208d987713f46dfd34ef128bb16c404d10f": "stader",
"0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce": "shiba-inu",
"0x5ab6a4f46ce182356b6fa2661ed8ebcafce995ad": "sportium",
"0x476c5e26a75bd202a9683ffd34359c0cc15be0ff": "serum",
"0x6b3595068778dd592e39a122f4f5a5cf09c90fe2": "sushi",
"0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9": "swipe",
"0x2e95cea14dd384429eb3c4331b776c4cfbb6fcd9": "throne",
"0x05d3606d5c81eb9b7b18530995ec9b29da05faba": "tomoe",
"0x2c537e5624e4af88a7ae4060c022609376c8d0eb": "bilira",
"0x8564653879a18c560e7c0ea0e084c516c62f5653": "upbots",
"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984": "uniswap",
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "usd-coin",
"0xdac17f958d2ee523a2206206994597c13d831ec7": "tether",
"0x0c572544a4ee47904d54aaa6a970af96b6f00e1b": "wasder",
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": "wrapped-bitcoin",
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2": "ethereum",
"0x72b886d09c117654ab7da13a14d603001de0b777": "xdefi",
"0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e": "yearn-finance",
"0x1a7e4e63778b4f12a199c062f3efdd288afcbce8": "ageur",
"0x707f9118e33a9b8998bea41dd0d46f38bb963fc8": "ethereum",
"0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0": "wrapped-steth",
"0xa2cd3d43c775978a96bdbf12d733d5a1ed94fb18": "chain-2"
},
"3": {
"terra193c42lfwmlkasvcw22l9qqzc5q2dx208tkd7wl": "bitlocus",
"uluna": "terra-luna",
"terra13awdgcx40tz5uygkgm79dytez3x87rpg4uhnvu": "playnity",
"uusd": "terrausd",
"terra1hzh9vpxhsk8253se0vv5jj6etdvxu3nv8z07zu": "anchorust"
},
"4": {
"0x7e46d5eb5b7ca573b367275fee94af1945f5b636": "abitshadow-token",
"0xe9e7cea3dedca5984780bafc599bd69add087d56": "binance-usd",
"0x8ebc361536094fd5b4ffb8521e31900614c9f55d": "darcmatter-coin",
"0x2170ed0880ac9a755fd29b2688956bd959f933f8": "weth",
"0x3019bf2a2ef8040c242c9a4c5c4bd4c81678b2a1": "stepn",
"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d": "usd-coin",
"0x55d398326f99059ff775485246999027b3197955": "tether",
"0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c": "wbnb",
"0xfafd4cb703b25cb22f43d017e7e0d75febc26743": "weyu",
"0xfa40d8fc324bcdd6bbae0e086de886c571c225d4": "wizardia"
},
"5": {
"0x9c891326fd8b1a713974f73bb604677e1e63396d": "islamicoin",
"0x2791bca1f2de4661ed88a30c99a7a9449aa84174": "usd-coin",
"0xc2132d05d31c914a87c6611c10748aeb04b58e8f": "tether",
"0x7ceb23fd6bc0add59e62ac25578270cff1b9f619": "weth",
"0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270": "matic-network"
},
"6": {
"0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": "usd-coin",
"0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664": "usd-coin",
"0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7": "tether",
"0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7": "avalanche-2",
"0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be": "benqi-liquid-staked-avax"
},
"7": {
"0x94fbffe5698db6f54d6ca524dbe673a7729014be": "usd-coin",
"0x366ef31c8dc715cbeff5fa54ad106dc9c25c6153": "tether-usd-wormhole-from-bsc",
"0x3223f17957ba502cbe71401d55a0db26e5f7c68f": "ethereum-wormhole",
"0x21c718c22d52d0f3a789b752d4c2fd5908a8a733": "oasis-network"
},
"8": {
"0": "algorand",
"31566704": "usd-coin",
"312769": "tether"
},
"9": {
"0x8BEc47865aDe3B172A928df8f990Bc7f2A3b9f79": "aurora",
"0xE4B9e004389d91e4134a28F19BD833cBA1d994B6": "frax",
"0xb12bfca5a55806aaf64e99521918a4bf0fc40802": "usd-coin",
"0x4988a896b1227218e4a686fde5eabdcabd91571f": "tether",
"0x5183e1b1091804bc2602586919e6880ac1cf2896": "usn",
"0xc9bdeed33cd01541e1eed10f90519d2c06fe3feb": "weth",
"0xc4bdd27c33ec7daa6fcfd8532ddb524bf4038096": "wrapped-terra"
},
"10": {
"0x321162cd933e2be498cd2267a90534a804051b11": "wrapped-bitcoin",
"0x74b23882a30290451a17c44f4f05243b6b58c76d": "weth",
"0x260b3e40c714ce8196465ec824cd8bb915081812": "iron-bsc",
"0x04068da6c83afcfa0e13ba15a6696662335d5b75": "usd-coin",
"0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83": "wrapped-fantom"
},
"11": {
"0x0000000000000000000100000000000000000080": "karura",
"0x0000000000000000000100000000000000000082": "kusama",
"0x0000000000000000000500000000000000000007": "tether",
"0x0000000000000000000100000000000000000081": "acala-dollar"
},
"12": {
"0x0000000000000000000100000000000000000000": "acala",
"0x0000000000000000000100000000000000000002": "polkadot",
"0x0000000000000000000100000000000000000001": "acala-dollar"
},
"13": {
"0x5c74070fdea071359b86082bd9f9b3deaafbe32b": "dai",
"0x5fff3a6c16c2208103f318f4713d4d90601a7313": "kleva",
"0x5096db80b21ef45230c9e423c373f1fc9c0198dd": "wemix-token",
"0xe4f05a66ec68b54a58b17c22107b02e0232cc817": "klay-token",
"0xcee8faf64bb97a73bb51e115aa89c17ffa8dd167": "tether"
},
"14": {
"0x471ece3750da237f93b8e339c536989b8978a438": "celo",
"0x46c9757c5497c5b1f2eb73ae79b6b67d119b0b58": "impactmarket",
"0xd8763cba276a3738e6de85b4b3bf5fded6d6ca73": "celo-euro",
"0x765de816845861e75a25fca122bb6898b8b1282a": "celo-dollar"
},
"15": {
"near": "near",
"token.sweat": "sweatcoin"
},
"16": {
"0xacc15dc74880c9944775448304b263d191c6077f": "moonbeam"
},
"18": {
"uluna": "terra-luna-2"
},
"22": {
"0x1::aptos_coin::AptosCoin": "aptos"
},
"28": {
"axpla": "xpla"
}
}

View File

@ -1,270 +0,0 @@
import { formatUnits } from "ethers/lib/utils";
import { BigNumber } from "ethers";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import { getEmitterAddressAlgorand } from "@certusone/wormhole-sdk";
import { calcLogicSigAccount } from "@certusone/wormhole-sdk/lib/cjs/algorand";
export const ALGORAND_HOST = {
algodToken: "",
algodServer: "https://mainnet-api.algonode.cloud", //"https://mainnet-idx.algonode.cloud", //"https://mainnet-api.algonode.cloud",
algodPort: "",
};
import { CHAIN_INFO_MAP, DISALLOWLISTED_ADDRESSES, sleepFor } from "./utils";
require("dotenv").config();
import allowList = require("./allowList.json");
import algosdk from "algosdk";
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
async function findAlgoMetadata(provider, tokenAddress) {
const index = parseInt(tokenAddress);
if (index === 0) {
const definition = {
tokenAddress: tokenAddress,
decimals: Number(6),
name: "ALGO",
symbol: "ALGO",
};
return definition;
}
let decimals = undefined;
let name = undefined;
let symbol = undefined;
try {
const resp = await provider.getAssetByID(index).do();
decimals = resp.params.decimals;
name = resp.params.name;
symbol = resp.params["unit-name"];
} catch {
console.error(`could not get ${index} metadata`);
}
return {
tokenAddress: tokenAddress,
decimals: decimals,
name: name,
symbol: symbol,
};
}
export async function getNativeAlgoAddress(
algoClient: any,
token_bridge: any,
assetId: any
) {
const { doesExist, lsa } = await calcLogicSigAccount(
algoClient,
BigInt(token_bridge),
BigInt(assetId),
Buffer.from("native", "binary").toString("hex")
);
return lsa.address();
}
async function getBridgeBalanceOnChain(chainInfo, tokenList: any[]) {
const bridgeAddress = chainInfo.token_bridge_address;
var tokenAccounts = [];
const provider = new algosdk.Algodv2(
ALGORAND_HOST.algodToken,
ALGORAND_HOST.algodServer,
ALGORAND_HOST.algodPort
);
tokenAccounts = [];
let i = 0;
while (i < tokenList.length) {
let tokenAddress = tokenList[i].tokenAddress;
const tokenInfo = await findAlgoMetadata(provider, tokenAddress);
let balance = BigNumber.from("0");
try {
let nativeAlgoAddr = await getNativeAlgoAddress(
provider,
bridgeAddress,
parseInt(tokenAddress)
);
let nativeAlgoInfo = await provider
.accountInformation(nativeAlgoAddr)
.do();
if (tokenAddress == 0) {
// native ALGO balance
balance = nativeAlgoInfo?.amount;
} else {
// other native asset
if (nativeAlgoInfo?.assets.length == 0) {
balance = BigNumber.from("0");
} else {
balance = nativeAlgoInfo?.assets[0].amount;
}
}
} catch {
console.error(`could not get ${tokenAddress} balance`);
}
tokenAccounts.push({
tokenAddress: tokenAddress,
name: tokenInfo.name,
decimals: tokenInfo.decimals,
symbol: tokenInfo.symbol,
balance: balance,
});
i++;
}
return tokenAccounts;
}
export async function getAlgoTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
console.log(chainId);
var tokenAccounts = [];
if (useAllowList) {
const allowList = getAllowList(chainId);
var tokenList = [];
Object.keys(allowList).forEach((address) => {
tokenList.push({
tokenAddress: address,
});
});
tokenAccounts = await getBridgeBalanceOnChain(chainInfo, tokenList);
// console.log("tokenAccounts", tokenAccounts);
} /*else {
tokenAccounts = await getBridgeBalanceScanner(chainInfo);
}*/
return tokenAccounts;
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
console.log("allowlist?", useAllowList);
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
} else {
// use tokenAddresses from tokenInfos/custody
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x.tokenAddress)
);
const sorted = balancesUSDFiltered.sort((a, b) =>
a.tokenBalanceUSD < b.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
return [];
}
export async function getAlgoCustody(chainInfo, useAllowList = true) {
const tokenAccounts = await getAlgoTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo.platform} token accounts=${tokenAccounts.length}`
);
const custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo.platform} token accounts=${custody.length}`
);
return custody;
}
export async function grabAlgoCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getAlgoCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,
emitter_address: getEmitterAddressAlgorand(
BigInt(chainInfo.token_bridge_address)
),
balances: balances,
};
return chainInfo_;
}
// const chain = process.env.chain;
// const useAllowListstr = process.env.allowlist || "false";
// (async () => {
// const chainInfo = CHAIN_INFO_MAP[chain];
// const useAllowList = true ? useAllowListstr === "true" : false;
// const balances = await getAlgoCustody(chainInfo, useAllowList);
// console.log(balances);
// })();

View File

@ -1,233 +0,0 @@
import { formatUnits } from "ethers/lib/utils";
import { BigNumber } from "ethers";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import allowList = require("./allowList.json");
import { AptosClient } from "aptos";
import {
getTypeFromExternalAddress,
isValidAptosType,
} from "@certusone/wormhole-sdk";
import { CHAIN_INFO_MAP, DISALLOWLISTED_ADDRESSES } from "./utils";
require("dotenv").config();
async function findAptosMetadata(provider, tokenAddress) {
let decimals = 0;
let name = undefined;
let symbol = undefined;
try {
const coinInfo = await provider.getAccountResource(
tokenAddress.split("::")[0],
`0x1::coin::CoinInfo<${tokenAddress}>`
);
const metaData = coinInfo.data;
decimals = metaData["decimals"];
name = metaData["name"];
symbol = metaData["symbol"];
} catch (e) {
console.log(`could not find meta_data for address=${tokenAddress}`);
}
return {
tokenAddress: tokenAddress,
decimals: Number(decimals),
name: name,
symbol: symbol,
};
}
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
async function getBridgeBalanceOnChain(chainInfo, tokenList: any[]) {
const bridgeAddress = chainInfo.token_bridge_address;
var tokenAccounts = [];
const provider = new AptosClient(chainInfo.endpoint_url);
tokenAccounts = [];
let i = 0;
while (i < tokenList.length) {
let tokenAddress = tokenList[i].tokenAddress;
let tokenAddressQualified = tokenAddress;
if (!isValidAptosType(tokenAddress)) {
tokenAddressQualified = await getTypeFromExternalAddress(
provider,
bridgeAddress,
tokenAddress
);
console.log("converting hash to aptos type", tokenAddressQualified);
}
const tokenInfo = await findAptosMetadata(provider, tokenAddressQualified);
let balance = BigNumber.from("0");
try {
const accountResource = await provider.getAccountResource(
bridgeAddress,
`0x1::coin::CoinStore<${tokenAddressQualified}>`
);
balance = accountResource?.data["coin"]["value"];
} catch {
console.error(`could not get aptos balance for ${tokenAddressQualified}`);
}
tokenAccounts.push({
tokenAddress: tokenAddress,
name: tokenInfo.name,
decimals: tokenInfo.decimals,
symbol: tokenInfo.symbol,
balance: BigNumber.from(balance),
});
i++;
}
return tokenAccounts;
}
export async function getAptosTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
console.log(chainId);
var tokenAccounts = [];
if (useAllowList) {
const allowList = getAllowList(chainId);
var tokenList = [];
Object.keys(allowList).forEach((address) => {
tokenList.push({
tokenAddress: address,
});
});
tokenAccounts = await getBridgeBalanceOnChain(chainInfo, tokenList);
} /*else {
// pull from transactions table
tokenAccounts = await getBridgeBalanceScanner(chainInfo);
}*/
return tokenAccounts;
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
console.log("allowlist?", useAllowList);
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
} else {
// use tokenAddresses from tokenInfos/custody
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x.tokenAddress)
);
const sorted = balancesUSDFiltered.sort((a, b) =>
a.tokenBalanceUSD < b.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
return [];
}
export async function getAptosCustody(chainInfo, useAllowList = true) {
const tokenAccounts = await getAptosTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo.platform} token accounts=${tokenAccounts.length}`
);
const custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo.platform} token accounts=${custody.length}`
);
return custody;
}
export async function grabAptosCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getAptosCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,
emitter_address:
"0000000000000000000000000000000000000000000000000000000000000001",
balances: balances,
};
return chainInfo_;
}
// const chain = process.env.chain;
// const useAllowListstr = process.env.allowlist || "false";
// (async () => {
// const chainInfo = CHAIN_INFO_MAP[chain];
// const useAllowList = true ? useAllowListstr === "true" : false;
// const balances = await getAptosCustody(chainInfo, useAllowList);
// console.log(balances);
// })();

View File

@ -1,132 +0,0 @@
import axios from "axios";
import { COIN_GECKO_EXCEPTIONS } from "./utils";
const CoinGecko = require("coingecko-api");
function sleep(milliseconds) {
const date = Date.now();
let currentDate = 0;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
//2. Initiate the CoinGecko API Client
const CoinGeckoClient = new CoinGecko(); //use justin's VIP coin gecko api key?
async function getCoinsList() {
let data = await CoinGeckoClient.coins.list();
//console.log(data)
return;
}
export async function getTokenPrice(tokenAddress) {
let tokenContractInfo = await CoinGeckoClient.coins.fetchCoinContractInfo(
tokenAddress
);
let data = tokenContractInfo["data"];
var price = 0;
try {
price = data["market_data"]["current_price"].usd;
} catch (e) {
console.log("could not find price for address=", tokenAddress);
}
return price;
}
export async function getTokenPrices(tokenAddresses) {
//only for ethereum chain
let tokenContractInfos;
try {
tokenContractInfos = await CoinGeckoClient.simple.fetchTokenPrice({
contract_addresses: tokenAddresses,
vs_currencies: "usd",
});
} catch (e) {
console.log(e);
console.log("could not find prices for addresses");
}
return tokenContractInfos["data"];
}
function getKeyByValue(object, value) {
return Object.keys(object).find((key) => object[key] === value);
}
export async function getCoinGeckoMap() {
// pull coin gecko mapping of token address to coingecko id
var coinMap;
const map_query = `https://api.coingecko.com/api/v3/coins/list?include_platform=true`;
try {
const req = await axios.get(map_query);
coinMap = req.data;
} catch (e) {
console.log(e);
console.log("could not find prices for addresses");
}
var coinMapTransformed: any[] = [];
for (let i = 0; i < coinMap.length; i++) {
const coin = coinMap[i];
const platforms = coin?.platforms;
Object.entries(platforms).forEach((entry) => {
const [platform, contractAddress] = entry;
if (contractAddress != "" && contractAddress != null) {
coinMapTransformed.push({
contractAddress: contractAddress,
coinGeckoId: coin.id,
});
}
});
}
return coinMapTransformed;
}
export async function getTokenPricesGET(
chainId: number,
platform: String,
tokenAddresses: String[]
) {
let data;
const addresses = tokenAddresses.join("%2C");
const price_query = `https://api.coingecko.com/api/v3/simple/token_price/${platform}?contract_addresses=${addresses}&vs_currencies=usd`;
//console.log(price_query)
try {
const tokenContractInfos = await axios.get(price_query);
data = tokenContractInfos.data;
} catch (e) {
console.log(e);
console.log("could not find prices for addresses");
}
// find prices included in exceptions using coin gecko id and add it back to the results
const filteredIds = COIN_GECKO_EXCEPTIONS.filter(
(x) => tokenAddresses.includes(x.tokenAddress) && x.chainId == chainId
);
const coinGeckoIds = filteredIds.map((x) => x.coinGeckoId);
const additional_data = await getTokenPricesCGID(coinGeckoIds);
let gecko_id_data = {};
for (let i = 0; i < filteredIds.length; i++) {
let cg_id = filteredIds[i];
gecko_id_data[cg_id.tokenAddress] = additional_data[cg_id.coinGeckoId];
}
return { ...data, ...gecko_id_data };
}
export async function getTokenPricesCGID(coinGeckoIds: String[]) {
let data;
const ids = coinGeckoIds.join("%2C");
const price_query = `https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd`;
console.log(price_query);
try {
const tokenContractInfos = await axios.get(price_query);
data = tokenContractInfos.data;
} catch (e) {
// console.log(e);
console.log("could not find prices for ids", coinGeckoIds);
}
return data;
}

View File

@ -1,137 +0,0 @@
import { grabTerraCustodyData } from "./getTerraCustody";
import { grabSolanaCustodyData } from "./getSolanaCustody";
import { grabEvmCustodyData } from "./getEvmCustody";
import { MongoClient } from "mongodb";
import { grabNearCustodyData } from "./getNearCustody";
import { grabAlgoCustodyData } from "./getAlgorandCustody";
import { grabAptosCustodyData } from "./getAptosCustody";
import { sleepFor } from "./utils";
interface Token {
tokenAddress: string;
name: string;
decimals: number;
symbol: string;
balance: BigInt;
qty: number;
tokenPrice: number;
tokenBalanceUSD: number;
}
interface CustodyInfo {
_id: string;
updatedAt: string;
chainName: string;
chainId: number;
emitterAddress: string;
custodyUSD: number;
tokens: Token[];
}
async function updateTable(chainInfo, client: MongoClient) {
if (chainInfo === undefined) {
return;
}
const custodyList = chainInfo.balances;
if (custodyList.length === 0) {
return;
}
try {
const totalCustodyUSD = custodyList
.map((x) => x.tokenBalanceUSD)
.reduce((partialSum, a) => partialSum + a, 0);
console.log("totalCustodyUSD=", totalCustodyUSD);
const database = client.db("onchain_data");
// Specifying a Schema is optional, but it enables type hints on
// finds and inserts
const chainId = chainInfo.chain_id;
const emitterAddress = chainInfo.emitter_address;
const custody = database.collection<CustodyInfo>("custody");
const result = await custody.updateOne(
{ _id: `${chainId}/${emitterAddress}` },
{
$set: {
updatedAt: new Date().toISOString(),
chainName: chainInfo.name,
chainId: chainId,
emitterAddress: emitterAddress,
custodyUSD: totalCustodyUSD,
tokens: custodyList,
_id: `${chainId}/${emitterAddress}`,
},
},
{ upsert: true }
);
console.log(`A document was inserted with the _id: ${result.upsertedId}`);
} catch (e) {
console.log(encodeURIComponent);
}
return;
}
const useAllowListstr = process.env.allowlist || "false";
export async function getCustodyData() {
const uri = process.env.MONGODB_URI;
if (uri === "" || uri === undefined) {
console.log("No mongodb uri supplied");
return -1;
}
const client = new MongoClient(uri);
const useAllowList = true ? useAllowListstr === "true" : false;
const timeout = 5000;
const promises = [
grabSolanaCustodyData("1", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("2", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabTerraCustodyData("3", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("4", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("5", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("6", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("7", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabAlgoCustodyData("8", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("9", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("10", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("11", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("12", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("13", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("14", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabNearCustodyData("15", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabEvmCustodyData("16", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabTerraCustodyData("18", useAllowList),
await new Promise((res) => setTimeout(res, timeout)),
grabAptosCustodyData("22", useAllowList),
// grabTerraCustodyData("28", useAllowList),
];
const output = await Promise.all(promises);
// iterate through chains & insert into mongodb
try {
for (let i = 0; i < output.length; i++) {
const data = output[i];
await updateTable(data, client);
}
} catch (e) {
console.log(e);
} finally {
await client.close();
}
}
export default getCustodyData;

View File

@ -1,375 +0,0 @@
import { formatUnits } from "ethers/lib/utils";
import { ethers } from "ethers";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import {
CHAIN_ID_OASIS,
isEVMChain,
CHAIN_ID_KARURA,
CHAIN_ID_ACALA,
CHAIN_ID_CELO,
getEmitterAddressEth,
} from "@certusone/wormhole-sdk";
import { abi as Erc20Abi } from "./abi/erc20.json";
import axios from "axios";
import {
DISALLOWLISTED_ADDRESSES,
CHAIN_INFO_MAP,
newProvider,
sleepFor,
} from "./utils";
// current allowlist used for stats/govenor
import allowList = require("./allowList.json");
require("dotenv").config();
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
async function getBridgeBalanceScanner(chainInfo) {
const chainId = chainInfo.chain_id;
const bridgeAddress = chainInfo.token_bridge_address;
const url = chainInfo.urlStem;
// Get native token balances locked in contract
const balance_apireqstring = `${url}/api?module=account&action=balance&address=${bridgeAddress}`;
const balance_resp = await axios.get(balance_apireqstring);
var tokenAccounts = [];
if (balance_resp.status == 200) {
const data = balance_resp["data"];
if (data.hasOwnProperty("result")) {
const balance = data["result"];
var nativeInfo = {};
var symbol = "";
if (chainId == 7) {
symbol = "ROSE";
} else if (chainId == 11) {
symbol = "KAR";
} else if (chainId == 12) {
symbol: "ACA";
} else if (chainId == 14) {
symbol: "CELO";
}
nativeInfo = {
decimals: 18,
name: symbol,
tokenAddress: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee2",
balance: balance,
symbol: symbol,
};
tokenAccounts.push(nativeInfo);
} else {
console.log("object has no native balance data");
}
} else {
console.log(balance_resp.status);
}
// Get locked token list from scan site
const apiReqString = `${url}/api?module=account&action=tokenlist&address=${bridgeAddress}`;
console.log(apiReqString);
const response = await axios.get(apiReqString);
if (response.status == 200) {
const data = response["data"];
if (data.hasOwnProperty("result")) {
const tokenData = data["result"];
tokenAccounts = [
...tokenAccounts,
...tokenData.map((x) => ({
decimals: x.decimals,
name: x.name,
tokenAddress: x.contractAddress,
balance: x.balance,
symbol: x.symbol,
})),
];
return tokenAccounts;
} else {
console.log("object has no data");
return [];
}
} else {
console.log(response.status);
return [];
}
}
async function getBridgeBalanceCovalent(chainInfo) {
/* Get token list (also has bridge balance and prices, but could be stale, so will query onchain)*/
const covalentChainId: string = chainInfo.covalentChain;
const bridgeAddress: string = chainInfo.token_bridge_address;
const covalentApiKey = process.env.REACT_APP_COVALENT_API_KEY;
const apiReqString = `https://api.covalenthq.com/v1/${covalentChainId}/address/${bridgeAddress}/balances_v2/?quote-currency=USD&format=JSON&nft=true&no-nft-fetch=false&key=${covalentApiKey}`;
console.log(apiReqString);
const response = await axios.get(apiReqString, {
headers: { "User-Agent": "Mozilla/5.0" },
});
if (response.status == 200) {
const data = response["data"];
if (data.hasOwnProperty("data")) {
const tokenData = data["data"];
// tokenData["items"].forEach((x) => console.log(x));
const tokenAccounts = tokenData["items"]
.filter((item) => item.type !== "nft")
.map((x) => ({
decimals: x.contract_decimals,
name: x.contract_name,
tokenAddress: x.contract_address,
balance: x.balance,
price: x.quote_rate,
}));
return tokenAccounts;
} else {
console.log("object has no data");
return [];
}
} else {
console.log(response.status);
return [];
}
}
//for evm chains
async function getTokenContract(
address: string,
provider:
| ethers.providers.JsonRpcProvider
| ethers.providers.JsonRpcBatchProvider
) {
const contract = new ethers.Contract(address, Erc20Abi, provider);
return contract;
}
async function getBridgeBalanceOnChain(chainInfo, tokenList: any[]) {
const bridgeAddress = chainInfo.token_bridge_address;
let provider = newProvider(
chainInfo.endpoint_url,
true
) as ethers.providers.JsonRpcBatchProvider;
var tokenAccounts = [];
let i = 0;
let chunksize = 100;
while (i < tokenList.length) {
const tokenContracts = await Promise.all(
tokenList
.slice(i, i + chunksize)
.map((token) => getTokenContract(token.tokenAddress, provider))
);
const tokenInfos = await Promise.all(
tokenContracts.map((tokenContract) =>
Promise.all([
tokenContract.address.toLowerCase(),
tokenContract.name(),
tokenContract.decimals(),
tokenContract.symbol(),
tokenContract.balanceOf(bridgeAddress),
])
)
);
tokenInfos.forEach((tokenInfo) => {
tokenAccounts.push({
tokenAddress: tokenInfo[0],
name: tokenInfo[1],
decimals: tokenInfo[2],
symbol: tokenInfo[3],
balance: tokenInfo[4],
});
});
i += chunksize;
}
return tokenAccounts;
}
export async function getEvmTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
console.log(chainId);
if (!isEVMChain(chainId)) {
console.log(`error. ${chainId} is not evm chain`);
return [];
}
var tokenAccounts = [];
if (useAllowList) {
const allowList = getAllowList(chainId);
var tokenList = [];
Object.keys(allowList).forEach((address) => {
tokenList.push({
tokenAddress: address,
});
});
// console.log(tokenList);
tokenAccounts = await getBridgeBalanceOnChain(
chainInfo,
tokenList.filter(
(token) =>
token.tokenAddress.toLowerCase() !=
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" // filter out native token
)
);
// console.log("tokenAccounts", tokenAccounts);
} else {
if (
chainId == CHAIN_ID_OASIS ||
chainId == CHAIN_ID_KARURA ||
chainId == CHAIN_ID_ACALA ||
chainId == CHAIN_ID_CELO
) {
tokenAccounts = await getBridgeBalanceScanner(chainInfo);
} else {
// use convalent to get token addresses
const tokenList = await getBridgeBalanceCovalent(chainInfo);
// console.log(tokenList.filter((token) => console.log(token)));
tokenAccounts = await getBridgeBalanceOnChain(
chainInfo,
tokenList.filter(
(token) =>
token.tokenAddress.toLowerCase() !=
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" // native token
)
);
// console.log(tokenAccounts);
}
}
return tokenAccounts;
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
console.log("allowlist?", useAllowList);
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
if (prices === undefined) {
console.log(`could not find ids for ${chainInfo.chain_id}`);
return [];
}
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
// console.log(tokenPrices);
} else {
// use tokenAddresses from tokenInfos/custody
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x.tokenAddress)
);
const sorted = balancesUSDFiltered.sort((a, b) =>
a.tokenBalanceUSD < b.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
return [];
}
export async function getEvmCustody(chainInfo, useAllowList = true) {
const tokenAccounts = await getEvmTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo.platform} token accounts=${tokenAccounts.length}`
);
const custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo.platform} token accounts=${custody.length}`
);
return custody;
}
export async function grabEvmCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getEvmCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,
emitter_address: getEmitterAddressEth(chainInfo.token_bridge_address),
balances: balances,
};
return chainInfo_;
}
// const chain = process.env.chain;
// const useAllowListstr = process.env.chain || "false";
// (async () => {
// const chainInfo = CHAIN_INFO_MAP[chain];
// const useAllowList = true ? useAllowListstr === "true" : false;
// const balances = await getEvmCustody(chainInfo, useAllowList);
// console.log(balances);
// await updateTable(chainInfo, balances);
// })();

View File

@ -1,240 +0,0 @@
import { formatUnits } from "ethers/lib/utils";
import { BigNumber } from "ethers";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import {
ChainId,
CHAIN_ID_NEAR,
getEmitterAddressNear,
} from "@certusone/wormhole-sdk";
import {
connect as nearConnect,
keyStores as nearKeyStores,
utils as nearUtils,
Account as nearAccount,
providers as nearProviders,
} from "near-api-js";
import { CHAIN_INFO_MAP, DISALLOWLISTED_ADDRESSES, sleepFor } from "./utils";
require("dotenv").config();
async function findNearMetadata(userAccount, tokenAddress) {
if (tokenAddress === "near") {
//transfering native near
return {
decimals: 24,
name: "NEAR",
symbol: "NEAR",
};
} else {
const meta_data = await userAccount.viewFunction({
contractId: tokenAddress,
methodName: "ft_metadata",
args: {},
});
return meta_data;
}
}
import allowList = require("./allowList.json");
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
async function getBridgeBalanceOnChain(chainInfo, tokenList: any[]) {
const bridgeAddress = chainInfo.token_bridge_address;
// const provider = new nearProviders.JsonRpcProvider(chainInfo.endpoint_url);
const ACCOUNT_ID = "sender.mainnet";
var tokenAccounts = [];
let near = await nearConnect({
headers: {},
networkId: "mainnet",
nodeUrl: chainInfo.endpoint_url,
});
tokenAccounts = [];
const userAccount = new nearAccount(near.connection, bridgeAddress);
let i = 0;
while (i < tokenList.length) {
let tokenAddress = tokenList[i].tokenAddress;
const tokenInfo = await findNearMetadata(userAccount, tokenAddress);
let balance = BigNumber.from("0");
if (tokenAddress === "near") {
const nearBalanceInfo = await userAccount.getAccountBalance();
balance = BigNumber.from(nearBalanceInfo.total);
} else {
balance = await userAccount.viewFunction({
contractId: tokenAddress,
methodName: "ft_balance_of",
args: {
account_id: "contract.portalbridge.near",
},
});
}
tokenAccounts.push({
tokenAddress: tokenAddress,
name: tokenInfo.name,
decimals: tokenInfo.decimals,
symbol: tokenInfo.symbol,
balance: balance,
});
i++;
}
return tokenAccounts;
}
export async function getNearTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
console.log(chainId);
var tokenAccounts = [];
if (useAllowList) {
const allowList = getAllowList(chainId);
var tokenList = [];
Object.keys(allowList).forEach((address) => {
tokenList.push({
tokenAddress: address,
});
});
tokenAccounts = await getBridgeBalanceOnChain(chainInfo, tokenList);
// console.log("tokenAccounts", tokenAccounts);
} /*else {
tokenAccounts = await getBridgeBalanceScanner(chainInfo);
}*/
return tokenAccounts;
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
console.log("allowlist?", useAllowList);
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
if (prices === undefined) {
console.log(`could not find ids for ${chainInfo.chain_id}`);
return [];
}
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
} else {
// use tokenAddresses from tokenInfos/custody
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x.tokenAddress)
);
const sorted = balancesUSDFiltered.sort((a, b) =>
a.tokenBalanceUSD < b.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
return [];
}
export async function getNearCustody(chainInfo, useAllowList = true) {
const tokenAccounts = await getNearTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo.platform} token accounts=${tokenAccounts.length}`
);
const custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo.platform} token accounts=${custody.length}`
);
return custody;
}
export async function grabNearCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getNearCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Near data");
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,
emitter_address: getEmitterAddressNear(chainInfo.token_bridge_address),
balances: balances,
};
return chainInfo_;
}
// const chain = process.env.chain;
// const useAllowListstr = process.env.allowlist || "false";
// (async () => {
// const chainInfo = CHAIN_INFO_MAP[chain];
// const useAllowList = true ? useAllowListstr === "true" : false;
// const balances = await getNearCustody(chainInfo, useAllowList);
// console.log(balances);
// })();

View File

@ -1,338 +0,0 @@
import { formatUnits } from "ethers/lib/utils";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, AccountInfo, PublicKey } from "@solana/web3.js";
import { Connection as ConnectionMeta, programs } from "@metaplex/js";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import { CHAIN_INFO_MAP, DISALLOWLISTED_ADDRESSES, sleepFor } from "./utils";
// current allowlist used for stats/govenor
import allowList = require("./allowList.json");
import { getEmitterAddressSolana } from "@certusone/wormhole-sdk";
import { BigNumber } from "ethers";
require("dotenv").config();
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
const {
metadata: { Metadata },
} = programs;
export async function getMultipleAccountsRPC(
connection: Connection,
pubkeys: PublicKey[]
): Promise<(AccountInfo<Buffer> | null)[]> {
return getMultipleAccounts(connection, pubkeys, "confirmed");
}
export const getMultipleAccounts = async (
connection: any,
pubkeys: PublicKey[],
commitment: string
) => {
return (
await Promise.all(connection.getMultipleAccountsInfo(pubkeys, commitment))
).flat();
};
export function shortenAddress(address: string) {
return address.length > 10
? `${address.slice(0, 4)}...${address.slice(-4)}`
: address;
}
export const METADATA_REPLACE = new RegExp("\u0000", "g");
export const EDITION_MARKER_BIT_SIZE = 248;
export const METADATA_PREFIX = "metadata";
export const EDITION = "edition";
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export type StringPublicKey = string;
export enum MetadataKey {
Uninitialized = 0,
MetadataV1 = 4,
EditionV1 = 1,
MasterEditionV1 = 2,
MasterEditionV2 = 6,
EditionMarker = 7,
}
export const METADATA_PROGRAM_ID =
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" as StringPublicKey;
export const getMetadataAddress = async (
mintKey: string
): Promise<[PublicKey, number]> => {
const seeds = [
Buffer.from("metadata"),
new PublicKey(METADATA_PROGRAM_ID).toBuffer(),
new PublicKey(mintKey).toBuffer(),
];
return PublicKey.findProgramAddress(
seeds,
new PublicKey(METADATA_PROGRAM_ID)
);
};
async function getSolanaMetaData(
mintAddresses: string[],
connection: Connection
) {
const metaAddresses = [];
for (let i = 0; i < mintAddresses.length; i++) {
let mintAddress = mintAddresses[i];
try {
const metaAddress = await getMetadataAddress(mintAddress);
metaAddresses.push({
mint: mintAddress,
meta: metaAddress[0].toString(),
});
} catch (e) {
continue;
}
}
let storeMetadata = {};
// Get store metadata
for (let i = 0; i < metaAddresses.length; i++) {
let mintkey = new PublicKey(metaAddresses[i].mint);
let pubkey = new PublicKey(metaAddresses[i].meta);
try {
const metadata = await Metadata.load(connection, pubkey);
const metadatadata = metadata.data?.data || {};
metadatadata["metakey"] = pubkey.toString();
metadatadata["tokenAddress"] = mintkey.toString();
storeMetadata[mintkey.toString()] = metadatadata;
} catch (e) {
continue;
}
}
return storeMetadata;
}
export async function getSolanaTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
const custodyAddress = chainInfo.custody_address;
const connection = new Connection(chainInfo.endpoint_url);
try {
var mintAddresses = [];
var tokenAccounts = [];
if (useAllowList) {
const allowList = getAllowList(chainId);
var mintAddresses = [];
Object.keys(allowList).forEach((address) => {
mintAddresses.push(address);
});
for (let i = 0; i < mintAddresses.length; i++) {
const mintAddress = mintAddresses[i];
const parsedAccount = await connection.getParsedTokenAccountsByOwner(
new PublicKey(custodyAddress),
{
mint: new PublicKey(mintAddress),
}
);
const tokenAccount_ = parsedAccount.value.at(-1);
const tokenAccount = tokenAccount_.account.data.parsed.info;
if (tokenAccount.tokenAmount?.amount > 0) {
// console.log(
// `token=${mintAddress} has a balance=${tokenAccount.tokenAmount?.amount}`
// );
tokenAccounts.push(tokenAccount);
} else {
console.log(`${tokenAccount} has a 0 balance`);
}
await sleepFor(1000);
}
} else {
const allAccounts = await connection.getParsedTokenAccountsByOwner(
new PublicKey(custodyAddress),
{ programId: TOKEN_PROGRAM_ID },
"confirmed"
);
allAccounts.value.forEach((account) => {
if (account.account.data.parsed?.info?.tokenAmount?.amount > 0) {
// get all token accounts with nonzero balance
tokenAccounts.push(account.account.data.parsed?.info);
}
});
// get mint addresses from token accounts and find metadata address
mintAddresses = tokenAccounts.map((x) => x.mint);
}
const storeMetadata = await getSolanaMetaData(mintAddresses, connection);
tokenAccounts = tokenAccounts.map((custody) => ({
...custody,
...custody["tokenAmount"],
...storeMetadata[custody.mint],
}));
// do a little tidying up
tokenAccounts = tokenAccounts.map((account) => ({
tokenAddress: account["tokenAddress"],
name: account["name"],
decimals: account["decimals"],
symbol: account["symbol"],
balance: BigNumber.from(account["amount"]),
}));
return tokenAccounts;
} catch (e) {
console.log(e);
}
return [];
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
if (prices === undefined) {
console.log(`could not find ids for ${chainInfo.chain_id}`);
return [];
}
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// console.log(prices);
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
// console.log(tokenPrices);
} else {
// use tokenAddresses from tokenInfos/custody
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
const prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
// console.log("tokenPrices", tokenPrices);
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x.tokenAddress)
);
var sorted = balancesUSDFiltered.sort((a, b) =>
a.tokenBalanceUSD < b.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
return [];
}
export async function getSolanaCustody(chainInfo, useAllowList = true) {
const endpoint = chainInfo.endpoint_url;
const bridgeAddress = chainInfo.token_bridge_address;
const connection = new Connection(endpoint);
const nativeBalance = await connection.getBalance(
new PublicKey(bridgeAddress)
);
// console.log("amount of sol in custody=", nativeBalance);
const solanaCustodyNativeUSD = 0; //solanaCustodyNative.map(x => (parseInt(x.balance) / (10.0 ** x.decimals) * x.price)).reduce((partialSum, a) => partialSum + a, 0);
// grab token accounts from rpc/allowlist
const tokenAccounts = await getSolanaTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo.platform} token accounts=${tokenAccounts.length}`
);
// tokenAccounts.forEach((x) => console.log(x));
const custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo.platform} token accounts=${custody.length}`
);
return custody;
}
export async function grabSolanaCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getSolanaCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Solana data");
}
if (balances.length === 0) {
console.log(`could not get ${chainInfo.name} custody data`);
}
const chainInfo_ = {
...chainInfo,
emitter_address:
"ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5",
balances: balances,
};
return chainInfo_;
}
// const useAllowListstr = process.env.chain || "false";
// (async () => {
// const useAllowList = true ? useAllowListstr === "true" : false;
// const chainInfo = CHAIN_INFO_MAP["1"];
// const balances = await getSolanaCustody(chainInfo, useAllowList);
// await updateTable(chainInfo, balances);
// })();

View File

@ -1,348 +0,0 @@
import { LCDClient } from "@terra-money/terra.js";
import { formatUnits } from "ethers/lib/utils";
import { getTokenPricesCGID, getTokenPricesGET } from "./getCoinGeckoPrices";
import {
CHAIN_ID_TERRA,
CHAIN_ID_TERRA2,
isNativeCosmWasmDenom,
getEmitterAddressTerra,
} from "@certusone/wormhole-sdk";
import axios from "axios";
import { DISALLOWLISTED_ADDRESSES, CHAIN_INFO_MAP } from "./utils";
// current allowlist used for stats/govenor
import allowList = require("./allowList.json");
import { BigNumber } from "ethers";
require("dotenv").config();
function formatTerraNative(address: string) {
let symbol = address.slice(1);
if (address != "uluna") {
symbol = symbol.slice(0, -1) + "t";
}
return symbol;
}
function getAllowList(chainId) {
if (Object.keys(allowList).includes(chainId.toString())) {
return allowList[chainId];
} else {
return [];
}
}
function calcTokenQty(tokenInfo) {
return Number(formatUnits(tokenInfo.balance, tokenInfo.decimals));
}
async function contractQuery(
address: string,
query: object,
host
): Promise<any> {
//return this.provider.wasm.contractQuery(address, query);
const lcd = new LCDClient(host);
let info = undefined;
try {
info = await lcd.wasm.contractQuery(address, query);
} catch (e) {
console.log("could not query contract info: ", address);
}
return info;
}
async function queryTokenInfo(address: string, host): Promise<any> {
return await contractQuery(
address,
{
token_info: {},
},
host
);
}
function getNativeTerraTokenInfo(address, chainId) {
if (address === "uluna") {
if (chainId === CHAIN_ID_TERRA) {
return {
name: "Luna Classic",
symbol: "LUNC",
decimals: 6,
tokenAddress: address,
};
} else {
return {
name: "Luna",
symbol: "LUNA",
decimals: 6,
tokenAddress: address,
};
}
} else if (address === "uusd") {
return {
name: "UST",
symbol: "UST",
decimals: 6,
tokenAddress: address,
};
}
return {};
}
export async function getTerraTokenAccounts(chainInfo, useAllowList) {
const chainId = chainInfo.chain_id;
const bridgeAddress = chainInfo.token_bridge_address;
var TERRA_HOST;
var network;
if (chainId == CHAIN_ID_TERRA) {
TERRA_HOST = {
URL: "https://columbus-lcd.terra.dev",
chainID: "columbus-5",
name: "mainnet",
};
network = "classic";
} else if (chainId == CHAIN_ID_TERRA2) {
TERRA_HOST = {
URL: "https://phoenix-lcd.terra.dev",
chainID: "phoenix-1",
name: "mainnet",
};
network = "mainnet";
}
const lcd = new LCDClient(TERRA_HOST);
var tokenList = [];
var tokenData = {};
if (useAllowList) {
const allowList = getAllowList(chainId);
Object.keys(allowList).forEach((address) => {
tokenList.push(address);
});
for (let i = 0; i < tokenList.length; i++) {
let address = tokenList[i];
var tokenInfo = undefined;
if (isNativeCosmWasmDenom(chainId, address)) {
console.log("isNativeCosmWasmDenom?", address);
tokenInfo = getNativeTerraTokenInfo(address, chainId);
} else {
tokenInfo = await queryTokenInfo(address, TERRA_HOST);
}
tokenData[address] = tokenInfo;
}
// console.log("tokenData", tokenData);
} else {
const token_url = "https://assets.terra.money/cw20/tokens.json";
const response = await axios.get(token_url);
if (response.status == 200) {
const data = response["data"];
if (data.hasOwnProperty(network)) {
tokenData = data[network];
tokenList = Object.keys(tokenData);
} else {
console.log("object has no data");
}
} else {
console.log(response.status);
}
}
let tokenAccounts = [];
let nativeTokens = [];
try {
const address = bridgeAddress;
} catch (e) {
console.log(e);
}
for (let i = 0; i < tokenList.length; i++) {
const tokenAddress = tokenList[i];
try {
let token = await lcd.wasm.contractQuery(tokenAddress, {
balance: { address: bridgeAddress },
});
let tokenInfo = tokenData[tokenAddress];
// console.log(tokenAddress, token, tokenInfo);
tokenAccounts.push({
tokenAddress: tokenAddress,
name: tokenInfo.name,
decimals: tokenInfo?.decimals || 6,
symbol: tokenInfo.symbol,
balance: BigNumber.from(token["balance"]),
});
} catch (e) {
console.log("could not find address=", tokenAddress);
}
}
// grab native tokens
const [balance] = await lcd.bank.balance(bridgeAddress);
balance.toData().forEach((token) => {
if (useAllowList) {
if (tokenList.includes(token.denom)) {
let tokenInfo = tokenData[token.denom];
tokenAccounts.push({
tokenAddress: token.denom,
name: tokenInfo.name,
decimals: tokenInfo?.decimals || 6,
symbol: tokenInfo.symbol,
balance: BigNumber.from(token.amount),
});
}
} else {
tokenAccounts.push({
tokenAddress: token.denom,
name: "native",
symbol: formatTerraNative(token.denom),
balance: token.amount,
decimals: "6",
});
}
});
// console.log("tokenAccounts=", tokenAccounts);
return tokenAccounts;
}
async function getTokenValues(chainInfo, tokenInfos: any[], useAllowList) {
try {
const custody = tokenInfos.map((tokenInfo) => ({
...tokenInfo,
qty: calcTokenQty(tokenInfo),
}));
const custodyFiltered = custody.filter((c) => c.qty > 0);
var tokenPrices = {};
var prices = [];
if (useAllowList) {
// use coingecko ids from allowlist
const allowList = getAllowList(chainInfo.chain_id);
const cgids: string[] = Object.values(allowList);
// input array of cgids, returns json with cgid:price
prices = await getTokenPricesCGID(cgids);
if (prices === undefined) {
console.log(`could not find ids for ${chainInfo.chain_id}`);
return [];
}
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
// have to map cgid: price to tokenAddress: price
for (const [key, value] of Object.entries(allowList)) {
for (const [key1, value1] of Object.entries(prices)) {
if (key1 === value) {
tokenPrices[key] = prices[key1];
}
}
}
} else {
let j = 0;
let chunk_size = 100;
while (j < custodyFiltered.length) {
prices = await getTokenPricesGET(
chainInfo.chain_id,
chainInfo.platform,
custodyFiltered.slice(j, j + chunk_size).map((x) => x.tokenAddress)
);
for (const [key, value] of Object.entries(prices)) {
if (!value.hasOwnProperty("usd")) {
prices[key] = { usd: 0 };
}
}
tokenPrices = { ...tokenPrices, ...prices };
j += chunk_size;
}
}
// filter list by those with coin gecko prices
const filteredBalances = custodyFiltered.filter((x) =>
Object.keys(tokenPrices).includes(x?.tokenAddress)
);
// calculate usd balances. add price and usd balance to tokenInfos
const balancesUSD = filteredBalances.map((tokenInfo) => ({
...tokenInfo,
tokenPrice: tokenPrices[tokenInfo?.tokenAddress]["usd"],
tokenBalanceUSD:
tokenInfo.qty * tokenPrices[tokenInfo?.tokenAddress]["usd"],
}));
// filter out disallowlist addresses
const balancesUSDFiltered = balancesUSD.filter(
(x) => !DISALLOWLISTED_ADDRESSES.includes(x?.tokenAddress)
);
const sorted = balancesUSDFiltered.sort((a, b) =>
a?.tokenBalanceUSD < b?.tokenBalanceUSD ? 1 : -1
);
return sorted;
} catch (e) {
console.log(e);
}
}
export async function getTerraCustody(chainInfo, useAllowList) {
const tokenAccounts = await getTerraTokenAccounts(chainInfo, useAllowList);
console.log(
`Num of ${chainInfo?.platform} token accounts=${tokenAccounts?.length}`
);
let custody = undefined;
try {
custody = await getTokenValues(chainInfo, tokenAccounts, useAllowList);
console.log(
`Num of filtered ${chainInfo?.platform} token accounts=${custody?.length}`
);
} catch (e) {
console.log("could not fetch terra prices");
}
return custody;
}
export async function grabTerraCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
var balances = [];
try {
balances = await getTerraCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Solana data");
}
if (balances === undefined) {
console.log("could not pull terra balances");
return { balances: [] };
} else {
const chainInfo_ = {
...chainInfo,
emitter_address: await getEmitterAddressTerra(
chainInfo.token_bridge_address
),
balances: balances,
};
return chainInfo_;
}
}
// const chain = process.env.chain;
// const useAllowListstr = process.env.chain || "false";
// var func = async () => {
// const chainInfo = CHAIN_INFO_MAP[chain];
// const useAllowList = true ? useAllowListstr === "true" : false;
// const balances = await getTerraCustody(chainInfo, useAllowList);
// console.log(balances);
// await updateTable(chainInfo, balances);
// // console.log("terra custody (USD) = ", terraCustodyUSD);
// };
// func().then((x) => console.log("end"));

View File

@ -1,30 +0,0 @@
import getCustodyData from "./getCustodyData";
import { open } from "fs/promises";
let retryTimeout = 5 * 60 * 1000;
if (process.env.RETRY_TIMEOUT) {
try {
retryTimeout = parseInt(process.env.RETRY_TIMEOUT);
} catch (e) {
console.log(
`could not parseInt ${process.env.RETRY_TIMEOUT}. Using default timeout=${retryTimeout}`
);
}
}
const filename = "/app/ready";
let firstRun = true;
async function main() {
while (true) {
console.log(`${new Date().toISOString()} - fetching custody data`);
await getCustodyData();
if (firstRun) {
let fh = await open(filename, "a");
await fh.close();
firstRun = false;
}
console.log(`${new Date().toISOString()} - sleeping for ${retryTimeout}`);
await new Promise((resolve) => setTimeout(resolve, Number(retryTimeout)));
}
}
main();

View File

@ -1,417 +0,0 @@
import {
CHAIN_ID_ACALA,
CHAIN_ID_ALGORAND,
CHAIN_ID_APTOS,
CHAIN_ID_AURORA,
CHAIN_ID_AVAX,
CHAIN_ID_BSC,
CHAIN_ID_CELO,
CHAIN_ID_ETH,
CHAIN_ID_FANTOM,
CHAIN_ID_KARURA,
CHAIN_ID_KLAYTN,
CHAIN_ID_MOONBEAM,
CHAIN_ID_NEAR,
CHAIN_ID_OASIS,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
CHAIN_ID_TERRA2,
CHAIN_ID_XPLA,
} from "@certusone/wormhole-sdk";
export const WORMHOLE_RPC_HOSTS = [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
];
export const CHAIN_ID_MAP = {
0: undefined,
1: CHAIN_ID_SOLANA,
2: CHAIN_ID_ETH,
3: CHAIN_ID_TERRA,
4: CHAIN_ID_BSC,
5: CHAIN_ID_POLYGON,
6: CHAIN_ID_AVAX,
7: CHAIN_ID_OASIS,
8: CHAIN_ID_ALGORAND,
9: CHAIN_ID_AURORA,
10: CHAIN_ID_FANTOM,
11: CHAIN_ID_KARURA,
12: CHAIN_ID_ACALA,
13: CHAIN_ID_KLAYTN,
14: CHAIN_ID_CELO,
15: CHAIN_ID_NEAR,
16: CHAIN_ID_MOONBEAM,
18: CHAIN_ID_TERRA2,
22: CHAIN_ID_APTOS,
28: CHAIN_ID_XPLA,
};
import { ethers } from "ethers";
require("dotenv").config();
export const DISALLOWLISTED_ADDRESSES = [
"0x04132bf45511d03a58afd4f1d36a29d229ccc574",
"0xa79bd679ce21a2418be9e6f88b2186c9986bbe7d",
"0x931c3987040c90b6db09981c7c91ba155d3fa31f",
"0x8fb1a59ca2d57b51e5971a85277efe72c4492983",
"0xd52d9ba6fcbadb1fe1e3aca52cbb72c4d9bbb4ec",
"0x1353c55fd2beebd976d7acc4a7083b0618d94689",
"0xf0fbdb8a402ec0fc626db974b8d019c902deb486",
"0x1fd4a95f4335cf36cac85730289579c104544328",
"0x358aa13c52544eccef6b0add0f801012adad5ee3",
"0xbe32b7acd03bcc62f25ebabd169a35e69ef17601",
"0x7ffb3d637014488b63fb9858e279385685afc1e2",
"0x337dc89ebcc33a337307d58a51888af92cfdc81b",
"0x5Cb89Ac06F34f73B1A6b8000CEb0AfBc97d58B6b",
"0xd9F0446AedadCf16A12692E02FA26C617FA4D217",
"0xD7b41531456b636641F7e867eC77120441D1E1E8",
"0x9f607027b69f6e123bc3bd56a686b735fa75f30a",
"0x2a35965bbad6fd3964ef815d011c51ab1c546e67",
"0x053c070f0923a5b770cc59d7bf74ecff991cd0b8",
"0x3dab0f14ea515d5c842b631bd6df0f7f989c47b3",
"0x7ee4f716e3c716d61f6158bde3ed5ab03fb6b90c",
"0x90285e9567be274ae892c88d3ffd76c87d6c7904",
"0x2d4678e71590c56eb37869832a3642c405e1c252", // fake saitama on poly
"0x1e49f85f8f5d4ef948ccb953c0172c648b75222f",
"0x477c7802632f0d38f285a7fd7112a66c11b99db6",
"0xdaff96cc3d5e2fa982812ec12ce74833deb51327", //fake btc on bsc
"0xe389ac691bd2b0228daffff548fbce38470373e8", //fake wrapped matic on poly
"0x7e347498dfef39a88099e3e343140ae17cde260e", //wrapped avax on bsc
"0x86812b970bbdce75b4590243ba2cbff671d0b754", //fake tether on bsc
"0x3d8babf3afd0e1bfc843f9638f650fa50ae6c22b", //fake tether on eth
"0x0749902ae8ed9c6a508271bad18f185dba7185d4", //wrapped eth on polygon
"0x8e1c62f03b995938233ffa3762bd69f889016b3c", //fake luna2.0 on bsc
].map((x) => x.toLowerCase());
export const COIN_GECKO_EXCEPTIONS = [
{
chainId: 2,
tokenAddress: "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8".toLowerCase(),
coinGeckoId: "ethereum",
},
{
chainId: 2,
tokenAddress: "0xdAF566020156297E2837fDfaA6Fbba929A29461E".toLowerCase(),
coinGeckoId: "safe-coin-2",
},
{
chainId: 2,
tokenAddress: "0x5ab6A4F46Ce182356B6FA2661Ed8ebcAFce995aD".toLowerCase(),
coinGeckoId: "sportium",
},
{
chainId: 3,
tokenAddress: "uluna",
coinGeckoId: "terra-luna",
},
{
chainId: 3,
tokenAddress: "ukrw",
coinGeckoId: "terra-krw",
},
{
chainId: 7,
tokenAddress: "0x21c718c22d52d0f3a789b752d4c2fd5908a8a733",
coinGeckoId: "oasis-network", // wrapped-rose does not currently have prices
},
{
chainId: 7,
tokenAddress: "0x366ef31c8dc715cbeff5fa54ad106dc9c25c6153",
coinGeckoId: "tether",
},
{
chainId: 1,
tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
coinGeckoId: "usd-coin",
},
{
chainId: 1,
tokenAddress: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
coinGeckoId: "tether",
},
{
chainId: 11,
tokenAddress: "0x0000000000000000000500000000000000000007",
coinGeckoId: "tether",
},
{
chainId: 12,
tokenAddress: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee2",
coinGeckoId: "acala",
},
{
chainId: 12,
tokenAddress: "0x0000000000000000000100000000000000000001",
coinGeckoId: "acala-dollar",
},
{
chainId: 12,
tokenAddress: "0x0000000000000000000100000000000000000000",
coinGeckoId: "acala",
},
];
export function newProvider(
url: string,
batch: boolean = false
): ethers.providers.JsonRpcProvider | ethers.providers.JsonRpcBatchProvider {
// only support http(s), not ws(s) as the websocket constructor can blow up the entire process
// it uses a nasty setTimeout(()=>{},0) so we are unable to cleanly catch its errors
if (url.includes("http")) {
if (batch) {
return new ethers.providers.JsonRpcBatchProvider(url);
}
return new ethers.providers.JsonRpcProvider(url);
}
throw new Error("url does not start with http/https!");
}
export var CHAIN_INFO_MAP = {
"1": {
name: "solana",
evm: false,
chain_id: CHAIN_ID_SOLANA,
endpoint_url: process.env.SOLANA_RPC || "https://rpc.ankr.com/solana",
core_bridge: "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth",
token_bridge_address: "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb",
custody_address: "GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m",
platform: "solana",
covalentChain: 1399811149,
},
"2": {
name: "eth",
evm: true,
chain_id: CHAIN_ID_ETH,
endpoint_url: process.env.ETH_RPC || "https://rpc.ankr.com/eth",
core_bridge: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
token_bridge_address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585",
custody_address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585",
api_key: process.env.ETHERSCAN_API,
urlStem: `https://api.etherscan.io`,
platform: "ethereum",
covalentChain: 1,
},
"3": {
name: "terra",
evm: false,
chain_id: CHAIN_ID_TERRA,
endpoint_url: "",
core_bridge: "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5",
token_bridge_address: "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf",
custody_address: "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf",
urlStem: "https://columbus-fcd.terra.dev",
platform: "terra",
covalentChain: 3,
},
"4": {
name: "bsc",
evm: true,
chain_id: CHAIN_ID_BSC,
endpoint_url: process.env.BSC_RPC || "https://rpc.ankr.com/bsc ", //moralis_url
core_bridge: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
token_bridge_address: "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7",
custody_address: "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7",
api_key: process.env.BSCSCAN_API,
urlStem: `https://api.bscscan.com`,
platform: "binance-smart-chain",
covalentChain: 56,
},
"5": {
name: "polygon",
evm: true,
chain_id: CHAIN_ID_POLYGON,
endpoint_url: process.env.POLYGON_RPC || "https://rpc.ankr.com/polygon ",
core_bridge: "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7",
token_bridge_address: "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE",
custody_address: "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE",
api_key: process.env.POLYSCAN_API,
urlStem: `https://api.polygonscan.com`,
platform: "polygon-pos", //coingecko?,
covalentChain: 137,
},
"6": {
name: "avalanche",
evm: true,
chain_id: CHAIN_ID_AVAX,
endpoint_url: process.env.AVAX_RPC || "https://rpc.ankr.com/avalanche",
core_bridge: "0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c",
token_bridge_address: "0x0e082F06FF657D94310cB8cE8B0D9a04541d8052",
custody_address: "0x0e082F06FF657D94310cB8cE8B0D9a04541d8052",
api_key: process.env.SNOWTRACE_API,
urlStem: `https://api.snowtrace.io`,
platform: "avalanche", //coingecko?
covalentChain: 43114,
},
"7": {
name: "oasis",
evm: true,
chain_id: CHAIN_ID_OASIS,
endpoint_url: "https://emerald.oasis.dev",
core_bridge: "0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585",
token_bridge_address: "0x5848C791e09901b40A9Ef749f2a6735b418d7564",
custody_address: "0x5848C791e09901b40A9Ef749f2a6735b418d7564",
api_key: "",
urlStem: `https://explorer.emerald.oasis.dev`,
platform: "oasis", //coingecko?
covalentChain: 42262,
},
"8": {
name: "algorand",
evm: false,
chain_id: CHAIN_ID_ALGORAND,
endpoint_url: "https://node.algoexplorerapi.io",
core_bridge: "842125965",
token_bridge_address: "842126029",
custody_address: "842126029",
api_key: "",
urlStem: `https://algoexplorer.io`,
platform: "algorand", //coingecko?
covalentChain: undefined,
},
"9": {
name: "aurora",
evm: true,
chain_id: CHAIN_ID_AURORA,
endpoint_url: "https://mainnet.aurora.dev",
core_bridge: "0xa321448d90d4e5b0A732867c18eA198e75CAC48E",
token_bridge_address: "0x51b5123a7b0F9b2bA265f9c4C8de7D78D52f510F",
custody_address: "0x51b5123a7b0F9b2bA265f9c4C8de7D78D52f510F",
api_key: process.env.AURORA_API,
urlStem: `https://api.aurorascan.dev`, //?module=account&action=txlist&address={addressHash}
covalentChain: 1313161554,
platform: "aurora", //coingecko?
},
"10": {
name: "fantom",
evm: true,
chain_id: CHAIN_ID_FANTOM,
endpoint_url: "https://rpc.ftm.tools",
core_bridge: "0x126783A6Cb203a3E35344528B26ca3a0489a1485",
token_bridge_address: "0x7C9Fc5741288cDFdD83CeB07f3ea7e22618D79D2",
custody_address: "0x7C9Fc5741288cDFdD83CeB07f3ea7e22618D79D2",
api_key: process.env.FTMSCAN_API,
urlStem: `https://api.FtmScan.com`,
platform: "fantom", //coingecko?
covalentChain: 250,
},
"11": {
name: "karura",
evm: true,
chain_id: CHAIN_ID_KARURA,
endpoint_url: "https://eth-rpc-karura.aca-api.network",
core_bridge: "0xa321448d90d4e5b0A732867c18eA198e75CAC48E",
token_bridge_address: "0xae9d7fe007b3327AA64A32824Aaac52C42a6E624",
custody_address: "0xae9d7fe007b3327AA64A32824Aaac52C42a6E624",
api_key: "",
urlStem: `https://blockscout.karura.network`,
platform: "karura", //coingecko?
covalentChain: "",
},
"12": {
name: "acala",
evm: true,
chain_id: CHAIN_ID_ACALA,
endpoint_url: "https://eth-rpc-acala.aca-api.network",
core_bridge: "0xa321448d90d4e5b0A732867c18eA198e75CAC48E",
token_bridge_address: "0xae9d7fe007b3327AA64A32824Aaac52C42a6E624",
custody_address: "0xae9d7fe007b3327AA64A32824Aaac52C42a6E624",
api_key: "",
urlStem: `https://blockscout.acala.network`,
platform: "acala", //coingecko?
covalentChain: "",
},
"13": {
name: "klaytn",
evm: true,
chain_id: CHAIN_ID_KLAYTN,
endpoint_url: "https://klaytn-mainnet-rpc.allthatnode.com:8551",
core_bridge: "0x0C21603c4f3a6387e241c0091A7EA39E43E90bb7",
token_bridge_address: "0x5b08ac39EAED75c0439FC750d9FE7E1F9dD0193F",
custody_address: "0x5b08ac39EAED75c0439FC750d9FE7E1F9dD0193F",
api_key: process.env.KLAYTN_API,
urlStem: `https://scope.klaytn.com`,
platform: "klay-token", //coingecko?
covalentChain: "8217",
},
"14": {
name: "celo",
evm: true,
chain_id: CHAIN_ID_CELO,
endpoint_url: "https://forno.celo.org",
core_bridge: "0xa321448d90d4e5b0A732867c18eA198e75CAC48E",
token_bridge_address: "0x796Dff6D74F3E27060B71255Fe517BFb23C93eed",
custody_address: "0x796Dff6D74F3E27060B71255Fe517BFb23C93eed",
api_key: "",
urlStem: `https://explorer.celo.org`,
platform: "celo", //coingecko?
covalentChain: "42220",
},
"15": {
name: "near",
evm: false,
chain_id: CHAIN_ID_NEAR,
endpoint_url: "https://rpc.ankr.com/near",
core_bridge: "contract.wormhole_crypto.near",
token_bridge_address: "contract.portalbridge.near",
custody_address: "contract.portalbridge.near",
urlStem: "",
platform: "near",
covalentChain: undefined,
},
"16": {
name: "moonbeam",
evm: true,
chain_id: CHAIN_ID_MOONBEAM,
endpoint_url: "https://rpc.api.moonbeam.network",
core_bridge: "0xC8e2b0cD52Cf01b0Ce87d389Daa3d414d4cE29f3",
token_bridge_address: "0xB1731c586ca89a23809861c6103F0b96B3F57D92",
custody_address: "0xB1731c586ca89a23809861c6103F0b96B3F57D92",
api_key: process.env.MOONBEAM_API,
urlStem: "https://api-moonbeam.moonscan.io",
platform: "moonbeam",
covalentChain: 1284,
},
"18": {
name: "terra2",
evm: false,
chain_id: CHAIN_ID_TERRA2,
endpoint_url: "",
core_bridge:
"terra12mrnzvhx3rpej6843uge2yyfppfyd3u9c3uq223q8sl48huz9juqffcnhp",
token_bridge_address:
"terra153366q50k7t8nn7gec00hg66crnhkdggpgdtaxltaq6xrutkkz3s992fw9",
custody_address:
"terra153366q50k7t8nn7gec00hg66crnhkdggpgdtaxltaq6xrutkkz3s992fw9",
urlStem: "https://phoenix-fcd.terra.dev",
platform: "terra",
covalentChain: 3,
},
"22": {
name: "aptos",
evm: false,
chain_id: CHAIN_ID_APTOS,
endpoint_url: "https://fullnode.mainnet.aptoslabs.com/v1",
core_bridge:
"0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
token_bridge_address:
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
custody_address:
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
api_key: "",
urlStem: "",
platform: "aptos",
covalentChain: 0,
},
};
export async function sleepFor(timeInMs: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, timeInMs);
});
}

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"outDir": "lib",
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"lib": ["es2020"],
"skipLibCheck": true,
"allowJs": true,
"resolveJsonModule": true
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]
}