@alephium/web3 0.2.0-rc.3 → 0.2.0-rc.6
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/contracts/add/add.ral +5 -8
- package/contracts/greeter/greeter.ral +2 -2
- package/contracts/greeter/greeter_interface.ral +1 -0
- package/contracts/greeter_main.ral +0 -2
- package/contracts/main.ral +0 -2
- package/contracts/sub/sub.ral +1 -0
- package/contracts/test/metadata.ral +1 -0
- package/contracts/test/warnings.ral +3 -0
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/api-alephium.d.ts +16 -0
- package/dist/src/api/api-alephium.js +16 -0
- package/dist/src/contract/contract.d.ts +70 -47
- package/dist/src/contract/contract.js +302 -231
- package/gitignore +0 -1
- package/package.json +2 -4
- package/src/api/api-alephium.ts +30 -0
- package/src/contract/contract.ts +385 -327
- package/templates/base/package.json +2 -2
- package/templates/react/package.json +2 -2
- package/test/contract.test.ts +38 -22
- package/test/events.test.ts +14 -9
- package/test/transaction.test.ts +5 -3
|
@@ -39,7 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.toApiVal = exports.extractArray = exports.Script = exports.Contract = exports.
|
|
42
|
+
exports.toApiVal = exports.extractArray = exports.Script = exports.Contract = exports.Artifact = exports.Project = exports.DEFAULT_COMPILER_OPTIONS = void 0;
|
|
43
43
|
const buffer_1 = require("buffer/");
|
|
44
44
|
const cryptojs = __importStar(require("crypto-js"));
|
|
45
45
|
const crypto = __importStar(require("crypto"));
|
|
@@ -47,112 +47,290 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
47
47
|
const fs_2 = require("fs");
|
|
48
48
|
const ralph = __importStar(require("./ralph"));
|
|
49
49
|
const utils_1 = require("../utils");
|
|
50
|
+
var SourceType;
|
|
51
|
+
(function (SourceType) {
|
|
52
|
+
SourceType[SourceType["Contract"] = 0] = "Contract";
|
|
53
|
+
SourceType[SourceType["Script"] = 1] = "Script";
|
|
54
|
+
SourceType[SourceType["AbstractContract"] = 2] = "AbstractContract";
|
|
55
|
+
SourceType[SourceType["Interface"] = 3] = "Interface";
|
|
56
|
+
})(SourceType || (SourceType = {}));
|
|
57
|
+
exports.DEFAULT_COMPILER_OPTIONS = {
|
|
58
|
+
errorOnWarnings: true,
|
|
59
|
+
ignoreUnusedConstantsWarnings: true
|
|
60
|
+
};
|
|
61
|
+
class TypedMatcher {
|
|
62
|
+
constructor(pattern, type) {
|
|
63
|
+
this.matcher = new RegExp(pattern, 'mg');
|
|
64
|
+
this.type = type;
|
|
65
|
+
}
|
|
66
|
+
match(str) {
|
|
67
|
+
const results = str.match(this.matcher);
|
|
68
|
+
return results === null ? 0 : results.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
50
71
|
class SourceFile {
|
|
51
|
-
constructor(
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.contractPath = './contracts/' + this.dirPath + fileName;
|
|
60
|
-
this.artifactPath = './artifacts/' + this.dirPath + fileName + '.json';
|
|
61
|
-
}
|
|
72
|
+
constructor(type, sourceCode, contractPath) {
|
|
73
|
+
this.type = type;
|
|
74
|
+
this.sourceCode = sourceCode;
|
|
75
|
+
this.sourceCodeHash = cryptojs.SHA256(sourceCode).toString();
|
|
76
|
+
this.contractPath = contractPath;
|
|
77
|
+
}
|
|
78
|
+
getArtifactPath(artifactsRootPath) {
|
|
79
|
+
return artifactsRootPath + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json';
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
|
-
class
|
|
65
|
-
constructor(
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
82
|
+
class Compiled {
|
|
83
|
+
constructor(sourceFile, artifact, warnings) {
|
|
84
|
+
this.sourceFile = sourceFile;
|
|
85
|
+
this.artifact = artifact;
|
|
86
|
+
this.warnings = warnings;
|
|
68
87
|
}
|
|
69
|
-
|
|
70
|
-
|
|
88
|
+
}
|
|
89
|
+
class ProjectArtifact {
|
|
90
|
+
constructor(infos) {
|
|
91
|
+
this.infos = infos;
|
|
92
|
+
}
|
|
93
|
+
async saveToFile(rootPath) {
|
|
94
|
+
const filepath = rootPath + '/' + ProjectArtifact.artifactFileName;
|
|
95
|
+
const content = JSON.stringify(Object.fromEntries(this.infos), null, 2);
|
|
96
|
+
return fs_2.promises.writeFile(filepath, content);
|
|
71
97
|
}
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
98
|
+
sourceHasChanged(files) {
|
|
99
|
+
if (files.length !== this.infos.size) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const info = this.infos.get(file.contractPath);
|
|
104
|
+
if (typeof info === 'undefined' || info.sourceCodeHash !== file.sourceCodeHash) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
static async from(rootPath) {
|
|
111
|
+
const filepath = rootPath + '/' + ProjectArtifact.artifactFileName;
|
|
112
|
+
if (!fs_1.default.existsSync(filepath)) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const content = await fs_2.promises.readFile(filepath);
|
|
116
|
+
const files = new Map(Object.entries(JSON.parse(content.toString())));
|
|
117
|
+
return new ProjectArtifact(files);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
ProjectArtifact.artifactFileName = '.project.json';
|
|
121
|
+
class Project {
|
|
122
|
+
constructor(provider, contractsRootPath, artifactsRootPath, sourceFiles, contracts, scripts) {
|
|
123
|
+
this.nodeProvider = provider;
|
|
124
|
+
this.contractsRootPath = contractsRootPath;
|
|
125
|
+
this.artifactsRootPath = artifactsRootPath;
|
|
126
|
+
this.sourceFiles = sourceFiles;
|
|
127
|
+
this.contracts = contracts;
|
|
128
|
+
this.scripts = scripts;
|
|
129
|
+
}
|
|
130
|
+
getContractPath(path) {
|
|
131
|
+
return path.startsWith(`./${this.contractsRootPath}`)
|
|
132
|
+
? path.slice(2)
|
|
133
|
+
: path.startsWith(this.contractsRootPath)
|
|
134
|
+
? path
|
|
135
|
+
: this.contractsRootPath + '/' + path;
|
|
136
|
+
}
|
|
137
|
+
static checkCompilerWarnings(warnings, compilerOptions) {
|
|
138
|
+
const remains = compilerOptions.ignoreUnusedConstantsWarnings
|
|
139
|
+
? warnings.filter((s) => !s.includes('unused constants'))
|
|
140
|
+
: warnings;
|
|
141
|
+
if (remains.length !== 0) {
|
|
142
|
+
const prefixPerWarning = ' - ';
|
|
143
|
+
const warningString = prefixPerWarning + remains.join('\n' + prefixPerWarning);
|
|
144
|
+
const output = 'Compilation warnings:\n' + warningString + '\n';
|
|
145
|
+
if (compilerOptions.errorOnWarnings) {
|
|
146
|
+
throw new Error(output);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.log(output);
|
|
77
150
|
}
|
|
78
|
-
this._artifactCache.set(contract.codeHash, contract);
|
|
79
151
|
}
|
|
80
152
|
}
|
|
81
|
-
static
|
|
82
|
-
|
|
153
|
+
static contract(path, compilerOptions) {
|
|
154
|
+
const contractPath = Project.currentProject.getContractPath(path);
|
|
155
|
+
const contract = Project.currentProject.contracts.find((c) => c.sourceFile.contractPath === contractPath);
|
|
156
|
+
if (typeof contract === 'undefined') {
|
|
157
|
+
throw new Error(`Contract ${contractPath} does not exist`);
|
|
158
|
+
}
|
|
159
|
+
Project.checkCompilerWarnings(contract.warnings, { ...exports.DEFAULT_COMPILER_OPTIONS, ...compilerOptions });
|
|
160
|
+
return contract.artifact;
|
|
83
161
|
}
|
|
84
|
-
static
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
|
|
162
|
+
static script(path, compilerOptions) {
|
|
163
|
+
const contractPath = Project.currentProject.getContractPath(path);
|
|
164
|
+
const script = Project.currentProject.scripts.find((c) => c.sourceFile.contractPath === contractPath);
|
|
165
|
+
if (typeof script === 'undefined') {
|
|
166
|
+
throw new Error(`Script ${contractPath} does not exist`);
|
|
89
167
|
}
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
}
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
return new SourceFile(dirs, parts[parts.length - 1]);
|
|
168
|
+
Project.checkCompilerWarnings(script.warnings, { ...exports.DEFAULT_COMPILER_OPTIONS, ...compilerOptions });
|
|
169
|
+
return script.artifact;
|
|
108
170
|
}
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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);
|
|
171
|
+
async saveArtifactsToFile() {
|
|
172
|
+
const artifactsRootPath = this.artifactsRootPath;
|
|
173
|
+
const saveToFile = async function (compiled) {
|
|
174
|
+
const artifactPath = compiled.sourceFile.getArtifactPath(artifactsRootPath);
|
|
175
|
+
const folder = artifactPath.slice(0, artifactPath.lastIndexOf('/'));
|
|
176
|
+
if (!fs_1.default.existsSync(folder)) {
|
|
177
|
+
fs_1.default.mkdirSync(folder, { recursive: true });
|
|
122
178
|
}
|
|
179
|
+
return fs_2.promises.writeFile(artifactPath, compiled.artifact.toString());
|
|
180
|
+
};
|
|
181
|
+
for (const contract of this.contracts) {
|
|
182
|
+
await saveToFile(contract);
|
|
183
|
+
}
|
|
184
|
+
for (const script of this.scripts) {
|
|
185
|
+
await saveToFile(script);
|
|
123
186
|
}
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
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);
|
|
132
187
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
188
|
+
contractByCodeHash(codeHash) {
|
|
189
|
+
const contract = this.contracts.find((c) => c.artifact.codeHash === codeHash);
|
|
190
|
+
if (typeof contract === 'undefined') {
|
|
191
|
+
throw new Error(`Unknown code with code hash: ${codeHash}`);
|
|
136
192
|
}
|
|
193
|
+
return contract.artifact;
|
|
194
|
+
}
|
|
195
|
+
async saveProjectArtifactToFile() {
|
|
196
|
+
const files = new Map();
|
|
197
|
+
this.contracts.forEach((c) => {
|
|
198
|
+
files.set(c.sourceFile.contractPath, {
|
|
199
|
+
sourceCodeHash: c.sourceFile.sourceCodeHash,
|
|
200
|
+
warnings: c.warnings
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
this.scripts.forEach((s) => {
|
|
204
|
+
files.set(s.sourceFile.contractPath, {
|
|
205
|
+
sourceCodeHash: s.sourceFile.sourceCodeHash,
|
|
206
|
+
warnings: s.warnings
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
const compiledSize = this.contracts.length + this.scripts.length;
|
|
210
|
+
this.sourceFiles.slice(compiledSize).forEach((c) => {
|
|
211
|
+
files.set(c.contractPath, {
|
|
212
|
+
sourceCodeHash: c.sourceCodeHash,
|
|
213
|
+
warnings: []
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
const projectArtifact = new ProjectArtifact(files);
|
|
217
|
+
await projectArtifact.saveToFile(this.artifactsRootPath);
|
|
137
218
|
}
|
|
138
|
-
static async
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
219
|
+
static async compile(provider, files, contractsRootPath, artifactsRootPath) {
|
|
220
|
+
const sourceStr = files.map((f) => f.sourceCode).join('\n');
|
|
221
|
+
const result = await provider.contracts.postContractsCompileProject({
|
|
222
|
+
code: sourceStr
|
|
223
|
+
});
|
|
224
|
+
const contracts = [];
|
|
225
|
+
const scripts = [];
|
|
226
|
+
result.contracts.forEach((contractResult, index) => {
|
|
227
|
+
const sourceFile = files[`${index}`];
|
|
228
|
+
const contract = Contract.fromCompileResult(contractResult);
|
|
229
|
+
contracts.push(new Compiled(sourceFile, contract, contractResult.warnings));
|
|
230
|
+
});
|
|
231
|
+
result.scripts.forEach((scriptResult, index) => {
|
|
232
|
+
const sourceFile = files[index + contracts.length];
|
|
233
|
+
const script = Script.fromCompileResult(scriptResult);
|
|
234
|
+
scripts.push(new Compiled(sourceFile, script, scriptResult.warnings));
|
|
235
|
+
});
|
|
236
|
+
const project = new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts);
|
|
237
|
+
await project.saveArtifactsToFile();
|
|
238
|
+
await project.saveProjectArtifactToFile();
|
|
239
|
+
return project;
|
|
240
|
+
}
|
|
241
|
+
static async loadArtifacts(provider, files, projectArtifact, contractsRootPath, artifactsRootPath) {
|
|
242
|
+
try {
|
|
243
|
+
const contracts = [];
|
|
244
|
+
const scripts = [];
|
|
245
|
+
for (const file of files) {
|
|
246
|
+
const info = projectArtifact.infos.get(file.contractPath);
|
|
247
|
+
if (typeof info === 'undefined') {
|
|
248
|
+
throw Error(`Unable to find project info for ${file.contractPath}, please rebuild the project`);
|
|
249
|
+
}
|
|
250
|
+
const warnings = info.warnings;
|
|
251
|
+
const artifactPath = file.getArtifactPath(artifactsRootPath);
|
|
252
|
+
if (file.type === SourceType.Contract) {
|
|
253
|
+
const artifact = await Contract.fromArtifactFile(artifactPath);
|
|
254
|
+
contracts.push(new Compiled(file, artifact, warnings));
|
|
255
|
+
}
|
|
256
|
+
else if (file.type === SourceType.Script) {
|
|
257
|
+
const artifact = await Script.fromArtifactFile(artifactPath);
|
|
258
|
+
scripts.push(new Compiled(file, artifact, warnings));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts);
|
|
145
262
|
}
|
|
146
|
-
|
|
147
|
-
|
|
263
|
+
catch (error) {
|
|
264
|
+
console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`);
|
|
265
|
+
return Project.compile(provider, files, contractsRootPath, artifactsRootPath);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
static async loadSourceFile(dirPath, filename) {
|
|
269
|
+
const contractPath = dirPath + '/' + filename;
|
|
270
|
+
if (!filename.endsWith('.ral')) {
|
|
271
|
+
throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`);
|
|
272
|
+
}
|
|
273
|
+
const sourceBuffer = await fs_2.promises.readFile(contractPath);
|
|
274
|
+
const sourceStr = sourceBuffer.toString();
|
|
275
|
+
const results = this.matchers.map((m) => m.match(sourceStr));
|
|
276
|
+
const matchNumber = results.reduce((a, b) => a + b, 0);
|
|
277
|
+
if (matchNumber === 0) {
|
|
278
|
+
throw new Error(`No contract defined in file: ${contractPath}`);
|
|
279
|
+
}
|
|
280
|
+
if (matchNumber > 1) {
|
|
281
|
+
throw new Error(`Multiple definitions in file: ${contractPath}`);
|
|
282
|
+
}
|
|
283
|
+
const matcherIndex = results.indexOf(1);
|
|
284
|
+
const type = this.matchers[`${matcherIndex}`].type;
|
|
285
|
+
return new SourceFile(type, sourceStr, contractPath);
|
|
286
|
+
}
|
|
287
|
+
static async loadSourceFiles(contractsRootPath) {
|
|
288
|
+
const loadDir = async function (dirPath, results) {
|
|
289
|
+
const dirents = await fs_2.promises.readdir(dirPath, { withFileTypes: true });
|
|
290
|
+
for (const dirent of dirents) {
|
|
291
|
+
if (dirent.isFile()) {
|
|
292
|
+
const file = await Project.loadSourceFile(dirPath, dirent.name);
|
|
293
|
+
results.push(file);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const newPath = dirPath + '/' + dirent.name;
|
|
297
|
+
await loadDir(newPath, results);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const sourceFiles = [];
|
|
302
|
+
await loadDir(contractsRootPath, sourceFiles);
|
|
303
|
+
const contractAndScriptSize = sourceFiles.filter((f) => f.type === SourceType.Contract || f.type === SourceType.Script).length;
|
|
304
|
+
if (sourceFiles.length === 0 || contractAndScriptSize === 0) {
|
|
305
|
+
throw new Error('Project have no source files');
|
|
148
306
|
}
|
|
307
|
+
return sourceFiles.sort((a, b) => a.type - b.type);
|
|
149
308
|
}
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
309
|
+
static async build(provider, contractsRootPath = 'contracts', artifactsRootPath = 'artifacts') {
|
|
310
|
+
const sourceFiles = await Project.loadSourceFiles(contractsRootPath);
|
|
311
|
+
const projectArtifact = await ProjectArtifact.from(artifactsRootPath);
|
|
312
|
+
if (typeof projectArtifact === 'undefined' || projectArtifact.sourceHasChanged(sourceFiles)) {
|
|
313
|
+
Project.currentProject = await Project.compile(provider, sourceFiles, contractsRootPath, artifactsRootPath);
|
|
154
314
|
}
|
|
155
|
-
|
|
315
|
+
else {
|
|
316
|
+
Project.currentProject = await Project.loadArtifacts(provider, sourceFiles, projectArtifact, contractsRootPath, artifactsRootPath);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
exports.Project = Project;
|
|
321
|
+
Project.abstractContractMatcher = new TypedMatcher('^Abstract Contract [A-Z][a-zA-Z0-9]*', SourceType.AbstractContract);
|
|
322
|
+
Project.contractMatcher = new TypedMatcher('^Contract [A-Z][a-zA-Z0-9]*', SourceType.Contract);
|
|
323
|
+
Project.interfaceMatcher = new TypedMatcher('^Interface [A-Z][a-zA-Z0-9]* \\{', SourceType.Interface);
|
|
324
|
+
Project.scriptMatcher = new TypedMatcher('^TxScript [A-Z][a-zA-Z0-9]*', SourceType.Script);
|
|
325
|
+
Project.matchers = [
|
|
326
|
+
Project.abstractContractMatcher,
|
|
327
|
+
Project.contractMatcher,
|
|
328
|
+
Project.interfaceMatcher,
|
|
329
|
+
Project.scriptMatcher
|
|
330
|
+
];
|
|
331
|
+
class Artifact {
|
|
332
|
+
constructor(functions) {
|
|
333
|
+
this.functions = functions;
|
|
156
334
|
}
|
|
157
335
|
publicFunctions() {
|
|
158
336
|
return this.functions.filter((func) => func.isPublic).map((func) => func.name);
|
|
@@ -163,109 +341,52 @@ class Common {
|
|
|
163
341
|
usingAssetsInContractFunctions() {
|
|
164
342
|
return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name);
|
|
165
343
|
}
|
|
166
|
-
static checkCompilerWarnings(compiled, errorOnWarnings) {
|
|
167
|
-
if (compiled.warnings.length !== 0) {
|
|
168
|
-
const prefixPerWarning = ' - ';
|
|
169
|
-
const warningString = prefixPerWarning + compiled.warnings.join('\n' + prefixPerWarning);
|
|
170
|
-
const output = 'Compilation warnings:\n' + warningString + '\n';
|
|
171
|
-
if (errorOnWarnings) {
|
|
172
|
-
throw new Error(output);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
console.log(output);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
344
|
}
|
|
180
|
-
exports.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Common.scriptRegex = new RegExp('^TxScript [A-Z][a-zA-Z0-9]*', 'mg');
|
|
185
|
-
Common._artifactCache = new Map();
|
|
186
|
-
Common.artifactCacheCapacity = 20;
|
|
187
|
-
class Contract extends Common {
|
|
188
|
-
constructor(sourceCodeSha256, bytecode, codeHash, fieldsSig, eventsSig, functions) {
|
|
189
|
-
super(sourceCodeSha256, functions);
|
|
345
|
+
exports.Artifact = Artifact;
|
|
346
|
+
class Contract extends Artifact {
|
|
347
|
+
constructor(bytecode, codeHash, fieldsSig, eventsSig, functions) {
|
|
348
|
+
super(functions);
|
|
190
349
|
this.bytecode = bytecode;
|
|
191
350
|
this.codeHash = codeHash;
|
|
192
351
|
this.fieldsSig = fieldsSig;
|
|
193
352
|
this.eventsSig = eventsSig;
|
|
194
353
|
}
|
|
195
|
-
static checkCodeType(fileName, contractStr) {
|
|
196
|
-
const interfaceMatches = contractStr.match(Contract.interfaceRegex);
|
|
197
|
-
const contractMatches = contractStr.match(Contract.contractRegex);
|
|
198
|
-
if (interfaceMatches === null && contractMatches === null) {
|
|
199
|
-
throw new Error(`No contract found in: ${fileName}`);
|
|
200
|
-
}
|
|
201
|
-
if (interfaceMatches && contractMatches) {
|
|
202
|
-
throw new Error(`Multiple contracts and interfaces in: ${fileName}`);
|
|
203
|
-
}
|
|
204
|
-
if (interfaceMatches === null) {
|
|
205
|
-
if (contractMatches !== null && contractMatches.length > 1) {
|
|
206
|
-
throw new Error(`Multiple contracts in: ${fileName}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (contractMatches === null) {
|
|
210
|
-
if (interfaceMatches !== null && interfaceMatches.length > 1) {
|
|
211
|
-
throw new Error(`Multiple interfaces in: ${fileName}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
static async loadContractStr(sourceFile) {
|
|
216
|
-
return Common._loadContractStr(sourceFile, [], (code) => Contract.checkCodeType(sourceFile.contractPath, code));
|
|
217
|
-
}
|
|
218
|
-
static async fromSource(provider, path, errorOnWarnings = true) {
|
|
219
|
-
if (!fs_1.default.existsSync(Common._artifactsFolder())) {
|
|
220
|
-
fs_1.default.mkdirSync(Common._artifactsFolder(), { recursive: true });
|
|
221
|
-
}
|
|
222
|
-
const sourceFile = this.getSourceFile(path, []);
|
|
223
|
-
const contract = await Common._from(provider, sourceFile, (sourceFile) => Contract.loadContractStr(sourceFile), Contract.compile, errorOnWarnings);
|
|
224
|
-
this._putArtifactToCache(contract);
|
|
225
|
-
return contract;
|
|
226
|
-
}
|
|
227
|
-
static async compile(provider, sourceFile, contractStr, contractHash, errorOnWarnings) {
|
|
228
|
-
const compiled = await provider.contracts.postContractsCompileContract({ code: contractStr });
|
|
229
|
-
Common.checkCompilerWarnings(compiled, errorOnWarnings);
|
|
230
|
-
const artifact = new Contract(contractHash, compiled.bytecode, compiled.codeHash, compiled.fields, compiled.events, compiled.functions);
|
|
231
|
-
await artifact._saveToFile(sourceFile);
|
|
232
|
-
return artifact;
|
|
233
|
-
}
|
|
234
354
|
// TODO: safely parse json
|
|
235
355
|
static fromJson(artifact) {
|
|
236
|
-
if (artifact.
|
|
237
|
-
artifact.bytecode == null ||
|
|
356
|
+
if (artifact.bytecode == null ||
|
|
238
357
|
artifact.codeHash == null ||
|
|
239
358
|
artifact.fieldsSig == null ||
|
|
240
359
|
artifact.eventsSig == null ||
|
|
241
360
|
artifact.functions == null) {
|
|
242
361
|
throw Error('The artifact JSON for contract is incomplete');
|
|
243
362
|
}
|
|
244
|
-
const contract = new Contract(artifact.
|
|
245
|
-
this._putArtifactToCache(contract);
|
|
363
|
+
const contract = new Contract(artifact.bytecode, artifact.codeHash, artifact.fieldsSig, artifact.eventsSig, artifact.functions);
|
|
246
364
|
return contract;
|
|
247
365
|
}
|
|
366
|
+
static fromCompileResult(result) {
|
|
367
|
+
return new Contract(result.bytecode, result.codeHash, result.fields, result.events, result.functions);
|
|
368
|
+
}
|
|
248
369
|
// support both 'code.ral' and 'code.ral.json'
|
|
249
370
|
static async fromArtifactFile(path) {
|
|
250
|
-
const
|
|
251
|
-
const artifactPath = sourceFile.artifactPath;
|
|
252
|
-
const content = await fs_2.promises.readFile(artifactPath);
|
|
371
|
+
const content = await fs_2.promises.readFile(path);
|
|
253
372
|
const artifact = JSON.parse(content.toString());
|
|
254
373
|
return Contract.fromJson(artifact);
|
|
255
374
|
}
|
|
256
|
-
async fetchState(
|
|
257
|
-
const state = await
|
|
375
|
+
async fetchState(address, group) {
|
|
376
|
+
const state = await Project.currentProject.nodeProvider.contracts.getContractsAddressState(address, {
|
|
377
|
+
group: group
|
|
378
|
+
});
|
|
258
379
|
return this.fromApiContractState(state);
|
|
259
380
|
}
|
|
260
381
|
toString() {
|
|
261
|
-
|
|
262
|
-
sourceCodeSha256: this.sourceCodeSha256,
|
|
382
|
+
const object = {
|
|
263
383
|
bytecode: this.bytecode,
|
|
264
384
|
codeHash: this.codeHash,
|
|
265
385
|
fieldsSig: this.fieldsSig,
|
|
266
386
|
eventsSig: this.eventsSig,
|
|
267
387
|
functions: this.functions
|
|
268
|
-
}
|
|
388
|
+
};
|
|
389
|
+
return JSON.stringify(object, null, 2);
|
|
269
390
|
}
|
|
270
391
|
toState(fields, asset, address) {
|
|
271
392
|
const addressDef = typeof address !== 'undefined' ? address : Contract.randomAddress();
|
|
@@ -284,9 +405,9 @@ class Contract extends Common {
|
|
|
284
405
|
bytes[0] = 3;
|
|
285
406
|
return utils_1.bs58.encode(bytes);
|
|
286
407
|
}
|
|
287
|
-
async _test(
|
|
408
|
+
async _test(funcName, params, expectPublic, accessType) {
|
|
288
409
|
const apiParams = this.toTestContract(funcName, params);
|
|
289
|
-
const apiResult = await
|
|
410
|
+
const apiResult = await Project.currentProject.nodeProvider.contracts.postContractsTestContract(apiParams);
|
|
290
411
|
const methodIndex = typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName);
|
|
291
412
|
const isPublic = this.functions[`${methodIndex}`].isPublic;
|
|
292
413
|
if (isPublic === expectPublic) {
|
|
@@ -297,11 +418,11 @@ class Contract extends Common {
|
|
|
297
418
|
throw new Error(`The test method ${funcName} is not ${accessType}`);
|
|
298
419
|
}
|
|
299
420
|
}
|
|
300
|
-
async testPublicMethod(
|
|
301
|
-
return this._test(
|
|
421
|
+
async testPublicMethod(funcName, params) {
|
|
422
|
+
return this._test(funcName, params, true, 'public');
|
|
302
423
|
}
|
|
303
|
-
async testPrivateMethod(
|
|
304
|
-
return this._test(
|
|
424
|
+
async testPrivateMethod(funcName, params) {
|
|
425
|
+
return this._test(funcName, params, false, 'private');
|
|
305
426
|
}
|
|
306
427
|
toApiFields(fields) {
|
|
307
428
|
if (typeof fields === 'undefined') {
|
|
@@ -342,30 +463,8 @@ class Contract extends Common {
|
|
|
342
463
|
inputAssets: toApiInputAssets(params.inputAssets)
|
|
343
464
|
};
|
|
344
465
|
}
|
|
345
|
-
static async fromCodeHash(codeHash) {
|
|
346
|
-
const cached = this._getArtifactFromCache(codeHash);
|
|
347
|
-
if (typeof cached !== 'undefined') {
|
|
348
|
-
return cached;
|
|
349
|
-
}
|
|
350
|
-
const files = await fs_2.promises.readdir(Common._artifactsFolder());
|
|
351
|
-
for (const file of files) {
|
|
352
|
-
if (file.endsWith('.ral.json')) {
|
|
353
|
-
try {
|
|
354
|
-
const contract = await Contract.fromArtifactFile(file);
|
|
355
|
-
if (contract.codeHash === codeHash) {
|
|
356
|
-
return contract;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
catch (_) { }
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
throw new Error(`Unknown code with code hash: ${codeHash}`);
|
|
363
|
-
}
|
|
364
|
-
static async getFieldsSig(state) {
|
|
365
|
-
return Contract.fromCodeHash(state.codeHash).then((contract) => contract.fieldsSig);
|
|
366
|
-
}
|
|
367
466
|
async fromApiContractState(state) {
|
|
368
|
-
const contract =
|
|
467
|
+
const contract = Project.currentProject.contractByCodeHash(state.codeHash);
|
|
369
468
|
return {
|
|
370
469
|
address: state.address,
|
|
371
470
|
contractId: (0, utils_1.binToHex)((0, utils_1.contractIdFromAddress)(state.address)),
|
|
@@ -373,7 +472,7 @@ class Contract extends Common {
|
|
|
373
472
|
initialStateHash: state.initialStateHash,
|
|
374
473
|
codeHash: state.codeHash,
|
|
375
474
|
fields: fromApiFields(state.fields, contract.fieldsSig),
|
|
376
|
-
fieldsSig:
|
|
475
|
+
fieldsSig: contract.fieldsSig,
|
|
377
476
|
asset: fromApiAsset(state.asset)
|
|
378
477
|
};
|
|
379
478
|
}
|
|
@@ -386,7 +485,7 @@ class Contract extends Common {
|
|
|
386
485
|
eventSig = this.ContractDestroyedEvent;
|
|
387
486
|
}
|
|
388
487
|
else {
|
|
389
|
-
const contract =
|
|
488
|
+
const contract = Project.currentProject.contractByCodeHash(codeHash);
|
|
390
489
|
eventSig = contract.eventsSig[event.eventIndex];
|
|
391
490
|
}
|
|
392
491
|
return {
|
|
@@ -455,62 +554,34 @@ Contract.ContractDestroyedEvent = {
|
|
|
455
554
|
fieldNames: ['address'],
|
|
456
555
|
fieldTypes: ['Address']
|
|
457
556
|
};
|
|
458
|
-
class Script extends
|
|
459
|
-
constructor(
|
|
460
|
-
super(
|
|
557
|
+
class Script extends Artifact {
|
|
558
|
+
constructor(bytecodeTemplate, fieldsSig, functions) {
|
|
559
|
+
super(functions);
|
|
461
560
|
this.bytecodeTemplate = bytecodeTemplate;
|
|
462
561
|
this.fieldsSig = fieldsSig;
|
|
463
562
|
}
|
|
464
|
-
static
|
|
465
|
-
|
|
466
|
-
if (scriptMatches === null) {
|
|
467
|
-
throw new Error(`No script found in: ${fileName}`);
|
|
468
|
-
}
|
|
469
|
-
else if (scriptMatches.length > 1) {
|
|
470
|
-
throw new Error(`Multiple scripts in: ${fileName}`);
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
static async loadContractStr(sourceFile) {
|
|
477
|
-
return Common._loadContractStr(sourceFile, [], (code) => Script.checkCodeType(sourceFile.contractPath, code));
|
|
478
|
-
}
|
|
479
|
-
static async fromSource(provider, path, errorOnWarnings = true) {
|
|
480
|
-
const sourceFile = this.getSourceFile(path, []);
|
|
481
|
-
return Common._from(provider, sourceFile, (sourceFile) => Script.loadContractStr(sourceFile), Script.compile, errorOnWarnings);
|
|
482
|
-
}
|
|
483
|
-
static async compile(provider, sourceFile, scriptStr, contractHash, errorOnWarnings = true) {
|
|
484
|
-
const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr });
|
|
485
|
-
Common.checkCompilerWarnings(compiled, errorOnWarnings);
|
|
486
|
-
const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions);
|
|
487
|
-
await artifact._saveToFile(sourceFile);
|
|
488
|
-
return artifact;
|
|
563
|
+
static fromCompileResult(result) {
|
|
564
|
+
return new Script(result.bytecodeTemplate, result.fields, result.functions);
|
|
489
565
|
}
|
|
490
566
|
// TODO: safely parse json
|
|
491
567
|
static fromJson(artifact) {
|
|
492
|
-
if (artifact.
|
|
493
|
-
artifact.bytecodeTemplate == null ||
|
|
494
|
-
artifact.fieldsSig == null ||
|
|
495
|
-
artifact.functions == null) {
|
|
568
|
+
if (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
|
|
496
569
|
throw Error('The artifact JSON for script is incomplete');
|
|
497
570
|
}
|
|
498
|
-
return new Script(artifact.
|
|
571
|
+
return new Script(artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions);
|
|
499
572
|
}
|
|
500
573
|
static async fromArtifactFile(path) {
|
|
501
|
-
const
|
|
502
|
-
const artifactPath = sourceFile.artifactPath;
|
|
503
|
-
const content = await fs_2.promises.readFile(artifactPath);
|
|
574
|
+
const content = await fs_2.promises.readFile(path);
|
|
504
575
|
const artifact = JSON.parse(content.toString());
|
|
505
576
|
return this.fromJson(artifact);
|
|
506
577
|
}
|
|
507
578
|
toString() {
|
|
508
|
-
|
|
509
|
-
sourceCodeSha256: this.sourceCodeSha256,
|
|
579
|
+
const object = {
|
|
510
580
|
bytecodeTemplate: this.bytecodeTemplate,
|
|
511
581
|
fieldsSig: this.fieldsSig,
|
|
512
582
|
functions: this.functions
|
|
513
|
-
}
|
|
583
|
+
};
|
|
584
|
+
return JSON.stringify(object, null, 2);
|
|
514
585
|
}
|
|
515
586
|
async paramsForDeployment(params) {
|
|
516
587
|
const signerParams = {
|
package/gitignore
CHANGED