@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.
@@ -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
+ }
@@ -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 { node, NodeProvider } from '../api'
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(path: string): Contract {
279
- const contractPath = Project.currentProject.getContractPath(path)
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 ${contractPath} does not exist`)
297
+ throw new Error(`Contract "${name}" does not exist`)
283
298
  }
284
299
  return contract.artifact
285
300
  }
286
301
 
287
- static script(path: string): Script {
288
- const contractPath = Project.currentProject.getContractPath(path)
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 ${contractPath} does not exist`)
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?.map(toApiToken),
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 (artifact.bytecodeTemplate == null || artifact.fieldsSig == null || artifact.functions == null) {
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: typeof params.tokens !== 'undefined' ? params.tokens.map(toApiToken) : undefined,
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' ? extractNumber256(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: extractNumber256(asset.alphAmount),
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: decodeNumber256(asset.attoAlphAmount),
1069
- tokens: typeof asset.tokens !== 'undefined' ? asset.tokens.map(fromApiToken) : undefined
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: Token[]
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: decodeNumber256(asset.attoAlphAmount),
1187
- tokens: asset.tokens.map(fromApiToken),
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: decodeNumber256(asset.attoAlphAmount),
1197
- tokens: asset.tokens.map(fromApiToken)
1020
+ alphAmount: fromApiNumber256(asset.attoAlphAmount),
1021
+ tokens: fromApiTokens(asset.tokens)
1198
1022
  }
1199
1023
  } else {
1200
1024
  throw new Error(`Unknown output type: ${output}`)