Small cleanup dexVolumes folder (#3443)

* Add .vscode folder to .gitignore

* Add v0 test script

* Fix types

* Fix chain blocks type

* Change dex volumes readme

* Fix ts errors

* Remove workbench.colorCustomizations

* Finally fix ts errors

* Chore test script

* Revert "Remove workbench.colorCustomizations"

This reverts commit 8e8721096f2d148495511442e6d822f30e576f42.

* Revert back since it doesn't solve the issue

* Upgrade trilom/file-changes-action from 1.2.3 -> 1.2.4
This commit is contained in:
0xtawa
2022-08-04 09:41:30 +02:00
committed by GitHub
parent 786c3000ae
commit 22d1d42cc7
20 changed files with 221 additions and 113 deletions

View File

@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- id: file_changes - id: file_changes
uses: trilom/file-changes-action@v1.2.3 uses: trilom/file-changes-action@v1.2.4
with: with:
output: 'json' output: 'json'
fileOutput: 'json' fileOutput: 'json'

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ historical-data.js
yarn.lock yarn.lock
.DS_Store .DS_Store
projects/pooltogether/index.js projects/pooltogether/index.js
.vscode

View File

@@ -1,7 +0,0 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#591417",
"titleBar.activeBackground": "#7C1C20",
"titleBar.activeForeground": "#FEFBFB"
}
}

7
dexVolumes/README.md Normal file
View File

@@ -0,0 +1,7 @@
# DEX volumes
> **_NOTE:_** Under developement.
## Test an adapter
`npm run test-dex 1inch`

View File

@@ -1,12 +0,0 @@
const { getCurrentBlocks } = require("@defillama/sdk/build/computeTVL/blocks");
const { volume } = require("../pancakeswap");
const test = async () => {
const { timestamp, chainBlocks } = await getCurrentBlocks();
console.log(chainBlocks, "chainBlocks");
volume.bsc.fetch(timestamp).then((res) => {
console.log(res);
});
};
test();

View File

@@ -0,0 +1,63 @@
import * as path from 'path'
import type { ChainBlocks, DexAdapter, VolumeAdapter } from '../dexVolume.type';
import { chainsForBlocks } from "@defillama/sdk/build/computeTVL/blocks";
import { Chain } from '@defillama/sdk/build/general';
import handleError from '../../utils/handleError';
import { checkArguments, getLatestBlockRetry, printVolumes } from './utils';
// Add handler to rejections/exceptions
process.on('unhandledRejection', handleError)
process.on('uncaughtException', handleError)
// Check if all arguments are present
checkArguments(process.argv)
// Get path of module import
const passedFile = path.resolve(process.cwd(), `dexVolumes/${process.argv[2]}`);
(async () => {
console.info(`Running ${process.argv[2].toUpperCase()} adapter`)
// Import module to test
let module: DexAdapter = (await import(passedFile)).default
if ("volume" in module) {
// Get adapter
const volumeAdapter = module.volume
const volumes = await runAdapter(volumeAdapter)
printVolumes(volumes)
} else if ("breakdown" in module) {
const breakdownAdapter = module.breakdown
const allVolumes = await Promise.all(Object.entries(breakdownAdapter).map(async ([version, adapter]) =>
await runAdapter(adapter).then(res => ({ version, res }))
))
allVolumes.forEach((promise) => {
console.info(promise.version)
printVolumes(promise.res)
})
} else console.info("No compatible adapter found")
})()
async function runAdapter(volumeAdapter: VolumeAdapter) {
// Get chains to check
const chains = Object.keys(volumeAdapter).filter(item => typeof volumeAdapter[item] === 'object');
// Get lastest block
const chainBlocks: ChainBlocks = {};
await Promise.all(
chains.map(async (chainRaw) => {
const chain: Chain = chainRaw === "ava" ? "avax" : chainRaw as Chain
if (chainsForBlocks.includes(chain as Chain) || chain === "ethereum") {
const latestBlock = await getLatestBlockRetry(chain)
if (!latestBlock) throw new Error("latestBlock")
chainBlocks[chain] = latestBlock.number - 10
}
})
);
// Get volumes
const unixTimestamp = Math.round(Date.now() / 1000) - 60;
const volumes = await Promise.all(Object.keys(chainBlocks).map(
async chain => volumeAdapter[chain].fetch(unixTimestamp, chainBlocks)
.then(res => {
return { timestamp: unixTimestamp, totalVolume: res.totalVolume, dailyVolume: res.dailyVolume }
})
))
return volumes
}

