@alephium/web3 0.2.0-rc.2 → 0.2.0-rc.21
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/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/src/api/api-alephium.d.ts +126 -22
- package/dist/src/api/api-alephium.js +145 -79
- package/dist/src/api/api-explorer.d.ts +163 -48
- package/dist/src/api/api-explorer.js +157 -34
- package/dist/src/api/index.d.ts +3 -2
- package/dist/src/api/index.js +22 -3
- package/dist/src/api/types.d.ts +23 -0
- package/dist/src/api/types.js +240 -0
- package/dist/src/api/utils.d.ts +6 -0
- package/dist/{scripts/rename-gitignore.js → src/api/utils.js} +11 -6
- package/dist/src/contract/contract.d.ts +117 -69
- package/dist/src/contract/contract.js +401 -435
- package/dist/src/contract/events.d.ts +4 -4
- package/dist/src/contract/index.js +5 -1
- package/dist/src/contract/ralph.d.ts +5 -4
- package/dist/src/contract/ralph.js +27 -1
- package/dist/src/global.d.ts +4 -0
- package/dist/{scripts/stop-devnet.js → src/global.js} +17 -11
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +23 -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 +21 -11
- package/dist/src/signer/signer.js +46 -9
- package/dist/src/transaction/index.d.ts +0 -1
- package/dist/src/transaction/index.js +5 -2
- package/dist/src/transaction/status.d.ts +2 -1
- package/dist/src/utils/bs58.d.ts +1 -0
- package/dist/src/utils/bs58.js +13 -1
- 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 +6 -11
- package/dist/src/utils/utils.js +21 -25
- package/jest-config.json +11 -0
- package/package.json +11 -47
- package/src/api/api-alephium.ts +178 -30
- package/src/api/api-explorer.ts +234 -51
- package/src/api/index.ts +14 -3
- package/src/api/types.ts +233 -0
- package/{scripts/rename-gitignore.js → src/api/utils.ts} +7 -6
- package/src/contract/contract.ts +602 -527
- package/src/contract/events.ts +6 -6
- package/src/contract/ralph.ts +29 -4
- package/src/{transaction/sign-verify.ts → global.ts} +14 -15
- package/src/index.ts +7 -0
- package/src/signer/index.ts +0 -1
- package/src/signer/signer.ts +70 -19
- package/src/transaction/index.ts +0 -1
- package/src/transaction/status.ts +4 -2
- package/src/utils/bs58.ts +11 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/subscription.ts +3 -3
- package/src/utils/utils.ts +14 -22
- package/.eslintrc.json +0 -21
- package/LICENSE +0 -165
- package/contracts/add/add.ral +0 -16
- package/contracts/greeter/greeter.ral +0 -7
- package/contracts/greeter/greeter_interface.ral +0 -3
- package/contracts/greeter_main.ral +0 -9
- package/contracts/main.ral +0 -6
- package/contracts/sub/sub.ral +0 -9
- package/dev/user.conf +0 -29
- package/dist/scripts/create-project.d.ts +0 -2
- package/dist/scripts/create-project.js +0 -125
- package/dist/scripts/rename-gitignore.d.ts +0 -1
- package/dist/scripts/start-devnet.d.ts +0 -1
- package/dist/scripts/start-devnet.js +0 -131
- package/dist/scripts/stop-devnet.d.ts +0 -1
- 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/scripts/create-project.ts +0 -137
- package/scripts/start-devnet.js +0 -141
- package/scripts/stop-devnet.js +0 -32
- 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/templates/base/README.md +0 -34
- package/templates/base/package.json +0 -35
- package/templates/base/src/greeter.ts +0 -41
- package/templates/base/tsconfig.json +0 -19
- package/templates/react/README.md +0 -34
- package/templates/react/config-overrides.js +0 -18
- package/templates/react/package.json +0 -66
- package/templates/react/src/App.tsx +0 -42
- package/templates/react/src/artifacts/greeter.ral.json +0 -26
- package/templates/react/src/artifacts/greeter_main.ral.json +0 -22
- package/templates/shared/.eslintrc.json +0 -12
- package/templates/shared/scripts/header.js +0 -0
- 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,551 @@ 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 {
|
|
25
|
-
|
|
23
|
+
import {
|
|
24
|
+
fromApiArray,
|
|
25
|
+
fromApiNumber256,
|
|
26
|
+
toApiNumber256,
|
|
27
|
+
NamedVals,
|
|
28
|
+
node,
|
|
29
|
+
NodeProvider,
|
|
30
|
+
Number256,
|
|
31
|
+
toApiToken,
|
|
32
|
+
toApiVal,
|
|
33
|
+
Token,
|
|
34
|
+
Val,
|
|
35
|
+
toApiTokens,
|
|
36
|
+
fromApiTokens,
|
|
37
|
+
fromApiVals
|
|
38
|
+
} from '../api'
|
|
26
39
|
import { SignDeployContractTxParams, SignExecuteScriptTxParams, SignerWithNodeProvider } from '../signer'
|
|
27
40
|
import * as ralph from './ralph'
|
|
28
41
|
import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
|
|
42
|
+
import { getCurrentNodeProvider } from '../global'
|
|
43
|
+
import { web3 } from '..'
|
|
44
|
+
|
|
45
|
+
export type FieldsSig = node.FieldsSig
|
|
46
|
+
export type EventSig = node.EventSig
|
|
47
|
+
export type FunctionSig = node.FunctionSig
|
|
48
|
+
export type Fields = NamedVals
|
|
49
|
+
export type Arguments = NamedVals
|
|
50
|
+
|
|
51
|
+
enum SourceType {
|
|
52
|
+
Contract = 0,
|
|
53
|
+
Script = 1,
|
|
54
|
+
AbstractContract = 2,
|
|
55
|
+
Interface = 3
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type CompilerOptions = node.CompilerOptions & {
|
|
59
|
+
errorOnWarnings: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const DEFAULT_NODE_COMPILER_OPTIONS: node.CompilerOptions = {
|
|
63
|
+
ignoreUnusedConstantsWarnings: false,
|
|
64
|
+
ignoreUnusedVariablesWarnings: false,
|
|
65
|
+
ignoreUnusedFieldsWarnings: false,
|
|
66
|
+
ignoreUnusedPrivateFunctionsWarnings: false,
|
|
67
|
+
ignoreReadonlyCheckWarnings: false,
|
|
68
|
+
ignoreExternalCallCheckWarnings: false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { errorOnWarnings: true, ...DEFAULT_NODE_COMPILER_OPTIONS }
|
|
72
|
+
|
|
73
|
+
class TypedMatcher<T extends SourceType> {
|
|
74
|
+
matcher: RegExp
|
|
75
|
+
type: T
|
|
76
|
+
|
|
77
|
+
constructor(pattern: string, type: T) {
|
|
78
|
+
this.matcher = new RegExp(pattern, 'mg')
|
|
79
|
+
this.type = type
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
match(str: string): number {
|
|
83
|
+
const results = str.match(this.matcher)
|
|
84
|
+
return results === null ? 0 : results.length
|
|
85
|
+
}
|
|
86
|
+
}
|
|
29
87
|
|
|
30
88
|
class SourceFile {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
89
|
+
type: SourceType
|
|
90
|
+
contractPath: string
|
|
91
|
+
sourceCode: string
|
|
92
|
+
sourceCodeHash: string
|
|
93
|
+
|
|
94
|
+
getArtifactPath(artifactsRootDir: string): string {
|
|
95
|
+
return artifactsRootDir + this.contractPath.slice(this.contractPath.indexOf('/')) + '.json'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
constructor(type: SourceType, sourceCode: string, sourceCodeHash: string, contractPath: string) {
|
|
99
|
+
this.type = type
|
|
100
|
+
this.sourceCode = sourceCode
|
|
101
|
+
this.sourceCodeHash = sourceCodeHash
|
|
102
|
+
this.contractPath = contractPath
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static async from(type: SourceType, sourceCode: string, contractPath: string): Promise<SourceFile> {
|
|
106
|
+
const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
|
|
107
|
+
return new SourceFile(type, sourceCode, Buffer.from(sourceCodeHash).toString('hex'), contractPath)
|
|
46
108
|
}
|
|
47
109
|
}
|
|
48
110
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
111
|
+
class Compiled<T extends Artifact> {
|
|
112
|
+
sourceFile: SourceFile
|
|
113
|
+
artifact: T
|
|
114
|
+
warnings: string[]
|
|
115
|
+
|
|
116
|
+
constructor(sourceFile: SourceFile, artifact: T, warnings: string[]) {
|
|
117
|
+
this.sourceFile = sourceFile
|
|
118
|
+
this.artifact = artifact
|
|
119
|
+
this.warnings = warnings
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type CodeInfo = { sourceCodeHash: string; bytecodeDebugPatch: string; codeHashDebug: string; warnings: string[] }
|
|
124
|
+
|
|
125
|
+
class ProjectArtifact {
|
|
126
|
+
static readonly artifactFileName = '.project.json'
|
|
127
|
+
|
|
128
|
+
compilerOptionsUsed: node.CompilerOptions
|
|
129
|
+
infos: Map<string, CodeInfo>
|
|
130
|
+
|
|
131
|
+
static checkCompilerOptionsParameter(compilerOptions: node.CompilerOptions): void {
|
|
132
|
+
if (Object.keys(compilerOptions).length != Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
|
|
133
|
+
throw Error(`Not all compiler options are set: ${compilerOptions}`)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const combined = { ...compilerOptions, ...DEFAULT_NODE_COMPILER_OPTIONS }
|
|
137
|
+
if (Object.keys(combined).length !== Object.keys(DEFAULT_NODE_COMPILER_OPTIONS).length) {
|
|
138
|
+
throw Error(`There are unknown compiler options: ${compilerOptions}`)
|
|
70
139
|
}
|
|
71
140
|
}
|
|
72
141
|
|
|
73
|
-
constructor(
|
|
74
|
-
|
|
75
|
-
this.
|
|
142
|
+
constructor(compilerOptionsUsed: node.CompilerOptions, infos: Map<string, CodeInfo>) {
|
|
143
|
+
ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
|
|
144
|
+
this.compilerOptionsUsed = compilerOptionsUsed
|
|
145
|
+
this.infos = infos
|
|
76
146
|
}
|
|
77
147
|
|
|
78
|
-
|
|
79
|
-
|
|
148
|
+
async saveToFile(rootPath: string): Promise<void> {
|
|
149
|
+
const filepath = rootPath + '/' + ProjectArtifact.artifactFileName
|
|
150
|
+
const artifact = { compilerOptionsUsed: this.compilerOptionsUsed, infos: Object.fromEntries(this.infos) }
|
|
151
|
+
const content = JSON.stringify(artifact, null, 2)
|
|
152
|
+
return fsPromises.writeFile(filepath, content)
|
|
80
153
|
}
|
|
81
154
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
155
|
+
needToReCompile(compilerOptions: node.CompilerOptions, files: SourceFile[]): boolean {
|
|
156
|
+
ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
|
|
157
|
+
|
|
158
|
+
const optionsMatched = Object.entries(compilerOptions).every(([key, inputOption]) => {
|
|
159
|
+
const usedOption = this.compilerOptionsUsed[`${key}`]
|
|
160
|
+
return usedOption === inputOption
|
|
161
|
+
})
|
|
162
|
+
if (!optionsMatched) {
|
|
163
|
+
return true
|
|
87
164
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
dirs.pop()
|
|
98
|
-
break
|
|
99
|
-
}
|
|
100
|
-
default: {
|
|
101
|
-
dirs.push(part)
|
|
102
|
-
}
|
|
165
|
+
|
|
166
|
+
if (files.length !== this.infos.size) {
|
|
167
|
+
return true
|
|
168
|
+
}
|
|
169
|
+
for (const file of files) {
|
|
170
|
+
const info = this.infos.get(file.contractPath)
|
|
171
|
+
if (typeof info === 'undefined' || info.sourceCodeHash !== file.sourceCodeHash) {
|
|
172
|
+
return true
|
|
103
173
|
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static async from(rootPath: string): Promise<ProjectArtifact | undefined> {
|
|
180
|
+
const filepath = rootPath + '/' + ProjectArtifact.artifactFileName
|
|
181
|
+
if (!fs.existsSync(filepath)) {
|
|
182
|
+
return undefined
|
|
183
|
+
}
|
|
184
|
+
const content = await fsPromises.readFile(filepath)
|
|
185
|
+
const json = JSON.parse(content.toString())
|
|
186
|
+
const compilerOptionsUsed = json.compilerOptionsUsed as node.CompilerOptions
|
|
187
|
+
const files = new Map(Object.entries<CodeInfo>(json.infos))
|
|
188
|
+
return new ProjectArtifact(compilerOptionsUsed, files)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export class Project {
|
|
193
|
+
sourceFiles: SourceFile[]
|
|
194
|
+
contracts: Compiled<Contract>[]
|
|
195
|
+
scripts: Compiled<Script>[]
|
|
196
|
+
projectArtifact: ProjectArtifact
|
|
197
|
+
|
|
198
|
+
readonly contractsRootDir: string
|
|
199
|
+
readonly artifactsRootDir: string
|
|
200
|
+
|
|
201
|
+
static currentProject: Project
|
|
202
|
+
|
|
203
|
+
static readonly abstractContractMatcher = new TypedMatcher<SourceType>(
|
|
204
|
+
'^Abstract Contract [A-Z][a-zA-Z0-9]*',
|
|
205
|
+
SourceType.AbstractContract
|
|
206
|
+
)
|
|
207
|
+
static readonly contractMatcher = new TypedMatcher('^Contract [A-Z][a-zA-Z0-9]*', SourceType.Contract)
|
|
208
|
+
static readonly interfaceMatcher = new TypedMatcher('^Interface [A-Z][a-zA-Z0-9]* \\{', SourceType.Interface)
|
|
209
|
+
static readonly scriptMatcher = new TypedMatcher('^TxScript [A-Z][a-zA-Z0-9]*', SourceType.Script)
|
|
210
|
+
static readonly matchers = [
|
|
211
|
+
Project.abstractContractMatcher,
|
|
212
|
+
Project.contractMatcher,
|
|
213
|
+
Project.interfaceMatcher,
|
|
214
|
+
Project.scriptMatcher
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
static buildProjectArtifact(
|
|
218
|
+
sourceFiles: SourceFile[],
|
|
219
|
+
contracts: Compiled<Contract>[],
|
|
220
|
+
scripts: Compiled<Script>[],
|
|
221
|
+
compilerOptions: node.CompilerOptions
|
|
222
|
+
): ProjectArtifact {
|
|
223
|
+
const files: Map<string, CodeInfo> = new Map()
|
|
224
|
+
contracts.forEach((c) => {
|
|
225
|
+
files.set(c.sourceFile.contractPath, {
|
|
226
|
+
sourceCodeHash: c.sourceFile.sourceCodeHash,
|
|
227
|
+
bytecodeDebugPatch: c.artifact.bytecodeDebugPatch,
|
|
228
|
+
codeHashDebug: c.artifact.codeHashDebug,
|
|
229
|
+
warnings: c.warnings
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
scripts.forEach((s) => {
|
|
233
|
+
files.set(s.sourceFile.contractPath, {
|
|
234
|
+
sourceCodeHash: s.sourceFile.sourceCodeHash,
|
|
235
|
+
bytecodeDebugPatch: s.artifact.bytecodeDebugPatch,
|
|
236
|
+
codeHashDebug: '',
|
|
237
|
+
warnings: s.warnings
|
|
238
|
+
})
|
|
104
239
|
})
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const localImportsCache: string[] = []
|
|
114
|
-
let result = contractStr.replace(Common.importRegex, (match) => {
|
|
115
|
-
localImportsCache.push(match)
|
|
116
|
-
return ''
|
|
240
|
+
const compiledSize = contracts.length + scripts.length
|
|
241
|
+
sourceFiles.slice(compiledSize).forEach((c) => {
|
|
242
|
+
files.set(c.contractPath, {
|
|
243
|
+
sourceCodeHash: c.sourceCodeHash,
|
|
244
|
+
bytecodeDebugPatch: '',
|
|
245
|
+
codeHashDebug: '',
|
|
246
|
+
warnings: []
|
|
247
|
+
})
|
|
117
248
|
})
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
249
|
+
return new ProjectArtifact(compilerOptions, files)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private constructor(
|
|
253
|
+
contractsRootDir: string,
|
|
254
|
+
artifactsRootDir: string,
|
|
255
|
+
sourceFiles: SourceFile[],
|
|
256
|
+
contracts: Compiled<Contract>[],
|
|
257
|
+
scripts: Compiled<Script>[],
|
|
258
|
+
errorOnWarnings: boolean,
|
|
259
|
+
projectArtifact: ProjectArtifact
|
|
260
|
+
) {
|
|
261
|
+
this.contractsRootDir = contractsRootDir
|
|
262
|
+
this.artifactsRootDir = artifactsRootDir
|
|
263
|
+
this.sourceFiles = sourceFiles
|
|
264
|
+
this.contracts = contracts
|
|
265
|
+
this.scripts = scripts
|
|
266
|
+
this.projectArtifact = projectArtifact
|
|
267
|
+
|
|
268
|
+
if (errorOnWarnings) {
|
|
269
|
+
Project.checkCompilerWarnings(
|
|
270
|
+
[...contracts.map((c) => c.warnings).flat(), ...scripts.map((s) => s.warnings).flat()],
|
|
271
|
+
errorOnWarnings
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private getContractPath(path: string): string {
|
|
277
|
+
return path.startsWith(`./${this.contractsRootDir}`)
|
|
278
|
+
? path.slice(2)
|
|
279
|
+
: path.startsWith(this.contractsRootDir)
|
|
280
|
+
? path
|
|
281
|
+
: this.contractsRootDir + '/' + path
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
static checkCompilerWarnings(warnings: string[], errorOnWarnings: boolean): void {
|
|
285
|
+
if (warnings.length !== 0) {
|
|
286
|
+
const prefixPerWarning = ' - '
|
|
287
|
+
const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning)
|
|
288
|
+
const output = `Compilation warnings:\n` + warningString + '\n'
|
|
289
|
+
if (errorOnWarnings) {
|
|
290
|
+
throw new Error(output)
|
|
291
|
+
} else {
|
|
292
|
+
console.log(output)
|
|
127
293
|
}
|
|
128
294
|
}
|
|
129
|
-
return result
|
|
130
295
|
}
|
|
131
296
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
297
|
+
static contract(name: string): Contract {
|
|
298
|
+
const contract = Project.currentProject.contracts.find((c) => c.artifact.name === name)
|
|
299
|
+
if (typeof contract === 'undefined') {
|
|
300
|
+
throw new Error(`Contract "${name}" does not exist`)
|
|
301
|
+
}
|
|
302
|
+
return contract.artifact
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static script(name: string): Script {
|
|
306
|
+
const script = Project.currentProject.scripts.find((c) => c.artifact.name === name)
|
|
307
|
+
if (typeof script === 'undefined') {
|
|
308
|
+
throw new Error(`Script "${name}" does not exist`)
|
|
309
|
+
}
|
|
310
|
+
return script.artifact
|
|
311
|
+
}
|
|
140
312
|
|
|
141
|
-
|
|
142
|
-
|
|
313
|
+
private async saveArtifactsToFile(): Promise<void> {
|
|
314
|
+
const artifactsRootDir = this.artifactsRootDir
|
|
315
|
+
const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
|
|
316
|
+
const artifactDir = compiled.sourceFile.getArtifactPath(artifactsRootDir)
|
|
317
|
+
const folder = artifactDir.slice(0, artifactDir.lastIndexOf('/'))
|
|
318
|
+
if (!fs.existsSync(folder)) {
|
|
319
|
+
fs.mkdirSync(folder, { recursive: true })
|
|
320
|
+
}
|
|
321
|
+
return fsPromises.writeFile(artifactDir, compiled.artifact.toString())
|
|
322
|
+
}
|
|
323
|
+
for (const contract of this.contracts) {
|
|
324
|
+
await saveToFile(contract)
|
|
325
|
+
}
|
|
326
|
+
for (const script of this.scripts) {
|
|
327
|
+
await saveToFile(script)
|
|
328
|
+
}
|
|
329
|
+
await this.projectArtifact.saveToFile(this.artifactsRootDir)
|
|
143
330
|
}
|
|
144
331
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
332
|
+
contractByCodeHash(codeHash: string): Contract {
|
|
333
|
+
const contract = this.contracts.find(
|
|
334
|
+
(c) => c.artifact.codeHash === codeHash || c.artifact.codeHashDebug == codeHash
|
|
335
|
+
)
|
|
336
|
+
if (typeof contract === 'undefined') {
|
|
337
|
+
throw new Error(`Unknown code with code hash: ${codeHash}`)
|
|
148
338
|
}
|
|
339
|
+
return contract.artifact
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private static async compile(
|
|
343
|
+
provider: NodeProvider,
|
|
344
|
+
files: SourceFile[],
|
|
345
|
+
contractsRootDir: string,
|
|
346
|
+
artifactsRootDir: string,
|
|
347
|
+
errorOnWarnings: boolean,
|
|
348
|
+
compilerOptions: node.CompilerOptions
|
|
349
|
+
): Promise<Project> {
|
|
350
|
+
const sourceStr = files.map((f) => f.sourceCode).join('\n')
|
|
351
|
+
const result = await provider.contracts.postContractsCompileProject({
|
|
352
|
+
code: sourceStr,
|
|
353
|
+
compilerOptions: compilerOptions
|
|
354
|
+
})
|
|
355
|
+
const contracts: Compiled<Contract>[] = []
|
|
356
|
+
const scripts: Compiled<Script>[] = []
|
|
357
|
+
result.contracts.forEach((contractResult, index) => {
|
|
358
|
+
const sourceFile = files[`${index}`]
|
|
359
|
+
const contract = Contract.fromCompileResult(contractResult)
|
|
360
|
+
contracts.push(new Compiled(sourceFile, contract, contractResult.warnings))
|
|
361
|
+
})
|
|
362
|
+
result.scripts.forEach((scriptResult, index) => {
|
|
363
|
+
const sourceFile = files[index + contracts.length]
|
|
364
|
+
const script = Script.fromCompileResult(scriptResult)
|
|
365
|
+
scripts.push(new Compiled(sourceFile, script, scriptResult.warnings))
|
|
366
|
+
})
|
|
367
|
+
const projectArtifact = Project.buildProjectArtifact(files, contracts, scripts, compilerOptions)
|
|
368
|
+
const project = new Project(
|
|
369
|
+
contractsRootDir,
|
|
370
|
+
artifactsRootDir,
|
|
371
|
+
files,
|
|
372
|
+
contracts,
|
|
373
|
+
scripts,
|
|
374
|
+
errorOnWarnings,
|
|
375
|
+
projectArtifact
|
|
376
|
+
)
|
|
377
|
+
await project.saveArtifactsToFile()
|
|
378
|
+
return project
|
|
149
379
|
}
|
|
150
380
|
|
|
151
|
-
|
|
381
|
+
private static async loadArtifacts(
|
|
152
382
|
provider: NodeProvider,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
383
|
+
files: SourceFile[],
|
|
384
|
+
projectArtifact: ProjectArtifact,
|
|
385
|
+
contractsRootDir: string,
|
|
386
|
+
artifactsRootDir: string,
|
|
387
|
+
errorOnWarnings: boolean,
|
|
388
|
+
compilerOptions: node.CompilerOptions
|
|
389
|
+
): Promise<Project> {
|
|
390
|
+
try {
|
|
391
|
+
const contracts: Compiled<Contract>[] = []
|
|
392
|
+
const scripts: Compiled<Script>[] = []
|
|
393
|
+
for (const file of files) {
|
|
394
|
+
const info = projectArtifact.infos.get(file.contractPath)
|
|
395
|
+
if (typeof info === 'undefined') {
|
|
396
|
+
throw Error(`Unable to find project info for ${file.contractPath}, please rebuild the project`)
|
|
397
|
+
}
|
|
398
|
+
const warnings = info.warnings
|
|
399
|
+
const artifactDir = file.getArtifactPath(artifactsRootDir)
|
|
400
|
+
if (file.type === SourceType.Contract) {
|
|
401
|
+
const artifact = await Contract.fromArtifactFile(artifactDir, info.bytecodeDebugPatch, info.codeHashDebug)
|
|
402
|
+
contracts.push(new Compiled(file, artifact, warnings))
|
|
403
|
+
} else if (file.type === SourceType.Script) {
|
|
404
|
+
const artifact = await Script.fromArtifactFile(artifactDir, info.bytecodeDebugPatch)
|
|
405
|
+
scripts.push(new Compiled(file, artifact, warnings))
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return new Project(
|
|
410
|
+
contractsRootDir,
|
|
411
|
+
artifactsRootDir,
|
|
412
|
+
files,
|
|
413
|
+
contracts,
|
|
414
|
+
scripts,
|
|
415
|
+
errorOnWarnings,
|
|
416
|
+
projectArtifact
|
|
417
|
+
)
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`)
|
|
420
|
+
return Project.compile(provider, files, contractsRootDir, artifactsRootDir, errorOnWarnings, compilerOptions)
|
|
166
421
|
}
|
|
167
422
|
}
|
|
168
423
|
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
if (!
|
|
172
|
-
|
|
424
|
+
private static async loadSourceFile(dirPath: string, filename: string): Promise<SourceFile> {
|
|
425
|
+
const contractPath = dirPath + '/' + filename
|
|
426
|
+
if (!filename.endsWith('.ral')) {
|
|
427
|
+
throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const sourceBuffer = await fsPromises.readFile(contractPath)
|
|
431
|
+
const sourceStr = sourceBuffer.toString()
|
|
432
|
+
const results = this.matchers.map((m) => m.match(sourceStr))
|
|
433
|
+
const matchNumber = results.reduce((a, b) => a + b, 0)
|
|
434
|
+
if (matchNumber === 0) {
|
|
435
|
+
throw new Error(`No contract defined in file: ${contractPath}`)
|
|
436
|
+
}
|
|
437
|
+
if (matchNumber > 1) {
|
|
438
|
+
throw new Error(`Multiple definitions in file: ${contractPath}`)
|
|
439
|
+
}
|
|
440
|
+
const matcherIndex = results.indexOf(1)
|
|
441
|
+
const type = this.matchers[`${matcherIndex}`].type
|
|
442
|
+
return SourceFile.from(type, sourceStr, contractPath)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private static async loadSourceFiles(contractsRootDir: string): Promise<SourceFile[]> {
|
|
446
|
+
const loadDir = async function (dirPath: string, results: SourceFile[]): Promise<void> {
|
|
447
|
+
const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
|
|
448
|
+
for (const dirent of dirents) {
|
|
449
|
+
if (dirent.isFile()) {
|
|
450
|
+
const file = await Project.loadSourceFile(dirPath, dirent.name)
|
|
451
|
+
results.push(file)
|
|
452
|
+
} else {
|
|
453
|
+
const newPath = dirPath + '/' + dirent.name
|
|
454
|
+
await loadDir(newPath, results)
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const sourceFiles: SourceFile[] = []
|
|
459
|
+
await loadDir(contractsRootDir, sourceFiles)
|
|
460
|
+
const contractAndScriptSize = sourceFiles.filter(
|
|
461
|
+
(f) => f.type === SourceType.Contract || f.type === SourceType.Script
|
|
462
|
+
).length
|
|
463
|
+
if (sourceFiles.length === 0 || contractAndScriptSize === 0) {
|
|
464
|
+
throw new Error('Project have no source files')
|
|
465
|
+
}
|
|
466
|
+
return sourceFiles.sort((a, b) => a.type - b.type)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static readonly DEFAULT_CONTRACTS_DIR = 'contracts'
|
|
470
|
+
static readonly DEFAULT_ARTIFACTS_DIR = 'artifacts'
|
|
471
|
+
|
|
472
|
+
static async build(
|
|
473
|
+
compilerOptionsPartial: Partial<CompilerOptions> = {},
|
|
474
|
+
contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
|
|
475
|
+
artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR
|
|
476
|
+
): Promise<void> {
|
|
477
|
+
const provider = getCurrentNodeProvider()
|
|
478
|
+
const sourceFiles = await Project.loadSourceFiles(contractsRootDir)
|
|
479
|
+
const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
|
|
480
|
+
const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
|
|
481
|
+
if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
|
|
482
|
+
console.log(`Compiling contracts in folder "${contractsRootDir}"`)
|
|
483
|
+
Project.currentProject = await Project.compile(
|
|
484
|
+
provider,
|
|
485
|
+
sourceFiles,
|
|
486
|
+
contractsRootDir,
|
|
487
|
+
artifactsRootDir,
|
|
488
|
+
errorOnWarnings,
|
|
489
|
+
nodeCompilerOptions
|
|
490
|
+
)
|
|
491
|
+
} else {
|
|
492
|
+
console.log(`Contracts are compiled already. Loading them from folder "${artifactsRootDir}"`)
|
|
493
|
+
Project.currentProject = await Project.loadArtifacts(
|
|
494
|
+
provider,
|
|
495
|
+
sourceFiles,
|
|
496
|
+
projectArtifact,
|
|
497
|
+
contractsRootDir,
|
|
498
|
+
artifactsRootDir,
|
|
499
|
+
errorOnWarnings,
|
|
500
|
+
nodeCompilerOptions
|
|
501
|
+
)
|
|
173
502
|
}
|
|
174
|
-
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export abstract class Artifact {
|
|
507
|
+
readonly name: string
|
|
508
|
+
readonly functions: FunctionSig[]
|
|
509
|
+
|
|
510
|
+
constructor(name: string, functions: FunctionSig[]) {
|
|
511
|
+
this.name = name
|
|
512
|
+
this.functions = functions
|
|
175
513
|
}
|
|
176
514
|
|
|
177
515
|
abstract buildByteCodeToDeploy(initialFields?: Fields): string
|
|
516
|
+
|
|
517
|
+
publicFunctions(): string[] {
|
|
518
|
+
return this.functions.filter((func) => func.isPublic).map((func) => func.name)
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
usingPreapprovedAssetsFunctions(): string[] {
|
|
522
|
+
return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
usingAssetsInContractFunctions(): string[] {
|
|
526
|
+
return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name)
|
|
527
|
+
}
|
|
178
528
|
}
|
|
179
529
|
|
|
180
|
-
export class Contract extends
|
|
530
|
+
export class Contract extends Artifact {
|
|
181
531
|
readonly bytecode: string
|
|
532
|
+
readonly bytecodeDebugPatch: string
|
|
182
533
|
readonly codeHash: string
|
|
183
|
-
readonly fieldsSig:
|
|
184
|
-
readonly eventsSig:
|
|
534
|
+
readonly fieldsSig: FieldsSig
|
|
535
|
+
readonly eventsSig: EventSig[]
|
|
536
|
+
|
|
537
|
+
readonly bytecodeDebug: string
|
|
538
|
+
readonly codeHashDebug: string
|
|
185
539
|
|
|
186
540
|
constructor(
|
|
187
|
-
|
|
541
|
+
name: string,
|
|
188
542
|
bytecode: string,
|
|
543
|
+
bytecodeDebugPatch: string,
|
|
189
544
|
codeHash: string,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
545
|
+
codeHashDebug: string,
|
|
546
|
+
fieldsSig: FieldsSig,
|
|
547
|
+
eventsSig: EventSig[],
|
|
548
|
+
functions: FunctionSig[]
|
|
193
549
|
) {
|
|
194
|
-
super(
|
|
550
|
+
super(name, functions)
|
|
195
551
|
this.bytecode = bytecode
|
|
552
|
+
this.bytecodeDebugPatch = bytecodeDebugPatch
|
|
196
553
|
this.codeHash = codeHash
|
|
197
554
|
this.fieldsSig = fieldsSig
|
|
198
555
|
this.eventsSig = eventsSig
|
|
199
|
-
}
|
|
200
|
-
|
|
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
556
|
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|
557
|
+
this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
|
|
558
|
+
this.codeHashDebug = codeHashDebug
|
|
258
559
|
}
|
|
259
560
|
|
|
260
561
|
// TODO: safely parse json
|
|
261
|
-
static fromJson(artifact: any): Contract {
|
|
562
|
+
static fromJson(artifact: any, bytecodeDebugPatch = '', codeHashDebug = ''): Contract {
|
|
262
563
|
if (
|
|
263
|
-
artifact.
|
|
564
|
+
artifact.name == null ||
|
|
264
565
|
artifact.bytecode == null ||
|
|
265
566
|
artifact.codeHash == null ||
|
|
266
567
|
artifact.fieldsSig == null ||
|
|
@@ -270,44 +571,55 @@ export class Contract extends Common {
|
|
|
270
571
|
throw Error('The artifact JSON for contract is incomplete')
|
|
271
572
|
}
|
|
272
573
|
const contract = new Contract(
|
|
273
|
-
artifact.
|
|
574
|
+
artifact.name,
|
|
274
575
|
artifact.bytecode,
|
|
576
|
+
bytecodeDebugPatch,
|
|
275
577
|
artifact.codeHash,
|
|
578
|
+
codeHashDebug ? codeHashDebug : artifact.codeHash,
|
|
276
579
|
artifact.fieldsSig,
|
|
277
580
|
artifact.eventsSig,
|
|
278
581
|
artifact.functions
|
|
279
582
|
)
|
|
280
|
-
this._putArtifactToCache(contract)
|
|
281
583
|
return contract
|
|
282
584
|
}
|
|
283
585
|
|
|
586
|
+
static fromCompileResult(result: node.CompileContractResult): Contract {
|
|
587
|
+
return new Contract(
|
|
588
|
+
result.name,
|
|
589
|
+
result.bytecode,
|
|
590
|
+
result.bytecodeDebugPatch,
|
|
591
|
+
result.codeHash,
|
|
592
|
+
result.codeHashDebug,
|
|
593
|
+
result.fields,
|
|
594
|
+
result.events,
|
|
595
|
+
result.functions
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
|
|
284
599
|
// support both 'code.ral' and 'code.ral.json'
|
|
285
|
-
static async fromArtifactFile(path: string): Promise<Contract> {
|
|
286
|
-
const
|
|
287
|
-
const artifactPath = sourceFile.artifactPath
|
|
288
|
-
const content = await fsPromises.readFile(artifactPath)
|
|
600
|
+
static async fromArtifactFile(path: string, bytecodeDebugPatch: string, codeHashDebug: string): Promise<Contract> {
|
|
601
|
+
const content = await fsPromises.readFile(path)
|
|
289
602
|
const artifact = JSON.parse(content.toString())
|
|
290
|
-
return Contract.fromJson(artifact)
|
|
603
|
+
return Contract.fromJson(artifact, bytecodeDebugPatch, codeHashDebug)
|
|
291
604
|
}
|
|
292
605
|
|
|
293
|
-
async fetchState(
|
|
294
|
-
const state = await
|
|
606
|
+
async fetchState(address: string, group: number): Promise<ContractState> {
|
|
607
|
+
const state = await web3.getCurrentNodeProvider().contracts.getContractsAddressState(address, {
|
|
608
|
+
group: group
|
|
609
|
+
})
|
|
295
610
|
return this.fromApiContractState(state)
|
|
296
611
|
}
|
|
297
612
|
|
|
298
613
|
override toString(): string {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
null,
|
|
309
|
-
2
|
|
310
|
-
)
|
|
614
|
+
const object = {
|
|
615
|
+
name: this.name,
|
|
616
|
+
bytecode: this.bytecode,
|
|
617
|
+
codeHash: this.codeHash,
|
|
618
|
+
fieldsSig: this.fieldsSig,
|
|
619
|
+
eventsSig: this.eventsSig,
|
|
620
|
+
functions: this.functions
|
|
621
|
+
}
|
|
622
|
+
return JSON.stringify(object, null, 2)
|
|
311
623
|
}
|
|
312
624
|
|
|
313
625
|
toState(fields: Fields, asset: Asset, address?: string): ContractState {
|
|
@@ -323,25 +635,37 @@ export class Contract extends Common {
|
|
|
323
635
|
}
|
|
324
636
|
}
|
|
325
637
|
|
|
638
|
+
// no need to be cryptographically strong random
|
|
326
639
|
static randomAddress(): string {
|
|
327
|
-
const bytes =
|
|
640
|
+
const bytes = new Uint8Array(33)
|
|
641
|
+
crypto.getRandomValues(bytes)
|
|
328
642
|
bytes[0] = 3
|
|
329
643
|
return bs58.encode(bytes)
|
|
330
644
|
}
|
|
331
645
|
|
|
646
|
+
private _printDebugMessages(funcName: string, messages: DebugMessage[]) {
|
|
647
|
+
if (messages.length != 0) {
|
|
648
|
+
console.log(`Testing ${this.name}.${funcName}:`)
|
|
649
|
+
messages.forEach((m) => console.log(`Debug - ${m.contractAddress} - ${m.message}`))
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
332
653
|
private async _test(
|
|
333
|
-
provider: NodeProvider,
|
|
334
654
|
funcName: string,
|
|
335
655
|
params: TestContractParams,
|
|
336
656
|
expectPublic: boolean,
|
|
337
|
-
accessType: string
|
|
657
|
+
accessType: string,
|
|
658
|
+
printDebugMessages: boolean
|
|
338
659
|
): Promise<TestContractResult> {
|
|
339
660
|
const apiParams: node.TestContract = this.toTestContract(funcName, params)
|
|
340
|
-
const apiResult = await
|
|
661
|
+
const apiResult = await web3.getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
|
|
341
662
|
|
|
342
663
|
const methodIndex =
|
|
343
664
|
typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName)
|
|
344
|
-
const isPublic = this.functions[`${methodIndex}`].
|
|
665
|
+
const isPublic = this.functions[`${methodIndex}`].isPublic
|
|
666
|
+
if (printDebugMessages) {
|
|
667
|
+
this._printDebugMessages(funcName, apiResult.debugMessages)
|
|
668
|
+
}
|
|
345
669
|
if (isPublic === expectPublic) {
|
|
346
670
|
const result = await this.fromTestContractResult(methodIndex, apiResult)
|
|
347
671
|
return result
|
|
@@ -351,19 +675,19 @@ export class Contract extends Common {
|
|
|
351
675
|
}
|
|
352
676
|
|
|
353
677
|
async testPublicMethod(
|
|
354
|
-
provider: NodeProvider,
|
|
355
678
|
funcName: string,
|
|
356
|
-
params: TestContractParams
|
|
679
|
+
params: TestContractParams,
|
|
680
|
+
printDebugMessages = true
|
|
357
681
|
): Promise<TestContractResult> {
|
|
358
|
-
return this._test(
|
|
682
|
+
return this._test(funcName, params, true, 'public', printDebugMessages)
|
|
359
683
|
}
|
|
360
684
|
|
|
361
685
|
async testPrivateMethod(
|
|
362
|
-
provider: NodeProvider,
|
|
363
686
|
funcName: string,
|
|
364
|
-
params: TestContractParams
|
|
687
|
+
params: TestContractParams,
|
|
688
|
+
printDebugMessages = true
|
|
365
689
|
): Promise<TestContractResult> {
|
|
366
|
-
return this._test(
|
|
690
|
+
return this._test(funcName, params, false, 'private', printDebugMessages)
|
|
367
691
|
}
|
|
368
692
|
|
|
369
693
|
toApiFields(fields?: Fields): node.Val[] {
|
|
@@ -399,7 +723,7 @@ export class Contract extends Common {
|
|
|
399
723
|
return {
|
|
400
724
|
group: params.group,
|
|
401
725
|
address: params.address,
|
|
402
|
-
bytecode: this.
|
|
726
|
+
bytecode: this.bytecodeDebug,
|
|
403
727
|
initialFields: this.toApiFields(params.initialFields),
|
|
404
728
|
initialAsset: typeof params.initialAsset !== 'undefined' ? toApiAsset(params.initialAsset) : undefined,
|
|
405
729
|
methodIndex: this.getMethodIndex(funcName),
|
|
@@ -409,33 +733,8 @@ export class Contract extends Common {
|
|
|
409
733
|
}
|
|
410
734
|
}
|
|
411
735
|
|
|
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
736
|
async fromApiContractState(state: node.ContractState): Promise<ContractState> {
|
|
438
|
-
const contract =
|
|
737
|
+
const contract = Project.currentProject.contractByCodeHash(state.codeHash)
|
|
439
738
|
return {
|
|
440
739
|
address: state.address,
|
|
441
740
|
contractId: binToHex(contractIdFromAddress(state.address)),
|
|
@@ -443,21 +742,19 @@ export class Contract extends Common {
|
|
|
443
742
|
initialStateHash: state.initialStateHash,
|
|
444
743
|
codeHash: state.codeHash,
|
|
445
744
|
fields: fromApiFields(state.fields, contract.fieldsSig),
|
|
446
|
-
fieldsSig:
|
|
745
|
+
fieldsSig: contract.fieldsSig,
|
|
447
746
|
asset: fromApiAsset(state.asset)
|
|
448
747
|
}
|
|
449
748
|
}
|
|
450
749
|
|
|
451
|
-
static ContractCreatedEvent:
|
|
750
|
+
static ContractCreatedEvent: EventSig = {
|
|
452
751
|
name: 'ContractCreated',
|
|
453
|
-
signature: 'event ContractCreated(address:Address)',
|
|
454
752
|
fieldNames: ['address'],
|
|
455
753
|
fieldTypes: ['Address']
|
|
456
754
|
}
|
|
457
755
|
|
|
458
|
-
static ContractDestroyedEvent:
|
|
756
|
+
static ContractDestroyedEvent: EventSig = {
|
|
459
757
|
name: 'ContractDestroyed',
|
|
460
|
-
signature: 'event ContractDestroyed(address:Address)',
|
|
461
758
|
fieldNames: ['address'],
|
|
462
759
|
fieldTypes: ['Address']
|
|
463
760
|
}
|
|
@@ -466,14 +763,14 @@ export class Contract extends Common {
|
|
|
466
763
|
event: node.ContractEventByTxId,
|
|
467
764
|
codeHash: string | undefined
|
|
468
765
|
): Promise<ContractEventByTxId> {
|
|
469
|
-
let eventSig:
|
|
766
|
+
let eventSig: EventSig
|
|
470
767
|
|
|
471
768
|
if (event.eventIndex == -1) {
|
|
472
769
|
eventSig = this.ContractCreatedEvent
|
|
473
770
|
} else if (event.eventIndex == -2) {
|
|
474
771
|
eventSig = this.ContractDestroyedEvent
|
|
475
772
|
} else {
|
|
476
|
-
const contract =
|
|
773
|
+
const contract = Project.currentProject.contractByCodeHash(codeHash!)
|
|
477
774
|
eventSig = contract.eventsSig[event.eventIndex]
|
|
478
775
|
}
|
|
479
776
|
|
|
@@ -490,8 +787,8 @@ export class Contract extends Common {
|
|
|
490
787
|
addressToCodeHash.set(result.address, result.codeHash)
|
|
491
788
|
result.contracts.forEach((contract) => addressToCodeHash.set(contract.address, contract.codeHash))
|
|
492
789
|
return {
|
|
493
|
-
address: result.address,
|
|
494
790
|
contractId: binToHex(contractIdFromAddress(result.address)),
|
|
791
|
+
contractAddress: result.address,
|
|
495
792
|
returns: fromApiArray(result.returns, this.functions[`${methodIndex}`].returnTypes),
|
|
496
793
|
gasUsed: result.gasUsed,
|
|
497
794
|
contracts: await Promise.all(result.contracts.map((contract) => this.fromApiContractState(contract))),
|
|
@@ -506,7 +803,8 @@ export class Contract extends Common {
|
|
|
506
803
|
throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
|
|
507
804
|
}
|
|
508
805
|
})
|
|
509
|
-
)
|
|
806
|
+
),
|
|
807
|
+
debugMessages: result.debugMessages
|
|
510
808
|
}
|
|
511
809
|
}
|
|
512
810
|
|
|
@@ -517,7 +815,7 @@ export class Contract extends Common {
|
|
|
517
815
|
bytecode: bytecode,
|
|
518
816
|
initialAttoAlphAmount: extractOptionalNumber256(params.initialAttoAlphAmount),
|
|
519
817
|
issueTokenAmount: extractOptionalNumber256(params.issueTokenAmount),
|
|
520
|
-
initialTokenAmounts: params.initialTokenAmounts
|
|
818
|
+
initialTokenAmounts: toApiTokens(params.initialTokenAmounts),
|
|
521
819
|
gasAmount: params.gasAmount,
|
|
522
820
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
523
821
|
}
|
|
@@ -541,85 +839,61 @@ export class Contract extends Common {
|
|
|
541
839
|
}
|
|
542
840
|
}
|
|
543
841
|
|
|
544
|
-
export class Script extends
|
|
842
|
+
export class Script extends Artifact {
|
|
545
843
|
readonly bytecodeTemplate: string
|
|
546
|
-
readonly
|
|
844
|
+
readonly bytecodeDebugPatch: string
|
|
845
|
+
readonly fieldsSig: FieldsSig
|
|
547
846
|
|
|
548
847
|
constructor(
|
|
549
|
-
|
|
848
|
+
name: string,
|
|
550
849
|
bytecodeTemplate: string,
|
|
551
|
-
|
|
552
|
-
|
|
850
|
+
bytecodeDebugPatch: string,
|
|
851
|
+
fieldsSig: FieldsSig,
|
|
852
|
+
functions: FunctionSig[]
|
|
553
853
|
) {
|
|
554
|
-
super(
|
|
854
|
+
super(name, functions)
|
|
555
855
|
this.bytecodeTemplate = bytecodeTemplate
|
|
856
|
+
this.bytecodeDebugPatch = bytecodeDebugPatch
|
|
556
857
|
this.fieldsSig = fieldsSig
|
|
557
858
|
}
|
|
558
859
|
|
|
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
|
|
860
|
+
static fromCompileResult(result: node.CompileScriptResult): Script {
|
|
861
|
+
return new Script(result.name, result.bytecodeTemplate, result.bytecodeDebugPatch, result.fields, result.functions)
|
|
589
862
|
}
|
|
590
863
|
|
|
591
864
|
// TODO: safely parse json
|
|
592
|
-
static fromJson(artifact: any): Script {
|
|
865
|
+
static fromJson(artifact: any, bytecodeDebugPatch = ''): Script {
|
|
593
866
|
if (
|
|
594
|
-
artifact.
|
|
867
|
+
artifact.name == null ||
|
|
595
868
|
artifact.bytecodeTemplate == null ||
|
|
596
869
|
artifact.fieldsSig == null ||
|
|
597
870
|
artifact.functions == null
|
|
598
871
|
) {
|
|
599
872
|
throw Error('The artifact JSON for script is incomplete')
|
|
600
873
|
}
|
|
601
|
-
return new Script(
|
|
874
|
+
return new Script(
|
|
875
|
+
artifact.name,
|
|
876
|
+
artifact.bytecodeTemplate,
|
|
877
|
+
bytecodeDebugPatch,
|
|
878
|
+
artifact.fieldsSig,
|
|
879
|
+
artifact.functions
|
|
880
|
+
)
|
|
602
881
|
}
|
|
603
882
|
|
|
604
|
-
static async fromArtifactFile(path: string): Promise<Script> {
|
|
605
|
-
const
|
|
606
|
-
const artifactPath = sourceFile.artifactPath
|
|
607
|
-
const content = await fsPromises.readFile(artifactPath)
|
|
883
|
+
static async fromArtifactFile(path: string, bytecodeDebugPatch: string): Promise<Script> {
|
|
884
|
+
const content = await fsPromises.readFile(path)
|
|
608
885
|
const artifact = JSON.parse(content.toString())
|
|
609
|
-
return this.fromJson(artifact)
|
|
886
|
+
return this.fromJson(artifact, bytecodeDebugPatch)
|
|
610
887
|
}
|
|
611
888
|
|
|
612
889
|
override toString(): string {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
null,
|
|
621
|
-
2
|
|
622
|
-
)
|
|
890
|
+
const object = {
|
|
891
|
+
name: this.name,
|
|
892
|
+
bytecodeTemplate: this.bytecodeTemplate,
|
|
893
|
+
fieldsSig: this.fieldsSig,
|
|
894
|
+
functions: this.functions
|
|
895
|
+
}
|
|
896
|
+
return JSON.stringify(object, null, 2)
|
|
623
897
|
}
|
|
624
898
|
|
|
625
899
|
async paramsForDeployment(params: BuildExecuteScriptTx): Promise<SignExecuteScriptTxParams> {
|
|
@@ -627,7 +901,7 @@ export class Script extends Common {
|
|
|
627
901
|
signerAddress: params.signerAddress,
|
|
628
902
|
bytecode: this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {}),
|
|
629
903
|
attoAlphAmount: extractOptionalNumber256(params.attoAlphAmount),
|
|
630
|
-
tokens:
|
|
904
|
+
tokens: toApiTokens(params.tokens),
|
|
631
905
|
gasAmount: params.gasAmount,
|
|
632
906
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
633
907
|
}
|
|
@@ -650,162 +924,8 @@ export class Script extends Common {
|
|
|
650
924
|
}
|
|
651
925
|
}
|
|
652
926
|
|
|
653
|
-
export type Number256 = number | bigint | string
|
|
654
|
-
export type Val = Number256 | boolean | string | Val[]
|
|
655
|
-
export type NamedVals = Record<string, Val>
|
|
656
|
-
export type Fields = NamedVals
|
|
657
|
-
export type Arguments = NamedVals
|
|
658
|
-
|
|
659
|
-
function extractBoolean(v: Val): boolean {
|
|
660
|
-
if (typeof v === 'boolean') {
|
|
661
|
-
return v
|
|
662
|
-
} else {
|
|
663
|
-
throw new Error(`Invalid boolean value: ${v}`)
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// TODO: check integer bounds
|
|
668
|
-
function extractNumber256(v: Val): string {
|
|
669
|
-
if ((typeof v === 'number' && Number.isInteger(v)) || typeof v === 'bigint') {
|
|
670
|
-
return v.toString()
|
|
671
|
-
} else if (typeof v === 'string') {
|
|
672
|
-
return v
|
|
673
|
-
} else {
|
|
674
|
-
throw new Error(`Invalid 256 bit number: ${v}`)
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
927
|
function extractOptionalNumber256(v?: Val): string | undefined {
|
|
679
|
-
return typeof v !== 'undefined' ?
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// TODO: check hex string
|
|
683
|
-
function extractByteVec(v: Val): string {
|
|
684
|
-
if (typeof v === 'string') {
|
|
685
|
-
// try to convert from address to contract id
|
|
686
|
-
try {
|
|
687
|
-
const address = bs58.decode(v)
|
|
688
|
-
if (address.length == 33 && address[0] == 3) {
|
|
689
|
-
return Buffer.from(address.slice(1)).toString('hex')
|
|
690
|
-
}
|
|
691
|
-
} catch (_) {
|
|
692
|
-
return v as string
|
|
693
|
-
}
|
|
694
|
-
return v as string
|
|
695
|
-
} else {
|
|
696
|
-
throw new Error(`Invalid string: ${v}`)
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function extractBs58(v: Val): string {
|
|
701
|
-
if (typeof v === 'string') {
|
|
702
|
-
try {
|
|
703
|
-
bs58.decode(v)
|
|
704
|
-
return v as string
|
|
705
|
-
} catch (error) {
|
|
706
|
-
throw new Error(`Invalid base58 string: ${v}`)
|
|
707
|
-
}
|
|
708
|
-
} else {
|
|
709
|
-
throw new Error(`Invalid string: ${v}`)
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
function decodeNumber256(n: string): Number256 {
|
|
714
|
-
if (Number.isSafeInteger(Number.parseInt(n))) {
|
|
715
|
-
return Number(n)
|
|
716
|
-
} else {
|
|
717
|
-
return BigInt(n)
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
export function extractArray(tpe: string, v: Val): node.Val {
|
|
722
|
-
if (!Array.isArray(v)) {
|
|
723
|
-
throw new Error(`Expected array, got ${v}`)
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
727
|
-
if (semiColonIndex == -1) {
|
|
728
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
732
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
733
|
-
if ((v as Val[]).length != dim) {
|
|
734
|
-
throw new Error(`Invalid val dimension: ${v}`)
|
|
735
|
-
} else {
|
|
736
|
-
return { value: (v as Val[]).map((v) => toApiVal(v, subType)), type: 'Array' }
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
export function toApiVal(v: Val, tpe: string): node.Val {
|
|
741
|
-
if (tpe === 'Bool') {
|
|
742
|
-
return { value: extractBoolean(v), type: tpe }
|
|
743
|
-
} else if (tpe === 'U256' || tpe === 'I256') {
|
|
744
|
-
return { value: extractNumber256(v), type: tpe }
|
|
745
|
-
} else if (tpe === 'ByteVec') {
|
|
746
|
-
return { value: extractByteVec(v), type: tpe }
|
|
747
|
-
} else if (tpe === 'Address') {
|
|
748
|
-
return { value: extractBs58(v), type: tpe }
|
|
749
|
-
} else {
|
|
750
|
-
return extractArray(tpe, v)
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
function decodeArrayType(tpe: string): [baseType: string, dims: number[]] {
|
|
755
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
756
|
-
if (semiColonIndex === -1) {
|
|
757
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
761
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
762
|
-
if (subType[0] == '[') {
|
|
763
|
-
const [baseType, subDim] = decodeArrayType(subType)
|
|
764
|
-
return [baseType, (subDim.unshift(dim), subDim)]
|
|
765
|
-
} else {
|
|
766
|
-
return [subType, [dim]]
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function foldVals(vals: Val[], dims: number[]): Val {
|
|
771
|
-
if (dims.length == 1) {
|
|
772
|
-
return vals
|
|
773
|
-
} else {
|
|
774
|
-
const result: Val[] = []
|
|
775
|
-
const chunkSize = vals.length / dims[0]
|
|
776
|
-
const chunkDims = dims.slice(1)
|
|
777
|
-
for (let i = 0; i < vals.length; i += chunkSize) {
|
|
778
|
-
const chunk = vals.slice(i, i + chunkSize)
|
|
779
|
-
result.push(foldVals(chunk, chunkDims))
|
|
780
|
-
}
|
|
781
|
-
return result
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function _fromApiVal(vals: node.Val[], valIndex: number, tpe: string): [result: Val, nextIndex: number] {
|
|
786
|
-
if (vals.length === 0) {
|
|
787
|
-
throw new Error('Not enough Vals')
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
const firstVal = vals[`${valIndex}`]
|
|
791
|
-
if (tpe === 'Bool' && firstVal.type === tpe) {
|
|
792
|
-
return [firstVal.value as boolean, valIndex + 1]
|
|
793
|
-
} else if ((tpe === 'U256' || tpe === 'I256') && firstVal.type === tpe) {
|
|
794
|
-
return [decodeNumber256(firstVal.value as string), valIndex + 1]
|
|
795
|
-
} else if ((tpe === 'ByteVec' || tpe === 'Address') && firstVal.type === tpe) {
|
|
796
|
-
return [firstVal.value as string, valIndex + 1]
|
|
797
|
-
} else {
|
|
798
|
-
const [baseType, dims] = decodeArrayType(tpe)
|
|
799
|
-
const arraySize = dims.reduce((a, b) => a * b)
|
|
800
|
-
const nextIndex = valIndex + arraySize
|
|
801
|
-
const valsToUse = vals.slice(valIndex, nextIndex)
|
|
802
|
-
if (valsToUse.length == arraySize && valsToUse.every((val) => val.type === baseType)) {
|
|
803
|
-
const localVals = valsToUse.map((val) => fromApiVal(val, baseType))
|
|
804
|
-
return [foldVals(localVals, dims), nextIndex]
|
|
805
|
-
} else {
|
|
806
|
-
throw new Error(`Invalid array Val type: ${valsToUse}, ${tpe}`)
|
|
807
|
-
}
|
|
808
|
-
}
|
|
928
|
+
return typeof v !== 'undefined' ? toApiNumber256(v) : undefined
|
|
809
929
|
}
|
|
810
930
|
|
|
811
931
|
function fromApiFields(vals: node.Val[], fieldsSig: node.FieldsSig): Fields {
|
|
@@ -816,70 +936,22 @@ function fromApiEventFields(vals: node.Val[], eventSig: node.EventSig): Fields {
|
|
|
816
936
|
return fromApiVals(vals, eventSig.fieldNames, eventSig.fieldTypes)
|
|
817
937
|
}
|
|
818
938
|
|
|
819
|
-
function fromApiVals(vals: node.Val[], names: string[], types: string[]): Fields {
|
|
820
|
-
let valIndex = 0
|
|
821
|
-
const result: Fields = {}
|
|
822
|
-
types.forEach((currentType, index) => {
|
|
823
|
-
const currentName = names[`${index}`]
|
|
824
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
825
|
-
valIndex = nextIndex
|
|
826
|
-
result[`${currentName}`] = val
|
|
827
|
-
})
|
|
828
|
-
return result
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function fromApiArray(vals: node.Val[], types: string[]): Val[] {
|
|
832
|
-
let valIndex = 0
|
|
833
|
-
const result: Val[] = []
|
|
834
|
-
for (const currentType of types) {
|
|
835
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
836
|
-
result.push(val)
|
|
837
|
-
valIndex = nextIndex
|
|
838
|
-
}
|
|
839
|
-
return result
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function fromApiVal(v: node.Val, tpe: string): Val {
|
|
843
|
-
if (v.type === 'Bool' && v.type === tpe) {
|
|
844
|
-
return v.value as boolean
|
|
845
|
-
} else if ((v.type === 'U256' || v.type === 'I256') && v.type === tpe) {
|
|
846
|
-
return decodeNumber256(v.value as string)
|
|
847
|
-
} else if ((v.type === 'ByteVec' || v.type === 'Address') && v.type === tpe) {
|
|
848
|
-
return v.value as string
|
|
849
|
-
} else {
|
|
850
|
-
throw new Error(`Invalid node.Val type: ${v}`)
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
939
|
export interface Asset {
|
|
855
940
|
alphAmount: Number256
|
|
856
941
|
tokens?: Token[]
|
|
857
942
|
}
|
|
858
943
|
|
|
859
|
-
export interface Token {
|
|
860
|
-
id: string
|
|
861
|
-
amount: Number256
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
function toApiToken(token: Token): node.Token {
|
|
865
|
-
return { id: token.id, amount: extractNumber256(token.amount) }
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function fromApiToken(token: node.Token): Token {
|
|
869
|
-
return { id: token.id, amount: decodeNumber256(token.amount) }
|
|
870
|
-
}
|
|
871
|
-
|
|
872
944
|
function toApiAsset(asset: Asset): node.AssetState {
|
|
873
945
|
return {
|
|
874
|
-
attoAlphAmount:
|
|
946
|
+
attoAlphAmount: toApiNumber256(asset.alphAmount),
|
|
875
947
|
tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(toApiToken) : []
|
|
876
948
|
}
|
|
877
949
|
}
|
|
878
950
|
|
|
879
951
|
function fromApiAsset(asset: node.AssetState): Asset {
|
|
880
952
|
return {
|
|
881
|
-
alphAmount:
|
|
882
|
-
tokens:
|
|
953
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
954
|
+
tokens: fromApiTokens(asset.tokens)
|
|
883
955
|
}
|
|
884
956
|
}
|
|
885
957
|
|
|
@@ -895,7 +967,7 @@ export interface ContractState {
|
|
|
895
967
|
initialStateHash?: string
|
|
896
968
|
codeHash: string
|
|
897
969
|
fields: Fields
|
|
898
|
-
fieldsSig:
|
|
970
|
+
fieldsSig: FieldsSig
|
|
899
971
|
asset: Asset
|
|
900
972
|
}
|
|
901
973
|
|
|
@@ -918,12 +990,12 @@ function toApiContractState(state: ContractState): node.ContractState {
|
|
|
918
990
|
}
|
|
919
991
|
}
|
|
920
992
|
|
|
921
|
-
function toApiFields(fields: Fields, fieldsSig:
|
|
993
|
+
function toApiFields(fields: Fields, fieldsSig: FieldsSig): node.Val[] {
|
|
922
994
|
return toApiVals(fields, fieldsSig.names, fieldsSig.types)
|
|
923
995
|
}
|
|
924
996
|
|
|
925
|
-
function toApiArgs(args: Arguments, funcSig:
|
|
926
|
-
return toApiVals(args, funcSig.
|
|
997
|
+
function toApiArgs(args: Arguments, funcSig: FunctionSig): node.Val[] {
|
|
998
|
+
return toApiVals(args, funcSig.paramNames, funcSig.paramTypes)
|
|
927
999
|
}
|
|
928
1000
|
|
|
929
1001
|
function toApiVals(fields: Fields, names: string[], types: string[]): node.Val[] {
|
|
@@ -967,14 +1039,17 @@ export interface ContractEventByTxId {
|
|
|
967
1039
|
fields: Fields
|
|
968
1040
|
}
|
|
969
1041
|
|
|
1042
|
+
export type DebugMessage = node.DebugMessage
|
|
1043
|
+
|
|
970
1044
|
export interface TestContractResult {
|
|
971
|
-
address: string
|
|
972
1045
|
contractId: string
|
|
1046
|
+
contractAddress: string
|
|
973
1047
|
returns: Val[]
|
|
974
1048
|
gasUsed: number
|
|
975
1049
|
contracts: ContractState[]
|
|
976
1050
|
txOutputs: Output[]
|
|
977
1051
|
events: ContractEventByTxId[]
|
|
1052
|
+
debugMessages: DebugMessage[]
|
|
978
1053
|
}
|
|
979
1054
|
export declare type Output = AssetOutput | ContractOutput
|
|
980
1055
|
export interface AssetOutput extends Asset {
|
|
@@ -987,7 +1062,7 @@ export interface ContractOutput {
|
|
|
987
1062
|
type: string
|
|
988
1063
|
address: string
|
|
989
1064
|
alphAmount: Number256
|
|
990
|
-
tokens
|
|
1065
|
+
tokens?: Token[]
|
|
991
1066
|
}
|
|
992
1067
|
|
|
993
1068
|
function fromApiOutput(output: node.Output): Output {
|
|
@@ -996,8 +1071,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
996
1071
|
return {
|
|
997
1072
|
type: 'AssetOutput',
|
|
998
1073
|
address: asset.address,
|
|
999
|
-
alphAmount:
|
|
1000
|
-
tokens: asset.tokens
|
|
1074
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1075
|
+
tokens: fromApiTokens(asset.tokens),
|
|
1001
1076
|
lockTime: asset.lockTime,
|
|
1002
1077
|
message: asset.message
|
|
1003
1078
|
}
|
|
@@ -1006,8 +1081,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
1006
1081
|
return {
|
|
1007
1082
|
type: 'ContractOutput',
|
|
1008
1083
|
address: asset.address,
|
|
1009
|
-
alphAmount:
|
|
1010
|
-
tokens: asset.tokens
|
|
1084
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1085
|
+
tokens: fromApiTokens(asset.tokens)
|
|
1011
1086
|
}
|
|
1012
1087
|
} else {
|
|
1013
1088
|
throw new Error(`Unknown output type: ${output}`)
|