@alephium/web3 0.2.0-rc.1 → 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 (89) hide show
  1. package/.eslintignore +2 -2
  2. package/README.md +2 -135
  3. package/contracts/greeter/greeter.ral +2 -2
  4. package/contracts/greeter/greeter_interface.ral +1 -0
  5. package/contracts/greeter_main.ral +0 -2
  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 +44 -11
  11. package/dist/src/api/api-alephium.js +16 -0
  12. package/dist/src/api/api-explorer.d.ts +16 -16
  13. package/dist/src/api/index.d.ts +2 -2
  14. package/dist/src/api/index.js +18 -3
  15. package/dist/src/contract/contract.d.ts +101 -52
  16. package/dist/src/contract/contract.js +353 -219
  17. package/dist/src/contract/index.js +5 -1
  18. package/dist/src/global.d.ts +4 -0
  19. package/dist/src/global.js +38 -0
  20. package/dist/src/index.d.ts +2 -0
  21. package/dist/src/index.js +20 -1
  22. package/dist/src/signer/index.d.ts +0 -1
  23. package/dist/src/signer/index.js +5 -2
  24. package/dist/src/signer/signer.d.ts +1 -1
  25. package/dist/src/signer/signer.js +8 -3
  26. package/dist/src/transaction/index.d.ts +0 -1
  27. package/dist/src/transaction/index.js +5 -2
  28. package/dist/src/utils/index.d.ts +0 -1
  29. package/dist/src/utils/index.js +5 -2
  30. package/dist/src/utils/subscription.d.ts +0 -1
  31. package/dist/src/utils/subscription.js +2 -1
  32. package/dist/src/utils/utils.d.ts +5 -4
  33. package/dist/src/utils/utils.js +17 -16
  34. package/jest-config.json +11 -0
  35. package/package.json +6 -34
  36. package/scripts/create-project.ts +3 -2
  37. package/src/api/api-alephium.ts +53 -5
  38. package/src/api/index.ts +13 -3
  39. package/src/contract/contract.ts +505 -318
  40. package/src/contract/events.ts +2 -2
  41. package/src/{transaction/sign-verify.ts → global.ts} +14 -15
  42. package/src/index.ts +3 -0
  43. package/src/signer/index.ts +0 -1
  44. package/src/signer/signer.ts +3 -2
  45. package/src/transaction/index.ts +0 -1
  46. package/src/transaction/status.ts +1 -1
  47. package/src/utils/index.ts +0 -1
  48. package/src/utils/subscription.ts +3 -3
  49. package/src/utils/utils.ts +10 -10
  50. package/templates/base/package.json +2 -2
  51. package/templates/base/src/greeter.ts +8 -7
  52. package/templates/react/package.json +2 -2
  53. package/templates/react/src/App.tsx +2 -2
  54. package/.eslintrc.json +0 -21
  55. package/LICENSE +0 -165
  56. package/contracts/add/add.ral +0 -16
  57. package/contracts/main.ral +0 -6
  58. package/contracts/sub/sub.ral +0 -9
  59. package/dist/src/signer/node-wallet.d.ts +0 -13
  60. package/dist/src/signer/node-wallet.js +0 -60
  61. package/dist/src/test/index.d.ts +0 -7
  62. package/dist/src/test/index.js +0 -41
  63. package/dist/src/test/privatekey-wallet.d.ts +0 -12
  64. package/dist/src/test/privatekey-wallet.js +0 -68
  65. package/dist/src/transaction/sign-verify.d.ts +0 -2
  66. package/dist/src/transaction/sign-verify.js +0 -58
  67. package/dist/src/utils/password-crypto.d.ts +0 -2
  68. package/dist/src/utils/password-crypto.js +0 -69
  69. package/gitignore +0 -10
  70. package/src/contract/ralph.test.ts +0 -178
  71. package/src/fixtures/address.json +0 -36
  72. package/src/fixtures/balance.json +0 -9
  73. package/src/fixtures/self-clique.json +0 -19
  74. package/src/fixtures/transaction.json +0 -13
  75. package/src/fixtures/transactions.json +0 -179
  76. package/src/signer/fixtures/genesis.json +0 -26
  77. package/src/signer/fixtures/wallets.json +0 -26
  78. package/src/signer/node-wallet.ts +0 -74
  79. package/src/test/index.ts +0 -32
  80. package/src/test/privatekey-wallet.ts +0 -58
  81. package/src/transaction/sign-verify.test.ts +0 -50
  82. package/src/utils/address.test.ts +0 -47
  83. package/src/utils/djb2.test.ts +0 -35
  84. package/src/utils/password-crypto.test.ts +0 -27
  85. package/src/utils/password-crypto.ts +0 -77
  86. package/src/utils/utils.test.ts +0 -161
  87. package/test/contract.test.ts +0 -178
  88. package/test/events.test.ts +0 -138
  89. package/test/transaction.test.ts +0 -72