29
dexVolumes/cli/utils.ts Normal file
View File

@@ -0,0 +1,29 @@
import { getLatestBlock } from "@defillama/sdk/build/util";
import { FetchResult } from "../dexVolume.type";
export function checkArguments(argv: string[]) {
if (argv.length < 3) {
console.error(`Missing argument, you need to provide the filename of the adapter to test.
Eg: ts-node dexVolumes/cli/testAdapter.js dexVolumes/myadapter.js`);
process.exit(1);
}
}
export async function getLatestBlockRetry(chain: string) {
for (let i = 0; i < 5; i++) {
try {
return await getLatestBlock(chain);
} catch (e) {
throw new Error(`Couln't get block heights for chain "${chain}"\n${e}`);
}
}
}
export function printVolumes(volumes: FetchResult[]) {
volumes.forEach(element => {
console.info("----------")
console.info(`Daily: ${element.dailyVolume}`)
console.info(`Total: ${element.totalVolume}`)
console.info("----------")
});
}

View File

@@ -1,5 +1,5 @@
export type ChainBlocks = { export type ChainBlocks = {
[x: string]: number; [x: string]: number
}; };
export type FetchResult = { export type FetchResult = {

View File

@@ -1,34 +0,0 @@
const sdk = require("@defillama/sdk");
const retry = require("async-retry");
const axios = require("axios");
async function getBlock(timestamp, chain, chainBlocks, undefinedOk = false) {
if (
chainBlocks?.[chain] !== undefined ||
(process.env.HISTORICAL === undefined && undefinedOk)
) {
return chainBlocks[chain];
} else {
if (chain === "celo") {
return Number(
(
await retry(
async (bail) =>
await axios.get(
"https://explorer.celo.org/api?module=block&action=getblocknobytime&timestamp=" +
timestamp +
"&closest=before"
)
)
).data.result.blockNumber
);
}
return sdk.api.util
.lookupBlock(timestamp, { chain })
.then((blockData) => blockData.block);
}
}
module.exports = {
getBlock,
};

View File

@@ -1,38 +0,0 @@
const { request, gql } = require("graphql-request");
const {
DEFAULT_DAILY_VOLUME_FACTORY,
DEFAULT_DAILY_VOLUME_FIELD,
} = require("./getUniSubgraphVolume");
const getStartTimestamp =
({
endpoints,
chain,
dailyDataField = `${DEFAULT_DAILY_VOLUME_FACTORY}s`,
volumeField = DEFAULT_DAILY_VOLUME_FIELD,
dateField = "date",
first = 1000,
}) =>
async () => {
const query = gql`
{
${dailyDataField}(first: ${first}) {
${dateField}
${volumeField}
}
}
`;
const result = await request(endpoints[chain], query);
const days = result?.[dailyDataField];
const firstValidDay = days.find((data) => data[volumeField] !== "0");
return firstValidDay[dateField];
};
module.exports = {
getStartTimestamp,
};

View File

@@ -0,0 +1,46 @@
import { request, gql } from "graphql-request";
import { DEFAULT_DAILY_VOLUME_FACTORY, DEFAULT_DAILY_VOLUME_FIELD } from "./getUniSubgraphVolume";
interface IGetStartTimestamp {
endpoints: {
[chain: string]: string;
}
chain: string
dailyDataField: string
volumeField: string
dateField?: string
first?: number
}
const getStartTimestamp =
({
endpoints,
chain,
dailyDataField = `${DEFAULT_DAILY_VOLUME_FACTORY}s`,
volumeField = DEFAULT_DAILY_VOLUME_FIELD,
dateField = "date",
first = 1000,
}: IGetStartTimestamp) =>
async () => {
const query = gql`
{
${dailyDataField}(first: ${first}) {
${dateField}
${volumeField}
}
}
`;
const result = await request(endpoints[chain], query);
const days = result?.[dailyDataField];
const firstValidDay = days.find((data: any) => data[volumeField] !== "0");
return firstValidDay[dateField];
};
export {
getStartTimestamp,
};

View File

@@ -1,5 +1,7 @@
const { request, gql } = require("graphql-request"); import { Chain } from "@defillama/sdk/build/general";
const { getBlock } = require("./getBlock"); import { request, gql } from "graphql-request";
import { getBlock } from "../../projects/helper/getBlock";
import { ChainBlocks } from "../dexVolume.type";
const getUniqStartOfTodayTimestamp = (date = new Date()) => { const getUniqStartOfTodayTimestamp = (date = new Date()) => {
var date_utc = Date.UTC( var date_utc = Date.UTC(
@@ -11,7 +13,7 @@ const getUniqStartOfTodayTimestamp = (date = new Date()) => {
date.getUTCSeconds() date.getUTCSeconds()
); );
var startOfDay = new Date(date_utc); var startOfDay = new Date(date_utc);
var timestamp = startOfDay / 1000; var timestamp = startOfDay.getTime() / 1000;
return Math.floor(timestamp / 86400) * 86400; return Math.floor(timestamp / 86400) * 86400;
}; };
@@ -24,6 +26,24 @@ const DEFAULT_TOTAL_VOLUME_FIELD = "totalVolumeUSD";
const DEFAULT_DAILY_VOLUME_FACTORY = "uniswapDayData"; const DEFAULT_DAILY_VOLUME_FACTORY = "uniswapDayData";
const DEFAULT_DAILY_VOLUME_FIELD = "dailyVolumeUSD"; const DEFAULT_DAILY_VOLUME_FIELD = "dailyVolumeUSD";
interface IGetChainVolumeParams {
graphUrls: {
[chains: string]: string
},
totalVolume: {
factory: string,
field: string
},
dailyVolume?: {
factory: string,
field: string
},
customDailyVolume?: string,
hasDailyVolume?: boolean
hasTotalVolume?: boolean
getCustomBlock?: (timestamp: number) => Promise<number>
}
function getChainVolume({ function getChainVolume({
graphUrls, graphUrls,
totalVolume = { totalVolume = {
@@ -38,7 +58,7 @@ function getChainVolume({
hasDailyVolume = true, hasDailyVolume = true,
hasTotalVolume = true, hasTotalVolume = true,
getCustomBlock = undefined, getCustomBlock = undefined,
}) { }: IGetChainVolumeParams) {
const totalVolumeQuery = gql` const totalVolumeQuery = gql`
${totalVolume.factory}( ${totalVolume.factory}(
block: { number: $block } block: { number: $block }
@@ -56,18 +76,18 @@ function getChainVolume({
${dailyVolume.field} ${dailyVolume.field}
} }
`; `;
const graphQuery = gql` const graphQuery = gql`
query get_volume($block: Int, $id: Int) { query get_volume($block: Int, $id: Int) {
${hasTotalVolume ? totalVolumeQuery : ""} ${hasTotalVolume ? totalVolumeQuery : ""}
${hasDailyVolume ? dailyVolumeQuery : ""} ${hasDailyVolume ? dailyVolumeQuery : ""}
} }
`; `;
return (chain) => { return (chain: Chain) => {
return async (timestamp, chainBlocks) => { return async (timestamp: number, chainBlocks: ChainBlocks) => {
const block = const block =
(getCustomBlock && (await getCustomBlock(timestamp))) || getCustomBlock ?
(await getBlock(timestamp, chain, chainBlocks)); await getCustomBlock(timestamp) :
await getBlock(timestamp, chain, chainBlocks);
const id = getUniswapDateId(); const id = getUniswapDateId();
const graphRes = await request(graphUrls[chain], graphQuery, { const graphRes = await request(graphUrls[chain], graphQuery, {
block, block,
@@ -80,14 +100,14 @@ query get_volume($block: Int, $id: Int) {
totalVolume: graphRes[totalVolume.factory][0][totalVolume.field], totalVolume: graphRes[totalVolume.factory][0][totalVolume.field],
dailyVolume: hasDailyVolume dailyVolume: hasDailyVolume
? (graphRes?.[dailyVolume.factory]?.[dailyVolume.field] || "0") ?? ? (graphRes?.[dailyVolume.factory]?.[dailyVolume.field] || "0") ??
undefined undefined
: undefined, : undefined,
}; };
}; };
}; };
} }
module.exports = { export {
getUniqStartOfTodayTimestamp, getUniqStartOfTodayTimestamp,
getChainVolume, getChainVolume,
DEFAULT_TOTAL_VOLUME_FACTORY, DEFAULT_TOTAL_VOLUME_FACTORY,

View File

@@ -2,6 +2,7 @@ import { getChainVolume } from "../helper/getUniSubgraphVolume";
import { getStartTimestamp } from "../helper/getStartTimestamp"; import { getStartTimestamp } from "../helper/getStartTimestamp";
import { FANTOM } from "../helper/chains"; import { FANTOM } from "../helper/chains";
import { DexVolumeAdapter } from "../dexVolume.type"; import { DexVolumeAdapter } from "../dexVolume.type";
import { Chain } from "@defillama/sdk/build/general";
const endpoints = { const endpoints = {
// [AVAX]: "https://api.thegraph.com/subgraphs/name/soulswapfinance/avalanche-exchange // [AVAX]: "https://api.thegraph.com/subgraphs/name/soulswapfinance/avalanche-exchange
@@ -35,7 +36,7 @@ const volume = Object.keys(endpoints).reduce(
(acc, chain) => ({ (acc, chain) => ({
...acc, ...acc,
[chain]: { [chain]: {
fetch: graphs(chain), fetch: graphs(chain as Chain),
start: getStartTimestamp({ ...startTimeQuery, chain }), start: getStartTimestamp({ ...startTimeQuery, chain }),
}, },
}), }),

View File

@@ -13,6 +13,7 @@ import {
XDAI, XDAI,
} from "../helper/chains"; } from "../helper/chains";
import { DexVolumeAdapter } from "../dexVolume.type"; import { DexVolumeAdapter } from "../dexVolume.type";
import { Chain } from "@defillama/sdk/build/general";
const endpoints = { const endpoints = {
[ARBITRUM]: [ARBITRUM]:
@@ -67,7 +68,7 @@ const volume = Object.keys(endpoints).reduce(
(acc, chain) => ({ (acc, chain) => ({
...acc, ...acc,
[chain]: { [chain]: {
fetch: graphs(chain), fetch: graphs(chain as Chain),
start: getStartTimestamp({ ...startTimeQuery, chain }), start: getStartTimestamp({ ...startTimeQuery, chain }),
}, },
}), }),

View File

@@ -1,4 +1,4 @@
import path from "path"; import * as path from "path";
import axios from "axios"; import axios from "axios";
import { ethers } from "ethers"; import { ethers } from "ethers";
import { providers } from "./utils/ethers" import { providers } from "./utils/ethers"

View File

@@ -7,7 +7,8 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"weeklyChanges": "git pull && git diff $(git log -1 --before=@{7.days.ago} --format=%H) --stat | grep -E \"projects/\" | cut -d / -f 2 | cut -d \" \" -f 1 | uniq | wc -l", "weeklyChanges": "git pull && git diff $(git log -1 --before=@{7.days.ago} --format=%H) --stat | grep -E \"projects/\" | cut -d / -f 2 | cut -d \" \" -f 1 | uniq | wc -l",
"dev": "babel-watch curve.js", "dev": "babel-watch curve.js",
"test-interactive": "node utils/testInteractive" "test-interactive": "node utils/testInteractive",
"test-dex": "ts-node dexVolumes/cli/testAdapter.ts"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",

20
tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
// This is an alias to @tsconfig/node16: https://github.com/tsconfig/bases
"extends": "ts-node/node16/tsconfig.json",
// Most ts-node options can be specified here using their programmatic names.
"ts-node": {
// It is faster to skip typechecking.
// Remove if you want ts-node to do typechecking.
"transpileOnly": true,
"files": true,
"compilerOptions": {
// compilerOptions specified here will override those declared below,
// but *only* in ts-node. Useful if you want ts-node and tsc to use
// different options with a single tsconfig.json.
}
},
"compilerOptions": {
// typescript options here
"typeRoots": ["./typings/", "./node_modules/@types/"]
}
}

8
typings/getBlock.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
type ChainBlocks = {
[chain: string]: number
} //tmp fix
type Chain = string
declare module '*/getBlock' {
function getBlock(timestamp: number, chain: Chain, chainBlocks: ChainBlocks, undefinedOk?: boolean): Promise<number | ChainBlocks['']>
}

1
typings/handleError.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module '*/handleError';

1
typings/index.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module '*/helper/chains';