@alephium/web3 0.3.0-rc.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -908,7 +908,7 @@ export declare class HttpClient<SecurityDataType = unknown> {
908
908
  }
909
909
  /**
910
910
  * @title Alephium API
911
- * @version 1.6.2
911
+ * @version 1.6.4
912
912
  * @baseUrl ../
913
913
  */
914
914
  export declare class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
@@ -149,7 +149,7 @@ class HttpClient {
149
149
  exports.HttpClient = HttpClient;
150
150
  /**
151
151
  * @title Alephium API
152
- * @version 1.6.2
152
+ * @version 1.6.4
153
153
  * @baseUrl ../
154
154
  */
155
155
  class Api extends HttpClient {
@@ -27,9 +27,10 @@ declare class SourceInfo {
27
27
  contractRelativePath: string;
28
28
  sourceCode: string;
29
29
  sourceCodeHash: string;
30
+ isExternal: boolean;
30
31
  getArtifactPath(artifactsRootDir: string): string;
31
- constructor(type: SourceKind, name: string, sourceCode: string, sourceCodeHash: string, contractRelativePath: string);
32
- static from(type: SourceKind, name: string, sourceCode: string, contractRelativePath: string): Promise<SourceInfo>;
32
+ constructor(type: SourceKind, name: string, sourceCode: string, sourceCodeHash: string, contractRelativePath: string, isExternal: boolean);
33
+ static from(type: SourceKind, name: string, sourceCode: string, contractRelativePath: string, isExternal: boolean): Promise<SourceInfo>;
33
34
  }
34
35
  declare class Compiled<T extends Artifact> {
35
36
  sourceInfo: SourceInfo;
@@ -62,6 +63,7 @@ export declare class Project {
62
63
  readonly contractsRootDir: string;
63
64
  readonly artifactsRootDir: string;
64
65
  static currentProject: Project;
66
+ static readonly importRegex: RegExp;
65
67
  static readonly abstractContractMatcher: TypedMatcher<SourceKind>;
66
68
  static readonly contractMatcher: TypedMatcher<SourceKind.Contract>;
67
69
  static readonly interfaceMatcher: TypedMatcher<SourceKind.Interface>;
@@ -76,11 +78,13 @@ export declare class Project {
76
78
  contractByCodeHash(codeHash: string): Contract;
77
79
  private static compile;
78
80
  private static loadArtifacts;
81
+ private static getImportSourcePath;
82
+ private static handleImports;
79
83
  private static loadSourceFile;
80
84
  private static loadSourceFiles;
81
85
  static readonly DEFAULT_CONTRACTS_DIR = "contracts";
82
86
  static readonly DEFAULT_ARTIFACTS_DIR = "artifacts";
83
- static build(compilerOptionsPartial?: Partial<CompilerOptions>, contractsRootDir?: string, artifactsRootDir?: string): Promise<void>;
87
+ static build(compilerOptionsPartial?: Partial<CompilerOptions>, projectRootDir?: string, contractsRootDir?: string, artifactsRootDir?: string): Promise<void>;
84
88
  }
85
89
  export declare abstract class Artifact {
86
90
  readonly version: string;
@@ -76,20 +76,39 @@ class TypedMatcher {
76
76
  this.type = type;
77
77
  }
78
78
  }
79
+ function removeParentsPrefix(parts) {
80
+ let index = 0;
81
+ for (let i = 0; i < parts.length; i++) {
82
+ if (parts[`${i}`] === '..') {
83
+ index += 1;
84
+ }
85
+ else {
86
+ break;
87
+ }
88
+ }
89
+ return path.join(...parts.slice(index));
90
+ }
79
91
  class SourceInfo {
80
- constructor(type, name, sourceCode, sourceCodeHash, contractRelativePath) {
92
+ constructor(type, name, sourceCode, sourceCodeHash, contractRelativePath, isExternal) {
81
93
  this.type = type;
82
94
  this.name = name;
83
95
  this.sourceCode = sourceCode;
84
96
  this.sourceCodeHash = sourceCodeHash;
85
97
  this.contractRelativePath = contractRelativePath;
98
+ this.isExternal = isExternal;
86
99
  }
87
100
  getArtifactPath(artifactsRootDir) {
101
+ if (this.isExternal) {
102
+ const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep));
103
+ const externalPath = path.join('.external', relativePath);
104
+ return path.join(artifactsRootDir, externalPath) + '.json';
105
+ }
88
106
  return path.join(artifactsRootDir, this.contractRelativePath) + '.json';
89
107
  }
90
- static async from(type, name, sourceCode, contractRelativePath) {
108
+ static async from(type, name, sourceCode, contractRelativePath, isExternal) {
91
109
  const sourceCodeHash = await crypto_1.webcrypto.subtle.digest('SHA-256', buffer_1.Buffer.from(sourceCode));
92
- return new SourceInfo(type, name, sourceCode, buffer_1.Buffer.from(sourceCodeHash).toString('hex'), contractRelativePath);
110
+ const sourceCodeHashHex = buffer_1.Buffer.from(sourceCodeHash).toString('hex');
111
+ return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal);
93
112
  }
94
113
  }
95
114
  class Compiled {
@@ -301,49 +320,91 @@ class Project {
301
320
  return Project.compile(provider, sourceInfos, contractsRootDir, artifactsRootDir, errorOnWarnings, compilerOptions);
302
321
  }
303
322
  }
304
- static async loadSourceFile(contractsRootDir, dirPath, filename) {
305
- const contractPath = path.join(dirPath, filename);
306
- const contractRelativePath = path.relative(contractsRootDir, contractPath);
307
- if (!filename.endsWith('.ral')) {
308
- throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`);
323
+ static getImportSourcePath(projectRootDir, importPath) {
324
+ const parts = importPath.split(path.sep);
325
+ if (parts.length > 1 && parts[0] === 'std') {
326
+ const currentDir = path.dirname(__filename);
327
+ return path.join(...[currentDir, '..', '..', '..', importPath]);
309
328
  }
310
- const sourceBuffer = await fs_2.promises.readFile(contractPath);
311
- const sourceStr = sourceBuffer.toString();
312
- const sourceInfos = [];
329
+ let moduleDir = projectRootDir;
330
+ while (true) {
331
+ const expectedPath = path.join(...[moduleDir, 'node_modules', importPath]);
332
+ if (fs_1.default.existsSync(expectedPath)) {
333
+ return expectedPath;
334
+ }
335
+ const oldModuleDir = moduleDir;
336
+ moduleDir = path.join(moduleDir, '..');
337
+ if (oldModuleDir === moduleDir) {
338
+ throw new Error(`Specified import file does not exist: ${importPath}`);
339
+ }
340
+ }
341
+ }
342
+ static async handleImports(projectRootDir, contractRootDir, sourceStr, importsCache) {
343
+ const localImportsCache = [];
344
+ const result = sourceStr.replace(Project.importRegex, (match) => {
345
+ localImportsCache.push(match);
346
+ return '';
347
+ });
348
+ const externalSourceInfos = [];
349
+ for (const myImport of localImportsCache) {
350
+ const originImportPath = myImport.slice(8, -1);
351
+ const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral';
352
+ if (!importsCache.includes(importPath)) {
353
+ importsCache.push(importPath);
354
+ const sourcePath = Project.getImportSourcePath(projectRootDir, importPath);
355
+ const sourceInfos = await Project.loadSourceFile(projectRootDir, contractRootDir, sourcePath, importsCache, true);
356
+ externalSourceInfos.push(...sourceInfos);
357
+ }
358
+ }
359
+ return [result, externalSourceInfos];
360
+ }
361
+ static async loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, isExternal) {
362
+ const contractRelativePath = path.relative(contractsRootDir, sourcePath);
363
+ if (!sourcePath.endsWith('.ral')) {
364
+ throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`);
365
+ }
366
+ const sourceBuffer = await fs_2.promises.readFile(sourcePath);
367
+ const [sourceStr, externalSourceInfos] = await Project.handleImports(projectRootDir, contractsRootDir, sourceBuffer.toString(), importsCache);
368
+ if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
369
+ throw new Error(`Invalid import statements, source: ${sourcePath}`);
370
+ }
371
+ const sourceInfos = externalSourceInfos;
313
372
  for (const matcher of this.matchers) {
314
373
  const results = sourceStr.matchAll(matcher.matcher);
315
374
  for (const result of results) {
316
- const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath);
375
+ const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal);
317
376
  sourceInfos.push(sourceInfo);
318
377
  }
319
378
  }
