@alephium/web3 0.2.0-rc.1 → 0.2.0-rc.10

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 (89) hide show
  1. package/.eslintignore +2 -2
  2. package/README.md +2 -135
  3. package/contracts/greeter/greeter.ral +2 -2
  4. package/contracts/greeter/greeter_interface.ral +1 -0
  5. package/contracts/greeter_main.ral +0 -2
  6. package/dist/alephium-web3.min.js +1 -1
  7. package/dist/alephium-web3.min.js.LICENSE.txt +0 -17
  8. package/dist/alephium-web3.min.js.map +1 -1
  9. package/dist/scripts/create-project.js +3 -2
  10. package/dist/src/api/api-alephium.d.ts +44 -11
  11. package/dist/src/api/api-alephium.js +16 -0
  12. package/dist/src/api/api-explorer.d.ts +16 -16
  13. package/dist/src/api/index.d.ts +2 -2
  14. package/dist/src/api/index.js +18 -3
  15. package/dist/src/contract/contract.d.ts +101 -52
  16. package/dist/src/contract/contract.js +353 -219
  17. package/dist/src/contract/index.js +5 -1
  18. package/dist/src/global.d.ts +4 -0
  19. package/dist/src/global.js +38 -0
  20. package/dist/src/index.d.ts +2 -0
  21. package/dist/src/index.js +20 -1
  22. package/dist/src/signer/index.d.ts +0 -1
  23. package/dist/src/signer/index.js +5 -2
  24. package/dist/src/signer/signer.d.ts +1 -1
  25. package/dist/src/signer/signer.js +8 -3
  26. package/dist/src/transaction/index.d.ts +0 -1
  27. package/dist/src/transaction/index.js +5 -2
  28. package/dist/src/utils/index.d.ts +0 -1
  29. package/dist/src/utils/index.js +5 -2
  30. package/dist/src/utils/subscription.d.ts +0 -1
  31. package/dist/src/utils/subscription.js +2 -1
  32. package/dist/src/utils/utils.d.ts +5 -4
  33. package/dist/src/utils/utils.js +17 -16
  34. package/jest-config.json +11 -0
  35. package/package.json +6 -34
  36. package/scripts/create-project.ts +3 -2
  37. package/src/api/api-alephium.ts +53 -5
  38. package/src/api/index.ts +13 -3
  39. package/src/contract/contract.ts +505 -318
  40. package/src/contract/events.ts +2 -2
  41. package/src/{transaction/sign-verify.ts → global.ts} +14 -15
  42. package/src/index.ts +3 -0
  43. package/src/signer/index.ts +0 -1
  44. package/src/signer/signer.ts +3 -2
  45. package/src/transaction/index.ts +0 -1
  46. package/src/transaction/status.ts +1 -1
  47. package/src/utils/index.ts +0 -1
  48. package/src/utils/subscription.ts +3 -3
  49. package/src/utils/utils.ts +10 -10
  50. package/templates/base/package.json +2 -2
  51. package/templates/base/src/greeter.ts +8 -7
  52. package/templates/react/package.json +2 -2
  53. package/templates/react/src/App.tsx +2 -2
  54. package/.eslintrc.json +0 -21
  55. package/LICENSE +0 -165
  56. package/contracts/add/add.ral +0 -16
  57. package/contracts/main.ral +0 -6
  58. package/contracts/sub/sub.ral +0 -9
  59. package/dist/src/signer/node-wallet.d.ts +0 -13
  60. package/dist/src/signer/node-wallet.js +0 -60
  61. package/dist/src/test/index.d.ts +0 -7
  62. package/dist/src/test/index.js +0 -41
  63. package/dist/src/test/privatekey-wallet.d.ts +0 -12
  64. package/dist/src/test/privatekey-wallet.js +0 -68
  65. package/dist/src/transaction/sign-verify.d.ts +0 -2
  66. package/dist/src/transaction/sign-verify.js +0 -58
  67. package/dist/src/utils/password-crypto.d.ts +0 -2
  68. package/dist/src/utils/password-crypto.js +0 -69
  69. package/gitignore +0 -10
  70. package/src/contract/ralph.test.ts +0 -178
  71. package/src/fixtures/address.json +0 -36
  72. package/src/fixtures/balance.json +0 -9
  73. package/src/fixtures/self-clique.json +0 -19
  74. package/src/fixtures/transaction.json +0 -13
  75. package/src/fixtures/transactions.json +0 -179
  76. package/src/signer/fixtures/genesis.json +0 -26
  77. package/src/signer/fixtures/wallets.json +0 -26
  78. package/src/signer/node-wallet.ts +0 -74
  79. package/src/test/index.ts +0 -32
  80. package/src/test/privatekey-wallet.ts +0 -58
  81. package/src/transaction/sign-verify.test.ts +0 -50
  82. package/src/utils/address.test.ts +0 -47
  83. package/src/utils/djb2.test.ts +0 -35
  84. package/src/utils/password-crypto.test.ts +0 -27
  85. package/src/utils/password-crypto.ts +0 -77
  86. package/src/utils/utils.test.ts +0 -161
  87. package/test/contract.test.ts +0 -178
  88. package/test/events.test.ts +0 -138
  89. package/test/transaction.test.ts +0 -72
@@ -17,250 +17,517 @@ 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 { NodeProvider } from '../api'
25
- import { node } from '../api'
23
+ import { node, NodeProvider } from '../api'
26
24
  import { SignDeployContractTxParams, SignExecuteScriptTxParams, SignerWithNodeProvider } from '../signer'
27
25
  import * as ralph from './ralph'
28
26
  import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
