feat(per): Per solidity sdk (#1325)

* feat(per): add solidity sdk

* refactor(evm): refactor abi generator script

* feat(evm): add abi_generator package
This commit is contained in:
Amin Moghaddam 2024-02-29 08:10:50 +01:00 committed by GitHub
parent e986b69c9a
commit 9e7b6d94ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 347 additions and 261 deletions

View File

@ -2,10 +2,8 @@
"name": "@pythnetwork/express-relay-evm-js",
"version": "0.1.1",
"description": "Utilities for interacting with the express relay protocol",
"homepage": "https://pyth.network",
"author": {
"name": "Pyth Data Association"
},
"homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/js",
"author": "Douro Labs",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (C) 2024 Lavra Holdings Limited - All Rights Reserved
pragma solidity ^0.8.0;
interface IPERFeeReceiver {
// Receive the proceeds of an auction.
// @param permissionKey The permission key where the auction was conducted on.
function receiveAuctionProceedings(
bytes calldata permissionKey
) external payable;
}

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (C) 2024 Lavra Holdings Limited - All Rights Reserved
pragma solidity ^0.8.0;
interface IPERMulticall {
// Check if the combination of protocol and permissionKey is allowed within this transaction.
// This will return true if and only if it's being called while executing the auction winner call.
// @param protocol The address of the protocol that is gating an action behind this permission
// @param permissionKey The permission key that is being checked
// @return permissioned True if the permission is allowed, false otherwise
function isPermissioned(
address protocol,
bytes calldata permissionKey
) external view returns (bool permissioned);
}

View File

@ -0,0 +1,27 @@
# Pyth Express Relay Solidity SDK
## Install
### Truffle/Hardhat
If you are using Truffle or Hardhat, simply install the NPM package:
```bash
npm install @pythnetwork/per-sdk-solidity
```
### Foundry
If you are using Foundry, you will need to create an NPM project if you don't already have one.
From the root directory of your project, run:
```bash
npm init -y
npm install @pythnetwork/per-sdk-solidity
```
Then add the following line to your `remappings.txt` file:
```text
@pythnetwork/per-sdk-solidity/=node_modules/@pythnetwork/per-sdk-solidity
```

View File

@ -0,0 +1,15 @@
[
{
"inputs": [
{
"internalType": "bytes",
"name": "permissionKey",
"type": "bytes"
}
],
"name": "receiveAuctionProceedings",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]

View File

@ -0,0 +1,26 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "protocol",
"type": "address"
},
{
"internalType": "bytes",
"name": "permissionKey",
"type": "bytes"
}
],
"name": "isPermissioned",
"outputs": [
{
"internalType": "bool",
"name": "permissioned",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@ -0,0 +1,34 @@
{
"name": "@pythnetwork/per-sdk-solidity",
"version": "0.1.0",
"description": "Solidity SDK for interacting with Pyth express relay contracts",
"repository": {
"type": "git",
"url": "https://github.com/pyth-network/pyth-crosschain",
"directory": "express_relay/sdk/solidity"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"format": "npx prettier --write .",
"generate-abi": "npx generate-abis IPERMulticall IPERFeeReceiver",
"check-abi": "git diff --exit-code abis"
},
"keywords": [
"pyth",
"oracle",
"relay"
],
"author": "Douro Labs",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
},
"homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/solidity",
"devDependencies": {
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"abi_generator": "*"
}
}

266
package-lock.json generated
View File

@ -7,6 +7,7 @@
"name": "root",
"workspaces": [
"express_relay/sdk/js",
"express_relay/sdk/solidity",
"governance/xc_admin/packages/*",
"governance/multisig_wh_message_builder",
"price_pusher",
@ -17,6 +18,7 @@
"target_chains/cosmwasm/tools",
"target_chains/cosmwasm/deploy-scripts",
"target_chains/ethereum/contracts",
"target_chains/ethereum/abi_generator",
"target_chains/ethereum/entropy_sdk/solidity",
"target_chains/ethereum/sdk/js",
"target_chains/ethereum/sdk/solidity",
@ -2065,6 +2067,16 @@
"node": ">=12"
}
},
"express_relay/sdk/solidity": {
"name": "@pythnetwork/per-sdk-solidity",
"version": "0.1.0",
"license": "Apache-2.0",
"devDependencies": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"governance/multisig_wh_message_builder": {
"name": "@pythnetwork/pyth-multisig-wh-message-builder",
"version": "0.1.0",
@ -13166,6 +13178,10 @@
"resolved": "express_relay/sdk/js",
"link": true
},
"node_modules/@pythnetwork/per-sdk-solidity": {
"resolved": "express_relay/sdk/solidity",
"link": true
},
"node_modules/@pythnetwork/price-pusher": {
"resolved": "price_pusher",
"link": true
@ -21721,6 +21737,10 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"node_modules/abi_generator": {
"resolved": "target_chains/ethereum/abi_generator",
"link": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@ -58311,6 +58331,56 @@
"node": ">=12"
}
},
"target_chains/ethereum/abi_generator": {
"version": "0.0.0",
"license": "Apache 2",
"bin": {
"generate-abis": "src/generate.js"
},
"devDependencies": {
"prettier": "^2.7.1",
"solc": "^0.8.15"
}
},
"target_chains/ethereum/abi_generator/node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"target_chains/ethereum/abi_generator/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"target_chains/ethereum/abi_generator/node_modules/solc": {
"version": "0.8.24",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.24.tgz",
"integrity": "sha512-G5yUqjTUPc8Np74sCFwfsevhBPlUifUOfhYrgyu6CmYlC6feSw0YS6eZW47XDT23k3JYdKx5nJ+Q7whCEmNcoA==",
"dev": true,
"dependencies": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
},
"bin": {
"solcjs": "solc.js"
},
"engines": {
"node": ">=10.0.0"
}
},
"target_chains/ethereum/contracts": {
"name": "@pythnetwork/pyth-evm-contract",
"version": "1.4.0",
@ -58902,48 +58972,9 @@
"version": "1.1.3",
"license": "Apache-2.0",
"devDependencies": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
}
},
"target_chains/ethereum/entropy_sdk/solidity/node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"target_chains/ethereum/entropy_sdk/solidity/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"target_chains/ethereum/entropy_sdk/solidity/node_modules/solc": {
"version": "0.8.21",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.21.tgz",
"integrity": "sha512-N55ogy2dkTRwiONbj4e6wMZqUNaLZkiRcjGyeafjLYzo/tf/IvhHY5P5wpe+H3Fubh9idu071i8eOGO31s1ylg==",
"dev": true,
"dependencies": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
},
"bin": {
"solcjs": "solc.js"
},
"engines": {
"node": ">=10.0.0"
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"target_chains/ethereum/examples/coin_flip/app": {
@ -59486,48 +59517,9 @@
"version": "2.4.1",
"license": "Apache-2.0",
"devDependencies": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
}
},
"target_chains/ethereum/sdk/solidity/node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"target_chains/ethereum/sdk/solidity/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"target_chains/ethereum/sdk/solidity/node_modules/solc": {
"version": "0.8.19",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.19.tgz",
"integrity": "sha512-yqurS3wzC4LdEvmMobODXqprV4MYJcVtinuxgrp61ac8K2zz40vXA0eSAskSHPgv8dQo7Nux39i3QBsHx4pqyA==",
"dev": true,
"dependencies": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
},
"bin": {
"solcjs": "solc.js"
},
"engines": {
"node": ">=10.0.0"
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"target_chains/solana/sdk/js/pyth_solana_receiver": {
@ -67482,38 +67474,9 @@
"@pythnetwork/entropy-sdk-solidity": {
"version": "file:target_chains/ethereum/entropy_sdk/solidity",
"requires": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
},
"dependencies": {
"commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true
},
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
},
"solc": {
"version": "0.8.21",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.21.tgz",
"integrity": "sha512-N55ogy2dkTRwiONbj4e6wMZqUNaLZkiRcjGyeafjLYzo/tf/IvhHY5P5wpe+H3Fubh9idu071i8eOGO31s1ylg==",
"dev": true,
"requires": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
}
}
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"@pythnetwork/eth-coin-flip-example": {
@ -68814,6 +68777,14 @@
}
}
},
"@pythnetwork/per-sdk-solidity": {
"version": "file:express_relay/sdk/solidity",
"requires": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"@pythnetwork/price-pusher": {
"version": "file:price_pusher",
"requires": {
@ -71462,38 +71433,9 @@
"@pythnetwork/pyth-sdk-solidity": {
"version": "file:target_chains/ethereum/sdk/solidity",
"requires": {
"abi_generator": "*",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
},
"dependencies": {
"commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true
},
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
},
"solc": {
"version": "0.8.19",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.19.tgz",
"integrity": "sha512-yqurS3wzC4LdEvmMobODXqprV4MYJcVtinuxgrp61ac8K2zz40vXA0eSAskSHPgv8dQo7Nux39i3QBsHx4pqyA==",
"dev": true,
"requires": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
}
}
"prettier-plugin-solidity": "^1.0.0-rc.1"
}
},
"@pythnetwork/pyth-solana-receiver": {
@ -78479,6 +78421,42 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"abi_generator": {
"version": "file:target_chains/ethereum/abi_generator",
"requires": {
"prettier": "^2.7.1",
"solc": "^0.8.15"
},
"dependencies": {
"commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true
},
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
},
"solc": {
"version": "0.8.24",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.8.24.tgz",
"integrity": "sha512-G5yUqjTUPc8Np74sCFwfsevhBPlUifUOfhYrgyu6CmYlC6feSw0YS6eZW47XDT23k3JYdKx5nJ+Q7whCEmNcoA==",
"dev": true,
"requires": {
"command-exists": "^1.2.8",
"commander": "^8.1.0",
"follow-redirects": "^1.12.1",
"js-sha3": "0.8.0",
"memorystream": "^0.3.1",
"semver": "^5.5.0",
"tmp": "0.0.33"
}
}
}
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",

