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