27
+ import { CompileContractResult, CompileScriptResult } from '../api/api-alephium'
28
+ import { getCurrentNodeProvider } from '../global'
29
+
30
+ type FieldsSig = node.FieldsSig
31
+ type EventSig = node.EventSig
32
+ type FunctionSig = node.FunctionSig
33
+
34
+ enum SourceType {
35
+ Contract = 0,
36
+ Script = 1,
37
+ AbstractContract = 2,
38
+ Interface = 3
39
+ }
40
+
41
+ export type CompilerOptions = node.CompilerOptions & {
42
+ errorOnWarnings: boolean
43
+ }
44
+
45
+ export const DEFAULT_NODE_COMPILER_OPTIONS: node.CompilerOptions = {
46
+ ignoreUnusedConstantsWarnings: false,
47
+ ignoreUnusedVariablesWarnings: false,
48
+ ignoreUnusedFieldsWarnings: false,
49
+ ignoreUnusedPrivateFunctionsWarnings: false,
50
+ ignoreReadonlyCheckWarnings: false,
51
+ ignoreExternalCallCheckWarnings: false
52
+ }
53
+
54
+ export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { errorOnWarnings: true, ...DEFAULT_NODE_COMPILER_OPTIONS }
55
+
56
+ class TypedMatcher<T extends SourceType> {
57
+ matcher: RegExp
58
+ type: T
59
+
60
+ constructor(pattern: string, type: T) {
61
+ this.matcher = new RegExp(pattern, 'mg')
62
+ this.type = type
63
+ }
64
+
65
+ match(str: string): number {
66
+ const results = str.match(this.matcher)
67
+ return results === null ? 0 : results.length
68
+ }
69
+ }
29
70
 
