@alephium/web3 0.2.0-rc.10 → 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/contracts/greeter_main.ral +1 -1
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +4 -0
- package/dist/src/api/types.d.ts +23 -0
- package/dist/src/api/types.js +240 -0
- package/dist/src/contract/contract.d.ts +15 -24
- package/dist/src/contract/contract.js +41 -231
- package/dist/src/contract/events.d.ts +4 -4
- package/dist/src/contract/ralph.d.ts +4 -4
- package/dist/src/signer/signer.d.ts +20 -10
- package/dist/src/signer/signer.js +38 -6
- package/dist/src/transaction/status.d.ts +2 -1
- package/package.json +1 -1
- package/src/api/index.ts +1 -0
- package/src/api/types.ts +233 -0
- package/src/contract/contract.ts +60 -236
- package/src/contract/events.ts +4 -4
- package/src/contract/ralph.ts +4 -4
- package/src/signer/signer.ts +67 -17
- package/src/transaction/status.ts +3 -1
- package/templates/base/package.json +1 -1
- package/templates/base/src/greeter.ts +4 -4
- package/templates/react/package.json +1 -1
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 - 2022 The Alephium Authors
|
|
3
|
+
This file is part of the alephium project.
|
|
4
|
+
|
|
5
|
+
The library is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
The library is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Lesser General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
along with the library. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { assertType, bs58, Eq } from '../utils'
|
|
20
|
+
import * as node from './api-alephium'
|
|
21
|
+
|
|
22
|
+
export type Number256 = number | bigint | string
|
|
23
|
+
export type Val = Number256 | boolean | string | Val[]
|
|
24
|
+
export type NamedVals = Record<string, Val>
|
|
25
|
+
|
|
26
|
+
export interface Token {
|
|
27
|
+
id: string
|
|
28
|
+
amount: Number256
|
|
29
|
+
}
|
|
30
|
+
assertType<Eq<keyof Token, keyof node.Token>>
|
|
31
|
+
|
|
32
|
+
export function toApiToken(token: Token): node.Token {
|
|
33
|
+
return { id: token.id, amount: toApiNumber256(token.amount) }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function toApiTokens(tokens?: Token[]): node.Token[] | undefined {
|
|
37
|
+
return tokens?.map(toApiToken)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function fromApiToken(token: node.Token): Token {
|
|
41
|
+
return { id: token.id, amount: fromApiNumber256(token.amount) }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function fromApiTokens(tokens?: node.Token[]): Token[] | undefined {
|
|
45
|
+
return tokens?.map(fromApiToken)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function toApiBoolean(v: Val): boolean {
|
|
49
|
+
if (typeof v === 'boolean') {
|
|
50
|
+
return v
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(`Invalid boolean value: ${v}`)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// TODO: check integer bounds
|
|
57
|
+
export function toApiNumber256(v: Val): string {
|
|
58
|
+
if ((typeof v === 'number' && Number.isInteger(v)) || typeof v === 'bigint') {
|
|
59
|
+
return v.toString()
|
|
60
|
+
} else if (typeof v === 'string') {
|
|
61
|
+
return v
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(`Invalid 256 bit number: ${v}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function toApiNumber256Optional(v?: Val): string | undefined {
|
|
68
|
+
return v === undefined ? undefined : toApiNumber256(v)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function fromApiNumber256(n: string): Number256 {
|
|
72
|
+
if (Number.isSafeInteger(Number.parseInt(n))) {
|
|
73
|
+
return Number(n)
|
|
74
|
+
} else {
|
|
75
|
+
return BigInt(n)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// TODO: check hex string
|
|
80
|
+
export function toApiByteVec(v: Val): string {
|
|
81
|
+
if (typeof v === 'string') {
|
|
82
|
+
// try to convert from address to contract id
|
|
83
|
+
try {
|
|
84
|
+
const address = bs58.decode(v)
|
|
85
|
+
if (address.length == 33 && address[0] == 3) {
|
|
86
|
+
return Buffer.from(address.slice(1)).toString('hex')
|
|
87
|
+
}
|
|
88
|
+
} catch (_) {
|
|
89
|
+
return v as string
|
|
90
|
+
}
|
|
91
|
+
return v as string
|
|
92
|
+
} else {
|
|
93
|
+
throw new Error(`Invalid string: ${v}`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function toApiAddress(v: Val): string {
|
|
98
|
+
if (typeof v === 'string') {
|
|
99
|
+
try {
|
|
100
|
+
bs58.decode(v)
|
|
101
|
+
return v as string
|
|
102
|
+
} catch (error) {
|
|
103
|
+
throw new Error(`Invalid base58 string: ${v}`)
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
throw new Error(`Invalid string: ${v}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function toApiArray(tpe: string, v: Val): node.Val {
|
|
111
|
+
if (!Array.isArray(v)) {
|
|
112
|
+
throw new Error(`Expected array, got ${v}`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const semiColonIndex = tpe.lastIndexOf(';')
|
|
116
|
+
if (semiColonIndex == -1) {
|
|
117
|
+
throw new Error(`Invalid Val type: ${tpe}`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const subType = tpe.slice(1, semiColonIndex)
|
|
121
|
+
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
122
|
+
if ((v as Val[]).length != dim) {
|
|
123
|
+
throw new Error(`Invalid val dimension: ${v}`)
|
|
124
|
+
} else {
|
|
125
|
+
return { value: (v as Val[]).map((v) => toApiVal(v, subType)), type: 'Array' }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function toApiVal(v: Val, tpe: string): node.Val {
|
|
130
|
+
if (tpe === 'Bool') {
|
|
131
|
+
return { value: toApiBoolean(v), type: tpe }
|
|
132
|
+
} else if (tpe === 'U256' || tpe === 'I256') {
|
|
133
|
+
return { value: toApiNumber256(v), type: tpe }
|
|
134
|
+
} else if (tpe === 'ByteVec') {
|
|
135
|
+
return { value: toApiByteVec(v), type: tpe }
|
|
136
|
+
} else if (tpe === 'Address') {
|
|
137
|
+
return { value: toApiAddress(v), type: tpe }
|
|
138
|
+
} else {
|
|
139
|
+
return toApiArray(tpe, v)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _fromApiVal(vals: node.Val[], valIndex: number, tpe: string): [result: Val, nextIndex: number] {
|
|
144
|
+
if (vals.length === 0) {
|
|
145
|
+
throw new Error('Not enough Vals')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const firstVal = vals[`${valIndex}`]
|
|
149
|
+
if (tpe === 'Bool' && firstVal.type === tpe) {
|
|
150
|
+
return [firstVal.value as boolean, valIndex + 1]
|
|
151
|
+
} else if ((tpe === 'U256' || tpe === 'I256') && firstVal.type === tpe) {
|
|
152
|
+
return [fromApiNumber256(firstVal.value as string), valIndex + 1]
|
|
153
|
+
} else if ((tpe === 'ByteVec' || tpe === 'Address') && firstVal.type === tpe) {
|
|
154
|
+
return [firstVal.value as string, valIndex + 1]
|
|
155
|
+
} else {
|
|
156
|
+
const [baseType, dims] = decodeArrayType(tpe)
|
|
157
|
+
const arraySize = dims.reduce((a, b) => a * b)
|
|
158
|
+
const nextIndex = valIndex + arraySize
|
|
159
|
+
const valsToUse = vals.slice(valIndex, nextIndex)
|
|
160
|
+
if (valsToUse.length == arraySize && valsToUse.every((val) => val.type === baseType)) {
|
|
161
|
+
const localVals = valsToUse.map((val) => fromApiVal(val, baseType))
|
|
162
|
+
return [foldVals(localVals, dims), nextIndex]
|
|
163
|
+
} else {
|
|
164
|
+
throw new Error(`Invalid array Val type: ${valsToUse}, ${tpe}`)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function fromApiVals(vals: node.Val[], names: string[], types: string[]): NamedVals {
|
|
170
|
+
let valIndex = 0
|
|
171
|
+
const result: NamedVals = {}
|
|
172
|
+
types.forEach((currentType, index) => {
|
|
173
|
+
const currentName = names[`${index}`]
|
|
174
|
+
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
175
|
+
valIndex = nextIndex
|
|
176
|
+
result[`${currentName}`] = val
|
|
177
|
+
})
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function fromApiArray(vals: node.Val[], types: string[]): Val[] {
|
|
182
|
+
let valIndex = 0
|
|
183
|
+
const result: Val[] = []
|
|
184
|
+
for (const currentType of types) {
|
|
185
|
+
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
186
|
+
result.push(val)
|
|
187
|
+
valIndex = nextIndex
|
|
188
|
+
}
|
|
189
|
+
return result
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function fromApiVal(v: node.Val, tpe: string): Val {
|
|
193
|
+
if (v.type === 'Bool' && v.type === tpe) {
|
|
194
|
+
return v.value as boolean
|
|
195
|
+
} else if ((v.type === 'U256' || v.type === 'I256') && v.type === tpe) {
|
|
196
|
+
return fromApiNumber256(v.value as string)
|
|
197
|
+
} else if ((v.type === 'ByteVec' || v.type === 'Address') && v.type === tpe) {
|
|
198
|
+
return v.value as string
|
|
199
|
+
} else {
|
|
200
|
+
throw new Error(`Invalid node.Val type: ${v}`)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function decodeArrayType(tpe: string): [baseType: string, dims: number[]] {
|
|
205
|
+
const semiColonIndex = tpe.lastIndexOf(';')
|
|
206
|
+
if (semiColonIndex === -1) {
|
|
207
|
+
throw new Error(`Invalid Val type: ${tpe}`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const subType = tpe.slice(1, semiColonIndex)
|
|
211
|
+
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
212
|
+
if (subType[0] == '[') {
|
|
213
|
+
const [baseType, subDim] = decodeArrayType(subType)
|
|
214
|
+
return [baseType, (subDim.unshift(dim), subDim)]
|
|
215
|
+
} else {
|
|
216
|
+
return [subType, [dim]]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function foldVals(vals: Val[], dims: number[]): Val {
|
|
221
|
+
if (dims.length == 1) {
|
|
222
|
+
return vals
|
|
223
|
+
} else {
|
|
224
|
+
const result: Val[] = []
|
|
225
|
+
const chunkSize = vals.length / dims[0]
|
|
226
|
+
const chunkDims = dims.slice(1)
|
|
227
|
+
for (let i = 0; i < vals.length; i += chunkSize) {
|
|
228
|
+
const chunk = vals.slice(i, i + chunkSize)
|
|
229
|
+
result.push(foldVals(chunk, chunkDims))
|
|
230
|
+
}
|
|
231
|
+
return result
|
|
232
|
+
}
|
|
233
|
+
}
|
package/src/contract/contract.ts
CHANGED
|
@@ -20,16 +20,32 @@ import { Buffer } from 'buffer/'
|
|
|
20
20
|
import { webcrypto as crypto } from 'crypto'
|
|
21
21
|
import fs from 'fs'
|
|
22
22
|
import { promises as fsPromises } from 'fs'
|
|
23
|
-
import {
|
|
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'
|
|
24
39
|
import { SignDeployContractTxParams, SignExecuteScriptTxParams, SignerWithNodeProvider } from '../signer'
|
|
25
40
|
import * as ralph from './ralph'
|
|
26
41
|
import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
|
|
27
|
-
import { CompileContractResult, CompileScriptResult } from '../api/api-alephium'
|
|
28
42
|
import { getCurrentNodeProvider } from '../global'
|
|
29
43
|
|
|
30
|
-
type FieldsSig = node.FieldsSig
|
|
31
|
-
type EventSig = node.EventSig
|
|
32
|
-
type FunctionSig = node.FunctionSig
|
|
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
|
|
33
49
|
|
|
34
50
|
enum SourceType {
|
|
35
51
|
Contract = 0,
|
|
@@ -275,20 +291,18 @@ export class Project {
|
|
|
275
291
|
}
|
|
276
292
|
}
|
|
277
293
|
|
|
278
|
-
static contract(
|
|
279
|
-
const
|
|
280
|
-
const contract = Project.currentProject.contracts.find((c) => c.sourceFile.contractPath === contractPath)
|
|
294
|
+
static contract(name: string): Contract {
|
|
295
|
+
const contract = Project.currentProject.contracts.find((c) => c.artifact.name === name)
|
|
281
296
|
if (typeof contract === 'undefined') {
|
|
282
|
-
throw new Error(`Contract ${
|
|
297
|
+
throw new Error(`Contract "${name}" does not exist`)
|
|
283
298
|
}
|
|
284
299
|
return contract.artifact
|
|
285
300
|
}
|
|
286
301
|
|
|
287
|
-
static script(
|
|
288
|
-
const
|
|
289
|
-
const script = Project.currentProject.scripts.find((c) => c.sourceFile.contractPath === contractPath)
|
|
302
|
+
static script(name: string): Script {
|
|
303
|
+
const script = Project.currentProject.scripts.find((c) => c.artifact.name === name)
|
|
290
304
|
if (typeof script === 'undefined') {
|
|
291
|
-
throw new Error(`Script ${
|
|
305
|
+
throw new Error(`Script "${name}" does not exist`)
|
|
292
306
|
}
|
|
293
307
|
return script.artifact
|
|
294
308
|
}
|
|
@@ -484,9 +498,11 @@ export class Project {
|
|
|
484
498
|
}
|
|
485
499
|
|
|
486
500
|
export abstract class Artifact {
|
|
501
|
+
readonly name: string
|
|
487
502
|
readonly functions: FunctionSig[]
|
|
488
503
|
|
|
489
|
-
constructor(functions: FunctionSig[]) {
|
|
504
|
+
constructor(name: string, functions: FunctionSig[]) {
|
|
505
|
+
this.name = name
|
|
490
506
|
this.functions = functions
|
|
491
507
|
}
|
|
492
508
|
|
|
@@ -512,13 +528,14 @@ export class Contract extends Artifact {
|
|
|
512
528
|
readonly eventsSig: EventSig[]
|
|
513
529
|
|
|
514
530
|
constructor(
|
|
531
|
+
name: string,
|
|
515
532
|
bytecode: string,
|
|
516
533
|
codeHash: string,
|
|
517
534
|
fieldsSig: FieldsSig,
|
|
518
535
|
eventsSig: EventSig[],
|
|
519
536
|
functions: FunctionSig[]
|
|
520
537
|
) {
|
|
521
|
-
super(functions)
|
|
538
|
+
super(name, functions)
|
|
522
539
|
this.bytecode = bytecode
|
|
523
540
|
this.codeHash = codeHash
|
|
524
541
|
this.fieldsSig = fieldsSig
|
|
@@ -528,6 +545,7 @@ export class Contract extends Artifact {
|
|
|
528
545
|
// TODO: safely parse json
|
|
529
546
|
static fromJson(artifact: any): Contract {
|
|
530
547
|
if (
|
|
548
|
+
artifact.name == null ||
|
|
531
549
|
artifact.bytecode == null ||
|
|
532
550
|
artifact.codeHash == null ||
|
|
533
551
|
artifact.fieldsSig == null ||
|
|
@@ -537,6 +555,7 @@ export class Contract extends Artifact {
|
|
|
537
555
|
throw Error('The artifact JSON for contract is incomplete')
|
|
538
556
|
}
|
|
539
557
|
const contract = new Contract(
|
|
558
|
+
artifact.name,
|
|
540
559
|
artifact.bytecode,
|
|
541
560
|
artifact.codeHash,
|
|
542
561
|
artifact.fieldsSig,
|
|
@@ -546,8 +565,8 @@ export class Contract extends Artifact {
|
|
|
546
565
|
return contract
|
|
547
566
|
}
|
|
548
567
|
|
|
549
|
-
static fromCompileResult(result: CompileContractResult): Contract {
|
|
550
|
-
return new Contract(result.bytecode, result.codeHash, result.fields, result.events, result.functions)
|
|
568
|
+
static fromCompileResult(result: node.CompileContractResult): Contract {
|
|
569
|
+
return new Contract(result.name, result.bytecode, result.codeHash, result.fields, result.events, result.functions)
|
|
551
570
|
}
|
|
552
571
|
|
|
553
572
|
// support both 'code.ral' and 'code.ral.json'
|
|
@@ -566,6 +585,7 @@ export class Contract extends Artifact {
|
|
|
566
585
|
|
|
567
586
|
override toString(): string {
|
|
568
587
|
const object = {
|
|
588
|
+
name: this.name,
|
|
569
589
|
bytecode: this.bytecode,
|
|
570
590
|
codeHash: this.codeHash,
|
|
571
591
|
fieldsSig: this.fieldsSig,
|
|
@@ -748,7 +768,7 @@ export class Contract extends Artifact {
|
|
|
748
768
|
bytecode: bytecode,
|
|
749
769
|
initialAttoAlphAmount: extractOptionalNumber256(params.initialAttoAlphAmount),
|
|
750
770
|
issueTokenAmount: extractOptionalNumber256(params.issueTokenAmount),
|
|
751
|
-
initialTokenAmounts: params.initialTokenAmounts
|
|
771
|
+
initialTokenAmounts: toApiTokens(params.initialTokenAmounts),
|
|
752
772
|
gasAmount: params.gasAmount,
|
|
753
773
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
754
774
|
}
|
|
@@ -776,22 +796,27 @@ export class Script extends Artifact {
|
|
|
776
796
|
readonly bytecodeTemplate: string
|
|
777
797
|
readonly fieldsSig: FieldsSig
|
|
778
798
|
|
|
779
|
-
constructor(bytecodeTemplate: string, fieldsSig: FieldsSig, functions: FunctionSig[]) {
|
|
780
|
-
super(functions)
|
|
799
|
+
constructor(name: string, bytecodeTemplate: string, fieldsSig: FieldsSig, functions: FunctionSig[]) {
|
|
800
|
+
super(name, functions)
|
|
781
801
|
this.bytecodeTemplate = bytecodeTemplate
|
|
782
802
|
this.fieldsSig = fieldsSig
|
|
783
803
|
}
|
|
784
804
|
|
|
785
|
-
static fromCompileResult(result: CompileScriptResult): Script {
|
|
786
|
-
return new Script(result.bytecodeTemplate, result.fields, result.functions)
|
|
805
|
+
static fromCompileResult(result: node.CompileScriptResult): Script {
|
|
806
|
+
return new Script(result.name, result.bytecodeTemplate, result.fields, result.functions)
|
|
787
807
|
}
|
|
788
808
|
|
|
789
809
|
// TODO: safely parse json
|
|
790
810
|
static fromJson(artifact: any): Script {
|
|
791
|
-
if (
|
|
811
|
+
if (
|
|
812
|
+
artifact.name == null ||
|
|
813
|
+
artifact.bytecodeTemplate == null ||
|
|
814
|
+
artifact.fieldsSig == null ||
|
|
815
|
+
artifact.functions == null
|
|
816
|
+
) {
|
|
792
817
|
throw Error('The artifact JSON for script is incomplete')
|
|
793
818
|
}
|
|
794
|
-
return new Script(artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
|
|
819
|
+
return new Script(artifact.name, artifact.bytecodeTemplate, artifact.fieldsSig, artifact.functions)
|
|
795
820
|
}
|
|
796
821
|
|
|
797
822
|
static async fromArtifactFile(path: string): Promise<Script> {
|
|
@@ -802,6 +827,7 @@ export class Script extends Artifact {
|
|
|
802
827
|
|
|
803
828
|
override toString(): string {
|
|
804
829
|
const object = {
|
|
830
|
+
name: this.name,
|
|
805
831
|
bytecodeTemplate: this.bytecodeTemplate,
|
|
806
832
|
fieldsSig: this.fieldsSig,
|
|
807
833
|
functions: this.functions
|
|
@@ -814,7 +840,7 @@ export class Script extends Artifact {
|
|
|
814
840
|
signerAddress: params.signerAddress,
|
|
815
841
|
bytecode: this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {}),
|
|
816
842
|
attoAlphAmount: extractOptionalNumber256(params.attoAlphAmount),
|
|
817
|
-
tokens:
|
|
843
|
+
tokens: toApiTokens(params.tokens),
|
|
818
844
|
gasAmount: params.gasAmount,
|
|
819
845
|
gasPrice: extractOptionalNumber256(params.gasPrice)
|
|
820
846
|
}
|
|
@@ -837,162 +863,8 @@ export class Script extends Artifact {
|
|
|
837
863
|
}
|
|
838
864
|
}
|
|
839
865
|
|
|
840
|
-
export type Number256 = number | bigint | string
|
|
841
|
-
export type Val = Number256 | boolean | string | Val[]
|
|
842
|
-
export type NamedVals = Record<string, Val>
|
|
843
|
-
export type Fields = NamedVals
|
|
844
|
-
export type Arguments = NamedVals
|
|
845
|
-
|
|
846
|
-
function extractBoolean(v: Val): boolean {
|
|
847
|
-
if (typeof v === 'boolean') {
|
|
848
|
-
return v
|
|
849
|
-
} else {
|
|
850
|
-
throw new Error(`Invalid boolean value: ${v}`)
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// TODO: check integer bounds
|
|
855
|
-
function extractNumber256(v: Val): string {
|
|
856
|
-
if ((typeof v === 'number' && Number.isInteger(v)) || typeof v === 'bigint') {
|
|
857
|
-
return v.toString()
|
|
858
|
-
} else if (typeof v === 'string') {
|
|
859
|
-
return v
|
|
860
|
-
} else {
|
|
861
|
-
throw new Error(`Invalid 256 bit number: ${v}`)
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
866
|
function extractOptionalNumber256(v?: Val): string | undefined {
|
|
866
|
-
return typeof v !== 'undefined' ?
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// TODO: check hex string
|
|
870
|
-
function extractByteVec(v: Val): string {
|
|
871
|
-
if (typeof v === 'string') {
|
|
872
|
-
// try to convert from address to contract id
|
|
873
|
-
try {
|
|
874
|
-
const address = bs58.decode(v)
|
|
875
|
-
if (address.length == 33 && address[0] == 3) {
|
|
876
|
-
return Buffer.from(address.slice(1)).toString('hex')
|
|
877
|
-
}
|
|
878
|
-
} catch (_) {
|
|
879
|
-
return v as string
|
|
880
|
-
}
|
|
881
|
-
return v as string
|
|
882
|
-
} else {
|
|
883
|
-
throw new Error(`Invalid string: ${v}`)
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
function extractBs58(v: Val): string {
|
|
888
|
-
if (typeof v === 'string') {
|
|
889
|
-
try {
|
|
890
|
-
bs58.decode(v)
|
|
891
|
-
return v as string
|
|
892
|
-
} catch (error) {
|
|
893
|
-
throw new Error(`Invalid base58 string: ${v}`)
|
|
894
|
-
}
|
|
895
|
-
} else {
|
|
896
|
-
throw new Error(`Invalid string: ${v}`)
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
function decodeNumber256(n: string): Number256 {
|
|
901
|
-
if (Number.isSafeInteger(Number.parseInt(n))) {
|
|
902
|
-
return Number(n)
|
|
903
|
-
} else {
|
|
904
|
-
return BigInt(n)
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
export function extractArray(tpe: string, v: Val): node.Val {
|
|
909
|
-
if (!Array.isArray(v)) {
|
|
910
|
-
throw new Error(`Expected array, got ${v}`)
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
914
|
-
if (semiColonIndex == -1) {
|
|
915
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
919
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
920
|
-
if ((v as Val[]).length != dim) {
|
|
921
|
-
throw new Error(`Invalid val dimension: ${v}`)
|
|
922
|
-
} else {
|
|
923
|
-
return { value: (v as Val[]).map((v) => toApiVal(v, subType)), type: 'Array' }
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
export function toApiVal(v: Val, tpe: string): node.Val {
|
|
928
|
-
if (tpe === 'Bool') {
|
|
929
|
-
return { value: extractBoolean(v), type: tpe }
|
|
930
|
-
} else if (tpe === 'U256' || tpe === 'I256') {
|
|
931
|
-
return { value: extractNumber256(v), type: tpe }
|
|
932
|
-
} else if (tpe === 'ByteVec') {
|
|
933
|
-
return { value: extractByteVec(v), type: tpe }
|
|
934
|
-
} else if (tpe === 'Address') {
|
|
935
|
-
return { value: extractBs58(v), type: tpe }
|
|
936
|
-
} else {
|
|
937
|
-
return extractArray(tpe, v)
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
function decodeArrayType(tpe: string): [baseType: string, dims: number[]] {
|
|
942
|
-
const semiColonIndex = tpe.lastIndexOf(';')
|
|
943
|
-
if (semiColonIndex === -1) {
|
|
944
|
-
throw new Error(`Invalid Val type: ${tpe}`)
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
const subType = tpe.slice(1, semiColonIndex)
|
|
948
|
-
const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
|
|
949
|
-
if (subType[0] == '[') {
|
|
950
|
-
const [baseType, subDim] = decodeArrayType(subType)
|
|
951
|
-
return [baseType, (subDim.unshift(dim), subDim)]
|
|
952
|
-
} else {
|
|
953
|
-
return [subType, [dim]]
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
function foldVals(vals: Val[], dims: number[]): Val {
|
|
958
|
-
if (dims.length == 1) {
|
|
959
|
-
return vals
|
|
960
|
-
} else {
|
|
961
|
-
const result: Val[] = []
|
|
962
|
-
const chunkSize = vals.length / dims[0]
|
|
963
|
-
const chunkDims = dims.slice(1)
|
|
964
|
-
for (let i = 0; i < vals.length; i += chunkSize) {
|
|
965
|
-
const chunk = vals.slice(i, i + chunkSize)
|
|
966
|
-
result.push(foldVals(chunk, chunkDims))
|
|
967
|
-
}
|
|
968
|
-
return result
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
function _fromApiVal(vals: node.Val[], valIndex: number, tpe: string): [result: Val, nextIndex: number] {
|
|
973
|
-
if (vals.length === 0) {
|
|
974
|
-
throw new Error('Not enough Vals')
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
const firstVal = vals[`${valIndex}`]
|
|
978
|
-
if (tpe === 'Bool' && firstVal.type === tpe) {
|
|
979
|
-
return [firstVal.value as boolean, valIndex + 1]
|
|
980
|
-
} else if ((tpe === 'U256' || tpe === 'I256') && firstVal.type === tpe) {
|
|
981
|
-
return [decodeNumber256(firstVal.value as string), valIndex + 1]
|
|
982
|
-
} else if ((tpe === 'ByteVec' || tpe === 'Address') && firstVal.type === tpe) {
|
|
983
|
-
return [firstVal.value as string, valIndex + 1]
|
|
984
|
-
} else {
|
|
985
|
-
const [baseType, dims] = decodeArrayType(tpe)
|
|
986
|
-
const arraySize = dims.reduce((a, b) => a * b)
|
|
987
|
-
const nextIndex = valIndex + arraySize
|
|
988
|
-
const valsToUse = vals.slice(valIndex, nextIndex)
|
|
989
|
-
if (valsToUse.length == arraySize && valsToUse.every((val) => val.type === baseType)) {
|
|
990
|
-
const localVals = valsToUse.map((val) => fromApiVal(val, baseType))
|
|
991
|
-
return [foldVals(localVals, dims), nextIndex]
|
|
992
|
-
} else {
|
|
993
|
-
throw new Error(`Invalid array Val type: ${valsToUse}, ${tpe}`)
|
|
994
|
-
}
|
|
995
|
-
}
|
|
867
|
+
return typeof v !== 'undefined' ? toApiNumber256(v) : undefined
|
|
996
868
|
}
|
|
997
869
|
|
|
998
870
|
function fromApiFields(vals: node.Val[], fieldsSig: node.FieldsSig): Fields {
|
|
@@ -1003,70 +875,22 @@ function fromApiEventFields(vals: node.Val[], eventSig: node.EventSig): Fields {
|
|
|
1003
875
|
return fromApiVals(vals, eventSig.fieldNames, eventSig.fieldTypes)
|
|
1004
876
|
}
|
|
1005
877
|
|
|
1006
|
-
function fromApiVals(vals: node.Val[], names: string[], types: string[]): Fields {
|
|
1007
|
-
let valIndex = 0
|
|
1008
|
-
const result: Fields = {}
|
|
1009
|
-
types.forEach((currentType, index) => {
|
|
1010
|
-
const currentName = names[`${index}`]
|
|
1011
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
1012
|
-
valIndex = nextIndex
|
|
1013
|
-
result[`${currentName}`] = val
|
|
1014
|
-
})
|
|
1015
|
-
return result
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
function fromApiArray(vals: node.Val[], types: string[]): Val[] {
|
|
1019
|
-
let valIndex = 0
|
|
1020
|
-
const result: Val[] = []
|
|
1021
|
-
for (const currentType of types) {
|
|
1022
|
-
const [val, nextIndex] = _fromApiVal(vals, valIndex, currentType)
|
|
1023
|
-
result.push(val)
|
|
1024
|
-
valIndex = nextIndex
|
|
1025
|
-
}
|
|
1026
|
-
return result
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
function fromApiVal(v: node.Val, tpe: string): Val {
|
|
1030
|
-
if (v.type === 'Bool' && v.type === tpe) {
|
|
1031
|
-
return v.value as boolean
|
|
1032
|
-
} else if ((v.type === 'U256' || v.type === 'I256') && v.type === tpe) {
|
|
1033
|
-
return decodeNumber256(v.value as string)
|
|
1034
|
-
} else if ((v.type === 'ByteVec' || v.type === 'Address') && v.type === tpe) {
|
|
1035
|
-
return v.value as string
|
|
1036
|
-
} else {
|
|
1037
|
-
throw new Error(`Invalid node.Val type: ${v}`)
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
878
|
export interface Asset {
|
|
1042
879
|
alphAmount: Number256
|
|
1043
880
|
tokens?: Token[]
|
|
1044
881
|
}
|
|
1045
882
|
|
|
1046
|
-
export interface Token {
|
|
1047
|
-
id: string
|
|
1048
|
-
amount: Number256
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
function toApiToken(token: Token): node.Token {
|
|
1052
|
-
return { id: token.id, amount: extractNumber256(token.amount) }
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
function fromApiToken(token: node.Token): Token {
|
|
1056
|
-
return { id: token.id, amount: decodeNumber256(token.amount) }
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
883
|
function toApiAsset(asset: Asset): node.AssetState {
|
|
1060
884
|
return {
|
|
1061
|
-
attoAlphAmount:
|
|
885
|
+
attoAlphAmount: toApiNumber256(asset.alphAmount),
|
|
1062
886
|
tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(toApiToken) : []
|
|
1063
887
|
}
|
|
1064
888
|
}
|
|
1065
889
|
|
|
1066
890
|
function fromApiAsset(asset: node.AssetState): Asset {
|
|
1067
891
|
return {
|
|
1068
|
-
alphAmount:
|
|
1069
|
-
tokens:
|
|
892
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
893
|
+
tokens: fromApiTokens(asset.tokens)
|
|
1070
894
|
}
|
|
1071
895
|
}
|
|
1072
896
|
|
|
@@ -1174,7 +998,7 @@ export interface ContractOutput {
|
|
|
1174
998
|
type: string
|
|
1175
999
|
address: string
|
|
1176
1000
|
alphAmount: Number256
|
|
1177
|
-
tokens
|
|
1001
|
+
tokens?: Token[]
|
|
1178
1002
|
}
|
|
1179
1003
|
|
|
1180
1004
|
function fromApiOutput(output: node.Output): Output {
|
|
@@ -1183,8 +1007,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
1183
1007
|
return {
|
|
1184
1008
|
type: 'AssetOutput',
|
|
1185
1009
|
address: asset.address,
|
|
1186
|
-
alphAmount:
|
|
1187
|
-
tokens: asset.tokens
|
|
1010
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1011
|
+
tokens: fromApiTokens(asset.tokens),
|
|
1188
1012
|
lockTime: asset.lockTime,
|
|
1189
1013
|
message: asset.message
|
|
1190
1014
|
}
|
|
@@ -1193,8 +1017,8 @@ function fromApiOutput(output: node.Output): Output {
|
|
|
1193
1017
|
return {
|
|
1194
1018
|
type: 'ContractOutput',
|
|
1195
1019
|
address: asset.address,
|
|
1196
|
-
alphAmount:
|
|
1197
|
-
tokens: asset.tokens
|
|
1020
|
+
alphAmount: fromApiNumber256(asset.attoAlphAmount),
|
|
1021
|
+
tokens: fromApiTokens(asset.tokens)
|
|
1198
1022
|
}
|
|
1199
1023
|
} else {
|
|
1200
1024
|
throw new Error(`Unknown output type: ${output}`)
|