rocketh 0.4.41 → 0.5.1
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/.prettierignore +3 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +7 -0
- package/README.md +1 -21
- package/dist/chunk-KWY4QLNL.js +648 -0
- package/dist/chunk-KWY4QLNL.js.map +1 -0
- package/dist/cli.cjs +697 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +684 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -20
- package/src/cli.ts +37 -0
- package/src/environment/deployments.ts +79 -0
- package/src/environment/index.ts +392 -0
- package/src/environment/types.ts +158 -0
- package/src/executor/index.ts +302 -0
- package/src/executor/types.ts +42 -0
- package/src/index.ts +5 -0
- package/src/internal/types.ts +6 -0
- package/src/utils/fs.ts +70 -0
- package/src/utils/json.ts +26 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +5 -0
- package/.gitattributes +0 -1
- package/bitski_subprovider.js +0 -148
- package/geth_test_server.js +0 -194
- package/index.js +0 -424
- package/provider.js +0 -58
- package/providerengine.js +0 -128
- package/run.js +0 -1575
- package/run_ganache.js +0 -27
- package/utils.js +0 -188
- package/walletprovider.js +0 -232
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {traverseMultipleDirectory} from '../utils/fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import type {
|
|
5
|
+
Config,
|
|
6
|
+
Environment,
|
|
7
|
+
ResolvedConfig,
|
|
8
|
+
ResolvedNamedAccounts,
|
|
9
|
+
UnknownArtifacts,
|
|
10
|
+
UnknownDeployments,
|
|
11
|
+
UnresolvedUnknownNamedAccounts,
|
|
12
|
+
} from '../environment/types';
|
|
13
|
+
import {createEnvironment} from '../environment';
|
|
14
|
+
import {DeployScriptFunction, DeployScriptModule, ProvidedContext} from './types';
|
|
15
|
+
|
|
16
|
+
require('esbuild-register/dist/node').register();
|
|
17
|
+
|
|
18
|
+
export function execute<
|
|
19
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
20
|
+
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
|
|
21
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
22
|
+
>(
|
|
23
|
+
context: ProvidedContext<Artifacts, NamedAccounts>,
|
|
24
|
+
callback: DeployScriptFunction<Artifacts, ResolvedNamedAccounts<NamedAccounts>, Deployments>,
|
|
25
|
+
options: {tags?: string[]; dependencies?: string[]}
|
|
26
|
+
): DeployScriptModule<Artifacts, NamedAccounts, Deployments> {
|
|
27
|
+
const scriptModule = (env: Environment<Artifacts, ResolvedNamedAccounts<NamedAccounts>, Deployments>) =>
|
|
28
|
+
callback(env);
|
|
29
|
+
scriptModule.providedContext = context;
|
|
30
|
+
scriptModule.tags = options.tags;
|
|
31
|
+
scriptModule.dependencies = options.dependencies;
|
|
32
|
+
// TODO id + skip
|
|
33
|
+
return scriptModule as unknown as DeployScriptModule<Artifacts, NamedAccounts, Deployments>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ConfigOptions = {network: string; deployments?: string; scripts?: string; tags?: string};
|
|
37
|
+
|
|
38
|
+
export function readConfig(options: ConfigOptions, extra?: {ignoreMissingRPC?: boolean}): Config {
|
|
39
|
+
type Networks = {[name: string]: {rpcUrl: string}};
|
|
40
|
+
type ConfigFile = {networks: Networks};
|
|
41
|
+
let configFile: ConfigFile | undefined;
|
|
42
|
+
try {
|
|
43
|
+
const configString = fs.readFileSync('./rocketh.json', 'utf-8');
|
|
44
|
+
configFile = JSON.parse(configString);
|
|
45
|
+
} catch {}
|
|
46
|
+
|
|
47
|
+
let nodeUrl: string;
|
|
48
|
+
const fromEnv = process.env['ETH_NODE_URI_' + options.network];
|
|
49
|
+
if (typeof fromEnv === 'string') {
|
|
50
|
+
nodeUrl = fromEnv;
|
|
51
|
+
} else {
|
|
52
|
+
if (configFile) {
|
|
53
|
+
const network = configFile.networks && configFile.networks[options.network];
|
|
54
|
+
if (network) {
|
|
55
|
+
nodeUrl = network.rpcUrl;
|
|
56
|
+
} else {
|
|
57
|
+
if (extra?.ignoreMissingRPC) {
|
|
58
|
+
nodeUrl = '';
|
|
59
|
+
} else {
|
|
60
|
+
console.error(`network "${options.network}" is not configured. Please add it to the rocketh.json file`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
if (extra?.ignoreMissingRPC) {
|
|
66
|
+
nodeUrl = '';
|
|
67
|
+
} else {
|
|
68
|
+
console.error(`network "${options.network}" is not configured. Please add it to the rocketh.json file`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
nodeUrl,
|
|
76
|
+
networkName: options.network,
|
|
77
|
+
deployments: options.deployments,
|
|
78
|
+
scripts: options.scripts,
|
|
79
|
+
tags: typeof options.tags === 'undefined' ? undefined : options.tags.split(','),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function readAndResolveConfig(options: ConfigOptions, extra?: {ignoreMissingRPC?: boolean}): ResolvedConfig {
|
|
84
|
+
return resolveConfig(readConfig(options, extra));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function resolveConfig(config: Config): ResolvedConfig {
|
|
88
|
+
const resolvedConfig: ResolvedConfig = {
|
|
89
|
+
...config,
|
|
90
|
+
networkName: config.networkName || 'memory',
|
|
91
|
+
deployments: config.deployments || 'deployments',
|
|
92
|
+
scripts: config.scripts || 'deploy',
|
|
93
|
+
tags: config.tags || [],
|
|
94
|
+
};
|
|
95
|
+
return resolvedConfig;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function loadAndExecuteDeployments<
|
|
99
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
100
|
+
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
|
|
101
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
102
|
+
>(config: Config) {
|
|
103
|
+
const resolvedConfig = resolveConfig(config);
|
|
104
|
+
|
|
105
|
+
return executeDeployScripts<Artifacts, NamedAccounts, Deployments>(resolvedConfig);
|
|
106
|
+
|
|
107
|
+
// TODO
|
|
108
|
+
// await this.export(options);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function executeDeployScripts<
|
|
112
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
113
|
+
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
|
|
114
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
115
|
+
>(config: ResolvedConfig): Promise<Deployments> {
|
|
116
|
+
let filepaths;
|
|
117
|
+
filepaths = traverseMultipleDirectory([config.scripts]);
|
|
118
|
+
filepaths = filepaths
|
|
119
|
+
.filter((v) => !path.basename(v).startsWith('_'))
|
|
120
|
+
.sort((a: string, b: string) => {
|
|
121
|
+
if (a < b) {
|
|
122
|
+
return -1;
|
|
123
|
+
}
|
|
124
|
+
if (a > b) {
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
let providedContext: ProvidedContext<Artifacts, NamedAccounts> | undefined;
|
|
131
|
+
|
|
132
|
+
const scriptModuleByFilePath: {[filename: string]: DeployScriptModule} = {};
|
|
133
|
+
const scriptPathBags: {[tag: string]: string[]} = {};
|
|
134
|
+
const scriptFilePaths: string[] = [];
|
|
135
|
+
|
|
136
|
+
for (const filepath of filepaths) {
|
|
137
|
+
const scriptFilePath = path.resolve(filepath);
|
|
138
|
+
let scriptModule: DeployScriptModule;
|
|
139
|
+
try {
|
|
140
|
+
if (require.cache) {
|
|
141
|
+
delete require.cache[scriptFilePath]; // ensure we reload it every time, so changes are taken in consideration
|
|
142
|
+
}
|
|
143
|
+
scriptModule = require(scriptFilePath);
|
|
144
|
+
|
|
145
|
+
if ((scriptModule as any).default) {
|
|
146
|
+
scriptModule = (scriptModule as any).default as DeployScriptModule;
|
|
147
|
+
if ((scriptModule as any).default) {
|
|
148
|
+
console.warn(`double default...`);
|
|
149
|
+
scriptModule = (scriptModule as any).default as DeployScriptModule;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
scriptModuleByFilePath[scriptFilePath] = scriptModule;
|
|
153
|
+
if (providedContext && providedContext !== scriptModule.providedContext) {
|
|
154
|
+
throw new Error(`context between 2 scripts is different, please share the same across them`);
|
|
155
|
+
}
|
|
156
|
+
providedContext = scriptModule.providedContext as ProvidedContext<Artifacts, NamedAccounts>;
|
|
157
|
+
} catch (e) {
|
|
158
|
+
console.error(`could not import ${filepath}`);
|
|
159
|
+
throw e;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let scriptTags = scriptModule.tags;
|
|
163
|
+
if (scriptTags !== undefined) {
|
|
164
|
+
if (typeof scriptTags === 'string') {
|
|
165
|
+
scriptTags = [scriptTags];
|
|
166
|
+
}
|
|
167
|
+
for (const tag of scriptTags) {
|
|
168
|
+
if (tag.indexOf(',') >= 0) {
|
|
169
|
+
throw new Error('Tag cannot contains commas');
|
|
170
|
+
}
|
|
171
|
+
const bag = scriptPathBags[tag] || [];
|
|
172
|
+
scriptPathBags[tag] = bag;
|
|
173
|
+
bag.push(scriptFilePath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (config.tags !== undefined && config.tags.length > 0) {
|
|
178
|
+
let found = false;
|
|
179
|
+
if (scriptTags !== undefined) {
|
|
180
|
+
for (const tagToFind of config.tags) {
|
|
181
|
+
for (const tag of scriptTags) {
|
|
182
|
+
if (tag === tagToFind) {
|
|
183
|
+
scriptFilePaths.push(scriptFilePath);
|
|
184
|
+
found = true;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (found) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
scriptFilePaths.push(scriptFilePath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!providedContext) {
|
|
199
|
+
throw new Error(`no context loaded`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const {internal, external} = await createEnvironment(config, providedContext);
|
|
203
|
+
|
|
204
|
+
await internal.recoverTransactionsIfAny();
|
|
205
|
+
|
|
206
|
+
const scriptsRegisteredToRun: {[filename: string]: boolean} = {};
|
|
207
|
+
const scriptsToRun: Array<{
|
|
208
|
+
func: DeployScriptModule;
|
|
209
|
+
filePath: string;
|
|
210
|
+
}> = [];
|
|
211
|
+
const scriptsToRunAtTheEnd: Array<{
|
|
212
|
+
func: DeployScriptModule;
|
|
213
|
+
filePath: string;
|
|
214
|
+
}> = [];
|
|
215
|
+
function recurseDependencies(scriptFilePath: string) {
|
|
216
|
+
if (scriptsRegisteredToRun[scriptFilePath]) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const scriptModule = scriptModuleByFilePath[scriptFilePath];
|
|
220
|
+
if (scriptModule.dependencies) {
|
|
221
|
+
for (const dependency of scriptModule.dependencies) {
|
|
222
|
+
const scriptFilePathsToAdd = scriptPathBags[dependency];
|
|
223
|
+
if (scriptFilePathsToAdd) {
|
|
224
|
+
for (const scriptFilenameToAdd of scriptFilePathsToAdd) {
|
|
225
|
+
recurseDependencies(scriptFilenameToAdd);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!scriptsRegisteredToRun[scriptFilePath]) {
|
|
231
|
+
if (scriptModule.runAtTheEnd) {
|
|
232
|
+
scriptsToRunAtTheEnd.push({
|
|
233
|
+
filePath: scriptFilePath,
|
|
234
|
+
func: scriptModule,
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
scriptsToRun.push({
|
|
238
|
+
filePath: scriptFilePath,
|
|
239
|
+
func: scriptModule,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
scriptsRegisteredToRun[scriptFilePath] = true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (const scriptFilePath of scriptFilePaths) {
|
|
246
|
+
recurseDependencies(scriptFilePath);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const deployScript of scriptsToRun.concat(scriptsToRunAtTheEnd)) {
|
|
250
|
+
const filename = path.basename(deployScript.filePath);
|
|
251
|
+
// if (deployScript.func.id && this.db.migrations[deployScript.func.id]) {
|
|
252
|
+
// log(`skipping ${filename} as migrations already executed and complete`);
|
|
253
|
+
// continue;
|
|
254
|
+
// }
|
|
255
|
+
let skip = false;
|
|
256
|
+
if (deployScript.func.skip) {
|
|
257
|
+
try {
|
|
258
|
+
skip = await deployScript.func.skip(external);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.error(`skip failed for ${deployScript.filePath}`);
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!skip) {
|
|
265
|
+
let result;
|
|
266
|
+
try {
|
|
267
|
+
// console.log(`Executing...`);
|
|
268
|
+
result = await deployScript.func(external);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
console.error(`execution failed for ${deployScript.filePath}`);
|
|
271
|
+
throw e;
|
|
272
|
+
}
|
|
273
|
+
if (result && typeof result === 'boolean') {
|
|
274
|
+
// if (!deployScript.func.id) {
|
|
275
|
+
// throw new Error(
|
|
276
|
+
// `${deployScript.filePath} return true to not be executed again, but does not provide an id. the script function needs to have the field "id" to be set`
|
|
277
|
+
// );
|
|
278
|
+
// }
|
|
279
|
+
// this.db.migrations[deployScript.func.id] = Math.floor(Date.now() / 1000);
|
|
280
|
+
|
|
281
|
+
const deploymentFolderPath = config.deployments;
|
|
282
|
+
|
|
283
|
+
// TODO refactor to extract this whole path and folder existence stuff
|
|
284
|
+
// const toSave = this.db.writeDeploymentsToFiles && this.network.saveDeployments;
|
|
285
|
+
// if (toSave) {
|
|
286
|
+
// try {
|
|
287
|
+
// fs.mkdirSync(this.deploymentsPath);
|
|
288
|
+
// } catch (e) {}
|
|
289
|
+
// try {
|
|
290
|
+
// fs.mkdirSync(path.join(this.deploymentsPath, deploymentFolderPath));
|
|
291
|
+
// } catch (e) {}
|
|
292
|
+
// fs.writeFileSync(
|
|
293
|
+
// path.join(this.deploymentsPath, deploymentFolderPath, '.migrations.json'),
|
|
294
|
+
// JSON.stringify(this.db.migrations, null, ' ')
|
|
295
|
+
// );
|
|
296
|
+
// }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return external.deployments as Deployments;
|
|
302
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Environment,
|
|
3
|
+
ResolvedNamedAccounts,
|
|
4
|
+
UnknownArtifacts,
|
|
5
|
+
UnknownDeployments,
|
|
6
|
+
UnknownNamedAccounts,
|
|
7
|
+
UnresolvedUnknownNamedAccounts,
|
|
8
|
+
} from '../environment/types';
|
|
9
|
+
|
|
10
|
+
export type DeployScriptFunction<
|
|
11
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
12
|
+
NamedAccounts extends UnknownNamedAccounts = UnknownNamedAccounts,
|
|
13
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
14
|
+
> = (env: Environment<Artifacts, NamedAccounts, Deployments>) => Promise<void | boolean>;
|
|
15
|
+
|
|
16
|
+
export interface DeployScriptModule<
|
|
17
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
18
|
+
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts,
|
|
19
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
20
|
+
> {
|
|
21
|
+
(env: Environment<Artifacts, ResolvedNamedAccounts<NamedAccounts>, Deployments>): Promise<void | boolean>;
|
|
22
|
+
providedContext: ProvidedContext<Artifacts, NamedAccounts>;
|
|
23
|
+
skip?: (env: Environment<Artifacts, ResolvedNamedAccounts<NamedAccounts>, Deployments>) => Promise<boolean>;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
dependencies?: string[];
|
|
26
|
+
runAtTheEnd?: boolean;
|
|
27
|
+
id?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ProvidedContext<
|
|
31
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
32
|
+
NamedAccounts extends UnresolvedUnknownNamedAccounts = UnresolvedUnknownNamedAccounts
|
|
33
|
+
> = {
|
|
34
|
+
accounts?: NamedAccounts;
|
|
35
|
+
artifacts: Artifacts;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type ScriptCallback<
|
|
39
|
+
Artifacts extends UnknownArtifacts = UnknownArtifacts,
|
|
40
|
+
NamedAccounts extends UnknownNamedAccounts = UnknownNamedAccounts,
|
|
41
|
+
Deployments extends UnknownDeployments = UnknownDeployments
|
|
42
|
+
> = (env: Environment<Artifacts, NamedAccounts, Deployments>) => Promise<void>;
|
package/src/index.ts
ADDED
package/src/utils/fs.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// taken from https://github.com/vitejs/vite/blob/63524bac878e8d3771d34ad7ad2e10cd16870ff4/packages/vite/src/node/utils.ts#L371-L400
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
interface LookupFileOptions {
|
|
6
|
+
pathOnly?: boolean;
|
|
7
|
+
rootDir?: string;
|
|
8
|
+
predicate?: (file: string) => boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function lookupFile(dir: string, formats: string[], options?: LookupFileOptions): string | undefined {
|
|
12
|
+
for (const format of formats) {
|
|
13
|
+
const fullPath = path.join(dir, format);
|
|
14
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
15
|
+
const result = options?.pathOnly ? fullPath : fs.readFileSync(fullPath, 'utf-8');
|
|
16
|
+
if (!options?.predicate || options.predicate(result)) {
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const parentDir = path.dirname(dir);
|
|
22
|
+
if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) {
|
|
23
|
+
return lookupFile(parentDir, formats, options);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function traverseMultipleDirectory(dirs: string[]): string[] {
|
|
28
|
+
const filepaths = [];
|
|
29
|
+
for (const dir of dirs) {
|
|
30
|
+
let filesStats = traverse(dir);
|
|
31
|
+
filesStats = filesStats.filter((v) => !v.directory);
|
|
32
|
+
for (const filestat of filesStats) {
|
|
33
|
+
filepaths.push(path.join(dir, filestat.relativePath));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return filepaths;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const traverse = function (
|
|
40
|
+
dir: string,
|
|
41
|
+
result: any[] = [],
|
|
42
|
+
topDir?: string,
|
|
43
|
+
filter?: (name: string, stats: any) => boolean // TODO any is Stats
|
|
44
|
+
): Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
path: string;
|
|
47
|
+
relativePath: string;
|
|
48
|
+
mtimeMs: number;
|
|
49
|
+
directory: boolean;
|
|
50
|
+
}> {
|
|
51
|
+
fs.readdirSync(dir).forEach((name) => {
|
|
52
|
+
const fPath = path.resolve(dir, name);
|
|
53
|
+
const stats = fs.statSync(fPath);
|
|
54
|
+
if ((!filter && !name.startsWith('.')) || (filter && filter(name, stats))) {
|
|
55
|
+
const fileStats = {
|
|
56
|
+
name,
|
|
57
|
+
path: fPath,
|
|
58
|
+
relativePath: path.relative(topDir || dir, fPath),
|
|
59
|
+
mtimeMs: stats.mtimeMs,
|
|
60
|
+
directory: stats.isDirectory(),
|
|
61
|
+
};
|
|
62
|
+
if (fileStats.directory) {
|
|
63
|
+
result.push(fileStats);
|
|
64
|
+
return traverse(fPath, result, topDir || dir, filter);
|
|
65
|
+
}
|
|
66
|
+
result.push(fileStats);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// TODO share with db-utils
|
|
2
|
+
export function bnReplacer(k: string, v: any): any {
|
|
3
|
+
if (typeof v === 'bigint') {
|
|
4
|
+
return v.toString() + 'n';
|
|
5
|
+
}
|
|
6
|
+
return v;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function bnReviver(k: string, v: any): any {
|
|
10
|
+
if (
|
|
11
|
+
typeof v === 'string' &&
|
|
12
|
+
(v.startsWith('-') ? !isNaN(parseInt(v.charAt(1))) : !isNaN(parseInt(v.charAt(0)))) &&
|
|
13
|
+
v.charAt(v.length - 1) === 'n'
|
|
14
|
+
) {
|
|
15
|
+
return BigInt(v.slice(0, -1));
|
|
16
|
+
}
|
|
17
|
+
return v;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function JSONToString<T = unknown>(json: unknown, space?: string | number) {
|
|
21
|
+
return JSON.stringify(json, bnReplacer, space);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function stringToJSON<T = unknown>(str: string): T {
|
|
25
|
+
return JSON.parse(str, bnReviver);
|
|
26
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"moduleResolution": "node",
|
|
4
|
+
"lib": ["ES2020"],
|
|
5
|
+
"target": "ES2020",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"module": "Node16"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/tsup.config.ts
ADDED
package/.gitattributes
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
* text=auto eol=lf
|
package/bitski_subprovider.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
const ethers = require('ethers');
|
|
2
|
-
const { BigNumber } = ethers;
|
|
3
|
-
const Bitski = require("bitski-node");
|
|
4
|
-
const {
|
|
5
|
-
log
|
|
6
|
-
} = require('./utils');
|
|
7
|
-
|
|
8
|
-
const BitskiSubProvider = function(clientID, credentialID, secret, accounts, chainId, config) {
|
|
9
|
-
let network = 'mainnet';
|
|
10
|
-
if(chainId === '4') {
|
|
11
|
-
network = 'rinkeby';
|
|
12
|
-
} else if (chainId === '42') {
|
|
13
|
-
network = 'kovan';
|
|
14
|
-
} // else if (chainId == 3) {
|
|
15
|
-
// network = 'ropsten';
|
|
16
|
-
// }
|
|
17
|
-
|
|
18
|
-
this.lastId = 0;
|
|
19
|
-
const options = {
|
|
20
|
-
credentials: {
|
|
21
|
-
id: credentialID,
|
|
22
|
-
secret,
|
|
23
|
-
},
|
|
24
|
-
network,
|
|
25
|
-
debug: config.debug,
|
|
26
|
-
disableBlockTracking: true,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Pass options with the provider
|
|
30
|
-
this.bitskiProvider = Bitski.getProvider(clientID, options);
|
|
31
|
-
// this.bitskiProvider._blockTracker.stop();
|
|
32
|
-
this.accounts = accounts;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
BitskiSubProvider.prototype.setEngine = function(engine) {
|
|
36
|
-
this.engine = engine;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
BitskiSubProvider.prototype.fetchGasPrice = function() {
|
|
40
|
-
const self = this;
|
|
41
|
-
return new Promise((resolve, reject) => {
|
|
42
|
-
self.engine.sendAsync({id: ++this.lastId, method: 'eth_gasPrice', jsonrpc: '2.0'}, (error, json) =>{
|
|
43
|
-
if(error) {
|
|
44
|
-
reject(error);
|
|
45
|
-
} else {
|
|
46
|
-
resolve(json.result);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
BitskiSubProvider.prototype.fetchNonce = function(from) {
|
|
53
|
-
const self = this;
|
|
54
|
-
return new Promise((resolve, reject) => {
|
|
55
|
-
self.engine.sendAsync({ id: ++this.lastId, method: 'eth_getTransactionCount', params: [from, 'latest'], jsonrpc: '2.0'}, (error, json) =>{
|
|
56
|
-
if(error) {
|
|
57
|
-
reject(error);
|
|
58
|
-
} else {
|
|
59
|
-
resolve(json.result);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
BitskiSubProvider.prototype.fetchBalance = function(from) {
|
|
66
|
-
const self = this;
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
self.engine.sendAsync({ id: ++self.lastId, method: 'eth_getBalance', params: [from, 'latest'], jsonrpc: '2.0'}, (error, json) =>{
|
|
69
|
-
if(error) {
|
|
70
|
-
reject(error);
|
|
71
|
-
} else {
|
|
72
|
-
resolve(json.result);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
BitskiSubProvider.prototype.handleRequest = async function(payload, next, end) {
|
|
79
|
-
const self = this;
|
|
80
|
-
if (payload.method === 'eth_accounts' && this.accounts) {
|
|
81
|
-
return end(null, this.accounts);
|
|
82
|
-
} else if (payload.method === 'eth_sendTransaction') {
|
|
83
|
-
const rawTx = payload.params[0];
|
|
84
|
-
const from = rawTx.from;
|
|
85
|
-
|
|
86
|
-
if(!rawTx.gas) {
|
|
87
|
-
return end(new Error('gas not specified'));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const gasPrice = BigNumber.from(rawTx.gasPrice || await this.fetchGasPrice());
|
|
91
|
-
const gas = BigNumber.from(rawTx.gas);
|
|
92
|
-
const balance = BigNumber.from(await this.fetchBalance(from));
|
|
93
|
-
const value = BigNumber.from(rawTx.value || 0);
|
|
94
|
-
const balanceRequired = (gasPrice.mul(gas)).add(value);
|
|
95
|
-
|
|
96
|
-
// console.log({
|
|
97
|
-
// gas: gas.toString(),
|
|
98
|
-
// value: value.toString(),
|
|
99
|
-
// gasPrice: gasPrice.toString(),
|
|
100
|
-
// balanceRequired: balanceRequired.toString(),
|
|
101
|
-
// balance: balance.toString(),
|
|
102
|
-
// })
|
|
103
|
-
if(balance.lt(balanceRequired)) {
|
|
104
|
-
return end(new Error('Not enough balance: '
|
|
105
|
-
+ balanceRequired.toString()
|
|
106
|
-
+ '( ' + gas.toString() + ' gas x ' + gasPrice.toString() + ' gasPrice'
|
|
107
|
-
+ ' ) > ' + balance.toString()));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const nonce = await this.fetchNonce(from);
|
|
111
|
-
let result;
|
|
112
|
-
try{
|
|
113
|
-
rawTx.gasPrice = gasPrice.toHexString();
|
|
114
|
-
rawTx.value = value.toHexString();
|
|
115
|
-
rawTx.gas = gas.toHexString();
|
|
116
|
-
rawTx.nonce = nonce;
|
|
117
|
-
// console.log(JSON.stringify(rawTx, null, ' '));
|
|
118
|
-
result = await this.sendTransaction(rawTx);
|
|
119
|
-
}catch(e) {
|
|
120
|
-
return end(e);
|
|
121
|
-
}
|
|
122
|
-
return end(null, result);
|
|
123
|
-
} else if(payload.method == 'eth_sign') {
|
|
124
|
-
let signedMessage;
|
|
125
|
-
try{
|
|
126
|
-
signedMessage = await this.signMessage(payload.params[0], payload.params[1]);
|
|
127
|
-
} catch(e) {
|
|
128
|
-
return end(e);
|
|
129
|
-
}
|
|
130
|
-
return end(null, signedMessage);
|
|
131
|
-
} else {
|
|
132
|
-
next();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
BitskiSubProvider.prototype.signTransaction = async function(from, rawTx) {
|
|
137
|
-
return this.bitskiProvider.send('eth_signTransaction', [rawTx]);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
BitskiSubProvider.prototype.sendTransaction = async function(tx) {
|
|
141
|
-
return this.bitskiProvider.send('eth_sendTransaction', [tx]);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
BitskiSubProvider.prototype.signMessage = function(from, message) {
|
|
145
|
-
throw new Error('rocketh TODO: sign Message not implemented in bitski provider');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
module.exports = BitskiSubProvider;
|