@alephium/web3 0.36.1 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.map +1 -1
  3. package/dist/src/api/api-alephium.d.ts +6 -1
  4. package/dist/src/api/api-alephium.js +1 -1
  5. package/dist/src/api/types.d.ts +1 -1
  6. package/dist/src/codec/array-codec.d.ts +17 -0
  7. package/dist/src/codec/array-codec.js +59 -0
  8. package/dist/src/codec/asset-output-codec.d.ts +27 -0
  9. package/dist/src/codec/asset-output-codec.js +135 -0
  10. package/dist/src/codec/bigint-codec.d.ts +5 -0
  11. package/dist/src/codec/bigint-codec.js +86 -0
  12. package/dist/src/codec/bytestring-codec.d.ts +16 -0
  13. package/dist/src/codec/bytestring-codec.js +50 -0
  14. package/dist/src/codec/codec.d.ts +8 -0
  15. package/dist/src/codec/codec.js +9 -0
  16. package/dist/src/codec/compact-int-codec.d.ts +51 -0
  17. package/dist/src/codec/compact-int-codec.js +300 -0
  18. package/dist/src/codec/contract-codec.d.ts +23 -0
  19. package/dist/src/codec/contract-codec.js +81 -0
  20. package/dist/src/codec/contract-output-codec.d.ts +21 -0
  21. package/dist/src/codec/contract-output-codec.js +82 -0
  22. package/dist/src/codec/contract-output-ref-codec.d.ts +15 -0
  23. package/dist/src/codec/contract-output-ref-codec.js +38 -0
  24. package/dist/src/codec/either-codec.d.ts +17 -0
  25. package/dist/src/codec/either-codec.js +67 -0
  26. package/dist/src/codec/hash.d.ts +4 -0
  27. package/dist/src/codec/hash.js +23 -0
  28. package/dist/src/codec/index.d.ts +23 -0
  29. package/dist/src/codec/index.js +69 -0
  30. package/dist/src/codec/input-codec.d.ts +22 -0
  31. package/dist/src/codec/input-codec.js +71 -0
  32. package/dist/src/codec/instr-codec.d.ts +230 -0
  33. package/dist/src/codec/instr-codec.js +471 -0
  34. package/dist/src/codec/lockup-script-codec.d.ts +28 -0
  35. package/dist/src/codec/lockup-script-codec.js +80 -0
  36. package/dist/src/codec/long-codec.d.ts +9 -0
  37. package/dist/src/codec/long-codec.js +56 -0
  38. package/dist/src/codec/method-codec.d.ts +31 -0
  39. package/dist/src/codec/method-codec.js +78 -0
  40. package/dist/src/codec/option-codec.d.ts +15 -0
  41. package/dist/src/codec/option-codec.js +55 -0
  42. package/dist/src/codec/output-codec.d.ts +7 -0
  43. package/dist/src/codec/output-codec.js +26 -0
  44. package/dist/src/codec/script-codec.d.ts +16 -0
  45. package/dist/src/codec/script-codec.js +41 -0
  46. package/dist/src/codec/signature-codec.d.ts +14 -0
  47. package/dist/src/codec/signature-codec.js +19 -0
  48. package/dist/src/codec/signed-int-codec.d.ts +9 -0
  49. package/dist/src/codec/signed-int-codec.js +39 -0
  50. package/dist/src/codec/token-codec.d.ts +16 -0
  51. package/dist/src/codec/token-codec.js +46 -0
  52. package/dist/src/codec/transaction-codec.d.ts +27 -0
  53. package/dist/src/codec/transaction-codec.js +128 -0
  54. package/dist/src/codec/unlock-script-codec.d.ts +40 -0
  55. package/dist/src/codec/unlock-script-codec.js +170 -0
  56. package/dist/src/codec/unsigned-tx-codec.d.ts +30 -0
  57. package/dist/src/codec/unsigned-tx-codec.js +103 -0
  58. package/dist/src/contract/contract.d.ts +14 -8
  59. package/dist/src/contract/contract.js +205 -22
  60. package/dist/src/contract/ralph.d.ts +16 -0
  61. package/dist/src/contract/ralph.js +127 -1
  62. package/dist/src/index.d.ts +1 -0
  63. package/dist/src/index.js +2 -1
  64. package/package.json +5 -4
  65. package/src/api/api-alephium.ts +7 -1
  66. package/src/api/types.ts +1 -1
  67. package/src/codec/array-codec.ts +63 -0
  68. package/src/codec/asset-output-codec.ts +149 -0
  69. package/src/codec/bigint-codec.ts +92 -0
  70. package/src/codec/bytestring-codec.ts +56 -0
  71. package/src/codec/codec.ts +31 -0
  72. package/src/codec/compact-int-codec.ts +316 -0
  73. package/src/codec/contract-codec.ts +95 -0
  74. package/src/codec/contract-output-codec.ts +95 -0
  75. package/src/codec/contract-output-ref-codec.ts +42 -0
  76. package/src/codec/either-codec.ts +74 -0
  77. package/src/codec/hash.ts +35 -0
  78. package/src/codec/index.ts +41 -0
  79. package/src/codec/input-codec.ts +81 -0
  80. package/src/codec/instr-codec.ts +479 -0
  81. package/src/codec/lockup-script-codec.ts +99 -0
  82. package/src/codec/long-codec.ts +59 -0
  83. package/src/codec/method-codec.ts +97 -0
  84. package/src/codec/option-codec.ts +60 -0
  85. package/src/codec/output-codec.ts +26 -0
  86. package/src/codec/script-codec.ts +45 -0
  87. package/src/codec/signature-codec.ts +40 -0
  88. package/src/codec/signed-int-codec.ts +37 -0
  89. package/src/codec/token-codec.ts +51 -0
  90. package/src/codec/transaction-codec.ts +147 -0
  91. package/src/codec/unlock-script-codec.ts +194 -0
  92. package/src/codec/unsigned-tx-codec.ts +124 -0
  93. package/src/contract/contract.ts +299 -23
  94. package/src/contract/ralph.ts +140 -2
  95. package/src/index.ts +1 -1
