rocketh 0.15.15 → 0.16.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/CHANGELOG.md +11 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/environment/deployment-store.d.ts +3 -0
- package/dist/environment/deployment-store.d.ts.map +1 -0
- package/dist/environment/deployment-store.js +49 -0
- package/dist/environment/deployment-store.js.map +1 -0
- package/dist/environment/utils/artifacts.d.ts +4 -4
- package/dist/executor/index.d.ts +15 -6
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +39 -316
- package/dist/executor/index.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/logging.d.ts +1 -1
- package/dist/internal/logging.d.ts.map +1 -1
- package/package.json +9 -12
- package/src/cli.ts +2 -1
- package/src/environment/deployment-store.ts +72 -0
- package/src/executor/index.ts +89 -398
- package/src/index.ts +10 -11
- package/src/environment/deployments.ts +0 -135
- package/src/environment/index.ts +0 -696
- package/src/environment/providers/BaseProvider.ts +0 -13
- package/src/environment/providers/TransactionHashTracker.ts +0 -22
- package/src/environment/utils/artifacts.ts +0 -176
- package/src/environment/utils/chains.ts +0 -192
- package/src/executor/setup.test.ts +0 -151
- package/src/internal/logging.ts +0 -80
- package/src/internal/types.ts +0 -5
- package/src/types.ts +0 -601
- package/src/utils/eth.ts +0 -96
- package/src/utils/extensions.test.ts +0 -53
- package/src/utils/extensions.ts +0 -72
- package/src/utils/json.ts +0 -33
package/src/environment/index.ts
DELETED
|
@@ -1,696 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
AccountType,
|
|
5
|
-
Artifact,
|
|
6
|
-
Deployment,
|
|
7
|
-
Environment,
|
|
8
|
-
Signer,
|
|
9
|
-
PendingDeployment,
|
|
10
|
-
PendingTransaction,
|
|
11
|
-
ResolvedAccount,
|
|
12
|
-
ResolvedNamedAccounts,
|
|
13
|
-
ResolvedNamedSigners,
|
|
14
|
-
UnknownDeployments,
|
|
15
|
-
UnresolvedUnknownNamedAccounts,
|
|
16
|
-
UnresolvedNetworkSpecificData,
|
|
17
|
-
ResolvedNetworkSpecificData,
|
|
18
|
-
DataType,
|
|
19
|
-
ResolvedExecutionParams,
|
|
20
|
-
ResolvedUserConfig,
|
|
21
|
-
PendingExecution,
|
|
22
|
-
} from '../types.js';
|
|
23
|
-
import {Abi, Address} from 'abitype';
|
|
24
|
-
import {InternalEnvironment} from '../internal/types.js';
|
|
25
|
-
import path from 'node:path';
|
|
26
|
-
import {JSONToString, stringToJSON} from '../utils/json.js';
|
|
27
|
-
import {loadDeployments} from './deployments.js';
|
|
28
|
-
import {
|
|
29
|
-
EIP1193Account,
|
|
30
|
-
EIP1193Block,
|
|
31
|
-
EIP1193BlockWithTransactions,
|
|
32
|
-
EIP1193DATA,
|
|
33
|
-
EIP1193Transaction,
|
|
34
|
-
EIP1193TransactionReceipt,
|
|
35
|
-
} from 'eip-1193';
|
|
36
|
-
import {ProgressIndicator, log, spin} from '../internal/logging.js';
|
|
37
|
-
import {mergeArtifacts} from './utils/artifacts.js';
|
|
38
|
-
import {TransactionHashTracker, TransactionHashTrackerProvider} from './providers/TransactionHashTracker.js';
|
|
39
|
-
|
|
40
|
-
function wait(numSeconds: number): Promise<void> {
|
|
41
|
-
return new Promise((resolve) => {
|
|
42
|
-
setTimeout(resolve, numSeconds * 1000);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function displayTransaction(transaction: EIP1193Transaction) {
|
|
47
|
-
if (transaction.type === '0x2') {
|
|
48
|
-
return `(maxFeePerGas: ${BigInt(transaction.maxFeePerGas).toString()}, maxPriorityFeePerGas: ${BigInt(
|
|
49
|
-
transaction.maxPriorityFeePerGas
|
|
50
|
-
).toString()})`;
|
|
51
|
-
} else {
|
|
52
|
-
return `(gasPrice: ${BigInt(transaction.gasPrice).toString()})`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export async function createEnvironment<
|
|
57
|
-
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
|
|
58
|
-
Data extends UnresolvedNetworkSpecificData = UnresolvedNetworkSpecificData,
|
|
59
|
-
Deployments extends UnknownDeployments = UnknownDeployments
|
|
60
|
-
>(
|
|
61
|
-
userConfig: ResolvedUserConfig<NamedAccounts, Data>,
|
|
62
|
-
resolvedExecutionParams: ResolvedExecutionParams
|
|
63
|
-
): Promise<{internal: InternalEnvironment; external: Environment<NamedAccounts, Data, Deployments>}> {
|
|
64
|
-
const rawProvider = resolvedExecutionParams.provider;
|
|
65
|
-
|
|
66
|
-
const provider: TransactionHashTracker = new TransactionHashTrackerProvider(rawProvider);
|
|
67
|
-
|
|
68
|
-
const chainIdHex = await provider.request({method: 'eth_chainId'});
|
|
69
|
-
const chainId = '' + Number(chainIdHex);
|
|
70
|
-
let genesisHash: `0x${string}` | undefined;
|
|
71
|
-
try {
|
|
72
|
-
let genesisBlock: EIP1193Block | EIP1193BlockWithTransactions | null;
|
|
73
|
-
try {
|
|
74
|
-
genesisBlock = await provider.request({method: 'eth_getBlockByNumber', params: ['earliest', false]});
|
|
75
|
-
} catch {
|
|
76
|
-
genesisBlock = await provider.request({method: 'eth_getBlockByNumber', params: ['0x0', false]});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!genesisBlock) {
|
|
80
|
-
console.error(`failed to get genesis block, returned null`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
genesisHash = genesisBlock?.hash;
|
|
84
|
-
} catch (err) {
|
|
85
|
-
console.error(`failed to get genesis block`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const deploymentsFolder = userConfig.deployments;
|
|
89
|
-
const environmentName = resolvedExecutionParams.environment.name;
|
|
90
|
-
const saveDeployments = resolvedExecutionParams.saveDeployments;
|
|
91
|
-
let networkTags: {[tag: string]: boolean} = {};
|
|
92
|
-
for (const networkTag of resolvedExecutionParams.environment.tags) {
|
|
93
|
-
networkTags[networkTag] = true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const resolvedAccounts: {[name: string]: ResolvedAccount} = {};
|
|
97
|
-
|
|
98
|
-
const allRemoteAccounts = await provider.request({method: 'eth_accounts'});
|
|
99
|
-
const accountCache: {[name: string]: ResolvedAccount} = {};
|
|
100
|
-
|
|
101
|
-
async function getAccount(
|
|
102
|
-
name: string,
|
|
103
|
-
accounts: UnresolvedUnknownNamedAccounts,
|
|
104
|
-
accountDef: AccountType
|
|
105
|
-
): Promise<ResolvedAccount | undefined> {
|
|
106
|
-
if (accountCache[name]) {
|
|
107
|
-
return accountCache[name];
|
|
108
|
-
}
|
|
109
|
-
let account: ResolvedAccount | undefined;
|
|
110
|
-
if (typeof accountDef === 'number') {
|
|
111
|
-
const accountPerIndex = allRemoteAccounts[accountDef];
|
|
112
|
-
if (accountPerIndex) {
|
|
113
|
-
accountCache[name] = account = {
|
|
114
|
-
type: 'remote',
|
|
115
|
-
address: accountPerIndex,
|
|
116
|
-
signer: provider,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
} else if (typeof accountDef === 'string') {
|
|
120
|
-
if (accountDef.startsWith('0x')) {
|
|
121
|
-
if (accountDef.length === 66) {
|
|
122
|
-
const privateKeyProtocol = userConfig.signerProtocols?.['privateKey'];
|
|
123
|
-
if (privateKeyProtocol) {
|
|
124
|
-
const namedSigner = await privateKeyProtocol(`privateKey:${accountDef}`);
|
|
125
|
-
const [address] = await namedSigner.signer.request({method: 'eth_accounts'});
|
|
126
|
-
accountCache[name] = account = {
|
|
127
|
-
...namedSigner,
|
|
128
|
-
address,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
accountCache[name] = account = {
|
|
133
|
-
type: 'remote',
|
|
134
|
-
address: accountDef as `0x${string}`,
|
|
135
|
-
signer: provider,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
if (accountDef.indexOf(':') > 0) {
|
|
140
|
-
const [protocolID, extra] = accountDef.split(':');
|
|
141
|
-
const protocol = userConfig.signerProtocols?.[protocolID];
|
|
142
|
-
if (!protocol) {
|
|
143
|
-
throw new Error(`protocol: ${protocolID} is not supported`);
|
|
144
|
-
}
|
|
145
|
-
const namedSigner = await protocol(accountDef);
|
|
146
|
-
const [address] = await namedSigner.signer.request({method: 'eth_accounts'});
|
|
147
|
-
accountCache[name] = account = {
|
|
148
|
-
...namedSigner,
|
|
149
|
-
address,
|
|
150
|
-
};
|
|
151
|
-
} else {
|
|
152
|
-
const accountFetched = await getAccount(name, accounts, accounts[accountDef]);
|
|
153
|
-
if (accountFetched) {
|
|
154
|
-
accountCache[name] = account = accountFetched;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
// TODO allow for canonical chain name ?
|
|
160
|
-
const accountForNetwork = accountDef[environmentName] || accountDef[chainId] || accountDef['default'];
|
|
161
|
-
if (typeof accountForNetwork !== undefined) {
|
|
162
|
-
const accountFetched = await getAccount(name, accounts, accountForNetwork);
|
|
163
|
-
if (accountFetched) {
|
|
164
|
-
accountCache[name] = account = accountFetched;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return account;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (userConfig.accounts) {
|
|
173
|
-
const accountNames = Object.keys(userConfig.accounts);
|
|
174
|
-
for (const accountName of accountNames) {
|
|
175
|
-
const account = await getAccount(accountName, userConfig.accounts, userConfig.accounts[accountName]);
|
|
176
|
-
if (!account) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
`cannot get account for ${accountName} = ${JSON.stringify(
|
|
179
|
-
userConfig.accounts[accountName],
|
|
180
|
-
null,
|
|
181
|
-
2
|
|
182
|
-
)}\nEnsure your provider (or hardhat) has some accounts set up for ${environmentName}\n`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
(resolvedAccounts as any)[accountName] = account;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const resolvedData: ResolvedNetworkSpecificData<Data> = {} as ResolvedNetworkSpecificData<Data>;
|
|
190
|
-
async function getData<T = unknown>(name: string, dataDef: DataType<T>): Promise<T | undefined> {
|
|
191
|
-
const dataForNetwork = dataDef[environmentName] || dataDef[chainId] || dataDef['default'];
|
|
192
|
-
return dataForNetwork;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (userConfig.data) {
|
|
196
|
-
const dataFields = Object.keys(userConfig.data);
|
|
197
|
-
for (const dataField of dataFields) {
|
|
198
|
-
let fieldData = await getData(dataField, userConfig.data[dataField]);
|
|
199
|
-
(resolvedData as any)[dataField] = fieldData;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const context = {
|
|
204
|
-
accounts: resolvedAccounts,
|
|
205
|
-
data: resolvedData,
|
|
206
|
-
fork: resolvedExecutionParams.environment.fork,
|
|
207
|
-
saveDeployments,
|
|
208
|
-
tags: networkTags,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// console.log(`context`, JSON.stringify(context.network, null, 2));
|
|
212
|
-
|
|
213
|
-
const {deployments, migrations} = loadDeployments(
|
|
214
|
-
deploymentsFolder,
|
|
215
|
-
environmentName,
|
|
216
|
-
false,
|
|
217
|
-
context.fork
|
|
218
|
-
? undefined
|
|
219
|
-
: {
|
|
220
|
-
chainId,
|
|
221
|
-
genesisHash,
|
|
222
|
-
deleteDeploymentsIfDifferentGenesisHash: true,
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
const namedAccounts: {[name: string]: EIP1193Account} = {};
|
|
227
|
-
const namedSigners: {[name: string]: Signer} = {};
|
|
228
|
-
const addressSigners: {[name: `0x${string}`]: Signer} = {};
|
|
229
|
-
|
|
230
|
-
for (const entry of Object.entries(resolvedAccounts)) {
|
|
231
|
-
const name = entry[0];
|
|
232
|
-
const {address, ...namedSigner} = entry[1];
|
|
233
|
-
namedAccounts[name] = address;
|
|
234
|
-
addressSigners[address] = namedSigner;
|
|
235
|
-
namedSigners[name] = namedSigner;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const unnamedAccounts = allRemoteAccounts.filter((v) => !addressSigners[v]);
|
|
239
|
-
for (const account of unnamedAccounts) {
|
|
240
|
-
addressSigners[account] = {
|
|
241
|
-
type: 'remote',
|
|
242
|
-
signer: provider,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const perliminaryEnvironment = {
|
|
247
|
-
context: {
|
|
248
|
-
saveDeployments: context.saveDeployments,
|
|
249
|
-
},
|
|
250
|
-
name: environmentName,
|
|
251
|
-
tags: context.tags,
|
|
252
|
-
deployments: deployments as Deployments,
|
|
253
|
-
namedAccounts: namedAccounts as ResolvedNamedAccounts<NamedAccounts>,
|
|
254
|
-
data: resolvedData,
|
|
255
|
-
namedSigners: namedSigners as ResolvedNamedSigners<ResolvedNamedAccounts<NamedAccounts>>,
|
|
256
|
-
unnamedAccounts,
|
|
257
|
-
addressSigners: addressSigners,
|
|
258
|
-
network: {
|
|
259
|
-
chain: resolvedExecutionParams.chain,
|
|
260
|
-
fork: context.fork,
|
|
261
|
-
provider,
|
|
262
|
-
deterministicDeployment: resolvedExecutionParams.environment.deterministicDeployment,
|
|
263
|
-
|
|
264
|
-
// for backward compatibility
|
|
265
|
-
tags: context.tags,
|
|
266
|
-
},
|
|
267
|
-
extra: resolvedExecutionParams.extra || {},
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
function getDeploymentFolder(): string {
|
|
271
|
-
const folderPath = path.join(deploymentsFolder, environmentName);
|
|
272
|
-
return folderPath;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function ensureDeploymentFolder(): string {
|
|
276
|
-
const folderPath = getDeploymentFolder();
|
|
277
|
-
fs.mkdirSync(folderPath, {recursive: true});
|
|
278
|
-
// const chainIdFilepath = path.join(folderPath, '.chainId');
|
|
279
|
-
// if (!fs.existsSync(chainIdFilepath)) {
|
|
280
|
-
// fs.writeFileSync(chainIdFilepath, chainId);
|
|
281
|
-
// }
|
|
282
|
-
const chainFilepath = path.join(folderPath, '.chain');
|
|
283
|
-
if (!fs.existsSync(chainFilepath)) {
|
|
284
|
-
fs.writeFileSync(chainFilepath, JSON.stringify({chainId, genesisHash}));
|
|
285
|
-
}
|
|
286
|
-
return folderPath;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// const signer = {
|
|
290
|
-
// async sendTransaction(
|
|
291
|
-
// provider: EIP1193ProviderWithoutEvents,
|
|
292
|
-
// account: {
|
|
293
|
-
// addresss: EIP1193Account;
|
|
294
|
-
// config: unknown;
|
|
295
|
-
// },
|
|
296
|
-
// transaction: EIP1193TransactionEIP1193DATA
|
|
297
|
-
// ): Promise<EIP1193DATA> {
|
|
298
|
-
// return '0x';
|
|
299
|
-
// },
|
|
300
|
-
// };
|
|
301
|
-
|
|
302
|
-
// async function sendTransaction(transaction: EIP1193TransactionEIP1193DATA): Promise<EIP1193DATA> {
|
|
303
|
-
// return '0x';
|
|
304
|
-
// }
|
|
305
|
-
|
|
306
|
-
function get<TAbi extends Abi>(name: string): Deployment<TAbi> {
|
|
307
|
-
const deployment = deployments[name] as Deployment<TAbi>;
|
|
308
|
-
if (!deployment) {
|
|
309
|
-
throw new Error(`no deployment named "${name}" found.`);
|
|
310
|
-
}
|
|
311
|
-
return deployment;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function getOrNull<TAbi extends Abi>(name: string): Deployment<TAbi> | null {
|
|
315
|
-
return (deployments[name] || null) as Deployment<TAbi> | null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function hasMigrationBeenDone(id: string): boolean {
|
|
319
|
-
return migrations[id] ? true : false;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function recordMigration(id: string): void {
|
|
323
|
-
migrations[id] = Math.floor(Date.now() / 1000);
|
|
324
|
-
if (context.saveDeployments) {
|
|
325
|
-
const folderPath = ensureDeploymentFolder();
|
|
326
|
-
fs.writeFileSync(`${folderPath}/.migrations.json`, JSON.stringify(migrations));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function fromAddressToNamedABIOrNull<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]} | null {
|
|
331
|
-
let list: {name: string; artifact: Artifact<Abi>}[] = [];
|
|
332
|
-
for (const name of Object.keys(deployments)) {
|
|
333
|
-
const deployment = deployments[name];
|
|
334
|
-
if (deployment.address.toLowerCase() == address.toLowerCase()) {
|
|
335
|
-
list.push({name, artifact: deployment});
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (list.length === 0) {
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const {mergedABI} = mergeArtifacts(list);
|
|
343
|
-
return {
|
|
344
|
-
mergedABI: mergedABI as unknown as TAbi,
|
|
345
|
-
names: list.map((v) => v.name),
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function fromAddressToNamedABI<TAbi extends Abi>(address: Address): {mergedABI: TAbi; names: string[]} {
|
|
350
|
-
const n = fromAddressToNamedABIOrNull<TAbi>(address);
|
|
351
|
-
if (!n) {
|
|
352
|
-
throw new Error(`could not find artifact for address ${address}`);
|
|
353
|
-
}
|
|
354
|
-
return n;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async function save<TAbi extends Abi>(
|
|
358
|
-
name: string,
|
|
359
|
-
deployment: Deployment<TAbi>,
|
|
360
|
-
options?: {doNotCountAsNewDeployment?: boolean}
|
|
361
|
-
): Promise<Deployment<TAbi>> {
|
|
362
|
-
if (!options?.doNotCountAsNewDeployment) {
|
|
363
|
-
let numDeployments = 1;
|
|
364
|
-
const oldDeployment = deployments[name];
|
|
365
|
-
if (oldDeployment) {
|
|
366
|
-
numDeployments = (oldDeployment.numDeployments || 1) + 1;
|
|
367
|
-
}
|
|
368
|
-
deployments[name] = {...deployment, numDeployments};
|
|
369
|
-
} else {
|
|
370
|
-
deployments[name] = {...deployment, numDeployments: 1};
|
|
371
|
-
}
|
|
372
|
-
if (context.saveDeployments) {
|
|
373
|
-
const folderPath = ensureDeploymentFolder();
|
|
374
|
-
fs.writeFileSync(`${folderPath}/${name}.json`, JSONToString(deployment, 2));
|
|
375
|
-
}
|
|
376
|
-
return deployment;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
async function recoverTransactionsIfAny(): Promise<void> {
|
|
380
|
-
if (!context.saveDeployments) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
const folderPath = getDeploymentFolder();
|
|
384
|
-
const filepath = path.join(folderPath, '.pending_transactions.json');
|
|
385
|
-
let existingPendingTansactions: PendingTransaction[];
|
|
386
|
-
try {
|
|
387
|
-
existingPendingTansactions = stringToJSON(fs.readFileSync(filepath, 'utf-8'));
|
|
388
|
-
} catch {
|
|
389
|
-
existingPendingTansactions = [];
|
|
390
|
-
}
|
|
391
|
-
if (existingPendingTansactions.length > 0) {
|
|
392
|
-
while (existingPendingTansactions.length > 0) {
|
|
393
|
-
const pendingTransaction = existingPendingTansactions.shift();
|
|
394
|
-
if (pendingTransaction) {
|
|
395
|
-
if (pendingTransaction.type === 'deployment') {
|
|
396
|
-
const spinner = spin(
|
|
397
|
-
`recovering ${pendingTransaction.name} with transaction ${pendingTransaction.transaction.hash}`
|
|
398
|
-
);
|
|
399
|
-
try {
|
|
400
|
-
await waitForDeploymentTransactionAndSave(pendingTransaction);
|
|
401
|
-
fs.writeFileSync(filepath, JSONToString(existingPendingTansactions, 2));
|
|
402
|
-
spinner.succeed();
|
|
403
|
-
} catch (e) {
|
|
404
|
-
spinner.fail();
|
|
405
|
-
throw e;
|
|
406
|
-
}
|
|
407
|
-
} else {
|
|
408
|
-
const spinner = spin(`recovering execution's transaction ${pendingTransaction.transaction.hash}`);
|
|
409
|
-
try {
|
|
410
|
-
await waitForTransaction(pendingTransaction.transaction.hash);
|
|
411
|
-
fs.writeFileSync(filepath, JSONToString(existingPendingTansactions, 2));
|
|
412
|
-
spinner.succeed();
|
|
413
|
-
} catch (e) {
|
|
414
|
-
spinner.fail();
|
|
415
|
-
throw e;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
fs.rmSync(filepath);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
async function savePendingTransaction(pendingTransaction: PendingTransaction) {
|
|
425
|
-
if (context.saveDeployments) {
|
|
426
|
-
const folderPath = ensureDeploymentFolder();
|
|
427
|
-
const filepath = path.join(folderPath, '.pending_transactions.json');
|
|
428
|
-
let existingPendinTransactions: PendingTransaction[];
|
|
429
|
-
try {
|
|
430
|
-
existingPendinTransactions = stringToJSON(fs.readFileSync(filepath, 'utf-8'));
|
|
431
|
-
} catch {
|
|
432
|
-
existingPendinTransactions = [];
|
|
433
|
-
}
|
|
434
|
-
existingPendinTransactions.push(pendingTransaction);
|
|
435
|
-
fs.writeFileSync(filepath, JSONToString(existingPendinTransactions, 2));
|
|
436
|
-
}
|
|
437
|
-
return deployments;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
async function waitForTransactionReceipt(params: {
|
|
441
|
-
hash: EIP1193DATA;
|
|
442
|
-
// confirmations?: number; // TODO
|
|
443
|
-
// timeout?: number; // TODO
|
|
444
|
-
}): Promise<EIP1193TransactionReceipt> {
|
|
445
|
-
const {hash, pollingInterval} = {pollingInterval: resolvedExecutionParams.pollingInterval, ...params};
|
|
446
|
-
|
|
447
|
-
let receipt: EIP1193TransactionReceipt | null = null;
|
|
448
|
-
try {
|
|
449
|
-
receipt = await provider.request({
|
|
450
|
-
method: 'eth_getTransactionReceipt',
|
|
451
|
-
params: [hash],
|
|
452
|
-
});
|
|
453
|
-
} catch (err) {}
|
|
454
|
-
|
|
455
|
-
if (!receipt || !receipt.blockHash) {
|
|
456
|
-
await wait(pollingInterval);
|
|
457
|
-
return waitForTransactionReceipt(params);
|
|
458
|
-
}
|
|
459
|
-
return receipt;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
async function deleteTransaction<TAbi extends Abi = Abi>(hash: string) {
|
|
463
|
-
if (context.saveDeployments) {
|
|
464
|
-
const folderPath = ensureDeploymentFolder();
|
|
465
|
-
const filepath = path.join(folderPath, '.pending_transactions.json');
|
|
466
|
-
let existingPendinTransactions: PendingTransaction[];
|
|
467
|
-
try {
|
|
468
|
-
existingPendinTransactions = stringToJSON(fs.readFileSync(filepath, 'utf-8'));
|
|
469
|
-
} catch {
|
|
470
|
-
existingPendinTransactions = [];
|
|
471
|
-
}
|
|
472
|
-
existingPendinTransactions = existingPendinTransactions.filter((v) => v.transaction.hash !== hash);
|
|
473
|
-
if (existingPendinTransactions.length === 0) {
|
|
474
|
-
fs.rmSync(filepath);
|
|
475
|
-
} else {
|
|
476
|
-
fs.writeFileSync(filepath, JSONToString(existingPendinTransactions, 2));
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
async function exportDeploymentsAsTypes() {
|
|
482
|
-
const folderPath = './generated';
|
|
483
|
-
fs.mkdirSync(folderPath, {recursive: true});
|
|
484
|
-
fs.writeFileSync(`${folderPath}/deployments.ts`, `export default ${JSONToString(deployments, 2)} as const;`);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async function waitForTransaction(
|
|
488
|
-
hash: `0x${string}`,
|
|
489
|
-
info?: {message?: string; transaction?: EIP1193Transaction | null}
|
|
490
|
-
): Promise<EIP1193TransactionReceipt> {
|
|
491
|
-
const spinner = spin(
|
|
492
|
-
info?.message
|
|
493
|
-
? info.message
|
|
494
|
-
: ` - Broadcasting tx:\n ${hash}${
|
|
495
|
-
info?.transaction ? `\n ${displayTransaction(info?.transaction)}` : ''
|
|
496
|
-
}`
|
|
497
|
-
);
|
|
498
|
-
let receipt: EIP1193TransactionReceipt;
|
|
499
|
-
try {
|
|
500
|
-
receipt = await waitForTransactionReceipt({
|
|
501
|
-
hash,
|
|
502
|
-
});
|
|
503
|
-
} catch (e) {
|
|
504
|
-
spinner.fail();
|
|
505
|
-
throw e;
|
|
506
|
-
}
|
|
507
|
-
if (!receipt) {
|
|
508
|
-
throw new Error(`receipt for ${hash} not found`);
|
|
509
|
-
} else {
|
|
510
|
-
spinner.succeed();
|
|
511
|
-
}
|
|
512
|
-
return receipt;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async function waitForDeploymentTransactionAndSave<TAbi extends Abi = Abi>(
|
|
516
|
-
pendingDeployment: PendingDeployment<TAbi>,
|
|
517
|
-
transaction?: EIP1193Transaction | null
|
|
518
|
-
): Promise<Deployment<TAbi>> {
|
|
519
|
-
const nameToDisplay = pendingDeployment.name || '<no name>';
|
|
520
|
-
const message = ` - Deploying ${nameToDisplay} with tx:\n ${pendingDeployment.transaction.hash}${
|
|
521
|
-
transaction ? `\n ${displayTransaction(transaction)}` : ''
|
|
522
|
-
}`;
|
|
523
|
-
const receipt = await waitForTransaction(pendingDeployment.transaction.hash, {
|
|
524
|
-
message,
|
|
525
|
-
transaction,
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// TODO we could make pendingDeployment.expectedAddress a spec for fetching address from event too
|
|
529
|
-
const contractAddress = pendingDeployment.expectedAddress || receipt.contractAddress;
|
|
530
|
-
if (!contractAddress) {
|
|
531
|
-
console.error(receipt);
|
|
532
|
-
throw new Error(`no contract address found for ${nameToDisplay}`);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
showMessage(` => ${contractAddress}`);
|
|
536
|
-
|
|
537
|
-
const {abi, ...artifactObjectWithoutABI} = pendingDeployment.partialDeployment;
|
|
538
|
-
|
|
539
|
-
if (!pendingDeployment.transaction.nonce) {
|
|
540
|
-
// const spinner = spin(`fetching nonce for ${pendingDeployment.transaction.hash}`);
|
|
541
|
-
let transaction: EIP1193Transaction | null = null;
|
|
542
|
-
try {
|
|
543
|
-
transaction = await provider.request({
|
|
544
|
-
method: 'eth_getTransactionByHash',
|
|
545
|
-
params: [pendingDeployment.transaction.hash],
|
|
546
|
-
});
|
|
547
|
-
} catch (e) {
|
|
548
|
-
// spinner.fail(`failed to get transaction, even after receipt was found`);
|
|
549
|
-
throw e;
|
|
550
|
-
}
|
|
551
|
-
if (!transaction) {
|
|
552
|
-
// spinner.fail(`tx ${pendingDeployment.transaction.hash} not found, even after receipt was found`);
|
|
553
|
-
// or : spinner.stop();
|
|
554
|
-
} else {
|
|
555
|
-
// spinner.stop();
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if (transaction) {
|
|
559
|
-
pendingDeployment.transaction = {
|
|
560
|
-
nonce: transaction.nonce,
|
|
561
|
-
hash: transaction.hash,
|
|
562
|
-
origin: transaction.from,
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// TODO options
|
|
568
|
-
for (const key of Object.keys(artifactObjectWithoutABI)) {
|
|
569
|
-
if (key.startsWith('_')) {
|
|
570
|
-
delete (artifactObjectWithoutABI as any)[key];
|
|
571
|
-
}
|
|
572
|
-
if (key === 'evm') {
|
|
573
|
-
if (artifactObjectWithoutABI.evm) {
|
|
574
|
-
if ('gasEstimates' in artifactObjectWithoutABI['evm']) {
|
|
575
|
-
const {gasEstimates} = artifactObjectWithoutABI.evm;
|
|
576
|
-
artifactObjectWithoutABI.evm = {
|
|
577
|
-
gasEstimates,
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const deployment = {
|
|
585
|
-
address: contractAddress,
|
|
586
|
-
abi,
|
|
587
|
-
...artifactObjectWithoutABI,
|
|
588
|
-
transaction: pendingDeployment.transaction,
|
|
589
|
-
receipt: {
|
|
590
|
-
blockHash: receipt.blockHash,
|
|
591
|
-
blockNumber: receipt.blockNumber,
|
|
592
|
-
transactionIndex: receipt.transactionIndex,
|
|
593
|
-
},
|
|
594
|
-
};
|
|
595
|
-
if (pendingDeployment.name) {
|
|
596
|
-
return save(pendingDeployment.name, deployment);
|
|
597
|
-
} else {
|
|
598
|
-
return deployment;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
async function savePendingExecution(pendingExecution: PendingExecution) {
|
|
603
|
-
await savePendingTransaction(pendingExecution);
|
|
604
|
-
let transaction: EIP1193Transaction | null = null;
|
|
605
|
-
const spinner = spin(); // TODO spin(`fetching tx from peers ${pendingDeployment.txHash}`);
|
|
606
|
-
try {
|
|
607
|
-
transaction = await provider.request({
|
|
608
|
-
method: 'eth_getTransactionByHash',
|
|
609
|
-
params: [pendingExecution.transaction.hash],
|
|
610
|
-
});
|
|
611
|
-
} catch (e) {
|
|
612
|
-
spinner.fail();
|
|
613
|
-
throw e;
|
|
614
|
-
}
|
|
615
|
-
if (!transaction) {
|
|
616
|
-
// spinner.fail(`execution tx ${pendingExecution.transaction.hash} not found in the mempool yet`);
|
|
617
|
-
spinner.stop();
|
|
618
|
-
} else {
|
|
619
|
-
spinner.stop();
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
if (transaction) {
|
|
623
|
-
pendingExecution.transaction.nonce = transaction.nonce;
|
|
624
|
-
pendingExecution.transaction.origin = transaction.from;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const receipt = await waitForTransaction(pendingExecution.transaction.hash, {transaction});
|
|
628
|
-
|
|
629
|
-
await deleteTransaction(pendingExecution.transaction.hash);
|
|
630
|
-
return receipt;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
async function savePendingDeployment<TAbi extends Abi = Abi>(pendingDeployment: PendingDeployment<TAbi>) {
|
|
634
|
-
await savePendingTransaction(pendingDeployment);
|
|
635
|
-
let transaction: EIP1193Transaction | null = null;
|
|
636
|
-
const spinner = spin(); // TODO spin(`fetching tx from peers ${pendingDeployment.txHash}`);
|
|
637
|
-
try {
|
|
638
|
-
transaction = await provider.request({
|
|
639
|
-
method: 'eth_getTransactionByHash',
|
|
640
|
-
params: [pendingDeployment.transaction.hash],
|
|
641
|
-
});
|
|
642
|
-
} catch (e) {
|
|
643
|
-
spinner.fail(`failed to fetch tx ${pendingDeployment.transaction.hash}. Can't know its status`);
|
|
644
|
-
throw e;
|
|
645
|
-
}
|
|
646
|
-
if (!transaction) {
|
|
647
|
-
// spinner.fail(`deployment tx ${pendingDeployment.transaction.hash} not found in the mempool yet`);
|
|
648
|
-
spinner.stop();
|
|
649
|
-
} else {
|
|
650
|
-
spinner.stop();
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (transaction) {
|
|
654
|
-
// we update the tx data with the one we get from the network
|
|
655
|
-
pendingDeployment = {
|
|
656
|
-
...pendingDeployment,
|
|
657
|
-
transaction: {hash: transaction.hash, nonce: transaction.nonce, origin: transaction.from},
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
const deployment = await waitForDeploymentTransactionAndSave<TAbi>(pendingDeployment, transaction);
|
|
662
|
-
await deleteTransaction(pendingDeployment.transaction.hash);
|
|
663
|
-
return deployment;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
function showMessage(message: string) {
|
|
667
|
-
log(message);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
function showProgress(message?: string): ProgressIndicator {
|
|
671
|
-
return spin(message);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
let env: Environment<NamedAccounts, Data, Deployments> = {
|
|
675
|
-
...perliminaryEnvironment,
|
|
676
|
-
save,
|
|
677
|
-
savePendingDeployment,
|
|
678
|
-
savePendingExecution,
|
|
679
|
-
get,
|
|
680
|
-
getOrNull,
|
|
681
|
-
fromAddressToNamedABI,
|
|
682
|
-
fromAddressToNamedABIOrNull,
|
|
683
|
-
showMessage,
|
|
684
|
-
showProgress,
|
|
685
|
-
hasMigrationBeenDone,
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
return {
|
|
689
|
-
external: env,
|
|
690
|
-
internal: {
|
|
691
|
-
exportDeploymentsAsTypes,
|
|
692
|
-
recoverTransactionsIfAny,
|
|
693
|
-
recordMigration,
|
|
694
|
-
},
|
|
695
|
-
};
|
|
696
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {EIP1193GenericRequest, EIP1193GenericRequestProvider, EIP1193ProviderWithoutEvents} from 'eip-1193';
|
|
2
|
-
|
|
3
|
-
export abstract class BaseProvider implements EIP1193ProviderWithoutEvents {
|
|
4
|
-
constructor(protected provider: EIP1193ProviderWithoutEvents) {}
|
|
5
|
-
|
|
6
|
-
request(args: any): Promise<any> {
|
|
7
|
-
return this._request(args);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
protected abstract _request<T = unknown, V extends EIP1193GenericRequest = EIP1193GenericRequest>(
|
|
11
|
-
args: V
|
|
12
|
-
): Promise<T>;
|
|
13
|
-
}
|