@alephium/web3 0.2.0-test.1 → 0.2.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 (128) hide show
  1. package/.eslintignore +2 -2
  2. package/README.md +2 -135
  3. package/dist/alephium-web3.min.js +1 -1
  4. package/dist/alephium-web3.min.js.LICENSE.txt +0 -17
  5. package/dist/alephium-web3.min.js.map +1 -1
  6. package/dist/src/api/api-alephium.d.ts +116 -18
  7. package/dist/src/api/api-alephium.js +146 -81
  8. package/dist/src/api/api-explorer.d.ts +178 -51
  9. package/dist/src/api/api-explorer.js +172 -37
  10. package/dist/src/api/index.d.ts +40 -5
  11. package/dist/src/api/index.js +115 -7
  12. package/dist/src/api/types.d.ts +23 -0
  13. package/dist/src/api/types.js +235 -0
  14. package/dist/src/api/utils.d.ts +6 -0
  15. package/dist/{scripts/rename-gitignore.js → src/api/utils.js} +11 -6
  16. package/dist/src/contract/contract.d.ts +69 -55
  17. package/dist/src/contract/contract.js +241 -387
  18. package/dist/src/contract/events.d.ts +4 -4
  19. package/dist/src/contract/events.js +2 -1
  20. package/dist/src/contract/index.js +5 -1
  21. package/dist/src/contract/ralph.d.ts +5 -4
  22. package/dist/src/contract/ralph.js +27 -1
  23. package/dist/src/global.d.ts +6 -2
  24. package/dist/src/global.js +19 -3
  25. package/dist/src/index.d.ts +2 -1
  26. package/dist/src/index.js +23 -2
  27. package/dist/src/signer/index.d.ts +0 -1
  28. package/dist/src/signer/index.js +5 -2
  29. package/dist/src/signer/signer.d.ts +59 -60
  30. package/dist/src/signer/signer.js +99 -70
  31. package/dist/src/transaction/index.d.ts +0 -1
  32. package/dist/src/transaction/index.js +5 -2
  33. package/dist/src/transaction/status.d.ts +2 -1
  34. package/dist/src/transaction/status.js +2 -1
  35. package/dist/src/utils/bs58.d.ts +1 -0
  36. package/dist/src/utils/bs58.js +13 -1
  37. package/dist/src/utils/index.d.ts +0 -1
  38. package/dist/src/utils/index.js +5 -2
  39. package/dist/src/utils/subscription.d.ts +0 -2
  40. package/dist/src/utils/subscription.js +0 -2
  41. package/dist/src/utils/utils.d.ts +4 -9
  42. package/dist/src/utils/utils.js +20 -24
  43. package/jest-config.json +11 -0
  44. package/package.json +11 -45
  45. package/src/api/api-alephium.ts +163 -26
  46. package/src/api/api-explorer.ts +247 -54
  47. package/src/api/index.ts +140 -6
  48. package/src/api/types.ts +229 -0
  49. package/{scripts/rename-gitignore.js → src/api/utils.ts} +7 -6
  50. package/src/contract/contract.ts +405 -432
  51. package/src/contract/events.ts +6 -5
  52. package/src/contract/ralph.ts +29 -4
  53. package/src/global.ts +23 -3
  54. package/src/index.ts +7 -1
  55. package/src/signer/index.ts +0 -1
  56. package/src/signer/signer.ts +165 -135
  57. package/src/transaction/index.ts +0 -1
  58. package/src/transaction/status.ts +5 -2
  59. package/src/utils/bs58.ts +11 -0
  60. package/src/utils/index.ts +0 -1
  61. package/src/utils/subscription.ts +0 -4
  62. package/src/utils/utils.ts +11 -19
  63. package/webpack.config.js +3 -0
  64. package/.eslintrc.json +0 -21
  65. package/LICENSE +0 -165
  66. package/contracts/add/add.ral +0 -13
  67. package/contracts/greeter/greeter.ral +0 -7
  68. package/contracts/greeter/greeter_interface.ral +0 -4
  69. package/contracts/greeter_main.ral +0 -7
  70. package/contracts/main.ral +0 -4
  71. package/contracts/sub/sub.ral +0 -10
  72. package/contracts/test/metadata.ral +0 -18
  73. package/contracts/test/warnings.ral +0 -8
  74. package/dev/user.conf +0 -29
  75. package/dist/scripts/create-project.d.ts +0 -2
  76. package/dist/scripts/create-project.js +0 -125
  77. package/dist/scripts/rename-gitignore.d.ts +0 -1
  78. package/dist/scripts/start-devnet.d.ts +0 -1
  79. package/dist/scripts/start-devnet.js +0 -131
  80. package/dist/scripts/stop-devnet.d.ts +0 -1
  81. package/dist/scripts/stop-devnet.js +0 -32
  82. package/dist/src/signer/node-wallet.d.ts +0 -11
  83. package/dist/src/signer/node-wallet.js +0 -57
  84. package/dist/src/test/index.d.ts +0 -6
  85. package/dist/src/test/index.js +0 -41
  86. package/dist/src/test/privatekey-wallet.d.ts +0 -11
  87. package/dist/src/test/privatekey-wallet.js +0 -68
  88. package/dist/src/transaction/sign-verify.d.ts +0 -2
  89. package/dist/src/transaction/sign-verify.js +0 -58
  90. package/dist/src/utils/password-crypto.d.ts +0 -2
  91. package/dist/src/utils/password-crypto.js +0 -69
  92. package/gitignore +0 -9
  93. package/scripts/create-project.ts +0 -137
  94. package/scripts/start-devnet.js +0 -141
  95. package/scripts/stop-devnet.js +0 -32
  96. package/src/contract/ralph.test.ts +0 -178
  97. package/src/fixtures/address.json +0 -36
  98. package/src/fixtures/balance.json +0 -9
  99. package/src/fixtures/self-clique.json +0 -19
  100. package/src/fixtures/transaction.json +0 -13
  101. package/src/fixtures/transactions.json +0 -179
  102. package/src/signer/fixtures/genesis.json +0 -26
  103. package/src/signer/fixtures/wallets.json +0 -26
  104. package/src/signer/node-wallet.ts +0 -65
  105. package/src/test/index.ts +0 -31
  106. package/src/test/privatekey-wallet.ts +0 -57
  107. package/src/transaction/sign-verify.test.ts +0 -50
  108. package/src/transaction/sign-verify.ts +0 -39
  109. package/src/utils/address.test.ts +0 -47
  110. package/src/utils/djb2.test.ts +0 -35
  111. package/src/utils/password-crypto.test.ts +0 -27
  112. package/src/utils/password-crypto.ts +0 -77
  113. package/src/utils/utils.test.ts +0 -161
  114. package/templates/base/README.md +0 -34
  115. package/templates/base/package.json +0 -35
  116. package/templates/base/src/greeter.ts +0 -42
  117. package/templates/base/tsconfig.json +0 -19
  118. package/templates/react/README.md +0 -34
  119. package/templates/react/config-overrides.js +0 -18
  120. package/templates/react/package.json +0 -66
  121. package/templates/react/src/App.tsx +0 -42
  122. package/templates/react/src/artifacts/greeter.ral.json +0 -26
  123. package/templates/react/src/artifacts/greeter_main.ral.json +0 -22
  124. package/templates/shared/.eslintrc.json +0 -12
  125. package/templates/shared/scripts/header.js +0 -0
  126. package/test/contract.test.ts +0 -213
  127. package/test/events.test.ts +0 -141
  128. package/test/transaction.test.ts +0 -73
@@ -17,20 +17,41 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import { Buffer } from 'buffer/'
20
- import * as cryptojs from 'crypto-js'
21
- import * as crypto from 'crypto'
20
+ import { webcrypto as crypto } from 'crypto'
22
21
  import fs from 'fs'
23
22
  import { promises as fsPromises } from 'fs'
24
- import { node, NodeProvider } from '../api'
25
- import { SignDeployContractTxParams, SignExecuteScriptTxParams, SignerWithNodeProvider } from '../signer'
23
+ import {
24
+ fromApiArray,
25
+ fromApiNumber256,
26
+ toApiNumber256,
27
+ NamedVals,
28
+ node,
29
+ NodeProvider,
30
+ Number256,
31
+ toApiToken,
32
+ toApiVal,
33
+ Token,
34
+ Val,
35
+ fromApiTokens,
36
+ fromApiVals
37
+ } from '../api'
38
+ import {
39
+ SignDeployContractTxParams,
40
+ SignExecuteScriptTxParams,
41
+ SignerProvider,
42
+ SignExecuteScriptTxResult,
43
+ SignDeployContractTxResult
44
+ } from '../signer'
26
45
  import * as ralph from './ralph'
