@skalenetwork/upgrade-tools 0.0.1-custom.1 → 0.0.1-custom.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/abi.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Interface } from "ethers/lib/utils";
2
+ export declare function getAbi(contractInterface: Interface): [];
package/dist/abi.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAbi = void 0;
4
+ function getAbi(contractInterface) {
5
+ const abi = JSON.parse(contractInterface.format("json"));
6
+ abi.forEach((obj) => {
7
+ if (obj.type === "function") {
8
+ const func = obj;
9
+ func.inputs.concat(func.outputs).forEach((output) => {
10
+ Object.assign(output, Object.assign({ name: "" }, output));
11
+ });
12
+ }
13
+ });
14
+ return abi;
15
+ }
16
+ exports.getAbi = getAbi;
@@ -0,0 +1,25 @@
1
+ import type { ethers } from "ethers";
2
+ import { HardhatEthersHelpers } from "@nomiclabs/hardhat-ethers/types";
3
+ declare type Ethers = typeof ethers & HardhatEthersHelpers;
4
+ interface SafeMultisigTransaction {
5
+ safe: string;
6
+ to: string;
7
+ value: number;
8
+ data?: string;
9
+ operation: number;
10
+ gasToken?: string;
11
+ safeTxGas: number;
12
+ baseGas: number;
13
+ gasPrice: number;
14
+ refundReceiver?: string;
15
+ nonce: number;
16
+ contractTransactionHash: string;
17
+ sender: string;
18
+ signature?: string;
19
+ origin?: string;
20
+ }
21
+ export declare function getSafeTransactionUrl(chainId: number): string;
22
+ export declare function getSafeRelayUrl(chainId: number): string;
23
+ export declare function createMultiSendTransaction(ethers: Ethers, safeAddress: string, privateKey: string, transactions: string[], isSafeMock?: boolean): Promise<SafeMultisigTransaction>;
24
+ export declare function sendSafeTransaction(safe: string, chainId: number, safeTx: SafeMultisigTransaction): Promise<void>;
25
+ export {};
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
+ return new (P || (P = Promise))(function (resolve, reject) {
24
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
28
+ });
29
+ };
30
+ var __importDefault = (this && this.__importDefault) || function (mod) {
31
+ return (mod && mod.__esModule) ? mod : { "default": mod };
32
+ };
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.sendSafeTransaction = exports.createMultiSendTransaction = exports.getSafeRelayUrl = exports.getSafeTransactionUrl = void 0;
35
+ const axios_1 = __importDefault(require("axios"));
36
+ const ethers_eip712_1 = require("ethers-eip712");
37
+ const ethUtil = __importStar(require("ethereumjs-util"));
38
+ const chalk_1 = __importDefault(require("chalk"));
39
+ var Network;
40
+ (function (Network) {
41
+ Network[Network["MAINNET"] = 1] = "MAINNET";
42
+ Network[Network["RINKEBY"] = 4] = "RINKEBY";
43
+ Network[Network["GANACHE"] = 1337] = "GANACHE";
44
+ Network[Network["HARDHAT"] = 31337] = "HARDHAT";
45
+ })(Network || (Network = {}));
46
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
47
+ const ADDRESSES = {
48
+ multiSend: {
49
+ [Network.MAINNET]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
50
+ [Network.RINKEBY]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
51
+ },
52
+ };
53
+ const URLS = {
54
+ safe_transaction: {
55
+ [Network.MAINNET]: "https://safe-transaction.mainnet.gnosis.io",
56
+ [Network.RINKEBY]: "https://safe-transaction.rinkeby.gnosis.io",
57
+ },
58
+ safe_relay: {
59
+ [Network.MAINNET]: "https://safe-relay.mainnet.gnosis.io",
60
+ [Network.RINKEBY]: "https://safe-relay.rinkeby.gnosis.io",
61
+ }
62
+ };
63
+ function getMultiSendAddress(chainId) {
64
+ if (chainId === Network.MAINNET) {
65
+ return ADDRESSES.multiSend[chainId];
66
+ }
67
+ else if (chainId === Network.RINKEBY) {
68
+ return ADDRESSES.multiSend[chainId];
69
+ }
70
+ else if ([Network.GANACHE, Network.HARDHAT].includes(chainId)) {
71
+ return ZERO_ADDRESS;
72
+ }
73
+ else {
74
+ throw Error(`Can't get multiSend contract at network with chainId = ${chainId}`);
75
+ }
76
+ }
77
+ function getSafeTransactionUrl(chainId) {
78
+ if (chainId === Network.MAINNET) {
79
+ return URLS.safe_transaction[chainId];
80
+ }
81
+ else if (chainId === 4) {
82
+ return URLS.safe_transaction[chainId];
83
+ }
84
+ else {
85
+ throw Error(`Can't get safe-transaction url at network with chainId = ${chainId}`);
86
+ }
87
+ }
88
+ exports.getSafeTransactionUrl = getSafeTransactionUrl;
89
+ function getSafeRelayUrl(chainId) {
90
+ if (chainId === 1) {
91
+ return URLS.safe_relay[chainId];
92
+ }
93
+ else if (chainId === 4) {
94
+ return URLS.safe_relay[chainId];
95
+ }
96
+ else {
97
+ throw Error(`Can't get safe-relay url at network with chainId = ${chainId}`);
98
+ }
99
+ }
100
+ exports.getSafeRelayUrl = getSafeRelayUrl;
101
+ function concatTransactions(transactions) {
102
+ return "0x" + transactions.map((transaction) => {
103
+ if (transaction.startsWith("0x")) {
104
+ return transaction.slice(2);
105
+ }
106
+ else {
107
+ return transaction;
108
+ }
109
+ }).join("");
110
+ }
111
+ function createMultiSendTransaction(ethers, safeAddress, privateKey, transactions, isSafeMock = false) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ const chainId = (yield ethers.provider.getNetwork()).chainId;
114
+ const multiSendAddress = getMultiSendAddress(chainId);
115
+ const multiSendAbi = [{ "constant": false, "inputs": [{ "internalType": "bytes", "name": "transactions", "type": "bytes" }], "name": "multiSend", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }];
116
+ const multiSend = new ethers.Contract(multiSendAddress, new ethers.utils.Interface(multiSendAbi), ethers.provider);
117
+ let nonce = 0;
118
+ if (!isSafeMock) {
119
+ try {
120
+ const nonceResponse = yield axios_1.default.get(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safeAddress}/`);
121
+ nonce = nonceResponse.data.nonce;
122
+ }
123
+ catch (e) {
124
+ if (!(e instanceof Error) || !e.toString().startsWith("Error: Can't get safe-transaction url")) {
125
+ throw e;
126
+ }
127
+ }
128
+ }
129
+ const tx = {
130
+ "safe": safeAddress,
131
+ "to": multiSend.address,
132
+ "value": 0,
133
+ "data": multiSend.interface.encodeFunctionData("multiSend", [concatTransactions(transactions)]),
134
+ "operation": 1,
135
+ "gasToken": ZERO_ADDRESS,
136
+ "safeTxGas": 0,
137
+ "baseGas": 0,
138
+ "gasPrice": 0,
139
+ "refundReceiver": ZERO_ADDRESS,
140
+ "nonce": nonce, // Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
141
+ };
142
+ const typedData = {
143
+ types: {
144
+ EIP712Domain: [
145
+ { name: "verifyingContract", type: "address" },
146
+ ],
147
+ SafeTx: [
148
+ { type: "address", name: "to" },
149
+ { type: "uint256", name: "value" },
150
+ { type: "bytes", name: "data" },
151
+ { type: "uint8", name: "operation" },
152
+ { type: "uint256", name: "safeTxGas" },
153
+ { type: "uint256", name: "baseGas" },
154
+ { type: "uint256", name: "gasPrice" },
155
+ { type: "address", name: "gasToken" },
156
+ { type: "address", name: "refundReceiver" },
157
+ { type: "uint256", name: "nonce" },
158
+ ]
159
+ },
160
+ primaryType: 'SafeTx',
161
+ domain: {
162
+ verifyingContract: safeAddress
163
+ },
164
+ message: Object.assign(Object.assign({}, tx), { data: ethers.utils.arrayify(tx.data) })
165
+ };
166
+ const digest = ethers_eip712_1.TypedDataUtils.encodeDigest(typedData);
167
+ const digestHex = ethers.utils.hexlify(digest);
168
+ const privateKeyBuffer = ethUtil.toBuffer(privateKey);
169
+ const { r, s, v } = ethUtil.ecsign(ethUtil.toBuffer(digestHex), privateKeyBuffer);
170
+ const signature = ethUtil.toRpcSig(v, r, s).toString();
171
+ const txToSend = Object.assign(Object.assign({}, tx), { "contractTransactionHash": digestHex,
172
+ // Owner of the Safe proposing the transaction. Must match one of the signatures
173
+ "sender": ethers.utils.getAddress(ethUtil.bufferToHex(ethUtil.privateToAddress(privateKeyBuffer))), "signature": signature, "origin": "Upgrade skale-manager" // Give more information about the transaction, e.g. "My Custom Safe app"
174
+ });
175
+ return txToSend;
176
+ });
177
+ }
178
+ exports.createMultiSendTransaction = createMultiSendTransaction;
179
+ function sendSafeTransaction(safe, chainId, safeTx) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ try {
182
+ console.log("Estimate gas");
183
+ const estimateRequest = safeTx;
184
+ try {
185
+ const estimateResponse = yield axios_1.default.post(`${getSafeRelayUrl(chainId)}/api/v2/safes/${safe}/transactions/estimate/`, estimateRequest);
186
+ console.log(chalk_1.default.cyan(`Recommend to set gas limit to ${parseInt(estimateResponse.data.safeTxGas, 10) + parseInt(estimateResponse.data.baseGas, 10)}`));
187
+ }
188
+ catch (e) {
189
+ console.log(chalk_1.default.red("Failed to estimate gas"));
190
+ console.log(e);
191
+ }
192
+ console.log(chalk_1.default.green("Send transaction to gnosis safe"));
193
+ yield axios_1.default.post(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safe}/multisig-transactions/`, safeTx);
194
+ }
195
+ catch (e) {
196
+ if (axios_1.default.isAxiosError(e)) {
197
+ if (e.response) {
198
+ console.log(JSON.stringify(e.response.data, null, 4));
199
+ console.log(chalk_1.default.red(`Request failed with ${e.response.status} code`));
200
+ }
201
+ else {
202
+ console.log(chalk_1.default.red("Request failed with unknown reason"));
203
+ }
204
+ }
205
+ throw e;
206
+ }
207
+ });
208
+ }
209
+ exports.sendSafeTransaction = sendSafeTransaction;
@@ -1,6 +1,4 @@
1
1
  import { HardhatUserConfig } from "hardhat/config";