@@ -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,210 +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");
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;
74
+ }
75
+ match(str) {
76
+ const results = str.match(this.matcher);
77
+ return results === null ? 0 : results.length;
78
+ }
79
+ }
50
80
  class SourceFile {
51
- constructor(dirs, fileName) {
52
- this.dirs = dirs;
53
- this.dirPath = dirs.length === 0 ? '' : dirs.join('/') + '/';
54
- if (fileName.endsWith('.json')) {
55
- this.contractPath = './contracts/' + this.dirPath + fileName.slice(0, -5);
56
- this.artifactPath = './artifacts/' + this.dirPath + fileName;
57
- }
58
- else {
59
- this.contractPath = './contracts/' + this.dirPath + fileName;
60
- this.artifactPath = './artifacts/' + this.dirPath + fileName + '.json';
61
- }
81
+ constructor(type, sourceCode, sourceCodeHash, contractPath) {
82
+ this.type = type;
83
+ this.sourceCode = sourceCode;
84
+ this.sourceCodeHash = sourceCodeHash;
85
+ this.contractPath = contractPath;
86
+ }
87
+ getArtifactPath(artifactsRootPath) {
88
+ return artifactsRootPath + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json';
89
+ }
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);
62
93
  }
63
94
  }
64
- class Common {
65
- constructor(sourceCodeSha256, functions) {
66
- this.sourceCodeSha256 = sourceCodeSha256;
67
- this.functions = functions;
95
+ class Compiled {
96
+ constructor(sourceFile, artifact, warnings) {
97
+ this.sourceFile = sourceFile;
98
+ this.artifact = artifact;
99
+ this.warnings = warnings;
68
100
  }
69
- static _getArtifactFromCache(codeHash) {
70
- return this._artifactCache.get(codeHash);
101
+ }
102
+ class ProjectArtifact {
103
+ constructor(compilerOptionsUsed, infos) {
104
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed);
105
+ this.compilerOptionsUsed = compilerOptionsUsed;
106
+ this.infos = infos;
71
107
  }
72
- static _putArtifactToCache(contract) {
73
- if (!this._artifactCache.has(contract.codeHash)) {
74
- if (this._artifactCache.size >= this.artifactCacheCapacity) {
75
- const keyToDelete = this._artifactCache.keys().next().value;
76
- this._artifactCache.delete(keyToDelete);
77
- }
78
- this._artifactCache.set(contract.codeHash, contract);
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}`);
111
+ }
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}`);
79
115
  }
80
116
  }
81
- static _artifactsFolder() {
82
- 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);
83
122
  }
84
- static getSourceFile(path, _dirs) {
85
- const parts = path.split('/');
86
- const dirs = Array.from(_dirs);
87
- if (parts.length === 1) {
88
- return new SourceFile(dirs, path);
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;
128
+ });
129
+ if (!optionsMatched) {
130
+ return true;
89
131
  }
90
- parts.slice(0, parts.length - 1).forEach((part) => {
91
- switch (part) {
92
- case '.': {
93
- break;
94
- }
95
- case '..': {
96
- if (dirs.length === 0) {
97
- throw new Error('Invalid file path: ' + path);
98
- }
99
- dirs.pop();
100
- break;
101
- }
102
- default: {
103
- dirs.push(part);
104
- }
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;
105
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
+ });
106
176
  });
107
- return new SourceFile(dirs, parts[parts.length - 1]);
108
- }
109
- static async _handleImports(pathes, contractStr, importsCache) {
110
- const localImportsCache = [];
111
- let result = contractStr.replace(Common.importRegex, (match) => {
112
- localImportsCache.push(match);
113
- return '';
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
+ });
114
189
  });