27
46
  import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
28
- import { CompileContractResult, CompileScriptResult } from '../api/api-alephium'
29
47
  import { getCurrentNodeProvider } from '../global'
48
+ import { web3 } from '..'
30
49
 
31
- type FieldsSig = node.FieldsSig
32
- type EventSig = node.EventSig
33
- type FunctionSig = node.FunctionSig
50
+ export type FieldsSig = node.FieldsSig
51
+ export type EventSig = node.EventSig
52
+ export type FunctionSig = node.FunctionSig
53
+ export type Fields = NamedVals
54
+ export type Arguments = NamedVals
34
55
 
35
56
  enum SourceType {
36
57
  Contract = 0,
@@ -39,54 +60,57 @@ enum SourceType {
39
60
  Interface = 3
40
61
  }
41
62
 
42
- export type CompilerOptions = {
63
+ export type CompilerOptions = node.CompilerOptions & {
43
64
  errorOnWarnings: boolean
44
- ignoreUnusedConstantsWarnings: boolean
45
65
  }
46
66
 
47
- export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = {
48
- errorOnWarnings: true,
49
- ignoreUnusedConstantsWarnings: true
67
+ export const DEFAULT_NODE_COMPILER_OPTIONS: node.CompilerOptions = {
68
+ ignoreUnusedConstantsWarnings: false,
69
+ ignoreUnusedVariablesWarnings: false,
70
+ ignoreUnusedFieldsWarnings: false,
71
+ ignoreUnusedPrivateFunctionsWarnings: false,
72
+ ignoreUpdateFieldsCheckWarnings: false,
73
+ ignoreExternalCallCheckWarnings: false
50
74
  }
51
75
 
76
+ export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { errorOnWarnings: true, ...DEFAULT_NODE_COMPILER_OPTIONS }
77
+
52
78
  class TypedMatcher<T extends SourceType> {
53
79
  matcher: RegExp
54
80
  type: T
55
- name: string | undefined
56
81
 
57
82
  constructor(pattern: string, type: T) {
58
83
  this.matcher = new RegExp(pattern, 'mg')
59
84
  this.type = type
60
- this.name = undefined
61
85
  }
62
86
 
63
87
  match(str: string): number {
64
- const results = [...str.matchAll(this.matcher)]
65
- if (results.length > 0) {
66
- this.name = results[0][1]
67
- }
68
- return results.length
88
+ const results = str.match(this.matcher)
89
+ return results === null ? 0 : results.length
69
90
  }
70
91
  }
71
92
 
72
93
  class SourceFile {
73
94
  type: SourceType
74
- typeId: string
75
95
  contractPath: string
76
96
  sourceCode: string
77
97
  sourceCodeHash: string
78
98
 
79
- getArtifactPath(artifactsRootPath: string): string {
80
- return artifactsRootPath + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json'
99
+ getArtifactPath(artifactsRootDir: string): string {
100
+ return artifactsRootDir + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json'
81
101
  }
82
102
 
83
- constructor(type: SourceType, typeId: string, sourceCode: string, contractPath: string) {
103
+ constructor(type: SourceType, sourceCode: string, sourceCodeHash: string, contractPath: string) {
84
104
  this.type = type
85
- this.typeId = typeId
86
105
  this.sourceCode = sourceCode
87
- this.sourceCodeHash = cryptojs.SHA256(sourceCode).toString()
106
+ this.sourceCodeHash = sourceCodeHash
88
107
  this.contractPath = contractPath
89
108
  }
109
+
110
+ static async from(type: SourceType, sourceCode: string, contractPath: string): Promise<SourceFile> {
111
+ const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
112
+ return new SourceFile(type, sourceCode, Buffer.from(sourceCodeHash).toString('hex'), contractPath)
113
+ }
90
114
  }
91
115
 
92
116
  class Compiled<T extends Artifact> {
@@ -101,22 +125,49 @@ class Compiled<T extends Artifact> {
101
125
  }
102
126
  }
103
127
 
128
+ type CodeInfo = { sourceCodeHash: string; bytecodeDebugPatch: string; codeHashDebug: string; warnings: string[] }
129
+
104
130
  class ProjectArtifact {
105
131
  static readonly artifactFileName = '.project.json'
106
132
 
107
- infos: Map<string, { sourceCodeHash: string; warnings: string[] }>
133
+ compilerOptionsUsed: node.CompilerOptions
134
+ infos: Map<string, CodeInfo>
135
+
136
+ static checkCompilerOptionsParameter(compilerOptions: node.CompilerOptions): void {
137
+ if (Object.keys(compilerOptions).length != Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
138
+ throw Error(`Not all compiler options are set: ${compilerOptions}`)
139
+ }
140
+
141
+ const combined = { ...compilerOptions, ...DEFAULT_NODE_COMPILER_OPTIONS }
142
+ if (Object.keys(combined).length !== Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
143
+ throw Error(`There are unknown compiler options: ${compilerOptions}`)
144
+ }
145
+ }
108
146
 
109
- constructor(infos: Map<string, { sourceCodeHash: string; warnings: string[] }>) {
147
+ constructor(compilerOptionsUsed: node.CompilerOptions, infos: Map<string, CodeInfo>) {
148
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
149
+ this.compilerOptionsUsed = compilerOptionsUsed
110
150
  this.infos = infos
111
151
  }
112
152
 
113
153
  async saveToFile(rootPath: string): Promise<void> {
114
154
  const filepath = rootPath + '/' + ProjectArtifact.artifactFileName
115
- const content = JSON.stringify(Object.fromEntries(this.infos), null, 2)
155
+ const artifact = { compilerOptionsUsed: this.compilerOptionsUsed, infos: Object.fromEntries(this.infos) }
156
+ const content = JSON.stringify(artifact, null, 2)
116
157
  return fsPromises.writeFile(filepath, content)
117
158
  }
118
159
 
119
- sourceHasChanged(files: SourceFile[]): boolean {
160
+ needToReCompile(compilerOptions: node.CompilerOptions, files: SourceFile[]): boolean {
161
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
162
+
163
+ const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
164
+ const usedOption = this.compilerOptionsUsed[`${key}`]
165
+ return usedOption === inputOption
166
+ })
167
+ if (!optionsMatched) {
168
+ return true
169
+ }
170
+
120
171
  if (files.length !== this.infos.size) {
121
172
  return true
122
173
  }
@@ -126,6 +177,7 @@ class ProjectArtifact {
126
177
  return true
127
178
  }
128
179
  }
180
+
129
181
  return false
130
182
  }
131
183
 
@@ -135,10 +187,10 @@ class ProjectArtifact {
135
187
  return undefined
136
188
  }
137
189
  const content = await fsPromises.readFile(filepath)
138
- const files = new Map(
139
- Object.entries<{ sourceCodeHash: string; warnings: string[] }>(JSON.parse(content.toString()))
140
- )
141
- return new ProjectArtifact(files)
190
+ const json = JSON.parse(content.toString())
191
+ const compilerOptionsUsed = json.compilerOptionsUsed as node.CompilerOptions
192
+ const files = new Map(Object.entries<CodeInfo>(json.infos))
193
+ return new ProjectArtifact(compilerOptionsUsed, files)
142
194
  }
143
195
  }
144
196
 
@@ -146,20 +198,20 @@ export class Project {
146
198
  sourceFiles: SourceFile[]
147
199
  contracts: Compiled<Contract>[]
148
200
  scripts: Compiled<Script>[]
201
+ projectArtifact: ProjectArtifact
149
202
 
150
- readonly contractsRootPath: string
151
- readonly artifactsRootPath: string
152
- readonly nodeProvider: NodeProvider
203
+ readonly contractsRootDir: string
204
+ readonly artifactsRootDir: string
153
205
 
154
206
  static currentProject: Project
155
207
 
156
208
  static readonly abstractContractMatcher = new TypedMatcher<SourceType>(
157
- '^Abstract Contract ([A-Z][a-zA-Z0-9]*)\\(*',
209
+ '^Abstract Contract [A-Z][a-zA-Z0-9]*',
158
210
  SourceType.AbstractContract
159
211
  )
160
- static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)\\(*', SourceType.Contract)
161
- static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*) \\{', SourceType.Interface)
162
- static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)( \\{*|\\(*)', SourceType.Script)
212
+ static readonly contractMatcher = new TypedMatcher('^Contract [A-Z][a-zA-Z0-9]*', SourceType.Contract)
213
+ static readonly interfaceMatcher = new TypedMatcher('^Interface [A-Z][a-zA-Z0-9]* \\{', SourceType.Interface)
214
+ static readonly scriptMatcher = new TypedMatcher('^TxScript [A-Z][a-zA-Z0-9]*', SourceType.Script)
163
215
  static readonly matchers = [
164
216
  Project.abstractContractMatcher,
165
217
  Project.contractMatcher,
@@ -167,39 +219,79 @@ export class Project {
167
219
  Project.scriptMatcher
168
220
  ]
169
221
 
222
+ static buildProjectArtifact(
223
+ sourceFiles: SourceFile[],
224
+ contracts: Compiled<Contract>[],
225
+ scripts: Compiled<Script>[],
226
+ compilerOptions: node.CompilerOptions
227
+ ): ProjectArtifact {
228
+ const files: Map<string, CodeInfo> = new Map()
229
+ contracts.forEach((c) => {
230
+ files.set(c.sourceFile.contractPath, {
231
+ sourceCodeHash: c.sourceFile.sourceCodeHash,
232
+ bytecodeDebugPatch: c.artifact.bytecodeDebugPatch,
233
+ codeHashDebug: c.artifact.codeHashDebug,
234
+ warnings: c.warnings
235
+ })
236
+ })
237
+ scripts.forEach((s) => {
238
+ files.set(s.sourceFile.contractPath, {
239
+ sourceCodeHash: s.sourceFile.sourceCodeHash,
240
+ bytecodeDebugPatch: s.artifact.bytecodeDebugPatch,
241
+ codeHashDebug: '',
242
+ warnings: s.warnings
243
+ })
244
+ })
245
+ const compiledSize = contracts.length + scripts.length
246
+ sourceFiles.slice(compiledSize).forEach((c) => {
247
+ files.set(c.contractPath, {
248
+ sourceCodeHash: c.sourceCodeHash,
249
+ bytecodeDebugPatch: '',
250
+ codeHashDebug: '',
251
+ warnings: []
252
+ })
253
+ })
254
+ return new ProjectArtifact(compilerOptions, files)
255
+ }
256
+
170
257
  private constructor(
171
- provider: NodeProvider,
172
- contractsRootPath: string,
173
- artifactsRootPath: string,
258
+ contractsRootDir: string,
259
+ artifactsRootDir: string,
174
260
  sourceFiles: SourceFile[],
175
261
  contracts: Compiled<Contract>[],
176
- scripts: Compiled<Script>[]
262
+ scripts: Compiled<Script>[],
263
+ errorOnWarnings: boolean,
264
+ projectArtifact: ProjectArtifact
177
265
  ) {
178
- this.nodeProvider = provider
179
- this.contractsRootPath = contractsRootPath
180
- this.artifactsRootPath = artifactsRootPath
266
+ this.contractsRootDir = contractsRootDir
267
+ this.artifactsRootDir = artifactsRootDir
181
268
  this.sourceFiles = sourceFiles
182
269
  this.contracts = contracts
183
270
  this.scripts = scripts
271
+ this.projectArtifact = projectArtifact
272
+
273
+ if (errorOnWarnings) {
274
+ Project.checkCompilerWarnings(
275
+ [...contracts.map((c) => c.warnings).flat(), ...scripts.map((s) => s.warnings).flat()],
276
+ errorOnWarnings
277
+ )
278
+ }
184
279
  }
185
280
 
186
281
  private getContractPath(path: string): string {
187
- return path.startsWith(`./${this.contractsRootPath}`)
282
+ return path.startsWith(`./${this.contractsRootDir}`)
188
283
  ? path.slice(2)
189
- : path.startsWith(this.contractsRootPath)
284
+ : path.startsWith(this.contractsRootDir)
190
285
  ? path
191
- : this.contractsRootPath + '/' + path
286
+ : this.contractsRootDir + '/' + path
192
287
  }
193
288
 
194
- private static checkCompilerWarnings(warnings: string[], compilerOptions: CompilerOptions): void {
195
- const remains = compilerOptions.ignoreUnusedConstantsWarnings
196
- ? warnings.filter((s) => !s.includes('unused constants'))
197
- : warnings
198
- if (remains.length !== 0) {
289
+ static checkCompilerWarnings(warnings: string[], errorOnWarnings: boolean): void {
290
+ if (warnings.length !== 0) {
199
291
  const prefixPerWarning = ' - '
200
- const warningString = prefixPerWarning + remains.join('\n' + prefixPerWarning)
201
- const output = 'Compilation warnings:\n' + warningString + '\n'
202
- if (compilerOptions.errorOnWarnings) {
292
+ const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
293
+ const output = `Compilation warnings:\n` + warningString + '\n'
294
+ if (errorOnWarnings) {
203
295
  throw new Error(output)
204
296
  } else {
205
297
  console.log(output)
@@ -207,102 +299,87 @@ export class Project {
207
299
  }
208
300
  }
209
301
 
210
- static contract(path: string, compilerOptions?: Partial<CompilerOptions>): Contract {
211
- const contractPath = Project.currentProject.getContractPath(path)
212
- const contract = Project.currentProject.contracts.find((c) => c.sourceFile.contractPath === contractPath)
302
+ static contract(name: string): Contract {
303
+ const contract = Project.currentProject.contracts.find((c) => c.artifact.name === name)
213
304
  if (typeof contract === 'undefined') {
214
- throw new Error(`Contract ${contractPath} does not exist`)
305
+ throw new Error(`Contract "${name}" does not exist`)
215
306
  }
216
- Project.checkCompilerWarnings(contract.warnings, { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptions })
217
307
  return contract.artifact
218
308
  }
219
309
 
220
- static script(path: string, compilerOptions?: Partial<CompilerOptions>): Script {
221
- const contractPath = Project.currentProject.getContractPath(path)
222
- const script = Project.currentProject.scripts.find((c) => c.sourceFile.contractPath === contractPath)
310
+ static script(name: string): Script {
311
+ const script = Project.currentProject.scripts.find((c) => c.artifact.name === name)
223
312
  if (typeof script === 'undefined') {
224
- throw new Error(`Script ${contractPath} does not exist`)
313
+ throw new Error(`Script "${name}" does not exist`)
225
314
  }
226
- Project.checkCompilerWarnings(script.warnings, { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptions })
227
315
  return script.artifact
228
316
  }
229
317
 
230
318
  private async saveArtifactsToFile(): Promise<void> {
231
- const artifactsRootPath = this.artifactsRootPath
319
+ const artifactsRootDir = this.artifactsRootDir
232
320
  const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
233
- const artifactPath = compiled.sourceFile.getArtifactPath(artifactsRootPath)
234
- const folder = artifactPath.slice(0, artifactPath.lastIndexOf('/'))
321
+ const artifactDir = compiled.sourceFile.getArtifactPath(artifactsRootDir)
322
+ const folder = artifactDir.slice(0, artifactDir.lastIndexOf('/'))
235
323
  if (!fs.existsSync(folder)) {
236
324
  fs.mkdirSync(folder, { recursive: true })
237
325
  }
238
- return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
326
+ return fsPromises.writeFile(artifactDir, compiled.artifact.toString())
239
327
  }
240
328
  for (const contract of this.contracts) {
241
- await saveToFile(contract)
329
+ saveToFile(contract)
242
330
  }
243
331
  for (const script of this.scripts) {
244
332
  await saveToFile(script)
245
333
  }
334
+ await this.projectArtifact.saveToFile(this.artifactsRootDir)
246
335
  }
247
336
 
248
337
  contractByCodeHash(codeHash: string): Contract {
249
- const contract = this.contracts.find((c) => c.artifact.codeHash === codeHash)
338
+ const contract = this.contracts.find(
339
+ (c) => c.artifact.codeHash === codeHash || c.artifact.codeHashDebug == codeHash
340
+ )
250
341
  if (typeof contract === 'undefined') {
251
342
  throw new Error(`Unknown code with code hash: ${codeHash}`)
252
343
  }
253
344
  return contract.artifact
254
345
  }
255
346
 
256
- private async saveProjectArtifactToFile(): Promise<void> {
257
- const files: Map<string, { sourceCodeHash: string; warnings: string[] }> = new Map()
258
- this.contracts.forEach((c) => {
259
- files.set(c.sourceFile.contractPath, {
260
- sourceCodeHash: c.sourceFile.sourceCodeHash,
261
- warnings: c.warnings
262
- })
263
- })
264
- this.scripts.forEach((s) => {
265
- files.set(s.sourceFile.contractPath, {
266
- sourceCodeHash: s.sourceFile.sourceCodeHash,
267
- warnings: s.warnings
268
- })
269
- })
270
- const compiledSize = this.contracts.length + this.scripts.length
271
- this.sourceFiles.slice(compiledSize).forEach((c) => {
272
- files.set(c.contractPath, {
273
- sourceCodeHash: c.sourceCodeHash,
274
- warnings: []
275
- })
276
- })
277
- const projectArtifact = new ProjectArtifact(files)
278
- await projectArtifact.saveToFile(this.artifactsRootPath)
279
- }
280
-
281
347
  private static async compile(
282
348
  provider: NodeProvider,
283
349
  files: SourceFile[],
284
- contractsRootPath: string,
285
- artifactsRootPath: string
350
+ contractsRootDir: string,
351
+ artifactsRootDir: string,
352
+ errorOnWarnings: boolean,
353
+ compilerOptions: node.CompilerOptions
286
354
  ): Promise<Project> {
287
355
  const sourceStr = files.map((f) => f.sourceCode).join('\n')
288
356
  const result = await provider.contracts.postContractsCompileProject({
289
- code: sourceStr
357
+ code: sourceStr,
358
+ compilerOptions: compilerOptions
290
359
  })
291
360
  const contracts: Compiled<Contract>[] = []
292
361
  const scripts: Compiled<Script>[] = []
293
362
  result.contracts.forEach((contractResult, index) => {
294
363
  const sourceFile = files[`${index}`]
295
- const contract = Contract.fromCompileResult(sourceFile.typeId, contractResult)
364
+ const contract = Contract.fromCompileResult(contractResult)
296
365
  contracts.push(new Compiled(sourceFile, contract, contractResult.warnings))
297
366
  })
298
367
  result.scripts.forEach((scriptResult, index) => {
299
368
  const sourceFile = files[index + contracts.length]
300
- const script = Script.fromCompileResult(sourceFile.typeId, scriptResult)
369
+ const script = Script.fromCompileResult(scriptResult)
301
370
  scripts.push(new Compiled(sourceFile, script, scriptResult.warnings))
302
371
  })
303
- const project = new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts)
372
+ const projectArtifact = Project.buildProjectArtifact(files, contracts, scripts, compilerOptions)
373
+ const project = new Project(
374
+ contractsRootDir,
375
+ artifactsRootDir,
376
+ files,
377
+ contracts,
378
+ scripts,
379
+ errorOnWarnings,
380
+ projectArtifact
381
+ )
304
382
  await project.saveArtifactsToFile()
305
- await project.saveProjectArtifactToFile()
306
383
  return project
307
384
  }
308
385
 
@@ -310,8 +387,10 @@ export class Project {
310
387
  provider: NodeProvider,
311
388
  files: SourceFile[],
312
389
  projectArtifact: ProjectArtifact,
313
- contractsRootPath: string,
314
- artifactsRootPath: string
390
+ contractsRootDir: string,
391
+ artifactsRootDir: string,
392
+ errorOnWarnings: boolean,
393
+ compilerOptions: node.CompilerOptions
315
394
  ): Promise<Project> {
316
395
  try {
317
396
  const contracts: Compiled<Contract>[] = []
@@ -322,19 +401,28 @@ export class Project {
322
401
  throw Error(`Unable to find project info for ${file.contractPath}, please rebuild the project`)
323
402
  }
324
403
  const warnings = info.warnings
325
- const artifactPath = file.getArtifactPath(artifactsRootPath)
404
+ const artifactDir = file.getArtifactPath(artifactsRootDir)
326
405
  if (file.type === SourceType.Contract) {
327
- const artifact = await Contract.fromArtifactFile(artifactPath)
406
+ const artifact = await Contract.fromArtifactFile(artifactDir, info.bytecodeDebugPatch, info.codeHashDebug)
328
407
  contracts.push(new Compiled(file, artifact, warnings))
329
408
  } else if (file.type === SourceType.Script) {
330
- const artifact = await Script.fromArtifactFile(artifactPath)
409
+ const artifact = await Script.fromArtifactFile(artifactDir, info.bytecodeDebugPatch)
331
410
  scripts.push(new Compiled(file, artifact, warnings))
332
411
  }
333
412
  }
334
- return new Project(provider, contractsRootPath, artifactsRootPath, files, contracts, scripts)
413
+
414
+ return new Project(
415
+ contractsRootDir,
416
+ artifactsRootDir,
417
+ files,
418
+ contracts,
419
+ scripts,
420
+ errorOnWarnings,
421
+ projectArtifact
422
+ )
335
423
  } catch (error) {
336
424
  console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
337
- return Project.compile(provider, files, contractsRootPath, artifactsRootPath)
425
+ return Project.compile(provider, files, contractsRootDir, artifactsRootDir, errorOnWarnings, compilerOptions)
338
426
  }
339
427
  }
340
428
 
@@ -355,15 +443,11 @@ export class Project {
355
443
  throw new Error(`Multiple definitions in file: ${contractPath}`)
356
444
  }
357
445
  const matcherIndex = results.indexOf(1)
358
- const matcher = this.matchers[`${matcherIndex}`]
359
- const type = matcher.type
360
- if (matcher.name === undefined) {
361
- throw new Error(`Invalid definition in file: ${contractPath}`)
362
- }
363
- return new SourceFile(type, matcher.name, sourceStr, contractPath)
446
+ const type = this.matchers[`${matcherIndex}`].type
447
+ return SourceFile.from(type, sourceStr, contractPath)
364
448
  }
365
449
 
366
- private static async loadSourceFiles(contractsRootPath: string): Promise<SourceFile[]> {
450
+ private static async loadSourceFiles(contractsRootDir: string): Promise<SourceFile[]> {
367
451
  const loadDir = async function (dirPath: string, results: SourceFile[]): Promise<void> {
368
452
  const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
369
453
  for (const dirent of dirents) {
@@ -377,7 +461,7 @@ export class Project {
377
461
  }
378
462
  }
379
463
  const sourceFiles: SourceFile[] = []
380
- await loadDir(contractsRootPath, sourceFiles)
464
+ await loadDir(contractsRootDir, sourceFiles)
381
465
  const contractAndScriptSize = sourceFiles.filter(
382
466
  (f) => f.type === SourceType.Contract || f.type === SourceType.Script
383
467
  ).length
@@ -387,30 +471,51 @@ export class Project {
387
471
  return sourceFiles.sort((a, b) => a.type - b.type)
388
472
  }
389
473
 
390
- static async build(contractsRootPath = 'contracts', artifactsRootPath = 'artifacts'): Promise<void> {
474
+ static readonly DEFAULT_CONTRACTS_DIR = 'contracts'
475
+ static readonly DEFAULT_ARTIFACTS_DIR = 'artifacts'
476
+
477
+ static async build(
478
+ compilerOptionsPartial: Partial<CompilerOptions> = {},
479
+ contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
480
+ artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR
481
+ ): Promise<void> {
391
482
  const provider = getCurrentNodeProvider()
392
- const sourceFiles = await Project.loadSourceFiles(contractsRootPath)
393
- const projectArtifact = await ProjectArtifact.from(artifactsRootPath)
394
- if (typeof projectArtifact === 'undefined' || projectArtifact.sourceHasChanged(sourceFiles)) {
395
- Project.currentProject = await Project.compile(provider, sourceFiles, contractsRootPath, artifactsRootPath)
483
+ const sourceFiles = await Project.loadSourceFiles(contractsRootDir)
484
+ const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
485
+ const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
486
+ if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
487
+ console.log(`Compiling contracts in folder "${contractsRootDir}"`)
488
+ Project.currentProject = await Project.compile(
489
+ provider,
490
+ sourceFiles,
491
+ contractsRootDir,
492
+ artifactsRootDir,
493
+ errorOnWarnings,
494
+ nodeCompilerOptions
495
+ )
396
496
  } else {
497
+ console.log(`Contracts are compiled already. Loading them from folder "${artifactsRootDir}"`)
397
498
  Project.currentProject = await Project.loadArtifacts(
398
499
  provider,
399
500
  sourceFiles,
400
501
  projectArtifact,
401
- contractsRootPath,
402
- artifactsRootPath
502
+ contractsRootDir,
503
+ artifactsRootDir,
504
+ errorOnWarnings,
505
+ nodeCompilerOptions
403
506
  )
404
507
  }
405
508
  }
406
509
  }
407
510
 
408
511
  export abstract class Artifact {
409
- readonly typeId: string
512
+ readonly version: string
513
+ readonly name: string
410
514
  readonly functions: FunctionSig[]
411
515
 
412
- constructor(typeId: string, functions: FunctionSig[]) {
413
- this.typeId = typeId
516
+ constructor(version: string, name: string, functions: FunctionSig[]) {
517
+ this.version = version
518
+ this.name = name
414
519
  this.functions = functions
415
520
  }
416
521
 
@@ -431,28 +536,41 @@ export abstract class Artifact {
431
536
 
432
537
  export class Contract extends Artifact {
433
538
  readonly bytecode: string
539
+ readonly bytecodeDebugPatch: string
434
540
  readonly codeHash: string
435
541
  readonly fieldsSig: FieldsSig
436
542
  readonly eventsSig: EventSig[]
437
543
 
544
+ readonly bytecodeDebug: string
545
+ readonly codeHashDebug: string
546
+
438
547
  constructor(
439
- typeId: string,
548
+ version: string,
549
+ name: string,
440
550
  bytecode: string,
551
+ bytecodeDebugPatch: string,
441
552
  codeHash: string,
553
+ codeHashDebug: string,
442
554
  fieldsSig: FieldsSig,
443
555
  eventsSig: EventSig[],
444
556
  functions: FunctionSig[]
445
557
  ) {
446
- super(typeId, functions)
558
+ super(version, name, functions)
447
559
  this.bytecode = bytecode
560
+ this.bytecodeDebugPatch = bytecodeDebugPatch
448
561
  this.codeHash = codeHash
449
562
  this.fieldsSig = fieldsSig
450
563
  this.eventsSig = eventsSig
564
+
565
+ this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
566
+ this.codeHashDebug = codeHashDebug
451
567
  }
452
568
 
453
569
  // TODO: safely parse json
454
- static fromJson(artifact: any): Contract {
570
+ static fromJson(artifact: any, bytecodeDebugPatch = '', codeHashDebug = ''): Contract {
455
571
  if (
572
+ artifact.version == null ||
573
+ artifact.name == null ||
456
574
  artifact.bytecode == null ||
457
575
  artifact.codeHash == null ||
458
576
  artifact.fieldsSig == null ||
@@ -462,9 +580,12 @@ export class Contract extends Artifact {
462
580
  throw Error('The artifact JSON for contract is incomplete')
463
581
  }
464
582
  const contract = new Contract(
465
- artifact.typeId,
583
+ artifact.version,
584
+ artifact.name,
466
585
  artifact.bytecode,
586
+ bytecodeDebugPatch,
467
587
  artifact.codeHash,
588
+ codeHashDebug ? codeHashDebug : artifact.codeHash,
468
589
  artifact.fieldsSig,
469
590
  artifact.eventsSig,
470
591
  artifact.functions
@@ -472,19 +593,29 @@ export class Contract extends Artifact {
472
593
  return contract
473
594
  }
474
595
 
475
- static fromCompileResult(typeId: string, result: CompileContractResult): Contract {
476
- return new Contract(typeId, result.bytecode, result.codeHash, result.fields, result.events, result.functions)
596
+ static fromCompileResult(result: node.CompileContractResult): Contract {
597
+ return new Contract(
598
+ result.version,
599
+ result.name,
600
+ result.bytecode,
601
+ result.bytecodeDebugPatch,
602
+ result.codeHash,
603
+ result.codeHashDebug,
604
+ result.fields,
605
+ result.events,
606
+ result.functions
607
+ )
477
608
  }
478
609
 
479
610
  // support both 'code.ral' and 'code.ral.json'
480
- static async fromArtifactFile(path: string): Promise<Contract> {
611
+ static async fromArtifactFile(path: string, bytecodeDebugPatch: string, codeHashDebug: string): Promise<Contract> {
481
612
  const content = await fsPromises.readFile(path)
482
613
  const artifact = JSON.parse(content.toString())
483
- return Contract.fromJson(artifact)
614
+ return Contract.fromJson(artifact, bytecodeDebugPatch, codeHashDebug)
484
615
  }
485
616
 
486
617
  async fetchState(address: string, group: number): Promise<ContractState> {
487
- const state = await Project.currentProject.nodeProvider.contracts.getContractsAddressState(address, {
618
+ const state = await web3.getCurrentNodeProvider().contracts.getContractsAddressState(address, {
488
619
  group: group
489
620
  })
490
621
  return this.fromApiContractState(state)
@@ -492,7 +623,8 @@ export class Contract extends Artifact {
492
623
 
493
624
  override toString(): string {
494
625
  const object = {
495
- typeId: this.typeId,
626
+ version: this.version,
627
+ name: this.name,
496
628
  bytecode: this.bytecode,
497
629
  codeHash: this.codeHash,
498
630
  fieldsSig: this.fieldsSig,
@@ -515,24 +647,37 @@ export class Contract extends Artifact {
515
647
  }
516
648
  }
517
649
 
650
+ // no need to be cryptographically strong random
518
651
  static randomAddress(): string {
519
- const bytes = crypto.randomBytes(33)
652
+ const bytes = new Uint8Array(33)
653
+ crypto.getRandomValues(bytes)
520
654
  bytes[0] = 3
521
655
  return bs58.encode(bytes)
522
656
  }
523
657
 
658
+ private _printDebugMessages(funcName: string, messages: DebugMessage[]) {
659
+ if (messages.length != 0) {
660
+ console.log(`Testing ${this.name}.${funcName}:`)
661
+ messages.forEach((m) => console.log(`Debug - ${m.contractAddress} - ${m.message}`))
662
+ }
663
+ }
664
+
524
665
  private async _test(
525
666
  funcName: string,
526
667
  params: TestContractParams,
527
668
  expectPublic: boolean,
528
- accessType: string
669
+ accessType: string,
670
+ printDebugMessages: boolean
529
671
  ): Promise<TestContractResult> {
530
672
  const apiParams: node.TestContract = this.toTestContract(funcName, params)
531
- const apiResult = await Project.currentProject.nodeProvider.contracts.postContractsTestContract(apiParams)
673
+ const apiResult = await web3.getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
532
674
 
533
675
  const methodIndex =
534
676
  typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName)
535
677
  const isPublic = this.functions[`${methodIndex}`].isPublic
678
+ if (printDebugMessages) {
679
+ this._printDebugMessages(funcName, apiResult.debugMessages)
680
+ }
536
681
  if (isPublic === expectPublic) {
537
682
  const result = await this.fromTestContractResult(methodIndex, apiResult)
538
683
  return result
@@ -541,12 +686,20 @@ export class Contract extends Artifact {
541
686
  }
542
687
  }
543
688
 
544
- async testPublicMethod(funcName: string, params: TestContractParams): Promise<TestContractResult> {
545
- return this._test(funcName, params, true, 'public')
689
+ async testPublicMethod(
690
+ funcName: string,
691
+ params: TestContractParams,
692
+ printDebugMessages = true
693
+ ): Promise<TestContractResult> {
694
+ return this._test(funcName, params, true, 'public', printDebugMessages)
546
695
  }
547
696
 
548
- async testPrivateMethod(funcName: string, params: TestContractParams): Promise<TestContractResult> {
549
- return this._test(funcName, params, false, 'private')
697
+ async testPrivateMethod(
698
+ funcName: string,
699
+ params: TestContractParams,
700
+ printDebugMessages = true
701
+ ): Promise<TestContractResult> {
702
+ return this._test(funcName, params, false, 'private', printDebugMessages)
550
703
  }
551
704
 
552
705
  toApiFields(fields?: Fields): node.Val[] {
@@ -582,7 +735,7 @@ export class Contract extends Artifact {
582
735
  return {
583
736
  group: params.group,
584
737
  address: params.address,
585
- bytecode: this.bytecode,
738
+ bytecode: this.bytecodeDebug,
586
739
  initialFields: this.toApiFields(params.initialFields),
587
740
  initialAsset: typeof params.initialAsset !== 'undefined' ? toApiAsset(params.initialAsset) : undefined,
588
741
  methodIndex: this.getMethodIndex(funcName),
@@ -592,20 +745,24 @@ export class Contract extends Artifact {
592
745
  }
593
746
  }
594
747
 
595
- async fromApiContractState(state: node.ContractState): Promise<ContractState> {
596
- const contract = Project.currentProject.contractByCodeHash(state.codeHash)
748
+ fromApiContractState(state: node.ContractState): ContractState {
597
749
  return {
598
750
  address: state.address,
599
751
  contractId: binToHex(contractIdFromAddress(state.address)),
600
752
  bytecode: state.bytecode,
601
753
  initialStateHash: state.initialStateHash,
602
754
  codeHash: state.codeHash,
603
- fields: fromApiFields(state.fields, contract.fieldsSig),
604
- fieldsSig: contract.fieldsSig,
755
+ fields: fromApiFields(state.fields, this.fieldsSig),
756
+ fieldsSig: this.fieldsSig,
605
757
  asset: fromApiAsset(state.asset)
606
758
  }
607
759
  }
608
760
 
761
+ static fromApiContractState(state: node.ContractState): ContractState {
762
+ const contract = Project.currentProject.contractByCodeHash(state.codeHash)
763
+ return contract.fromApiContractState(state)
764
+ }
765
+
609
766
  static ContractCreatedEvent: EventSig = {
610
767
  name: 'ContractCreated',
611
768
  fieldNames: ['address'],
@@ -618,10 +775,7 @@ export class Contract extends Artifact {
618
775
  fieldTypes: ['Address']
619
776
  }
620
777
 
621
- static async fromApiEvent(
622
- event: node.ContractEventByTxId,
623
- codeHash: string | undefined
624
- ): Promise<ContractEventByTxId> {
778
+ static fromApiEvent(event: node.ContractEventByTxId, codeHash: string | undefined): ContractEventByTxId {
625
779
  let eventSig: EventSig
626
780
 
627
781
  if (event.eventIndex == -1) {
@@ -641,55 +795,53 @@ export class Contract extends Artifact {
641
795
  }
642
796
  }
643
797
 
644
- async fromTestContractResult(methodIndex: number, result: node.TestContractResult): Promise<TestContractResult> {
798
+ fromTestContractResult(methodIndex: number, result: node.TestContractResult): TestContractResult {
645
799
  const addressToCodeHash = new Map<string, string>()
646
800
  addressToCodeHash.set(result.address, result.codeHash)
647
801
  result.contracts.forEach((contract) => addressToCodeHash.set(contract.address, contract.codeHash))
648
802
  return {
649
- address: result.address,
650
803
  contractId: binToHex(contractIdFromAddress(result.address)),
804
+ contractAddress: result.address,
651
805
  returns: fromApiArray(result.returns, this.functions[`${methodIndex}`].returnTypes),
652
806
  gasUsed: result.gasUsed,
653
- contracts: await Promise.all(result.contracts.map((contract) => this.fromApiContractState(contract))),
807
+ contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract)),
654
808
  txOutputs: result.txOutputs.map(fromApiOutput),
655
- events: await Promise.all(
656
- result.events.map((event) => {
657
- const contractAddress = event.contractAddress
658
- const codeHash = addressToCodeHash.get(contractAddress)
659
- if (typeof codeHash !== 'undefined' || event.eventIndex < 0) {
660
- return Contract.fromApiEvent(event, codeHash)
661
- } else {
662
- throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
663
- }
664
- })
665
- )
809
+ events: result.events.map((event) => {
810
+ const contractAddress = event.contractAddress
811
+ const codeHash = addressToCodeHash.get(contractAddress)
812
+ if (typeof codeHash !== 'undefined' || event.eventIndex < 0) {
813
+ return Contract.fromApiEvent(event, codeHash)
814
+ } else {
815
+ throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
816
+ }
817
+ }),
818
+ debugMessages: result.debugMessages
666
819
  }
667
820
  }
668
821
 
669
- async paramsForDeployment(params: BuildDeployContractTx): Promise<SignDeployContractTxParams> {
822
+ async txParamsForDeployment(
823
+ signer: SignerProvider,
824
+ params: Omit<BuildDeployContractTx, 'signerAddress'>
825
+ ): Promise<SignDeployContractTxParams> {
670
826
  const bytecode = this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {})
671
827
  const signerParams: SignDeployContractTxParams = {
672
- signerAddress: params.signerAddress,
828
+ signerAddress: (await signer.getSelectedAccount()).address,
673
829
  bytecode: bytecode,
674
- initialAttoAlphAmount: extractOptionalNumber256(params.initialAttoAlphAmount),
675
- issueTokenAmount: extractOptionalNumber256(params.issueTokenAmount),
676
- initialTokenAmounts: params.initialTokenAmounts?.map(toApiToken),
830
+ initialAttoAlphAmount: params.initialAttoAlphAmount,
831
+ issueTokenAmount: params.issueTokenAmount,
832
+ initialTokenAmounts: params.initialTokenAmounts,
677
833
  gasAmount: params.gasAmount,
678
- gasPrice: extractOptionalNumber256(params.gasPrice)
834
+ gasPrice: params.gasPrice
679
835
  }
680
836
  return signerParams
681
837
  }
682
838
 
683
- async transactionForDeployment(
684
- signer: SignerWithNodeProvider,
839
+ async deploy(
840
+ signer: SignerProvider,
685
841
  params: Omit<BuildDeployContractTx, 'signerAddress'>
686
- ): Promise<DeployContractTransaction> {
687
- const signerParams = await this.paramsForDeployment({
688
- ...params,
689
- signerAddress: (await signer.getAccounts())[0].address
690
- })
691
- const response = await signer.buildContractCreationTx(signerParams)
692
- return fromApiDeployContractUnsignedTx(response)
842
+ ): Promise<SignDeployContractTxResult> {
843
+ const signerParams = await this.txParamsForDeployment(signer, params)
844
+ return signer.signAndSubmitDeployContractTx(signerParams)
693
845
  }
694
846
 
695
847
  buildByteCodeToDeploy(initialFields: Fields): string {
@@ -699,35 +851,65 @@ export class Contract extends Artifact {
699
851
 
700
852
  export class Script extends Artifact {
701
853
  readonly bytecodeTemplate: string
854
+ readonly bytecodeDebugPatch: string
702
855
  readonly fieldsSig: FieldsSig
703
856
 
704
- constructor(typeId: string, bytecodeTemplate: string, fieldsSig: FieldsSig, functions: FunctionSig[]) {
705
- super(typeId, functions)
857
+ constructor(
858
+ version: string,
859
+ name: string,
860
+ bytecodeTemplate: string,
861
+ bytecodeDebugPatch: string,
862
+ fieldsSig: FieldsSig,
863
+ functions: FunctionSig[]
864
+ ) {
865
+ super(version, name, functions)
706
866
  this.bytecodeTemplate = bytecodeTemplate
867
+ this.bytecodeDebugPatch = bytecodeDebugPatch
707
868
  this.fieldsSig = fieldsSig
708
869
  }
709
870
 
710
- static fromCompileResult(typeId: string, result: CompileScriptResult): Script {
711
- return new Script(typeId, result.bytecodeTemplate, result.fields, result.functions)
871
+ static fromCompileResult(result: node.CompileScriptResult): Script {
872
+ return new Script(
873
+ result.version,
874
+ result.name,
875
+ result.bytecodeTemplate,
876
+ result.bytecodeDebugPatch,
877
+ result.fields,
878
+ result.functions
879
+ )
712
880
  }
713
881
 
714
882
  // TODO: safely parse json
715
- static fromJson(artifact: any): Script {
716
- if (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
883
+ static fromJson(artifact: any, bytecodeDebugPatch = ''): Script {
884
+ if (
885
+ artifact.version == null ||
886
+ artifact.name == null ||
887
+ artifact.bytecodeTemplate == null ||
888
+ artifact.fieldsSig == null ||
889
+ artifact.functions == null
890
+ ) {
717
891
  throw Error('The artifact JSON for script is incomplete')
718
892
  }
719
- return new Script(artifact.typeId, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
893
+ return new Script(
894
+ artifact.version,
895
+ artifact.name,
896
+ artifact.bytecodeTemplate,
897
+ bytecodeDebugPatch,
898
+ artifact.fieldsSig,
899
+ artifact.functions
900
+ )
720
901
  }
721
902
 
722
- static async fromArtifactFile(path: string): Promise<Script> {
903
+ static async fromArtifactFile(path: string, bytecodeDebugPatch: string): Promise<Script> {
723
904
  const content = await fsPromises.readFile(path)
724
905
  const artifact = JSON.parse(content.toString())
725
- return this.fromJson(artifact)
906
+ return this.fromJson(artifact, bytecodeDebugPatch)
726
907
  }
727
908
 
728
909
  override toString(): string {
729
910
  const object = {
730
- typeId: this.typeId,
911
+ version: this.version,
912
+ name: this.name,
731
913
  bytecodeTemplate: this.bytecodeTemplate,
732
914
  fieldsSig: this.fieldsSig,
733
915
  functions: this.functions
@@ -735,27 +917,27 @@ export class Script extends Artifact {
735
917
  return JSON.stringify(object, null, 2)
736
918
  }
737
919
 
738
- async paramsForDeployment(params: BuildExecuteScriptTx): Promise<SignExecuteScriptTxParams> {
920
+ async txParamsForExecution(
921
+ signer: SignerProvider,
922
+ params: Omit<BuildExecuteScriptTx, 'signerAddress'>
923
+ ): Promise<SignExecuteScriptTxParams> {
739
924
  const signerParams: SignExecuteScriptTxParams = {
740
- signerAddress: params.signerAddress,
925
+ signerAddress: (await signer.getSelectedAccount()).address,
741
926
  bytecode: this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {}),
742
- attoAlphAmount: extractOptionalNumber256(params.attoAlphAmount),
743
- tokens: typeof params.tokens !== 'undefined' ? params.tokens.map(toApiToken) : undefined,
927
+ attoAlphAmount: params.attoAlphAmount,
928
+ tokens: params.tokens,
744
929
  gasAmount: params.gasAmount,
745
- gasPrice: extractOptionalNumber256(params.gasPrice)
930
+ gasPrice: params.gasPrice
746
931
  }
747
932
  return signerParams
748
933
  }
749
934
 
750
- async transactionForDeployment(
751
- signer: SignerWithNodeProvider,
935
+ async execute(
936
+ signer: SignerProvider,
752
937
  params: Omit<BuildExecuteScriptTx, 'signerAddress'>
753
- ): Promise<BuildScriptTxResult> {
754
- const signerParams = await this.paramsForDeployment({
755
- ...params,
756
- signerAddress: (await signer.getAccounts())[0].address
757
- })
758
- return await signer.buildScriptTx(signerParams)
938
+ ): Promise<SignExecuteScriptTxResult> {
939
+ const signerParams = await this.txParamsForExecution(signer, params)
940
+ return await signer.signAndSubmitExecuteScriptTx(signerParams)
759
941
  }
760
942
 
761
943
  buildByteCodeToDeploy(initialFields: Fields): string {
@@ -763,164 +945,6 @@ export class Script extends Artifact {
763
945
  }
764
946
  }
765
947
 
766
- export type Number256 = number | bigint | string
767
- export type Val = Number256 | boolean | string | Val[]
768
- export type NamedVals = Record<string, Val>
769
- export type Fields = NamedVals
770
- export type Arguments = NamedVals
771
-
772
- function extractBoolean(v: Val): boolean {
773
- if (typeof v === 'boolean') {
774
- return v
775
- } else {
776
- throw new Error(`Invalid boolean value: ${v}`)
777
- }
778
- }
779
-
780
- // TODO: check integer bounds
781
- function extractNumber256(v: Val): string {
782
- if ((typeof v === 'number' && Number.isInteger(v)) || typeof v === 'bigint') {
783
- return v.toString()
784
- } else if (typeof v === 'string') {
785
- return v
786
- } else {
787
- throw new Error(`Invalid 256 bit number: ${v}`)
788
- }
789
- }
790
-
791
- function extractOptionalNumber256(v?: Val): string | undefined {
792
- return typeof v !== 'undefined' ? extractNumber256(v) : undefined
793
- }
794
-
795
- // TODO: check hex string
796
- function extractByteVec(v: Val): string {
797
- if (typeof v === 'string') {
798
- // try to convert from address to contract id
799
- try {
800
- const address = bs58.decode(v)
801
- if (address.length == 33 && address[0] == 3) {
802
- return Buffer.from(address.slice(1)).toString('hex')
803
- }
804
- } catch (_) {
805
- return v as string
806
- }
807
- return v as string
808
- } else {
809
- throw new Error(`Invalid string: ${v}`)
810
- }
811
- }
812
-
813
- function extractBs58(v: Val): string {
814
- if (typeof v === 'string') {
815
- try {
816
- bs58.decode(v)
817
- return v as string
818
- } catch (error) {
819
- throw new Error(`Invalid base58 string: ${v}`)
820
- }
821
- } else {
822
- throw new Error(`Invalid string: ${v}`)
823
- }
824
- }
825
-
826
- function decodeNumber256(n: string): Number256 {
827
- if (Number.isSafeInteger(Number.parseInt(n))) {
828
- return Number(n)
829
- } else {
830
- return BigInt(n)
831
- }
832
- }
833
-
834
- export function extractArray(tpe: string, v: Val): node.Val {
835
- if (!Array.isArray(v)) {
836
- throw new Error(`Expected array, got ${v}`)
837
- }
838
-
839
- const semiColonIndex = tpe.lastIndexOf(';')
840
- if (semiColonIndex == -1) {
841
- throw new Error(`Invalid Val type: ${tpe}`)
842
- }
843
-
844
- const subType = tpe.slice(1, semiColonIndex)
845
- const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
846
- if ((v as Val[]).length != dim) {
847
- throw new Error(`Invalid val dimension: ${v}`)
848
- } else {
849
- return { value: (v as Val[]).map((v) => toApiVal(v, subType)), type: 'Array' }
850
- }
851
- }
852
-
853
- export function toApiVal(v: Val, tpe: string): node.Val {
854
- if (tpe === 'Bool') {
855
- return { value: extractBoolean(v), type: tpe }
856
- } else if (tpe === 'U256' || tpe === 'I256') {
857
- return { value: extractNumber256(v), type: tpe }
858
- } else if (tpe === 'ByteVec') {
859
- return { value: extractByteVec(v), type: tpe }
860
- } else if (tpe === 'Address') {
861
- return { value: extractBs58(v), type: tpe }
862
- } else {
863
- return extractArray(tpe, v)
864
- }
865
- }
866
-
867
- function decodeArrayType(tpe: string): [baseType: string, dims: number[]] {
868
- const semiColonIndex = tpe.lastIndexOf(';')
869
- if (semiColonIndex === -1) {
870
- throw new Error(`Invalid Val type: ${tpe}`)
871
- }
872
-
873
- const subType = tpe.slice(1, semiColonIndex)
874
- const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
875
- if (subType[0] == '[') {
876
- const [baseType, subDim] = decodeArrayType(subType)
877
- return [baseType, (subDim.unshift(dim), subDim)]
878
- } else {
879
- return [subType, [dim]]
880
- }
881
- }
882
-
883
- function foldVals(vals: Val[], dims: number[]): Val {
884
- if (dims.length == 1) {
885
- return vals
886
- } else {
887
- const result: Val[] = []
888
- const chunkSize = vals.length / dims[0]
889
- const chunkDims = dims.slice(1)
890
- for (let i = 0; i < vals.length; i += chunkSize) {
891
- const chunk = vals.slice(i, i + chunkSize)
892
- result.push(foldVals(chunk, chunkDims))
893
- }
894
- return result
895
- }
896
- }
897
-
898
- function _fromApiVal(vals: node.Val[], valIndex: number, tpe: string): [result: Val, nextIndex: number] {
899
- if (vals.length === 0) {
900
- throw new Error('Not enough Vals')
901
- }
902
-
903
- const firstVal = vals[`${valIndex}`]
904
- if (tpe === 'Bool' && firstVal.type === tpe) {
905
- return [firstVal.value as boolean, valIndex + 1]
906
- } else if ((tpe === 'U256' || tpe === 'I256') && firstVal.type === tpe) {
907
- return [decodeNumber256(firstVal.value as string), valIndex + 1]
908
- } else if ((tpe === 'ByteVec' || tpe === 'Address') && firstVal.type === tpe) {
909
- return [firstVal.value as string, valIndex + 1]
910
- } else {
911
- const [baseType, dims] = decodeArrayType(tpe)
912
- const arraySize = dims.reduce((a, b) => a * b)
913
- const nextIndex = valIndex + arraySize
914
- const valsToUse = vals.slice(valIndex, nextIndex)
915
- if (valsToUse.length == arraySize && valsToUse.every((val) => val.type === baseType)) {
916
- const localVals = valsToUse.map((val) => fromApiVal(val, baseType))
917
- return [foldVals(localVals, dims), nextIndex]
918
- } else {
919
- throw new Error(`Invalid array Val type: ${valsToUse}, ${tpe}`)
920
- }
921
- }
922
- }
923
-
924
948
  function fromApiFields(vals: node.Val[], fieldsSig: node.FieldsSig): Fields {
925
949
  return fromApiVals(vals, fieldsSig.names, fieldsSig.types)
926
950
  }
@@ -929,70 +953,22 @@ function fromApiEventFields(vals: node.Val[], eventSig: node.EventSig): Fields {
929
953
  return fromApiVals(vals, eventSig.fieldNames, eventSig.fieldTypes)
930
954
  }
931
955
 
932
- function fromApiVals(vals: node.Val[], names: string[], types: string[]): Fields {
933
- let valIndex = 0
934
- const result: Fields = {}
935
- types.forEach((currentType, index) => {
936
- const currentName = names[`${index}`]
937
- const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
938
- valIndex = nextIndex
939
- result[`${currentName}`] = val
940
- })
941
- return result
942
- }
943
-
944
- function fromApiArray(vals: node.Val[], types: string[]): Val[] {
945
- let valIndex = 0
946
- const result: Val[] = []
947
- for (const currentType of types) {
948
- const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
949
- result.push(val)
950
- valIndex = nextIndex
951
- }
952
- return result
953
- }
954
-
955
- function fromApiVal(v: node.Val, tpe: string): Val {
956
- if (v.type === 'Bool' && v.type === tpe) {
957
- return v.value as boolean
958
- } else if ((v.type === 'U256' || v.type === 'I256') && v.type === tpe) {
959
- return decodeNumber256(v.value as string)
960
- } else if ((v.type === 'ByteVec' || v.type === 'Address') && v.type === tpe) {
961
- return v.value as string
962
- } else {
963
- throw new Error(`Invalid node.Val type: ${v}`)
964
- }
965
- }
966
-
967
956
  export interface Asset {
968
957
  alphAmount: Number256
969
958
  tokens?: Token[]
970
959
  }
971
960
 
972
- export interface Token {
973
- id: string
974
- amount: Number256
975
- }
976
-
977
- function toApiToken(token: Token): node.Token {
978
- return { id: token.id, amount: extractNumber256(token.amount) }
979
- }
980
-
981
- function fromApiToken(token: node.Token): Token {
982
- return { id: token.id, amount: decodeNumber256(token.amount) }
983
- }
984
-
985
961
  function toApiAsset(asset: Asset): node.AssetState {
986
962
  return {
987
- attoAlphAmount: extractNumber256(asset.alphAmount),
963
+ attoAlphAmount: toApiNumber256(asset.alphAmount),
988
964
  tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(toApiToken) : []
989
965
  }
990
966
  }
991
967
 
992
968
  function fromApiAsset(asset: node.AssetState): Asset {
993
969
  return {
994
- alphAmount: decodeNumber256(asset.attoAlphAmount),
995
- tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(fromApiToken) : undefined
970
+ alphAmount: fromApiNumber256(asset.attoAlphAmount),
971
+ tokens: fromApiTokens(asset.tokens)
996
972
  }
997
973
  }
998
974
 
@@ -1080,14 +1056,17 @@ export interface ContractEventByTxId {
1080
1056
  fields: Fields
1081
1057
  }
1082
1058
 
1059
+ export type DebugMessage = node.DebugMessage
1060
+
1083
1061
  export interface TestContractResult {
1084
- address: string
1085
1062
  contractId: string
1063
+ contractAddress: string
1086
1064
  returns: Val[]
1087
1065
  gasUsed: number
1088
1066
  contracts: ContractState[]
1089
1067
  txOutputs: Output[]
1090
1068
  events: ContractEventByTxId[]
1069
+ debugMessages: DebugMessage[]
1091
1070
  }
1092
1071
  export declare type Output = AssetOutput | ContractOutput
1093
1072
  export interface AssetOutput extends Asset {
@@ -1100,7 +1079,7 @@ export interface ContractOutput {
1100
1079
  type: string
1101
1080
  address: string
1102
1081
  alphAmount: Number256
1103
- tokens: Token[]
1082
+ tokens?: Token[]
1104
1083
  }
1105
1084
 
1106
1085
  function fromApiOutput(output: node.Output): Output {
@@ -1109,8 +1088,8 @@ function fromApiOutput(output: node.Output): Output {
1109
1088
  return {
1110
1089
  type: 'AssetOutput',
1111
1090
  address: asset.address,
1112
- alphAmount: decodeNumber256(asset.attoAlphAmount),
1113
- tokens: asset.tokens.map(fromApiToken),
1091
+ alphAmount: fromApiNumber256(asset.attoAlphAmount),
1092
+ tokens: fromApiTokens(asset.tokens),
1114
1093
  lockTime: asset.lockTime,
1115
1094
  message: asset.message
1116
1095
  }
@@ -1119,8 +1098,8 @@ function fromApiOutput(output: node.Output): Output {
1119
1098
  return {
1120
1099
  type: 'ContractOutput',
1121
1100
  address: asset.address,
1122
- alphAmount: decodeNumber256(asset.attoAlphAmount),
1123
- tokens: asset.tokens.map(fromApiToken)
1101
+ alphAmount: fromApiNumber256(asset.attoAlphAmount),
1102
+ tokens: fromApiTokens(asset.tokens)
1124
1103
  }
1125
1104
  } else {
1126
1105
  throw new Error(`Unknown output type: ${output}`)
@@ -1136,21 +1115,16 @@ export interface DeployContractTransaction {
1136
1115
  contractId: string
1137
1116
  }
1138
1117
 
1139
- function fromApiDeployContractUnsignedTx(result: node.BuildDeployContractTxResult): DeployContractTransaction {
1140
- return { ...result, contractId: binToHex(contractIdFromAddress(result.contractAddress)) }
1141
- }
1142
-
1143
1118
  type BuildTxParams<T> = Omit<T, 'bytecode'> & { initialFields?: Val[] }
1144
1119
 
1145
1120
  export interface BuildDeployContractTx {
1146
1121
  signerAddress: string
1147
1122
  initialFields?: Fields
1148
- initialAttoAlphAmount?: string
1123
+ initialAttoAlphAmount?: Number256
1149
1124
  initialTokenAmounts?: Token[]
1150
1125
  issueTokenAmount?: Number256
1151
1126
  gasAmount?: number
1152
1127
  gasPrice?: Number256
1153
- submitTx?: boolean
1154
1128
  }
1155
1129
  assertType<Eq<keyof BuildDeployContractTx, keyof BuildTxParams<SignDeployContractTxParams>>>()
1156
1130
 
@@ -1161,7 +1135,6 @@ export interface BuildExecuteScriptTx {
1161
1135
  tokens?: Token[]
1162
1136
  gasAmount?: number
1163
1137
  gasPrice?: Number256
1164
- submitTx?: boolean
1165
1138
  }
1166
1139
  assertType<Eq<keyof BuildExecuteScriptTx, keyof BuildTxParams<SignExecuteScriptTxParams>>>()
1167
1140