@@ -57,19 +57,34 @@ import {
57
57
  WebCrypto,
58
58
  hexToBinUnsafe,
59
59
  isDevnet,
60
- addressFromContractId
60
+ addressFromContractId,
61
+ subContractId
61
62
  } from '../utils'
62
63
  import { getCurrentNodeProvider } from '../global'
63
64
  import * as path from 'path'
64
65
  import { EventSubscribeOptions, EventSubscription, subscribeToEvents } from './events'
65
- import { ONE_ALPH } from '../constants'
66
+ import { ONE_ALPH, TOTAL_NUMBER_OF_GROUPS } from '../constants'
66
67
  import * as blake from 'blakejs'
67
68
  import { parseError } from '../utils/error'
68
69
  import { isContractDebugMessageEnabled } from '../debug'
70
+ import {
71
+ contract,
72
+ Method,
73
+ LoadLocal,
74
+ LoadImmFieldByIndex,
75
+ LoadMutFieldByIndex,
76
+ CallerContractId,
77
+ LoadImmField,
78
+ ByteVecEq,
79
+ Assert,
80
+ StoreMutFieldByIndex,
81
+ DestroySelf
82
+ } from '../codec'
69
83
 
70
84
  const crypto = new WebCrypto()
71
85
 
72
86
  export type FieldsSig = node.FieldsSig
87
+ export type MapsSig = node.MapsSig
73
88
  export type EventSig = node.EventSig
74
89
  export type FunctionSig = node.FunctionSig
75
90
  export type Fields = NamedVals
