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