@alephium/web3 0.2.0-rc.3 → 0.2.0-rc.30
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 +117 -17
- 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 +107 -68
- package/dist/src/contract/contract.js +395 -451
- 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 +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 +25 -13
- package/dist/src/signer/signer.js +51 -16
- 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 +4 -9
- package/dist/src/utils/utils.js +20 -24
- package/jest-config.json +11 -0
- package/package.json +11 -47
- package/src/api/api-alephium.ts +169 -25
- 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 +579 -545
- package/src/contract/events.ts +6 -5
- 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 +79 -27
- package/src/transaction/index.ts +0 -1
- package/src/transaction/status.ts +5 -2
- package/src/utils/bs58.ts +11 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/subscription.ts +1 -3
- package/src/utils/utils.ts +11 -19
- 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/contracts/test/metadata.ral +0 -17
- package/contracts/test/warnings.ral +0 -5
- 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 -197
- package/test/events.test.ts +0 -138
- package/test/transaction.test.ts +0 -72
package/src/contract/contract.ts
CHANGED
|
@@ -17,172 +17,499 @@ 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
|
-
|
|
111
|
+
class Compiled<T extends Artifact> {
|
|
112
|
+
sourceFile: SourceFile
|
|
113
|
+
artifact: T
|
|
114
|
+
warnings: string[]
|
|
52
115
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
116
|
+
constructor(sourceFile: SourceFile, artifact: T, warnings: string[]) {
|
|
117
|
+
this.sourceFile = sourceFile
|
|
118
|
+
this.artifact = artifact
|
|
119
|
+
this.warnings = warnings
|
|
120
|
+
}
|
|
121
|
+
}
|
|
56
122
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
static readonly
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
this._artifactCache.set(contract.codeHash, contract)
|
|
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}`)
|
|
74
139
|
}
|
|
75
140
|
}
|
|
76
141
|
|
|
77
|
-
constructor(
|
|
78
|
-
|
|
79
|
-
this.
|
|
142
|
+
constructor(compilerOptionsUsed: node.CompilerOptions, infos: Map<string, CodeInfo>) {
|
|
143
|
+
ProjectArtifact.checkCompilerOptionsParameter(compilerOptionsUsed)
|
|
144
|
+
this.compilerOptionsUsed = compilerOptionsUsed
|
|
145
|
+
this.infos = infos
|
|
80
146
|
}
|
|
81
147
|
|
|
82
|
-
|
|
83
|
-
|
|
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)
|
|
84
153
|
}
|
|
85
154
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
91
164
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
dirs.pop()
|
|
102
|
-
break
|
|
103
|
-
}
|
|
104
|
-
default: {
|
|
105
|
-
dirs.push(part)
|
|
106
|
-
}
|
|
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
|
|
107
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
|
+
})
|
|
108
239
|
})
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const localImportsCache: string[] = []
|
|
118
|
-
let result = contractStr.replace(Common.importRegex, (match) => {
|
|
119
|
-
localImportsCache.push(match)
|
|
120
|
-
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
|
+
})
|
|
121
248
|
})
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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)
|
|
131
293
|
}
|
|
132
294
|
}
|
|
133
|
-
return result
|
|
134
295
|
}
|
|
135
296
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const contractStr = contractBuffer.toString()
|
|
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
|
+
}
|
|
144
304
|
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
147
311
|
}
|
|
148
312
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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)
|
|
152
328
|
}
|
|
329
|
+
await this.projectArtifact.saveToFile(this.artifactsRootDir)
|
|
153
330
|
}
|
|
154
331
|
|
|
155
|
-
|
|
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}`)
|
|
338
|
+
}
|
|
339
|
+
return contract.artifact
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private static async compile(
|
|
156
343
|
provider: NodeProvider,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private static async loadArtifacts(
|
|
382
|
+
provider: NodeProvider,
|
|
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)
|
|
177
421
|
}
|
|
178
422
|
}
|
|
179
423
|
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
if (!
|
|
183
|
-
|
|
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
|
+
)
|
|
184
502
|
}
|
|
185
|
-
|
|
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
|
|
186
513
|
}
|
|
187
514
|
|
|
188
515
|
abstract buildByteCodeToDeploy(initialFields?: Fields): string
|
|
@@ -198,109 +525,43 @@ export abstract class Common {
|
|
|
198
525
|
usingAssetsInContractFunctions(): string[] {
|
|
199
526
|
return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name)
|
|
200
527
|
}
|
|
201
|
-
|
|
202
|
-
protected static checkCompilerWarnings(compiled: { warnings: string[] }, errorOnWarnings: boolean): void {
|
|
203
|
-
if (compiled.warnings.length !== 0) {
|
|
204
|
-
const prefixPerWarning = ' - '
|
|
205
|
-
const warningString = prefixPerWarning + compiled.warnings.join('\n' + prefixPerWarning)
|
|
206
|
-
const output = 'Compilation warnings:\n' + warningString + '\n'
|
|
207
|
-
if (errorOnWarnings) {
|
|
208
|
-
throw new Error(output)
|
|
209
|
-
} else {
|
|
210
|
-
console.log(output)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
528
|
}
|
|
215
529
|
|
|
216
|
-
export class Contract extends
|
|
530
|
+
export class Contract extends Artifact {
|
|
217
531
|
readonly bytecode: string
|
|
532
|
+
readonly bytecodeDebugPatch: string
|
|
218
533
|
readonly codeHash: string
|
|
219
534
|
readonly fieldsSig: FieldsSig
|
|
220
535
|
readonly eventsSig: EventSig[]
|
|
221
536
|
|
|
537
|
+
readonly bytecodeDebug: string
|
|
538
|
+
readonly codeHashDebug: string
|
|
539
|
+
|
|
222
540
|
constructor(
|
|
223
|
-
|
|
541
|
+
name: string,
|
|
224
542
|
bytecode: string,
|
|
543
|
+
bytecodeDebugPatch: string,
|
|
225
544
|
codeHash: string,
|
|
545
|
+
codeHashDebug: string,
|
|
226
546
|
fieldsSig: FieldsSig,
|
|
227
547
|
eventsSig: EventSig[],
|
|
228
548
|
functions: FunctionSig[]
|
|
229
549
|
) {
|
|
230
|
-
super(
|
|
550
|
+
super(name, functions)
|
|
231
551
|
this.bytecode = bytecode
|
|
552
|
+
this.bytecodeDebugPatch = bytecodeDebugPatch
|
|
232
553
|
this.codeHash = codeHash
|
|
233
554
|
this.fieldsSig = fieldsSig
|
|
234
555
|
this.eventsSig = eventsSig
|
|
235
|
-
}
|
|
236
556
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const contractMatches = contractStr.match(Contract.contractRegex)
|
|
240
|
-
if (interfaceMatches === null && contractMatches === null) {
|
|
241
|
-
throw new Error(`No contract found in: ${fileName}`)
|
|
242
|
-
}
|
|
243
|
-
if (interfaceMatches && contractMatches) {
|
|
244
|
-
throw new Error(`Multiple contracts and interfaces in: ${fileName}`)
|
|
245
|
-
}
|
|
246
|
-
if (interfaceMatches === null) {
|
|
247
|
-
if (contractMatches !== null && contractMatches.length > 1) {
|
|
248
|
-
throw new Error(`Multiple contracts in: ${fileName}`)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (contractMatches === null) {
|
|
252
|
-
if (interfaceMatches !== null && interfaceMatches.length > 1) {
|
|
253
|
-
throw new Error(`Multiple interfaces in: ${fileName}`)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
|
|
259
|
-
return Common._loadContractStr(sourceFile, [], (code) => Contract.checkCodeType(sourceFile.contractPath, code))
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
static async fromSource(provider: NodeProvider, path: string, errorOnWarnings = true): Promise<Contract> {
|
|
263
|
-
if (!fs.existsSync(Common._artifactsFolder())) {
|
|
264
|
-
fs.mkdirSync(Common._artifactsFolder(), { recursive: true })
|
|
265
|
-
}
|
|
266
|
-
const sourceFile = this.getSourceFile(path, [])
|
|
267
|
-
const contract = await Common._from(
|
|
268
|
-
provider,
|
|
269
|
-
sourceFile,
|
|
270
|
-
(sourceFile) => Contract.loadContractStr(sourceFile),
|
|
271
|
-
Contract.compile,
|
|
272
|
-
errorOnWarnings
|
|
273
|
-
)
|
|
274
|
-
this._putArtifactToCache(contract)
|
|
275
|
-
return contract
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
private static async compile(
|
|
279
|
-
provider: NodeProvider,
|
|
280
|
-
sourceFile: SourceFile,
|
|
281
|
-
contractStr: string,
|
|
282
|
-
contractHash: string,
|
|
283
|
-
errorOnWarnings: boolean
|
|
284
|
-
): Promise<Contract> {
|
|
285
|
-
const compiled = await provider.contracts.postContractsCompileContract({ code: contractStr })
|
|
286
|
-
Common.checkCompilerWarnings(compiled, errorOnWarnings)
|
|
287
|
-
|
|
288
|
-
const artifact = new Contract(
|
|
289
|
-
contractHash,
|
|
290
|
-
compiled.bytecode,
|
|
291
|
-
compiled.codeHash,
|
|
292
|
-
compiled.fields,
|
|
293
|
-
compiled.events,
|
|
294
|
-
compiled.functions
|
|
295
|
-
)
|
|
296
|
-
await artifact._saveToFile(sourceFile)
|
|
297
|
-
return artifact
|
|
557
|
+
this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
|
|
558
|
+
this.codeHashDebug = codeHashDebug
|
|
298
559
|
}
|
|
299
560
|
|
|
300
561
|
// TODO: safely parse json
|
|
301
|
-
static fromJson(artifact: any): Contract {
|
|
562
|
+
static fromJson(artifact: any, bytecodeDebugPatch = '', codeHashDebug = ''): Contract {
|
|
302
563
|
if (
|
|
303
|
-
artifact.
|
|
564
|
+
artifact.name == null ||
|
|
304
565
|
artifact.bytecode == null ||
|
|
305
566
|
artifact.codeHash == null ||
|
|
306
567
|
artifact.fieldsSig == null ||
|
|
@@ -310,44 +571,55 @@ export class Contract extends Common {
|
|
|
310
571
|
throw Error('The artifact JSON for contract is incomplete')
|
|
311
572
|
}
|
|
312
573
|
const contract = new Contract(
|
|
313
|
-
artifact.
|
|
574
|
+
artifact.name,
|
|
314
575
|
artifact.bytecode,
|
|
576
|
+
bytecodeDebugPatch,
|
|
315
577
|
artifact.codeHash,
|
|
578
|
+
codeHashDebug ? codeHashDebug : artifact.codeHash,
|
|
316
579
|
artifact.fieldsSig,
|
|
317
580
|
artifact.eventsSig,
|
|
318
581
|
artifact.functions
|
|
319
582
|
)
|
|
320
|
-
this._putArtifactToCache(contract)
|
|
321
583
|
return contract
|
|
322
584
|
}
|
|
323
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
|
+
|
|
324
599
|
// support both 'code.ral' and 'code.ral.json'
|
|
325
|
-
static async fromArtifactFile(path: string): Promise<Contract> {
|
|
326
|
-
const
|
|
327
|
-
const artifactPath = sourceFile.artifactPath
|
|
328
|
-
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)
|
|
329
602
|
const artifact = JSON.parse(content.toString())
|
|
330
|
-
return Contract.fromJson(artifact)
|
|
603
|
+
return Contract.fromJson(artifact, bytecodeDebugPatch, codeHashDebug)
|
|
331
604
|
}
|
|
332
605
|
|
|
333
|
-
async fetchState(
|
|
334
|
-
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
|
+
})
|
|
335
610
|
return this.fromApiContractState(state)
|
|
336
611
|
}
|
|
337
612
|
|
|
338
613
|
override toString(): string {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
null,
|
|
349
|
-
2
|
|
350
|
-
)
|
|
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)
|
|
351
623
|
}
|
|
352
624
|
|
|
353
625
|
toState(fields: Fields, asset: Asset, address?: string): ContractState {
|
|
@@ -363,25 +635,37 @@ export class Contract extends Common {
|
|
|
363
635
|
}
|
|
364
636
|
}
|
|
365
637
|
|
|
638
|
+
// no need to be cryptographically strong random
|
|
366
639
|
static randomAddress(): string {
|
|
367
|
-
const bytes =
|
|
640
|
+
const bytes = new Uint8Array(33)
|
|
641
|
+
crypto.getRandomValues(bytes)
|
|
368
642
|
bytes[0] = 3
|
|
369
643
|
return bs58.encode(bytes)
|
|
370
644
|
}
|
|
371
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
|
+
|
|
372
653
|
private async _test(
|
|
373
|
-
provider: NodeProvider,
|
|
374
654
|
funcName: string,
|
|
375
655
|
params: TestContractParams,
|
|
376
656
|
expectPublic: boolean,
|
|
377
|
-
accessType: string
|
|
657
|
+
accessType: string,
|
|
658
|
+
printDebugMessages: boolean
|
|
378
659
|
): Promise<TestContractResult> {
|
|
379
660
|
const apiParams: node.TestContract = this.toTestContract(funcName, params)
|
|
380
|
-
const apiResult = await
|
|
661
|
+
const apiResult = await web3.getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
|
|
381
662
|
|
|
382
663
|
const methodIndex =
|
|
383
664
|
typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName)
|
|
384
665
|
const isPublic = this.functions[`${methodIndex}`].isPublic
|
|
666
|
+
if (printDebugMessages) {
|
|
667
|
+
this._printDebugMessages(funcName, apiResult.debugMessages)
|
|
668
|
+
}
|
|
385
669
|
if (isPublic === expectPublic) {
|
|
386
670
|
const result = await this.fromTestContractResult(methodIndex, apiResult)
|
|
387
671
|
return result
|
|
@@ -391,19 +675,19 @@ export class Contract extends Common {
|
|
|
391
675
|
}
|
|
392
676
|
|
|
393
677
|
async testPublicMethod(
|
|
394
|
-
provider: NodeProvider,
|
|
395
678
|
funcName: string,
|
|
396
|
-
params: TestContractParams
|
|
679
|
+
params: TestContractParams,
|
|
680
|
+
printDebugMessages = true
|
|
397
681
|
): Promise<TestContractResult> {
|
|
398
|
-
return this._test(
|
|
682
|
+
return this._test(funcName, params, true, 'public', printDebugMessages)
|
|
399
683
|
}
|
|
400
684
|
|
|
401
685
|
async testPrivateMethod(
|
|
402
|
-
provider: NodeProvider,
|
|
403
686
|
funcName: string,
|
|
404
|
-
params: TestContractParams
|
|
687
|
+
params: TestContractParams,
|
|
688
|
+
printDebugMessages = true
|
|
405
689
|
): Promise<TestContractResult> {
|
|
406
|
-
return this._test(
|
|
690
|
+
return this._test(funcName, params, false, 'private', printDebugMessages)
|
|
407
691
|
}
|
|
408
692
|
|
|
409
693
|
toApiFields(fields?: Fields): node.Val[] {
|
|
@@ -439,7 +723,7 @@ export class Contract extends Common {
|
|
|
439
723
|
return {
|
|
440
724
|
group: params.group,
|
|
441
725
|
address: params.address,
|
|
442
|
-
bytecode: this.
|
|
726
|
+
bytecode: this.bytecodeDebug,
|
|
443
727
|
initialFields: this.toApiFields(params.initialFields),
|
|
444
728
|
initialAsset: typeof params.initialAsset !== 'undefined' ? toApiAsset(params.initialAsset) : undefined,
|
|
445
729
|
methodIndex: this.getMethodIndex(funcName),
|
|
@@ -449,33 +733,8 @@ export class Contract extends Common {
|
|
|
449
733
|
}
|
|
450
734
|
}
|
|
451
735
|
|
|
452
|
-
static async fromCodeHash(codeHash: string): Promise<Contract> {
|
|
453
|
-
const cached = this._getArtifactFromCache(codeHash)
|
|
454
|
-
if (typeof cached !== 'undefined') {
|
|
455
|
-
return cached as Contract
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const files = await fsPromises.readdir(Common._artifactsFolder())
|
|
459
|
-
for (const file of files) {
|
|
460
|
-
if (file.endsWith('.ral.json')) {
|
|
461
|
-
try {
|
|
462
|
-
const contract = await Contract.fromArtifactFile(file)
|
|
463
|
-
if (contract.codeHash === codeHash) {
|
|
464
|
-
return contract as Contract
|
|
465
|
-
}
|
|
466
|
-
} catch (_) {}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
throw new Error(`Unknown code with code hash: ${codeHash}`)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
static async getFieldsSig(state: node.ContractState): Promise<FieldsSig> {
|
|
474
|
-
return Contract.fromCodeHash(state.codeHash).then((contract) => contract.fieldsSig)
|
|
475
|
-
}
|
|
476
|
-
|
|
477
736
|
async fromApiContractState(state: node.ContractState): Promise<ContractState> {
|
|
478
|
-
const contract =
|
|
737
|
+
const contract = Project.currentProject.contractByCodeHash(state.codeHash)
|
|
479
738
|
return {
|
|
480
739
|
address: state.address,
|
|
481
740
|
contractId: binToHex(contractIdFromAddress(state.address)),
|
|
@@ -483,7 +742,7 @@ export class Contract extends Common {
|
|
|
483
742
|
initialStateHash: state.initialStateHash,
|
|
484
743
|
codeHash: state.codeHash,
|
|
485
744
|
fields: fromApiFields(state.fields, contract.fieldsSig),
|
|
486
|
-
fieldsSig:
|
|
745
|
+
fieldsSig: contract.fieldsSig,
|
|
487
746
|
asset: fromApiAsset(state.asset)
|
|
488
747
|
}
|
|
489
748
|
}
|
|
@@ -511,7 +770,7 @@ export class Contract extends Common {
|
|
|
511
770
|
} else if (event.eventIndex == -2) {
|
|
512
771
|
eventSig = this.ContractDestroyedEvent
|
|
513
772
|
} else {
|
|
514
|
-
const contract =
|
|
773
|
+
const contract = Project.currentProject.contractByCodeHash(codeHash!)
|
|
515
774
|
eventSig = contract.eventsSig[event.eventIndex]
|
|
516
775
|
}
|
|
517
776
|
|
|
@@ -528,8 +787,8 @@ export class Contract extends Common {
|
|
|
528
787
|
addressToCodeHash.set(result.address, result.codeHash)
|
|
529
788
|
result.contracts.forEach((contract) => addressToCodeHash.set(contract.address, contract.codeHash))
|
|
530
789
|
return {
|
|
531
|
-
address: result.address,
|
|
532
790
|
contractId: binToHex(contractIdFromAddress(result.address)),
|
|
791
|
+
contractAddress: result.address,
|
|
533
792
|
returns: fromApiArray(result.returns, this.functions[`${methodIndex}`].returnTypes),
|
|
534
793
|
gasUsed: result.gasUsed,
|
|
535
794
|
contracts: await Promise.all(result.contracts.map((contract) => this.fromApiContractState(contract))),
|
|
@@ -544,7 +803,8 @@ export class Contract extends Common {
|
|
|
544
803
|
throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
|
|
545
804
|
}
|
|
546
805
|
})
|
|
547
|
-
)
|
|
806
|
+
),
|
|
807
|
+
debugMessages: result.debugMessages
|
|
548
808
|
}
|
|
549
809
|
}
|
|
550
810
|
|
|
@@ -555,7 +815,7 @@ export class Contract extends Common {
|
|
|
555
815
|
bytecode: bytecode,
|
|
556
816
|
initialAttoAlphAmount: extractOptionalNumber256(params.initialAttoAlphAmount),
|
|
557
817
|
issueTokenAmount: extractOptionalNumber256(params.issueTokenAmount),
|
|
558
|
-
initialTokenAmounts: params.initialTokenAmounts
|
|
818
|
+
initialTokenAmounts: toApiTokens(params.initialTokenAmounts),
|
|
559
819
|
gasAmount: params.gasAmount,
|
|
560
820
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
561
821
|
}
|
|
@@ -568,7 +828,7 @@ export class Contract extends Common {
|
|
|
568
828
|
): Promise<DeployContractTransaction> {
|
|
569
829
|
const signerParams = await this.paramsForDeployment({
|
|
570
830
|
...params,
|
|
571
|
-
signerAddress: (await signer.
|
|
831
|
+
signerAddress: (await signer.getActiveAccount()).address
|
|
572
832
|
})
|
|
573
833
|
const response = await signer.buildContractCreationTx(signerParams)
|
|
574
834
|
return fromApiDeployContractUnsignedTx(response)
|
|
@@ -579,88 +839,61 @@ export class Contract extends Common {
|
|
|
579
839
|
}
|
|
580
840
|
}
|
|
581
841
|
|
|
582
|
-
export class Script extends
|
|
842
|
+
export class Script extends Artifact {
|
|
583
843
|
readonly bytecodeTemplate: string
|
|
844
|
+
readonly bytecodeDebugPatch: string
|
|
584
845
|
readonly fieldsSig: FieldsSig
|
|
585
846
|
|
|
586
|
-
constructor(
|
|
587
|
-
|
|
847
|
+
constructor(
|
|
848
|
+
name: string,
|
|
849
|
+
bytecodeTemplate: string,
|
|
850
|
+
bytecodeDebugPatch: string,
|
|
851
|
+
fieldsSig: FieldsSig,
|
|
852
|
+
functions: FunctionSig[]
|
|
853
|
+
) {
|
|
854
|
+
super(name, functions)
|
|
588
855
|
this.bytecodeTemplate = bytecodeTemplate
|
|
856
|
+
this.bytecodeDebugPatch = bytecodeDebugPatch
|
|
589
857
|
this.fieldsSig = fieldsSig
|
|
590
858
|
}
|
|
591
859
|
|
|
592
|
-
static
|
|
593
|
-
|
|
594
|
-
if (scriptMatches === null) {
|
|
595
|
-
throw new Error(`No script found in: ${fileName}`)
|
|
596
|
-
} else if (scriptMatches.length > 1) {
|
|
597
|
-
throw new Error(`Multiple scripts in: ${fileName}`)
|
|
598
|
-
} else {
|
|
599
|
-
return
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
private static async loadContractStr(sourceFile: SourceFile): Promise<string> {
|
|
604
|
-
return Common._loadContractStr(sourceFile, [], (code) => Script.checkCodeType(sourceFile.contractPath, code))
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
static async fromSource(provider: NodeProvider, path: string, errorOnWarnings = true): Promise<Script> {
|
|
608
|
-
const sourceFile = this.getSourceFile(path, [])
|
|
609
|
-
return Common._from(
|
|
610
|
-
provider,
|
|
611
|
-
sourceFile,
|
|
612
|
-
(sourceFile) => Script.loadContractStr(sourceFile),
|
|
613
|
-
Script.compile,
|
|
614
|
-
errorOnWarnings
|
|
615
|
-
)
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
private static async compile(
|
|
619
|
-
provider: NodeProvider,
|
|
620
|
-
sourceFile: SourceFile,
|
|
621
|
-
scriptStr: string,
|
|
622
|
-
contractHash: string,
|
|
623
|
-
errorOnWarnings = true
|
|
624
|
-
): Promise<Script> {
|
|
625
|
-
const compiled = await provider.contracts.postContractsCompileScript({ code: scriptStr })
|
|
626
|
-
Common.checkCompilerWarnings(compiled, errorOnWarnings)
|
|
627
|
-
const artifact = new Script(contractHash, compiled.bytecodeTemplate, compiled.fields, compiled.functions)
|
|
628
|
-
await artifact._saveToFile(sourceFile)
|
|
629
|
-
return artifact
|
|
860
|
+
static fromCompileResult(result: node.CompileScriptResult): Script {
|
|
861
|
+
return new Script(result.name, result.bytecodeTemplate, result.bytecodeDebugPatch, result.fields, result.functions)
|
|
630
862
|
}
|
|
631
863
|
|
|
632
864
|
// TODO: safely parse json
|
|
633
|
-
static fromJson(artifact: any): Script {
|
|
865
|
+
static fromJson(artifact: any, bytecodeDebugPatch = ''): Script {
|
|
634
866
|
if (
|
|
635
|
-
artifact.
|
|
867
|
+
artifact.name == null ||
|
|
636
868
|
artifact.bytecodeTemplate == null ||
|
|
637
869
|
artifact.fieldsSig == null ||
|
|
638
870
|
artifact.functions == null
|
|
639
871
|
) {
|
|
640
872
|
throw Error('The artifact JSON for script is incomplete')
|
|
641
873
|
}
|
|
642
|
-
return new Script(
|
|
874
|
+
return new Script(
|
|
875
|
+
artifact.name,
|
|
876
|
+
artifact.bytecodeTemplate,
|
|
877
|
+
bytecodeDebugPatch,
|
|
878
|
+
artifact.fieldsSig,
|
|
879
|
+
artifact.functions
|
|
880
|
+
)
|
|
643
881
|
}
|
|
644
882
|
|
|
645
|
-
static async fromArtifactFile(path: string): Promise<Script> {
|
|
646
|
-
const
|
|
647
|
-
const artifactPath = sourceFile.artifactPath
|
|
648
|
-
const content = await fsPromises.readFile(artifactPath)
|
|
883
|
+
static async fromArtifactFile(path: string, bytecodeDebugPatch: string): Promise<Script> {
|
|
884
|
+
const content = await fsPromises.readFile(path)
|
|
649
885
|
const artifact = JSON.parse(content.toString())
|
|
650
|
-
return this.fromJson(artifact)
|
|
886
|
+
return this.fromJson(artifact, bytecodeDebugPatch)
|
|
651
887
|
}
|
|
652
888
|
|
|
653
889
|
override toString(): string {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
null,
|
|
662
|
-
2
|
|
663
|
-
)
|
|
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)
|
|
664
897
|
}
|
|
665
898
|
|
|
666
899
|
async paramsForDeployment(params: BuildExecuteScriptTx): Promise<SignExecuteScriptTxParams> {
|
|
@@ -668,7 +901,7 @@ export class Script extends Common {
|
|
|
668
901
|
signerAddress: params.signerAddress,
|
|
669
902
|
bytecode: this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {}),
|
|
670
903
|
attoAlphAmount: extractOptionalNumber256(params.attoAlphAmount),
|
|
671
|
-
tokens:
|
|
904
|
+
tokens: toApiTokens(params.tokens),
|
|
672
905
|
gasAmount: params.gasAmount,
|
|
673
906
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
674
907
|
}
|
|
@@ -681,7 +914,7 @@ export class Script extends Common {
|
|
|
681
914
|
): Promise<BuildScriptTxResult> {
|
|
682
915
|
const signerParams = await this.paramsForDeployment({
|
|
683
916
|
...params,
|
|
684
|
-
signerAddress: (await signer.
|
|
917
|
+
signerAddress: (await signer.getActiveAccount()).address
|
|
685
918
|
})
|
|
686
919
|
return await signer.buildScriptTx(signerParams)
|
|
687
920
|
}
|
|
@@ -691,162 +924,8 @@ export class Script extends Common {
|
|
|
691
924
|
}
|
|
692
925
|
}
|
|
693
926
|
|
|
694
|
-
export type Number256 = number | bigint | string
|
|
695
|
-
export type Val = Number256 | boolean | string | Val[]
|
|
696
|
-
export type NamedVals = Record<string, Val>
|
|
697
|
-
export type Fields = NamedVals
|
|
698
|
-
export type Arguments = NamedVals
|
|
699
|
-
|
|
700
|
-
function extractBoolean(v: Val): boolean {
|
|
701
|
-
if (typeof v === 'boolean') {
|
|
702
|
-
return v
|
|
703
|
-
} else {
|
|
704
|
-
throw new Error(`Invalid boolean value: ${v}`)
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// TODO: check integer bounds
|
|
709
|
-
function extractNumber256(v: Val): string {
|
|
710
|
-
if ((typeof v === 'number' && Number.isInteger(v)) || typeof v === 'bigint') {
|
|
711
|
-
return v.toString()
|
|
712
|
-
} else if (typeof v === 'string') {
|
|
713
|
-
return v
|
|
714
|
-
} else {
|
|
715
|
-
throw new Error(`Invalid 256 bit number: ${v}`)
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
927
|
function extractOptionalNumber256(v?: Val): string | undefined {
|
|
720
|
-
return typeof v !== 'undefined' ?
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// TODO: check hex string
|
|
724
|
-
function extractByteVec(v: Val): string {
|
|
725
|
-
if (typeof v === 'string') {
|
|
726
|
-
// try to convert from address to contract id
|
|
727
|
-
try {
|
|
728
|
-
const address = bs58.decode(v)
|
|
729
|
-
if (address.length == 33 && address[0] == 3) {
|
|
730
|
-
return Buffer.from(address.slice(1)).toString('hex')
|
|
731
|
-
}
|
|
732
|
-
} catch (_) {
|
|
733
|
-
return v as string
|
|
734
|
-
}
|
|
735
|
-
return v as string
|
|
736
|
-
} else {
|
|
737
|
-
throw new Error(`Invalid string: ${v}`)
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
function extractBs58(v: Val): string {
|
|
742
|
-
if (typeof v === 'string') {
|
|
743
|
-
try {
|
|
744
|
-
bs58.decode(v)
|
|
745
|
-
return v as string
|
|
746
|
-
} catch (error) {
|
|
747
|
-
throw new Error(`Invalid base58 string: ${v}`)
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
throw new Error(`Invalid string: ${v}`)
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
function decodeNumber256(n: string): Number256 {
|
|
755
|
-
if (Number.isSafeInteger(Number.parseInt(n))) {
|
|
756
|
-
return Number(n)
|
|
757
|
-
} else {
|
|
758
|
-
return BigInt(n)
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
export function extractArray(tpe: string, v: Val): node.Val {
|
|
763
|
-
if (!Array.isArray(v)) {
|
|
764
|
-
throw new Error(`Expected array, got ${v}`)
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
768
|
-
if (semiColonIndex == -1) {
|
|
769
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
773
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
774
|
-
if ((v as Val[]).length != dim) {
|
|
775
|
-
throw new Error(`Invalid val dimension: ${v}`)
|
|
776
|
-
} else {
|
|
777
|
-
return { value: (v as Val[]).map((v) => toApiVal(v, subType)), type: 'Array' }
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
export function toApiVal(v: Val, tpe: string): node.Val {
|
|
782
|
-
if (tpe === 'Bool') {
|
|
783
|
-
return { value: extractBoolean(v), type: tpe }
|
|
784
|
-
} else if (tpe === 'U256' || tpe === 'I256') {
|
|
785
|
-
return { value: extractNumber256(v), type: tpe }
|
|
786
|
-
} else if (tpe === 'ByteVec') {
|
|
787
|
-
return { value: extractByteVec(v), type: tpe }
|
|
788
|
-
} else if (tpe === 'Address') {
|
|
789
|
-
return { value: extractBs58(v), type: tpe }
|
|
790
|
-
} else {
|
|
791
|
-
return extractArray(tpe, v)
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
function decodeArrayType(tpe: string): [baseType: string, dims: number[]] {
|
|
796
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
797
|
-
if (semiColonIndex === -1) {
|
|
798
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
802
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
803
|
-
if (subType[0] == '[') {
|
|
804
|
-
const [baseType, subDim] = decodeArrayType(subType)
|
|
805
|
-
return [baseType, (subDim.unshift(dim), subDim)]
|
|
806
|
-
} else {
|
|
807
|
-
return [subType, [dim]]
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
function foldVals(vals: Val[], dims: number[]): Val {
|
|
812
|
-
if (dims.length == 1) {
|
|
813
|
-
return vals
|
|
814
|
-
} else {
|
|
815
|
-
const result: Val[] = []
|
|
816
|
-
const chunkSize = vals.length / dims[0]
|
|
817
|
-
const chunkDims = dims.slice(1)
|
|
818
|
-
for (let i = 0; i < vals.length; i += chunkSize) {
|
|
819
|
-
const chunk = vals.slice(i, i + chunkSize)
|
|
820
|
-
result.push(foldVals(chunk, chunkDims))
|
|
821
|
-
}
|
|
822
|
-
return result
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
function _fromApiVal(vals: node.Val[], valIndex: number, tpe: string): [result: Val, nextIndex: number] {
|
|
827
|
-
if (vals.length === 0) {
|
|
828
|
-
throw new Error('Not enough Vals')
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
const firstVal = vals[`${valIndex}`]
|
|
832
|
-
if (tpe === 'Bool' && firstVal.type === tpe) {
|
|
833
|
-
return [firstVal.value as boolean, valIndex + 1]
|
|
834
|
-
} else if ((tpe === 'U256' || tpe === 'I256') && firstVal.type === tpe) {
|
|
835
|
-
return [decodeNumber256(firstVal.value as string), valIndex + 1]
|
|
836
|
-
} else if ((tpe === 'ByteVec' || tpe === 'Address') && firstVal.type === tpe) {
|
|
837
|
-
return [firstVal.value as string, valIndex + 1]
|
|
838
|
-
} else {
|
|
839
|
-
const [baseType, dims] = decodeArrayType(tpe)
|
|
840
|
-
const arraySize = dims.reduce((a, b) => a * b)
|
|
841
|
-
const nextIndex = valIndex + arraySize
|
|
842
|
-
const valsToUse = vals.slice(valIndex, nextIndex)
|
|
843
|
-
if (valsToUse.length == arraySize && valsToUse.every((val) => val.type === baseType)) {
|
|
844
|
-
const localVals = valsToUse.map((val) => fromApiVal(val, baseType))
|
|
845
|
-
return [foldVals(localVals, dims), nextIndex]
|
|
846
|
-
} else {
|
|
847
|
-
throw new Error(`Invalid array Val type: ${valsToUse}, ${tpe}`)
|
|
848
|
-
}
|
|
849
|
-
}
|
|
928
|
+
return typeof v !== 'undefined' ? toApiNumber256(v) : undefined
|
|
850
929
|
}
|
|
851
930
|
|
|
852
931
|
function fromApiFields(vals: node.Val[], fieldsSig: node.FieldsSig): Fields {
|
|
@@ -857,70 +936,22 @@ function fromApiEventFields(vals: node.Val[], eventSig: node.EventSig): Fields {
|
|
|
857
936
|
return fromApiVals(vals, eventSig.fieldNames, eventSig.fieldTypes)
|
|
858
937
|
}
|
|
859
938
|
|
|
860
|
-
function fromApiVals(vals: node.Val[], names: string[], types: string[]): Fields {
|
|
861
|
-
let valIndex = 0
|
|
862
|
-
const result: Fields = {}
|
|
863
|
-
types.forEach((currentType, index) => {
|
|
864
|
-
const currentName = names[`${index}`]
|
|
865
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
866
|
-
valIndex = nextIndex
|
|
867
|
-
result[`${currentName}`] = val
|
|
868
|
-
})
|
|
869
|
-
return result
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
function fromApiArray(vals: node.Val[], types: string[]): Val[] {
|
|
873
|
-
let valIndex = 0
|
|
874
|
-
const result: Val[] = []
|
|
875
|
-
for (const currentType of types) {
|
|
876
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
877
|
-
result.push(val)
|
|
878
|
-
valIndex = nextIndex
|
|
879
|
-
}
|
|
880
|
-
return result
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
function fromApiVal(v: node.Val, tpe: string): Val {
|
|
884
|
-
if (v.type === 'Bool' && v.type === tpe) {
|
|
885
|
-
return v.value as boolean
|
|
886
|
-
} else if ((v.type === 'U256' || v.type === 'I256') && v.type === tpe) {
|
|
887
|
-
return decodeNumber256(v.value as string)
|
|
888
|
-
} else if ((v.type === 'ByteVec' || v.type === 'Address') && v.type === tpe) {
|
|
889
|
-
return v.value as string
|
|
890
|
-
} else {
|
|
891
|
-
throw new Error(`Invalid node.Val type: ${v}`)
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
939
|
export interface Asset {
|
|
896
940
|
alphAmount: Number256
|
|
897
941
|
tokens?: Token[]
|
|
898
942
|
}
|
|
899
943
|
|
|
900
|
-
export interface Token {
|
|
901
|
-
id: string
|
|
902
|
-
amount: Number256
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
function toApiToken(token: Token): node.Token {
|
|
906
|
-
return { id: token.id, amount: extractNumber256(token.amount) }
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
function fromApiToken(token: node.Token): Token {
|
|
910
|
-
return { id: token.id, amount: decodeNumber256(token.amount) }
|
|
911
|
-
}
|
|
912
|
-
|
|
913
944
|
function toApiAsset(asset: Asset): node.AssetState {
|
|
914
945
|
return {
|
|
915
|
-
attoAlphAmount:
|
|
946
|
+
attoAlphAmount: toApiNumber256(asset.alphAmount),
|
|
916
947
|
tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(toApiToken) : []
|
|
917
948
|
}
|
|
918
949
|
}
|
|
919
950
|
|
|
920
951
|
function fromApiAsset(asset: node.AssetState): Asset {
|
|
921
952
|
return {
|
|
922
|
-
alphAmount:
|
|
923
|
-
tokens:
|
|
953
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
954
|
+
tokens: fromApiTokens(asset.tokens)
|
|
924
955
|
}
|
|
925
956
|
}
|
|
926
957
|
|
|
@@ -1008,14 +1039,17 @@ export interface ContractEventByTxId {
|
|
|
1008
1039
|
fields: Fields
|
|
1009
1040
|
}
|
|
1010
1041
|
|
|
1042
|
+
export type DebugMessage = node.DebugMessage
|
|
1043
|
+
|
|
1011
1044
|
export interface TestContractResult {
|
|
1012
|
-
address: string
|
|
1013
1045
|
contractId: string
|
|
1046
|
+
contractAddress: string
|
|
1014
1047
|
returns: Val[]
|
|
1015
1048
|
gasUsed: number
|
|
1016
1049
|
contracts: ContractState[]
|
|
1017
1050
|
txOutputs: Output[]
|
|
1018
1051
|
events: ContractEventByTxId[]
|
|
1052
|
+
debugMessages: DebugMessage[]
|
|
1019
1053
|
}
|
|
1020
1054
|
export declare type Output = AssetOutput | ContractOutput
|
|
1021
1055
|
export interface AssetOutput extends Asset {
|
|
@@ -1028,7 +1062,7 @@ export interface ContractOutput {
|
|
|
1028
1062
|
type: string
|
|
1029
1063
|
address: string
|
|
1030
1064
|
alphAmount: Number256
|
|
1031
|
-
tokens
|
|
1065
|
+
tokens?: Token[]
|
|
1032
1066
|
}
|
|
1033
1067
|
|
|
1034
1068
|
function fromApiOutput(output: node.Output): Output {
|
|
@@ -1037,8 +1071,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
1037
1071
|
return {
|
|
1038
1072
|
type: 'AssetOutput',
|
|
1039
1073
|
address: asset.address,
|
|
1040
|
-
alphAmount:
|
|
1041
|
-
tokens: asset.tokens
|
|
1074
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1075
|
+
tokens: fromApiTokens(asset.tokens),
|
|
1042
1076
|
lockTime: asset.lockTime,
|
|
1043
1077
|
message: asset.message
|
|
1044
1078
|
}
|
|
@@ -1047,8 +1081,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
1047
1081
|
return {
|
|
1048
1082
|
type: 'ContractOutput',
|
|
1049
1083
|
address: asset.address,
|
|
1050
|
-
alphAmount:
|
|
1051
|
-
tokens: asset.tokens
|
|
1084
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1085
|
+
tokens: fromApiTokens(asset.tokens)
|
|
1052
1086
|
}
|
|
1053
1087
|
} else {
|
|
1054
1088
|
throw new Error(`Unknown output type: ${output}`)
|