Skip to content

Commit

Permalink
Merge pull request #204 from skalenetwork/skale-contracts
Browse files Browse the repository at this point in the history
Integrate Skale contracts
  • Loading branch information
DimaStebaev authored Sep 12, 2023
2 parents 90a3bec + ec16e29 commit ce65a05
Show file tree
Hide file tree
Showing 17 changed files with 515 additions and 317 deletions.
30 changes: 0 additions & 30 deletions .eslintrc

This file was deleted.

20 changes: 20 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint-env node */
module.exports = {
extends: [
// 'eslint:all',
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
rules: {
"object-curly-spacing": [ "error", "always" ],
"padded-blocks": [ "error", "never" ],
"one-var": ["error", "consecutive"]
},
ignorePatterns: [
"dist/**",
"typechain-types/**"
]
};
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
- beta
- develop
env:
NODE_VERSION: 16
NODE_VERSION: 18

jobs:
build:
Expand Down
1 change: 1 addition & 0 deletions dictionaries/names.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ feldman
jbaylina
jordi
kavoon
nomicfoundation
payvin
payvint
stebaiev
Expand Down
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const config: HardhatUserConfig = {
]
},
typechain: {
target: "ethers-v5",
externalArtifacts: ['node_modules/@openzeppelin/upgrades-core/artifacts/[!b]*.json']
}
};
Expand Down
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@skalenetwork/upgrade-tools",
"version": "2.0.1",
"version": "3.0.0",
"description": "Scripts to support upgrades of smart contracts",
"files": [
"dist/**/*"
Expand All @@ -21,35 +21,36 @@
"fullCheck": "yarn compile && yarn lint && yarn eslint && yarn cspell && yarn slither",
"lint": "npx solhint \"contracts/**/*.sol\"",
"slither": "slither .",
"eslint": "npx eslint --cache --ext .js,.jsx,.ts,.tsx .",
"eslint": "npx eslint .",
"cspell": "npx cspell \"**/*\""
},
"devDependencies": {
"@tsconfig/recommended": "^1.0.1",
"@tsconfig/recommended": "^1.0.2",
"@typechain/ethers-v5": "^9.0.0",
"@typechain/hardhat": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"@typechain/hardhat": "^8.0.0",
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"cspell": "^7.0.0",
"eslint": "^8.15.0",
"install-peers-cli": "^2.2.0",
"solhint": "^3.3.7",
"ts-node": "^10.5.0",
"typechain": "^7.0.0",
"typechain": "^8.2.0",
"typescript": "^5.1.6"
},
"dependencies": {
"@openzeppelin/contracts-upgradeable": "^4.4.2",
"@safe-global/api-kit": "^1.3.0",
"@safe-global/protocol-kit": "^1.2.0",
"@safe-global/safe-core-sdk-types": "^2.2.0",
"@skalenetwork/skale-contracts-ethers-v5": "0.1.0-develop.0",
"axios": "^1.4.0",
"ethereumjs-util": "^7.1.4"
},
"peerDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.4",
"@openzeppelin/hardhat-upgrades": "^1.14.0",
"@openzeppelin/upgrades-core": "^1.12.0",
"@openzeppelin/upgrades-core": "^1.27.1",
"@types/mocha": "^9.1.0",
"ethers": "^5.7.2",
"hardhat": "2.8.3 - 2.16.1"
Expand Down
2 changes: 1 addition & 1 deletion src/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function getAbi(contractInterface: Interface) {
if (obj.type === "function") {
const func = obj as {name: string, type: string, inputs: object[], outputs: object[]};
func.inputs.concat(func.outputs).forEach((output: object) => {
Object.assign(output, Object.assign({name: ""}, output));
Object.assign(output, Object.assign({ name: "" }, output));
})
}
});
Expand Down
26 changes: 16 additions & 10 deletions src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { SkaleManifestData } from "./types/SkaleManifestData";
import { Artifact } from "hardhat/types";

async function _deployLibrary(libraryName: string) {
const Library = await ethers.getContractFactory(libraryName);
const library = await Library.deploy();
const
Library = await ethers.getContractFactory(libraryName),
library = await Library.deploy();
await library.deployed();
return library.address;
}
Expand Down Expand Up @@ -39,9 +40,10 @@ function _linkBytecode(artifact: Artifact, libraries: Map<string, string>) {
}

export async function getLinkedContractFactory(contractName: string, libraries: Map<string, string>) {
const cArtifact = await artifacts.readArtifact(contractName);
const linkedBytecode = _linkBytecode(cArtifact, libraries);
const ContractFactory = await ethers.getContractFactory(cArtifact.abi, linkedBytecode);
const
cArtifact = await artifacts.readArtifact(contractName),
linkedBytecode = _linkBytecode(cArtifact, libraries),
ContractFactory = await ethers.getContractFactory(cArtifact.abi, linkedBytecode);
return ContractFactory;
}