View File

@ -2,6 +2,7 @@
"name": "root",
"workspaces": [
"express_relay/sdk/js",
"express_relay/sdk/solidity",
"governance/xc_admin/packages/*",
"governance/multisig_wh_message_builder",
"price_pusher",
@ -12,6 +13,7 @@
"target_chains/cosmwasm/tools",
"target_chains/cosmwasm/deploy-scripts",
"target_chains/ethereum/contracts",
"target_chains/ethereum/abi_generator",
"target_chains/ethereum/entropy_sdk/solidity",
"target_chains/ethereum/sdk/js",
"target_chains/ethereum/sdk/solidity",

View File

@ -0,0 +1,28 @@
{
"name": "abi_generator",
"version": "0.0.0",
"private": "true",
"description": "",
"author": "",
"homepage": "https://github.com/pyth-network/pyth-crosschain",
"license": "Apache 2",
"main": "src/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/pyth-network/pyth-crosschain.git"
},
"bugs": {
"url": "https://github.com/pyth-network/pyth-crosschain/issues"
},
"bin": {
"generate-abis": "src/generate.js"
},
"scripts": {
"format": "npx prettier --write .",
"generate": "node src/generate.js"
},
"devDependencies": {
"prettier": "^2.7.1",
"solc": "^0.8.15"
}
}