115
- for (const myImport of localImportsCache) {
116
- const relativePath = myImport.slice(8, -1);
117
- const importSourceFile = this.getSourceFile(relativePath, pathes);
118
- if (!importsCache.includes(importSourceFile.contractPath)) {
119
- importsCache.push(importSourceFile.contractPath);
120
- const importContractStr = await Common._loadContractStr(importSourceFile, importsCache, (code) => Contract.checkCodeType(importSourceFile.contractPath, code));
121
- result = result.concat('\n', importContractStr);
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);
122
209
  }
123
210
  }
124
- return result;
125
211
  }
126
- static async _loadContractStr(sourceFile, importsCache, validate) {
127
- const contractPath = sourceFile.contractPath;
128
- const contractBuffer = await fs_2.promises.readFile(contractPath);
129
- const contractStr = contractBuffer.toString();
130
- validate(contractStr);
131
- return Common._handleImports(sourceFile.dirs, 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;
132
219
  }
133
- static checkFileNameExtension(fileName) {
134
- if (!fileName.endsWith('.ral')) {
135
- 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`);
136
225
  }
226
+ return script.artifact;
137
227
  }
138
- static async _from(provider, sourceFile, loadContractStr, compile) {
139
- Common.checkFileNameExtension(sourceFile.contractPath);
140
- const contractStr = await loadContractStr(sourceFile, []);
141
- const contractHash = cryptojs.SHA256(contractStr).toString();
142
- const existingContract = this._getArtifactFromCache(contractHash);
143
- if (typeof existingContract !== 'undefined') {
144
- 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);
145
240
  }
146
- else {
147
- return compile(provider, sourceFile, contractStr, contractHash);
241
+ for (const script of this.scripts) {
242
+ await saveToFile(script);
148
243
  }
244
+ await this.projectArtifact.saveToFile(this.artifactsRootPath);
149
245
  }
150
- _saveToFile(sourceFile) {
151
- const folder = Common._artifactsFolder() + sourceFile.dirPath;
152
- if (!fs_1.default.existsSync(folder)) {
153
- fs_1.default.mkdirSync(folder, { recursive: true });
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}`);
154
250
  }
155
- return fs_2.promises.writeFile(sourceFile.artifactPath, this.toString());
251
+ return contract.artifact;
156
252
  }
