Skip to content

Commit

Permalink
Add listed super tokens and super token logic contracts to verificati…
Browse files Browse the repository at this point in the history
…on check (#1693)
  • Loading branch information
elvijsTDL authored Sep 22, 2023
1 parent e0df62a commit 5bd8e85
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 23 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/daily-slack-message.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18.x


- name: Send slack message
run: node tasks/daily-slack-bot.js
working-directory: tasks
run: |
npm install ethers --force
node daily-slack-bot.js
env:
CI_SLACK_WEBHOOK: ${{ secrets.CI_SLACK_WEBHOOK }}
ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
Expand Down
119 changes: 97 additions & 22 deletions tasks/daily-slack-bot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const https = require("https");
const ethers = require("ethers")

const workflowPath =
"https://api.github.com/repos/superfluid-finance/protocol-monorepo/actions/runs?per_page=100";
Expand All @@ -18,8 +19,7 @@ const topSectionMessage =
"Looks like there are some lonely pull requests open in your area";
const workflowFileName = ".github/workflows/ci.canary.yml";
const metadataLink =
"https://raw.githubusercontent.com/superfluid-finance/metadata/master/networks.json";

"https://raw.githubusercontent.com/superfluid-finance/protocol-monorepo/dev/packages/metadata/networks.json";
const redImage =
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Solid_red.svg/512px-Solid_red.svg.png?20150316143248";
const orangeImage =
Expand Down Expand Up @@ -100,6 +100,8 @@ const networkSpecificData = {
},
};