View File

@ -0,0 +1,5 @@
#!/usr/bin/env node
const { generateAbi } = require("./index");
const contracts = process.argv.slice(2);
generateAbi(contracts);

View File

@ -0,0 +1,56 @@
const fs = require("fs");
const solc = require("solc");
/**
* Generate ABI files for the given contracts and save them in the `abis` directory.
* Creates the `abis` directory if it does not exist.
*
* @param contracts List of file names assuming each contract is in the file with the same name.
*/
function generateAbi(contracts) {
var sources = {};
var outputSelection = {};
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
sources[contractFile] = {
content: fs.readFileSync(contractFile).toString(),
};
outputSelection[contractFile] = {};
outputSelection[contractFile][contract] = ["abi"];
}
var input = {
language: "Solidity",
sources,
settings: {
outputSelection,
},
};
function findImports(path) {
return {
contents: fs.readFileSync(path).toString(),
};
}
const output = JSON.parse(
solc.compile(JSON.stringify(input), { import: findImports })
);
if (!fs.existsSync("abis")) {
fs.mkdirSync("abis");
}
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
const abi = output.contracts[contractFile][contract].abi;
fs.writeFileSync(
`abis/${contract}.json`,
JSON.stringify(abi, null, 2) + "\n"
);
}
}
module.exports = { generateAbi };

