@alephium/web3 0.41.0 → 0.43.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.
Files changed (123) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.map +1 -1
  3. package/dist/src/{utils → address}/address.js +15 -19
  4. package/dist/src/address/index.d.ts +1 -0
  5. package/dist/src/address/index.js +34 -0
  6. package/dist/src/api/node-provider.js +10 -9
  7. package/dist/src/api/types.js +1 -1
  8. package/dist/src/codec/array-codec.d.ts +2 -3
  9. package/dist/src/codec/array-codec.js +1 -2
  10. package/dist/src/codec/asset-output-codec.d.ts +3 -4
  11. package/dist/src/codec/asset-output-codec.js +10 -12
  12. package/dist/src/codec/bigint-codec.d.ts +2 -3
  13. package/dist/src/codec/bigint-codec.js +2 -3
  14. package/dist/src/codec/bytestring-codec.d.ts +5 -6
  15. package/dist/src/codec/bytestring-codec.js +5 -5
  16. package/dist/src/codec/codec.d.ts +2 -3
  17. package/dist/src/codec/compact-int-codec.d.ts +12 -13
  18. package/dist/src/codec/compact-int-codec.js +22 -22
  19. package/dist/src/codec/contract-codec.d.ts +5 -6
  20. package/dist/src/codec/contract-codec.js +24 -24
  21. package/dist/src/codec/contract-output-codec.d.ts +2 -3
  22. package/dist/src/codec/contract-output-codec.js +8 -10
  23. package/dist/src/codec/contract-output-ref-codec.d.ts +3 -4
  24. package/dist/src/codec/contract-output-ref-codec.js +2 -2
  25. package/dist/src/codec/either-codec.d.ts +2 -3
  26. package/dist/src/codec/either-codec.js +1 -2
  27. package/dist/src/codec/hash.d.ts +2 -3
  28. package/dist/src/codec/hash.js +17 -0
  29. package/dist/src/codec/input-codec.d.ts +3 -4
  30. package/dist/src/codec/input-codec.js +6 -6
  31. package/dist/src/codec/instr-codec.d.ts +2 -3
  32. package/dist/src/codec/instr-codec.js +1 -2
  33. package/dist/src/codec/lockup-script-codec.d.ts +5 -6
  34. package/dist/src/codec/lockup-script-codec.js +1 -2
  35. package/dist/src/codec/long-codec.d.ts +2 -3
  36. package/dist/src/codec/long-codec.js +1 -2
  37. package/dist/src/codec/method-codec.d.ts +2 -3
  38. package/dist/src/codec/method-codec.js +1 -2
  39. package/dist/src/codec/option-codec.d.ts +3 -4
  40. package/dist/src/codec/option-codec.js +2 -3
  41. package/dist/src/codec/script-codec.d.ts +4 -5
  42. package/dist/src/codec/script-codec.js +1 -3
  43. package/dist/src/codec/signature-codec.d.ts +3 -4
  44. package/dist/src/codec/signature-codec.js +17 -0
  45. package/dist/src/codec/signed-int-codec.d.ts +2 -3
  46. package/dist/src/codec/signed-int-codec.js +1 -2
  47. package/dist/src/codec/token-codec.d.ts +3 -4
  48. package/dist/src/codec/token-codec.js +3 -3
  49. package/dist/src/codec/transaction-codec.d.ts +4 -5
  50. package/dist/src/codec/transaction-codec.js +12 -13
  51. package/dist/src/codec/unlock-script-codec.d.ts +5 -6
  52. package/dist/src/codec/unlock-script-codec.js +14 -15
  53. package/dist/src/codec/unsigned-tx-codec.d.ts +4 -5
  54. package/dist/src/codec/unsigned-tx-codec.js +4 -5
  55. package/dist/src/contract/contract.d.ts +8 -95
  56. package/dist/src/contract/contract.js +31 -564
  57. package/dist/src/contract/ralph.d.ts +1 -1
  58. package/dist/src/contract/ralph.js +9 -10
  59. package/dist/src/{utils → exchange}/exchange.js +5 -5
  60. package/dist/src/exchange/index.d.ts +1 -0
  61. package/dist/src/exchange/index.js +25 -0
  62. package/dist/src/index.d.ts +2 -0
  63. package/dist/src/index.js +2 -0
  64. package/dist/src/signer/signer.js +4 -5
  65. package/dist/src/signer/tx-builder.js +3 -3
  66. package/dist/src/transaction/index.d.ts +1 -0
  67. package/dist/src/transaction/index.js +1 -0
  68. package/dist/src/transaction/utils.d.ts +2 -0
  69. package/dist/src/transaction/utils.js +34 -0
  70. package/dist/src/utils/index.d.ts +0 -2
  71. package/dist/src/utils/index.js +0 -7
  72. package/dist/src/utils/sign.js +1 -1
  73. package/dist/src/utils/utils.d.ts +1 -0
  74. package/dist/src/utils/utils.js +30 -11
  75. package/dist/src/utils/webcrypto.js +3 -4
  76. package/package.json +1 -2
  77. package/src/{utils → address}/address.ts +15 -19
  78. package/src/address/index.ts +19 -0
  79. package/src/api/node-provider.ts +2 -9
  80. package/src/api/types.ts +2 -2
  81. package/src/codec/array-codec.ts +3 -4
  82. package/src/codec/asset-output-codec.ts +16 -18
  83. package/src/codec/bigint-codec.ts +4 -5
  84. package/src/codec/bytestring-codec.ts +8 -8
  85. package/src/codec/codec.ts +2 -3
  86. package/src/codec/compact-int-codec.ts +36 -37
  87. package/src/codec/contract-codec.ts +28 -27
  88. package/src/codec/contract-output-codec.ts +11 -13
  89. package/src/codec/contract-output-ref-codec.ts +5 -5
  90. package/src/codec/either-codec.ts +3 -4
  91. package/src/codec/hash.ts +2 -3
  92. package/src/codec/input-codec.ts +10 -10
  93. package/src/codec/instr-codec.ts +3 -4
  94. package/src/codec/lockup-script-codec.ts +8 -9
  95. package/src/codec/long-codec.ts +3 -4
  96. package/src/codec/method-codec.ts +3 -4
  97. package/src/codec/option-codec.ts +4 -5
  98. package/src/codec/script-codec.ts +5 -7
  99. package/src/codec/signature-codec.ts +3 -4
  100. package/src/codec/signed-int-codec.ts +3 -4
  101. package/src/codec/token-codec.ts +6 -6
  102. package/src/codec/transaction-codec.ts +17 -18
  103. package/src/codec/unlock-script-codec.ts +26 -27
  104. package/src/codec/unsigned-tx-codec.ts +10 -11
  105. package/src/contract/contract.ts +18 -790
  106. package/src/contract/ralph.ts +10 -11
  107. package/src/{utils → exchange}/exchange.ts +3 -11
  108. package/src/exchange/index.ts +19 -0
  109. package/src/index.ts +2 -0
  110. package/src/signer/signer.ts +2 -3
  111. package/src/signer/tx-builder.ts +2 -2
  112. package/src/transaction/index.ts +1 -0
  113. package/src/transaction/utils.ts +38 -0
  114. package/src/utils/index.ts +0 -2
  115. package/src/utils/sign.ts +1 -1
  116. package/src/utils/utils.ts +29 -10
  117. package/src/utils/webcrypto.ts +3 -4
  118. package/webpack.config.js +1 -6
  119. package/dist/src/utils/error.d.ts +0 -15
  120. package/dist/src/utils/error.js +0 -66
  121. package/src/utils/error.ts +0 -77
  122. /package/dist/src/{utils → address}/address.d.ts +0 -0
  123. /package/dist/src/{utils → exchange}/exchange.d.ts +0 -0