@@ -890,6 +905,7 @@ export class Contract extends Artifact {
890
905
  readonly constants: Constant[]
891
906
  readonly enums: Enum[]
892
907
  readonly structs: Struct[]
908
+ readonly mapsSig?: MapsSig
893
909
  readonly stdInterfaceId?: HexString
894
910
 
895
911
  readonly bytecodeDebug: string
@@ -908,6 +924,7 @@ export class Contract extends Artifact {
908
924
  constants: Constant[],
909
925
  enums: Enum[],
910
926
  structs: Struct[],
927
+ mapsSig?: MapsSig,
911
928
  stdInterfaceId?: HexString
912
929
  ) {
913
930
  super(version, name, functions)
@@ -919,6 +936,7 @@ export class Contract extends Artifact {
919
936
  this.constants = constants
920
937
  this.enums = enums
921
938
  this.structs = structs
939
+ this.mapsSig = mapsSig
922
940
  this.stdInterfaceId = stdInterfaceId
923
941
 
924
942
  this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
@@ -953,6 +971,7 @@ export class Contract extends Artifact {
953
971
  artifact.constants,
954
972
  artifact.enums,
955
973
  structs,
974
+ artifact.mapsSig === null ? undefined : artifact.mapsSig,
956
975
  artifact.stdInterfaceId === null ? undefined : artifact.stdInterfaceId
957
976
  )
958
977
  return contract
@@ -972,6 +991,7 @@ export class Contract extends Artifact {
972
991
  result.constants,
973
992
  result.enums,
974
993
  structs,
994
+ result.maps,
975
995
  result.stdInterfaceId
976
996
  )
977
997
  }
@@ -1000,6 +1020,9 @@ export class Contract extends Artifact {
1000
1020
  constants: this.constants,
1001
1021
  enums: this.enums
1002
1022
  }
1023
+ if (this.mapsSig !== undefined) {
1024
+ object.mapsSig = this.mapsSig
1025
+ }
1003
1026
  if (this.stdInterfaceId !== undefined) {
1004
1027
  object.stdInterfaceId = this.stdInterfaceId
1005
1028
  }
@@ -1542,7 +1565,16 @@ function toApiInputAssets(inputAssets?: InputAsset[]): node.TestInputAsset[] | u
1542
1565
  return typeof inputAssets !== 'undefined' ? inputAssets.map(toApiInputAsset) : undefined
1543
1566
  }
1544
1567
 
1545
- export interface TestContractParams<F extends Fields = Fields, A extends Arguments = Arguments> {
1568
+ export type TestContractParamsWithoutMaps<F extends Fields = Fields, A extends Arguments = Arguments> = Omit<
1569
+ TestContractParams<F, A>,
1570
+ 'initialMaps'
1571
+ >
1572
+
1573
+ export interface TestContractParams<
1574
+ F extends Fields = Fields,
1575
+ A extends Arguments = Arguments,
1576
+ M extends Record<string, Map<Val, Val>> = Record<string, Map<Val, Val>>
1577
+ > {
1546
1578
  group?: number // default 0
1547
1579
  address?: string
1548
1580
  callerAddress?: string
@@ -1550,6 +1582,7 @@ export interface TestContractParams<F extends Fields = Fields, A extends Argumen
1550
1582
  blockTimeStamp?: number
1551
1583
  txId?: string
1552
1584
  initialFields: F
1585
+ initialMaps?: M
1553
1586
  initialAsset?: Asset // default 1 ALPH
1554
1587
  testArgs: A
1555
1588
  existingContracts?: ContractState[] // default no existing contracts
@@ -1567,11 +1600,14 @@ export interface ContractEvent<T extends Fields = Fields> {
1567
1600
 
1568
1601
  export type DebugMessage = node.DebugMessage
1569
1602
 
1570
- export interface TestContractResult<R> {
1603
+ export type TestContractResultWithoutMaps<R> = Omit<TestContractResult<R>, 'maps'>
1604
+
1605
+ export interface TestContractResult<R, M extends Record<string, Map<Val, Val>> = Record<string, Map<Val, Val>>> {
1571
1606
  contractId: string
1572
1607
  contractAddress: string
1573
1608
  returns: R
1574
1609
  gasUsed: number
1610
+ maps?: M
1575
1611
  contracts: ContractState[]
1576
1612
  txOutputs: Output[]
1577
1613
  events: ContractEvent[]
@@ -1722,14 +1758,19 @@ export interface CallContractResult<R> {
1722
1758
  debugMessages: DebugMessage[]
1723
1759
  }
1724
1760
 
1725
- function specialContractAddress(n: number): string {
1761
+ function specialContractAddress(eventIndex: number, groupIndex: number): string {
1726
1762
  const bytes = new Uint8Array(32).fill(0)
1727
- bytes[31] = n
1763
+ bytes[30] = eventIndex
1764
+ bytes[31] = groupIndex
1728
1765
  return addressFromContractId(binToHex(bytes))
1729
1766
  }
1730
1767
 
1731
- export const CreateContractEventAddress = specialContractAddress(-1)
1732
- export const DestroyContractEventAddress = specialContractAddress(-2)
1768
+ export const CreateContractEventAddresses = Array.from(Array(TOTAL_NUMBER_OF_GROUPS).keys()).map((groupIndex) =>
1769
+ specialContractAddress(Contract.ContractCreatedEventIndex, groupIndex)
1770
+ )
1771
+ export const DestroyContractEventAddresses = Array.from(Array(TOTAL_NUMBER_OF_GROUPS).keys()).map((groupIndex) =>
1772
+ specialContractAddress(Contract.ContractDestroyedEventIndex, groupIndex)
1773
+ )
1733
1774
 
1734
1775
  export type ContractCreatedEventFields = {
1735
1776
  address: Address
@@ -1819,23 +1860,244 @@ export function addStdIdToFields<F extends Fields>(
1819
1860
  : { ...fields, __stdInterfaceId: stdInterfaceIdPrefix + contract.stdInterfaceId }
1820
1861
  }
1821
1862
 
1822
- export async function testMethod<I extends ContractInstance, F extends Fields, A extends Arguments, R>(
1823
- contract: ContractFactory<I, F>,
1863
+ function calcWrapperContractId(
1864
+ parentContractId: string,
1865
+ mapIndex: number,
1866
+ key: Val,
1867
+ keyType: string,
1868
+ group: number
1869
+ ): string {
1870
+ const prefix = ralph.encodeMapPrefix(mapIndex)
1871
+ const encodedKey = ralph.primitiveToByteVec(key, keyType)
1872
+ const path = binToHex(prefix) + binToHex(encodedKey)
1873
+ return subContractId(parentContractId, path, group)
1874
+ }
1875
+
1876
+ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; codeHash: string } {
1877
+ const { immFields, mutFields } = ralph.calcFieldSize(type, true, structs)
1878
+ const loadImmFieldByIndex: Method = {
1879
+ isPublic: true,
1880
+ assetModifier: 0,
1881
+ argsLength: 1,
1882
+ localsLength: 1,
1883
+ returnLength: 1,
1884
+ instrs: [LoadLocal(0), LoadImmFieldByIndex]
1885
+ }
1886
+ const loadMutFieldByIndex: Method = {
1887
+ ...loadImmFieldByIndex,
1888
+ instrs: [LoadLocal(0), LoadMutFieldByIndex]
1889
+ }
1890
+ const parentContractIdIndex = immFields
1891
+ const storeMutFieldByIndex: Method = {
1892
+ ...loadImmFieldByIndex,
1893
+ argsLength: 2,
1894
+ localsLength: 2,
1895
+ returnLength: 0,
1896
+ instrs: [
1897
+ CallerContractId,
1898
+ LoadImmField(parentContractIdIndex),
1899
+ ByteVecEq,
1900
+ Assert,
1901
+ LoadLocal(0), // value
1902
+ LoadLocal(1), // index
1903
+ StoreMutFieldByIndex
1904
+ ]
1905
+ }
1906
+ const destroy: Method = {
1907
+ isPublic: true,
1908
+ assetModifier: 2,
1909
+ argsLength: 1,
1910
+ localsLength: 1,
1911
+ returnLength: 0,
1912
+ instrs: [CallerContractId, LoadImmField(parentContractIdIndex), ByteVecEq, Assert, LoadLocal(0), DestroySelf]
1913
+ }
1914
+ const c = {
1915
+ fieldLength: immFields + mutFields + 1, // parentContractId
1916
+ methods: [loadImmFieldByIndex, loadMutFieldByIndex, storeMutFieldByIndex, destroy]
1917
+ }
1918
+ const bytecode = contract.contractCodec.encode(contract.toHalfDecoded(c))
1919
+ const codeHash = blake.blake2b(bytecode, undefined, 32)
1920
+ return { bytecode: binToHex(bytecode), codeHash: binToHex(codeHash) }
1921
+ }
1922
+
1923
+ function getContractFieldsSig(mapValueType: string): FieldsSig {
1924
+ return {
1925
+ names: ['value', 'parentContractId'],
1926
+ types: [mapValueType, 'ByteVec'],
1927
+ isMutable: [true, false]
1928
+ }
1929
+ }
1930
+
1931
+ function mapToExistingContracts(
1932
+ contract: Contract,
1933
+ parentContractId: string,
1934
+ group: number,
1935
+ map: Map<Val, Val>,
1936
+ mapIndex: number,
1937
+ type: string
1938
+ ): ContractState[] {
1939
+ const [keyType, valueType] = ralph.parseMapType(type)
1940
+ const generatedContract = genCodeForType(valueType, contract.structs)
1941
+ return Array.from(map.entries()).map(([key, value]) => {
1942
+ const fields = { value, parentContractId }
1943
+ const contractId = calcWrapperContractId(parentContractId, mapIndex, key, keyType, group)
1944
+ return {
1945
+ ...generatedContract,
1946
+ address: addressFromContractId(contractId),
1947
+ contractId: contractId,
1948
+ fieldsSig: getContractFieldsSig(valueType),
1949
+ fields,
1950
+ asset: { alphAmount: ONE_ALPH }
1951
+ }
1952
+ })
1953
+ }
1954
+
1955
+ function mapsToExistingContracts(
1956
+ contract: Contract,
1957
+ parentContractId: string,
1958
+ group: number,
1959
+ initialMaps: Record<string, Map<Val, Val>>
1960
+ ) {
1961
+ const mapsSig = contract.mapsSig
1962
+ if (mapsSig === undefined) return []
1963
+ const contractStates: ContractState[] = []
1964
+ Object.keys(initialMaps).forEach((name) => {
1965
+ const index = mapsSig.names.findIndex((n) => n === name)
1966
+ if (index === -1) throw new Error(`Map var ${name} does not exist in contract ${contract.name}`)
1967
+ const mapType = mapsSig.types[`${index}`]
1968
+ const states = mapToExistingContracts(contract, parentContractId, group, initialMaps[`${name}`], index, mapType)
1969
+ contractStates.push(...states)
1970
+ })
1971
+ return contractStates
1972
+ }
1973
+
1974
+ export async function testMethod<
1975
+ I extends ContractInstance,
1976
+ F extends Fields,
1977
+ A extends Arguments,
1978
+ R,
1979
+ M extends Record<string, Map<Val, Val>> = Record<string, Map<Val, Val>>
1980
+ >(
1981
+ factory: ContractFactory<I, F>,
1824
1982
  methodName: string,
1825
- params: Optional<TestContractParams<F, A>, 'testArgs' | 'initialFields'>
1826
- ): Promise<TestContractResult<R>> {
1983
+ params: Optional<TestContractParams<F, A, M>, 'testArgs' | 'initialFields'>
1984
+ ): Promise<TestContractResult<R, M>> {
1827
1985
  const txId = params?.txId ?? randomTxId()
1828
- const initialFields = params.initialFields === undefined ? {} : params.initialFields
1829
- const apiParams = contract.contract.toApiTestContractParams(methodName, {
1986
+ const contract = factory.contract
1987
+ const address = params.address ?? addressFromContractId(binToHex(crypto.getRandomValues(new Uint8Array(32))))
1988
+ const contractId = binToHex(contractIdFromAddress(address))
1989
+ const group = params.group ?? 0
1990
+ const initialMaps = params.initialMaps ?? {}
1991
+ const contractStates = mapsToExistingContracts(contract, contractId, group, initialMaps)
1992
+ const apiParams = contract.toApiTestContractParams(methodName, {
1830
1993
  ...params,
1994
+ address,
1831
1995
  txId: txId,
1832
- initialFields: addStdIdToFields(contract.contract, initialFields),
1833
- testArgs: params.testArgs === undefined ? {} : params.testArgs
1996
+ initialFields: addStdIdToFields(contract, params.initialFields ?? {}),
1997
+ testArgs: params.testArgs === undefined ? {} : params.testArgs,
1998
+ existingContracts: (params.existingContracts ?? []).concat(contractStates)
1834
1999
  })
1835
2000
  const apiResult = await getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
1836
- const testResult = contract.contract.fromApiTestContractResult(methodName, apiResult, txId)
1837
- contract.contract.printDebugMessages(methodName, testResult.debugMessages)
1838
- return testResult as TestContractResult<R>
2001
+ const maps = existingContractsToMaps(contract, address, group, apiResult, initialMaps)
2002
+ const testResult = contract.fromApiTestContractResult(methodName, apiResult, txId)
2003
+ contract.printDebugMessages(methodName, testResult.debugMessages)
2004
+ return {
2005
+ ...testResult,
2006
+ maps
2007
+ } as TestContractResult<R, M>
2008
+ }
2009
+
2010
+ interface MapInfo {
2011
+ name: string
2012
+ value: Map<Val, Val>
2013
+ keyType: string
2014
+ valueType: string
2015
+ index: number
2016
+ }
2017
+
2018
+ function buildMapInfo(contract: Contract, fields: Fields): MapInfo[] {
2019
+ const mapsSig = contract.mapsSig
2020
+ if (mapsSig === undefined) return []
2021
+ return mapsSig.names.map((name, index) => {
2022
+ const mapType = mapsSig.types[`${index}`]
2023
+ const value = (fields[`${name}`] ?? new Map<Val, Val>()) as Map<Val, Val>
2024
+ const [keyType, valueType] = ralph.parseMapType(mapType)
2025
+ return { name, value, keyType, valueType, index }
2026
+ })
2027
+ }
2028
+
2029
+ function extractFromEventLog(
2030
+ contract: Contract,
2031
+ result: node.TestContractResult,
2032
+ allMaps: MapInfo[],
2033
+ address: string,
2034
+ group: number
2035
+ ): string[] {
2036
+ const parentContractId = binToHex(contractIdFromAddress(address))
2037
+ const newInserted: string[] = []
2038
+ result.debugMessages.forEach((message) => {
2039
+ if (message.contractAddress !== address) return
2040
+ const decoded = ralph.tryDecodeMapDebugLog(message.message)
2041
+ if (decoded === undefined) return
2042
+ const map = allMaps[`${decoded.mapIndex}`]
2043
+ const decodedKey = ralph.decodePrimitive(decoded.encodedKey, map.keyType)
2044
+ const contractId = subContractId(parentContractId, decoded.path, group)
2045
+ if (!decoded.isInsert) {
2046
+ map.value.delete(decodedKey)
2047
+ return
2048
+ }
2049
+ const state = result.contracts.find((s) => s.address === addressFromContractId(contractId))
2050
+ if (state === undefined) {
2051
+ throw new Error(`Cannot find contract state for map value, map field: ${map.name}, value type: ${map.valueType}`)
2052
+ }
2053
+ newInserted.push(state.address)
2054
+ const fieldsSig = getContractFieldsSig(map.valueType)
2055
+ const fields = fromApiFields(state.immFields, state.mutFields, fieldsSig, contract.structs)
2056
+ map.value.set(decodedKey, fields['value'])
2057
+ })
2058
+ return newInserted
2059
+ }
2060
+
2061
+ function updateMaps(
2062
+ contract: Contract,
2063
+ result: node.TestContractResult,
2064
+ allMaps: MapInfo[],
2065
+ address: Address,
2066
+ group: number
2067
+ ): string[] {
2068
+ const parentContractId = binToHex(contractIdFromAddress(address))
2069
+ const updated: string[] = []
2070
+ allMaps.forEach((map) => {
2071
+ Array.from(map.value.keys()).forEach((key) => {
2072
+ const contractId = calcWrapperContractId(parentContractId, map.index, key, map.keyType, group)
2073
+ const updatedState = result.contracts.find((s) => s.address === addressFromContractId(contractId))
2074
+ if (updatedState === undefined) return
2075
+ updated.push(updatedState.address)
2076
+ const fieldsSig = getContractFieldsSig(map.valueType)
2077
+ const fields = fromApiFields(updatedState.immFields, updatedState.mutFields, fieldsSig, contract.structs)
2078
+ map.value.set(key, fields['value'])
2079
+ })
2080
+ })
2081
+ return updated
2082
+ }
2083
+
2084
+ function existingContractsToMaps(
2085
+ contract: Contract,
2086
+ address: Address,
2087
+ group: number,
2088
+ result: node.TestContractResult,
2089
+ maps: Record<string, Map<Val, Val>>
2090
+ ): Record<string, Map<Val, Val>> {
2091
+ const allMaps = buildMapInfo(contract, maps)
2092
+ const updated = updateMaps(contract, result, allMaps, address, group)
2093
+ const newInserted = extractFromEventLog(contract, result, allMaps, address, group)
2094
+ const mapEntries = updated.concat(newInserted)
2095
+ const remainContracts = result.contracts.filter((c) => mapEntries.find((addr) => c.address === addr) === undefined)
2096
+ result.contracts = remainContracts
2097
+ return allMaps.reduce((acc, map) => {
2098
+ acc[`${map.name}`] = map.value
2099
+ return acc
2100
+ }, {})
1839
2101
  }
1840
2102
 
1841
2103
  export abstract class ContractInstance {
@@ -1862,18 +2124,29 @@ export async function fetchContractState<F extends Fields, I extends ContractIns
1862
2124
  }
1863
2125
  }
1864
2126
 
2127
+ function checkGroupIndex(groupIndex: number) {
2128
+ if (groupIndex < 0 || groupIndex >= TOTAL_NUMBER_OF_GROUPS) {
2129
+ throw new Error(
2130
+ `Invalid group index ${groupIndex}, expected a value within the range [0, ${TOTAL_NUMBER_OF_GROUPS})`
2131
+ )
2132
+ }
2133
+ }
2134
+
1865
2135
  export function subscribeContractCreatedEvent(
1866
2136
  options: EventSubscribeOptions<ContractCreatedEvent>,
2137
+ fromGroup: number,
1867
2138
  fromCount?: number
1868
2139
  ): EventSubscription {
2140
+ checkGroupIndex(fromGroup)
2141
+ const contractAddress = CreateContractEventAddresses[`${fromGroup}`]
1869
2142
  return subscribeEventsFromContract(
1870
2143
  options,
1871
- CreateContractEventAddress,
2144
+ contractAddress,
1872
2145
  Contract.ContractCreatedEventIndex,
1873
2146
  (event) => {
1874
2147
  return {
1875
2148
  ...decodeContractCreatedEvent(event),
1876
- contractAddress: CreateContractEventAddress
2149
+ contractAddress: contractAddress
1877
2150
  }
1878
2151
  },
1879
2152
  fromCount
@@ -1882,16 +2155,19 @@ export function subscribeContractCreatedEvent(
1882
2155
 
1883
2156
  export function subscribeContractDestroyedEvent(
1884
2157
  options: EventSubscribeOptions<ContractDestroyedEvent>,
2158
+ fromGroup: number,
1885
2159
  fromCount?: number
1886
2160
  ): EventSubscription {
2161
+ checkGroupIndex(fromGroup)
2162
+ const contractAddress = DestroyContractEventAddresses[`${fromGroup}`]
1887
2163
  return subscribeEventsFromContract(
1888
2164
  options,
1889
- DestroyContractEventAddress,
2165
+ contractAddress,
1890
2166
  Contract.ContractDestroyedEventIndex,
1891
2167
  (event) => {
1892
2168
  return {
1893
2169
  ...decodeContractDestroyedEvent(event),
1894
- contractAddress: DestroyContractEventAddress
2170
+ contractAddress: contractAddress
1895
2171
  }
1896
2172
  },
1897
2173
  fromCount
@@ -17,9 +17,11 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import { Buffer } from 'buffer/'
20
- import { PrimitiveTypes, Val, decodeArrayType, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256 } from '../api'
21
- import { binToHex, bs58, isHexString } from '../utils'
20
+ import { Val, decodeArrayType, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256 } from '../api'
21
+ import { binToHex, bs58, hexToBinUnsafe, isHexString } from '../utils'
22
22
  import { Fields, FieldsSig, Struct } from './contract'
23
+ import { compactSignedIntCodec, compactUnsignedIntCodec } from '../codec'
24
+ import { lockupScriptCodec } from '../codec/lockup-script-codec'
23
25
 
24
26
  const bigIntZero = BigInt(0)
25
27
 
@@ -52,6 +54,13 @@ export function encodeBool(bool: boolean): Uint8Array {
52
54
  return bool ? Uint8Array.from([1]) : Uint8Array.from([0])
53
55
  }
54
56
 
57
+ export function decodeBool(bytes: Uint8Array): boolean {
58
+ if (bytes.length !== 1) {
59
+ throw new Error(`Expected one byte for encoded bool, got ${bytes.length}`)
60
+ }
61
+ return bytes[0] === 1 ? true : false
62
+ }
63
+
55
64
  export function encodeI256(i256: bigint): Uint8Array {
56
65
  if (i256 >= bigIntZero) {
57
66
  return encodeI256Positive(i256)
@@ -247,6 +256,135 @@ export function encodeScriptField(tpe: string, value: Val): Uint8Array {
247
256
  throw invalidScriptField(tpe, value)
248
257
  }
249
258
 
259
+ export function splitFields(fieldsSig: FieldsSig): [FieldsSig, FieldsSig] {
260
+ return fieldsSig.types.reduce<[FieldsSig, FieldsSig]>(
261
+ ([mapFields, fieldsExceptMaps], type, index) => {
262
+ const fieldSig = type.startsWith('Map[') ? mapFields : fieldsExceptMaps
263
+ fieldSig.names.push(fieldsSig.names[`${index}`])
264
+ fieldSig.types.push(type)
265
+ fieldSig.isMutable.push(fieldsSig.isMutable[`${index}`])
266
+ return [mapFields, fieldsExceptMaps]
267
+ },
268
+ [
269
+ { names: [], types: [], isMutable: [] },
270
+ { names: [], types: [], isMutable: [] }
271
+ ]
272
+ )
273
+ }
274
+
275
+ export function parseMapType(type: string): [string, string] {
276
+ if (!type.startsWith('Map[')) {
277
+ throw new Error(`Expected map type, got ${type}`)
278
+ }
279
+ const keyStartIndex = type.indexOf('[')
280
+ const keyEndIndex = type.indexOf(',')
281
+ return [type.slice(keyStartIndex + 1, keyEndIndex), type.slice(keyEndIndex + 1, type.length - 1)]
282
+ }
283
+
284
+ export function encodeMapPrefix(mapIndex: number): Uint8Array {
285
+ const str = `__map__${mapIndex}__`
286
+ const bytes = new Uint8Array(str.length)
287
+ for (let i = 0; i < str.length; i += 1) {
288
+ bytes[i] = str.charCodeAt(i)
289
+ }
290
+ return bytes
291
+ }
292
+
293
+ function fromAscii(str: string): string {
294
+ let result = ''
295
+ for (let i = 0; i < str.length; i += 2) {
296
+ const ascii = parseInt(str.slice(i, i + 2), 16)
297
+ result += String.fromCharCode(ascii)
298
+ }
299
+ return result
300
+ }
301
+
302
+ export function calcFieldSize(
303
+ type: string,
304
+ isMutable: boolean,
305
+ structs: Struct[]
306
+ ): { immFields: number; mutFields: number } {
307
+ const struct = structs.find((s) => s.name === type)
308
+ if (struct !== undefined) {
309
+ return struct.fieldTypes.reduce(
310
+ (acc, fieldType, index) => {
311
+ const isFieldMutable = isMutable && struct.isMutable[`${index}`]
312
+ const subFieldSize = calcFieldSize(fieldType, isFieldMutable, structs)
313
+ return {
314
+ immFields: acc.immFields + subFieldSize.immFields,
315
+ mutFields: acc.mutFields + subFieldSize.mutFields
316
+ }
317
+ },
318
+ { immFields: 0, mutFields: 0 }
319
+ )
320
+ }
321
+ if (type.startsWith('[')) {
322
+ const [baseType, size] = decodeArrayType(type)
323
+ const base = calcFieldSize(baseType, isMutable, structs)
324
+ return { immFields: base.immFields * size, mutFields: base.mutFields * size }
325
+ }
326
+ return isMutable ? { immFields: 0, mutFields: 1 } : { immFields: 1, mutFields: 0 }
327
+ }
328
+
329
+ export function tryDecodeMapDebugLog(
330
+ message: string
331
+ ): { path: string; mapIndex: number; encodedKey: Uint8Array; isInsert: boolean } | undefined {
332
+ const prefix = '5f5f6d61705f5f' // __map__
333
+ const parts = message.split(',')
334
+ if (!message.startsWith(prefix) || parts.length !== 2) return undefined
335
+ if (parts[1] !== 'true' && parts[1] !== 'false') return undefined
336
+
337
+ if (!isHexString(parts[0])) return undefined
338
+ const remain = parts[0].slice(prefix.length)
339
+ const suffixIndex = remain.indexOf('5f5f') // __
340
+ if (suffixIndex === -1) return undefined
341
+
342
+ const encodedMapIndex = remain.slice(0, suffixIndex)
343
+ const mapIndex = parseInt(fromAscii(encodedMapIndex))
344
+ const encodedKey = hexToBinUnsafe(remain.slice(suffixIndex + 4))
345
+ const isInsert = parts[1] === 'true' ? true : false
346
+ return { path: parts[0], mapIndex, encodedKey, isInsert }
347
+ }
348
+
349
+ export function decodePrimitive(value: Uint8Array, type: string): Val {
350
+ switch (type) {
351
+ case 'Bool':
352
+ return decodeBool(value)
353
+ case 'I256':
354
+ return compactSignedIntCodec.decodeI256(Buffer.from(value))
355
+ case 'U256':
356
+ return compactUnsignedIntCodec.decodeU256(Buffer.from(value))
357
+ case 'ByteVec':
358
+ return binToHex(value)
359
+ case 'Address':
360
+ return bs58.encode(value)
361
+ default:
362
+ throw Error(`Expected primitive type, got ${type}`)
363
+ }
364
+ }
365
+
366
+ export function primitiveToByteVec(value: Val, type: string): Uint8Array {
367
+ switch (type) {
368
+ case 'Bool':
369
+ const byte = toApiBoolean(value) ? 1 : 0
370
+ return new Uint8Array([byte])
371
+ case 'I256':
372
+ const i256 = toApiNumber256(value)
373
+ return encodeI256(BigInt(i256))
374
+ case 'U256':
375
+ const u256 = toApiNumber256(value)
376
+ return encodeU256(BigInt(u256))
377
+ case 'ByteVec':
378
+ const hexStr = toApiByteVec(value)
379
+ return encodeByteVec(hexStr)
380
+ case 'Address':
381
+ const address = toApiAddress(value)
382
+ return encodeAddress(address)
383
+ default:
384
+ throw Error(`Expected primitive type, got ${type}`)
385
+ }
386
+ }
387
+
250
388
  export function flattenFields(
251
389
  fields: Fields,
252
390
  names: string[],
package/src/index.ts CHANGED
@@ -29,6 +29,6 @@ export * from './token'
29
29
 
30
30
  export * from './constants'
31
31
  export * as web3 from './global'
32
-
32
+ export * as codec from './codec'
33
33
  export * as utils from './utils'
34
34
  export * from './debug'