@skalenetwork/upgrade-tools 1.0.0 → 2.0.0-develop.0
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/README.md +66 -11
- package/dist/hardhat.config.js +1 -1
- package/dist/src/deploy.d.ts +0 -1
- package/dist/src/deploy.js +4 -6
- package/dist/src/gnosis-safe.d.ts +1 -1
- package/dist/src/gnosis-safe.js +118 -42
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/submitters/auto-submitter.d.ts +11 -0
- package/dist/src/submitters/auto-submitter.js +176 -0
- package/dist/src/submitters/eoa-submitter.d.ts +5 -0
- package/dist/src/submitters/eoa-submitter.js +34 -0
- package/dist/src/submitters/index.d.ts +6 -0
- package/dist/src/submitters/index.js +22 -0
- package/dist/src/submitters/safe-ima-legacy-marionette-submitter.d.ts +6 -0
- package/dist/src/submitters/safe-ima-legacy-marionette-submitter.js +71 -0
- package/dist/src/submitters/safe-ima-marionette-submitter.d.ts +7 -0
- package/dist/src/submitters/safe-ima-marionette-submitter.js +80 -0
- package/dist/src/submitters/safe-submitter.d.ts +8 -0
- package/dist/src/submitters/safe-submitter.js +44 -0
- package/dist/src/submitters/safe-to-ima-submitter.d.ts +9 -0
- package/dist/src/submitters/safe-to-ima-submitter.js +39 -0
- package/dist/src/submitters/submitter.d.ts +5 -0
- package/dist/src/submitters/submitter.js +21 -0
- package/dist/src/submitters/types/marionette.d.ts +13 -0
- package/dist/src/submitters/types/marionette.js +4 -0
- package/dist/src/types/SkaleABIFile.d.ts +3 -0
- package/dist/src/{types.js → types/SkaleABIFile.js} +0 -0
- package/dist/src/{types.d.ts → types/SkaleManifestData.d.ts} +0 -3
- package/dist/src/types/SkaleManifestData.js +2 -0
- package/dist/src/upgrader.d.ts +19 -0
- package/dist/src/upgrader.js +140 -0
- package/dist/typechain-types/factories/AdminUpgradeabilityProxy__factory.d.ts +1 -1
- package/dist/typechain-types/factories/AdminUpgradeabilityProxy__factory.js +1 -1
- package/dist/typechain-types/factories/ProxyAdmin__factory.d.ts +1 -1
- package/dist/typechain-types/factories/ProxyAdmin__factory.js +1 -1
- package/dist/typechain-types/factories/SafeMock__factory.d.ts +1 -1
- package/dist/typechain-types/factories/SafeMock__factory.js +1 -1
- package/package.json +1 -1
- package/dist/src/upgrade.d.ts +0 -7
- package/dist/src/upgrade.js +0 -203
package/README.md
CHANGED
|
@@ -4,19 +4,74 @@ Scripts to support upgrades of smart contracts. The package contains common used
|
|
|
4
4
|
|
|
5
5
|
## Upgrade scripts
|
|
6
6
|
|
|
7
|
-
To write upgrade script
|
|
7
|
+
To write an upgrade script extend `Upgrader` class.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import {
|
|
10
|
+
import { Upgrader } from "@skalenetwork/upgrade-tools";
|
|
11
|
+
|
|
12
|
+
class ExampleContractUpgrader extends Upgrader {
|
|
13
|
+
|
|
14
|
+
getDeployedVersion = async () => {
|
|
15
|
+
return await (await this.getExampleContract()).version();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
setVersion = async (newVersion: string) => {
|
|
19
|
+
const exampleContract = await this.getExampleContract();
|
|
20
|
+
const setVersionTransaction = {
|
|
21
|
+
to: exampleContract.address,
|
|
22
|
+
data: exampleContract.interface.encodeFunctionData("setVersion", [newVersion])
|
|
23
|
+
};
|
|
24
|
+
this.transactions.push(setVersionTransaction);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getExampleContract() {
|
|
28
|
+
return await ethers.getContractAt("ExampleContract", this.abi["example_contract_address"] as string);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
const abi = JSON.parse(await fs.readFile(process.env.ABI, "utf-8")) as SkaleABIFile;
|
|
34
|
+
|
|
35
|
+
const upgrader = new ExampleContractUpgrader(
|
|
36
|
+
"ExampleContract",
|
|
37
|
+
"1.0.0",
|
|
38
|
+
abi,
|
|
39
|
+
["ExampleContract"]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await upgrader.upgrade();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (require.main === module) {
|
|
46
|
+
main()
|
|
47
|
+
.then(() => process.exit(0))
|
|
48
|
+
.catch(error => {
|
|
49
|
+
console.error(error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Abstract functions
|
|
56
|
+
|
|
57
|
+
The `Upgrader` has 2 abstract functions. It's mandatory to override them:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
abstract getDeployedVersion: () => Promise<string | undefined>
|
|
61
|
+
abstract setVersion: (newVersion: string) => Promise<void>
|
|
11
62
|
```
|
|
12
63
|
|
|
13
|
-
|
|
64
|
+
- `getDeployedVersion` returns string representing a deployed version of the contract
|
|
65
|
+
- `setVersion` creates a transaction to set a new version of the contract
|
|
66
|
+
|
|
67
|
+
### Protected functions
|
|
68
|
+
|
|
69
|
+
There are functions that may be overridden to customize an upgrade process
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
deployNewContracts = () => { return Promise.resolve() };
|
|
73
|
+
initialize = () => { return Promise.resolve() };
|
|
74
|
+
```
|
|
14
75
|
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
17
|
-
- `getDeployedVersion` - a function to request current version from smart contracts
|
|
18
|
-
- `setVersion` - function that sets version to smart contracts
|
|
19
|
-
- `safeMockAccessRequirements` - list of smart contracts that requires ownership changing for successful upgrade (when EOA + SafeMock are used during upgrade)
|
|
20
|
-
- `contractNamesToUpgrade` - list of smart contracts to upgrade
|
|
21
|
-
- `deployNewContracts` - optional - a function that deploys new smart contracts
|
|
22
|
-
- `initialize` - optional - a function that setup smart contracts after upgrade
|
|
76
|
+
- `deployNewContracts` is called before proxies upgrade and is used to deploy new instances
|
|
77
|
+
- `initialize` is called after proxies upgrade and is used to send initializing transactions
|
package/dist/hardhat.config.js
CHANGED
package/dist/src/deploy.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export declare function deployLibraries(libraryNames: string[]): Promise<Map<string, string>>;
|
|
2
2
|
export declare function getLinkedContractFactory(contractName: string, libraries: Map<string, string>): Promise<import("ethers").ContractFactory>;
|
|
3
|
-
export declare function getContractKeyInAbiFile(contract: string): string;
|
|
4
3
|
export declare function getManifestFile(): Promise<string>;
|
|
5
4
|
export declare function getContractFactory(contract: string): Promise<import("ethers").ContractFactory>;
|
package/dist/src/deploy.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.getContractFactory = exports.getManifestFile = exports.
|
|
12
|
+
exports.getContractFactory = exports.getManifestFile = exports.getLinkedContractFactory = exports.deployLibraries = void 0;
|
|
13
13
|
const upgrades_core_1 = require("@openzeppelin/upgrades-core");
|
|
14
14
|
const hardhat_1 = require("hardhat");
|
|
15
15
|
const fs_1 = require("fs");
|
|
@@ -58,10 +58,6 @@ function getLinkedContractFactory(contractName, libraries) {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
exports.getLinkedContractFactory = getLinkedContractFactory;
|
|
61
|
-
function getContractKeyInAbiFile(contract) {
|
|
62
|
-
return contract.replace(/([a-zA-Z])(?=[A-Z])/g, '$1_').toLowerCase();
|
|
63
|
-
}
|
|
64
|
-
exports.getContractKeyInAbiFile = getContractKeyInAbiFile;
|
|
65
61
|
function getManifestFile() {
|
|
66
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
67
63
|
return (yield upgrades_core_1.Manifest.forNetwork(hardhat_1.ethers.provider)).file;
|
|
@@ -90,7 +86,9 @@ function getContractFactory(contract) {
|
|
|
90
86
|
Object.assign(libraryArtifacts, manifest.libraries);
|
|
91
87
|
}
|
|
92
88
|
finally {
|
|
93
|
-
|
|
89
|
+
if (manifest !== undefined) {
|
|
90
|
+
Object.assign(manifest, { libraries: libraryArtifacts });
|
|
91
|
+
}
|
|
94
92
|
yield fs_1.promises.writeFile(yield getManifestFile(), JSON.stringify(manifest, null, 4));
|
|
95
93
|
}
|
|
96
94
|
return yield getLinkedContractFactory(contract, libraries);
|
|
@@ -20,6 +20,6 @@ interface SafeMultisigTransaction {
|
|
|
20
20
|
}
|
|
21
21
|
export declare function getSafeTransactionUrl(chainId: number): string;
|
|
22
22
|
export declare function getSafeRelayUrl(chainId: number): string;
|
|
23
|
-
export declare function createMultiSendTransaction(ethers: Ethers, safeAddress: string, privateKey: string, transactions: string[],
|
|
23
|
+
export declare function createMultiSendTransaction(ethers: Ethers, safeAddress: string, privateKey: string, transactions: string[], chainId: number, nonce?: number): Promise<SafeMultisigTransaction>;
|
|
24
24
|
export declare function sendSafeTransaction(safe: string, chainId: number, safeTx: SafeMultisigTransaction): Promise<void>;
|
|
25
25
|
export {};
|
package/dist/src/gnosis-safe.js
CHANGED
|
@@ -44,44 +44,33 @@ var Network;
|
|
|
44
44
|
(function (Network) {
|
|
45
45
|
Network[Network["MAINNET"] = 1] = "MAINNET";
|
|
46
46
|
Network[Network["RINKEBY"] = 4] = "RINKEBY";
|
|
47
|
+
Network[Network["GOERLI"] = 5] = "GOERLI";
|
|
47
48
|
Network[Network["GANACHE"] = 1337] = "GANACHE";
|
|
48
49
|
Network[Network["HARDHAT"] = 31337] = "HARDHAT";
|
|
49
50
|
})(Network || (Network = {}));
|
|
51
|
+
// constants
|
|
50
52
|
const ADDRESSES = {
|
|
51
53
|
multiSend: {
|
|
52
54
|
[Network.MAINNET]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
|
|
53
55
|
[Network.RINKEBY]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
|
|
56
|
+
[Network.GOERLI]: "0x8D29bE29923b68abfDD21e541b9374737B49cdAD",
|
|
54
57
|
},
|
|
55
58
|
};
|
|
56
59
|
const URLS = {
|
|
57
60
|
safe_transaction: {
|
|
58
61
|
[Network.MAINNET]: "https://safe-transaction.mainnet.gnosis.io",
|
|
59
62
|
[Network.RINKEBY]: "https://safe-transaction.rinkeby.gnosis.io",
|
|
63
|
+
[Network.GOERLI]: "https://safe-transaction.goerli.gnosis.io",
|
|
60
64
|
},
|
|
61
65
|
safe_relay: {
|
|
62
66
|
[Network.MAINNET]: "https://safe-relay.mainnet.gnosis.io",
|
|
63
67
|
[Network.RINKEBY]: "https://safe-relay.rinkeby.gnosis.io",
|
|
68
|
+
[Network.GOERLI]: "https://safe-relay.goerli.gnosis.io",
|
|
64
69
|
}
|
|
65
70
|
};
|
|
66
|
-
|
|
67
|
-
if (chainId === Network.MAINNET) {
|
|
68
|
-
return ADDRESSES.multiSend[chainId];
|
|
69
|
-
}
|
|
70
|
-
else if (chainId === Network.RINKEBY) {
|
|
71
|
-
return ADDRESSES.multiSend[chainId];
|
|
72
|
-
}
|
|
73
|
-
else if ([Network.GANACHE, Network.HARDHAT].includes(chainId)) {
|
|
74
|
-
return ethers_1.ethers.constants.AddressZero;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
throw Error(`Can't get multiSend contract at network with chainId = ${chainId}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
71
|
+
// public functions
|
|
80
72
|
function getSafeTransactionUrl(chainId) {
|
|
81
|
-
if (chainId
|
|
82
|
-
return URLS.safe_transaction[chainId];
|
|
83
|
-
}
|
|
84
|
-
else if (chainId === 4) {
|
|
73
|
+
if (Object.keys(URLS.safe_transaction).includes(chainId.toString())) {
|
|
85
74
|
return URLS.safe_transaction[chainId];
|
|
86
75
|
}
|
|
87
76
|
else {
|
|
@@ -90,10 +79,7 @@ function getSafeTransactionUrl(chainId) {
|
|
|
90
79
|
}
|
|
91
80
|
exports.getSafeTransactionUrl = getSafeTransactionUrl;
|
|
92
81
|
function getSafeRelayUrl(chainId) {
|
|
93
|
-
if (chainId
|
|
94
|
-
return URLS.safe_relay[chainId];
|
|
95
|
-
}
|
|
96
|
-
else if (chainId === 4) {
|
|
82
|
+
if (Object.keys(URLS.safe_relay).includes(chainId.toString())) {
|
|
97
83
|
return URLS.safe_relay[chainId];
|
|
98
84
|
}
|
|
99
85
|
else {
|
|
@@ -101,29 +87,34 @@ function getSafeRelayUrl(chainId) {
|
|
|
101
87
|
}
|
|
102
88
|
}
|
|
103
89
|
exports.getSafeRelayUrl = getSafeRelayUrl;
|
|
104
|
-
function
|
|
105
|
-
return "0x" + transactions.map((transaction) => {
|
|
106
|
-
if (transaction.startsWith("0x")) {
|
|
107
|
-
return transaction.slice(2);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
return transaction;
|
|
111
|
-
}
|
|
112
|
-
}).join("");
|
|
113
|
-
}
|
|
114
|
-
function createMultiSendTransaction(ethers, safeAddress, privateKey, transactions, isSafeMock = false) {
|
|
90
|
+
function createMultiSendTransaction(ethers, safeAddress, privateKey, transactions, chainId, nonce) {
|
|
115
91
|
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
-
const chainId = (yield ethers.provider.getNetwork()).chainId;
|
|
117
92
|
const multiSendAddress = getMultiSendAddress(chainId);
|
|
118
93
|
const multiSendAbi = [{ "constant": false, "inputs": [{ "internalType": "bytes", "name": "transactions", "type": "bytes" }], "name": "multiSend", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }];
|
|
119
94
|
const multiSend = new ethers.Contract(multiSendAddress, new ethers.utils.Interface(multiSendAbi), ethers.provider);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let nonce = 0;
|
|
123
|
-
if (!isSafeMock) {
|
|
95
|
+
let nonceValue = 0;
|
|
96
|
+
if (nonce === undefined) {
|
|
124
97
|
try {
|
|
125
|
-
|
|
126
|
-
|
|
98
|
+
if (process.env.NONCE) {
|
|
99
|
+
// NONCE variable is set
|
|
100
|
+
if (isNaN(Number.parseInt(process.env.NONCE))) {
|
|
101
|
+
// NONCE variable is not a number
|
|
102
|
+
if (process.env.NONCE.toLowerCase() === "pending") {
|
|
103
|
+
nonceValue = yield getSafeNonceWithPending(chainId, safeAddress);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
nonceValue = yield getSafeNonce(chainId, safeAddress);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// NONCE variable is a number
|
|
111
|
+
nonceValue = Number.parseInt(process.env.NONCE);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// NONCE variable is not set
|
|
116
|
+
nonceValue = yield getSafeNonce(chainId, safeAddress);
|
|
117
|
+
}
|
|
127
118
|
}
|
|
128
119
|
catch (e) {
|
|
129
120
|
if (!(e instanceof Error) || !e.toString().startsWith("Error: Can't get safe-transaction url")) {
|
|
@@ -131,6 +122,10 @@ function createMultiSendTransaction(ethers, safeAddress, privateKey, transaction
|
|
|
131
122
|
}
|
|
132
123
|
}
|
|
133
124
|
}
|
|
125
|
+
else {
|
|
126
|
+
nonceValue = nonce;
|
|
127
|
+
}
|
|
128
|
+
console.log("Will send tx to Gnosis with nonce", nonceValue);
|
|
134
129
|
const tx = {
|
|
135
130
|
"safe": safeAddress,
|
|
136
131
|
"to": multiSend.address,
|
|
@@ -142,9 +137,9 @@ function createMultiSendTransaction(ethers, safeAddress, privateKey, transaction
|
|
|
142
137
|
"baseGas": 0,
|
|
143
138
|
"gasPrice": 0,
|
|
144
139
|
"refundReceiver": ethers.constants.AddressZero,
|
|
145
|
-
"nonce":
|
|
140
|
+
"nonce": nonceValue, // Nonce of the Safe, transaction cannot be executed until Safe's nonce is not equal to this nonce
|
|
146
141
|
};
|
|
147
|
-
const digestHex =
|
|
142
|
+
const digestHex = getTransactionHash(tx.to, tx.value, tx.data, tx.operation, tx.safeTxGas, tx.baseGas, tx.gasPrice, tx.gasToken, tx.refundReceiver, tx.nonce, safeAddress, chainId);
|
|
148
143
|
const privateKeyBuffer = ethUtil.toBuffer(privateKey);
|
|
149
144
|
const { r, s, v } = ethUtil.ecsign(ethUtil.toBuffer(digestHex), privateKeyBuffer);
|
|
150
145
|
const signature = ethUtil.toRpcSig(v, r, s).toString();
|
|
@@ -187,3 +182,84 @@ function sendSafeTransaction(safe, chainId, safeTx) {
|
|
|
187
182
|
});
|
|
188
183
|
}
|
|
189
184
|
exports.sendSafeTransaction = sendSafeTransaction;
|
|
185
|
+
// private functions
|
|
186
|
+
function getMultiSendAddress(chainId) {
|
|
187
|
+
if ([Network.GANACHE, Network.HARDHAT].includes(chainId)) {
|
|
188
|
+
return ethers_1.ethers.constants.AddressZero;
|
|
189
|
+
}
|
|
190
|
+
else if (Object.keys(ADDRESSES.multiSend).includes(chainId.toString())) {
|
|
191
|
+
return ADDRESSES.multiSend[chainId];
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
throw Error(`Can't get multiSend contract at network with chainId = ${chainId}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function concatTransactions(transactions) {
|
|
198
|
+
return "0x" + transactions.map((transaction) => {
|
|
199
|
+
if (transaction.startsWith("0x")) {
|
|
200
|
+
return transaction.slice(2);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
return transaction;
|
|
204
|
+
}
|
|
205
|
+
}).join("");
|
|
206
|
+
}
|
|
207
|
+
function getSafeNonce(chainId, safeAddress) {
|
|
208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
+
const safeInfo = yield axios_1.default.get(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safeAddress}/`);
|
|
210
|
+
return safeInfo.data.nonce;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function getSafeNonceWithPending(chainId, safeAddress) {
|
|
214
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
215
|
+
const allTransactions = yield axios_1.default.get(`${getSafeTransactionUrl(chainId)}/api/v1/safes/${safeAddress}/all-transactions/?executed=false&queued=true&trusted=true`);
|
|
216
|
+
if (allTransactions.data.results.length > 0) {
|
|
217
|
+
return allTransactions.data.results[0].nonce + 1;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function getDomainSeparator(safeAddress, chainId) {
|
|
225
|
+
const DOMAIN_SEPARATOR_TYPEHASH = "0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218";
|
|
226
|
+
return ethers_1.ethers.utils.solidityKeccak256(["bytes"], [
|
|
227
|
+
ethers_1.ethers.utils.defaultAbiCoder.encode(["bytes32", "uint256", "address"], [DOMAIN_SEPARATOR_TYPEHASH, chainId, safeAddress])
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
function encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce, safeAddress, chainId) {
|
|
231
|
+
const dataHash = ethers_1.ethers.utils.solidityKeccak256(["bytes"], [data]);
|
|
232
|
+
const SAFE_TX_TYPEHASH = "0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8";
|
|
233
|
+
const encoded = ethers_1.ethers.utils.defaultAbiCoder.encode([
|
|
234
|
+
"bytes32",
|
|
235
|
+
"address",
|
|
236
|
+
"uint256",
|
|
237
|
+
"bytes32",
|
|
238
|
+
"uint256",
|
|
239
|
+
"uint256",
|
|
240
|
+
"uint256",
|
|
241
|
+
"uint256",
|
|
242
|
+
"address",
|
|
243
|
+
"address",
|
|
244
|
+
"uint256"
|
|
245
|
+
], [
|
|
246
|
+
SAFE_TX_TYPEHASH,
|
|
247
|
+
to,
|
|
248
|
+
value,
|
|
249
|
+
dataHash,
|
|
250
|
+
operation,
|
|
251
|
+
safeTxGas,
|
|
252
|
+
baseGas,
|
|
253
|
+
gasPrice,
|
|
254
|
+
gasToken,
|
|
255
|
+
refundReceiver,
|
|
256
|
+
_nonce
|
|
257
|
+
]);
|
|
258
|
+
const encodedHash = ethers_1.ethers.utils.solidityKeccak256(["bytes"], [encoded]);
|
|
259
|
+
return ethers_1.ethers.utils.solidityPack(["bytes1", "bytes1", "bytes32", "bytes32"], ["0x19", "0x01", getDomainSeparator(safeAddress, chainId), encodedHash]);
|
|
260
|
+
}
|
|
261
|
+
function getTransactionHash(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce, safeAddress, chainId) {
|
|
262
|
+
return ethers_1.ethers.utils.solidityKeccak256(["bytes"], [
|
|
263
|
+
encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce, safeAddress, chainId)
|
|
264
|
+
]);
|
|
265
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./abi";
|
|
|
2
2
|
export * from "./deploy";
|
|
3
3
|
export * from "./gnosis-safe";
|
|
4
4
|
export * from "./multiSend";
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./types";
|
|
5
|
+
export * from "./submitters";
|
|
7
6
|
export * from "./verification";
|
|
8
7
|
export * from "./version";
|
|
8
|
+
export * from "./upgrader";
|
package/dist/src/index.js
CHANGED
|
@@ -18,7 +18,7 @@ __exportStar(require("./abi"), exports);
|
|
|
18
18
|
__exportStar(require("./deploy"), exports);
|
|
19
19
|
__exportStar(require("./gnosis-safe"), exports);
|
|
20
20
|
__exportStar(require("./multiSend"), exports);
|
|
21
|
-
__exportStar(require("./
|
|
22
|
-
__exportStar(require("./types"), exports);
|
|
21
|
+
__exportStar(require("./submitters"), exports);
|
|
23
22
|
__exportStar(require("./verification"), exports);
|
|
24
23
|
__exportStar(require("./version"), exports);
|
|
24
|
+
__exportStar(require("./upgrader"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { UnsignedTransaction } from "ethers";
|
|
2
|
+
import { Submitter } from "./submitter";
|
|
3
|
+
import { SkaleABIFile } from "../types/SkaleABIFile";
|
|
4
|
+
export declare class AutoSubmitter extends Submitter {
|
|
5
|
+
submit(transactions: UnsignedTransaction[]): Promise<void>;
|
|
6
|
+
_getImaAbi(): Promise<SkaleABIFile>;
|
|
7
|
+
_getSafeAddress(): string;
|
|
8
|
+
_getSchainHash(): string;
|
|
9
|
+
_getMainnetChainId(): number;
|
|
10
|
+
_versionFunctionExists(): Promise<boolean>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.AutoSubmitter = void 0;
|
|
39
|
+
const admin_1 = require("@openzeppelin/hardhat-upgrades/dist/admin");
|
|
40
|
+
const submitter_1 = require("./submitter");
|
|
41
|
+
const hardhat_1 = __importStar(require("hardhat"));
|
|
42
|
+
const eoa_submitter_1 = require("./eoa-submitter");
|
|
43
|
+
const safe_submitter_1 = require("./safe-submitter");
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const safe_ima_legacy_marionette_submitter_1 = require("./safe-ima-legacy-marionette-submitter");
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const marionette_1 = require("./types/marionette");
|
|
48
|
+
class AutoSubmitter extends submitter_1.Submitter {
|
|
49
|
+
submit(transactions) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
let submitter;
|
|
52
|
+
const proxyAdmin = yield (0, admin_1.getManifestAdmin)(hardhat_1.default);
|
|
53
|
+
const owner = yield proxyAdmin.owner();
|
|
54
|
+
if ((yield hardhat_1.default.ethers.provider.getCode(owner)) === "0x") {
|
|
55
|
+
console.log("Owner is not a contract");
|
|
56
|
+
submitter = new eoa_submitter_1.EoaSubmitter();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.log("Owner is a contract");
|
|
60
|
+
if (hardhat_1.ethers.utils.getAddress(owner) == hardhat_1.ethers.utils.getAddress(marionette_1.MARIONETTE_ADDRESS)) {
|
|
61
|
+
console.log("Marionette owner is detected");
|
|
62
|
+
const imaAbi = yield this._getImaAbi();
|
|
63
|
+
const safeAddress = this._getSafeAddress();
|
|
64
|
+
const schainHash = this._getSchainHash();
|
|
65
|
+
const mainnetChainId = this._getMainnetChainId();
|
|
66
|
+
// TODO: after marionette has multiSend functionality
|
|
67
|
+
// query version and properly select a submitter
|
|
68
|
+
// based on it
|
|
69
|
+
//
|
|
70
|
+
// if (await this._versionFunctionExists()) {
|
|
71
|
+
// console.log("version() function was found. Use normal Marionette")
|
|
72
|
+
// submitter = new SafeImaMarionetteSubmitter(
|
|
73
|
+
// safeAddress,
|
|
74
|
+
// imaAbi,
|
|
75
|
+
// schainHash,
|
|
76
|
+
// mainnetChainId
|
|
77
|
+
// )
|
|
78
|
+
// } else {
|
|
79
|
+
// console.log("No version() function was found. Use legacy Marionette")
|
|
80
|
+
// submitter = new SafeImaLegacyMarionetteSubmitter(
|
|
81
|
+
// safeAddress,
|
|
82
|
+
// imaAbi,
|
|
83
|
+
// schainHash,
|
|
84
|
+
// mainnetChainId
|
|
85
|
+
// )
|
|
86
|
+
// }
|
|
87
|
+
submitter = new safe_ima_legacy_marionette_submitter_1.SafeImaLegacyMarionetteSubmitter(safeAddress, imaAbi, schainHash, mainnetChainId);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// assuming owner is a Gnosis Safe
|
|
91
|
+
console.log("Using Gnosis Safe");
|
|
92
|
+
submitter = new safe_submitter_1.SafeSubmitter(owner);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
yield submitter.submit(transactions);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// private
|
|
99
|
+
_getImaAbi() {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
if (!process.env.IMA_ABI) {
|
|
102
|
+
console.log(chalk_1.default.red("Set path to ima abi to IMA_ABI environment variable"));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
return JSON.parse(yield fs_1.promises.readFile(process.env.IMA_ABI, "utf-8"));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
_getSafeAddress() {
|
|
109
|
+
if (!process.env.SAFE_ADDRESS) {
|
|
110
|
+
console.log(chalk_1.default.red("Set Gnosis Safe owner address to SAFE_ADDRESS environment variable"));
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
return process.env.SAFE_ADDRESS;
|
|
114
|
+
}
|
|
115
|
+
_getSchainHash() {
|
|
116
|
+
// query Context to get schain hash
|
|
117
|
+
if (!process.env.SCHAIN_HASH) {
|
|
118
|
+
if (!process.env.SCHAIN_NAME) {
|
|
119
|
+
console.log(chalk_1.default.red("Set schain name to SCHAIN_NAME environment variable"));
|
|
120
|
+
console.log(chalk_1.default.red("or schain hash to SCHAIN_HASH environment variable"));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return hardhat_1.ethers.utils.solidityKeccak256(["string"], [process.env.SCHAIN_NAME]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
return process.env.SCHAIN_HASH;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
_getMainnetChainId() {
|
|
132
|
+
if (!process.env.MAINNET_CHAIN_ID) {
|
|
133
|
+
console.log(chalk_1.default.red("Set chainId of mainnet to MAINNET_CHAIN_ID environment variable"));
|
|
134
|
+
console.log(chalk_1.default.red("Use 1 for Ethereum mainnet or 5 for Goerli"));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return Number.parseInt(process.env.MAINNET_CHAIN_ID);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
_versionFunctionExists() {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
const bytecode = yield hardhat_1.default.ethers.provider.getCode(marionette_1.MARIONETTE_ADDRESS);
|
|
144
|
+
// If the bytecode doesn't include the function selector version()
|
|
145
|
+
// is definitely not present
|
|
146
|
+
if (!bytecode.includes(hardhat_1.ethers.utils.id("version()").slice(2, 10))) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const marionette = new hardhat_1.ethers.Contract(marionette_1.MARIONETTE_ADDRESS, [{
|
|
150
|
+
"inputs": [],
|
|
151
|
+
"name": "version",
|
|
152
|
+
"outputs": [
|
|
153
|
+
{
|
|
154
|
+
"internalType": "string",
|
|
155
|
+
"name": "",
|
|
156
|
+
"type": "string"
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"stateMutability": "view",
|
|
160
|
+
"type": "function"
|
|
161
|
+
}], hardhat_1.default.ethers.provider);
|
|
162
|
+
// If gas estimation doesn't revert then an execution is possible
|
|
163
|
+
// given the provided function selector
|
|
164
|
+
try {
|
|
165
|
+
yield marionette.estimateGas.version();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch (_a) {
|
|
169
|
+
// Otherwise (revert) we assume that there is no entry in the jump table
|
|
170
|
+
// meaning that the contract doesn't include version()
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.AutoSubmitter = AutoSubmitter;
|
|
@@ -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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EoaSubmitter = void 0;
|
|
13
|
+
const hardhat_1 = require("hardhat");
|
|
14
|
+
const submitter_1 = require("./submitter");
|
|
15
|
+
class EoaSubmitter extends submitter_1.Submitter {
|
|
16
|
+
submit(transactions) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
this._atomicityWarning();
|
|
19
|
+
const [deployer] = yield hardhat_1.ethers.getSigners();
|
|
20
|
+
for (const transaction of transactions) {
|
|
21
|
+
console.log("Send transaction");
|
|
22
|
+
const response = yield deployer.sendTransaction({
|
|
23
|
+
to: transaction.to,
|
|
24
|
+
value: transaction.value,
|
|
25
|
+
data: transaction.data
|
|
26
|
+
});
|
|
27
|
+
console.log(`Waiting for a transaction with nonce ${response.nonce}`);
|
|
28
|
+
yield response.wait();
|
|
29
|
+
console.log("The transaction was sent");
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.EoaSubmitter = EoaSubmitter;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./auto-submitter"), exports);
|
|
18
|
+
__exportStar(require("./eoa-submitter"), exports);
|
|
19
|
+
__exportStar(require("./safe-ima-legacy-marionette-submitter"), exports);
|
|
20
|
+
__exportStar(require("./safe-submitter"), exports);
|
|
21
|
+
__exportStar(require("./safe-to-ima-submitter"), exports);
|
|
22
|
+
__exportStar(require("./submitter"), exports);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { UnsignedTransaction } from "ethers";
|
|
2
|
+
import { SafeToImaSubmitter } from "./safe-to-ima-submitter";
|
|
3
|
+
export declare class SafeImaLegacyMarionetteSubmitter extends SafeToImaSubmitter {
|
|
4
|
+
marionette: import("ethers").Contract;
|
|
5
|
+
submit(transactions: UnsignedTransaction[]): Promise<void>;
|
|
6
|
+
}
|