@alephium/web3 0.1.0 → 0.2.0-rc.2

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 (52) hide show
  1. package/contracts/{add.ral → add/add.ral} +2 -2
  2. package/contracts/{greeter.ral → greeter/greeter.ral} +1 -1
  3. package/contracts/{greeter_interface.ral → greeter/greeter_interface.ral} +0 -0
  4. package/contracts/greeter_main.ral +3 -3
  5. package/contracts/main.ral +1 -1
  6. package/contracts/{sub.ral → sub/sub.ral} +1 -1
  7. package/dist/alephium-web3.min.js +1 -1
  8. package/dist/alephium-web3.min.js.map +1 -1
  9. package/dist/scripts/create-project.js +2 -1
  10. package/dist/src/api/api-alephium.d.ts +14 -2
  11. package/dist/src/api/api-alephium.js +3 -3
  12. package/dist/src/api/api-explorer.d.ts +16 -0
  13. package/dist/src/api/api-explorer.js +26 -0
  14. package/dist/src/contract/contract.d.ts +18 -10
  15. package/dist/src/contract/contract.js +96 -57
  16. package/dist/src/contract/events.d.ts +7 -25
  17. package/dist/src/contract/events.js +18 -31
  18. package/dist/src/index.d.ts +1 -0
  19. package/dist/src/index.js +1 -0
  20. package/dist/src/transaction/index.d.ts +2 -0
  21. package/dist/src/transaction/index.js +31 -0
  22. package/dist/src/{utils/transaction.d.ts → transaction/sign-verify.d.ts} +0 -0
  23. package/dist/src/{utils/transaction.js → transaction/sign-verify.js} +1 -1
  24. package/dist/src/transaction/status.d.ts +10 -0
  25. package/dist/src/transaction/status.js +48 -0
  26. package/dist/src/utils/index.d.ts +1 -1
  27. package/dist/src/utils/index.js +1 -1
  28. package/dist/src/utils/subscription.d.ts +24 -0
  29. package/dist/src/utils/subscription.js +52 -0
  30. package/dist/src/utils/utils.d.ts +2 -0
  31. package/dist/src/utils/utils.js +10 -2
  32. package/package.json +3 -3
  33. package/scripts/create-project.ts +2 -1
  34. package/src/api/api-alephium.ts +20 -3
  35. package/src/api/api-explorer.ts +30 -0
  36. package/src/contract/contract.ts +113 -67
  37. package/src/contract/events.ts +21 -48
  38. package/src/index.ts +1 -0
  39. package/src/signer/signer.ts +1 -1
  40. package/src/transaction/index.ts +20 -0
  41. package/src/{utils/transaction.test.ts → transaction/sign-verify.test.ts} +1 -1
  42. package/src/{utils/transaction.ts → transaction/sign-verify.ts} +1 -1
  43. package/src/transaction/status.ts +58 -0
  44. package/src/utils/index.ts +1 -1
  45. package/src/utils/subscription.ts +72 -0
  46. package/src/utils/utils.test.ts +3 -2
  47. package/src/utils/utils.ts +9 -1
  48. package/templates/base/package.json +2 -2
  49. package/templates/react/package.json +2 -2
  50. package/test/contract.test.ts +24 -7
  51. package/test/events.test.ts +18 -19
  52. package/test/transaction.test.ts +72 -0