@@ -16,8 +16,6 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import { Buffer } from 'buffer/'
20
- import fs from 'fs'
21
19
  import { promises as fsPromises } from 'fs'
22
20
  import {
23
21
  fromApiNumber256,
@@ -37,7 +35,6 @@ import {
37
35
  fromApiPrimitiveVal,
38
36
  tryGetCallResult
39
37
  } from '../api'
40
- import { CompileProjectResult } from '../api/api-alephium'
41
38
  import {
42
39
  SignDeployContractTxParams,
43
40
  SignDeployContractTxResult,
@@ -49,25 +46,20 @@ import * as ralph from './ralph'
49
46
  import {
50
47
  bs58,
51
48
  binToHex,
52
- contractIdFromAddress,
53
49
  Subscription,
54
50
  assertType,
55
51
  Eq,
56
52
  Optional,
57
- groupOfAddress,
58
53
  WebCrypto,
59
54
  hexToBinUnsafe,
60
55
  isDevnet,
61
- addressFromContractId,
62
- subContractId,
63
56
  HexString
64
57
  } from '../utils'
58
+ import { contractIdFromAddress, groupOfAddress, addressFromContractId, subContractId } from '../address'
65
59
  import { getCurrentNodeProvider } from '../global'
66
- import * as path from 'path'
67
60
  import { EventSubscribeOptions, EventSubscription, subscribeToEvents } from './events'
68
61
  import { ONE_ALPH, TOTAL_NUMBER_OF_GROUPS } from '../constants'
69
62
  import * as blake from 'blakejs'
70
- import { parseError } from '../utils/error'
71
63
  import { isContractDebugMessageEnabled } from '../debug'
72
64
  import {
73
65
  contract,
@@ -96,14 +88,6 @@ export type Enum = node.Enum
96
88
 
97
89
  export const StdIdFieldName = '__stdInterfaceId'
98
90
 
99
- enum SourceKind {
100
- Contract = 0,
101
- Script = 1,
102
- AbstractContract = 2,
103
- Interface = 3,
104
- Struct = 4
105
- }
106
-
107
91
  export type CompilerOptions = node.CompilerOptions & {
108
92
  errorOnWarnings: boolean
109
93
  }
@@ -119,239 +103,6 @@ export const DEFAULT_NODE_COMPILER_OPTIONS: node.CompilerOptions = {
119
103
 
120
104
  export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { errorOnWarnings: true, ...DEFAULT_NODE_COMPILER_OPTIONS }
121
105
 
122
- class TypedMatcher<T extends SourceKind> {
123
- matcher: RegExp
124
- type: T
125
-
126
- constructor(pattern: string, type: T) {
127
- this.matcher = new RegExp(pattern, 'mg')
128
- this.type = type
129
- }
130
- }
131
-
132
- function removeParentsPrefix(parts: string[]): string {
133
- let index = 0
134
- for (let i = 0; i < parts.length; i++) {
135
- if (parts[`${i}`] === '..') {
136
- index += 1
137
- } else {
138
- break
139
- }
140
- }
141
- return path.join(...parts.slice(index))
142
- }
143
-
144
- class SourceInfo {
145
- type: SourceKind
146
- name: string
147
- contractRelativePath: string
148
- sourceCode: string
149
- sourceCodeHash: string
150
- isExternal: boolean
151
-
152
- getArtifactPath(artifactsRootDir: string): string {
153
- let fullPath: string
154
- if (this.isExternal) {
155
- const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep))
156
- const externalPath = path.join('.external', relativePath)
157
- fullPath = path.join(artifactsRootDir, externalPath)
158
- } else {
159
- fullPath = path.join(artifactsRootDir, this.contractRelativePath)
160
- }
161
- return path.join(path.dirname(fullPath), `${this.name}.ral.json`)
162
- }
163
-
164
- constructor(
165
- type: SourceKind,
166
- name: string,
167
- sourceCode: string,
168
- sourceCodeHash: string,
169
- contractRelativePath: string,
170
- isExternal: boolean
171
- ) {
172
- this.type = type
173
- this.name = name
174
- this.sourceCode = sourceCode
175
- this.sourceCodeHash = sourceCodeHash
176
- this.contractRelativePath = contractRelativePath
177
- this.isExternal = isExternal
178
- }
179
-
180
- static async from(
181
- type: SourceKind,
182
- name: string,
183
- sourceCode: string,
184
- contractRelativePath: string,
185
- isExternal: boolean
186
- ): Promise<SourceInfo> {
187
- const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
188
- const sourceCodeHashHex = Buffer.from(sourceCodeHash).toString('hex')
189
- return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal)
190
- }
191
- }
192
-
193
- class Compiled<T extends Artifact> {
194
- sourceInfo: SourceInfo
195
- artifact: T
196
- warnings: string[]
197
-
198
- constructor(sourceInfo: SourceInfo, artifact: T, warnings: string[]) {
199
- this.sourceInfo = sourceInfo
200
- this.artifact = artifact
201
- this.warnings = warnings
202
- }
203
- }
204
-
205
- type CodeInfo = {
206
- sourceFile: string
207
- sourceCodeHash: string
208
- bytecodeDebugPatch: string
209
- codeHashDebug: string
210
- warnings: string[]
211
- }
212
-
213
- type SourceInfoIndexes = {
214
- sourceInfo: SourceInfo
215
- startIndex: number
216
- endIndex: number
217
- }
218
-
219
- function findSourceInfoAtLineNumber(sources: SourceInfo[], line: number): SourceInfoIndexes | undefined {
220
- let currentLine = 0
221
- const sourceInfosWithLine: SourceInfoIndexes[] = sources.map((source) => {
222
- const startIndex = currentLine + 1
223
- currentLine += source.sourceCode.split('\n').length
224
- const endIndex = currentLine
225
- return { sourceInfo: source, startIndex: startIndex, endIndex: endIndex }
226
- })
227
-
228
- const sourceInfo = sourceInfosWithLine.find((sourceInfoWithLine) => {
229
- return line >= sourceInfoWithLine.startIndex && line <= sourceInfoWithLine.endIndex
230
- })
231
-
232
- return sourceInfo
233
- }
234
-
235
- export class ProjectArtifact {
236
- static readonly artifactFileName = '.project.json'
237
-
238
- fullNodeVersion: string
239
- compilerOptionsUsed: node.CompilerOptions
240
- infos: Map<string, CodeInfo>
241
-
242
- static checkCompilerOptionsParameter(compilerOptions: node.CompilerOptions): void {
243
- if (Object.keys(compilerOptions).length != Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
244
- throw Error(`Not all compiler options are set: ${compilerOptions}`)
245
- }
246
-
247
- const combined = { ...compilerOptions, ...DEFAULT_NODE_COMPILER_OPTIONS }
248
- if (Object.keys(combined).length !== Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
249
- throw Error(`There are unknown compiler options: ${compilerOptions}`)
250
- }
251
- }
252
-
253
- constructor(fullNodeVersion: string, compilerOptionsUsed: node.CompilerOptions, infos: Map<string, CodeInfo>) {
254
- ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
255
- this.fullNodeVersion = fullNodeVersion
256
- this.compilerOptionsUsed = compilerOptionsUsed
257
- this.infos = infos
258
- }
259
-
260
- static isCodeChanged(current: ProjectArtifact, previous: ProjectArtifact): boolean {
261
- if (current.infos.size !== previous.infos.size) {
262
- return true
263
- }
264
- for (const [name, codeInfo] of current.infos) {
265
- const prevCodeInfo = previous.infos.get(name)
266
- if (prevCodeInfo?.codeHashDebug !== codeInfo.codeHashDebug) {
267
- return true
268
- }
269
- }
270
- return false
271
- }
272
-
273
- async saveToFile(rootPath: string): Promise<void> {
274
- const filepath = path.join(rootPath, ProjectArtifact.artifactFileName)
275
- const artifact = {
276
- fullNodeVersion: this.fullNodeVersion,
277
- compilerOptionsUsed: this.compilerOptionsUsed,
278
- infos: Object.fromEntries(new Map([...this.infos].sort()))
279
- }
280
- const content = JSON.stringify(artifact, null, 2)
281
- return fsPromises.writeFile(filepath, content)
282
- }
283
-
284
- getChangedSources(sourceInfos: SourceInfo[]): SourceInfo[] {
285
- const result: SourceInfo[] = []
286
- for (const sourceInfo of sourceInfos) {
287
- const info = this.infos.get(sourceInfo.name)
288
- if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
289
- result.push(sourceInfo)
290
- }
291
- }
292
- return result
293
- }
294
-
295
- needToReCompile(compilerOptions: node.CompilerOptions, fullNodeVersion: string): boolean {
296
- ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
297
- if (this.fullNodeVersion !== fullNodeVersion) {
298
- return true
299
- }
300
-
301
- const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
302
- const usedOption = this.compilerOptionsUsed[`${key}`]
303
- return usedOption === inputOption
304
- })
305
- if (!optionsMatched) {
306
- return true
307
- }
308
-
309
- return false
310
- }
311
-
312
- static async from(rootPath: string): Promise<ProjectArtifact | undefined> {
313
- const filepath = path.join(rootPath, ProjectArtifact.artifactFileName)
314
- if (!fs.existsSync(filepath)) {
315
- return undefined
316
- }
317
- try {
318
- const content = await fsPromises.readFile(filepath)
319
- const json = JSON.parse(content.toString())
320
- const fullNodeVersion = json.fullNodeVersion as string
321
- const compilerOptionsUsed = json.compilerOptionsUsed as node.CompilerOptions
322
- const files = new Map(Object.entries<CodeInfo>(json.infos))
323
- return new ProjectArtifact(fullNodeVersion, compilerOptionsUsed, files)
324
- } catch (error) {
325
- console.error(`Failed to load project artifact, error: ${error}`)
326
- return undefined
327
- }
328
- }
329
- }
330
-
331
- function removeOldArtifacts(dir: string, sourceFiles: SourceInfo[]) {
332
- const files = fs.readdirSync(dir)
333
- files.forEach((file) => {
334
- const filePath = path.join(dir, file)
335
- const stat = fs.statSync(filePath)
336
- if (stat.isDirectory()) {
337
- removeOldArtifacts(filePath, sourceFiles)
338
- } else if (filePath.endsWith('.ral.json') || filePath.endsWith('.ral')) {
339
- const filename = path.basename(filePath)
340
- const artifactName = filename.slice(0, filename.indexOf('.'))
341
- const sourceFile = sourceFiles.find(
342
- (s) => s.name === artifactName && (s.type === SourceKind.Contract || s.type === SourceKind.Script)
343
- )
344
- if (sourceFile === undefined) {
345
- fs.unlinkSync(filePath)
346
- }
347
- }
348
- })
349
-
350
- if (fs.readdirSync(dir).length === 0) {
351
- fs.rmdirSync(dir)
352
- }
353
- }
354
-
355
106
  export class Struct {
356
107
  name: string
357
108
  fieldNames: string[]
@@ -386,527 +137,6 @@ export class Struct {
386
137
  }
387
138
  }
