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