320
379
  return sourceInfos;
321
380
  }
322
- static async loadSourceFiles(contractsRootDir) {
323
- const loadDir = async function (dirPath, results) {
381
+ static async loadSourceFiles(projectRootDir, contractsRootDir) {
382
+ const importsCache = [];
383
+ const sourceInfos = [];
384
+ const loadDir = async function (dirPath) {
324
385
  const dirents = await fs_2.promises.readdir(dirPath, { withFileTypes: true });
325
386
  for (const dirent of dirents) {
326
387
  if (dirent.isFile()) {
327
- const sourceInfos = await Project.loadSourceFile(contractsRootDir, dirPath, dirent.name);
328
- results.push(...sourceInfos);
388
+ const sourcePath = path.join(dirPath, dirent.name);
389
+ const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false);
390
+ sourceInfos.push(...infos);
329
391
  }
330
392
  else {
331
393
  const newPath = path.join(dirPath, dirent.name);
332
- await loadDir(newPath, results);
394
+ await loadDir(newPath);
333
395
  }
334
396
  }
335
397
  };
336
- const sourceInfos = [];
337
- await loadDir(contractsRootDir, sourceInfos);
398
+ await loadDir(contractsRootDir);
338
399
  const contractAndScriptSize = sourceInfos.filter((f) => f.type === SourceKind.Contract || f.type === SourceKind.Script).length;
339
400
  if (sourceInfos.length === 0 || contractAndScriptSize === 0) {
340
401
  throw new Error('Project have no source files');
341
402
  }
342
403
  return sourceInfos.sort((a, b) => a.type - b.type);
343
404
  }
344
- static async build(compilerOptionsPartial = {}, contractsRootDir = Project.DEFAULT_CONTRACTS_DIR, artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR) {
405
+ static async build(compilerOptionsPartial = {}, projectRootDir = '.', contractsRootDir = Project.DEFAULT_CONTRACTS_DIR, artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR) {
345
406
  const provider = (0, global_1.getCurrentNodeProvider)();
346
- const sourceFiles = await Project.loadSourceFiles(contractsRootDir);
407
+ const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir);
347
408
  const { errorOnWarnings, ...nodeCompilerOptions } = { ...exports.DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial };
348
409
  const projectArtifact = await ProjectArtifact.from(artifactsRootDir);
349
410
  if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
@@ -357,9 +418,10 @@ class Project {
357
418
  }
358
419
  }
359
420
  exports.Project = Project;
421
+ Project.importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg');
360
422
  Project.abstractContractMatcher = new TypedMatcher('^Abstract Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.AbstractContract);
361
423
  Project.contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract);
362
- Project.interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*) \\{', SourceKind.Interface);
424
+ Project.interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface);
363
425
  Project.scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script);