@@ -0,0 +1,10 @@
1
+ import { TxStatus } from '../api/api-alephium';
2
+ import { Subscription, SubscribeOptions } from '../utils';
3
+ export declare class TxStatusSubscription extends Subscription<TxStatus> {
4
+ readonly txId: string;
5
+ readonly fromGroup?: number;
6
+ readonly toGroup?: number;
7
+ constructor(options: SubscribeOptions<TxStatus>, txId: string, fromGroup?: number, toGroup?: number);
8
+ polling(): Promise<void>;
9
+ }
10
+ export declare function subscribeToTxStatus(options: SubscribeOptions<TxStatus>, txId: string, fromGroup?: number, toGroup?: number): TxStatusSubscription;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /*
3
+ Copyright 2018 - 2022 The Alephium Authors
4
+ This file is part of the alephium project.
5
+
6
+ The library is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ The library is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with the library. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.subscribeToTxStatus = exports.TxStatusSubscription = void 0;
21
+ const utils_1 = require("../utils");
22
+ class TxStatusSubscription extends utils_1.Subscription {
23
+ constructor(options, txId, fromGroup, toGroup) {
24
+ super(options);
25
+ this.txId = txId;
26
+ this.fromGroup = fromGroup;
27
+ this.toGroup = toGroup;
28
+ this.startPolling();
29
+ }
30
+ async polling() {
31
+ try {
32
+ const txStatus = await this.provider.transactions.getTransactionsStatus({
33
+ txId: this.txId,
34
+ fromGroup: this.fromGroup,
35
+ toGroup: this.toGroup
36
+ });
37
+ await this.messageCallback(txStatus);
38
+ }
39
+ catch (err) {
40
+ await this.errorCallback(err, this);
41
+ }
42
+ }
43
+ }
44
+ exports.TxStatusSubscription = TxStatusSubscription;
45
+ function subscribeToTxStatus(options, txId, fromGroup, toGroup) {
46
+ return new TxStatusSubscription(options, txId, fromGroup, toGroup);
47
+ }
48
+ exports.subscribeToTxStatus = subscribeToTxStatus;
@@ -2,5 +2,5 @@ export * from './address';
2
2
  export * from './bs58';
3
3
  export * from './djb2';
4
4
  export * from './password-crypto';
5
- export * from './transaction';
6
5
  export * from './utils';
6
+ export * from './subscription';
@@ -31,5 +31,5 @@ __exportStar(require("./address"), exports);
31
31
  __exportStar(require("./bs58"), exports);
32
32
  __exportStar(require("./djb2"), exports);
33
33
  __exportStar(require("./password-crypto"), exports);
34
- __exportStar(require("./transaction"), exports);
35
34
  __exportStar(require("./utils"), exports);
35
+ __exportStar(require("./subscription"), exports);
@@ -0,0 +1,24 @@
1
+ import EventEmitter from 'eventemitter3';
2
+ import { NodeProvider } from '../api';
3
+ declare type MessageCallback<Message> = (message: Message) => Promise<void>;
4
+ declare type ErrorCallback<Message> = (error: any, subscription: Subscription<Message>) => Promise<void>;
5
+ export interface SubscribeOptions<Message> {
6
+ provider: NodeProvider;
7
+ pollingInterval: number;
8
+ messageCallback: MessageCallback<Message>;
9
+ errorCallback: ErrorCallback<Message>;
10
+ }
11
+ export declare abstract class Subscription<Message> {
12
+ provider: NodeProvider;
13
+ pollingInterval: number;
14
+ protected messageCallback: MessageCallback<Message>;
15
+ protected errorCallback: ErrorCallback<Message>;
16
+ protected task: ReturnType<typeof setTimeout> | undefined;
17
+ protected eventEmitter: EventEmitter;
18
+ protected cancelled: boolean;
19
+ constructor(options: SubscribeOptions<Message>);
20
+ startPolling(): void;
21
+ unsubscribe(): void;
22
+ abstract polling(): Promise<void>;
23
+ }
24
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /*
3
+ Copyright 2018 - 2022 The Alephium Authors
4
+ This file is part of the alephium project.
5
+
6
+ The library is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ The library is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with the library. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+ var __importDefault = (this && this.__importDefault) || function (mod) {
20
+ return (mod && mod.__esModule) ? mod : { "default": mod };
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.Subscription = void 0;
24
+ const eventemitter3_1 = __importDefault(require("eventemitter3"));
25
+ class Subscription {
26
+ constructor(options) {
27
+ this.provider = options.provider;
28
+ this.pollingInterval = options.pollingInterval;
29
+ this.messageCallback = options.messageCallback;
30
+ this.errorCallback = options.errorCallback;
31
+ this.task = undefined;
32
+ this.cancelled = false;
33
+ this.eventEmitter = new eventemitter3_1.default();
34
+ }
35
+ startPolling() {
36
+ this.eventEmitter.on('tick', async () => {
37
+ await this.polling();
38
+ if (!this.cancelled) {
39
+ this.task = setTimeout(() => this.eventEmitter.emit('tick'), this.pollingInterval);
40
+ }
41
+ });
42
+ this.eventEmitter.emit('tick');
43
+ }
44
+ unsubscribe() {
45
+ this.eventEmitter.removeAllListeners();
46
+ this.cancelled = true;
47
+ if (typeof this.task !== 'undefined') {
48
+ clearTimeout(this.task);
49
+ }
50
+ }
51
+ }
52
+ exports.Subscription = Subscription;
@@ -20,6 +20,8 @@ export declare function addressFromContractId(contractId: string): string;
20
20
  export declare function contractIdFromTx(txId: string, outputIndex: number): string;
21
21
  export declare function subContractId(parentContractId: string, pathInHex: string): string;
22
22
  export declare function stringToHex(str: string): string;
23
+ export declare function hexToString(str: any): string;
24
+ export declare function timeout(ms: number): Promise<unknown>;
23
25
  declare type _Eq<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
24
26
  export declare type Eq<X, Y> = _Eq<{
25
27
  [P in keyof X]: X[P];
@@ -20,7 +20,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
20
20
  return (mod && mod.__esModule) ? mod : { "default": mod };
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.assertType = exports.stringToHex = exports.subContractId = exports.contractIdFromTx = exports.addressFromContractId = exports.addressFromPublicKey = exports.publicKeyFromPrivateKey = exports.binToHex = exports.hexToBinUnsafe = exports.tokenIdFromAddress = exports.contractIdFromAddress = exports.groupOfAddress = exports.isHexString = exports.signatureDecode = exports.signatureEncode = exports.convertHttpResponse = void 0;
23
+ exports.assertType = exports.timeout = exports.hexToString = exports.stringToHex = exports.subContractId = exports.contractIdFromTx = exports.addressFromContractId = exports.addressFromPublicKey = exports.publicKeyFromPrivateKey = exports.binToHex = exports.hexToBinUnsafe = exports.tokenIdFromAddress = exports.contractIdFromAddress = exports.groupOfAddress = exports.isHexString = exports.signatureDecode = exports.signatureEncode = exports.convertHttpResponse = void 0;
24
24
  const elliptic_1 = require("elliptic");
25
25
  const bn_js_1 = __importDefault(require("bn.js"));
26
26
  const blakejs_1 = __importDefault(require("blakejs"));
@@ -182,7 +182,7 @@ function contractIdFromTx(txId, outputIndex) {
182
182
  }
183
183
  exports.contractIdFromTx = contractIdFromTx;
184
184
  function subContractId(parentContractId, pathInHex) {
185
- const data = buffer_1.Buffer.concat([hexToBinUnsafe(pathInHex), hexToBinUnsafe(parentContractId)]);
185
+ const data = buffer_1.Buffer.concat([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)]);
186
186
  return binToHex(blakejs_1.default.blake2b(blakejs_1.default.blake2b(data, undefined, 32), undefined, 32));
187
187
  }
188
188
  exports.subContractId = subContractId;
@@ -194,6 +194,14 @@ function stringToHex(str) {
194
194
  return hex;
195
195
  }
196
196
  exports.stringToHex = stringToHex;
197
+ function hexToString(str) {
198
+ return buffer_1.Buffer.from(str.toString(), 'hex').toString();
199
+ }
200
+ exports.hexToString = hexToString;
201
+ function timeout(ms) {
202
+ return new Promise((resolve) => setTimeout(resolve, ms));
203
+ }
204
+ exports.timeout = timeout;
197
205
  // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
198
206
  function assertType() { }
199
207
  exports.assertType = assertType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alephium/web3",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-rc.2",
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,8 +27,8 @@
27
27
  },
28
28
  "author": "Alephium dev <dev@alephium.org>",
29
29
  "config": {
30
- "alephium_version": "1.4.2",
31
- "explorer_backend_version": "1.7.0"
30
+ "alephium_version": "1.5.0-rc2",
31
+ "explorer_backend_version": "1.7.1"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "rm -rf dist/* && npx tsc --build . && webpack",
@@ -87,7 +87,8 @@ function prepareShared(packageRoot: string, projectRoot: string) {
87
87
 
88
88
  function prepareBase(packageRoot: string, projectRoot: string) {
89
89
  prepareShared(packageRoot, projectRoot)
90
- copy('contracts', ['greeter.ral', 'greeter_interface.ral', 'greeter_main.ral'])
90
+ copy('contracts', ['greeter_main.ral'])
91
+ copy('contracts/greeter', ['greeter.ral', 'greeter_interface.ral'])
91
92
  fsExtra.copySync(path.join(packageRoot, 'templates/base'), projectRoot)
92
93
  }
93
94
 
@@ -96,6 +96,8 @@ export interface Balance {
96
96
 
97
97
  /** @format x.x ALPH */
