@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.
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/api-alephium.d.ts +6 -1
- package/dist/src/api/api-alephium.js +1 -1
- package/dist/src/api/types.d.ts +1 -1
- package/dist/src/codec/array-codec.d.ts +17 -0
- package/dist/src/codec/array-codec.js +59 -0
- package/dist/src/codec/asset-output-codec.d.ts +27 -0
- package/dist/src/codec/asset-output-codec.js +135 -0
- package/dist/src/codec/bigint-codec.d.ts +5 -0
- package/dist/src/codec/bigint-codec.js +86 -0
- package/dist/src/codec/bytestring-codec.d.ts +16 -0
- package/dist/src/codec/bytestring-codec.js +50 -0
- package/dist/src/codec/codec.d.ts +8 -0
- package/dist/src/codec/codec.js +9 -0
- package/dist/src/codec/compact-int-codec.d.ts +51 -0
- package/dist/src/codec/compact-int-codec.js +300 -0
- package/dist/src/codec/contract-codec.d.ts +23 -0
- package/dist/src/codec/contract-codec.js +81 -0
- package/dist/src/codec/contract-output-codec.d.ts +21 -0
- package/dist/src/codec/contract-output-codec.js +82 -0
- package/dist/src/codec/contract-output-ref-codec.d.ts +15 -0
- package/dist/src/codec/contract-output-ref-codec.js +38 -0
- package/dist/src/codec/either-codec.d.ts +17 -0
- package/dist/src/codec/either-codec.js +67 -0
- package/dist/src/codec/hash.d.ts +4 -0
- package/dist/src/codec/hash.js +23 -0
- package/dist/src/codec/index.d.ts +23 -0
- package/dist/src/codec/index.js +69 -0
- package/dist/src/codec/input-codec.d.ts +22 -0
- package/dist/src/codec/input-codec.js +71 -0
- package/dist/src/codec/instr-codec.d.ts +230 -0
- package/dist/src/codec/instr-codec.js +471 -0
- package/dist/src/codec/lockup-script-codec.d.ts +28 -0
- package/dist/src/codec/lockup-script-codec.js +80 -0
- package/dist/src/codec/long-codec.d.ts +9 -0
- package/dist/src/codec/long-codec.js +56 -0
- package/dist/src/codec/method-codec.d.ts +31 -0
- package/dist/src/codec/method-codec.js +78 -0
- package/dist/src/codec/option-codec.d.ts +15 -0
- package/dist/src/codec/option-codec.js +55 -0
- package/dist/src/codec/output-codec.d.ts +7 -0
- package/dist/src/codec/output-codec.js +26 -0
- package/dist/src/codec/script-codec.d.ts +16 -0
- package/dist/src/codec/script-codec.js +41 -0
- package/dist/src/codec/signature-codec.d.ts +14 -0
- package/dist/src/codec/signature-codec.js +19 -0
- package/dist/src/codec/signed-int-codec.d.ts +9 -0
- package/dist/src/codec/signed-int-codec.js +39 -0
- package/dist/src/codec/token-codec.d.ts +16 -0
- package/dist/src/codec/token-codec.js +46 -0
- package/dist/src/codec/transaction-codec.d.ts +27 -0
- package/dist/src/codec/transaction-codec.js +128 -0
- package/dist/src/codec/unlock-script-codec.d.ts +40 -0
- package/dist/src/codec/unlock-script-codec.js +170 -0
- package/dist/src/codec/unsigned-tx-codec.d.ts +30 -0
- package/dist/src/codec/unsigned-tx-codec.js +103 -0
- package/dist/src/contract/contract.d.ts +14 -8
- package/dist/src/contract/contract.js +205 -22
- package/dist/src/contract/ralph.d.ts +16 -0
- package/dist/src/contract/ralph.js +127 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +2 -1
- package/package.json +5 -4
- package/src/api/api-alephium.ts +7 -1
- package/src/api/types.ts +1 -1
- package/src/codec/array-codec.ts +63 -0
- package/src/codec/asset-output-codec.ts +149 -0
- package/src/codec/bigint-codec.ts +92 -0
- package/src/codec/bytestring-codec.ts +56 -0
- package/src/codec/codec.ts +31 -0
- package/src/codec/compact-int-codec.ts +316 -0
- package/src/codec/contract-codec.ts +95 -0
- package/src/codec/contract-output-codec.ts +95 -0
- package/src/codec/contract-output-ref-codec.ts +42 -0
- package/src/codec/either-codec.ts +74 -0
- package/src/codec/hash.ts +35 -0
- package/src/codec/index.ts +41 -0
- package/src/codec/input-codec.ts +81 -0
- package/src/codec/instr-codec.ts +479 -0
- package/src/codec/lockup-script-codec.ts +99 -0
- package/src/codec/long-codec.ts +59 -0
- package/src/codec/method-codec.ts +97 -0
- package/src/codec/option-codec.ts +60 -0
- package/src/codec/output-codec.ts +26 -0
- package/src/codec/script-codec.ts +45 -0
- package/src/codec/signature-codec.ts +40 -0
- package/src/codec/signed-int-codec.ts +37 -0
- package/src/codec/token-codec.ts +51 -0
- package/src/codec/transaction-codec.ts +147 -0
- package/src/codec/unlock-script-codec.ts +194 -0
- package/src/codec/unsigned-tx-codec.ts +124 -0
- package/src/contract/contract.ts +299 -23
- package/src/contract/ralph.ts +140 -2
- package/src/index.ts +1 -1
package/src/contract/contract.ts
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
1761
|
+
function specialContractAddress(eventIndex: number, groupIndex: number): string {
|
|
1726
1762
|
const bytes = new Uint8Array(32).fill(0)
|
|
1727
|
-
bytes[
|
|
1763
|
+
bytes[30] = eventIndex
|
|
1764
|
+
bytes[31] = groupIndex
|
|
1728
1765
|
return addressFromContractId(binToHex(bytes))
|
|
1729
1766
|
}
|
|
1730
1767
|
|
|
1731
|
-
export const
|
|
1732
|
-
|
|
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
|
-
|
|
1823
|
-
|
|
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
|
|
1829
|
-
const
|
|
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
|
|
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
|
|
1837
|
-
contract.
|
|
1838
|
-
|
|
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
|
-
|
|
2144
|
+
contractAddress,
|
|
1872
2145
|
Contract.ContractCreatedEventIndex,
|
|
1873
2146
|
(event) => {
|
|
1874
2147
|
return {
|
|
1875
2148
|
...decodeContractCreatedEvent(event),
|
|
1876
|
-
contractAddress:
|
|
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
|
-
|
|
2165
|
+
contractAddress,
|
|
1890
2166
|
Contract.ContractDestroyedEventIndex,
|
|
1891
2167
|
(event) => {
|
|
1892
2168
|
return {
|
|
1893
2169
|
...decodeContractDestroyedEvent(event),
|
|
1894
|
-
contractAddress:
|
|
2170
|
+
contractAddress: contractAddress
|
|
1895
2171
|
}
|
|
1896
2172
|
},
|
|
1897
2173
|
fromCount
|
package/src/contract/ralph.ts
CHANGED
|
@@ -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 {
|
|
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[],
|