View File

@ -12,7 +12,7 @@
},
"scripts": {
"format": "npx prettier --write .",
"generate-abi": "node scripts/generateAbi.js",
"generate-abi": "npx generate-abis IEntropy EntropyErrors EntropyEvents EntropyStructs",
"check-abi": "git diff --exit-code abis"
},
"keywords": [
@ -29,6 +29,6 @@
"devDependencies": {
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
"abi_generator": "*"
}
}

View File

@ -1,54 +0,0 @@
const fs = require("fs");
const solc = require("solc");
// Assuming each contract is in the file with the same name.
var contracts = [
"IEntropy",
"EntropyErrors",
"EntropyEvents",
"EntropyStructs",
];
var sources = {};
var outputSelection = {};
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
sources[contractFile] = {
content: fs.readFileSync(contractFile).toString(),
};
outputSelection[contractFile] = {};
outputSelection[contractFile][contract] = ["abi"];
}
var input = {
language: "Solidity",
sources,
settings: {
outputSelection,
},
};
function findImports(path) {
return {
contents: fs.readFileSync(path).toString(),
};
}
const output = JSON.parse(
solc.compile(JSON.stringify(input), { import: findImports })
);
if (!fs.existsSync("abis")) {
fs.mkdirSync("abis");
}
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
const abi = output.contracts[contractFile][contract].abi;
fs.writeFileSync(
`abis/${contract}.json`,
JSON.stringify(abi, null, 2) + "\n"
);
}

View File

@ -9,7 +9,7 @@
},
"scripts": {
"format": "npx prettier --write .",
"generate-abi": "node scripts/generateAbi.js",
"generate-abi": "npx generate-abis IPyth IPythEvents AbstractPyth MockPyth PythErrors",
"check-abi": "git diff --exit-code abis",
"build": "solcjs --bin MockPyth.sol --base-path . -o build/"
},
@ -27,6 +27,6 @@
"devDependencies": {
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"solc": "^0.8.15"
"abi_generator": "*"
}
}

View File

@ -1,55 +0,0 @@
const fs = require("fs");
const solc = require("solc");
// Assuming each contract is in the file with the same name.
var contracts = [
"IPyth",
"IPythEvents",
"AbstractPyth",
"MockPyth",
"PythErrors",
];
var sources = {};
var outputSelection = {};
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
sources[contractFile] = {
content: fs.readFileSync(contractFile).toString(),
};
outputSelection[contractFile] = {};
outputSelection[contractFile][contract] = ["abi"];
}
var input = {
language: "Solidity",
sources,
settings: {
outputSelection,
},
};
function findImports(path) {
return {
contents: fs.readFileSync(path).toString(),
};
}
const output = JSON.parse(
solc.compile(JSON.stringify(input), { import: findImports })
);
if (!fs.existsSync("abis")) {
fs.mkdirSync("abis");
}
for (let contract of contracts) {
const contractFile = `${contract}.sol`;
const abi = output.contracts[contractFile][contract].abi;
fs.writeFileSync(
`abis/${contract}.json`,
JSON.stringify(abi, null, 2) + "\n"
);
}