2021-04-22 18:48:54 +00:00
|
|
|
import axios from "axios";
|
2021-04-26 19:53:08 +00:00
|
|
|
import { exec } from "child_process";
|
|
|
|
import dotenv from "dotenv";
|
2021-04-22 21:02:32 +00:00
|
|
|
import fs from "fs";
|
2021-04-22 18:48:54 +00:00
|
|
|
|
2021-04-24 23:54:18 +00:00
|
|
|
import manualProtocols from "../allprotocols.json";
|
|
|
|
|
2021-04-26 19:53:08 +00:00
|
|
|
dotenv.config();
|
|
|
|
|
2021-04-22 18:25:49 +00:00
|
|
|
const SNAPSHOT_API = "https://hub.snapshot.page/api/";
|
|
|
|
|
2021-04-22 18:48:54 +00:00
|
|
|
interface Space {
|
|
|
|
strategies: any[];
|
|
|
|
plugins: any;
|
|
|
|
filters: any;
|
|
|
|
name: string;
|
|
|
|
network: string; // '1', '56', etc
|
2021-04-22 18:53:20 +00:00
|
|
|
members?: [];
|
2021-04-22 18:48:54 +00:00
|
|
|
symbol: string;
|
|
|
|
domain: string;
|
|
|
|
about: string;
|
|
|
|
twitter: string;
|
2021-04-23 19:53:02 +00:00
|
|
|
private?: boolean;
|
2021-04-22 18:48:54 +00:00
|
|
|
github: string;
|
|
|
|
}
|
|
|
|
|
2021-04-23 19:53:02 +00:00
|
|
|
const deadOrInvalidSpaces = [
|
2021-04-27 19:17:15 +00:00
|
|
|
"Aave",
|
2021-04-23 19:53:02 +00:00
|
|
|
"FinNexus",
|
|
|
|
"ForTube(Ethereum)",
|
|
|
|
"GoldMining Token",
|
|
|
|
"Percent",
|
|
|
|
"QIAN(ETH)",
|
|
|
|
"ROPETHEVOTE",
|
|
|
|
"yearn.finance (archive)",
|
|
|
|
"KAIJU",
|
|
|
|
"OMG",
|
|
|
|
"Evolution Land",
|
|
|
|
"Yam Finance Signal",
|
2021-04-24 23:54:18 +00:00
|
|
|
"Yam Finance",
|
2021-04-23 19:53:02 +00:00
|
|
|
"BIOPset House of Representatives",
|
2021-04-27 17:28:09 +00:00
|
|
|
"BIOPset",
|
2021-04-27 19:17:15 +00:00
|
|
|
"BIOPsetHouse",
|
|
|
|
"MarsFinance",
|
2021-04-23 19:53:02 +00:00
|
|
|
"BeetsDAO",
|
|
|
|
"Scoobi-doge",
|
2021-04-24 23:54:18 +00:00
|
|
|
"GamyFi Governance",
|
|
|
|
"Proof Of Humanity",
|
|
|
|
"Strudel Finance",
|
|
|
|
"BOTE LABS",
|
|
|
|
"Aave",
|
|
|
|
"CoFiX",
|
|
|
|
"Shadowpakt",
|
|
|
|
"SouthChain Digital Asset Network",
|
|
|
|
"Gentlemen's Bank",
|
|
|
|
"AngelDAO",
|
|
|
|
"Index",
|
2021-04-27 19:17:15 +00:00
|
|
|
"Compound Uniswap",
|
2021-04-23 19:53:02 +00:00
|
|
|
];
|
|
|
|
|
2021-04-27 17:28:09 +00:00
|
|
|
const handSelectedSpaces = ["Ampleforth", "BadgerDAO", "dHEDGE DAO"];
|
2021-04-24 23:54:18 +00:00
|
|
|
|
2021-04-22 18:48:54 +00:00
|
|
|
function filterObject(obj: any, predicate: Function) {
|
|
|
|
let result: Record<string, any> = {};
|
|
|
|
|
|
|
|
for (let key in obj) {
|
2021-04-22 18:53:20 +00:00
|
|
|
if (obj.hasOwnProperty(key) && predicate(obj[key])) {
|
2021-04-22 18:48:54 +00:00
|
|
|
result[key] = obj[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchAllSpaces(): Promise<Record<string, Space>> {
|
|
|
|
return axios.get(`${SNAPSHOT_API}/spaces`).then((res) => {
|
|
|
|
const spaces = res.data;
|
|
|
|
|
|
|
|
return spaces;
|
2021-04-22 18:25:49 +00:00
|
|
|
});
|
|
|
|
}
|
2021-04-22 18:48:54 +00:00
|
|
|
|
|
|
|
async function fetchMainnetSpaces() {
|
|
|
|
const allSpaces = await fetchAllSpaces();
|
|
|
|
|
2021-04-23 19:53:02 +00:00
|
|
|
const filtered = filterObject(allSpaces, (space: Space) => space.network === "1" && !space.private);
|
2021-04-22 18:48:54 +00:00
|
|
|
|
|
|
|
return filtered;
|
|
|
|
}
|
|
|
|
|
2021-04-22 19:23:19 +00:00
|
|
|
function extractTokenAddress(space: Space): string | null {
|
2021-04-23 19:53:02 +00:00
|
|
|
const strategyWithAddress = space.strategies.find(
|
|
|
|
(strategy) => strategy.name === "erc20-balance-of" || strategy.name === "ctoken",
|
|
|
|
);
|
2021-04-22 19:23:19 +00:00
|
|
|
|
|
|
|
if (strategyWithAddress) {
|
|
|
|
return strategyWithAddress.params.address;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-23 19:01:04 +00:00
|
|
|
async function extractTokenAbi(tokenAddress: string | null) {
|
|
|
|
if (!tokenAddress) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = await axios.get(
|
2021-04-26 19:53:08 +00:00
|
|
|
`https://api.etherscan.io/api?module=contract&action=getabi&address=${tokenAddress}&apikey=${process.env.ETHERSCAN_API_TOKEN}`,
|
2021-04-22 21:02:32 +00:00
|
|
|
);
|
|
|
|
|
2021-04-26 19:53:08 +00:00
|
|
|
return JSON.parse(res.data.result);
|
2021-04-22 21:02:32 +00:00
|
|
|
}
|
|
|
|
|
2021-04-23 19:59:06 +00:00
|
|
|
// workaround for Etherscan api request limit
|
|
|
|
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
|
|
|
|
2021-04-24 23:54:18 +00:00
|
|
|
async function createSkeletons(spaces: Record<string, any>) {
|
|
|
|
for (let i = 0; i < Object.keys(spaces).length; i++) {
|
|
|
|
const key = Object.keys(spaces)[i];
|
2021-04-22 19:23:19 +00:00
|
|
|
|
2021-04-23 21:13:48 +00:00
|
|
|
console.log("Writing: ", key);
|
|
|
|
|
2021-04-24 23:54:18 +00:00
|
|
|
const tokenAddress = extractTokenAddress(spaces[key]);
|
2021-04-27 17:28:09 +00:00
|
|
|
const suffix = spaces[key].symbol;
|
|
|
|
const name = spaces[key].name;
|
2021-04-23 19:01:04 +00:00
|
|
|
|
2021-04-27 17:28:09 +00:00
|
|
|
exec(`sh ./scripts/add_new_protocol.sh ${key} ${tokenAddress} ${suffix} ${name}`, (error, stdout, stderr) => {
|
2021-04-23 19:01:04 +00:00
|
|
|
if (error !== null) {
|
|
|
|
console.log(`exec error: ${error}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-04-23 19:53:02 +00:00
|
|
|
if (tokenAddress) {
|
|
|
|
const tokenAbi = await extractTokenAbi(tokenAddress);
|
2021-04-23 19:01:04 +00:00
|
|
|
|
2021-04-26 19:53:08 +00:00
|
|
|
fs.writeFileSync(`./protocols/${key}/contracts/token.json`, JSON.stringify(tokenAbi));
|
2021-04-23 19:53:02 +00:00
|
|
|
}
|
2021-04-23 19:59:06 +00:00
|
|
|
|
2021-04-23 21:49:22 +00:00
|
|
|
await delay(1000); // workaround for Etherscan api request limit
|
2021-04-22 19:23:19 +00:00
|
|
|
}
|
2021-04-22 18:53:20 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 23:54:18 +00:00
|
|
|
async function run(argv: any) {
|
|
|
|
const args = argv.slice(2);
|
|
|
|
|
|
|
|
if (args[0] === "full") {
|
|
|
|
const allMainnetSpaces = await fetchMainnetSpaces();
|
|
|
|
|
|
|
|
const withMembers = filterObject(
|
|
|
|
allMainnetSpaces,
|
|
|
|
(space: Space) => handSelectedSpaces.includes(space.name) || (space.members && space.members.length > 3),
|
|
|
|
);
|
|
|
|
|
|
|
|
const notDed = filterObject(withMembers, (space: Space) => !deadOrInvalidSpaces.includes(space.name));
|
|
|
|
|
|
|
|
fs.writeFileSync("./allprotocols.json", JSON.stringify(notDed));
|
|
|
|
|
|
|
|
createSkeletons(notDed);
|
|
|
|
} else if (args[0] === "semi") {
|
|
|
|
createSkeletons(manualProtocols);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
run(process.argv);
|