364
426
  Project.matchers = [
365
427
  Project.abstractContractMatcher,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alephium/web3",
3
- "version": "0.3.0-rc.7",
3
+ "version": "0.4.0",
4
4
  "description": "A JS/TS library to interact with the Alephium platform",
5
5
  "license": "GPL",
6
6
  "main": "dist/src/index.js",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "author": "Alephium dev <dev@alephium.org>",
29
29
  "config": {
30
- "alephium_version": "1.6.2",
30
+ "alephium_version": "1.6.4",
31
31
  "explorer_backend_version": "1.12.0-rc2"
32
32
  },
33
33
  "scripts": {
@@ -1332,7 +1332,7 @@ export class HttpClient<SecurityDataType = unknown> {
1332
1332
 
1333
1333
  /**
1334
1334
  * @title Alephium API
1335
- * @version 1.6.2
1335
+ * @version 1.6.4
1336
1336
  * @baseUrl ../
1337
1337
  */
1338
1338
  export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
@@ -86,14 +86,32 @@ class TypedMatcher<T extends SourceKind> {
86
86
  }
87
87
  }
88
88
 
89
+ function removeParentsPrefix(parts: string[]): string {
90
+ let index = 0
91
+ for (let i = 0; i < parts.length; i++) {
92
+ if (parts[`${i}`] === '..') {
93
+ index += 1
94
+ } else {
95
+ break
96
+ }
97
+ }
98
+ return path.join(...parts.slice(index))
99
+ }
100
+
89
101
  class SourceInfo {
90
102
  type: SourceKind
91
103
  name: string
92
104
  contractRelativePath: string
93
105
  sourceCode: string
94
106
  sourceCodeHash: string
107
+ isExternal: boolean
95
108
 
96
109
  getArtifactPath(artifactsRootDir: string): string {
110
+ if (this.isExternal) {
111
+ const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep))
112
+ const externalPath = path.join('.external', relativePath)
113
+ return path.join(artifactsRootDir, externalPath) + '.json'
114
+ }
97
115
  return path.join(artifactsRootDir, this.contractRelativePath) + '.json'
98
116
  }
99
117
 
@@ -102,23 +120,27 @@ class SourceInfo {
102
120
  name: string,
103
121
  sourceCode: string,
104
122
  sourceCodeHash: string,
105
- contractRelativePath: string
123
+ contractRelativePath: string,
124
+ isExternal: boolean
106
125
  ) {
107
126
  this.type = type
108
127
  this.name = name
109
128
  this.sourceCode = sourceCode
110
129
  this.sourceCodeHash = sourceCodeHash
111
130
  this.contractRelativePath = contractRelativePath
131
+ this.isExternal = isExternal
112
132
  }
113
133
 
114
134
  static async from(
115
135
  type: SourceKind,
116
136
  name: string,
117
137
  sourceCode: string,
118
- contractRelativePath: string
138
+ contractRelativePath: string,
139
+ isExternal: boolean
119
140
  ): Promise<SourceInfo> {
120
141
  const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
121
- return new SourceInfo(type, name, sourceCode, Buffer.from(sourceCodeHash).toString('hex'), contractRelativePath)
142
+ const sourceCodeHashHex = Buffer.from(sourceCodeHash).toString('hex')
143
+ return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal)
122
144
  }
