@alephium/web3 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/node-provider.d.ts +2 -0
- package/dist/src/api/node-provider.js +12 -6
- package/dist/src/api/utils.d.ts +1 -1
- package/dist/src/block/block.d.ts +28 -0
- package/dist/src/block/block.js +131 -0
- package/dist/src/block/index.d.ts +1 -0
- package/dist/src/block/index.js +22 -0
- package/dist/src/codec/contract-output-codec.js +4 -4
- package/dist/src/codec/lockup-script-codec.js +2 -2
- package/dist/src/codec/method-codec.d.ts +3 -1
- package/dist/src/codec/method-codec.js +27 -2
- package/dist/src/codec/script-codec.d.ts +11 -6
- package/dist/src/codec/script-codec.js +13 -2
- package/dist/src/codec/transaction-codec.js +2 -2
- package/dist/src/codec/unlock-script-codec.d.ts +2 -2
- package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
- package/dist/src/contract/contract.d.ts +23 -101
- package/dist/src/contract/contract.js +52 -538
- package/dist/src/contract/events.d.ts +1 -2
- package/dist/src/contract/events.js +28 -14
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/signer/tx-builder.js +4 -4
- package/dist/src/transaction/index.d.ts +1 -0
- package/dist/src/transaction/index.js +1 -0
- package/dist/src/transaction/status.js +28 -4
- package/dist/src/transaction/utils.d.ts +2 -0
- package/dist/src/transaction/utils.js +34 -0
- package/dist/src/utils/address.js +29 -16
- package/dist/src/utils/exchange.js +25 -15
- package/dist/src/utils/number.d.ts +1 -1
- package/dist/src/utils/sign.js +6 -6
- package/dist/src/utils/subscription.d.ts +4 -4
- package/dist/src/utils/subscription.js +1 -1
- package/package.json +3 -3
- package/src/api/node-provider.ts +8 -1
- package/src/api/utils.ts +1 -1
- package/src/block/block.ts +139 -0
- package/src/block/index.ts +19 -0
- package/src/codec/contract-output-codec.ts +1 -1
- package/src/codec/lockup-script-codec.ts +3 -3
- package/src/codec/method-codec.ts +41 -3
- package/src/codec/script-codec.ts +23 -5
- package/src/codec/transaction-codec.ts +1 -1
- package/src/codec/unlock-script-codec.ts +2 -2
- package/src/codec/unsigned-tx-codec.ts +2 -2
- package/src/contract/contract.ts +72 -779
- package/src/contract/events.ts +6 -18
- package/src/index.ts +1 -0
- package/src/signer/tx-builder.ts +2 -2
- package/src/transaction/index.ts +1 -0
- package/src/transaction/status.ts +4 -4
- package/src/transaction/utils.ts +38 -0
- package/src/utils/address.ts +15 -2
- package/src/utils/exchange.ts +32 -10
- package/src/utils/number.ts +1 -1
- package/src/utils/sign.ts +1 -1
- package/src/utils/subscription.ts +4 -4
- package/std/fungible_token_interface.ral +1 -0
- package/std/nft_collection_interface.ral +1 -0
- package/std/nft_collection_with_royalty_interface.ral +1 -0
- package/std/nft_interface.ral +1 -0
- package/webpack.config.js +0 -1
- package/dist/src/utils/error.d.ts +0 -15
- package/dist/src/utils/error.js +0 -66
- package/src/utils/error.ts +0 -77
package/src/contract/contract.ts
CHANGED
|
@@ -17,7 +17,6 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { Buffer } from 'buffer/'
|
|
20
|
-
import fs from 'fs'
|
|
21
20
|
import { promises as fsPromises } from 'fs'
|
|
22
21
|
import {
|
|
23
22
|
fromApiNumber256,
|
|
@@ -34,9 +33,9 @@ import {
|
|
|
34
33
|
getDefaultPrimitiveValue,
|
|
35
34
|
PrimitiveTypes,
|
|
36
35
|
decodeArrayType,
|
|
37
|
-
fromApiPrimitiveVal
|
|
36
|
+
fromApiPrimitiveVal,
|
|
37
|
+
tryGetCallResult
|
|
38
38
|
} from '../api'
|
|
39
|
-
import { CompileProjectResult } from '../api/api-alephium'
|
|
40
39
|
import {
|
|
41
40
|
SignDeployContractTxParams,
|
|
42
41
|
SignDeployContractTxResult,
|
|
@@ -62,11 +61,9 @@ import {
|
|
|
62
61
|
HexString
|
|
63
62
|
} from '../utils'
|
|
64
63
|
import { getCurrentNodeProvider } from '../global'
|
|
65
|
-
import * as path from 'path'
|
|
66
64
|
import { EventSubscribeOptions, EventSubscription, subscribeToEvents } from './events'
|
|
67
65
|
import { ONE_ALPH, TOTAL_NUMBER_OF_GROUPS } from '../constants'
|
|
68
66
|
import * as blake from 'blakejs'
|
|
69
|
-
import { parseError } from '../utils/error'
|
|
70
67
|
import { isContractDebugMessageEnabled } from '../debug'
|
|
71
68
|
import {
|
|
72
69
|
contract,
|
|
@@ -87,7 +84,7 @@ const crypto = new WebCrypto()
|
|
|
87
84
|
export type FieldsSig = node.FieldsSig
|
|
88
85
|
export type MapsSig = node.MapsSig
|
|
89
86
|
export type EventSig = node.EventSig
|
|
90
|
-
export type FunctionSig = node.FunctionSig
|
|
87
|
+
export type FunctionSig = Omit<node.FunctionSig, 'isPublic' | 'usePreapprovedAssets' | 'useAssetsInContract'>
|
|
91
88
|
export type Fields = NamedVals
|
|
92
89
|
export type Arguments = NamedVals
|
|
93
90
|
export type Constant = node.Constant
|
|
@@ -95,14 +92,6 @@ export type Enum = node.Enum
|
|
|
95
92
|
|
|
96
93
|
export const StdIdFieldName = '__stdInterfaceId'
|
|
97
94
|
|
|
98
|
-
enum SourceKind {
|
|
99
|
-
Contract = 0,
|
|
100
|
-
Script = 1,
|
|
101
|
-
AbstractContract = 2,
|
|
102
|
-
Interface = 3,
|
|
103
|
-
Struct = 4
|
|
104
|
-
}
|
|
105
|
-
|
|
106
95
|
export type CompilerOptions = node.CompilerOptions & {
|
|
107
96
|
errorOnWarnings: boolean
|
|
108
97
|
}
|
|
@@ -118,231 +107,6 @@ export const DEFAULT_NODE_COMPILER_OPTIONS: node.CompilerOptions = {
|
|
|
118
107
|
|
|
119
108
|
export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { errorOnWarnings: true, ...DEFAULT_NODE_COMPILER_OPTIONS }
|
|
120
109
|
|
|
121
|
-
class TypedMatcher<T extends SourceKind> {
|
|
122
|
-
matcher: RegExp
|
|
123
|
-
type: T
|
|
124
|
-
|
|
125
|
-
constructor(pattern: string, type: T) {
|
|
126
|
-
this.matcher = new RegExp(pattern, 'mg')
|
|
127
|
-
this.type = type
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function removeParentsPrefix(parts: string[]): string {
|
|
132
|
-
let index = 0
|
|
133
|
-
for (let i = 0; i < parts.length; i++) {
|
|
134
|
-
if (parts[`${i}`] === '..') {
|
|
135
|
-
index += 1
|
|
136
|
-
} else {
|
|
137
|
-
break
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return path.join(...parts.slice(index))
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
class SourceInfo {
|
|
144
|
-
type: SourceKind
|
|
145
|
-
name: string
|
|
146
|
-
contractRelativePath: string
|
|
147
|
-
sourceCode: string
|
|
148
|
-
sourceCodeHash: string
|
|
149
|
-
isExternal: boolean
|
|
150
|
-
|
|
151
|
-
getArtifactPath(artifactsRootDir: string): string {
|
|
152
|
-
let fullPath: string
|
|
153
|
-
if (this.isExternal) {
|
|
154
|
-
const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep))
|
|
155
|
-
const externalPath = path.join('.external', relativePath)
|
|
156
|
-
fullPath = path.join(artifactsRootDir, externalPath)
|
|
157
|
-
} else {
|
|
158
|
-
fullPath = path.join(artifactsRootDir, this.contractRelativePath)
|
|
159
|
-
}
|
|
160
|
-
return path.join(path.dirname(fullPath), `${this.name}.ral.json`)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
constructor(
|
|
164
|
-
type: SourceKind,
|
|
165
|
-
name: string,
|
|
166
|
-
sourceCode: string,
|
|
167
|
-
sourceCodeHash: string,
|
|
168
|
-
contractRelativePath: string,
|
|
169
|
-
isExternal: boolean
|
|
170
|
-
) {
|
|
171
|
-
this.type = type
|
|
172
|
-
this.name = name
|
|
173
|
-
this.sourceCode = sourceCode
|
|
174
|
-
this.sourceCodeHash = sourceCodeHash
|
|
175
|
-
this.contractRelativePath = contractRelativePath
|
|
176
|
-
this.isExternal = isExternal
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
static async from(
|
|
180
|
-
type: SourceKind,
|
|
181
|
-
name: string,
|
|
182
|
-
sourceCode: string,
|
|
183
|
-
contractRelativePath: string,
|
|
184
|
-
isExternal: boolean
|
|
185
|
-
): Promise<SourceInfo> {
|
|
186
|
-
const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
|
|
187
|
-
const sourceCodeHashHex = Buffer.from(sourceCodeHash).toString('hex')
|
|
188
|
-
return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
class Compiled<T extends Artifact> {
|
|
193
|
-
sourceInfo: SourceInfo
|
|
194
|
-
artifact: T
|
|
195
|
-
warnings: string[]
|
|
196
|
-
|
|
197
|
-
constructor(sourceInfo: SourceInfo, artifact: T, warnings: string[]) {
|
|
198
|
-
this.sourceInfo = sourceInfo
|
|
199
|
-
this.artifact = artifact
|
|
200
|
-
this.warnings = warnings
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
type CodeInfo = {
|
|
205
|
-
sourceFile: string
|
|
206
|
-
sourceCodeHash: string
|
|
207
|
-
bytecodeDebugPatch: string
|
|
208
|
-
codeHashDebug: string
|
|
209
|
-
warnings: string[]
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
type SourceInfoIndexes = {
|
|
213
|
-
sourceInfo: SourceInfo
|
|
214
|
-
startIndex: number
|
|
215
|
-
endIndex: number
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function findSourceInfoAtLineNumber(sources: SourceInfo[], line: number): SourceInfoIndexes | undefined {
|
|
219
|
-
let currentLine = 0
|
|
220
|
-
const sourceInfosWithLine: SourceInfoIndexes[] = sources.map((source) => {
|
|
221
|
-
const startIndex = currentLine + 1
|
|
222
|
-
currentLine += source.sourceCode.split('\n').length
|
|
223
|
-
const endIndex = currentLine
|
|
224
|
-
return { sourceInfo: source, startIndex: startIndex, endIndex: endIndex }
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
const sourceInfo = sourceInfosWithLine.find((sourceInfoWithLine) => {
|
|
228
|
-
return line >= sourceInfoWithLine.startIndex && line <= sourceInfoWithLine.endIndex
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
return sourceInfo
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export class ProjectArtifact {
|
|
235
|
-
static readonly artifactFileName = '.project.json'
|
|
236
|
-
|
|
237
|
-
fullNodeVersion: string
|
|
238
|
-
compilerOptionsUsed: node.CompilerOptions
|
|
239
|
-
infos: Map<string, CodeInfo>
|
|
240
|
-
|
|
241
|
-
static checkCompilerOptionsParameter(compilerOptions: node.CompilerOptions): void {
|
|
242
|
-
if (Object.keys(compilerOptions).length != Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
|
|
243
|
-
throw Error(`Not all compiler options are set: ${compilerOptions}`)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const combined = { ...compilerOptions, ...DEFAULT_NODE_COMPILER_OPTIONS }
|
|
247
|
-
if (Object.keys(combined).length !== Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
|
|
248
|
-
throw Error(`There are unknown compiler options: ${compilerOptions}`)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
constructor(fullNodeVersion: string, compilerOptionsUsed: node.CompilerOptions, infos: Map<string, CodeInfo>) {
|
|
253
|
-
ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
|
|
254
|
-
this.fullNodeVersion = fullNodeVersion
|
|
255
|
-
this.compilerOptionsUsed = compilerOptionsUsed
|
|
256
|
-
this.infos = infos
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
static isCodeChanged(current: ProjectArtifact, previous: ProjectArtifact): boolean {
|
|
260
|
-
if (current.infos.size !== previous.infos.size) {
|
|
261
|
-
return true
|
|
262
|
-
}
|
|
263
|
-
for (const [name, codeInfo] of current.infos) {
|
|
264
|
-
const prevCodeInfo = previous.infos.get(name)
|
|
265
|
-
if (prevCodeInfo?.codeHashDebug !== codeInfo.codeHashDebug) {
|
|
266
|
-
return true
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return false
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
async saveToFile(rootPath: string): Promise<void> {
|
|
273
|
-
const filepath = path.join(rootPath, ProjectArtifact.artifactFileName)
|
|
274
|
-
const artifact = {
|
|
275
|
-
fullNodeVersion: this.fullNodeVersion,
|
|
276
|
-
compilerOptionsUsed: this.compilerOptionsUsed,
|
|
277
|
-
infos: Object.fromEntries(new Map([...this.infos].sort()))
|
|
278
|
-
}
|
|
279
|
-
const content = JSON.stringify(artifact, null, 2)
|
|
280
|
-
return fsPromises.writeFile(filepath, content)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
needToReCompile(compilerOptions: node.CompilerOptions, sourceInfos: SourceInfo[], fullNodeVersion: string): boolean {
|
|
284
|
-
ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
|
|
285
|
-
if (this.fullNodeVersion !== fullNodeVersion) {
|
|
286
|
-
return true
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
|
|
290
|
-
const usedOption = this.compilerOptionsUsed[`${key}`]
|
|
291
|
-
return usedOption === inputOption
|
|
292
|
-
})
|
|
293
|
-
if (!optionsMatched) {
|
|
294
|
-
return true
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (sourceInfos.length !== this.infos.size) {
|
|
298
|
-
return true
|
|
299
|
-
}
|
|
300
|
-
for (const sourceInfo of sourceInfos) {
|
|
301
|
-
const info = this.infos.get(sourceInfo.name)
|
|
302
|
-
if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
|
|
303
|
-
return true
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return false
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
static async from(rootPath: string): Promise<ProjectArtifact | undefined> {
|
|
311
|
-
const filepath = path.join(rootPath, ProjectArtifact.artifactFileName)
|
|
312
|
-
if (!fs.existsSync(filepath)) {
|
|
313
|
-
return undefined
|
|
314
|
-
}
|
|
315
|
-
try {
|
|
316
|
-
const content = await fsPromises.readFile(filepath)
|
|
317
|
-
const json = JSON.parse(content.toString())
|
|
318
|
-
const fullNodeVersion = json.fullNodeVersion as string
|
|
319
|
-
const compilerOptionsUsed = json.compilerOptionsUsed as node.CompilerOptions
|
|
320
|
-
const files = new Map(Object.entries<CodeInfo>(json.infos))
|
|
321
|
-
return new ProjectArtifact(fullNodeVersion, compilerOptionsUsed, files)
|
|
322
|
-
} catch (error) {
|
|
323
|
-
console.error(`Failed to load project artifact, error: ${error}`)
|
|
324
|
-
return undefined
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function removeOldArtifacts(dir: string) {
|
|
330
|
-
const files = fs.readdirSync(dir)
|
|
331
|
-
files.forEach((file) => {
|
|
332
|
-
const filePath = path.join(dir, file)
|
|
333
|
-
const stat = fs.statSync(filePath)
|
|
334
|
-
if (stat.isDirectory()) {
|
|
335
|
-
removeOldArtifacts(filePath)
|
|
336
|
-
} else if (filePath.endsWith('.ral.json') || filePath.endsWith('.ral')) {
|
|
337
|
-
fs.unlinkSync(filePath)
|
|
338
|
-
}
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
if (fs.readdirSync(dir).length === 0) {
|
|
342
|
-
fs.rmdirSync(dir)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
110
|
export class Struct {
|
|
347
111
|
name: string
|
|
348
112
|
fieldNames: string[]
|
|
@@ -377,491 +141,6 @@ export class Struct {
|
|
|
377
141
|
}
|
|
378
142
|
}
|
|
379
143
|
|
|
380
|
-
export class Project {
|
|
381
|
-
sourceInfos: SourceInfo[]
|
|
382
|
-
contracts: Map<string, Compiled<Contract>>
|
|
383
|
-
scripts: Map<string, Compiled<Script>>
|
|
384
|
-
structs: Struct[]
|
|
385
|
-
projectArtifact: ProjectArtifact
|
|
386
|
-
|
|
387
|
-
readonly contractsRootDir: string
|
|
388
|
-
readonly artifactsRootDir: string
|
|
389
|
-
|
|
390
|
-
static currentProject: Project
|
|
391
|
-
|
|
392
|
-
static readonly importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg')
|
|
393
|
-
static readonly abstractContractMatcher = new TypedMatcher<SourceKind>(
|
|
394
|
-
'^Abstract Contract ([A-Z][a-zA-Z0-9]*)',
|
|
395
|
-
SourceKind.AbstractContract
|
|
396
|
-
)
|
|
397
|
-
static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract)
|
|
398
|
-
static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface)
|
|
399
|
-
static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script)
|
|
400
|
-
static readonly structMatcher = new TypedMatcher('struct ([A-Z][a-zA-Z0-9]*)', SourceKind.Struct)
|
|
401
|
-
static readonly matchers = [
|
|
402
|
-
Project.abstractContractMatcher,
|
|
403
|
-
Project.contractMatcher,
|
|
404
|
-
Project.interfaceMatcher,
|
|
405
|
-
Project.scriptMatcher,
|
|
406
|
-
Project.structMatcher
|
|
407
|
-
]
|
|
408
|
-
|
|
409
|
-
static buildProjectArtifact(
|
|
410
|
-
fullNodeVersion: string,
|
|
411
|
-
sourceInfos: SourceInfo[],
|
|
412
|
-
contracts: Map<string, Compiled<Contract>>,
|
|
413
|
-
scripts: Map<string, Compiled<Script>>,
|
|
414
|
-
compilerOptions: node.CompilerOptions
|
|
415
|
-
): ProjectArtifact {
|
|
416
|
-
const files: Map<string, CodeInfo> = new Map()
|
|
417
|
-
contracts.forEach((c) => {
|
|
418
|
-
files.set(c.artifact.name, {
|
|
419
|
-
sourceFile: c.sourceInfo.contractRelativePath,
|
|
420
|
-
sourceCodeHash: c.sourceInfo.sourceCodeHash,
|
|
421
|
-
bytecodeDebugPatch: c.artifact.bytecodeDebugPatch,
|
|
422
|
-
codeHashDebug: c.artifact.codeHashDebug,
|
|
423
|
-
warnings: c.warnings
|
|
424
|
-
})
|
|
425
|
-
})
|
|
426
|
-
scripts.forEach((s) => {
|
|
427
|
-
files.set(s.artifact.name, {
|
|
428
|
-
sourceFile: s.sourceInfo.contractRelativePath,
|
|
429
|
-
sourceCodeHash: s.sourceInfo.sourceCodeHash,
|
|
430
|
-
bytecodeDebugPatch: s.artifact.bytecodeDebugPatch,
|
|
431
|
-
codeHashDebug: '',
|
|
432
|
-
warnings: s.warnings
|
|
433
|
-
})
|
|
434
|
-
})
|
|
435
|
-
const compiledSize = contracts.size + scripts.size
|
|
436
|
-
sourceInfos.slice(compiledSize).forEach((c) => {
|
|
437
|
-
files.set(c.name, {
|
|
438
|
-
sourceFile: c.contractRelativePath,
|
|
439
|
-
sourceCodeHash: c.sourceCodeHash,
|
|
440
|
-
bytecodeDebugPatch: '',
|
|
441
|
-
codeHashDebug: '',
|
|
442
|
-
warnings: []
|
|
443
|
-
})
|
|
444
|
-
})
|
|
445
|
-
return new ProjectArtifact(fullNodeVersion, compilerOptions, files)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
private constructor(
|
|
449
|
-
contractsRootDir: string,
|
|
450
|
-
artifactsRootDir: string,
|
|
451
|
-
sourceInfos: SourceInfo[],
|
|
452
|
-
contracts: Map<string, Compiled<Contract>>,
|
|
453
|
-
scripts: Map<string, Compiled<Script>>,
|
|
454
|
-
structs: Struct[],
|
|
455
|
-
errorOnWarnings: boolean,
|
|
456
|
-
projectArtifact: ProjectArtifact
|
|
457
|
-
) {
|
|
458
|
-
this.contractsRootDir = contractsRootDir
|
|
459
|
-
this.artifactsRootDir = artifactsRootDir
|
|
460
|
-
this.sourceInfos = sourceInfos
|
|
461
|
-
this.contracts = contracts
|
|
462
|
-
this.scripts = scripts
|
|
463
|
-
this.structs = structs
|
|
464
|
-
this.projectArtifact = projectArtifact
|
|
465
|
-
|
|
466
|
-
if (errorOnWarnings) {
|
|
467
|
-
Project.checkCompilerWarnings(
|
|
468
|
-
[
|
|
469
|
-
...[...contracts.entries()].map((c) => c[1].warnings).flat(),
|
|
470
|
-
...[...scripts.entries()].map((s) => s[1].warnings).flat()
|
|
471
|
-
],
|
|
472
|
-
errorOnWarnings
|
|
473
|
-
)
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
static checkCompilerWarnings(warnings: string[], errorOnWarnings: boolean): void {
|
|
478
|
-
if (warnings.length !== 0) {
|
|
479
|
-
const prefixPerWarning = ' - '
|
|
480
|
-
const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
|
|
481
|
-
const output = `Compilation warnings:\n` + warningString + '\n'
|
|
482
|
-
if (errorOnWarnings) {
|
|
483
|
-
throw new Error(output)
|
|
484
|
-
} else {
|
|
485
|
-
console.log(output)
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
static contract(name: string): Contract {
|
|
491
|
-
const contract = Project.currentProject.contracts.get(name)
|
|
492
|
-
if (typeof contract === 'undefined') {
|
|
493
|
-
throw new Error(`Contract "${name}" does not exist`)
|
|
494
|
-
}
|
|
495
|
-
return contract.artifact
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
static script(name: string): Script {
|
|
499
|
-
const script = Project.currentProject.scripts.get(name)
|
|
500
|
-
if (typeof script === 'undefined') {
|
|
501
|
-
throw new Error(`Script "${name}" does not exist`)
|
|
502
|
-
}
|
|
503
|
-
return script.artifact
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
private static async loadStructs(artifactsRootDir: string): Promise<Struct[]> {
|
|
507
|
-
const filePath = path.join(artifactsRootDir, 'structs.ral.json')
|
|
508
|
-
if (!fs.existsSync(filePath)) return []
|
|
509
|
-
const content = await fsPromises.readFile(filePath)
|
|
510
|
-
const json = JSON.parse(content.toString())
|
|
511
|
-
if (!Array.isArray(json)) {
|
|
512
|
-
throw Error(`Invalid structs JSON: ${content}`)
|
|
513
|
-
}
|
|
514
|
-
return Array.from(json).map((item) => Struct.fromJson(item))
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
private async saveStructsToFile(): Promise<void> {
|
|
518
|
-
if (this.structs.length === 0) return
|
|
519
|
-
const structs = this.structs.map((s) => s.toJson())
|
|
520
|
-
const filePath = path.join(this.artifactsRootDir, 'structs.ral.json')
|
|
521
|
-
return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2))
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
private async saveArtifactsToFile(projectRootDir: string): Promise<void> {
|
|
525
|
-
const artifactsRootDir = this.artifactsRootDir
|
|
526
|
-
const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
|
|
527
|
-
const artifactPath = compiled.sourceInfo.getArtifactPath(artifactsRootDir)
|
|
528
|
-
const dirname = path.dirname(artifactPath)
|
|
529
|
-
if (!fs.existsSync(dirname)) {
|
|
530
|
-
fs.mkdirSync(dirname, { recursive: true })
|
|
531
|
-
}
|
|
532
|
-
return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
|
|
533
|
-
}
|
|
534
|
-
this.contracts.forEach((contract) => saveToFile(contract))
|
|
535
|
-
this.scripts.forEach((script) => saveToFile(script))
|
|
536
|
-
this.saveStructsToFile()
|
|
537
|
-
await this.projectArtifact.saveToFile(projectRootDir)
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
contractByCodeHash(codeHash: string): Contract {
|
|
541
|
-
const contract = [...this.contracts.values()].find(
|
|
542
|
-
(c) => c.artifact.codeHash === codeHash || c.artifact.codeHashDebug == codeHash
|
|
543
|
-
)
|
|
544
|
-
if (typeof contract === 'undefined') {
|
|
545
|
-
throw new Error(`Unknown code with code hash: ${codeHash}`)
|
|
546
|
-
}
|
|
547
|
-
return contract.artifact
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
private static async getCompileResult(
|
|
551
|
-
provider: NodeProvider,
|
|
552
|
-
compilerOptions: node.CompilerOptions,
|
|
553
|
-
sources: SourceInfo[]
|
|
554
|
-
): Promise<CompileProjectResult> {
|
|
555
|
-
try {
|
|
556
|
-
const sourceStr = sources.map((f) => f.sourceCode).join('\n')
|
|
557
|
-
return await provider.contracts.postContractsCompileProject({
|
|
558
|
-
code: sourceStr,
|
|
559
|
-
compilerOptions: compilerOptions
|
|
560
|
-
})
|
|
561
|
-
} catch (error) {
|
|
562
|
-
if (!(error instanceof Error)) {
|
|
563
|
-
throw error
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const parsed = parseError(error.message)
|
|
567
|
-
if (!parsed) {
|
|
568
|
-
throw error
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const sourceInfo = findSourceInfoAtLineNumber(sources, parsed.lineStart)
|
|
572
|
-
if (!sourceInfo) {
|
|
573
|
-
throw error
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const shiftIndex = parsed.lineStart - sourceInfo.startIndex + 1
|
|
577
|
-
const newError = parsed.reformat(shiftIndex, sourceInfo.sourceInfo.contractRelativePath)
|
|
578
|
-
throw new Error(newError)
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
private static async compile(
|
|
583
|
-
fullNodeVersion: string,
|
|
584
|
-
provider: NodeProvider,
|
|
585
|
-
sourceInfos: SourceInfo[],
|
|
586
|
-
projectRootDir: string,
|
|
587
|
-
contractsRootDir: string,
|
|
588
|
-
artifactsRootDir: string,
|
|
589
|
-
errorOnWarnings: boolean,
|
|
590
|
-
compilerOptions: node.CompilerOptions
|
|
591
|
-
): Promise<Project> {
|
|
592
|
-
const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => {
|
|
593
|
-
if (acc.find((info) => info.sourceCodeHash === sourceInfo.sourceCodeHash) === undefined) {
|
|
594
|
-
acc.push(sourceInfo)
|
|
595
|
-
}
|
|
596
|
-
return acc
|
|
597
|
-
}, [])
|
|
598
|
-
|
|
599
|
-
const result = await Project.getCompileResult(provider, compilerOptions, removeDuplicates)
|
|
600
|
-
const contracts = new Map<string, Compiled<Contract>>()
|
|
601
|
-
const scripts = new Map<string, Compiled<Script>>()
|
|
602
|
-
const structs = result.structs === undefined ? [] : result.structs.map((item) => Struct.fromStructSig(item))
|
|
603
|
-
result.contracts.forEach((contractResult) => {
|
|
604
|
-
const sourceInfo = sourceInfos.find(
|
|
605
|
-
(sourceInfo) => sourceInfo.type === SourceKind.Contract && sourceInfo.name === contractResult.name
|
|
606
|
-
)
|
|
607
|
-
if (sourceInfo === undefined) {
|
|
608
|
-
// this should never happen
|
|
609
|
-
throw new Error(`SourceInfo does not exist for contract ${contractResult.name}`)
|
|
610
|
-
}
|
|
611
|
-
const contract = Contract.fromCompileResult(contractResult, structs)
|
|
612
|
-
contracts.set(contract.name, new Compiled(sourceInfo, contract, contractResult.warnings))
|
|
613
|
-
})
|
|
614
|
-
result.scripts.forEach((scriptResult) => {
|
|
615
|
-
const sourceInfo = sourceInfos.find(
|
|
616
|
-
(sourceInfo) => sourceInfo.type === SourceKind.Script && sourceInfo.name === scriptResult.name
|
|
617
|
-
)
|
|
618
|
-
if (sourceInfo === undefined) {
|
|
619
|
-
// this should never happen
|
|
620
|
-
throw new Error(`SourceInfo does not exist for script ${scriptResult.name}`)
|
|
621
|
-
}
|
|
622
|
-
const script = Script.fromCompileResult(scriptResult, structs)
|
|
623
|
-
scripts.set(script.name, new Compiled(sourceInfo, script, scriptResult.warnings))
|
|
624
|
-
})
|
|
625
|
-
const projectArtifact = Project.buildProjectArtifact(
|
|
626
|
-
fullNodeVersion,
|
|
627
|
-
sourceInfos,
|
|
628
|
-
contracts,
|
|
629
|
-
scripts,
|
|
630
|
-
compilerOptions
|
|
631
|
-
)
|
|
632
|
-
const project = new Project(
|
|
633
|
-
contractsRootDir,
|
|
634
|
-
artifactsRootDir,
|
|
635
|
-
sourceInfos,
|
|
636
|
-
contracts,
|
|
637
|
-
scripts,
|
|
638
|
-
structs,
|
|
639
|
-
errorOnWarnings,
|
|
640
|
-
projectArtifact
|
|
641
|
-
)
|
|
642
|
-
await project.saveArtifactsToFile(projectRootDir)
|
|
643
|
-
return project
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
private static async loadArtifacts(
|
|
647
|
-
provider: NodeProvider,
|
|
648
|
-
sourceInfos: SourceInfo[],
|
|
649
|
-
projectArtifact: ProjectArtifact,
|
|
650
|
-
projectRootDir: string,
|
|
651
|
-
contractsRootDir: string,
|
|
652
|
-
artifactsRootDir: string,
|
|
653
|
-
errorOnWarnings: boolean,
|
|
654
|
-
compilerOptions: node.CompilerOptions
|
|
655
|
-
): Promise<Project> {
|
|
656
|
-
try {
|
|
657
|
-
const contracts = new Map<string, Compiled<Contract>>()
|
|
658
|
-
const scripts = new Map<string, Compiled<Script>>()
|
|
659
|
-
const structs = await Project.loadStructs(artifactsRootDir)
|
|
660
|
-
for (const sourceInfo of sourceInfos) {
|
|
661
|
-
const info = projectArtifact.infos.get(sourceInfo.name)
|
|
662
|
-
if (typeof info === 'undefined') {
|
|
663
|
-
throw Error(`Unable to find project info for ${sourceInfo.name}, please rebuild the project`)
|
|
664
|
-
}
|
|
665
|
-
const warnings = info.warnings
|
|
666
|
-
const artifactDir = sourceInfo.getArtifactPath(artifactsRootDir)
|
|
667
|
-
if (sourceInfo.type === SourceKind.Contract) {
|
|
668
|
-
const artifact = await Contract.fromArtifactFile(
|
|
669
|
-
artifactDir,
|
|
670
|
-
info.bytecodeDebugPatch,
|
|
671
|
-
info.codeHashDebug,
|
|
672
|
-
structs
|
|
673
|
-
)
|
|
674
|
-
contracts.set(artifact.name, new Compiled(sourceInfo, artifact, warnings))
|
|
675
|
-
} else if (sourceInfo.type === SourceKind.Script) {
|
|
676
|
-
const artifact = await Script.fromArtifactFile(artifactDir, info.bytecodeDebugPatch, structs)
|
|
677
|
-
scripts.set(artifact.name, new Compiled(sourceInfo, artifact, warnings))
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
return new Project(
|
|
682
|
-
contractsRootDir,
|
|
683
|
-
artifactsRootDir,
|
|
684
|
-
sourceInfos,
|
|
685
|
-
contracts,
|
|
686
|
-
scripts,
|
|
687
|
-
structs,
|
|
688
|
-
errorOnWarnings,
|
|
689
|
-
projectArtifact
|
|
690
|
-
)
|
|
691
|
-
} catch (error) {
|
|
692
|
-
console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
|
|
693
|
-
return Project.compile(
|
|
694
|
-
projectArtifact.fullNodeVersion,
|
|
695
|
-
provider,
|
|
696
|
-
sourceInfos,
|
|
697
|
-
projectRootDir,
|
|
698
|
-
contractsRootDir,
|
|
699
|
-
artifactsRootDir,
|
|
700
|
-
errorOnWarnings,
|
|
701
|
-
compilerOptions
|
|
702
|
-
)
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
private static getImportSourcePath(projectRootDir: string, importPath: string): string {
|
|
707
|
-
const parts = importPath.split('/')
|
|
708
|
-
if (parts.length > 1 && parts[0] === 'std') {
|
|
709
|
-
const currentDir = path.dirname(__filename)
|
|
710
|
-
return path.join(...[currentDir, '..', '..', '..', importPath])
|
|
711
|
-
}
|
|
712
|
-
let moduleDir = projectRootDir
|
|
713
|
-
while (true) {
|
|
714
|
-
const expectedPath = path.join(...[moduleDir, 'node_modules', importPath])
|
|
715
|
-
if (fs.existsSync(expectedPath)) {
|
|
716
|
-
return expectedPath
|
|
717
|
-
}
|
|
718
|
-
const oldModuleDir = moduleDir
|
|
719
|
-
moduleDir = path.join(moduleDir, '..')
|
|
720
|
-
if (oldModuleDir === moduleDir) {
|
|
721
|
-
throw new Error(`Specified import file does not exist: ${importPath}`)
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
private static async handleImports(
|
|
727
|
-
projectRootDir: string,
|
|
728
|
-
contractRootDir: string,
|
|
729
|
-
sourceStr: string,
|
|
730
|
-
importsCache: string[]
|
|
731
|
-
): Promise<[string, SourceInfo[]]> {
|
|
732
|
-
const localImportsCache: string[] = []
|
|
733
|
-
const result = sourceStr.replace(Project.importRegex, (match) => {
|
|
734
|
-
localImportsCache.push(match)
|
|
735
|
-
return ''
|
|
736
|
-
})
|
|
737
|
-
const externalSourceInfos: SourceInfo[] = []
|
|
738
|
-
for (const myImport of localImportsCache) {
|
|
739
|
-
const originImportPath = myImport.slice(8, -1)
|
|
740
|
-
const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral'
|
|
741
|
-
if (!importsCache.includes(importPath)) {
|
|
742
|
-
importsCache.push(importPath)
|
|
743
|
-
const sourcePath = Project.getImportSourcePath(projectRootDir, importPath)
|
|
744
|
-
const sourceInfos = await Project.loadSourceFile(
|
|
745
|
-
projectRootDir,
|
|
746
|
-
contractRootDir,
|
|
747
|
-
sourcePath,
|
|
748
|
-
importsCache,
|
|
749
|
-
true
|
|
750
|
-
)
|
|
751
|
-
externalSourceInfos.push(...sourceInfos)
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return [result, externalSourceInfos]
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
private static async loadSourceFile(
|
|
758
|
-
projectRootDir: string,
|
|
759
|
-
contractsRootDir: string,
|
|
760
|
-
sourcePath: string,
|
|
761
|
-
importsCache: string[],
|
|
762
|
-
isExternal: boolean
|
|
763
|
-
): Promise<SourceInfo[]> {
|
|
764
|
-
const contractRelativePath = path.relative(contractsRootDir, sourcePath)
|
|
765
|
-
if (!sourcePath.endsWith('.ral')) {
|
|
766
|
-
throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`)
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const sourceBuffer = await fsPromises.readFile(sourcePath)
|
|
770
|
-
const [sourceStr, externalSourceInfos] = await Project.handleImports(
|
|
771
|
-
projectRootDir,
|
|
772
|
-
contractsRootDir,
|
|
773
|
-
sourceBuffer.toString(),
|
|
774
|
-
importsCache
|
|
775
|
-
)
|
|
776
|
-
if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
|
|
777
|
-
throw new Error(`Invalid import statements, source: ${sourcePath}`)
|
|
778
|
-
}
|
|
779
|
-
const sourceInfos = externalSourceInfos
|
|
780
|
-
for (const matcher of this.matchers) {
|
|
781
|
-
const results = sourceStr.matchAll(matcher.matcher)
|
|
782
|
-
for (const result of results) {
|
|
783
|
-
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal)
|
|
784
|
-
sourceInfos.push(sourceInfo)
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
return sourceInfos
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
private static async loadSourceFiles(projectRootDir: string, contractsRootDir: string): Promise<SourceInfo[]> {
|
|
791
|
-
const importsCache: string[] = []
|
|
792
|
-
const sourceInfos: SourceInfo[] = []
|
|
793
|
-
const loadDir = async function (dirPath: string): Promise<void> {
|
|
794
|
-
const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
|
|
795
|
-
for (const dirent of dirents) {
|
|
796
|
-
if (dirent.isFile()) {
|
|
797
|
-
const sourcePath = path.join(dirPath, dirent.name)
|
|
798
|
-
const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false)
|
|
799
|
-
sourceInfos.push(...infos)
|
|
800
|
-
} else {
|
|
801
|
-
const newPath = path.join(dirPath, dirent.name)
|
|
802
|
-
await loadDir(newPath)
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
await loadDir(contractsRootDir)
|
|
807
|
-
const contractAndScriptSize = sourceInfos.filter(
|
|
808
|
-
(f) => f.type === SourceKind.Contract || f.type === SourceKind.Script
|
|
809
|
-
).length
|
|
810
|
-
if (sourceInfos.length === 0 || contractAndScriptSize === 0) {
|
|
811
|
-
throw new Error('Project have no source files')
|
|
812
|
-
}
|
|
813
|
-
return sourceInfos.sort((a, b) => a.type - b.type)
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
static readonly DEFAULT_CONTRACTS_DIR = 'contracts'
|
|
817
|
-
static readonly DEFAULT_ARTIFACTS_DIR = 'artifacts'
|
|
818
|
-
|
|
819
|
-
static async build(
|
|
820
|
-
compilerOptionsPartial: Partial<CompilerOptions> = {},
|
|
821
|
-
projectRootDir = '.',
|
|
822
|
-
contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
|
|
823
|
-
artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR,
|
|
824
|
-
defaultFullNodeVersion: string | undefined = undefined
|
|
825
|
-
): Promise<void> {
|
|
826
|
-
const provider = getCurrentNodeProvider()
|
|
827
|
-
const fullNodeVersion = defaultFullNodeVersion ?? (await provider.infos.getInfosVersion()).version
|
|
828
|
-
const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
|
|
829
|
-
const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
|
|
830
|
-
const projectArtifact = await ProjectArtifact.from(projectRootDir)
|
|
831
|
-
if (
|
|
832
|
-
projectArtifact === undefined ||
|
|
833
|
-
projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles, fullNodeVersion)
|
|
834
|
-
) {
|
|
835
|
-
if (fs.existsSync(artifactsRootDir)) {
|
|
836
|
-
removeOldArtifacts(artifactsRootDir)
|
|
837
|
-
}
|
|
838
|
-
console.log(`Compiling contracts in folder "${contractsRootDir}"`)
|
|
839
|
-
Project.currentProject = await Project.compile(
|
|
840
|
-
fullNodeVersion,
|
|
841
|
-
provider,
|
|
842
|
-
sourceFiles,
|
|
843
|
-
projectRootDir,
|
|
844
|
-
contractsRootDir,
|
|
845
|
-
artifactsRootDir,
|
|
846
|
-
errorOnWarnings,
|
|
847
|
-
nodeCompilerOptions
|
|
848
|
-
)
|
|
849
|
-
} else {
|
|
850
|
-
console.log(`Contracts are compiled already. Loading them from folder "${artifactsRootDir}"`)
|
|
851
|
-
Project.currentProject = await Project.loadArtifacts(
|
|
852
|
-
provider,
|
|
853
|
-
sourceFiles,
|
|
854
|
-
projectArtifact,
|
|
855
|
-
projectRootDir,
|
|
856
|
-
contractsRootDir,
|
|
857
|
-
artifactsRootDir,
|
|
858
|
-
errorOnWarnings,
|
|
859
|
-
nodeCompilerOptions
|
|
860
|
-
)
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
144
|
export abstract class Artifact {
|
|
866
145
|
readonly version: string
|
|
867
146
|
readonly name: string
|
|
@@ -875,18 +154,6 @@ export abstract class Artifact {
|
|
|
875
154
|
|
|
876
155
|
abstract buildByteCodeToDeploy(initialFields: Fields, isDevnet: boolean): string
|
|
877
156
|
|
|
878
|
-
publicFunctions(): string[] {
|
|
879
|
-
return this.functions.filter((func) => func.isPublic).map((func) => func.name)
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
usingPreapprovedAssetsFunctions(): string[] {
|
|
883
|
-
return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name)
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
usingAssetsInContractFunctions(): string[] {
|
|
887
|
-
return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name)
|
|
888
|
-
}
|
|
889
|
-
|
|
890
157
|
async isDevnet(signer: SignerProvider): Promise<boolean> {
|
|
891
158
|
if (!signer.nodeProvider) {
|
|
892
159
|
return false
|
|
@@ -896,6 +163,16 @@ export abstract class Artifact {
|
|
|
896
163
|
}
|
|
897
164
|
}
|
|
898
165
|
|
|
166
|
+
function fromFunctionSig(sig: node.FunctionSig): FunctionSig {
|
|
167
|
+
return {
|
|
168
|
+
name: sig.name,
|
|
169
|
+
paramNames: sig.paramNames,
|
|
170
|
+
paramTypes: sig.paramTypes,
|
|
171
|
+
paramIsMutable: sig.paramIsMutable,
|
|
172
|
+
returnTypes: sig.returnTypes
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
899
176
|
export class Contract extends Artifact {
|
|
900
177
|
readonly bytecode: string
|
|
901
178
|
readonly bytecodeDebugPatch: string
|
|
@@ -910,6 +187,7 @@ export class Contract extends Artifact {
|
|
|
910
187
|
|
|
911
188
|
readonly bytecodeDebug: string
|
|
912
189
|
readonly codeHashDebug: string
|
|
190
|
+
readonly decodedMethods: Method[]
|
|
913
191
|
|
|
914
192
|
constructor(
|
|
915
193
|
version: string,
|
|
@@ -941,6 +219,20 @@ export class Contract extends Artifact {
|
|
|
941
219
|
|
|
942
220
|
this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
|
|
943
221
|
this.codeHashDebug = codeHashDebug
|
|
222
|
+
|
|
223
|
+
this.decodedMethods = contract.contractCodec.decodeContract(Buffer.from(bytecode, 'hex')).methods
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
publicFunctions(): FunctionSig[] {
|
|
227
|
+
return this.functions.filter((_, index) => this.decodedMethods[`${index}`].isPublic)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
usingPreapprovedAssetsFunctions(): FunctionSig[] {
|
|
231
|
+
return this.functions.filter((_, index) => this.decodedMethods[`${index}`].usePreapprovedAssets)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
usingAssetsInContractFunctions(): FunctionSig[] {
|
|
235
|
+
return this.functions.filter((_, index) => this.decodedMethods[`${index}`].useContractAssets)
|
|
944
236
|
}
|
|
945
237
|
|
|
946
238
|
// TODO: safely parse json
|
|
@@ -987,7 +279,7 @@ export class Contract extends Artifact {
|
|
|
987
279
|
result.codeHashDebug,
|
|
988
280
|
result.fields,
|
|
989
281
|
result.events,
|
|
990
|
-
result.functions,
|
|
282
|
+
result.functions.map(fromFunctionSig),
|
|
991
283
|
result.constants,
|
|
992
284
|
result.enums,
|
|
993
285
|
structs,
|
|
@@ -1144,11 +436,9 @@ export class Contract extends Artifact {
|
|
|
1144
436
|
|
|
1145
437
|
static fromApiContractState(
|
|
1146
438
|
state: node.ContractState,
|
|
1147
|
-
getContractByCodeHash
|
|
439
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1148
440
|
): ContractState {
|
|
1149
|
-
const contract = getContractByCodeHash
|
|
1150
|
-
? getContractByCodeHash(state.codeHash)
|
|
1151
|
-
: Project.currentProject.contractByCodeHash(state.codeHash)
|
|
441
|
+
const contract = getContractByCodeHash(state.codeHash)
|
|
1152
442
|
return contract.fromApiContractState(state)
|
|
1153
443
|
}
|
|
1154
444
|
|
|
@@ -1170,7 +460,7 @@ export class Contract extends Artifact {
|
|
|
1170
460
|
event: node.ContractEventByTxId,
|
|
1171
461
|
codeHash: string | undefined,
|
|
1172
462
|
txId: string,
|
|
1173
|
-
getContractByCodeHash
|
|
463
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1174
464
|
): ContractEvent {
|
|
1175
465
|
let fields: Fields
|
|
1176
466
|
let name: string
|
|
@@ -1182,9 +472,7 @@ export class Contract extends Artifact {
|
|
|
1182
472
|
fields = fromApiEventFields(event.fields, Contract.ContractDestroyedEvent, true)
|
|
1183
473
|
name = Contract.ContractDestroyedEvent.name
|
|
1184
474
|
} else {
|
|
1185
|
-
const contract = getContractByCodeHash
|
|
1186
|
-
? getContractByCodeHash(codeHash!)
|
|
1187
|
-
: Project.currentProject.contractByCodeHash(codeHash!)
|
|
475
|
+
const contract = getContractByCodeHash(codeHash!)
|
|
1188
476
|
const eventSig = contract.eventsSig[event.eventIndex]
|
|
1189
477
|
fields = fromApiEventFields(event.fields, eventSig)
|
|
1190
478
|
name = eventSig.name
|
|
@@ -1203,7 +491,8 @@ export class Contract extends Artifact {
|
|
|
1203
491
|
fromApiTestContractResult(
|
|
1204
492
|
methodName: string,
|
|
1205
493
|
result: node.TestContractResult,
|
|
1206
|
-
txId: string
|
|
494
|
+
txId: string,
|
|
495
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1207
496
|
): TestContractResult<unknown> {
|
|
1208
497
|
const methodIndex = this.functions.findIndex((sig) => sig.name === methodName)
|
|
1209
498
|
const returnTypes = this.functions[`${methodIndex}`].returnTypes
|
|
@@ -1218,9 +507,9 @@ export class Contract extends Artifact {
|
|
|
1218
507
|
contractAddress: result.address,
|
|
1219
508
|
returns: returns,
|
|
1220
509
|
gasUsed: result.gasUsed,
|
|
1221
|
-
contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract)),
|
|
510
|
+
contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract, getContractByCodeHash)),
|
|
1222
511
|
txOutputs: result.txOutputs.map(fromApiOutput),
|
|
1223
|
-
events: Contract.fromApiEvents(result.events, addressToCodeHash, txId),
|
|
512
|
+
events: Contract.fromApiEvents(result.events, addressToCodeHash, txId, getContractByCodeHash),
|
|
1224
513
|
debugMessages: result.debugMessages
|
|
1225
514
|
}
|
|
1226
515
|
}
|
|
@@ -1264,7 +553,7 @@ export class Contract extends Artifact {
|
|
|
1264
553
|
events: node.ContractEventByTxId[],
|
|
1265
554
|
addressToCodeHash: Map<string, string>,
|
|
1266
555
|
txId: string,
|
|
1267
|
-
getContractByCodeHash
|
|
556
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1268
557
|
): ContractEvent[] {
|
|
1269
558
|
return events.map((event) => {
|
|
1270
559
|
const contractAddress = event.contractAddress
|
|
@@ -1298,7 +587,7 @@ export class Contract extends Artifact {
|
|
|
1298
587
|
result: node.CallContractResult,
|
|
1299
588
|
txId: string,
|
|
1300
589
|
methodIndex: number,
|
|
1301
|
-
getContractByCodeHash
|
|
590
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1302
591
|
): CallContractResult<unknown> {
|
|
1303
592
|
const returnTypes = this.functions[`${methodIndex}`].returnTypes
|
|
1304
593
|
const callResult = tryGetCallResult(result)
|
|
@@ -1348,7 +637,7 @@ export class Script extends Artifact {
|
|
|
1348
637
|
result.bytecodeTemplate,
|
|
1349
638
|
result.bytecodeDebugPatch,
|
|
1350
639
|
result.fields,
|
|
1351
|
-
result.functions,
|
|
640
|
+
result.functions.map(fromFunctionSig),
|
|
1352
641
|
structs
|
|
1353
642
|
)
|
|
1354
643
|
}
|
|
@@ -1831,14 +1120,14 @@ export function subscribeEventsFromContract<T extends Fields, M extends Contract
|
|
|
1831
1120
|
decodeFunc: (event: node.ContractEvent) => M,
|
|
1832
1121
|
fromCount?: number
|
|
1833
1122
|
): EventSubscription {
|
|
1834
|
-
const messageCallback = (event: node.ContractEvent)
|
|
1123
|
+
const messageCallback = (event: node.ContractEvent) => {
|
|
1835
1124
|
if (event.eventIndex !== eventIndex) {
|
|
1836
1125
|
return Promise.resolve()
|
|
1837
1126
|
}
|
|
1838
1127
|
return options.messageCallback(decodeFunc(event))
|
|
1839
1128
|
}
|
|
1840
1129
|
|
|
1841
|
-
const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>)
|
|
1130
|
+
const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>) => {
|
|
1842
1131
|
return options.errorCallback(err, subscription as unknown as Subscription<M>)
|
|
1843
1132
|
}
|
|
1844
1133
|
const opt: EventSubscribeOptions<node.ContractEvent> = {
|
|
@@ -1877,7 +1166,9 @@ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; co
|
|
|
1877
1166
|
const { immFields, mutFields } = ralph.calcFieldSize(type, true, structs)
|
|
1878
1167
|
const loadImmFieldByIndex: Method = {
|
|
1879
1168
|
isPublic: true,
|
|
1880
|
-
|
|
1169
|
+
usePreapprovedAssets: false,
|
|
1170
|
+
useContractAssets: false,
|
|
1171
|
+
usePayToContractOnly: false,
|
|
1881
1172
|
argsLength: 1,
|
|
1882
1173
|
localsLength: 1,
|
|
1883
1174
|
returnLength: 1,
|
|
@@ -1905,7 +1196,9 @@ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; co
|
|
|
1905
1196
|
}
|
|
1906
1197
|
const destroy: Method = {
|
|
1907
1198
|
isPublic: true,
|
|
1908
|
-
|
|
1199
|
+
usePreapprovedAssets: false,
|
|
1200
|
+
useContractAssets: true,
|
|
1201
|
+
usePayToContractOnly: false,
|
|
1909
1202
|
argsLength: 1,
|
|
1910
1203
|
localsLength: 1,
|
|
1911
1204
|
returnLength: 0,
|
|
@@ -1980,7 +1273,8 @@ export async function testMethod<
|
|
|
1980
1273
|
>(
|
|
1981
1274
|
factory: ContractFactory<I, F>,
|
|
1982
1275
|
methodName: string,
|
|
1983
|
-
params: Optional<TestContractParams<F, A, M>, 'testArgs' | 'initialFields'
|
|
1276
|
+
params: Optional<TestContractParams<F, A, M>, 'testArgs' | 'initialFields'>,
|
|
1277
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
1984
1278
|
): Promise<TestContractResult<R, M>> {
|
|
1985
1279
|
const txId = params?.txId ?? randomTxId()
|
|
1986
1280
|
const contract = factory.contract
|
|
@@ -1999,7 +1293,7 @@ export async function testMethod<
|
|
|
1999
1293
|
})
|
|
2000
1294
|
const apiResult = await getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
|
|
2001
1295
|
const maps = existingContractsToMaps(contract, address, group, apiResult, initialMaps)
|
|
2002
|
-
const testResult = contract.fromApiTestContractResult(methodName, apiResult, txId)
|
|
1296
|
+
const testResult = contract.fromApiTestContractResult(methodName, apiResult, txId, getContractByCodeHash)
|
|
2003
1297
|
contract.printDebugMessages(methodName, testResult.debugMessages)
|
|
2004
1298
|
return {
|
|
2005
1299
|
...testResult,
|
|
@@ -2008,24 +1302,36 @@ export async function testMethod<
|
|
|
2008
1302
|
}
|
|
2009
1303
|
|
|
2010
1304
|
export class RalphMap<K extends Val, V extends Val> {
|
|
1305
|
+
private readonly groupIndex: number
|
|
2011
1306
|
constructor(
|
|
2012
1307
|
private readonly parentContract: Contract,
|
|
2013
|
-
private readonly
|
|
1308
|
+
private readonly parentContractId: HexString,
|
|
2014
1309
|
private readonly mapName: string
|
|
2015
|
-
) {
|
|
1310
|
+
) {
|
|
1311
|
+
this.groupIndex = groupOfAddress(addressFromContractId(parentContractId))
|
|
1312
|
+
}
|
|
2016
1313
|
|
|
2017
1314
|
async get(key: K): Promise<V | undefined> {
|
|
2018
|
-
return getMapItem(this.parentContract, this.
|
|
1315
|
+
return getMapItem(this.parentContract, this.parentContractId, this.groupIndex, this.mapName, key)
|
|
2019
1316
|
}
|
|
2020
1317
|
|
|
2021
1318
|
async contains(key: K): Promise<boolean> {
|
|
2022
1319
|
return this.get(key).then((v) => v !== undefined)
|
|
2023
1320
|
}
|
|
1321
|
+
|
|
1322
|
+
toJSON() {
|
|
1323
|
+
return {
|
|
1324
|
+
parentContractId: this.parentContractId,
|
|
1325
|
+
mapName: this.mapName,
|
|
1326
|
+
groupIndex: this.groupIndex
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
2024
1329
|
}
|
|
2025
1330
|
|
|
2026
1331
|
export async function getMapItem<R extends Val>(
|
|
2027
1332
|
parentContract: Contract,
|
|
2028
|
-
|
|
1333
|
+
parentContractId: HexString,
|
|
1334
|
+
groupIndex: number,
|
|
2029
1335
|
mapName: string,
|
|
2030
1336
|
key: Val
|
|
2031
1337
|
): Promise<R | undefined> {
|
|
@@ -2035,13 +1341,7 @@ export async function getMapItem<R extends Val>(
|
|
|
2035
1341
|
throw new Error(`Map ${mapName} does not exist in contract ${parentContract.name}`)
|
|
2036
1342
|
}
|
|
2037
1343
|
const [keyType, valueType] = ralph.parseMapType(mapType)
|
|
2038
|
-
const mapItemContractId = calcWrapperContractId(
|
|
2039
|
-
parentInstance.contractId,
|
|
2040
|
-
index!,
|
|
2041
|
-
key,
|
|
2042
|
-
keyType,
|
|
2043
|
-
parentInstance.groupIndex
|
|
2044
|
-
)
|
|
1344
|
+
const mapItemContractId = calcWrapperContractId(parentContractId, index!, key, keyType, groupIndex)
|
|
2045
1345
|
const mapItemAddress = addressFromContractId(mapItemContractId)
|
|
2046
1346
|
try {
|
|
2047
1347
|
const state = await getCurrentNodeProvider().contracts.getContractsAddressState(mapItemAddress)
|
|
@@ -2271,13 +1571,13 @@ export function subscribeContractEvents(
|
|
|
2271
1571
|
options: EventSubscribeOptions<ContractEvent<any>>,
|
|
2272
1572
|
fromCount?: number
|
|
2273
1573
|
): EventSubscription {
|
|
2274
|
-
const messageCallback = (event: node.ContractEvent)
|
|
1574
|
+
const messageCallback = (event: node.ContractEvent) => {
|
|
2275
1575
|
return options.messageCallback({
|
|
2276
1576
|
...decodeEvent(contract, instance, event, event.eventIndex),
|
|
2277
1577
|
contractAddress: instance.address
|
|
2278
1578
|
})
|
|
2279
1579
|
}
|
|
2280
|
-
const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>)
|
|
1580
|
+
const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>) => {
|
|
2281
1581
|
return options.errorCallback(err, subscription as unknown as Subscription<ContractEvent<any>>)
|
|
2282
1582
|
}
|
|
2283
1583
|
const opt: EventSubscribeOptions<node.ContractEvent> = {
|
|
@@ -2294,7 +1594,7 @@ export async function callMethod<I extends ContractInstance, F extends Fields, A
|
|
|
2294
1594
|
instance: ContractInstance,
|
|
2295
1595
|
methodName: string,
|
|
2296
1596
|
params: Optional<CallContractParams<A>, 'args'>,
|
|
2297
|
-
getContractByCodeHash
|
|
1597
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
2298
1598
|
): Promise<CallContractResult<R>> {
|
|
2299
1599
|
const methodIndex = contract.contract.getMethodIndex(methodName)
|
|
2300
1600
|
const txId = params?.txId ?? randomTxId()
|
|
@@ -2314,7 +1614,7 @@ export async function multicallMethods<I extends ContractInstance, F extends Fie
|
|
|
2314
1614
|
contract: ContractFactory<I, F>,
|
|
2315
1615
|
instance: ContractInstance,
|
|
2316
1616
|
calls: Record<string, Optional<CallContractParams<any>, 'args'>>,
|
|
2317
|
-
getContractByCodeHash
|
|
1617
|
+
getContractByCodeHash: (codeHash: string) => Contract
|
|
2318
1618
|
): Promise<Record<string, CallContractResult<any>>> {
|
|
2319
1619
|
const callEntries = Object.entries(calls)
|
|
2320
1620
|
const callsParams = callEntries.map((entry) => {
|
|
@@ -2369,10 +1669,3 @@ export const getContractIdFromUnsignedTx = async (
|
|
|
2369
1669
|
|
|
2370
1670
|
// This function only works in the simple case where a single non-subcontract is created in the tx
|
|
2371
1671
|
export const getTokenIdFromUnsignedTx = getContractIdFromUnsignedTx
|
|
2372
|
-
|
|
2373
|
-
export function tryGetCallResult(result: node.CallContractResult): node.CallContractSucceeded {
|
|
2374
|
-
if (result.type === 'CallContractFailed') {
|
|
2375
|
-
throw new Error(`Failed to call contract, error: ${(result as node.CallContractFailed).error}`)
|
|
2376
|
-
}
|
|
2377
|
-
return result as node.CallContractSucceeded
|
|
2378
|
-
}
|