Expand All @@ -60,21 +62,25 @@ export async function getContractFactory(contract: string) {
libraryNames.push(libraryName);
}

const libraries = await deployLibraries(libraryNames);
const libraryArtifacts: {[key: string]: unknown} = {};
const
libraries = await deployLibraries(libraryNames),
libraryArtifacts: { [key: string]: unknown } = {};
for (const [libraryName, libraryAddress] of libraries.entries()) {
const { bytecode } = await artifacts.readArtifact(libraryName);
libraryArtifacts[libraryName] = {"address": libraryAddress, "bytecodeHash": hashBytecode(bytecode)};
libraryArtifacts[libraryName] = {
"address": libraryAddress,
"bytecodeHash": hashBytecode(bytecode)
};
}
let manifest;
try {
manifest = JSON.parse(await fs.readFile(await getManifestFile(), "utf-8")) as SkaleManifestData;
Object.assign(libraryArtifacts, manifest.libraries);
} finally {
if (manifest !== undefined) {
Object.assign(manifest, {libraries: libraryArtifacts});
Object.assign(manifest, { libraries: libraryArtifacts });
}
await fs.writeFile(await getManifestFile(), JSON.stringify(manifest, null, 4));
}
return await getLinkedContractFactory(contract, libraries);
}
}
67 changes: 35 additions & 32 deletions src/gnosis-safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,23 @@ export async function createMultiSendTransaction(safeAddress: string, transactio
});
}

const safeService = await getSafeService();
const nonce = await safeService.getNextNonce(safeAddress);
const
safeService = await getSafeService(),
nonce = await safeService.getNextNonce(safeAddress);
console.log("Will send tx to Gnosis with nonce", nonce);

const options = {
safeTxGas: "0", // Max gas to use in the transaction
baseGas: "0", // Gas costs not related to the transaction execution (signature check, refund payment...)
gasPrice: "0", // Gas price used for the refund calculation
gasToken: ethers.constants.AddressZero, // Token address (hold by the Safe) to be used as a refund to the sender, if `null` is Ether
refundReceiver: ethers.constants.AddressZero, // Address of receiver of gas payment (or `null` if tx.origin)
nonce: nonce // Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
}

const ethAdapter = await getEthAdapter();
const safeSdk = await Safe.create({ ethAdapter, safeAddress })
const safeTransaction = await safeSdk.createTransaction({ safeTransactionData, options })
const
options = {
safeTxGas: "0", // Max gas to use in the transaction
baseGas: "0", // Gas costs not related to the transaction execution (signature check, refund payment...)
gasPrice: "0", // Gas price used for the refund calculation
gasToken: ethers.constants.AddressZero, // Token address (hold by the Safe) to be used as a refund to the sender, if `null` is Ether
refundReceiver: ethers.constants.AddressZero, // Address of receiver of gas payment (or `null` if tx.origin)
nonce: nonce // Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
},
ethAdapter = await getEthAdapter(),
safeSdk = await Safe.create({ ethAdapter, safeAddress }),
safeTransaction = await safeSdk.createTransaction({ safeTransactionData, options });

await estimateSafeTransaction(safeAddress, safeTransactionData);

Expand Down Expand Up @@ -78,12 +79,13 @@ async function estimateSafeTransaction(safeAddress: string, safeTransactionData:
}