123
145
  }
124
146
 
@@ -223,12 +245,13 @@ export class Project {
223
245
 
224
246
  static currentProject: Project
225
247
 
248
+ static readonly importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg')
226
249
  static readonly abstractContractMatcher = new TypedMatcher<SourceKind>(
227
250
  '^Abstract Contract ([A-Z][a-zA-Z0-9]*)',
228
251
  SourceKind.AbstractContract
229
252
  )
230
253
  static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract)
231
- static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*) \\{', SourceKind.Interface)
254
+ static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface)
232
255
  static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script)
233
256
  static readonly matchers = [
234
257
  Project.abstractContractMatcher,
@@ -445,45 +468,107 @@ export class Project {
445
468
  }
446
469
  }
447
470
 
471
+ private static getImportSourcePath(projectRootDir: string, importPath: string): string {
472
+ const parts = importPath.split(path.sep)
473
+ if (parts.length > 1 && parts[0] === 'std') {
474
+ const currentDir = path.dirname(__filename)
475
+ return path.join(...[currentDir, '..', '..', '..', importPath])
476
+ }
477
+ let moduleDir = projectRootDir
478
+ while (true) {
479
+ const expectedPath = path.join(...[moduleDir, 'node_modules', importPath])
480
+ if (fs.existsSync(expectedPath)) {
481
+ return expectedPath
482
+ }
483
+ const oldModuleDir = moduleDir
484
+ moduleDir = path.join(moduleDir, '..')
485
+ if (oldModuleDir === moduleDir) {
486
+ throw new Error(`Specified import file does not exist: ${importPath}`)
487
+ }
488
+ }
489
+ }
490
+
491
+ private static async handleImports(
492
+ projectRootDir: string,
493
+ contractRootDir: string,
494
+ sourceStr: string,
495
+ importsCache: string[]
496
+ ): Promise<[string, SourceInfo[]]> {
497
+ const localImportsCache: string[] = []
498
+ const result = sourceStr.replace(Project.importRegex, (match) => {
499
+ localImportsCache.push(match)
500
+ return ''
501
+ })
502
+ const externalSourceInfos: SourceInfo[] = []
503
+ for (const myImport of localImportsCache) {
504
+ const originImportPath = myImport.slice(8, -1)
505
+ const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral'
506
+ if (!importsCache.includes(importPath)) {
507
+ importsCache.push(importPath)
508
+ const sourcePath = Project.getImportSourcePath(projectRootDir, importPath)
509
+ const sourceInfos = await Project.loadSourceFile(
510
+ projectRootDir,
511
+ contractRootDir,
512
+ sourcePath,
513
+ importsCache,
514
+ true
515
+ )
516
+ externalSourceInfos.push(...sourceInfos)
517
+ }
518
+ }
519
+ return [result, externalSourceInfos]
520
+ }
521
+
448
522
  private static async loadSourceFile(
523
+ projectRootDir: string,
449
524
  contractsRootDir: string,
450
- dirPath: string,
451
- filename: string
525
+ sourcePath: string,
526
+ importsCache: string[],
527
+ isExternal: boolean
452
528
  ): Promise<SourceInfo[]> {
453
- const contractPath = path.join(dirPath, filename)
454
- const contractRelativePath = path.relative(contractsRootDir, contractPath)
455
- if (!filename.endsWith('.ral')) {
456
- throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`)
529
+ const contractRelativePath = path.relative(contractsRootDir, sourcePath)
530
+ if (!sourcePath.endsWith('.ral')) {
531
+ throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`)
457
532
  }
