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