async function proposeTransaction(safeAddress: string, safeTransaction: SafeTransaction) {
const [ safeOwner ] = await ethers.getSigners();
const ethAdapter = await getEthAdapter();
const safeSdk = await Safe.create({ ethAdapter, safeAddress })
const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);
const senderSignature = await safeSdk.signTransactionHash(safeTxHash);
const safeService = await getSafeService();
const
[ safeOwner ] = await ethers.getSigners(),
ethAdapter = await getEthAdapter(),
safeSdk = await Safe.create({ ethAdapter, safeAddress }),
safeTxHash = await safeSdk.getTransactionHash(safeTransaction),
senderSignature = await safeSdk.signTransactionHash(safeTxHash),
safeService = await getSafeService();
await safeService.proposeTransaction({
safeAddress,
safeTransactionData: safeTransaction.data,
Expand All @@ -94,22 +96,23 @@ async function proposeTransaction(safeAddress: string, safeTransaction: SafeTran
}

async function getEthAdapter(): Promise<EthersAdapter> {
const [safeOwner] = await ethers.getSigners();
const ethAdapter = new EthersAdapter({
ethers,
signerOrProvider: safeOwner
});
const
[safeOwner] = await ethers.getSigners(),
ethAdapter = new EthersAdapter({
ethers,
signerOrProvider: safeOwner
});
return ethAdapter;

}

async function getSafeService() {
const chainId = (await ethers.provider.getNetwork()).chainId;
const ethAdapter: EthersAdapter = await getEthAdapter();
const safeService = new SafeApiKit({
txServiceUrl: getSafeTransactionUrl(chainId),
ethAdapter
});
const
chainId = (await ethers.provider.getNetwork()).chainId,
ethAdapter: EthersAdapter = await getEthAdapter(),
safeService = new SafeApiKit({
txServiceUrl: getSafeTransactionUrl(chainId),
ethAdapter
});
return safeService;
}

Expand Down
43 changes: 23 additions & 20 deletions src/submitters/auto-submitter.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { getManifestAdmin } from "@openzeppelin/hardhat-upgrades/dist/admin";
import { UnsignedTransaction } from "ethers";
import { Transaction } from "ethers";
import { ProxyAdmin } from "../../typechain-types";
import { Submitter } from "./submitter";
import hre, { ethers } from "hardhat";
import { EoaSubmitter } from "./eoa-submitter";
import { SafeSubmitter } from "./safe-submitter";
import chalk from "chalk";
import { SafeImaLegacyMarionetteSubmitter } from "./safe-ima-legacy-marionette-submitter";
import { SkaleABIFile } from "../types/SkaleABIFile";
import { promises as fs } from 'fs';
import { Marionette, MARIONETTE_ADDRESS } from "./types/marionette";
import { MARIONETTE_ADDRESS } from "./types/marionette";
import { skaleContracts } from "@skalenetwork/skale-contracts-ethers-v5";

export class AutoSubmitter extends Submitter {

async submit(transactions: UnsignedTransaction[]) {
async submit(transactions: Transaction[]) {
let submitter: Submitter;
const proxyAdmin = await getManifestAdmin(hre) as ProxyAdmin;
const owner = await proxyAdmin.owner();
// TODO: remove unknown when move everything to ethers 6
const
proxyAdmin = await getManifestAdmin(hre) as unknown as ProxyAdmin,
owner = await proxyAdmin.owner();
if (await hre.ethers.provider.getCode(owner) === "0x") {
console.log("Owner is not a contract");
submitter = new EoaSubmitter();
Expand All @@ -26,10 +26,11 @@ export class AutoSubmitter extends Submitter {
if (ethers.utils.getAddress(owner) == ethers.utils.getAddress(MARIONETTE_ADDRESS)) {
console.log("Marionette owner is detected");

const imaAbi = await this._getImaAbi();
const safeAddress = this._getSafeAddress();
const schainHash = this._getSchainHash();
const mainnetChainId = this._getMainnetChainId();
const
imaInstance = await this._getImaInstance(),
mainnetChainId = this._getMainnetChainId(),
safeAddress = this._getSafeAddress(),
schainHash = this._getSchainHash();

// TODO: after marionette has multiSend functionality
// query version and properly select a submitter
Expand All @@ -55,11 +56,10 @@ export class AutoSubmitter extends Submitter {

submitter = new SafeImaLegacyMarionetteSubmitter(
safeAddress,
imaAbi,
imaInstance,
schainHash,
mainnetChainId
)

} else {
// assuming owner is a Gnosis Safe
console.log("Using Gnosis Safe");
Expand All @@ -72,12 +72,15 @@ export class AutoSubmitter extends Submitter {

// private

async _getImaAbi() {
if (!process.env.IMA_ABI) {
console.log(chalk.red("Set path to ima abi to IMA_ABI environment variable"));
async _getImaInstance() {
if (!process.env.IMA) {
console.log(chalk.red("Set target IMA alias to IMA environment variable"));
process.exit(1);
}
return JSON.parse(await fs.readFile(process.env.IMA_ABI, "utf-8")) as SkaleABIFile;
const
network = await skaleContracts.getNetworkByProvider(ethers.provider),
ima = await network.getProject("ima");
return await ima.getInstance(process.env.IMA);
}

_getSafeAddress() {
Expand Down Expand Up @@ -137,7 +140,7 @@ export class AutoSubmitter extends Submitter {
"stateMutability": "view",
"type": "function"
}],
hre.ethers.provider) as Marionette;
hre.ethers.provider);

// If gas estimation doesn't revert then an execution is possible
// given the provided function selector
Expand All @@ -150,4 +153,4 @@ export class AutoSubmitter extends Submitter {
return false;
}
}
}
}
Loading

0 comments on commit ce65a05

Please sign in to comment.