30
71
  class SourceFile {
31
- readonly dirs: string[]
32
- readonly dirPath: string
33
- readonly contractPath: string
34
- readonly artifactPath: string
35
-
36
- constructor(dirs: string[], fileName: string) {
37
- this.dirs = dirs
38
- this.dirPath = dirs.length === 0 ? '' : dirs.join('/') + '/'
39
- if (fileName.endsWith('.json')) {
40
- this.contractPath = './contracts/' + this.dirPath + fileName.slice(0, -5)
41
- this.artifactPath = './artifacts/' + this.dirPath + fileName
42
- } else {
43
- this.contractPath = './contracts/' + this.dirPath + fileName
44
- this.artifactPath = './artifacts/' + this.dirPath + fileName + '.json'
45
- }
72
+ type: SourceType
73
+ contractPath: string
74
+ sourceCode: string
75
+ sourceCodeHash: string
76
+
77
+ getArtifactPath(artifactsRootPath: string): string {
78
+ return artifactsRootPath + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json'
79
+ }
80
+
81
+ constructor(type: SourceType, sourceCode: string, sourceCodeHash: string, contractPath: string) {
82
+ this.type = type
83
+ this.sourceCode = sourceCode
84
+ this.sourceCodeHash = sourceCodeHash
85
+ this.contractPath = contractPath
86
+ }
87
+
88
+ static async from(type: SourceType, sourceCode: string, contractPath: string): Promise<SourceFile> {
89
+ const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
90
+ return new SourceFile(type, sourceCode, Buffer.from(sourceCodeHash).toString('hex'), contractPath)
46
91
  }
47
92
  }
48
93
 
49
- export abstract class Common {
50
- readonly sourceCodeSha256: string
51
- readonly functions: node.FunctionSig[]
52
-
53
- static readonly importRegex = new RegExp('^import "([^"/]+/(([^"]+)/)?)?[a-z][a-z_0-9]*.ral"', 'mg')
54
- static readonly contractRegex = new RegExp('^(Abstract[ ]+)?Contract [A-Z][a-zA-Z0-9]*', 'mg')
55
- static readonly interfaceRegex = new RegExp('^Interface [A-Z][a-zA-Z0-9]* \\{', 'mg')
56
- static readonly scriptRegex = new RegExp('^TxScript [A-Z][a-zA-Z0-9]*', 'mg')
57
-
58
- private static _artifactCache: Map<string, Contract | Script> = new Map<string, Contract | Script>()
59
- static artifactCacheCapacity = 20
60
- protected static _getArtifactFromCache(codeHash: string): Contract | Script | undefined {
61
- return this._artifactCache.get(codeHash)
62
- }
63
- protected static _putArtifactToCache(contract: Contract): void {
64
- if (!this._artifactCache.has(contract.codeHash)) {
65
- if (this._artifactCache.size >= this.artifactCacheCapacity) {
66
- const keyToDelete = this._artifactCache.keys().next().value
67
- this._artifactCache.delete(keyToDelete)
68
- }
69
- this._artifactCache.set(contract.codeHash, contract)
94
+ class Compiled<T extends Artifact> {
95
+ sourceFile: SourceFile
96
+ artifact: T
97
+ warnings: string[]
98
+
99
+ constructor(sourceFile: SourceFile, artifact: T, warnings: string[]) {
100
+ this.sourceFile = sourceFile
101
+ this.artifact = artifact
102
+ this.warnings = warnings
103
+ }
104
+ }
105
+
106
+ class ProjectArtifact {
107
+ static readonly artifactFileName = '.project.json'
108
+
109
+ compilerOptionsUsed: node.CompilerOptions
110
+ infos: Map<string, { sourceCodeHash: string; warnings: string[] }>
111
+
112
+ static checkCompilerOptionsParameter(compilerOptions: node.CompilerOptions): void {
113
+ if (Object.keys(compilerOptions).length != Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
114
+ throw Error(`Not all compiler options are set: ${compilerOptions}`)
115
+ }
116
+
117
+ const combined = { ...compilerOptions, ...DEFAULT_NODE_COMPILER_OPTIONS }
118
+ if (Object.keys(combined).length !== Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
119
+ throw Error(`There are unknown compiler options: ${compilerOptions}`)
70
120
  }
71
121
  }
72
122
 
73
- constructor(sourceCodeSha256: string, functions: node.FunctionSig[]) {
74
- this.sourceCodeSha256 = sourceCodeSha256
75
- this.functions = functions
123
+ constructor(
124
+ compilerOptionsUsed: node.CompilerOptions,
125
+ infos: Map<string, { sourceCodeHash: string; warnings: string[] }>
126
+ ) {
127
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
128
+ this.compilerOptionsUsed = compilerOptionsUsed
129
+ this.infos = infos
76
130
  }
77
131
 
78
- protected static _artifactsFolder(): string {
79
- return './artifacts/'
132
+ async saveToFile(rootPath: string): Promise<void> {
133
+ const filepath = rootPath + '/' + ProjectArtifact.artifactFileName
134
+ const artifact = { compilerOptionsUsed: this.compilerOptionsUsed, infos: Object.fromEntries(this.infos) }
135
+ const content = JSON.stringify(artifact, null, 2)
136
+ return fsPromises.writeFile(filepath, content)
80
137
  }
81
138
 
82
- static getSourceFile(path: string, _dirs: string[]): SourceFile {
83
- const parts = path.split('/')
84
- const dirs = Array.from(_dirs)
85
- if (parts.length === 1) {
86
- return new SourceFile(dirs, path)
139
+ needToReCompile(compilerOptions: node.CompilerOptions, files: SourceFile[]): boolean {
140
+ ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
141
+
142
+ const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
143
+ const usedOption = this.compilerOptionsUsed[`${key}`]
144
+ return usedOption === inputOption
145
+ })
146
+ if (!optionsMatched) {
147
+ return true
148
+ }
149
+
150
+ if (files.length !== this.infos.size) {
151
+ return true
87
152
  }
88
- parts.slice(0, parts.length - 1).forEach((part) => {
89
- switch (part) {
90
- case '.': {
91
- break
92
- }
93
- case '..': {
94
- if (dirs.length === 0) {
95
- throw new Error('Invalid file path: ' + path)
96
- }
97
- dirs.pop()
98
- break
99
- }
100
- default: {
101
- dirs.push(part)
102
- }
153
+ for (const file of files) {
154
+ const info = this.infos.get(file.contractPath)
155
+ if (typeof info === 'undefined' || info.sourceCodeHash !== file.sourceCodeHash) {
156
+ return true
103
157
  }
158
+ }
159
+
160
+ return false
161
+ }
162
+
163
+ static async from(rootPath: string): Promise<ProjectArtifact | undefined> {
164
+ const filepath = rootPath + '/' + ProjectArtifact.artifactFileName
165
+ if (!fs.existsSync(filepath)) {
166
+ return undefined
167
+ }
168
+ const content = await fsPromises.readFile(filepath)
169
+ const json = JSON.parse(content.toString())
170
+ const compilerOptionsUsed = json.compilerOptionsUsed as node.CompilerOptions
171
+ const files = new Map(Object.entries<{ sourceCodeHash: string; warnings: string[] }>(json.infos))
172
+ return new ProjectArtifact(compilerOptionsUsed, files)
173
+ }
174
+ }
175
+
176
+ export class Project {
177
+ sourceFiles: SourceFile[]
178
+ contracts: Compiled<Contract>[]
179
+ scripts: Compiled<Script>[]
180
+ projectArtifact: ProjectArtifact
181
+
182
+ readonly contractsRootPath: string
183
+ readonly artifactsRootPath: string
184
+ readonly nodeProvider: NodeProvider
185
+
186
+ static currentProject: Project
187
+
188
+ static readonly abstractContractMatcher = new TypedMatcher<SourceType>(
189
+ '^Abstract Contract [A-Z][a-zA-Z0-9]*',
190
+ SourceType.AbstractContract
191
+ )
192
+ static readonly contractMatcher = new TypedMatcher('^Contract [A-Z][a-zA-Z0-9]*', SourceType.Contract)
193
+ static readonly interfaceMatcher = new TypedMatcher('^Interface [A-Z][a-zA-Z0-9]* \\{', SourceType.Interface)
194
+ static readonly scriptMatcher = new TypedMatcher('^TxScript [A-Z][a-zA-Z0-9]*', SourceType.Script)
195
+ static readonly matchers = [
196
+ Project.abstractContractMatcher,
197
+ Project.contractMatcher,
198
+ Project.interfaceMatcher,
199
+ Project.scriptMatcher
200
+ ]
201
+
202
+ static buildProjectArtifact(
203
+ sourceFiles: SourceFile[],
204
+ contracts: Compiled<Contract>[],
205
+ scripts: Compiled<Script>[],
206
+ compilerOptions: node.CompilerOptions
207
+ ): ProjectArtifact {
208
+ const files: Map<string, { sourceCodeHash: string; warnings: string[] }> = new Map()
209
+ contracts.forEach((c) => {
210
+ files.set(c.sourceFile.contractPath, {
211
+ sourceCodeHash: c.sourceFile.sourceCodeHash,
212
+ warnings: c.warnings
213
+ })
214
+ })
215
+ scripts.forEach((s) => {
216
+ files.set(s.sourceFile.contractPath, {
217
+ sourceCodeHash: s.sourceFile.sourceCodeHash,
218
+ warnings: s.warnings
219
+ })
104
220
  })
105
- return new SourceFile(dirs, parts[parts.length - 1])
106
- }
107
-
108
- protected static async _handleImports(
109
- pathes: string[],
110
- contractStr: string,
111
- importsCache: string[]
112
- ): Promise<string> {
113
- const localImportsCache: string[] = []
114
- let result = contractStr.replace(Common.importRegex, (match) => {
115
- localImportsCache.push(match)
116
- return ''
221
+ const compiledSize = contracts.length + scripts.length
222
+ sourceFiles.slice(compiledSize).forEach((c) => {
223
+ files.set(c.contractPath, {
224
+ sourceCodeHash: c.sourceCodeHash,
225
+ warnings: []
226
+ })
117
227
  })
118
- for (const myImport of localImportsCache) {
119
- const relativePath = myImport.slice(8, -1)
120
- const importSourceFile = this.getSourceFile(relativePath, pathes)
121
- if (!importsCache.includes(importSourceFile.contractPath)) {
122
- importsCache.push(importSourceFile.contractPath)
123
- const importContractStr = await Common._loadContractStr(importSourceFile, importsCache, (code) =>
124
- Contract.checkCodeType(importSourceFile.contractPath, code)
125
- )
126
- result = result.concat('\n', importContractStr)
228
+ return new ProjectArtifact(compilerOptions, files)
229
+ }
230
+
231
+ private constructor(
232
+ provider: NodeProvider,
233
+ contractsRootPath: string,
234
+ artifactsRootPath: string,
235
+ sourceFiles: SourceFile[],
236
+ contracts: Compiled<Contract>[],
237
+ scripts: Compiled<Script>[],
238
+ errorOnWarnings: boolean,
239
+ projectArtifact: ProjectArtifact
240
+ ) {
241
+ this.nodeProvider = provider
242
+ this.contractsRootPath = contractsRootPath
243
+ this.artifactsRootPath = artifactsRootPath
244
+ this.sourceFiles = sourceFiles
245
+ this.contracts = contracts
246
+ this.scripts = scripts
247
+ this.projectArtifact = projectArtifact
248
+
249
+ if (errorOnWarnings) {
250
+ Project.checkCompilerWarnings(
251
+ [...contracts.map((c) => c.warnings).flat(), ...scripts.map((s) => s.warnings).flat()],
252
+ errorOnWarnings
253
+ )
254
+ }
255
+ }
256
+
257
+ private getContractPath(path: string): string {
258
+ return path.startsWith(`./${this.contractsRootPath}`)
259
+ ? path.slice(2)
260
+ : path.startsWith(this.contractsRootPath)
261
+ ? path
262
+ : this.contractsRootPath + '/' + path
263
+ }
264
+
265
+ static checkCompilerWarnings(warnings: string[], errorOnWarnings: boolean): void {
266
+ if (warnings.length !== 0) {
267
+ const prefixPerWarning = ' - '
268
+ const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
269
+ const output = `Compilation warnings:\n` + warningString + '\n'
270
+ if (errorOnWarnings) {
271
+ throw new Error(output)
272
+ } else {
273
+ console.log(output)
127
274
  }
128
275
  }
129
- return result
130
276
  }
131
277
 
132
- protected static async _loadContractStr(
133
- sourceFile: SourceFile,
134
- importsCache: string[],
135
- validate: (code: string) => void
136
- ): Promise<string> {
137
- const contractPath = sourceFile.contractPath
138
- const contractBuffer = await fsPromises.readFile(contractPath)
139
- const contractStr = contractBuffer.toString()
278
+ static contract(path: string): Contract {
279
+ const contractPath = Project.currentProject.getContractPath(path)
280
+ const contract = Project.currentProject.contracts.find((c) => c.sourceFile.contractPath === contractPath)
281
+ if (typeof contract === 'undefined') {
282
+ throw new Error(`Contract ${contractPath} does not exist`)
283
+ }
284
+ return contract.artifact
285
+ }
140
286
 
141
- validate(contractStr)
142
- return Common._handleImports(sourceFile.dirs, contractStr, importsCache)
287
+ static script(path: string): Script {
288
+ const contractPath = Project.currentProject.getContractPath(path)
289
+ const script = Project.currentProject.scripts.find((c) => c.sourceFile.contractPath === contractPath)
290
+ if (typeof script === 'undefined') {
291
+ throw new Error(`Script ${contractPath} does not exist`)
292
+ }
293
+ return script.artifact
143
294
  }
144
295
 
145
- static checkFileNameExtension(fileName: string): void {
146
- if (!fileName.endsWith('.ral')) {
147
- throw new Error('Smart contract file name should end with ".ral"')
296
+ private async saveArtifactsToFile(): Promise<void> {
297
+ const artifactsRootPath = this.artifactsRootPath
298
+ const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
299
+ const artifactPath = compiled.sourceFile.getArtifactPath(artifactsRootPath)
300
+ const folder = artifactPath.slice(0, artifactPath.lastIndexOf('/'))
301
+ if (!fs.existsSync(folder)) {
302
+ fs.mkdirSync(folder, { recursive: true })
303
+ }
304
+ return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
305
+ }
306
+ for (const contract of this.contracts) {
307
+ await saveToFile(contract)
308
+ }
309
+ for (const script of this.scripts) {
310
+ await saveToFile(script)
148
311
  }
312
+ await this.projectArtifact.saveToFile(this.artifactsRootPath)
149
313
  }
150
314
 
151
- protected static async _from<T extends { sourceCodeSha256: string }>(
315
+ contractByCodeHash(codeHash: string): Contract {
316
+ const contract = this.contracts.find((c) => c.artifact.codeHash === codeHash)
317
+ if (typeof contract === 'undefined') {
318
+ throw new Error(`Unknown code with code hash: ${codeHash}`)
319
+ }
320
+ return contract.artifact
321
+ }
322
+
323
+ private static async compile(
152
324
  provider: NodeProvider,
153
- sourceFile: SourceFile,
154
- loadContractStr: (sourceFile: SourceFile, importsCache: string[]) => Promise<string>,
155
- compile: (provider: NodeProvider, sourceFile: SourceFile, contractStr: string, contractHash: string) => Promise<T>
156
- ): Promise<T> {
157
- Common.checkFileNameExtension(sourceFile.contractPath)
158
-
159
- const contractStr = await loadContractStr(sourceFile, [])
160
- const contractHash = cryptojs.SHA256(contractStr).toString()
161
- const existingContract = this._getArtifactFromCache(contractHash)
162
- if (typeof existingContract !== 'undefined') {
163
- return existingContract as unknown as T
164
- } else {
165
- return compile(provider, sourceFile, contractStr, contractHash)
325
+ files: SourceFile[],
326
+ contractsRootPath: string,
327
+ artifactsRootPath: string,
328
+ errorOnWarnings: boolean,
329
+ compilerOptions: node.CompilerOptions
330
+ ): Promise<Project> {
331
+ const sourceStr = files.map((f) => f.sourceCode).join('\n')
332
+ const result = await provider.contracts.postContractsCompileProject({
333
+ code: sourceStr,
334
+ compilerOptions: compilerOptions
335
+ })
336
+ const contracts: Compiled<Contract>[] = []
337
+ const scripts: Compiled<Script>[] = []
338
+ result.contracts.forEach((contractResult, index) => {
339
+ const sourceFile = files[`${index}`]
340
+ const contract = Contract.fromCompileResult(contractResult)
341
+ contracts.push(new Compiled(sourceFile, contract, contractResult.warnings))
342
+ })
343
+ result.scripts.forEach((scriptResult, index) => {
344
+ const sourceFile = files[index + contracts.length]
345
+ const script = Script.fromCompileResult(scriptResult)
346
+ scripts.push(new Compiled(sourceFile, script, scriptResult.warnings))
347
+ })
348
+ const projectArtifact = Project.buildProjectArtifact(files, contracts, scripts, compilerOptions)
349
+ const project = new Project(
350
+ provider,
351
+ contractsRootPath,
352
+ artifactsRootPath,
353
+ files,
354
+ contracts,
355
+ scripts,
356
+ errorOnWarnings,
357
+ projectArtifact
358
+ )
359
+ await project.saveArtifactsToFile()
360
+ return project
361
+ }
362
+
363
+ private static async loadArtifacts(
364
+ provider: NodeProvider,
365
+ files: SourceFile[],
366
+ projectArtifact: ProjectArtifact,
367
+ contractsRootPath: string,
368
+ artifactsRootPath: string,
369
+ errorOnWarnings: boolean,
370
+ compilerOptions: node.CompilerOptions
371
+ ): Promise<Project> {
372
+ try {
373
+ const contracts: Compiled<Contract>[] = []
374
+ const scripts: Compiled<Script>[] = []
375
+ for (const file of files) {
376
+ const info = projectArtifact.infos.get(file.contractPath)
377
+ if (typeof info === 'undefined') {
378
+ throw Error(`Unable to find project info for ${file.contractPath}, please rebuild the project`)
379
+ }
380
+ const warnings = info.warnings
381
+ const artifactPath = file.getArtifactPath(artifactsRootPath)
382
+ if (file.type === SourceType.Contract) {
383
+ const artifact = await Contract.fromArtifactFile(artifactPath)
384
+ contracts.push(new Compiled(file, artifact, warnings))
385
+ } else if (file.type === SourceType.Script) {
386
+ const artifact = await Script.fromArtifactFile(artifactPath)
387
+ scripts.push(new Compiled(file, artifact, warnings))
388
+ }
389
+ }
390
+
391
+ return new Project(
392
+ provider,
393
+ contractsRootPath,
394
+ artifactsRootPath,
395
+ files,
396
+ contracts,
397
+ scripts,
398
+ errorOnWarnings,
399
+ projectArtifact
400
+ )
401
+ } catch (error) {
402
+ console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
403
+ return Project.compile(provider, files, contractsRootPath, artifactsRootPath, errorOnWarnings, compilerOptions)
166
404
  }
167
405
  }
168
406
 
169
- protected _saveToFile(sourceFile: SourceFile): Promise<void> {
170
- const folder = Common._artifactsFolder() + sourceFile.dirPath
171
- if (!fs.existsSync(folder)) {
172
- fs.mkdirSync(folder, { recursive: true })
407
+ private static async loadSourceFile(dirPath: string, filename: string): Promise<SourceFile> {
408
+ const contractPath = dirPath + '/' + filename
409
+ if (!filename.endsWith('.ral')) {
410
+ throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`)
411
+ }
412
+
413
+ const sourceBuffer = await fsPromises.readFile(contractPath)
414
+ const sourceStr = sourceBuffer.toString()
415
+ const results = this.matchers.map((m) => m.match(sourceStr))
416
+ const matchNumber = results.reduce((a, b) => a + b, 0)
417
+ if (matchNumber === 0) {
418
+ throw new Error(`No contract defined in file: ${contractPath}`)
419
+ }
420
+ if (matchNumber > 1) {
421
+ throw new Error(`Multiple definitions in file: ${contractPath}`)
422
+ }
423
+ const matcherIndex = results.indexOf(1)
424
+ const type = this.matchers[`${matcherIndex}`].type
425
+ return SourceFile.from(type, sourceStr, contractPath)
426
+ }
427
+
428
+ private static async loadSourceFiles(contractsRootPath: string): Promise<SourceFile[]> {
429
+ const loadDir = async function (dirPath: string, results: SourceFile[]): Promise<void> {
430
+ const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
431
+ for (const dirent of dirents) {
432
+ if (dirent.isFile()) {
433
+ const file = await Project.loadSourceFile(dirPath, dirent.name)
434
+ results.push(file)
435
+ } else {
436
+ const newPath = dirPath + '/' + dirent.name
437
+ await loadDir(newPath, results)
438
+ }
439
+ }
440
+ }
441
+ const sourceFiles: SourceFile[] = []
442
+ await loadDir(contractsRootPath, sourceFiles)
443
+ const contractAndScriptSize = sourceFiles.filter(
444
+ (f) => f.type === SourceType.Contract || f.type === SourceType.Script
445
+ ).length
446
+ if (sourceFiles.length === 0 || contractAndScriptSize === 0) {
447
+ throw new Error('Project have no source files')
173
448
  }
174
- return fsPromises.writeFile(sourceFile.artifactPath, this.toString())
449
+ return sourceFiles.sort((a, b) => a.type - b.type)
450
+ }
451
+
452
+ static async build(
453
+ compilerOptionsPartial: Partial<CompilerOptions> = {},
454
+ contractsRootPath = 'contracts',
455
+ artifactsRootPath = 'artifacts'
456
+ ): Promise<void> {
457
+ const provider = getCurrentNodeProvider()
458
+ const sourceFiles = await Project.loadSourceFiles(contractsRootPath)
459
+ const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
460
+ const projectArtifact = await ProjectArtifact.from(artifactsRootPath)
461
+ if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
462
+ console.log(`Compile contracts in folder "${contractsRootPath}"`)
463
+ Project.currentProject = await Project.compile(
464
+ provider,
465
+ sourceFiles,
466
+ contractsRootPath,
467
+ artifactsRootPath,
468
+ errorOnWarnings,
469
+ nodeCompilerOptions
470
+ )
471
+ } else {
472
+ console.log(`Load compiled contracts from folder "${artifactsRootPath}"`)
473
+ Project.currentProject = await Project.loadArtifacts(
474
+ provider,
475
+ sourceFiles,
476
+ projectArtifact,
477
+ contractsRootPath,
478
+ artifactsRootPath,
479
+ errorOnWarnings,
480
+ nodeCompilerOptions
481
+ )
482
+ }
483
+ }
484
+ }
485
+
486
+ export abstract class Artifact {
487
+ readonly functions: FunctionSig[]
488
+
489
+ constructor(functions: FunctionSig[]) {
490
+ this.functions = functions
175
491
  }
176
492
 
177
493
  abstract buildByteCodeToDeploy(initialFields?: Fields): string
494
+
495
+ publicFunctions(): string[] {
496
+ return this.functions.filter((func) => func.isPublic).map((func) => func.name)
497
+ }
498
+
499
+ usingPreapprovedAssetsFunctions(): string[] {
500
+ return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name)
501
+ }
502
+
503
+ usingAssetsInContractFunctions(): string[] {
504
+ return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name)
505
+ }
178
506
  }
179
507
 
180
- export class Contract extends Common {
508
+ export class Contract extends Artifact {
181
509
  readonly bytecode: string
182
510
  readonly codeHash: string
183
- readonly fieldsSig: node.FieldsSig
184
- readonly eventsSig: node.EventSig[]
511
+ readonly fieldsSig: FieldsSig
512
+ readonly eventsSig: EventSig[]
185
513
 
186
514
  constructor(
187
- sourceCodeSha256: string,
188
515
  bytecode: string,
189
516
  codeHash: string,
190
- fieldsSig: node.FieldsSig,
191
- eventsSig: node.EventSig[],
192
- functions: node.FunctionSig[]
517
+ fieldsSig: FieldsSig,
518
+ eventsSig: EventSig[],
519
+ functions: FunctionSig[]
193
520
  ) {
194
- super(sourceCodeSha256, functions)
521
+ super(functions)
195
522
  this.bytecode = bytecode
196
523
  this.codeHash = codeHash
197
524
  this.fieldsSig = fieldsSig
198
525
  this.eventsSig = eventsSig
199
526
  }
200
527
 
201
- static checkCodeType(fileName: string, contractStr: string): void {
202
- const interfaceMatches = contractStr.match(Contract.interfaceRegex)
203
- const contractMatches = contractStr.match(Contract.contractRegex)
204
- if (interfaceMatches === null && contractMatches === null) {
205
- throw new Error(`No contract found in: ${fileName}`)
206
- }
207
- if (interfaceMatches && contractMatches) {
208
- throw new Error(`Multiple contracts and interfaces in: ${fileName}`)
209
- }
210
- if (interfaceMatches === null) {
211
- if (contractMatches !== null && contractMatches.length > 1) {
212
- throw new Error(`Multiple contracts in: ${fileName}`)
213
- }
214
- }
215
- if (contractMatches === null) {
216
- if (interfaceMatches !== null && interfaceMatches.length > 1) {
217
- throw new Error(`Multiple interfaces in: ${fileName}`)
218
- }
219
- }
220
- }
221
-
222
- private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
223
- return Common._loadContractStr(sourceFile, [], (code) => Contract.checkCodeType(sourceFile.contractPath, code))
224
- }
225
-
226
- static async fromSource(provider: NodeProvider, path: string): Promise<Contract> {
227
- if (!fs.existsSync(Common._artifactsFolder())) {
228
- fs.mkdirSync(Common._artifactsFolder(), { recursive: true })
229
- }
230
- const sourceFile = this.getSourceFile(path, [])
231
- const contract = await Common._from(
232
- provider,
233
- sourceFile,
234
- (sourceFile) => Contract.loadContractStr(sourceFile),
235
- Contract.compile
236
- )
237
- this._putArtifactToCache(contract)
238
- return contract
239
- }
240
-
241
- private static async compile(
242
- provider: NodeProvider,
243
- sourceFile: SourceFile,
244
- contractStr: string,
245
- contractHash: string
246
- ): Promise<Contract> {
247
- const compiled = await provider.contracts.postContractsCompileContract({ code: contractStr })
248
- const artifact = new Contract(
249
- contractHash,
250
- compiled.bytecode,
251
- compiled.codeHash,
252
- compiled.fields,
253
- compiled.events,
254
- compiled.functions
255
- )
256
- await artifact._saveToFile(sourceFile)
257
- return artifact
258
- }
259
-
260
528
  // TODO: safely parse json
261
529
  static fromJson(artifact: any): Contract {
262
530
  if (
263
- artifact.sourceCodeSha256 == null ||
264
531
  artifact.bytecode == null ||
265
532
  artifact.codeHash == null ||
266
533
  artifact.fieldsSig == null ||
@@ -270,44 +537,42 @@ export class Contract extends Common {
270
537
  throw Error('The artifact JSON for contract is incomplete')
271
538
  }
272
539
  const contract = new Contract(
273
- artifact.sourceCodeSha256,
274
540
  artifact.bytecode,
275
541
  artifact.codeHash,
276
542
  artifact.fieldsSig,
277
543
  artifact.eventsSig,
278
544
  artifact.functions
279
545
  )
280
- this._putArtifactToCache(contract)
281
546
  return contract
282
547
  }
283
548
 
549
+ static fromCompileResult(result: CompileContractResult): Contract {
550
+ return new Contract(result.bytecode, result.codeHash, result.fields, result.events, result.functions)
551
+ }
552
+
284
553
  // support both 'code.ral' and 'code.ral.json'
285
554
  static async fromArtifactFile(path: string): Promise<Contract> {
286
- const sourceFile = this.getSourceFile(path, [])
287
- const artifactPath = sourceFile.artifactPath
288
- const content = await fsPromises.readFile(artifactPath)
555
+ const content = await fsPromises.readFile(path)
289
556
  const artifact = JSON.parse(content.toString())
290
557
  return Contract.fromJson(artifact)
291
558
  }
292
559
 
293
- async fetchState(provider: NodeProvider, address: string, group: number): Promise<ContractState> {
294
- const state = await provider.contracts.getContractsAddressState(address, { group: group })
560
+ async fetchState(address: string, group: number): Promise<ContractState> {
561
+ const state = await Project.currentProject.nodeProvider.contracts.getContractsAddressState(address, {
562
+ group: group
563
+ })
295
564
  return this.fromApiContractState(state)
296
565
  }
297
566
 
298
567
  override toString(): string {
299
- return JSON.stringify(
300
- {
301
- sourceCodeSha256: this.sourceCodeSha256,
302
- bytecode: this.bytecode,
303
- codeHash: this.codeHash,
304
- fieldsSig: this.fieldsSig,
305
- eventsSig: this.eventsSig,
306
- functions: this.functions
307
- },
308
- null,
309
- 2
310
- )
568
+ const object = {
569
+ bytecode: this.bytecode,
570
+ codeHash: this.codeHash,
571
+ fieldsSig: this.fieldsSig,
572
+ eventsSig: this.eventsSig,
573
+ functions: this.functions
574
+ }
575
+ return JSON.stringify(object, null, 2)
311
576
  }
312
577
 
313
578
  toState(fields: Fields, asset: Asset, address?: string): ContractState {
@@ -323,25 +588,26 @@ export class Contract extends Common {
323
588
  }
324
589
  }
325
590
 
591
+ // no need to be cryptographically strong random
326
592
  static randomAddress(): string {
327
- const bytes = crypto.randomBytes(33)
593
+ const bytes = new Uint8Array(33)
594
+ crypto.getRandomValues(bytes)
328
595
  bytes[0] = 3
329
596
  return bs58.encode(bytes)
330
597
  }
331
598
 
332
599
  private async _test(
333
- provider: NodeProvider,
334
600
  funcName: string,
335
601
  params: TestContractParams,
336
602
  expectPublic: boolean,
337
603
  accessType: string
338
604
  ): Promise<TestContractResult> {
339
605
  const apiParams: node.TestContract = this.toTestContract(funcName, params)
340
- const apiResult = await provider.contracts.postContractsTestContract(apiParams)
606
+ const apiResult = await Project.currentProject.nodeProvider.contracts.postContractsTestContract(apiParams)
341
607
 
342
608
  const methodIndex =
343
609
  typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName)
344
- const isPublic = this.functions[`${methodIndex}`].signature.indexOf('pub ') !== -1
610
+ const isPublic = this.functions[`${methodIndex}`].isPublic
345
611
  if (isPublic === expectPublic) {
346
612
  const result = await this.fromTestContractResult(methodIndex, apiResult)
347
613
  return result
@@ -350,20 +616,12 @@ export class Contract extends Common {
350
616
  }
351
617
  }
352
618
 
353
- async testPublicMethod(
354
- provider: NodeProvider,
355
- funcName: string,
356
- params: TestContractParams
357
- ): Promise<TestContractResult> {
358
- return this._test(provider, funcName, params, true, 'public')
619
+ async testPublicMethod(funcName: string, params: TestContractParams): Promise<TestContractResult> {
620
+ return this._test(funcName, params, true, 'public')
359
621
  }
360
622
 
361
- async testPrivateMethod(
362
- provider: NodeProvider,
363
- funcName: string,
364
- params: TestContractParams
365
- ): Promise<TestContractResult> {
366
- return this._test(provider, funcName, params, false, 'private')
623
+ async testPrivateMethod(funcName: string, params: TestContractParams): Promise<TestContractResult> {
624
+ return this._test(funcName, params, false, 'private')
367
625
  }
368
626
 
369
627
  toApiFields(fields?: Fields): node.Val[] {
@@ -409,33 +667,8 @@ export class Contract extends Common {
409
667
  }
410
668
  }
411
669
 
412
- static async fromCodeHash(codeHash: string): Promise<Contract> {
413
- const cached = this._getArtifactFromCache(codeHash)
414
- if (typeof cached !== 'undefined') {
415
- return cached as Contract
416
- }
417
-
418
- const files = await fsPromises.readdir(Common._artifactsFolder())
419
- for (const file of files) {
420
- if (file.endsWith('.ral.json')) {
421
- try {
422
- const contract = await Contract.fromArtifactFile(file)
423
- if (contract.codeHash === codeHash) {
424
- return contract as Contract
425
- }
426
- } catch (_) {}
427
- }
428
- }
429
-
430
- throw new Error(`Unknown code with code hash: ${codeHash}`)
431
- }
432
-
433
- static async getFieldsSig(state: node.ContractState): Promise<node.FieldsSig> {
434
- return Contract.fromCodeHash(state.codeHash).then((contract) => contract.fieldsSig)
435
- }
436
-
437
670
  async fromApiContractState(state: node.ContractState): Promise<ContractState> {
438
- const contract = await Contract.fromCodeHash(state.codeHash)
671
+ const contract = Project.currentProject.contractByCodeHash(state.codeHash)
439
672
  return {
440
673
  address: state.address,
441
674
  contractId: binToHex(contractIdFromAddress(state.address)),
@@ -443,21 +676,19 @@ export class Contract extends Common {
443
676
  initialStateHash: state.initialStateHash,
444
677
  codeHash: state.codeHash,
445
678
  fields: fromApiFields(state.fields, contract.fieldsSig),
446
- fieldsSig: await Contract.getFieldsSig(state),
679
+ fieldsSig: contract.fieldsSig,
447
680
  asset: fromApiAsset(state.asset)
448
681
  }
449
682
  }
450
683
 
451
- static ContractCreatedEvent: node.EventSig = {
684
+ static ContractCreatedEvent: EventSig = {
452
685
  name: 'ContractCreated',
453
- signature: 'event ContractCreated(address:Address)',
454
686
  fieldNames: ['address'],
455
687
  fieldTypes: ['Address']
456
688
  }
457
689
 
458
- static ContractDestroyedEvent: node.EventSig = {
690
+ static ContractDestroyedEvent: EventSig = {
459
691
  name: 'ContractDestroyed',
460
- signature: 'event ContractDestroyed(address:Address)',
461
692
  fieldNames: ['address'],
462
693
  fieldTypes: ['Address']
463
694
  }
@@ -466,14 +697,14 @@ export class Contract extends Common {
466
697
  event: node.ContractEventByTxId,
467
698
  codeHash: string | undefined
468
699
  ): Promise<ContractEventByTxId> {
469
- let eventSig: node.EventSig
700
+ let eventSig: EventSig
470
701
 
471
702
  if (event.eventIndex == -1) {
472
703
  eventSig = this.ContractCreatedEvent
473
704
  } else if (event.eventIndex == -2) {
474
705
  eventSig = this.ContractDestroyedEvent
475
706
  } else {
476
- const contract = await Contract.fromCodeHash(codeHash!)
707
+ const contract = Project.currentProject.contractByCodeHash(codeHash!)
477
708
  eventSig = contract.eventsSig[event.eventIndex]
478
709
  }
479
710
 
@@ -541,85 +772,41 @@ export class Contract extends Common {
541
772
  }
542
773
  }
543
774
 
544
- export class Script extends Common {
775
+ export class Script extends Artifact {
545
776
  readonly bytecodeTemplate: string
546
- readonly fieldsSig: node.FieldsSig
777
+ readonly fieldsSig: FieldsSig
547
778
 
548
- constructor(
549
- sourceCodeSha256: string,
550
- bytecodeTemplate: string,
551
- fieldsSig: node.FieldsSig,
552
- functions: node.FunctionSig[]
553
- ) {
554
- super(sourceCodeSha256, functions)
779
+ constructor(bytecodeTemplate: string, fieldsSig: FieldsSig, functions: FunctionSig[]) {
780
+ super(functions)
555
781
  this.bytecodeTemplate = bytecodeTemplate
556
782
  this.fieldsSig = fieldsSig
557
783
  }
558
784
 
559
- static checkCodeType(fileName: string, contractStr: string): void {
560
- const scriptMatches = contractStr.match(this.scriptRegex)
561
- if (scriptMatches === null) {
562
- throw new Error(`No script found in: ${fileName}`)
563
- } else if (scriptMatches.length > 1) {
564
- throw new Error(`Multiple scripts in: ${fileName}`)
565
- } else {
566
- return
567
- }
568
- }
569
-
570
- private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
571
- return Common._loadContractStr(sourceFile, [], (code) => Script.checkCodeType(sourceFile.contractPath, code))
572
- }
573
-
574
- static async fromSource(provider: NodeProvider, path: string): Promise<Script> {
575
- const sourceFile = this.getSourceFile(path, [])
576
- return Common._from(provider, sourceFile, (sourceFile) => Script.loadContractStr(sourceFile), Script.compile)
577
- }
578
-
579
- private static async compile(
580
- provider: NodeProvider,
581
- sourceFile: SourceFile,
582
- scriptStr: string,
583
- contractHash: string
584
- ): Promise<Script> {
585
- const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr })
586
- const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions)
587
- await artifact._saveToFile(sourceFile)
588
- return artifact
785
+ static fromCompileResult(result: CompileScriptResult): Script {
786
+ return new Script(result.bytecodeTemplate, result.fields, result.functions)
589
787
  }
590
788
 
591
789
  // TODO: safely parse json
592
790
  static fromJson(artifact: any): Script {
593
- if (
594
- artifact.sourceCodeSha256 == null ||
595
- artifact.bytecodeTemplate == null ||
596
- artifact.fieldsSig == null ||
597
- artifact.functions == null
598
- ) {
791
+ if (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
599
792
  throw Error('The artifact JSON for script is incomplete')
600
793
  }
601
- return new Script(artifact.sourceCodeSha256, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
794
+ return new Script(artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
602
795
  }
603
796
 
604
797
  static async fromArtifactFile(path: string): Promise<Script> {
605
- const sourceFile = this.getSourceFile(path, [])
606
- const artifactPath = sourceFile.artifactPath
607
- const content = await fsPromises.readFile(artifactPath)
798
+ const content = await fsPromises.readFile(path)
608
799
  const artifact = JSON.parse(content.toString())
609
800
  return this.fromJson(artifact)
610
801
  }
611
802
 
612
803
  override toString(): string {
613
- return JSON.stringify(
614
- {
615
- sourceCodeSha256: this.sourceCodeSha256,
616
- bytecodeTemplate: this.bytecodeTemplate,
617
- fieldsSig: this.fieldsSig,
618
- functions: this.functions
619
- },
620
- null,
621
- 2
622
- )
804
+ const object = {
805
+ bytecodeTemplate: this.bytecodeTemplate,
806
+ fieldsSig: this.fieldsSig,
807
+ functions: this.functions
808
+ }
809
+ return JSON.stringify(object, null, 2)
623
810
  }
624
811
 
625
812
  async paramsForDeployment(params: BuildExecuteScriptTx): Promise<SignExecuteScriptTxParams> {
@@ -895,7 +1082,7 @@ export interface ContractState {
895
1082
  initialStateHash?: string
896
1083
  codeHash: string
897
1084
  fields: Fields
898
- fieldsSig: node.FieldsSig
1085
+ fieldsSig: FieldsSig
899
1086
  asset: Asset
900
1087
  }
901
1088
 
@@ -918,12 +1105,12 @@ function toApiContractState(state: ContractState): node.ContractState {
918
1105
  }
919
1106
  }
920
1107
 
921
- function toApiFields(fields: Fields, fieldsSig: node.FieldsSig): node.Val[] {
1108
+ function toApiFields(fields: Fields, fieldsSig: FieldsSig): node.Val[] {
922
1109
  return toApiVals(fields, fieldsSig.names, fieldsSig.types)
923
1110
  }
924
1111
 
925
- function toApiArgs(args: Arguments, funcSig: node.FunctionSig): node.Val[] {
926
- return toApiVals(args, funcSig.argNames, funcSig.argTypes)
1112
+ function toApiArgs(args: Arguments, funcSig: FunctionSig): node.Val[] {
1113
+ return toApiVals(args, funcSig.paramNames, funcSig.paramTypes)
927
1114
  }
928
1115
 
929
1116
  function toApiVals(fields: Fields, names: string[], types: string[]): node.Val[] {