2
2
  import "@nomiclabs/hardhat-ethers";
3
-
4
- const config: HardhatUserConfig = {};
5
-
3
+ declare const config: HardhatUserConfig;
6
4
  export default config;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("@nomiclabs/hardhat-ethers");
4
+ const config = {};
5
+ exports.default = config;
File without changes
@@ -0,0 +1,2 @@
1
+ import { BigNumber } from "ethers";
2
+ export declare function encodeTransaction(operation: 0 | 1, to: string, value: BigNumber | number, data: string): string;
@@ -1,33 +1,32 @@
1
- import { BigNumber } from "ethers";
2
-
3
- function padWithZeros(value: string, targetLength: number) {
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeTransaction = void 0;
4
+ const ethers_1 = require("ethers");
5
+ function padWithZeros(value, targetLength) {
4
6
  return ("0".repeat(targetLength) + value).slice(-targetLength);
5
7
  }
6
-
7
- export function encodeTransaction(operation: 0 | 1, to: string, value: BigNumber | number, data: string) {
8
+ function encodeTransaction(operation, to, value, data) {
8
9
  /// operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte),
9
10
  /// to as a address (=> 20 bytes),
10
11
  /// value as a uint256 (=> 32 bytes),
11
12
  /// data length as a uint256 (=> 32 bytes),
12
13
  /// data as bytes.
13
-
14
14
  let _operation;
15
15
  if (operation === 0) {
16
16
  _operation = "00";
17
- } else if (operation === 1) {
17
+ }
18
+ else if (operation === 1) {
18
19
  _operation = "01";
19
- } else {
20
+ }
21
+ else {
20
22
  throw Error(`Operation has an incorrect value`);
21
23
  }
22
-
23
24
  let _to = to;
24
25
  if (to.startsWith("0x")) {
25
26
  _to = _to.slice(2);
26
27
  }
27
28
  _to = padWithZeros(_to, 20 * 2);
28
-
29
- const _value = padWithZeros(BigNumber.from(value).toHexString().slice(2), 32 * 2);
30
-
29
+ const _value = padWithZeros(ethers_1.BigNumber.from(value).toHexString().slice(2), 32 * 2);
31
30
  let _data = data;
32
31
  if (data.startsWith("0x")) {
33
32
  _data = _data.slice(2);
@@ -35,9 +34,7 @@ export function encodeTransaction(operation: 0 | 1, to: string, value: BigNumber
35
34
  if (_data.length % 2 !== 0) {
36
35
  _data = "0" + _data;
37
36
  }
38
-
39
37
  const _dataLength = padWithZeros((_data.length / 2).toString(16), 32 * 2);
40
-
41
38
  return "0x" + [
42
39
  _operation,
43
40
  _to,
@@ -45,4 +42,5 @@ export function encodeTransaction(operation: 0 | 1, to: string, value: BigNumber
45
42
  _dataLength,
46
43
  _data,
47
44
  ].join("");
48
- }
45
+ }
46
+ exports.encodeTransaction = encodeTransaction;
@@ -1,14 +1,12 @@
1
1
  import { ManifestData } from "@openzeppelin/upgrades-core";
2
-
3
2
  export interface SkaleManifestData extends ManifestData {
4
3
  libraries: {
5
4
  [libraryName: string]: {
6
- address: string
7
- bytecodeHash: string
8
- }
9
- }
5
+ address: string;
6
+ bytecodeHash: string;
7
+ };
8
+ };
10
9
  }
11
-
12
10
  export interface SkaleABIFile {
13
- [key: string]: string | []
14
- }
11
+ [key: string]: string | [];
12
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,2 @@
1
+ export declare function verify(contractName: string, contractAddress: string, constructorArguments: object): Promise<void>;
2
+ export declare function verifyProxy(contractName: string, proxyAddress: string, constructorArguments: object): Promise<void>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.verifyProxy = exports.verify = void 0;
16
+ const hardhat_1 = require("hardhat");
17
+ const chalk_1 = __importDefault(require("chalk"));
18
+ const upgrades_core_1 = require("@openzeppelin/upgrades-core");
19
+ function verify(contractName, contractAddress, constructorArguments) {
20
+ return __awaiter(this, void 0, void 0, function* () {
21
+ if (![1337, 31337].includes((yield hardhat_1.ethers.provider.getNetwork()).chainId)) {
22
+ for (let retry = 0; retry <= 5; ++retry) {
23
+ try {
24
+ yield (0, hardhat_1.run)("verify:verify", {
25
+ address: contractAddress,
26
+ constructorArguments
27
+ });
28
+ break;
29
+ }
30
+ catch (e) {
31
+ if (e instanceof Error) {
32
+ if (e.toString().includes("Contract source code already verified")) {
33
+ console.log(chalk_1.default.grey(`${contractName} is already verified`));
34
+ return;
35
+ }
36
+ console.log(chalk_1.default.red(`Contract ${contractName} was not verified on etherscan`));
37
+ console.log(e.toString());
38
+ }
39
+ else {
40
+ console.log("Unknown exception type:", e);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ });
46
+ }
47
+ exports.verify = verify;
48
+ function verifyProxy(contractName, proxyAddress, constructorArguments) {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ yield verify(contractName, yield (0, upgrades_core_1.getImplementationAddress)(hardhat_1.network.provider, proxyAddress), constructorArguments);
51
+ });
52
+ }
53
+ exports.verifyProxy = verifyProxy;
@@ -0,0 +1 @@
1
+ export declare function getVersion(): Promise<string>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getVersion = void 0;
16
+ const fs_1 = require("fs");
17
+ const child_process_1 = require("child_process");
18
+ const util_1 = __importDefault(require("util"));
19
+ const exec = util_1.default.promisify(child_process_1.exec);
20
+ function getVersion() {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ if (process.env.VERSION) {
23
+ return process.env.VERSION;
24
+ }
25
+ try {
26
+ const tag = (yield exec("git describe --tags")).stdout.trim();
27
+ return tag;
28
+ }
29
+ catch (_a) {
30
+ return (yield fs_1.promises.readFile("VERSION", "utf-8")).trim();
31
+ }
32
+ });
33
+ }
34
+ exports.getVersion = getVersion;
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@skalenetwork/upgrade-tools",
3
- "version": "0.0.1-custom.1",
3
+ "version": "0.0.1-custom.2",
4
4
  "description": "Scripts to support upgrades of smart contracts",
5
+ "files": [
6
+ "dist/**/*"
7
+ ],
5
8
  "main": "dist/index.js",
6
9
  "types": "dist/index.d.ts",
7
10
  "repository": "git@github.com:skalenetwork/upgrade-tools.git",
package/src/abi.ts DELETED
@@ -1,16 +0,0 @@
1
- import { Interface } from "ethers/lib/utils";
2
-
3
- export function getAbi(contractInterface: Interface) {
4
- const abi = JSON.parse(contractInterface.format("json") as string) as [];
5
-
6
- abi.forEach((obj: {type: string}) => {
7
- if (obj.type === "function") {
8
- const func = obj as {name: string, type: string, inputs: object[], outputs: object[]};
9
- func.inputs.concat(func.outputs).forEach((output: object) => {
10
- Object.assign(output, Object.assign({name: ""}, output));
11
- })
12
- }
13
- });
14
-
15
- return abi;
16
- }
@@ -1,238 +0,0 @@
1
- import axios from "axios";
2
- import { TypedDataUtils } from "ethers-eip712";
3
- import * as ethUtil from 'ethereumjs-util';
4
- import chalk from "chalk";
5
- import type { ethers } from "ethers";
6
- import { HardhatEthersHelpers } from "@nomiclabs/hardhat-ethers/types";
7
-
8
- type Ethers = typeof ethers & HardhatEthersHelpers;
9
-
10
- enum Network {
11
- MAINNET = 1,
12
- RINKEBY = 4,
13
- GANACHE = 1337,
14
- HARDHAT = 31337,
15
- }
16
-
17
- const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
18
-
19
- const ADDRESSES = {
20
- multiSend: {
21
- [Network.MAINNET]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
22
- [Network.RINKEBY]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
23
- },
24
- }
25
-
26
- const URLS = {
27
- safe_transaction: {
28
- [Network.MAINNET]: "https://safe-transaction.mainnet.gnosis.io",
29
- [Network.RINKEBY]: "https://safe-transaction.rinkeby.gnosis.io",
30
- },
31
- safe_relay: {
32
- [Network.MAINNET]: "https://safe-relay.mainnet.gnosis.io",
33
- [Network.RINKEBY]: "https://safe-relay.rinkeby.gnosis.io",
34
- }
35
- }
36
-
37
- interface SafeInfoResponse{
38
- address: string,
39
- nonce: number,
40
- threshold: number,
41
- owners: string[],
42
- masterCopy: string,
43
- modules: string[],
44
- fallbackHandler: string,
45
- guard: string,
46
- version: string
47
- }
48
-
49
- interface SafeMultisigEstimateTx{
50
- safe: string,
51
- to: string,
52
- value: number,
53
- data?: string
54
- operation: number
55
- gasToken?: string
56
- }
57
-
58
- interface SafeMultisigEstimateTxResponseV2 {
59
- safeTxGas: string,
60
- baseGas: string,
61
- dataGas: string,
62
- operationalGas: string,
63
- gasPrice: string,
64
- lastUsedNonce: number,
65
- gasToken: string,
66
- refundReceiver: string
67
- }
68
-
69
- interface SafeMultisigTransaction {
70
- safe: string,
71
- to: string,
72
- value: number,
73
- data?: string,
74
- operation: number,
75
- gasToken?: string,
76
- safeTxGas: number,
77
- baseGas: number,
78
- gasPrice: number,
79
- refundReceiver?: string,
80
- nonce: number,
81
- contractTransactionHash: string,
82
- sender: string,
83
- signature?: string,
84
- origin?: string
85
- }
86
-
87
- function getMultiSendAddress(chainId: number) {
88
- if (chainId === Network.MAINNET) {
89
- return ADDRESSES.multiSend[chainId];
90
- } else if (chainId === Network.RINKEBY) {
91
- return ADDRESSES.multiSend[chainId];
92
- } else if ([Network.GANACHE, Network.HARDHAT].includes(chainId)) {
93
- return ZERO_ADDRESS;
94
- } else {
95
- throw Error(`Can't get multiSend contract at network with chainId = ${chainId}`);
96
- }
97
- }
98
-
99
- export function getSafeTransactionUrl(chainId: number) {
100
- if (chainId === Network.MAINNET) {
101
- return URLS.safe_transaction[chainId];
102
- } else if (chainId === 4) {
103
- return URLS.safe_transaction[chainId];
104
- } else {
105
- throw Error(`Can't get safe-transaction url at network with chainId = ${chainId}`);
106
- }
107
- }
108
-
109
- export function getSafeRelayUrl(chainId: number) {
110
- if (chainId === 1) {
111
- return URLS.safe_relay[chainId];
112
- } else if (chainId === 4) {
113
- return URLS.safe_relay[chainId];
114
- } else {
115
- throw Error(`Can't get safe-relay url at network with chainId = ${chainId}`);
116
- }
117
- }
118
-
119
- function concatTransactions(transactions: string[]) {
120
- return "0x" + transactions.map( (transaction) => {
121
- if (transaction.startsWith("0x")) {
122
- return transaction.slice(2);
123
- } else {
124
- return transaction;
125
- }
126
- }).join("");
127
- }
128
-
129
- export async function createMultiSendTransaction(ethers: Ethers, safeAddress: string, privateKey: string, transactions: string[], isSafeMock = false) {
130
- const chainId = (await ethers.provider.getNetwork()).chainId;
131
- const multiSendAddress = getMultiSendAddress(chainId);
132
- const multiSendAbi = [{"constant":false,"inputs":[{"internalType":"bytes","name":"transactions","type":"bytes"}],"name":"multiSend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}];
133
- const multiSend = new ethers.Contract(multiSendAddress, new ethers.utils.Interface(multiSendAbi), ethers.provider);
134
-
135
- let nonce = 0;
136
- if (!isSafeMock) {
137
- try {
138
- const nonceResponse = await axios.get<SafeInfoResponse>(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safeAddress}/`);
139
- nonce = nonceResponse.data.nonce;
140
- } catch (e) {
141
- if (!(e instanceof Error) || !e.toString().startsWith("Error: Can't get safe-transaction url")) {
142
- throw e;
143
- }
144
- }
145
- }
146
-
147
- const tx = {
148
- "safe": safeAddress,
149
- "to": multiSend.address,
150
- "value": 0, // Value in wei
151
- "data": multiSend.interface.encodeFunctionData("multiSend", [ concatTransactions(transactions) ]),
152
- "operation": 1, // 0 CALL, 1 DELEGATE_CALL
153
- "gasToken": ZERO_ADDRESS, // Token address (hold by the Safe) to be used as a refund to the sender, if `null` is Ether
154
- "safeTxGas": 0, // Max gas to use in the transaction
155
- "baseGas": 0, // Gas costs not related to the transaction execution (signature check, refund payment...)
156
- "gasPrice": 0, // Gas price used for the refund calculation
157
- "refundReceiver": ZERO_ADDRESS, // Address of receiver of gas payment (or `null` if tx.origin)
158
- "nonce": nonce, // Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
159
- }
160
-
161
- const typedData = {
162
- types: {
163
- EIP712Domain: [
164
- {name: "verifyingContract", type: "address"},
165
- ],
166
- SafeTx: [
167
- { type: "address", name: "to" },
168
- { type: "uint256", name: "value" },
169
- { type: "bytes", name: "data" },
170
- { type: "uint8", name: "operation" },
171
- { type: "uint256", name: "safeTxGas" },
172
- { type: "uint256", name: "baseGas" },
173
- { type: "uint256", name: "gasPrice" },
174
- { type: "address", name: "gasToken" },
175
- { type: "address", name: "refundReceiver" },
176
- { type: "uint256", name: "nonce" },
177
- ]
178
- },
179
- primaryType: 'SafeTx' as const,
180
- domain: {
181
- verifyingContract: safeAddress
182
- },
183
- message: {
184
- ...tx,
185
- data: ethers.utils.arrayify(tx.data)
186
- }
187
- }
188
-
189
- const digest = TypedDataUtils.encodeDigest(typedData)
190
- const digestHex = ethers.utils.hexlify(digest)
191
-
192
- const privateKeyBuffer = ethUtil.toBuffer(privateKey);
193
- const { r, s, v } = ethUtil.ecsign(ethUtil.toBuffer(digestHex), privateKeyBuffer);
194
- const signature = ethUtil.toRpcSig(v, r, s).toString();
195
-
196
- const txToSend: SafeMultisigTransaction = {
197
- ...tx,
198
- "contractTransactionHash": digestHex, // Contract transaction hash calculated from all the field
199
- // Owner of the Safe proposing the transaction. Must match one of the signatures
200
- "sender": ethers.utils.getAddress(ethUtil.bufferToHex(ethUtil.privateToAddress(privateKeyBuffer))),
201
- "signature": signature, // One or more ethereum ECDSA signatures of the `contractTransactionHash` as an hex string
202
- "origin": "Upgrade skale-manager" // Give more information about the transaction, e.g. "My Custom Safe app"
203
- }
204
-
205
- return txToSend;
206
- }
207
-
208
- export async function sendSafeTransaction(safe: string, chainId: number, safeTx: SafeMultisigTransaction) {
209
- try {
210
- console.log("Estimate gas");
211
- const estimateRequest: SafeMultisigEstimateTx = safeTx;
212
-
213
- try {
214
- const estimateResponse = await axios.post<SafeMultisigEstimateTxResponseV2>(
215
- `${getSafeRelayUrl(chainId)}/api/v2/safes/${safe}/transactions/estimate/`,
216
- estimateRequest
217
- );
218
- console.log(chalk.cyan(`Recommend to set gas limit to ${
219
- parseInt(estimateResponse.data.safeTxGas, 10) + parseInt(estimateResponse.data.baseGas, 10)}`));
220
- } catch (e) {
221
- console.log(chalk.red("Failed to estimate gas"));
222
- console.log(e);
223
- }
224
-
225
- console.log(chalk.green("Send transaction to gnosis safe"));
226
- await axios.post(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safe}/multisig-transactions/`, safeTx)
227
- } catch (e) {
228
- if (axios.isAxiosError(e)) {
229
- if (e.response) {
230
- console.log(JSON.stringify(e.response.data, null, 4))
231
- console.log(chalk.red(`Request failed with ${e.response.status} code`));
232
- } else {
233
- console.log(chalk.red("Request failed with unknown reason"));
234
- }
235
- }
236
- throw e;
237
- }
238
- }
@@ -1,32 +0,0 @@
1
- import { ethers, run, network } from "hardhat";
2
- import chalk from "chalk";
3
- import { getImplementationAddress } from "@openzeppelin/upgrades-core";
4
-
5
- export async function verify(contractName: string, contractAddress: string, constructorArguments: object) {
6
- if (![1337, 31337].includes((await ethers.provider.getNetwork()).chainId)) {
7
- for (let retry = 0; retry <= 5; ++retry) {
8
- try {
9
- await run("verify:verify", {
10
- address: contractAddress,
11
- constructorArguments
12
- });
13
- break;
14
- } catch (e) {
15
- if (e instanceof Error) {
16
- if (e.toString().includes("Contract source code already verified")) {
17
- console.log(chalk.grey(`${contractName} is already verified`));
18
- return;
19
- }
20
- console.log(chalk.red(`Contract ${contractName} was not verified on etherscan`));
21
- console.log(e.toString());
22
- } else {
23
- console.log("Unknown exception type:", e)
24
- }
25
- }
26
- }
27
- }
28
- }
29
-
30
- export async function verifyProxy(contractName: string, proxyAddress: string, constructorArguments: object) {
31
- await verify(contractName, await getImplementationAddress(network.provider, proxyAddress), constructorArguments);
32
- }
package/src/version.ts DELETED
@@ -1,16 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import { exec as asyncExec } from "child_process";
3
- import util from 'util';
4
- const exec = util.promisify(asyncExec);
5
-
6
- export async function getVersion() {
7
- if (process.env.VERSION) {
8
- return process.env.VERSION;
9
- }
10
- try {
11
- const tag = (await exec("git describe --tags")).stdout.trim();
12
- return tag;
13
- } catch {
14
- return (await fs.readFile("VERSION", "utf-8")).trim();
15
- }
16
- }
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "@tsconfig/recommended/tsconfig.json",
3
-
4
- "compilerOptions": {
5
- "outDir": "./dist",
6
- "declaration": true,
7
- },
8
- "include": ["src"],
9
- "exclude": ["node_modules", "dist"]
10
- }