const superTokenFactoryABI = [{ "inputs": [{ "internalType": "contract ISuperfluid", "name": "host", "type": "address" }, { "internalType": "contract ISuperToken", "name": "superTokenLogic", "type": "address" }, { "internalType": "contract IConstantOutflowNFT", "name": "constantOutflowNFTLogic", "type": "address" }, { "internalType": "contract IConstantInflowNFT", "name": "constantInflowNFTLogic", "type": "address" }, { "internalType": "contract IPoolAdminNFT", "name": "poolAdminNFT", "type": "address" }, { "internalType": "contract IPoolMemberNFT", "name": "poolMemberNFT", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_ALREADY_EXISTS", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_DOES_NOT_EXIST", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_NON_UPGRADEABLE_IS_DEPRECATED", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_ONLY_GOVERNANCE_OWNER", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_ONLY_HOST", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_UNINITIALIZED", "type": "error" }, { "inputs": [], "name": "SUPER_TOKEN_FACTORY_ZERO_ADDRESS", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "bytes32", "name": "uuid", "type": "bytes32" }, { "indexed": false, "internalType": "address", "name": "codeAddress", "type": "address" }], "name": "CodeUpdated", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "contract ISuperToken", "name": "token", "type": "address" }], "name": "CustomSuperTokenCreated", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], "name": "Initialized", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "contract ISuperToken", "name": "token", "type": "address" }], "name": "SuperTokenCreated", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "contract ISuperToken", "name": "tokenLogic", "type": "address" }], "name": "SuperTokenLogicCreated", "type": "event" }, { "inputs": [], "name": "CONSTANT_INFLOW_NFT_LOGIC", "outputs": [{ "internalType": "contract IConstantInflowNFT", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "CONSTANT_OUTFLOW_NFT_LOGIC", "outputs": [{ "internalType": "contract IConstantOutflowNFT", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "POOL_ADMIN_NFT_LOGIC", "outputs": [{ "internalType": "contract IPoolAdminNFT", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "POOL_MEMBER_NFT_LOGIC", "outputs": [{ "internalType": "contract IPoolMemberNFT", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "_SUPER_TOKEN_LOGIC", "outputs": [{ "internalType": "contract ISuperToken", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "castrate", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_underlyingToken", "type": "address" }], "name": "computeCanonicalERC20WrapperAddress", "outputs": [{ "internalType": "address", "name": "superTokenAddress", "type": "address" }, { "internalType": "bool", "name": "isDeployed", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "contract ERC20WithTokenInfo", "name": "_underlyingToken", "type": "address" }], "name": "createCanonicalERC20Wrapper", "outputs": [{ "internalType": "contract ISuperToken", "name": "", "type": "address" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "contract ERC20WithTokenInfo", "name": "underlyingToken", "type": "address" }, { "internalType": "enum ISuperTokenFactory.Upgradability", "name": "upgradability", "type": "uint8" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "symbol", "type": "string" }], "name": "createERC20Wrapper", "outputs": [{ "internalType": "contract ISuperToken", "name": "superToken", "type": "address" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "contract IERC20", "name": "underlyingToken", "type": "address" }, { "internalType": "uint8", "name": "underlyingDecimals", "type": "uint8" }, { "internalType": "enum ISuperTokenFactory.Upgradability", "name": "upgradability", "type": "uint8" }, { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "symbol", "type": "string" }], "name": "createERC20Wrapper", "outputs": [{ "internalType": "contract ISuperToken", "name": "superToken", "type": "address" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_underlyingTokenAddress", "type": "address" }], "name": "getCanonicalERC20Wrapper", "outputs": [{ "internalType": "address", "name": "superTokenAddress", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getCodeAddress", "outputs": [{ "internalType": "address", "name": "codeAddress", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getHost", "outputs": [{ "internalType": "address", "name": "host", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getSuperTokenLogic", "outputs": [{ "internalType": "contract ISuperToken", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "initialize", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "underlyingToken", "type": "address" }, { "internalType": "address", "name": "superToken", "type": "address" }], "internalType": "struct SuperTokenFactoryBase.InitializeData[]", "name": "_data", "type": "tuple[]" }], "name": "initializeCanonicalWrapperSuperTokens", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "customSuperTokenProxy", "type": "address" }], "name": "initializeCustomSuperToken", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "proxiableUUID", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "newAddress", "type": "address" }], "name": "updateCode", "outputs": [], "stateMutability": "nonpayable", "type": "function" }]

async function getDataAsJson(url) {
let options = {
headers: {
Expand Down Expand Up @@ -159,6 +161,18 @@ async function sendMessageToSlack(data) {
req.end();
}

async function getSuperTokenLogicAddress(network) {
const rpcUrl = "https://rpc-endpoints.superfluid.dev/" + network.name
const provider = new ethers.JsonRpcProvider(rpcUrl)
const contract = new ethers.Contract(network.contractsV1.superTokenFactory, superTokenFactoryABI, provider)
try {
return await contract.getSuperTokenLogic()
} catch (e) {
console.log(e)
}
}


async function checkNetworkContractVerification(network) {
if (networkSpecificData[network.name] === undefined) {
return "";
Expand All @@ -167,6 +181,17 @@ async function checkNetworkContractVerification(network) {
contractsToCheck.nativeTokenWrapper = network.nativeTokenWrapper;
contractsToCheck.wrapperToken =
networkSpecificData[network.name].wrapperTokenAddress;
contractsToCheck.superTokenLogic = await getSuperTokenLogicAddress(network)
if (network.contractsV1.autowrap) {
contractsToCheck.autoWrapManager = network.contractsV1.autowrap.manager
contractsToCheck.autoWrapStrategy = network.contractsV1.autowrap.wrapStrategy
delete network.contractsV1.autowrap
}
const networkTokenAddressList = await getNetworkTokenAddressList(network);
contractsToCheck = {
...contractsToCheck,
...networkTokenAddressList
};
let networkMessage = "";
for (const [contractName, address] of Object.entries(contractsToCheck)) {
networkMessage += await checkIndividualContractVerification(
Expand All @@ -183,6 +208,56 @@ async function checkNetworkContractVerification(network) {
}
}

async function getNetworkTokenAddressList(network) {
return new Promise((resolve, reject) => {
let response = '';
const hostName = network.subgraphV1.hostedEndpoint ? "api.thegraph.com" : "subgraph.satsuma-prod.com";
const path = network.subgraphV1.hostedEndpoint ? network.subgraphV1.hostedEndpoint.split(hostName)[1] : network.subgraphV1.satsumaEndpoint.split(hostName)[1];
let options = {
headers: {
"Content-Type": "application/json",
"User-Agent": "Elvi.js slack bot",
},
hostname: hostName,
path: path,
method: "POST",
};

const req = https
.request(options, (res) => {
console.log("Status Code:", res.statusCode);

res.on("data", (chunk) => {
response += chunk.toString();
});

res.on("end", () => {
try {
const parsedResponse = JSON.parse(response);
const newObject = {};
parsedResponse.data.tokens.forEach((token) => {
newObject[token.symbol] = token.id;
});
resolve(newObject);
} catch (error) {
reject(error);
}
});
})
.on("error", (err) => {
console.log("Error: ", err.message);
reject(err);
});

req.write(
JSON.stringify({
query: "query { tokens(where: {isListed: true}) { symbol id }}",
})
);
req.end();
});
}

async function checkIndividualContractVerification(
network,
contractName,
Expand All @@ -197,7 +272,7 @@ async function checkIndividualContractVerification(
if (result.status === undefined) {
throw new Error(`Failed checking ${contractName}: ${contractAddress}`);
}
if(result.result === "Invalid API Key") {
if (result.result === "Invalid API Key") {
throw new Error(`Invalid API key for ${network.name}}`);
}
if (
Expand Down Expand Up @@ -235,8 +310,8 @@ async function checkIndividualContractVerification(
const lastWorkflowId = lastWorkflow.id;
const lastWorkflowUsage = await getDataAsJson(
"https://api.github.com/repos/superfluid-finance/protocol-monorepo/actions/runs/" +
lastWorkflowId +
"/timing"
lastWorkflowId +
"/timing"
);

const workflowStatus = lastWorkflow.status;
Expand All @@ -257,8 +332,8 @@ async function checkIndividualContractVerification(
async function getPrOldestCommit(prJson) {
let allCommits = await getDataAsJson(
"https://api.github.com/repos/superfluid-finance/protocol-monorepo/pulls/" +
prJson.number +
"/commits"
prJson.number +
"/commits"
);
return allCommits[allCommits.length - 1];
}
Expand Down Expand Up @@ -450,14 +525,14 @@ async function checkIndividualContractVerification(
addSectionWithImage(
webhookPayload,
"Please have a look at: *<" +
oldestDraftPRUrl +
"|" +
oldestDraftPRTitle +
">*\nColumbus would have went to America " +
americaTrips +
" times already by this time ,do something with this as this has been open for *" +
lastDraftPrUpdateBeforeDays +
"* days",
oldestDraftPRUrl +
"|" +
oldestDraftPRTitle +
">*\nColumbus would have went to America " +
americaTrips +
" times already by this time ,do something with this as this has been open for *" +
lastDraftPrUpdateBeforeDays +
"* days",
redWarningIcon,
"It took them 36 days"
);
Expand All @@ -482,10 +557,10 @@ async function checkIndividualContractVerification(
} else {
let draftMessage = oldestDraftPR
? "There are no open PRs????? *<" +
allPullRequests +
"|" +
amountOfDraftPRs +
" pull requests are in draft , you might want to look into those>*"
allPullRequests +
"|" +
amountOfDraftPRs +
" pull requests are in draft , you might want to look into those>*"
: "There are no open and draft PRs? What is this, why u no work, you might want to read this:\n*<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request|How to create a pull request>*";
addSectionWithImage(
webhookPayload,
Expand Down Expand Up @@ -516,9 +591,9 @@ async function checkIndividualContractVerification(
addContextWithImage(
webhookPayload,
"*The PR has been last updated before " +
lastUpdatedBeforeDays +
" days*\nLast commit: " +
oldestPRMessage,
lastUpdatedBeforeDays +
" days*\nLast commit: " +
oldestPRMessage,
imageToAddToContext,
imageText
);
Expand Down

0 comments on commit 5bd8e85

Please sign in to comment.