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