hicity-toolkit/packages/token-lists/test/default.test.ts

189 lines
7.4 KiB
TypeScript

/* eslint-disable no-restricted-syntax */
import Ajv from "ajv";
import fs from "fs";
import path from "path";
import { getAddress } from "@ethersproject/address";
import { schema } from "@uniswap/token-lists";
import currentPancakeswapDefaultList from "../lists/pancakeswap-default.json";
import currentPancakeswapExtendedtList from "../lists/pancakeswap-extended.json";
import currentPancakeswapTop15List from "../lists/pancakeswap-top-15.json";
import currentPancakeswapTop100tList from "../lists/pancakeswap-top-100.json";
import { buildList, VersionBump } from "../src/buildList";
const currentLists = {
"pancakeswap-default": currentPancakeswapDefaultList,
"pancakeswap-extended": currentPancakeswapExtendedtList,
"pancakeswap-top-100": currentPancakeswapTop100tList,
"pancakeswap-top-15": currentPancakeswapTop15List,
};
const ajv = new Ajv({ allErrors: true, format: "full" });
const validate = ajv.compile(schema);
const pathToImages = process.env.CI
? path.join(process.env.GITHUB_WORKSPACE, "packages", "token-lists", "lists", "images")
: path.join(path.resolve(), "lists", "images");
const logoFiles = fs.readdirSync(pathToImages);
// Modified https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_get
const getByAjvPath = (obj, propertyPath: string, defaultValue = undefined) => {
const travel = (regexp) =>
String.prototype.split
.call(propertyPath.substring(1), regexp)
.filter(Boolean)
.reduce((res, key) => (res !== null && res !== undefined ? res[key] : res), obj);
const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/);
return result === undefined || result === obj ? defaultValue : result;
};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
toBeDeclaredOnce(type: string, parameter: string, chainId: number): CustomMatcherResult;
toBeValidTokenList(): CustomMatcherResult;
toBeValidLogo(): CustomMatcherResult;
}
}
}
expect.extend({
toBeDeclaredOnce(received, type: string, parameter: string, chainId: number) {
if (typeof received === "undefined") {
return {
message: () => ``,
pass: true,
};
}
return {
message: () => `Token ${type} ${parameter} on chain ${chainId} should be declared only once.`,
pass: false,
};
},
toBeValidTokenList(tokenList) {
const isValid = validate(tokenList);
if (isValid) {
return {
message: () => ``,
pass: true,
};
}
const validationSummary = validate.errors
.map((error) => {
const value = getByAjvPath(tokenList, error.dataPath);
return `- ${error.dataPath.split(".").pop()} ${value} ${error.message}`;
})
.join("\n");
return {
message: () => `Validation failed:\n${validationSummary}`,
pass: false,
};
},
toBeValidLogo(token) {
// TW logos are always checksummed
const hasTWLogo =
token.logoURI === `https://assets.trustwalletapp.com/blockchains/smartchain/assets/${token.address}/logo.png`;
let hasLocalLogo = false;
const refersToLocalLogo =
token.logoURI === `https://tokens.pancakeswap.finance/images/${token.address}.png` ||
token.logoURI === `https://tokens.pancakeswap.finance/images/${token.address.toLowerCase()}.png`;
if (token.logoURI === "https://tokens.pancakeswap.finance/images/0x4e6415a5727ea08aae4580057187923aec331227.png") {
console.log("refersToLocalLogo", refersToLocalLogo);
}
if (refersToLocalLogo) {
const fileName = token.logoURI.split("/").pop();
// Note: fs.existsSync can't be used here because its not case sensetive
hasLocalLogo = logoFiles.includes(fileName);
}
if (hasTWLogo || hasLocalLogo) {
return {
message: () => ``,
pass: true,
};
}
return {
message: () => `Token ${token.symbol} (${token.address}) has invalid logo: ${token.logoURI}`,
pass: false,
};
},
});
describe.each([["pancakeswap-default"], ["pancakeswap-extended"], ["pancakeswap-top-100"], ["pancakeswap-top-15"]])(
"buildList %s",
(listName) => {
const defaultTokenList = buildList(listName);
it("validates", () => {
expect(defaultTokenList).toBeValidTokenList();
});
it("contains no duplicate addresses", () => {
const map = {};
for (const token of defaultTokenList.tokens) {
const key = `${token.chainId}-${token.address.toLowerCase()}`;
expect(map[key]).toBeDeclaredOnce("address", token.address.toLowerCase(), token.chainId);
map[key] = true;
}
});
// Commented out since we now have duplicate symbols ("ONE") on exchange
// doesn't seem to affect any functionality at the moment though
// it("contains no duplicate symbols", () => {
// const map = {};
// for (const token of defaultTokenList.tokens) {
// const key = `${token.chainId}-${token.symbol.toLowerCase()}`;
// expect(map[key]).toBeDeclaredOnce("symbol", token.symbol.toLowerCase(), token.chainId);
// map[key] = true;
// }
// });
it("contains no duplicate names", () => {
const map = {};
for (const token of defaultTokenList.tokens) {
const key = `${token.chainId}-${token.name.toLowerCase()}`;
expect(map[key]).toBeDeclaredOnce("name", token.name.toLowerCase(), token.chainId);
map[key] = true;
}
});
it("all addresses are valid and checksummed", () => {
for (const token of defaultTokenList.tokens) {
expect(getAddress(token.address)).toBe(token.address);
}
});
it("all tokens have correct logos", () => {
for (const token of defaultTokenList.tokens) {
expect(token).toBeValidLogo();
}
});
it("version gets patch bump if no versionBump sepcified", () => {
expect(defaultTokenList.version.major).toBe(currentLists[listName].version.major);
expect(defaultTokenList.version.minor).toBe(currentLists[listName].version.minor);
expect(defaultTokenList.version.patch).toBe(currentLists[listName].version.patch + 1);
});
it("version gets patch bump if patch versionBump is sepcified", () => {
const defaultTokenListPatchBump = buildList(listName, VersionBump.patch);
expect(defaultTokenListPatchBump.version.major).toBe(currentLists[listName].version.major);
expect(defaultTokenListPatchBump.version.minor).toBe(currentLists[listName].version.minor);
expect(defaultTokenListPatchBump.version.patch).toBe(currentLists[listName].version.patch + 1);
});
it("version gets minor bump if minor versionBump is sepcified", () => {
const defaultTokenListMinorBump = buildList(listName, VersionBump.minor);
expect(defaultTokenListMinorBump.version.major).toBe(currentLists[listName].version.major);
expect(defaultTokenListMinorBump.version.minor).toBe(currentLists[listName].version.minor + 1);
expect(defaultTokenListMinorBump.version.patch).toBe(currentLists[listName].version.patch);
});
it("version gets minor bump if major versionBump is sepcified", () => {
const defaultTokenListMajorBump = buildList(listName, VersionBump.major);
expect(defaultTokenListMajorBump.version.major).toBe(currentLists[listName].version.major + 1);
expect(defaultTokenListMajorBump.version.minor).toBe(currentLists[listName].version.minor);
expect(defaultTokenListMajorBump.version.patch).toBe(currentLists[listName].version.patch);
});
}
);