98
98
  lockedBalanceHint: string
99
+ tokenBalances?: Token[]
100
+ lockedTokenBalances?: Token[]
99
101
 
100
102
  /** @format int32 */
101
103
  utxoNum: number
@@ -205,6 +207,9 @@ export interface BuildDeployContractTx {
205
207
 
206
208
  /** @format uint256 */
207
209
  gasPrice?: string
210
+
211
+ /** @format block-hash */
212
+ targetBlockHash?: string
208
213
  }
209
214
 
210
215
  export interface BuildDeployContractTxResult {
@@ -244,6 +249,9 @@ export interface BuildExecuteScriptTx {
244
249
 
245
250
  /** @format uint256 */
246
251
  gasPrice?: string
252
+
253
+ /** @format block-hash */
254
+ targetBlockHash?: string
247
255
  }
248
256
 
249
257
  export interface BuildExecuteScriptTxResult {
@@ -309,6 +317,9 @@ export interface BuildSweepAddressTransactions {
309
317
 
310
318
  /** @format uint256 */
311
319
  gasPrice?: string
320
+
321
+ /** @format block-hash */
322
+ targetBlockHash?: string
312
323
  }
313
324
 
314
325
  export interface BuildSweepAddressTransactionsResult {
@@ -332,6 +343,9 @@ export interface BuildTransaction {
332
343
 
333
344
  /** @format uint256 */
334
345
  gasPrice?: string
346
+
347
+ /** @format block-hash */
348
+ targetBlockHash?: string
335
349
  }
336
350
 
337
351
  export interface BuildTransactionResult {
@@ -776,6 +790,9 @@ export interface Sweep {
776
790
 
777
791
  /** @format int32 */
778
792
  utxosLimit?: number
793
+
794
+ /** @format block-hash */
795
+ targetBlockHash?: string
779
796
  }
780
797
 
781
798
  export interface SweepAddressTransaction {
@@ -1097,7 +1114,7 @@ export enum ContentType {
1097
1114
  }
1098
1115
 
1099
1116
  export class HttpClient<SecurityDataType = unknown> {
1100
- public baseUrl: string = '{protocol}://{host}:{port}'
1117
+ public baseUrl: string = '../'
1101
1118
  private securityData: SecurityDataType | null = null
1102
1119
  private securityWorker?: ApiConfig<SecurityDataType>['securityWorker']
1103
1120
  private abortControllers = new Map<CancelToken, AbortController>()
@@ -1262,8 +1279,8 @@ export class HttpClient<SecurityDataType = unknown> {
1262
1279
 
1263
1280
  /**
1264
1281
  * @title Alephium API
1265
- * @version 1.4.2
1266
- * @baseUrl {protocol}://{host}:{port}
1282
+ * @version 1.5.0
1283
+ * @baseUrl ../
1267
1284
  */
1268
1285
  export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
1269
1286
  wallets = {
@@ -847,6 +847,36 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
847
847
  path: `/utils/sanity-check`,
848
848
  method: 'PUT',
849
849
  ...params
850
+ }).then(convertHttpResponse),
851
+
852
+ /**
853
+ * @description Update global log level, accepted: TRACE, DEBUG, INFO, WARN, ERROR
854
+ *
855
+ * @tags Utils
856
+ * @name PutUtilsUpdateGlobalLoglevel
857
+ * @request PUT:/utils/update-global-loglevel
858
+ */
859
+ putUtilsUpdateGlobalLoglevel: (data: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', params: RequestParams = {}) =>
860
+ this.request<void, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
861
+ path: `/utils/update-global-loglevel`,
862
+ method: 'PUT',
863
+ body: data,
864
+ ...params
865
+ }).then(convertHttpResponse),
866
+
867
+ /**
868
+ * @description Update logging file, only logback.xml is accepted
869
+ *
870
+ * @tags Utils
871
+ * @name PutUtilsUpdateLogConfig
872
+ * @request PUT:/utils/update-log-config
873
+ */
874
+ putUtilsUpdateLogConfig: (data: string, params: RequestParams = {}) =>
875
+ this.request<void, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
876
+ path: `/utils/update-log-config`,
877
+ method: 'PUT',
878
+ body: data,
879
+ ...params
850
880
  }).then(convertHttpResponse)
851
881
  }
852
882
  }
@@ -27,12 +27,31 @@ import { SignDeployContractTxParams, SignExecuteScriptTxParams, SignerWithNodePr
27
27
  import * as ralph from './ralph'
28
28
  import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
29
29
 
30
+ class SourceFile {
31
+ readonly dirs: string[]
32
+ readonly dirPath: string
33
+ readonly contractPath: string
34
+ readonly artifactPath: string
35
+
36
+ constructor(dirs: string[], fileName: string) {
37
+ this.dirs = dirs
38
+ this.dirPath = dirs.length === 0 ? '' : dirs.join('/') + '/'
39
+ if (fileName.endsWith('.json')) {
40
+ this.contractPath = './contracts/' + this.dirPath + fileName.slice(0, -5)
41
+ this.artifactPath = './artifacts/' + this.dirPath + fileName
42
+ } else {
43
+ this.contractPath = './contracts/' + this.dirPath + fileName
44
+ this.artifactPath = './artifacts/' + this.dirPath + fileName + '.json'
45
+ }
46
+ }
47
+ }
48
+
30
49
  export abstract class Common {
31
50
  readonly sourceCodeSha256: string
32
51
  readonly functions: node.FunctionSig[]
33
52
 
34
- static readonly importRegex = new RegExp('^import "[a-z][a-z_0-9]*.ral"', 'mg')
35
- static readonly contractRegex = new RegExp('^TxContract [A-Z][a-zA-Z0-9]*', 'mg')
53
+ static readonly importRegex = new RegExp('^import "([^"/]+/(([^"]+)/)?)?[a-z][a-z_0-9]*.ral"', 'mg')
54
+ static readonly contractRegex = new RegExp('^(Abstract[ ]+)?Contract [A-Z][a-zA-Z0-9]*', 'mg')
36
55
  static readonly interfaceRegex = new RegExp('^Interface [A-Z][a-zA-Z0-9]* \\{', 'mg')
37
56
  static readonly scriptRegex = new RegExp('^TxScript [A-Z][a-zA-Z0-9]*', 'mg')
38
57
 
@@ -56,38 +75,53 @@ export abstract class Common {
56
75
  this.functions = functions
57
76
  }
58
77
 
59
- protected static _contractPath(fileName: string): string {
60
- if (fileName.endsWith('.json')) {
61
- return `./contracts/${fileName.slice(0, -5)}`
62
- } else {
63
- return `./contracts/${fileName}`
64
- }
78
+ protected static _artifactsFolder(): string {
79
+ return './artifacts/'
65
80
  }
66
81
 
67
- protected static _artifactPath(fileName: string): string {
68
- if (fileName.endsWith('.json')) {
69
- return `./artifacts/${fileName}`
70
- } else {
71
- return `./artifacts/${fileName}.json`
82
+ static getSourceFile(path: string, _dirs: string[]): SourceFile {
83
+ const parts = path.split('/')
84
+ const dirs = Array.from(_dirs)
85
+ if (parts.length === 1) {
86
+ return new SourceFile(dirs, path)
72
87
  }
88
+ parts.slice(0, parts.length - 1).forEach((part) => {
89
+ switch (part) {
90
+ case '.': {
91
+ break
92
+ }
93
+ case '..': {
94
+ if (dirs.length === 0) {
95
+ throw new Error('Invalid file path: ' + path)
96
+ }
97
+ dirs.pop()
98
+ break
99
+ }
100
+ default: {
101
+ dirs.push(part)
102
+ }
103
+ }
104
+ })
105
+ return new SourceFile(dirs, parts[parts.length - 1])
73
106
  }
74
107
 
75
- protected static _artifactsFolder(): string {
76
- return './artifacts/'
77
- }
78
-
79
- protected static async _handleImports(contractStr: string, importsCache: string[]): Promise<string> {
108
+ protected static async _handleImports(
109
+ pathes: string[],
110
+ contractStr: string,
111
+ importsCache: string[]
112
+ ): Promise<string> {
80
113
  const localImportsCache: string[] = []
81
114
  let result = contractStr.replace(Common.importRegex, (match) => {
82
115
  localImportsCache.push(match)
83
116
  return ''
84
117
  })
85
118
  for (const myImport of localImportsCache) {
86
- const fileName = myImport.slice(8, -1)
87
- if (!importsCache.includes(fileName)) {
88
- importsCache.push(fileName)
89
- const importContractStr = await Common._loadContractStr(fileName, importsCache, (code) =>
90
- Contract.checkCodeType(fileName, code)
119
+ const relativePath = myImport.slice(8, -1)
120
+ const importSourceFile = this.getSourceFile(relativePath, pathes)
121
+ if (!importsCache.includes(importSourceFile.contractPath)) {
122
+ importsCache.push(importSourceFile.contractPath)
123
+ const importContractStr = await Common._loadContractStr(importSourceFile, importsCache, (code) =>
124
+ Contract.checkCodeType(importSourceFile.contractPath, code)
91
125
  )
92
126
  result = result.concat('\n', importContractStr)
93
127
  }
@@ -96,16 +130,16 @@ export abstract class Common {
96
130
  }
97
131
 
98
132
  protected static async _loadContractStr(
99
- fileName: string,
133
+ sourceFile: SourceFile,
100
134
  importsCache: string[],
101
- validate: (fileName: string) => void
135
+ validate: (code: string) => void
102
136
  ): Promise<string> {
103
- const contractPath = this._contractPath(fileName)
137
+ const contractPath = sourceFile.contractPath
104
138
  const contractBuffer = await fsPromises.readFile(contractPath)
105
139
  const contractStr = contractBuffer.toString()
106
140
 
107
141
  validate(contractStr)
108
- return Common._handleImports(contractStr, importsCache)
142
+ return Common._handleImports(sourceFile.dirs, contractStr, importsCache)
109
143
  }
110
144
 
111
145
  static checkFileNameExtension(fileName: string): void {
@@ -116,25 +150,28 @@ export abstract class Common {
116
150
 
117
151
  protected static async _from<T extends { sourceCodeSha256: string }>(
118
152
  provider: NodeProvider,
119
- fileName: string,
120
- loadContractStr: (fileName: string, importsCache: string[]) => Promise<string>,
121
- compile: (provider: NodeProvider, fileName: string, contractStr: string, contractHash: string) => Promise<T>
153
+ sourceFile: SourceFile,
154
+ loadContractStr: (sourceFile: SourceFile, importsCache: string[]) => Promise<string>,
155
+ compile: (provider: NodeProvider, sourceFile: SourceFile, contractStr: string, contractHash: string) => Promise<T>
122
156
  ): Promise<T> {
123
- Common.checkFileNameExtension(fileName)
157
+ Common.checkFileNameExtension(sourceFile.contractPath)
124
158
 
125
- const contractStr = await loadContractStr(fileName, [])
159
+ const contractStr = await loadContractStr(sourceFile, [])
126
160
  const contractHash = cryptojs.SHA256(contractStr).toString()
127
161
  const existingContract = this._getArtifactFromCache(contractHash)
128
162
  if (typeof existingContract !== 'undefined') {
129
163
  return existingContract as unknown as T
130
164
  } else {
131
- return compile(provider, fileName, contractStr, contractHash)
165
+ return compile(provider, sourceFile, contractStr, contractHash)
132
166
  }
133
167
  }
134
168
 
135
- protected _saveToFile(fileName: string): Promise<void> {
136
- const artifactPath = Common._artifactPath(fileName)
137
- return fsPromises.writeFile(artifactPath, this.toString())
169
+ protected _saveToFile(sourceFile: SourceFile): Promise<void> {
170
+ const folder = Common._artifactsFolder() + sourceFile.dirPath
171
+ if (!fs.existsSync(folder)) {
172
+ fs.mkdirSync(folder, { recursive: true })
173
+ }
174
+ return fsPromises.writeFile(sourceFile.artifactPath, this.toString())
138
175
  }
139
176
 
140
177
  abstract buildByteCodeToDeploy(initialFields?: Fields): string
@@ -163,32 +200,38 @@ export class Contract extends Common {
163
200
 
164
201
  static checkCodeType(fileName: string, contractStr: string): void {
165
202
  const interfaceMatches = contractStr.match(Contract.interfaceRegex)
166
- if (interfaceMatches) {
167
- return
168
- }
169
-
170
203
  const contractMatches = contractStr.match(Contract.contractRegex)
171
- if (contractMatches === null) {
204
+ if (interfaceMatches === null && contractMatches === null) {
172
205
  throw new Error(`No contract found in: ${fileName}`)
173
- } else if (contractMatches.length > 1) {
174
- throw new Error(`Multiple contracts in: ${fileName}`)
175
- } else {
176
- return
206
+ }
207
+ if (interfaceMatches && contractMatches) {
208
+ throw new Error(`Multiple contracts and interfaces in: ${fileName}`)
209
+ }
210
+ if (interfaceMatches === null) {
211
+ if (contractMatches !== null && contractMatches.length > 1) {
212
+ throw new Error(`Multiple contracts in: ${fileName}`)
213
+ }
214
+ }
215
+ if (contractMatches === null) {
216
+ if (interfaceMatches !== null && interfaceMatches.length > 1) {
217
+ throw new Error(`Multiple interfaces in: ${fileName}`)
218
+ }
177
219
  }
178
220
  }
179
221
 
180
- private static async loadContractStr(fileName: string, importsCache: string[]): Promise<string> {
181
- return Common._loadContractStr(fileName, importsCache, (code) => Contract.checkCodeType(fileName, code))
222
+ private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
223
+ return Common._loadContractStr(sourceFile, [], (code) => Contract.checkCodeType(sourceFile.contractPath, code))
182
224
  }
183
225
 
184
- static async fromSource(provider: NodeProvider, fileName: string): Promise<Contract> {
226
+ static async fromSource(provider: NodeProvider, path: string): Promise<Contract> {
185
227
  if (!fs.existsSync(Common._artifactsFolder())) {
186
228
  fs.mkdirSync(Common._artifactsFolder(), { recursive: true })
187
229
  }
230
+ const sourceFile = this.getSourceFile(path, [])
188
231
  const contract = await Common._from(
189
232
  provider,
190
- fileName,
191
- (fileName, importCaches) => Contract.loadContractStr(fileName, importCaches),
233
+ sourceFile,
234
+ (sourceFile) => Contract.loadContractStr(sourceFile),
192
235
  Contract.compile
193
236
  )
194
237
  this._putArtifactToCache(contract)
@@ -197,7 +240,7 @@ export class Contract extends Common {
197
240
 
198
241
  private static async compile(
199
242
  provider: NodeProvider,
200
- fileName: string,
243
+ sourceFile: SourceFile,
201
244
  contractStr: string,
202
245
  contractHash: string
203
246
  ): Promise<Contract> {
@@ -210,7 +253,7 @@ export class Contract extends Common {
210
253
  compiled.events,
211
254
  compiled.functions
212
255
  )
213
- await artifact._saveToFile(fileName)
256
+ await artifact._saveToFile(sourceFile)
214
257
  return artifact
215
258
  }
216
259
 
@@ -239,13 +282,19 @@ export class Contract extends Common {
239
282
  }
240
283
 
241
284
  // support both 'code.ral' and 'code.ral.json'
242
- static async fromArtifactFile(fileName: string): Promise<Contract> {
243
- const artifactPath = Contract._artifactPath(fileName)
285
+ static async fromArtifactFile(path: string): Promise<Contract> {
286
+ const sourceFile = this.getSourceFile(path, [])
287
+ const artifactPath = sourceFile.artifactPath
244
288
  const content = await fsPromises.readFile(artifactPath)
245
289
  const artifact = JSON.parse(content.toString())
246
290
  return Contract.fromJson(artifact)
247
291
  }
248
292
 
293
+ async fetchState(provider: NodeProvider, address: string, group: number): Promise<ContractState> {
294
+ const state = await provider.contracts.getContractsAddressState(address, { group: group })
295
+ return this.fromApiContractState(state)
296
+ }
297
+
249
298
  override toString(): string {
250
299
  return JSON.stringify(
251
300
  {
@@ -518,28 +567,24 @@ export class Script extends Common {
518
567
  }
519
568
  }
520
569
 
521
- private static async loadContractStr(fileName: string, importsCache: string[]): Promise<string> {
522
- return Common._loadContractStr(fileName, importsCache, (code) => Script.checkCodeType(fileName, code))
570
+ private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
571
+ return Common._loadContractStr(sourceFile, [], (code) => Script.checkCodeType(sourceFile.contractPath, code))
523
572
  }
524
573
 
525
- static async fromSource(provider: NodeProvider, fileName: string): Promise<Script> {
526
- return Common._from(
527
- provider,
528
- fileName,
529
- (fileName, importsCache) => Script.loadContractStr(fileName, importsCache),
530
- Script.compile
531
- )
574
+ static async fromSource(provider: NodeProvider, path: string): Promise<Script> {
575
+ const sourceFile = this.getSourceFile(path, [])
576
+ return Common._from(provider, sourceFile, (sourceFile) => Script.loadContractStr(sourceFile), Script.compile)
532
577
  }
533
578
 
534
579
  private static async compile(
535
580
  provider: NodeProvider,
536
- fileName: string,
581
+ sourceFile: SourceFile,
537
582
  scriptStr: string,
538
583
  contractHash: string
539
584
  ): Promise<Script> {
540
585
  const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr })
541
586
  const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions)
542
- await artifact._saveToFile(fileName)
587
+ await artifact._saveToFile(sourceFile)
543
588
  return artifact
544
589
  }
545
590
 
@@ -556,8 +601,9 @@ export class Script extends Common {
556
601
  return new Script(artifact.sourceCodeSha256, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
557
602
  }
558
603
 
559
- static async fromArtifactFile(fileName: string): Promise<Script> {
560
- const artifactPath = Common._artifactPath(fileName)
604
+ static async fromArtifactFile(path: string): Promise<Script> {
605
+ const sourceFile = this.getSourceFile(path, [])
606
+ const artifactPath = sourceFile.artifactPath
561
607
  const content = await fsPromises.readFile(artifactPath)
562
608
  const artifact = JSON.parse(content.toString())
563
609
  return this.fromJson(artifact)