@alephium/web3 0.1.0 → 0.2.0-rc.10

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.
Files changed (93) hide show
  1. package/.eslintignore +2 -2
  2. package/README.md +2 -135
  3. package/contracts/greeter/greeter.ral +7 -0
  4. package/contracts/{greeter_interface.ral → greeter/greeter_interface.ral} +1 -0
  5. package/contracts/greeter_main.ral +2 -4
  6. package/dist/alephium-web3.min.js +1 -1
  7. package/dist/alephium-web3.min.js.LICENSE.txt +0 -17
  8. package/dist/alephium-web3.min.js.map +1 -1
  9. package/dist/scripts/create-project.js +3 -2
  10. package/dist/src/api/api-alephium.d.ts +58 -13
  11. package/dist/src/api/api-alephium.js +19 -3
  12. package/dist/src/api/api-explorer.d.ts +32 -16
  13. package/dist/src/api/api-explorer.js +26 -0
  14. package/dist/src/api/index.d.ts +2 -2
  15. package/dist/src/api/index.js +18 -3
  16. package/dist/src/contract/contract.d.ts +105 -48
  17. package/dist/src/contract/contract.js +362 -189
  18. package/dist/src/contract/events.d.ts +7 -25
  19. package/dist/src/contract/events.js +18 -31
  20. package/dist/src/contract/index.js +5 -1
  21. package/dist/src/global.d.ts +4 -0
  22. package/dist/src/global.js +38 -0
  23. package/dist/src/index.d.ts +3 -0
  24. package/dist/src/index.js +21 -1
  25. package/dist/src/signer/index.d.ts +0 -1
  26. package/dist/src/signer/index.js +5 -2
  27. package/dist/src/signer/signer.d.ts +1 -1
  28. package/dist/src/signer/signer.js +8 -3
  29. package/dist/src/transaction/index.d.ts +1 -0
  30. package/dist/src/{test → transaction}/index.js +6 -13
  31. package/dist/src/transaction/status.d.ts +10 -0
  32. package/dist/src/transaction/status.js +48 -0
  33. package/dist/src/utils/index.d.ts +1 -2
  34. package/dist/src/utils/index.js +6 -3
  35. package/dist/src/utils/subscription.d.ts +23 -0
  36. package/dist/src/utils/subscription.js +53 -0
  37. package/dist/src/utils/utils.d.ts +5 -2
  38. package/dist/src/utils/utils.js +25 -16
  39. package/jest-config.json +11 -0
  40. package/package.json +7 -35
  41. package/scripts/create-project.ts +3 -2
  42. package/src/api/api-alephium.ts +73 -8
  43. package/src/api/api-explorer.ts +30 -0
  44. package/src/api/index.ts +13 -3
  45. package/src/contract/contract.ts +512 -279
  46. package/src/contract/events.ts +21 -48
  47. package/src/{utils/transaction.ts → global.ts} +14 -15
  48. package/src/index.ts +4 -0
  49. package/src/signer/index.ts +0 -1
  50. package/src/signer/signer.ts +4 -3
  51. package/src/{utils/password-crypto.test.ts → transaction/index.ts} +1 -9
  52. package/src/transaction/status.ts +58 -0
  53. package/src/utils/index.ts +1 -2
  54. package/src/utils/subscription.ts +72 -0
  55. package/src/utils/utils.ts +16 -8
  56. package/templates/base/package.json +2 -2
  57. package/templates/base/src/greeter.ts +8 -7
  58. package/templates/react/package.json +2 -2
  59. package/templates/react/src/App.tsx +2 -2
  60. package/.eslintrc.json +0 -21
  61. package/LICENSE +0 -165
  62. package/contracts/add.ral +0 -16
  63. package/contracts/greeter.ral +0 -7
  64. package/contracts/main.ral +0 -6
  65. package/contracts/sub.ral +0 -9
  66. package/dist/src/signer/node-wallet.d.ts +0 -13
  67. package/dist/src/signer/node-wallet.js +0 -60
  68. package/dist/src/test/index.d.ts +0 -7
  69. package/dist/src/test/privatekey-wallet.d.ts +0 -12
  70. package/dist/src/test/privatekey-wallet.js +0 -68
  71. package/dist/src/utils/password-crypto.d.ts +0 -2
  72. package/dist/src/utils/password-crypto.js +0 -69
  73. package/dist/src/utils/transaction.d.ts +0 -2
  74. package/dist/src/utils/transaction.js +0 -58
  75. package/gitignore +0 -10
  76. package/src/contract/ralph.test.ts +0 -178
  77. package/src/fixtures/address.json +0 -36
  78. package/src/fixtures/balance.json +0 -9
  79. package/src/fixtures/self-clique.json +0 -19
  80. package/src/fixtures/transaction.json +0 -13
  81. package/src/fixtures/transactions.json +0 -179
  82. package/src/signer/fixtures/genesis.json +0 -26
  83. package/src/signer/fixtures/wallets.json +0 -26
  84. package/src/signer/node-wallet.ts +0 -74
  85. package/src/test/index.ts +0 -32
  86. package/src/test/privatekey-wallet.ts +0 -58
  87. package/src/utils/address.test.ts +0 -47
  88. package/src/utils/djb2.test.ts +0 -35
  89. package/src/utils/password-crypto.ts +0 -77
  90. package/src/utils/transaction.test.ts +0 -50
  91. package/src/utils/utils.test.ts +0 -160
  92. package/test/contract.test.ts +0 -161
  93. package/test/events.test.ts +0 -139