458
533
 
459
- const sourceBuffer = await fsPromises.readFile(contractPath)
460
- const sourceStr = sourceBuffer.toString()
461
- const sourceInfos: SourceInfo[] = []
534
+ const sourceBuffer = await fsPromises.readFile(sourcePath)
535
+ const [sourceStr, externalSourceInfos] = await Project.handleImports(
536
+ projectRootDir,
537
+ contractsRootDir,
538
+ sourceBuffer.toString(),
539
+ importsCache
540
+ )
541
+ if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
542
+ throw new Error(`Invalid import statements, source: ${sourcePath}`)
543
+ }
544
+ const sourceInfos = externalSourceInfos
462
545
  for (const matcher of this.matchers) {
463
546
  const results = sourceStr.matchAll(matcher.matcher)
464
547
  for (const result of results) {
465
- const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath)
548
+ const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal)
466
549
  sourceInfos.push(sourceInfo)
467
550
  }
468
551
  }
469
552
  return sourceInfos
470
553
  }
471
554
 
472
- private static async loadSourceFiles(contractsRootDir: string): Promise<SourceInfo[]> {
473
- const loadDir = async function (dirPath: string, results: SourceInfo[]): Promise<void> {
555
+ private static async loadSourceFiles(projectRootDir: string, contractsRootDir: string): Promise<SourceInfo[]> {
556
+ const importsCache: string[] = []
557
+ const sourceInfos: SourceInfo[] = []
558
+ const loadDir = async function (dirPath: string): Promise<void> {
474
559
  const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
475
560
  for (const dirent of dirents) {
476
561
  if (dirent.isFile()) {
477
- const sourceInfos = await Project.loadSourceFile(contractsRootDir, dirPath, dirent.name)
478
- results.push(...sourceInfos)
562
+ const sourcePath = path.join(dirPath, dirent.name)
563
+ const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false)
564
+ sourceInfos.push(...infos)
479
565
  } else {
480
566
  const newPath = path.join(dirPath, dirent.name)
481
- await loadDir(newPath, results)
567
+ await loadDir(newPath)
482
568
  }
483
569
  }
484
570
  }
485
- const sourceInfos: SourceInfo[] = []
486
- await loadDir(contractsRootDir, sourceInfos)
571
+ await loadDir(contractsRootDir)
487
572
  const contractAndScriptSize = sourceInfos.filter(
488
573
  (f) => f.type === SourceKind.Contract || f.type === SourceKind.Script
489
574
  ).length
@@ -498,11 +583,12 @@ export class Project {
498
583
 
499
584
  static async build(
500
585
  compilerOptionsPartial: Partial<CompilerOptions> = {},
586
+ projectRootDir = '.',
501
587
  contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
502
588
  artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR
503
589
  ): Promise<void> {
504
590
  const provider = getCurrentNodeProvider()
505
- const sourceFiles = await Project.loadSourceFiles(contractsRootDir)
591
+ const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
506
592
  const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
507
593
  const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
508
594
  if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
@@ -0,0 +1,9 @@
1
+ Interface IToken {
2
+ pub fn getSymbol() -> ByteVec
3
+
4
+ pub fn getName() -> ByteVec
5
+
6
+ pub fn getDecimals() -> U256
7
+
8
+ pub fn getTotalSupply() -> U256
9
+ }