388
139
 
389
- export class Project {
390
- sourceInfos: SourceInfo[]
391
- contracts: Map<string, Compiled<Contract>>
392
- scripts: Map<string, Compiled<Script>>
393
- structs: Struct[]
394
- projectArtifact: ProjectArtifact
395
-
396
- readonly contractsRootDir: string
397
- readonly artifactsRootDir: string
398
-
399
- static currentProject: Project
400
-
401
- static readonly importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg')
402
- static readonly abstractContractMatcher = new TypedMatcher<SourceKind>(
403
- '^Abstract Contract ([A-Z][a-zA-Z0-9]*)',
404
- SourceKind.AbstractContract
405
- )
406
- static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract)
407
- static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface)
408
- static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script)
409
- static readonly structMatcher = new TypedMatcher('struct ([A-Z][a-zA-Z0-9]*)', SourceKind.Struct)
410
- static readonly matchers = [
411
- Project.abstractContractMatcher,
412
- Project.contractMatcher,
413
- Project.interfaceMatcher,
414
- Project.scriptMatcher,
415
- Project.structMatcher
416
- ]
417
-
418
- static buildProjectArtifact(
419
- fullNodeVersion: string,
420
- sourceInfos: SourceInfo[],
421
- contracts: Map<string, Compiled<Contract>>,
422
- scripts: Map<string, Compiled<Script>>,
423
- compilerOptions: node.CompilerOptions
424
- ): ProjectArtifact {
425
- const files: Map<string, CodeInfo> = new Map()
426
- contracts.forEach((c) => {
427
- files.set(c.artifact.name, {
428
- sourceFile: c.sourceInfo.contractRelativePath,
429
- sourceCodeHash: c.sourceInfo.sourceCodeHash,
430
- bytecodeDebugPatch: c.artifact.bytecodeDebugPatch,
431
- codeHashDebug: c.artifact.codeHashDebug,
432
- warnings: c.warnings
433
- })
434
- })
435
- scripts.forEach((s) => {
436
- files.set(s.artifact.name, {
437
- sourceFile: s.sourceInfo.contractRelativePath,
438
- sourceCodeHash: s.sourceInfo.sourceCodeHash,
439
- bytecodeDebugPatch: s.artifact.bytecodeDebugPatch,
440
- codeHashDebug: '',
441
- warnings: s.warnings
442
- })
443
- })
444
- const compiledSize = contracts.size + scripts.size
445
- sourceInfos.slice(compiledSize).forEach((c) => {
446
- files.set(c.name, {
447
- sourceFile: c.contractRelativePath,
448
- sourceCodeHash: c.sourceCodeHash,
449
- bytecodeDebugPatch: '',
450
- codeHashDebug: '',
451
- warnings: []
452
- })
453
- })
454
- return new ProjectArtifact(fullNodeVersion, compilerOptions, files)
455
- }
456
-
457
- private constructor(
458
- contractsRootDir: string,
459
- artifactsRootDir: string,
460
- sourceInfos: SourceInfo[],
461
- contracts: Map<string, Compiled<Contract>>,
462
- scripts: Map<string, Compiled<Script>>,
463
- structs: Struct[],
464
- errorOnWarnings: boolean,
465
- projectArtifact: ProjectArtifact
466
- ) {
467
- this.contractsRootDir = contractsRootDir
468
- this.artifactsRootDir = artifactsRootDir
469
- this.sourceInfos = sourceInfos
470
- this.contracts = contracts
471
- this.scripts = scripts
472
- this.structs = structs
473
- this.projectArtifact = projectArtifact
474
-
475
- if (errorOnWarnings) {
476
- Project.checkCompilerWarnings(
477
- [
478
- ...[...contracts.entries()].map((c) => c[1].warnings).flat(),
479
- ...[...scripts.entries()].map((s) => s[1].warnings).flat()
480
- ],
481
- errorOnWarnings
482
- )
483
- }
484
- }
485
-
486
- static checkCompilerWarnings(warnings: string[], errorOnWarnings: boolean): void {
487
- if (warnings.length !== 0) {
488
- const prefixPerWarning = ' - '
489
- const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
490
- const output = `Compilation warnings:\n` + warningString + '\n'
491
- if (errorOnWarnings) {
492
- throw new Error(output)
493
- } else {
494
- console.log(output)
495
- }
496
- }
497
- }
498
-
499
- static contract(name: string): Contract {
500
- const contract = Project.currentProject.contracts.get(name)
501
- if (typeof contract === 'undefined') {
502
- throw new Error(`Contract "${name}" does not exist`)
503
- }
504
- return contract.artifact
505
- }
506
-
507
- static script(name: string): Script {
508
- const script = Project.currentProject.scripts.get(name)
509
- if (typeof script === 'undefined') {
510
- throw new Error(`Script "${name}" does not exist`)
511
- }
512
- return script.artifact
513
- }
514
-
515
- private static async loadStructs(artifactsRootDir: string): Promise<Struct[]> {
516
- const filePath = path.join(artifactsRootDir, 'structs.ral.json')
517
- if (!fs.existsSync(filePath)) return []
518
- const content = await fsPromises.readFile(filePath)
519
- const json = JSON.parse(content.toString())
520
- if (!Array.isArray(json)) {
521
- throw Error(`Invalid structs JSON: ${content}`)
522
- }
523
- return Array.from(json).map((item) => Struct.fromJson(item))
524
- }
525
-
526
- private async saveStructsToFile(): Promise<void> {
527
- if (this.structs.length === 0) return
528
- const structs = this.structs.map((s) => s.toJson())
529
- const filePath = path.join(this.artifactsRootDir, 'structs.ral.json')
530
- return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2))
531
- }
532
-
533
- private async saveArtifactsToFile(
534
- projectRootDir: string,
535
- skipSaveArtifacts: boolean,
536
- changedSources: SourceInfo[]
537
- ): Promise<void> {
538
- const artifactsRootDir = this.artifactsRootDir
539
- const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
540
- const artifactPath = compiled.sourceInfo.getArtifactPath(artifactsRootDir)
541
- const dirname = path.dirname(artifactPath)
542
- if (!fs.existsSync(dirname)) {
543
- fs.mkdirSync(dirname, { recursive: true })
544
- }
545
- return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
546
- }
547
- for (const [_, contract] of this.contracts) {
548
- if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) {
549
- await saveToFile(contract)
550
- }
551
- }
552
- for (const [_, script] of this.scripts) {
553
- await saveToFile(script)
554
- }
555
- await this.saveStructsToFile()
556
- await this.saveProjectArtifact(projectRootDir, skipSaveArtifacts, changedSources)
557
- }
558
-
559
- private async saveProjectArtifact(projectRootDir: string, skipSaveArtifacts: boolean, changedSources: SourceInfo[]) {
560
- if (skipSaveArtifacts) {
561
- // we should not update the `codeHashDebug` if the `skipSaveArtifacts` is enabled
562
- const prevProjectArtifact = await ProjectArtifact.from(projectRootDir)
563
- if (prevProjectArtifact !== undefined) {
564
- for (const [name, info] of this.projectArtifact.infos) {
565
- if (changedSources.find((s) => s.name === name) === undefined) {
566
- const prevInfo = prevProjectArtifact.infos.get(name)
567
- info.bytecodeDebugPatch = prevInfo?.bytecodeDebugPatch ?? info.bytecodeDebugPatch
568
- info.codeHashDebug = prevInfo?.codeHashDebug ?? info.codeHashDebug
569
- }
570
- }
571
- }
572
- }
573
- await this.projectArtifact.saveToFile(projectRootDir)
574
- }
575
-
576
- contractByCodeHash(codeHash: string): Contract {
577
- const contract = [...this.contracts.values()].find(
578
- (c) => c.artifact.codeHash === codeHash || c.artifact.codeHashDebug == codeHash
579
- )
580
- if (typeof contract === 'undefined') {
581
- throw new Error(`Unknown code with code hash: ${codeHash}`)
582
- }
583
- return contract.artifact
584
- }
585
-
586
- private static async getCompileResult(
587
- provider: NodeProvider,
588
- compilerOptions: node.CompilerOptions,
589
- sources: SourceInfo[]
590
- ): Promise<CompileProjectResult> {
591
- try {
592
- const sourceStr = sources.map((f) => f.sourceCode).join('\n')
593
- return await provider.contracts.postContractsCompileProject({
594
- code: sourceStr,
595
- compilerOptions: compilerOptions
596
- })
597
- } catch (error) {
598
- if (!(error instanceof Error)) {
599
- throw error
600
- }
601
-
602
- const parsed = parseError(error.message)
603
- if (!parsed) {
604
- throw error
605
- }
606
-
607
- const sourceInfo = findSourceInfoAtLineNumber(sources, parsed.lineStart)
608
- if (!sourceInfo) {
609
- throw error
610
- }
611
-
612
- const shiftIndex = parsed.lineStart - sourceInfo.startIndex + 1
613
- const newError = parsed.reformat(shiftIndex, sourceInfo.sourceInfo.contractRelativePath)
614
- throw new Error(newError)
615
- }
616
- }
617
-
618
- private static async compile(
619
- fullNodeVersion: string,
620
- provider: NodeProvider,
621
- sourceInfos: SourceInfo[],
622
- projectRootDir: string,
623
- contractsRootDir: string,
624
- artifactsRootDir: string,
625
- errorOnWarnings: boolean,
626
- compilerOptions: node.CompilerOptions,
627
- changedSources: SourceInfo[],
628
- skipSaveArtifacts = false
629
- ): Promise<Project> {
630
- const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => {
631
- if (acc.find((info) => info.sourceCodeHash === sourceInfo.sourceCodeHash) === undefined) {
632
- acc.push(sourceInfo)
633
- }
634
- return acc
635
- }, [])
636
-
637
- const result = await Project.getCompileResult(provider, compilerOptions, removeDuplicates)
638
- const contracts = new Map<string, Compiled<Contract>>()
639
- const scripts = new Map<string, Compiled<Script>>()
640
- const structs = result.structs === undefined ? [] : result.structs.map((item) => Struct.fromStructSig(item))
641
- result.contracts.forEach((contractResult) => {
642
- const sourceInfo = sourceInfos.find(
643
- (sourceInfo) => sourceInfo.type === SourceKind.Contract && sourceInfo.name === contractResult.name
644
- )
645
- if (sourceInfo === undefined) {
646
- // this should never happen
647
- throw new Error(`SourceInfo does not exist for contract ${contractResult.name}`)
648
- }
649
- const contract = Contract.fromCompileResult(contractResult, structs)
650
- contracts.set(contract.name, new Compiled(sourceInfo, contract, contractResult.warnings))
651
- })
652
- result.scripts.forEach((scriptResult) => {
653
- const sourceInfo = sourceInfos.find(
654
- (sourceInfo) => sourceInfo.type === SourceKind.Script && sourceInfo.name === scriptResult.name
655
- )
656
- if (sourceInfo === undefined) {
657
- // this should never happen
658
- throw new Error(`SourceInfo does not exist for script ${scriptResult.name}`)
659
- }
660
- const script = Script.fromCompileResult(scriptResult, structs)
661
- scripts.set(script.name, new Compiled(sourceInfo, script, scriptResult.warnings))
662
- })
663
- const projectArtifact = Project.buildProjectArtifact(
664
- fullNodeVersion,
665
- sourceInfos,
666
- contracts,
667
- scripts,
668
- compilerOptions
669
- )
670
- const project = new Project(
671
- contractsRootDir,
672
- artifactsRootDir,
673
- sourceInfos,
674
- contracts,
675
- scripts,
676
- structs,
677
- errorOnWarnings,
678
- projectArtifact
679
- )
680
- await project.saveArtifactsToFile(projectRootDir, skipSaveArtifacts, changedSources)
681
- return project
682
- }
683
-
684
- private static async loadArtifacts(
685
- provider: NodeProvider,
686
- sourceInfos: SourceInfo[],
687
- projectRootDir: string,
688
- contractsRootDir: string,
689
- artifactsRootDir: string,
690
- errorOnWarnings: boolean,
691
- compilerOptions: node.CompilerOptions
692
- ): Promise<Project> {
693
- const projectArtifact = await ProjectArtifact.from(projectRootDir)
694
- if (projectArtifact === undefined) {
695
- throw Error('Failed to load project artifact')
696
- }
697
- try {
698
- const contracts = new Map<string, Compiled<Contract>>()
699
- const scripts = new Map<string, Compiled<Script>>()
700
- const structs = await Project.loadStructs(artifactsRootDir)
701
- for (const sourceInfo of sourceInfos) {
702
- const info = projectArtifact.infos.get(sourceInfo.name)
703
- if (typeof info === 'undefined') {
704
- throw Error(`Unable to find project info for ${sourceInfo.name}, please rebuild the project`)
705
- }
706
- const warnings = info.warnings
707
- const artifactDir = sourceInfo.getArtifactPath(artifactsRootDir)
708
- if (sourceInfo.type === SourceKind.Contract) {
709
- const artifact = await Contract.fromArtifactFile(
710
- artifactDir,
711
- info.bytecodeDebugPatch,
712
- info.codeHashDebug,
713
- structs
714
- )
715
- contracts.set(artifact.name, new Compiled(sourceInfo, artifact, warnings))
716
- } else if (sourceInfo.type === SourceKind.Script) {
717
- const artifact = await Script.fromArtifactFile(artifactDir, info.bytecodeDebugPatch, structs)
718
- scripts.set(artifact.name, new Compiled(sourceInfo, artifact, warnings))
719
- }
720
- }
721
-
722
- return new Project(
723
- contractsRootDir,
724
- artifactsRootDir,
725
- sourceInfos,
726
- contracts,
727
- scripts,
728
- structs,
729
- errorOnWarnings,
730
- projectArtifact
731
- )
732
- } catch (error) {
733
- console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
734
- return Project.compile(
735
- projectArtifact.fullNodeVersion,
736
- provider,
737
- sourceInfos,
738
- projectRootDir,
739
- contractsRootDir,
740
- artifactsRootDir,
741
- errorOnWarnings,
742
- compilerOptions,
743
- sourceInfos
744
- )
745
- }
746
- }
747
-
748
- private static getImportSourcePath(projectRootDir: string, importPath: string): string {
749
- const parts = importPath.split('/')
750
- if (parts.length > 1 && parts[0] === 'std') {
751
- const currentDir = path.dirname(__filename)
752
- return path.join(...[currentDir, '..', '..', '..', importPath])
753
- }
754
- let moduleDir = projectRootDir
755
- while (true) {
756
- const expectedPath = path.join(...[moduleDir, 'node_modules', importPath])
757
- if (fs.existsSync(expectedPath)) {
758
- return expectedPath
759
- }
760
- const oldModuleDir = moduleDir
761
- moduleDir = path.join(moduleDir, '..')
762
- if (oldModuleDir === moduleDir) {
763
- throw new Error(`Specified import file does not exist: ${importPath}`)
764
- }
765
- }
766
- }
767
-
768
- private static async handleImports(
769
- projectRootDir: string,
770
- contractRootDir: string,
771
- sourceStr: string,
772
- importsCache: string[]
773
- ): Promise<[string, SourceInfo[]]> {
774
- const localImportsCache: string[] = []
775
- const result = sourceStr.replace(Project.importRegex, (match) => {
776
- localImportsCache.push(match)
777
- return ''
778
- })
779
- const externalSourceInfos: SourceInfo[] = []
780
- for (const myImport of localImportsCache) {
781
- const originImportPath = myImport.slice(8, -1)
782
- const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral'
783
- if (!importsCache.includes(importPath)) {
784
- importsCache.push(importPath)
785
- const sourcePath = Project.getImportSourcePath(projectRootDir, importPath)
786
- const sourceInfos = await Project.loadSourceFile(
787
- projectRootDir,
788
- contractRootDir,
789
- sourcePath,
790
- importsCache,
791
- true
792
- )
793
- externalSourceInfos.push(...sourceInfos)
794
- }
795
- }
796
- return [result, externalSourceInfos]
797
- }
798
-
799
- private static async loadSourceFile(
800
- projectRootDir: string,
801
- contractsRootDir: string,
802
- sourcePath: string,
803
- importsCache: string[],
804
- isExternal: boolean
805
- ): Promise<SourceInfo[]> {
806
- const contractRelativePath = path.relative(contractsRootDir, sourcePath)
807
- if (!sourcePath.endsWith('.ral')) {
808
- throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`)
809
- }
810
-
811
- const sourceBuffer = await fsPromises.readFile(sourcePath)
812
- const [sourceStr, externalSourceInfos] = await Project.handleImports(
813
- projectRootDir,
814
- contractsRootDir,
815
- sourceBuffer.toString(),
816
- importsCache
817
- )
818
- if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
819
- throw new Error(`Invalid import statements, source: ${sourcePath}`)
820
- }
821
- const sourceInfos = externalSourceInfos
822
- for (const matcher of this.matchers) {
823
- const results = sourceStr.matchAll(matcher.matcher)
824
- for (const result of results) {
825
- const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal)
826
- sourceInfos.push(sourceInfo)
827
- }
828
- }
829
- return sourceInfos
830
- }
831
-
832
- private static async loadSourceFiles(projectRootDir: string, contractsRootDir: string): Promise<SourceInfo[]> {
833
- const importsCache: string[] = []
834
- const sourceInfos: SourceInfo[] = []
835
- const loadDir = async function (dirPath: string): Promise<void> {
836
- const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
837
- for (const dirent of dirents) {
838
- if (dirent.isFile()) {
839
- const sourcePath = path.join(dirPath, dirent.name)
840
- const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false)
841
- sourceInfos.push(...infos)
842
- } else {
843
- const newPath = path.join(dirPath, dirent.name)
844
- await loadDir(newPath)
845
- }
846
- }
847
- }
848
- await loadDir(contractsRootDir)
849
- const contractAndScriptSize = sourceInfos.filter(
850
- (f) => f.type === SourceKind.Contract || f.type === SourceKind.Script
851
- ).length
852
- if (sourceInfos.length === 0 || contractAndScriptSize === 0) {
853
- throw new Error('Project have no source files')
854
- }
855
- return sourceInfos.sort((a, b) => a.type - b.type)
856
- }
857
-
858
- static readonly DEFAULT_CONTRACTS_DIR = 'contracts'
859
- static readonly DEFAULT_ARTIFACTS_DIR = 'artifacts'
860
-
861
- static async build(
862
- compilerOptionsPartial: Partial<CompilerOptions> = {},
863
- projectRootDir = '.',
864
- contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
865
- artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR,
866
- defaultFullNodeVersion: string | undefined = undefined,
867
- skipSaveArtifacts = false
868
- ): Promise<void> {
869
- const provider = getCurrentNodeProvider()
870
- const fullNodeVersion = defaultFullNodeVersion ?? (await provider.infos.getInfosVersion()).version
871
- const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
872
- const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
873
- const projectArtifact = await ProjectArtifact.from(projectRootDir)
874
- const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles
875
- if (
876
- projectArtifact === undefined ||
877
- projectArtifact.needToReCompile(nodeCompilerOptions, fullNodeVersion) ||
878
- changedSources.length > 0
879
- ) {
880
- if (fs.existsSync(artifactsRootDir)) {
881
- removeOldArtifacts(artifactsRootDir, sourceFiles)
882
- }
883
- console.log(`Compiling contracts in folder "${contractsRootDir}"`)
884
- Project.currentProject = await Project.compile(
885
- fullNodeVersion,
886
- provider,
887
- sourceFiles,
888
- projectRootDir,
889
- contractsRootDir,
890
- artifactsRootDir,
891
- errorOnWarnings,
892
- nodeCompilerOptions,
893
- changedSources,
894
- skipSaveArtifacts
895
- )
896
- }
897
- // we need to reload those contracts that did not regenerate bytecode
898
- Project.currentProject = await Project.loadArtifacts(
899
- provider,
900
- sourceFiles,
901
- projectRootDir,
902
- contractsRootDir,
903
- artifactsRootDir,
904
- errorOnWarnings,
905
- nodeCompilerOptions
906
- )
907
- }
908
- }
909
-
910
140
  export abstract class Artifact {
911
141
  readonly version: string
912
142
  readonly name: string
@@ -986,7 +216,7 @@ export class Contract extends Artifact {
986
216
  this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
987
217
  this.codeHashDebug = codeHashDebug
988
218
 
989
- this.decodedMethods = contract.contractCodec.decodeContract(Buffer.from(bytecode, 'hex')).methods
219
+ this.decodedMethods = contract.contractCodec.decodeContract(hexToBinUnsafe(bytecode)).methods
990
220
  }
991
221
 
992
222
  publicFunctions(): FunctionSig[] {
@@ -1202,11 +432,9 @@ export class Contract extends Artifact {
1202
432
 
1203
433
  static fromApiContractState(
1204
434
  state: node.ContractState,
1205
- getContractByCodeHash?: (codeHash: string) => Contract
435
+ getContractByCodeHash: (codeHash: string) => Contract
1206
436
  ): ContractState {
1207
- const contract = getContractByCodeHash
1208
- ? getContractByCodeHash(state.codeHash)
1209
- : Project.currentProject.contractByCodeHash(state.codeHash)
437
+ const contract = getContractByCodeHash(state.codeHash)
1210
438
  return contract.fromApiContractState(state)
1211
439
  }
1212
440
 
@@ -1228,7 +456,7 @@ export class Contract extends Artifact {
1228
456
  event: node.ContractEventByTxId,
1229
457
  codeHash: string | undefined,
1230
458
  txId: string,
1231
- getContractByCodeHash?: (codeHash: string) => Contract
459
+ getContractByCodeHash: (codeHash: string) => Contract
1232
460
  ): ContractEvent {
1233
461
  let fields: Fields
1234
462
  let name: string
@@ -1240,9 +468,7 @@ export class Contract extends Artifact {
1240
468
  fields = fromApiEventFields(event.fields, Contract.ContractDestroyedEvent, true)
1241
469
  name = Contract.ContractDestroyedEvent.name
1242
470
  } else {
1243
- const contract = getContractByCodeHash
1244
- ? getContractByCodeHash(codeHash!)
1245
- : Project.currentProject.contractByCodeHash(codeHash!)
471
+ const contract = getContractByCodeHash(codeHash!)
1246
472
  const eventSig = contract.eventsSig[event.eventIndex]
1247
473
  fields = fromApiEventFields(event.fields, eventSig)
1248
474
  name = eventSig.name
@@ -1261,7 +487,8 @@ export class Contract extends Artifact {
1261
487
  fromApiTestContractResult(
1262
488
  methodName: string,
1263
489
  result: node.TestContractResult,
1264
- txId: string
490
+ txId: string,
491
+ getContractByCodeHash: (codeHash: string) => Contract
1265
492
  ): TestContractResult<unknown> {
1266
493
  const methodIndex = this.functions.findIndex((sig) => sig.name === methodName)
1267
494
  const returnTypes = this.functions[`${methodIndex}`].returnTypes
@@ -1276,9 +503,9 @@ export class Contract extends Artifact {
1276
503
  contractAddress: result.address,
1277
504
  returns: returns,
1278
505
  gasUsed: result.gasUsed,
1279
- contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract)),
506
+ contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract, getContractByCodeHash)),
1280
507
  txOutputs: result.txOutputs.map(fromApiOutput),
1281
- events: Contract.fromApiEvents(result.events, addressToCodeHash, txId),
508
+ events: Contract.fromApiEvents(result.events, addressToCodeHash, txId, getContractByCodeHash),
1282
509
  debugMessages: result.debugMessages
1283
510
  }
1284
511
  }
@@ -1322,7 +549,7 @@ export class Contract extends Artifact {
1322
549
  events: node.ContractEventByTxId[],
1323
550
  addressToCodeHash: Map<string, string>,
1324
551
  txId: string,
1325
- getContractByCodeHash?: (codeHash: string) => Contract
552
+ getContractByCodeHash: (codeHash: string) => Contract
1326
553
  ): ContractEvent[] {
1327
554
  return events.map((event) => {
1328
555
  const contractAddress = event.contractAddress
@@ -1356,7 +583,7 @@ export class Contract extends Artifact {
1356
583
  result: node.CallContractResult,
1357
584
  txId: string,
1358
585
  methodIndex: number,
1359
- getContractByCodeHash?: (codeHash: string) => Contract
586
+ getContractByCodeHash: (codeHash: string) => Contract
1360
587
  ): CallContractResult<unknown> {
1361
588
  const returnTypes = this.functions[`${methodIndex}`].returnTypes
1362
589
  const callResult = tryGetCallResult(result)
@@ -1977,7 +1204,7 @@ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; co
1977
1204
  fieldLength: immFields + mutFields + 1, // parentContractId
1978
1205
  methods: [loadImmFieldByIndex, loadMutFieldByIndex, storeMutFieldByIndex, destroy]
1979
1206
  }
1980
- const bytecode = contract.contractCodec.encode(contract.toHalfDecoded(c))
1207
+ const bytecode = contract.contractCodec.encodeContract(c)
1981
1208
  const codeHash = blake.blake2b(bytecode, undefined, 32)
1982
1209
  return { bytecode: binToHex(bytecode), codeHash: binToHex(codeHash) }
1983
1210
  }
@@ -2042,7 +1269,8 @@ export async function testMethod<
2042
1269
  >(
2043
1270
  factory: ContractFactory<I, F>,
2044
1271
  methodName: string,
2045
- params: Optional<TestContractParams<F, A, M>, 'testArgs' | 'initialFields'>
1272
+ params: Optional<TestContractParams<F, A, M>, 'testArgs' | 'initialFields'>,
1273
+ getContractByCodeHash: (codeHash: string) => Contract
2046
1274
  ): Promise<TestContractResult<R, M>> {
2047
1275
  const txId = params?.txId ?? randomTxId()
2048
1276
  const contract = factory.contract
@@ -2061,7 +1289,7 @@ export async function testMethod<
2061
1289
  })
2062
1290
  const apiResult = await getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
2063
1291
  const maps = existingContractsToMaps(contract, address, group, apiResult, initialMaps)
2064
- const testResult = contract.fromApiTestContractResult(methodName, apiResult, txId)
1292
+ const testResult = contract.fromApiTestContractResult(methodName, apiResult, txId, getContractByCodeHash)
2065
1293
  contract.printDebugMessages(methodName, testResult.debugMessages)
2066
1294
  return {
2067
1295
  ...testResult,
@@ -2362,7 +1590,7 @@ export async function callMethod<I extends ContractInstance, F extends Fields, A
2362
1590
  instance: ContractInstance,
2363
1591
  methodName: string,
2364
1592
  params: Optional<CallContractParams<A>, 'args'>,
2365
- getContractByCodeHash?: (codeHash: string) => Contract
1593
+ getContractByCodeHash: (codeHash: string) => Contract
2366
1594
  ): Promise<CallContractResult<R>> {
2367
1595
  const methodIndex = contract.contract.getMethodIndex(methodName)
2368
1596
  const txId = params?.txId ?? randomTxId()
@@ -2382,7 +1610,7 @@ export async function multicallMethods<I extends ContractInstance, F extends Fie
2382
1610
  contract: ContractFactory<I, F>,
2383
1611
  instance: ContractInstance,
2384
1612
  calls: Record<string, Optional<CallContractParams<any>, 'args'>>,
2385
- getContractByCodeHash?: (codeHash: string) => Contract
1613
+ getContractByCodeHash: (codeHash: string) => Contract
2386
1614
  ): Promise<Record<string, CallContractResult<any>>> {
2387
1615
  const callEntries = Object.entries(calls)
2388
1616
  const callsParams = callEntries.map((entry) => {