@@ -18,7 +18,11 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
18
18
  */
19
19
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
20
  if (k2 === undefined) k2 = k;
21
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
22
26
  }) : (function(o, m, k, k2) {
23
27
  if (k2 === undefined) k2 = k;
24
28
  o[k2] = m[k];
@@ -39,173 +43,389 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
43
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
44
  };
41
45
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.toApiVal = exports.extractArray = exports.Script = exports.Contract = exports.Common = void 0;
46
+ exports.toApiVal = exports.extractArray = exports.Script = exports.Contract = exports.Artifact = exports.Project = exports.DEFAULT_COMPILER_OPTIONS = exports.DEFAULT_NODE_COMPILER_OPTIONS = void 0;
43
47
  const buffer_1 = require("buffer/");
44
- const cryptojs = __importStar(require("crypto-js"));
45
- const crypto = __importStar(require("crypto"));
48
+ const crypto_1 = require("crypto");
46
49
  const fs_1 = __importDefault(require("fs"));
47
50
  const fs_2 = require("fs");
48
51
  const ralph = __importStar(require("./ralph"));
49
52
  const utils_1 = require("../utils");
50
- class Common {
51
- constructor(sourceCodeSha256, functions) {
52
- this.sourceCodeSha256 = sourceCodeSha256;
53
- this.functions = functions;
53
+ const global_1 = require("../global");
54
+ var SourceType;
55
+ (function (SourceType) {
56
+ SourceType[SourceType["Contract"] = 0] = "Contract";
57
+ SourceType[SourceType["Script"] = 1] = "Script";
58
+ SourceType[SourceType["AbstractContract"] = 2] = "AbstractContract";
59
+ SourceType[SourceType["Interface"] = 3] = "Interface";
60
+ })(SourceType || (SourceType = {}));
61
+ exports.DEFAULT_NODE_COMPILER_OPTIONS = {
62
+ ignoreUnusedConstantsWarnings: false,
63
+ ignoreUnusedVariablesWarnings: false,
64
+ ignoreUnusedFieldsWarnings: false,
65
+ ignoreUnusedPrivateFunctionsWarnings: false,
66
+ ignoreReadonlyCheckWarnings: false,
67
+ ignoreExternalCallCheckWarnings: false
68
+ };
69
+ exports.DEFAULT_COMPILER_OPTIONS = { errorOnWarnings: true, ...exports.DEFAULT_NODE_COMPILER_OPTIONS };
70
+ class TypedMatcher {
71
+ constructor(pattern, type) {
72
+ this.matcher = new RegExp(pattern, 'mg');
73
+ this.type = type;
54
74
  }
55
- static _getArtifactFromCache(codeHash) {
56
- return this._artifactCache.get(codeHash);
75
+ match(str) {
76
+ const results = str.match(this.matcher);
77
+ return results === null ? 0 : results.length;
57
78
  }
58
- static _putArtifactToCache(contract) {
59
- if (!this._artifactCache.has(contract.codeHash)) {
60
- if (this._artifactCache.size >= this.artifactCacheCapacity) {
61
- const keyToDelete = this._artifactCache.keys().next().value;
62
- this._artifactCache.delete(keyToDelete);
63
- }
64
- this._artifactCache.set(contract.codeHash, contract);
65
- }
79
+ }
80
+ class SourceFile {
81
+ constructor(type, sourceCode, sourceCodeHash, contractPath) {
82
+ this.type = type;
83
+ this.sourceCode = sourceCode;
84
+ this.sourceCodeHash = sourceCodeHash;
85
+ this.contractPath = contractPath;
66
86
  }
67
- static _contractPath(fileName) {
68
- if (fileName.endsWith('.json')) {
69
- return `./contracts/${fileName.slice(0, -5)}`;
70
- }
71
- else {
72
- return `./contracts/${fileName}`;
73
- }
87
+ getArtifactPath(artifactsRootPath) {
88
+ return artifactsRootPath + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json';
74
89
  }
75
- static _artifactPath(fileName) {
76
- if (fileName.endsWith('.json')) {
77
- return `./artifacts/${fileName}`;
90
+ static async from(type, sourceCode, contractPath) {
91
+ const sourceCodeHash = await crypto_1.webcrypto.subtle.digest('SHA-256', buffer_1.Buffer.from(sourceCode));
92
+ return new SourceFile(type, sourceCode, buffer_1.Buffer.from(sourceCodeHash).toString('hex'), contractPath);
93
+ }
94
+ }
95
+ class Compiled {
96
+ constructor(sourceFile, artifact, warnings) {
97
+ this.sourceFile = sourceFile;
98
+ this.artifact = artifact;
99
+ this.warnings = warnings;
100
+ }
101
+ }
102
+ class ProjectArtifact {
103
+ constructor(compilerOptionsUsed, infos) {
104
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed);
105
+ this.compilerOptionsUsed = compilerOptionsUsed;
106
+ this.infos = infos;
107
+ }
108
+ static checkCompilerOptionsParameter(compilerOptions) {
109
+ if (Object.keys(compilerOptions).length != Object.keys(exports.DEFAULT_NODE_COMPILER_OPTIONS).length) {
110
+ throw Error(`Not all compiler options are set: ${compilerOptions}`);
78
111
  }
79
- else {
80
- return `./artifacts/${fileName}.json`;
112
+ const combined = { ...compilerOptions, ...exports.DEFAULT_NODE_COMPILER_OPTIONS };
113
+ if (Object.keys(combined).length !== Object.keys(exports.DEFAULT_NODE_COMPILER_OPTIONS).length) {
114
+ throw Error(`There are unknown compiler options: ${compilerOptions}`);
81
115
  }
82
116
  }
83
- static _artifactsFolder() {
84
- return './artifacts/';
117
+ async saveToFile(rootPath) {
118
+ const filepath = rootPath + '/' + ProjectArtifact.artifactFileName;
119
+ const artifact = { compilerOptionsUsed: this.compilerOptionsUsed, infos: Object.fromEntries(this.infos) };
120
+ const content = JSON.stringify(artifact, null, 2);
121
+ return fs_2.promises.writeFile(filepath, content);
85
122
  }
86
- static async _handleImports(contractStr, importsCache) {
87
- const localImportsCache = [];
88
- let result = contractStr.replace(Common.importRegex, (match) => {
89
- localImportsCache.push(match);
90
- return '';
123
+ needToReCompile(compilerOptions, files) {
124
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptions);
125
+ const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
126
+ const usedOption = this.compilerOptionsUsed[`${key}`];
127
+ return usedOption === inputOption;
91
128
  });
92
- for (const myImport of localImportsCache) {
93
- const fileName = myImport.slice(8, -1);
94
- if (!importsCache.includes(fileName)) {
95
- importsCache.push(fileName);
96
- const importContractStr = await Common._loadContractStr(fileName, importsCache, (code) => Contract.checkCodeType(fileName, code));
97
- result = result.concat('\n', importContractStr);
129
+ if (!optionsMatched) {
130
+ return true;
131
+ }
132
+ if (files.length !== this.infos.size) {
133
+ return true;
134
+ }
135
+ for (const file of files) {
136
+ const info = this.infos.get(file.contractPath);
137
+ if (typeof info === 'undefined' || info.sourceCodeHash !== file.sourceCodeHash) {
138
+ return true;
139
+ }
140
+ }
141
+ return false;
142
+ }
143
+ static async from(rootPath) {
144
+ const filepath = rootPath + '/' + ProjectArtifact.artifactFileName;
145
+ if (!fs_1.default.existsSync(filepath)) {
146
+ return undefined;
147
+ }
148
+ const content = await fs_2.promises.readFile(filepath);
149
+ const json = JSON.parse(content.toString());
150
+ const compilerOptionsUsed = json.compilerOptionsUsed;
151
+ const files = new Map(Object.entries(json.infos));
152
+ return new ProjectArtifact(compilerOptionsUsed, files);
153
+ }
154
+ }
155
+ ProjectArtifact.artifactFileName = '.project.json';
156
+ class Project {
157
+ constructor(provider, contractsRootPath, artifactsRootPath, sourceFiles, contracts, scripts, errorOnWarnings, projectArtifact) {
158
+ this.nodeProvider = provider;
159
+ this.contractsRootPath = contractsRootPath;
160
+ this.artifactsRootPath = artifactsRootPath;
161
+ this.sourceFiles = sourceFiles;
162
+ this.contracts = contracts;
163
+ this.scripts = scripts;
164
+ this.projectArtifact = projectArtifact;
165
+ if (errorOnWarnings) {
166
+ Project.checkCompilerWarnings([...contracts.map((c) => c.warnings).flat(), ...scripts.map((s) => s.warnings).flat()], errorOnWarnings);
167
+ }
168
+ }
169
+ static buildProjectArtifact(sourceFiles, contracts, scripts, compilerOptions) {
170
+ const files = new Map();
171
+ contracts.forEach((c) => {
172
+ files.set(c.sourceFile.contractPath, {
173
+ sourceCodeHash: c.sourceFile.sourceCodeHash,
174
+ warnings: c.warnings
175
+ });
176
+ });
177
+ scripts.forEach((s) => {
178
+ files.set(s.sourceFile.contractPath, {
179
+ sourceCodeHash: s.sourceFile.sourceCodeHash,
180
+ warnings: s.warnings
181
+ });
182
+ });
183
+ const compiledSize = contracts.length + scripts.length;
184
+ sourceFiles.slice(compiledSize).forEach((c) => {
185
+ files.set(c.contractPath, {
186
+ sourceCodeHash: c.sourceCodeHash,
187
+ warnings: []
188
+ });
189
+ });
190
+ return new ProjectArtifact(compilerOptions, files);
191
+ }
192
+ getContractPath(path) {
193
+ return path.startsWith(`./${this.contractsRootPath}`)
194
+ ? path.slice(2)
195
+ : path.startsWith(this.contractsRootPath)
196
+ ? path
197
+ : this.contractsRootPath + '/' + path;
198
+ }
199
+ static checkCompilerWarnings(warnings, errorOnWarnings) {
200
+ if (warnings.length !== 0) {
201
+ const prefixPerWarning = ' - ';
202
+ const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning);
203
+ const output = `Compilation warnings:\n` + warningString + '\n';
204
+ if (errorOnWarnings) {
205
+ throw new Error(output);
206
+ }
207
+ else {
208
+ console.log(output);
98
209
  }
99
210
  }
100
- return result;
101
211
  }
102
- static async _loadContractStr(fileName, importsCache, validate) {
103
- const contractPath = this._contractPath(fileName);
104
- const contractBuffer = await fs_2.promises.readFile(contractPath);
105
- const contractStr = contractBuffer.toString();
106
- validate(contractStr);
107
- return Common._handleImports(contractStr, importsCache);
212
+ static contract(path) {
213
+ const contractPath = Project.currentProject.getContractPath(path);
214
+ const contract = Project.currentProject.contracts.find((c) => c.sourceFile.contractPath === contractPath);
215
+ if (typeof contract === 'undefined') {
216
+ throw new Error(`Contract ${contractPath} does not exist`);
217
+ }
218
+ return contract.artifact;
108
219
  }
109
- static checkFileNameExtension(fileName) {
110
- if (!fileName.endsWith('.ral')) {
111
- throw new Error('Smart contract file name should end with ".ral"');
220
+ static script(path) {
221
+ const contractPath = Project.currentProject.getContractPath(path);
222
+ const script = Project.currentProject.scripts.find((c) => c.sourceFile.contractPath === contractPath);
223
+ if (typeof script === 'undefined') {
224
+ throw new Error(`Script ${contractPath} does not exist`);
112
225
  }
226
+ return script.artifact;
113
227
  }
114
- static async _from(provider, fileName, loadContractStr, compile) {
115
- Common.checkFileNameExtension(fileName);
116
- const contractStr = await loadContractStr(fileName, []);
117
- const contractHash = cryptojs.SHA256(contractStr).toString();
118
- const existingContract = this._getArtifactFromCache(contractHash);
119
- if (typeof existingContract !== 'undefined') {
120
- return existingContract;
228
+ async saveArtifactsToFile() {
229
+ const artifactsRootPath = this.artifactsRootPath;
230
+ const saveToFile = async function (compiled) {
231
+ const artifactPath = compiled.sourceFile.getArtifactPath(artifactsRootPath);
232
+ const folder = artifactPath.slice(0, artifactPath.lastIndexOf('/'));
233
+ if (!fs_1.default.existsSync(folder)) {
234
+ fs_1.default.mkdirSync(folder, { recursive: true });
235
+ }
236
+ return fs_2.promises.writeFile(artifactPath, compiled.artifact.toString());
237
+ };
238
+ for (const contract of this.contracts) {
239
+ await saveToFile(contract);
121
240
  }
122
- else {
123
- return compile(provider, fileName, contractStr, contractHash);
241
+ for (const script of this.scripts) {
242
+ await saveToFile(script);
124
243
  }
244
+ await this.projectArtifact.saveToFile(this.artifactsRootPath);
125
245
  }
126
- _saveToFile(fileName) {
127
- const artifactPath = Common._artifactPath(fileName);
128
- return fs_2.promises.writeFile(artifactPath, this.toString());
246
+ contractByCodeHash(codeHash) {
247
+ const contract = this.contracts.find((c) => c.artifact.codeHash === codeHash);
248
+ if (typeof contract === 'undefined') {
249
+ throw new Error(`Unknown code with code hash: ${codeHash}`);
250
+ }
251
+ return contract.artifact;
129
252
  }
130
- }
131
- exports.Common = Common;
132
- Common.importRegex = new RegExp('^import "[a-z][a-z_0-9]*.ral"', 'mg');
133
- Common.contractRegex = new RegExp('^TxContract [A-Z][a-zA-Z0-9]*', 'mg');
134
- Common.interfaceRegex = new RegExp('^Interface [A-Z][a-zA-Z0-9]* \\{', 'mg');
135
- Common.scriptRegex = new RegExp('^TxScript [A-Z][a-zA-Z0-9]*', 'mg');
136
- Common._artifactCache = new Map();
137
- Common.artifactCacheCapacity = 20;
138
- class Contract extends Common {
139
- constructor(sourceCodeSha256, bytecode, codeHash, fieldsSig, eventsSig, functions) {
140
- super(sourceCodeSha256, functions);
141
- this.bytecode = bytecode;
142
- this.codeHash = codeHash;
143
- this.fieldsSig = fieldsSig;
144
- this.eventsSig = eventsSig;
253
+ static async compile(provider, files, contractsRootPath, artifactsRootPath, errorOnWarnings, compilerOptions) {
254
+ const sourceStr = files.map((f) => f.sourceCode).join('\n');
255
+ const result = await provider.contracts.postContractsCompileProject({
256
+ code: sourceStr,
257
+ compilerOptions: compilerOptions
258
+ });
259
+ const contracts = [];
260
+ const scripts = [];
261
+ result.contracts.forEach((contractResult, index) => {
262
+ const sourceFile = files[`${index}`];
263
+ const contract = Contract.fromCompileResult(contractResult);
264
+ contracts.push(new Compiled(sourceFile, contract, contractResult.warnings));
265
+ });
266
+ result.scripts.forEach((scriptResult, index) => {
267
+ const sourceFile = files[index + contracts.length];
268
+ const script = Script.fromCompileResult(scriptResult);
269
+ scripts.push(new Compiled(sourceFile, script, scriptResult.warnings));
270
+ });
271
+ const projectArtifact = Project.buildProjectArtifact(files, contracts, scripts, compilerOptions);
272
+ const project = new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts, errorOnWarnings, projectArtifact);
273
+ await project.saveArtifactsToFile();
274
+ return project;
145
275
  }
146
- static checkCodeType(fileName, contractStr) {
147
- const interfaceMatches = contractStr.match(Contract.interfaceRegex);
148
- if (interfaceMatches) {
149
- return;
150
- }
151
- const contractMatches = contractStr.match(Contract.contractRegex);
152
- if (contractMatches === null) {
153
- throw new Error(`No contract found in: ${fileName}`);
276
+ static async loadArtifacts(provider, files, projectArtifact, contractsRootPath, artifactsRootPath, errorOnWarnings, compilerOptions) {
277
+ try {
278
+ const contracts = [];
279
+ const scripts = [];
280
+ for (const file of files) {
281
+ const info = projectArtifact.infos.get(file.contractPath);
282
+ if (typeof info === 'undefined') {
283
+ throw Error(`Unable to find project info for ${file.contractPath}, please rebuild the project`);
284
+ }
285
+ const warnings = info.warnings;
286
+ const artifactPath = file.getArtifactPath(artifactsRootPath);
287
+ if (file.type === SourceType.Contract) {
288
+ const artifact = await Contract.fromArtifactFile(artifactPath);
289
+ contracts.push(new Compiled(file, artifact, warnings));
290
+ }
291
+ else if (file.type === SourceType.Script) {
292
+ const artifact = await Script.fromArtifactFile(artifactPath);
293
+ scripts.push(new Compiled(file, artifact, warnings));
294
+ }
295
+ }
296
+ return new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts, errorOnWarnings, projectArtifact);
154
297
  }
155
- else if (contractMatches.length > 1) {
156
- throw new Error(`Multiple contracts in: ${fileName}`);
298
+ catch (error) {
299
+ console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`);
300
+ return Project.compile(provider, files, contractsRootPath, artifactsRootPath, errorOnWarnings, compilerOptions);
301
+ }
302
+ }
303
+ static async loadSourceFile(dirPath, filename) {
304
+ const contractPath = dirPath + '/' + filename;
305
+ if (!filename.endsWith('.ral')) {
306
+ throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`);
307
+ }
308
+ const sourceBuffer = await fs_2.promises.readFile(contractPath);
309
+ const sourceStr = sourceBuffer.toString();
310
+ const results = this.matchers.map((m) => m.match(sourceStr));
311
+ const matchNumber = results.reduce((a, b) => a + b, 0);
312
+ if (matchNumber === 0) {
313
+ throw new Error(`No contract defined in file: ${contractPath}`);
314
+ }
315
+ if (matchNumber > 1) {
316
+ throw new Error(`Multiple definitions in file: ${contractPath}`);
317
+ }
318
+ const matcherIndex = results.indexOf(1);
319
+ const type = this.matchers[`${matcherIndex}`].type;
320
+ return SourceFile.from(type, sourceStr, contractPath);
321
+ }
322
+ static async loadSourceFiles(contractsRootPath) {
323
+ const loadDir = async function (dirPath, results) {
324
+ const dirents = await fs_2.promises.readdir(dirPath, { withFileTypes: true });
325
+ for (const dirent of dirents) {
326
+ if (dirent.isFile()) {
327
+ const file = await Project.loadSourceFile(dirPath, dirent.name);
328
+ results.push(file);
329
+ }
330
+ else {
331
+ const newPath = dirPath + '/' + dirent.name;
332
+ await loadDir(newPath, results);
333
+ }
334
+ }
335
+ };
336
+ const sourceFiles = [];
337
+ await loadDir(contractsRootPath, sourceFiles);
338
+ const contractAndScriptSize = sourceFiles.filter((f) => f.type === SourceType.Contract || f.type === SourceType.Script).length;
339
+ if (sourceFiles.length === 0 || contractAndScriptSize === 0) {
340
+ throw new Error('Project have no source files');
341
+ }
342
+ return sourceFiles.sort((a, b) => a.type - b.type);
343
+ }
344
+ static async build(compilerOptionsPartial = {}, contractsRootPath = 'contracts', artifactsRootPath = 'artifacts') {
345
+ const provider = (0, global_1.getCurrentNodeProvider)();
346
+ const sourceFiles = await Project.loadSourceFiles(contractsRootPath);
347
+ const { errorOnWarnings, ...nodeCompilerOptions } = { ...exports.DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial };
348
+ const projectArtifact = await ProjectArtifact.from(artifactsRootPath);
349
+ if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
350
+ console.log(`Compile contracts in folder "${contractsRootPath}"`);
351
+ Project.currentProject = await Project.compile(provider, sourceFiles, contractsRootPath, artifactsRootPath, errorOnWarnings, nodeCompilerOptions);
157
352
  }
158
353
  else {
159
- return;
160
- }
354
+ console.log(`Load compiled contracts from folder "${artifactsRootPath}"`);
355
+ Project.currentProject = await Project.loadArtifacts(provider, sourceFiles, projectArtifact, contractsRootPath, artifactsRootPath, errorOnWarnings, nodeCompilerOptions);
356
+ }
357
+ }
358
+ }
359
+ exports.Project = Project;
360
+ Project.abstractContractMatcher = new TypedMatcher('^Abstract Contract [A-Z][a-zA-Z0-9]*', SourceType.AbstractContract);
361
+ Project.contractMatcher = new TypedMatcher('^Contract [A-Z][a-zA-Z0-9]*', SourceType.Contract);
362
+ Project.interfaceMatcher = new TypedMatcher('^Interface [A-Z][a-zA-Z0-9]* \\{', SourceType.Interface);
363
+ Project.scriptMatcher = new TypedMatcher('^TxScript [A-Z][a-zA-Z0-9]*', SourceType.Script);
364
+ Project.matchers = [
365
+ Project.abstractContractMatcher,
366
+ Project.contractMatcher,
367
+ Project.interfaceMatcher,
368
+ Project.scriptMatcher
369
+ ];
370
+ class Artifact {
371
+ constructor(functions) {
372
+ this.functions = functions;
161
373
  }
162
- static async loadContractStr(fileName, importsCache) {
163
- return Common._loadContractStr(fileName, importsCache, (code) => Contract.checkCodeType(fileName, code));
374
+ publicFunctions() {
375
+ return this.functions.filter((func) => func.isPublic).map((func) => func.name);
164
376
  }
165
- static async fromSource(provider, fileName) {
166
- if (!fs_1.default.existsSync(Common._artifactsFolder())) {
167
- fs_1.default.mkdirSync(Common._artifactsFolder(), { recursive: true });
168
- }
169
- const contract = await Common._from(provider, fileName, (fileName, importCaches) => Contract.loadContractStr(fileName, importCaches), Contract.compile);
170
- this._putArtifactToCache(contract);
171
- return contract;
377
+ usingPreapprovedAssetsFunctions() {
378
+ return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name);
172
379
  }
173
- static async compile(provider, fileName, contractStr, contractHash) {
174
- const compiled = await provider.contracts.postContractsCompileContract({ code: contractStr });
175
- const artifact = new Contract(contractHash, compiled.bytecode, compiled.codeHash, compiled.fields, compiled.events, compiled.functions);
176
- await artifact._saveToFile(fileName);
177
- return artifact;
380
+ usingAssetsInContractFunctions() {
381
+ return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name);
382
+ }
383
+ }
384
+ exports.Artifact = Artifact;
385
+ class Contract extends Artifact {
386
+ constructor(bytecode, codeHash, fieldsSig, eventsSig, functions) {
387
+ super(functions);
388
+ this.bytecode = bytecode;
389
+ this.codeHash = codeHash;
390
+ this.fieldsSig = fieldsSig;
391
+ this.eventsSig = eventsSig;
178
392
  }
179
393
  // TODO: safely parse json
180
394
  static fromJson(artifact) {
181
- if (artifact.sourceCodeSha256 == null ||
182
- artifact.bytecode == null ||
395
+ if (artifact.bytecode == null ||
183
396
  artifact.codeHash == null ||
184
397
  artifact.fieldsSig == null ||
185
398
  artifact.eventsSig == null ||
186
399
  artifact.functions == null) {
187
400
  throw Error('The artifact JSON for contract is incomplete');
188
401
  }
189
- const contract = new Contract(artifact.sourceCodeSha256, artifact.bytecode, artifact.codeHash, artifact.fieldsSig, artifact.eventsSig, artifact.functions);
190
- this._putArtifactToCache(contract);
402
+ const contract = new Contract(artifact.bytecode, artifact.codeHash, artifact.fieldsSig, artifact.eventsSig, artifact.functions);
191
403
  return contract;
192
404
  }
405
+ static fromCompileResult(result) {
406
+ return new Contract(result.bytecode, result.codeHash, result.fields, result.events, result.functions);
407
+ }
193
408
  // support both 'code.ral' and 'code.ral.json'
194
- static async fromArtifactFile(fileName) {
195
- const artifactPath = Contract._artifactPath(fileName);
196
- const content = await fs_2.promises.readFile(artifactPath);
409
+ static async fromArtifactFile(path) {
410
+ const content = await fs_2.promises.readFile(path);
197
411
  const artifact = JSON.parse(content.toString());
198
412
  return Contract.fromJson(artifact);
199
413
  }
414
+ async fetchState(address, group) {
415
+ const state = await Project.currentProject.nodeProvider.contracts.getContractsAddressState(address, {
416
+ group: group
417
+ });
418
+ return this.fromApiContractState(state);
419
+ }
200
420
  toString() {
201
- return JSON.stringify({
202
- sourceCodeSha256: this.sourceCodeSha256,
421
+ const object = {
203
422
  bytecode: this.bytecode,
204
423
  codeHash: this.codeHash,
205
424
  fieldsSig: this.fieldsSig,
206
425
  eventsSig: this.eventsSig,
207
426
  functions: this.functions
208
- }, null, 2);
427
+ };
428
+ return JSON.stringify(object, null, 2);
209
429
  }
210
430
  toState(fields, asset, address) {
211
431
  const addressDef = typeof address !== 'undefined' ? address : Contract.randomAddress();
@@ -219,16 +439,18 @@ class Contract extends Common {
219
439
  asset: asset
220
440
  };
221
441
  }
442
+ // no need to be cryptographically strong random
222
443
  static randomAddress() {
223
- const bytes = crypto.randomBytes(33);
444
+ const bytes = new Uint8Array(33);
445
+ crypto_1.webcrypto.getRandomValues(bytes);
224
446
  bytes[0] = 3;
225
447
  return utils_1.bs58.encode(bytes);
226
448
  }
227
- async _test(provider, funcName, params, expectPublic, accessType) {
449
+ async _test(funcName, params, expectPublic, accessType) {
228
450
  const apiParams = this.toTestContract(funcName, params);
229
- const apiResult = await provider.contracts.postContractsTestContract(apiParams);
451
+ const apiResult = await Project.currentProject.nodeProvider.contracts.postContractsTestContract(apiParams);
230
452
  const methodIndex = typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName);
231
- const isPublic = this.functions[`${methodIndex}`].signature.indexOf('pub ') !== -1;
453
+ const isPublic = this.functions[`${methodIndex}`].isPublic;
232
454
  if (isPublic === expectPublic) {
233
455
  const result = await this.fromTestContractResult(methodIndex, apiResult);
234
456
  return result;
@@ -237,11 +459,11 @@ class Contract extends Common {
237
459
  throw new Error(`The test method ${funcName} is not ${accessType}`);
238
460
  }
239
461
  }
240
- async testPublicMethod(provider, funcName, params) {
241
- return this._test(provider, funcName, params, true, 'public');
462
+ async testPublicMethod(funcName, params) {
463
+ return this._test(funcName, params, true, 'public');
242
464
  }
243
- async testPrivateMethod(provider, funcName, params) {
244
- return this._test(provider, funcName, params, false, 'private');
465
+ async testPrivateMethod(funcName, params) {
466
+ return this._test(funcName, params, false, 'private');
245
467
  }
246
468
  toApiFields(fields) {
247
469
  if (typeof fields === 'undefined') {
@@ -282,30 +504,8 @@ class Contract extends Common {
282
504
  inputAssets: toApiInputAssets(params.inputAssets)
283
505
  };
284
506
  }
285
- static async fromCodeHash(codeHash) {
286
- const cached = this._getArtifactFromCache(codeHash);
287
- if (typeof cached !== 'undefined') {
288
- return cached;
289
- }
290
- const files = await fs_2.promises.readdir(Common._artifactsFolder());
291
- for (const file of files) {
292
- if (file.endsWith('.ral.json')) {
293
- try {
294
- const contract = await Contract.fromArtifactFile(file);
295
- if (contract.codeHash === codeHash) {
296
- return contract;
297
- }
298
- }
299
- catch (_) { }
300
- }
301
- }
302
- throw new Error(`Unknown code with code hash: ${codeHash}`);
303
- }
304
- static async getFieldsSig(state) {
305
- return Contract.fromCodeHash(state.codeHash).then((contract) => contract.fieldsSig);
306
- }
307
507
  async fromApiContractState(state) {
308
- const contract = await Contract.fromCodeHash(state.codeHash);
508
+ const contract = Project.currentProject.contractByCodeHash(state.codeHash);
309
509
  return {
310
510
  address: state.address,
311
511
  contractId: (0, utils_1.binToHex)((0, utils_1.contractIdFromAddress)(state.address)),
@@ -313,7 +513,7 @@ class Contract extends Common {
313
513
  initialStateHash: state.initialStateHash,
314
514
  codeHash: state.codeHash,
315
515
  fields: fromApiFields(state.fields, contract.fieldsSig),
316
- fieldsSig: await Contract.getFieldsSig(state),
516
+ fieldsSig: contract.fieldsSig,
317
517
  asset: fromApiAsset(state.asset)
318
518
  };
319
519
  }
@@ -326,7 +526,7 @@ class Contract extends Common {
326
526
  eventSig = this.ContractDestroyedEvent;
327
527
  }
328
528
  else {
329
- const contract = await Contract.fromCodeHash(codeHash);
529
+ const contract = Project.currentProject.contractByCodeHash(codeHash);
330
530
  eventSig = contract.eventsSig[event.eventIndex];
331
531
  }
332
532
  return {
@@ -387,69 +587,42 @@ class Contract extends Common {
387
587
  exports.Contract = Contract;
388
588
  Contract.ContractCreatedEvent = {
389
589
  name: 'ContractCreated',
390
- signature: 'event ContractCreated(address:Address)',
391
590
  fieldNames: ['address'],
392
591
  fieldTypes: ['Address']
393
592
  };
394
593
  Contract.ContractDestroyedEvent = {
395
594
  name: 'ContractDestroyed',
396
- signature: 'event ContractDestroyed(address:Address)',
397
595
  fieldNames: ['address'],
398
596
  fieldTypes: ['Address']
399
597
  };
400
- class Script extends Common {
401
- constructor(sourceCodeSha256, bytecodeTemplate, fieldsSig, functions) {
402
- super(sourceCodeSha256, functions);
598
+ class Script extends Artifact {
599
+ constructor(bytecodeTemplate, fieldsSig, functions) {
600
+ super(functions);
403
601
  this.bytecodeTemplate = bytecodeTemplate;
404
602
  this.fieldsSig = fieldsSig;
405
603
  }
406
- static checkCodeType(fileName, contractStr) {
407
- const scriptMatches = contractStr.match(this.scriptRegex);
408
- if (scriptMatches === null) {
409
- throw new Error(`No script found in: ${fileName}`);
410
- }
411
- else if (scriptMatches.length > 1) {
412
- throw new Error(`Multiple scripts in: ${fileName}`);
413
- }
414
- else {
415
- return;
416
- }
417
- }
418
- static async loadContractStr(fileName, importsCache) {
419
- return Common._loadContractStr(fileName, importsCache, (code) => Script.checkCodeType(fileName, code));
420
- }
421
- static async fromSource(provider, fileName) {
422
- return Common._from(provider, fileName, (fileName, importsCache) => Script.loadContractStr(fileName, importsCache), Script.compile);
423
- }
424
- static async compile(provider, fileName, scriptStr, contractHash) {
425
- const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr });
426
- const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions);
427
- await artifact._saveToFile(fileName);
428
- return artifact;
604
+ static fromCompileResult(result) {
605
+ return new Script(result.bytecodeTemplate, result.fields, result.functions);
429
606
  }
430
607
  // TODO: safely parse json
431
608
  static fromJson(artifact) {
432
- if (artifact.sourceCodeSha256 == null ||
433
- artifact.bytecodeTemplate == null ||
434
- artifact.fieldsSig == null ||
435
- artifact.functions == null) {
609
+ if (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
436
610
  throw Error('The artifact JSON for script is incomplete');
437
611
  }
438
- return new Script(artifact.sourceCodeSha256, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions);
612
+ return new Script(artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions);
439
613
  }
440
- static async fromArtifactFile(fileName) {
441
- const artifactPath = Common._artifactPath(fileName);
442
- const content = await fs_2.promises.readFile(artifactPath);
614
+ static async fromArtifactFile(path) {
615
+ const content = await fs_2.promises.readFile(path);
443
616
  const artifact = JSON.parse(content.toString());
444
617
  return this.fromJson(artifact);
445
618
  }
446
619
  toString() {
447
- return JSON.stringify({
448
- sourceCodeSha256: this.sourceCodeSha256,
620
+ const object = {
449
621
  bytecodeTemplate: this.bytecodeTemplate,
450
622
  fieldsSig: this.fieldsSig,
451
623
  functions: this.functions
452
- }, null, 2);
624
+ };
625
+ return JSON.stringify(object, null, 2);
453
626
  }
454
627
  async paramsForDeployment(params) {
455
628
  const signerParams = {
@@ -713,7 +886,7 @@ function toApiFields(fields, fieldsSig) {
713
886
  return toApiVals(fields, fieldsSig.names, fieldsSig.types);
714
887
  }
715
888
  function toApiArgs(args, funcSig) {
716
- return toApiVals(args, funcSig.argNames, funcSig.argTypes);
889
+ return toApiVals(args, funcSig.paramNames, funcSig.paramTypes);
717
890
  }
718
891
  function toApiVals(fields, names, types) {
719
892
  return names.map((name, index) => {