157
- }
158
- exports.Common = Common;
159
- Common.importRegex = new RegExp('^import "([^"/]+/(([^"]+)/)?)?[a-z][a-z_0-9]*.ral"', 'mg');
160
- Common.contractRegex = new RegExp('^(Abstract[ ]+)?Contract [A-Z][a-zA-Z0-9]*', 'mg');
161
- Common.interfaceRegex = new RegExp('^Interface [A-Z][a-zA-Z0-9]* \\{', 'mg');
162
- Common.scriptRegex = new RegExp('^TxScript [A-Z][a-zA-Z0-9]*', 'mg');
163
- Common._artifactCache = new Map();
164
- Common.artifactCacheCapacity = 20;
165
- class Contract extends Common {
166
- constructor(sourceCodeSha256, bytecode, codeHash, fieldsSig, eventsSig, functions) {
167
- super(sourceCodeSha256, functions);
168
- this.bytecode = bytecode;
169
- this.codeHash = codeHash;
170
- this.fieldsSig = fieldsSig;
171
- 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;
172
275
  }
173
- static checkCodeType(fileName, contractStr) {
174
- const interfaceMatches = contractStr.match(Contract.interfaceRegex);
175
- const contractMatches = contractStr.match(Contract.contractRegex);
176
- if (interfaceMatches === null && contractMatches === null) {
177
- throw new Error(`No contract found in: ${fileName}`);
178
- }
179
- if (interfaceMatches && contractMatches) {
180
- throw new Error(`Multiple contracts and interfaces in: ${fileName}`);
181
- }
182
- if (interfaceMatches === null) {
183
- if (contractMatches !== null && contractMatches.length > 1) {
184
- throw new Error(`Multiple contracts 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
+ }
185
295
  }
296
+ return new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts, errorOnWarnings, projectArtifact);
186
297
  }
187
- if (contractMatches === null) {
188
- if (interfaceMatches !== null && interfaceMatches.length > 1) {
189
- throw new Error(`Multiple interfaces 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
+ }
190
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);
191
352
  }
353
+ else {
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;
192
373
  }
193
- static async loadContractStr(sourceFile) {
194
- return Common._loadContractStr(sourceFile, [], (code) => Contract.checkCodeType(sourceFile.contractPath, code));
374
+ publicFunctions() {
375
+ return this.functions.filter((func) => func.isPublic).map((func) => func.name);
195
376
  }
196
- static async fromSource(provider, path) {
197
- if (!fs_1.default.existsSync(Common._artifactsFolder())) {
198
- fs_1.default.mkdirSync(Common._artifactsFolder(), { recursive: true });
199
- }
200
- const sourceFile = this.getSourceFile(path, []);
201
- const contract = await Common._from(provider, sourceFile, (sourceFile) => Contract.loadContractStr(sourceFile), Contract.compile);
202
- this._putArtifactToCache(contract);
203
- return contract;
377
+ usingPreapprovedAssetsFunctions() {
378
+ return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name);
204
379
  }
205
- static async compile(provider, sourceFile, contractStr, contractHash) {
206
- const compiled = await provider.contracts.postContractsCompileContract({ code: contractStr });
207
- const artifact = new Contract(contractHash, compiled.bytecode, compiled.codeHash, compiled.fields, compiled.events, compiled.functions);
208
- await artifact._saveToFile(sourceFile);
209
- 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;
210
392
  }
211
393
  // TODO: safely parse json
212
394
  static fromJson(artifact) {
213
- if (artifact.sourceCodeSha256 == null ||
214
- artifact.bytecode == null ||
395
+ if (artifact.bytecode == null ||
215
396
  artifact.codeHash == null ||
216
397
  artifact.fieldsSig == null ||
217
398
  artifact.eventsSig == null ||
218
399
  artifact.functions == null) {
219
400
  throw Error('The artifact JSON for contract is incomplete');
220
401
  }
221
- const contract = new Contract(artifact.sourceCodeSha256, artifact.bytecode, artifact.codeHash, artifact.fieldsSig, artifact.eventsSig, artifact.functions);
222
- this._putArtifactToCache(contract);
402
+ const contract = new Contract(artifact.bytecode, artifact.codeHash, artifact.fieldsSig, artifact.eventsSig, artifact.functions);
223
403
  return contract;
224
404
  }
405
+ static fromCompileResult(result) {
406
+ return new Contract(result.bytecode, result.codeHash, result.fields, result.events, result.functions);
407
+ }
225
408
  // support both 'code.ral' and 'code.ral.json'
226
409
  static async fromArtifactFile(path) {
227
- const sourceFile = this.getSourceFile(path, []);
228
- const artifactPath = sourceFile.artifactPath;
229
- const content = await fs_2.promises.readFile(artifactPath);
410
+ const content = await fs_2.promises.readFile(path);
230
411
  const artifact = JSON.parse(content.toString());
231
412
  return Contract.fromJson(artifact);
232
413
  }
233
- async fetchState(provider, address, group) {
234
- const state = await provider.contracts.getContractsAddressState(address, { group: group });
414
+ async fetchState(address, group) {
415
+ const state = await Project.currentProject.nodeProvider.contracts.getContractsAddressState(address, {
416
+ group: group
417
+ });
235
418
  return this.fromApiContractState(state);
236
419
  }
237
420
  toString() {
238
- return JSON.stringify({
239
- sourceCodeSha256: this.sourceCodeSha256,
421
+ const object = {
240
422
  bytecode: this.bytecode,
241
423
  codeHash: this.codeHash,
242
424
  fieldsSig: this.fieldsSig,
243
425
  eventsSig: this.eventsSig,
244
426
  functions: this.functions
245
- }, null, 2);
427
+ };
428
+ return JSON.stringify(object, null, 2);
246
429
  }
247
430
  toState(fields, asset, address) {
248
431
  const addressDef = typeof address !== 'undefined' ? address : Contract.randomAddress();
@@ -256,16 +439,18 @@ class Contract extends Common {
256
439
  asset: asset
257
440
  };
258
441
  }
442
+ // no need to be cryptographically strong random
259
443
  static randomAddress() {
260
- const bytes = crypto.randomBytes(33);
444
+ const bytes = new Uint8Array(33);
445
+ crypto_1.webcrypto.getRandomValues(bytes);
261
446
  bytes[0] = 3;
262
447
  return utils_1.bs58.encode(bytes);
263
448
  }
264
- async _test(provider, funcName, params, expectPublic, accessType) {
449
+ async _test(funcName, params, expectPublic, accessType) {
265
450
  const apiParams = this.toTestContract(funcName, params);
266
- const apiResult = await provider.contracts.postContractsTestContract(apiParams);
451
+ const apiResult = await Project.currentProject.nodeProvider.contracts.postContractsTestContract(apiParams);
267
452
  const methodIndex = typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName);
268
- const isPublic = this.functions[`${methodIndex}`].signature.indexOf('pub ') !== -1;
453
+ const isPublic = this.functions[`${methodIndex}`].isPublic;
269
454
  if (isPublic === expectPublic) {
270
455
  const result = await this.fromTestContractResult(methodIndex, apiResult);
271
456
  return result;
@@ -274,11 +459,11 @@ class Contract extends Common {
274
459
  throw new Error(`The test method ${funcName} is not ${accessType}`);
275
460
  }
276
461
  }
277
- async testPublicMethod(provider, funcName, params) {
278
- return this._test(provider, funcName, params, true, 'public');
462
+ async testPublicMethod(funcName, params) {
463
+ return this._test(funcName, params, true, 'public');
279
464
  }
280
- async testPrivateMethod(provider, funcName, params) {
281
- return this._test(provider, funcName, params, false, 'private');
465
+ async testPrivateMethod(funcName, params) {
466
+ return this._test(funcName, params, false, 'private');
282
467
  }
283
468
  toApiFields(fields) {
284
469
  if (typeof fields === 'undefined') {
@@ -319,30 +504,8 @@ class Contract extends Common {
319
504
  inputAssets: toApiInputAssets(params.inputAssets)
320
505
  };
321
506
  }
322
- static async fromCodeHash(codeHash) {
323
- const cached = this._getArtifactFromCache(codeHash);
324
- if (typeof cached !== 'undefined') {
325
- return cached;
326
- }
327
- const files = await fs_2.promises.readdir(Common._artifactsFolder());
328
- for (const file of files) {
329
- if (file.endsWith('.ral.json')) {
330
- try {
331
- const contract = await Contract.fromArtifactFile(file);
332
- if (contract.codeHash === codeHash) {
333
- return contract;
334
- }
335
- }
336
- catch (_) { }
337
- }
338
- }
339
- throw new Error(`Unknown code with code hash: ${codeHash}`);
340
- }
341
- static async getFieldsSig(state) {
342
- return Contract.fromCodeHash(state.codeHash).then((contract) => contract.fieldsSig);
343
- }
344
507
  async fromApiContractState(state) {
345
- const contract = await Contract.fromCodeHash(state.codeHash);
508
+ const contract = Project.currentProject.contractByCodeHash(state.codeHash);
346
509
  return {
347
510
  address: state.address,
348
511
  contractId: (0, utils_1.binToHex)((0, utils_1.contractIdFromAddress)(state.address)),
@@ -350,7 +513,7 @@ class Contract extends Common {
350
513
  initialStateHash: state.initialStateHash,
351
514
  codeHash: state.codeHash,
352
515
  fields: fromApiFields(state.fields, contract.fieldsSig),
353
- fieldsSig: await Contract.getFieldsSig(state),
516
+ fieldsSig: contract.fieldsSig,
354
517
  asset: fromApiAsset(state.asset)
355
518
  };
356
519
  }
@@ -363,7 +526,7 @@ class Contract extends Common {
363
526
  eventSig = this.ContractDestroyedEvent;
364
527
  }
365
528
  else {
366
- const contract = await Contract.fromCodeHash(codeHash);
529
+ const contract = Project.currentProject.contractByCodeHash(codeHash);
367
530
  eventSig = contract.eventsSig[event.eventIndex];
368
531
  }
369
532
  return {
@@ -424,71 +587,42 @@ class Contract extends Common {
424
587
  exports.Contract = Contract;
425
588
  Contract.ContractCreatedEvent = {
426
589
  name: 'ContractCreated',
427
- signature: 'event ContractCreated(address:Address)',
428
590
  fieldNames: ['address'],
429
591
  fieldTypes: ['Address']
430
592
  };
431
593
  Contract.ContractDestroyedEvent = {
432
594
  name: 'ContractDestroyed',
433
- signature: 'event ContractDestroyed(address:Address)',
434
595
  fieldNames: ['address'],
435
596
  fieldTypes: ['Address']
436
597
  };
437
- class Script extends Common {
438
- constructor(sourceCodeSha256, bytecodeTemplate, fieldsSig, functions) {
439
- super(sourceCodeSha256, functions);
598
+ class Script extends Artifact {
599
+ constructor(bytecodeTemplate, fieldsSig, functions) {
600
+ super(functions);
440
601
  this.bytecodeTemplate = bytecodeTemplate;
441
602
  this.fieldsSig = fieldsSig;
442
603
  }
443
- static checkCodeType(fileName, contractStr) {
444
- const scriptMatches = contractStr.match(this.scriptRegex);
445
- if (scriptMatches === null) {
446
- throw new Error(`No script found in: ${fileName}`);
447
- }
448
- else if (scriptMatches.length > 1) {
449
- throw new Error(`Multiple scripts in: ${fileName}`);
450
- }
451
- else {
452
- return;
453
- }
454
- }
455
- static async loadContractStr(sourceFile) {
456
- return Common._loadContractStr(sourceFile, [], (code) => Script.checkCodeType(sourceFile.contractPath, code));
457
- }
458
- static async fromSource(provider, path) {
459
- const sourceFile = this.getSourceFile(path, []);
460
- return Common._from(provider, sourceFile, (sourceFile) => Script.loadContractStr(sourceFile), Script.compile);
461
- }
462
- static async compile(provider, sourceFile, scriptStr, contractHash) {
463
- const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr });
464
- const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions);
465
- await artifact._saveToFile(sourceFile);
466
- return artifact;
604
+ static fromCompileResult(result) {
605
+ return new Script(result.bytecodeTemplate, result.fields, result.functions);
467
606
  }
468
607
  // TODO: safely parse json
469
608
  static fromJson(artifact) {
470
- if (artifact.sourceCodeSha256 == null ||
471
- artifact.bytecodeTemplate == null ||
472
- artifact.fieldsSig == null ||
473
- artifact.functions == null) {
609
+ if (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
474
610
  throw Error('The artifact JSON for script is incomplete');
475
611
  }
476
- return new Script(artifact.sourceCodeSha256, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions);
612
+ return new Script(artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions);
477
613
  }
478
614
  static async fromArtifactFile(path) {
479
- const sourceFile = this.getSourceFile(path, []);
480
- const artifactPath = sourceFile.artifactPath;
481
- const content = await fs_2.promises.readFile(artifactPath);
615
+ const content = await fs_2.promises.readFile(path);
482
616
  const artifact = JSON.parse(content.toString());
483
617
  return this.fromJson(artifact);
484
618
  }
485
619
  toString() {
486
- return JSON.stringify({
487
- sourceCodeSha256: this.sourceCodeSha256,
620
+ const object = {
488
621
  bytecodeTemplate: this.bytecodeTemplate,
489
622
  fieldsSig: this.fieldsSig,
490
623
  functions: this.functions
491
- }, null, 2);
624
+ };
625
+ return JSON.stringify(object, null, 2);
492
626
  }
493
627
  async paramsForDeployment(params) {
494
628
  const signerParams = {
@@ -752,7 +886,7 @@ function toApiFields(fields, fieldsSig) {
752
886
  return toApiVals(fields, fieldsSig.names, fieldsSig.types);
753
887
  }
754
888
  function toApiArgs(args, funcSig) {
755
- return toApiVals(args, funcSig.argNames, funcSig.argTypes);
889
+ return toApiVals(args, funcSig.paramNames, funcSig.paramTypes);
756
890
  }
757
891
  function toApiVals(fields, names, types) {
758
892
  return names.map((name, index) => {