@d9-network/ink 1.0.2 → 1.1.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/README.md +96 -0
- package/dist/index.cjs +612 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +705 -1
- package/dist/index.d.mts +705 -1
- package/dist/index.mjs +598 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
package/dist/index.cjs
CHANGED
|
@@ -630,12 +630,46 @@ var ContractEventParser = class {
|
|
|
630
630
|
return this.filterEvents(chainEvents).filter((e) => e.type === label);
|
|
631
631
|
}
|
|
632
632
|
/**
|
|
633
|
+
* Parse chain event records with proper typing
|
|
634
|
+
*
|
|
635
|
+
* Unlike parseEvent/filterEvents which accept `unknown[]`, this method
|
|
636
|
+
* accepts typed `ChainEventRecord[]` which eliminates the need for
|
|
637
|
+
* `as any` casts in user code.
|
|
638
|
+
*
|
|
639
|
+
* @param events - Array of typed chain event records
|
|
640
|
+
* @param options - Optional filter criteria
|
|
641
|
+
* @returns Array of typed contract events
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* // No more `as any` needed!
|
|
646
|
+
* const events: ChainEventRecord[] = await api.query.System.Events.getValue();
|
|
647
|
+
* const contractEvents = parser.parseChainEvents(events);
|
|
648
|
+
*
|
|
649
|
+
* for (const event of contractEvents) {
|
|
650
|
+
* if (event.type === "Transfer") {
|
|
651
|
+
* // Fully typed!
|
|
652
|
+
* console.log(event.value.from, event.value.to, event.value.value);
|
|
653
|
+
* }
|
|
654
|
+
* }
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
parseChainEvents(events, options) {
|
|
658
|
+
return this.filterEvents(events, options);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
633
661
|
* Get the contract address as SS58 string
|
|
634
662
|
*/
|
|
635
663
|
getContractAddress() {
|
|
636
664
|
return this.contractAddress;
|
|
637
665
|
}
|
|
638
666
|
/**
|
|
667
|
+
* Get the contract address as bytes
|
|
668
|
+
*/
|
|
669
|
+
getContractAddressBytes() {
|
|
670
|
+
return this.contractAddressBytes;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
639
673
|
* Extract ContractEmitted event from chain event structure
|
|
640
674
|
* Based on polkadot-api event format
|
|
641
675
|
*/
|
|
@@ -1348,6 +1382,101 @@ function createD9InkSdk(client, options = {}) {
|
|
|
1348
1382
|
};
|
|
1349
1383
|
}
|
|
1350
1384
|
|
|
1385
|
+
//#endregion
|
|
1386
|
+
//#region src/event-types.ts
|
|
1387
|
+
/**
|
|
1388
|
+
* Type guard for Contracts.ContractEmitted events
|
|
1389
|
+
*
|
|
1390
|
+
* @example
|
|
1391
|
+
* ```ts
|
|
1392
|
+
* for (const event of events) {
|
|
1393
|
+
* if (isContractEmittedEvent(event)) {
|
|
1394
|
+
* // event.event.value.value is ContractEmittedValue
|
|
1395
|
+
* const { contract, data } = event.event.value.value;
|
|
1396
|
+
* }
|
|
1397
|
+
* }
|
|
1398
|
+
* ```
|
|
1399
|
+
*/
|
|
1400
|
+
function isContractEmittedEvent(event) {
|
|
1401
|
+
return event.event?.type === "Contracts" && event.event?.value?.type === "ContractEmitted";
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Type guard for Contracts.Called events
|
|
1405
|
+
*
|
|
1406
|
+
* @example
|
|
1407
|
+
* ```ts
|
|
1408
|
+
* for (const event of events) {
|
|
1409
|
+
* if (isContractCalledEvent(event)) {
|
|
1410
|
+
* // event.event.value.value is ContractCalledValue
|
|
1411
|
+
* const { caller, contract } = event.event.value.value;
|
|
1412
|
+
* }
|
|
1413
|
+
* }
|
|
1414
|
+
* ```
|
|
1415
|
+
*/
|
|
1416
|
+
function isContractCalledEvent(event) {
|
|
1417
|
+
return event.event?.type === "Contracts" && event.event?.value?.type === "Called";
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Type guard for Balances.Transfer events
|
|
1421
|
+
*
|
|
1422
|
+
* @example
|
|
1423
|
+
* ```ts
|
|
1424
|
+
* for (const event of events) {
|
|
1425
|
+
* if (isBalancesTransferEvent(event)) {
|
|
1426
|
+
* const { from, to, amount } = event.event.value.value;
|
|
1427
|
+
* }
|
|
1428
|
+
* }
|
|
1429
|
+
* ```
|
|
1430
|
+
*/
|
|
1431
|
+
function isBalancesTransferEvent(event) {
|
|
1432
|
+
return event.event?.type === "Balances" && event.event?.value?.type === "Transfer";
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Check if event is in ApplyExtrinsic phase
|
|
1436
|
+
*
|
|
1437
|
+
* @example
|
|
1438
|
+
* ```ts
|
|
1439
|
+
* for (const event of events) {
|
|
1440
|
+
* if (isApplyExtrinsicPhase(event)) {
|
|
1441
|
+
* const extrinsicIndex = event.phase.value;
|
|
1442
|
+
* }
|
|
1443
|
+
* }
|
|
1444
|
+
* ```
|
|
1445
|
+
*/
|
|
1446
|
+
function isApplyExtrinsicPhase(event) {
|
|
1447
|
+
return event.phase?.type === "ApplyExtrinsic" && typeof event.phase.value === "number";
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Extract contract emitted data with proper typing
|
|
1451
|
+
*
|
|
1452
|
+
* Handles both Uint8Array and Binary object formats.
|
|
1453
|
+
*
|
|
1454
|
+
* @param event - Chain event record
|
|
1455
|
+
* @returns Extracted data or null if not a ContractEmitted event
|
|
1456
|
+
*/
|
|
1457
|
+
function extractContractEmittedData(event) {
|
|
1458
|
+
if (!isContractEmittedEvent(event)) return null;
|
|
1459
|
+
const { contract: contractRaw, data: dataRaw } = event.event.value.value;
|
|
1460
|
+
let contract;
|
|
1461
|
+
if (contractRaw instanceof Uint8Array) contract = contractRaw;
|
|
1462
|
+
else if (typeof contractRaw === "string") contract = new Uint8Array(0);
|
|
1463
|
+
else return null;
|
|
1464
|
+
let data;
|
|
1465
|
+
if (dataRaw instanceof Uint8Array) data = dataRaw;
|
|
1466
|
+
else if (dataRaw && typeof dataRaw === "object" && "asBytes" in dataRaw && typeof dataRaw.asBytes === "function") data = dataRaw.asBytes();
|
|
1467
|
+
else return null;
|
|
1468
|
+
const topics = [];
|
|
1469
|
+
if (event.topics && Array.isArray(event.topics)) {
|
|
1470
|
+
for (const topic of event.topics) if (topic instanceof Uint8Array) topics.push(topic);
|
|
1471
|
+
else if (topic && typeof topic === "object" && "asBytes" in topic && typeof topic.asBytes === "function") topics.push(topic.asBytes());
|
|
1472
|
+
}
|
|
1473
|
+
return {
|
|
1474
|
+
contract,
|
|
1475
|
+
data,
|
|
1476
|
+
topics
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1351
1480
|
//#endregion
|
|
1352
1481
|
//#region src/codec-builder-internal.ts
|
|
1353
1482
|
/**
|
|
@@ -1674,6 +1803,97 @@ var ContractCallParser = class {
|
|
|
1674
1803
|
for (let i = 0; i < selector.length; i++) if (selector[i] !== info.selector[i]) return false;
|
|
1675
1804
|
return true;
|
|
1676
1805
|
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Get selector for a message label
|
|
1808
|
+
*
|
|
1809
|
+
* @param label - Message label (e.g., "PSP22::transfer")
|
|
1810
|
+
* @returns Selector as Uint8Array or null if not found
|
|
1811
|
+
*
|
|
1812
|
+
* @example
|
|
1813
|
+
* ```ts
|
|
1814
|
+
* const selector = parser.getSelector("PSP22::transfer");
|
|
1815
|
+
* // selector is Uint8Array([0xdb, 0x20, 0xf9, 0xf5])
|
|
1816
|
+
* ```
|
|
1817
|
+
*/
|
|
1818
|
+
getSelector(label) {
|
|
1819
|
+
return this.messageDecoders.get(label)?.selector ?? null;
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Get selector as hex string for a message label
|
|
1823
|
+
*
|
|
1824
|
+
* @param label - Message label (e.g., "PSP22::transfer")
|
|
1825
|
+
* @returns Selector hex string (without 0x) or null if not found
|
|
1826
|
+
*
|
|
1827
|
+
* @example
|
|
1828
|
+
* ```ts
|
|
1829
|
+
* const selectorHex = parser.getSelectorHex("PSP22::transfer");
|
|
1830
|
+
* // selectorHex is "db20f9f5"
|
|
1831
|
+
* ```
|
|
1832
|
+
*/
|
|
1833
|
+
getSelectorHex(label) {
|
|
1834
|
+
const selector = this.getSelector(label);
|
|
1835
|
+
if (!selector) return null;
|
|
1836
|
+
return Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Get the label for a selector
|
|
1840
|
+
*
|
|
1841
|
+
* @param selector - Selector as Uint8Array or hex string (with or without 0x)
|
|
1842
|
+
* @returns Message label or null if not found
|
|
1843
|
+
*
|
|
1844
|
+
* @example
|
|
1845
|
+
* ```ts
|
|
1846
|
+
* const label = parser.getLabel("db20f9f5");
|
|
1847
|
+
* // label is "PSP22::transfer"
|
|
1848
|
+
*
|
|
1849
|
+
* // Also works with Uint8Array
|
|
1850
|
+
* const label2 = parser.getLabel(callData.slice(0, 4));
|
|
1851
|
+
* ```
|
|
1852
|
+
*/
|
|
1853
|
+
getLabel(selector) {
|
|
1854
|
+
const selectorHex = this.normalizeSelector(selector);
|
|
1855
|
+
return this.selectorToLabel.get(selectorHex) ?? null;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Get all selector-to-label mappings
|
|
1859
|
+
*
|
|
1860
|
+
* @returns Map of selector hex (without 0x) -> label
|
|
1861
|
+
*
|
|
1862
|
+
* @example
|
|
1863
|
+
* ```ts
|
|
1864
|
+
* const map = parser.getSelectorMap();
|
|
1865
|
+
* for (const [selectorHex, label] of map) {
|
|
1866
|
+
* console.log(`${selectorHex} -> ${label}`);
|
|
1867
|
+
* }
|
|
1868
|
+
* ```
|
|
1869
|
+
*/
|
|
1870
|
+
getSelectorMap() {
|
|
1871
|
+
return new Map(this.selectorToLabel);
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Check if a selector exists in this contract
|
|
1875
|
+
*
|
|
1876
|
+
* @param selector - Selector to check (Uint8Array or hex string)
|
|
1877
|
+
* @returns True if selector is valid for this contract
|
|
1878
|
+
*
|
|
1879
|
+
* @example
|
|
1880
|
+
* ```ts
|
|
1881
|
+
* if (parser.hasSelector("db20f9f5")) {
|
|
1882
|
+
* // This selector is valid for this contract
|
|
1883
|
+
* }
|
|
1884
|
+
* ```
|
|
1885
|
+
*/
|
|
1886
|
+
hasSelector(selector) {
|
|
1887
|
+
const selectorHex = this.normalizeSelector(selector);
|
|
1888
|
+
return this.selectorToLabel.has(selectorHex);
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Normalize selector to hex string (without 0x prefix)
|
|
1892
|
+
*/
|
|
1893
|
+
normalizeSelector(selector) {
|
|
1894
|
+
if (selector instanceof Uint8Array) return Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1895
|
+
return selector.startsWith("0x") ? selector.slice(2).toLowerCase() : selector.toLowerCase();
|
|
1896
|
+
}
|
|
1677
1897
|
};
|
|
1678
1898
|
/**
|
|
1679
1899
|
* Type guard for narrowing call types
|
|
@@ -1703,6 +1923,383 @@ function isCallType(call, label) {
|
|
|
1703
1923
|
return call.type === label;
|
|
1704
1924
|
}
|
|
1705
1925
|
|
|
1926
|
+
//#endregion
|
|
1927
|
+
//#region src/selectors.ts
|
|
1928
|
+
/**
|
|
1929
|
+
* PSP22 standard selectors for quick reference
|
|
1930
|
+
*
|
|
1931
|
+
* These are derived from the PSP22 standard and are consistent across implementations.
|
|
1932
|
+
* Use these when you need to quickly check if call data matches a specific PSP22 method.
|
|
1933
|
+
*
|
|
1934
|
+
* @example
|
|
1935
|
+
* ```ts
|
|
1936
|
+
* import { PSP22_SELECTORS, extractSelectorHex } from "@d9-network/ink";
|
|
1937
|
+
*
|
|
1938
|
+
* const selectorHex = extractSelectorHex(callData);
|
|
1939
|
+
* if (selectorHex === PSP22_SELECTORS.transfer) {
|
|
1940
|
+
* // This is a PSP22::transfer call
|
|
1941
|
+
* }
|
|
1942
|
+
* ```
|
|
1943
|
+
*/
|
|
1944
|
+
const PSP22_SELECTORS = {
|
|
1945
|
+
totalSupply: "162df8c2",
|
|
1946
|
+
balanceOf: "6568382f",
|
|
1947
|
+
allowance: "4d47d921",
|
|
1948
|
+
transfer: "db20f9f5",
|
|
1949
|
+
transferFrom: "54b3c76e",
|
|
1950
|
+
approve: "b20f1bbd",
|
|
1951
|
+
increaseAllowance: "96d6b57a",
|
|
1952
|
+
decreaseAllowance: "fecb57d5"
|
|
1953
|
+
};
|
|
1954
|
+
/**
|
|
1955
|
+
* Build a selector lookup map from contract metadata
|
|
1956
|
+
*
|
|
1957
|
+
* @param metadata - Contract metadata
|
|
1958
|
+
* @returns Map of selector hex (without 0x) -> SelectorInfo
|
|
1959
|
+
*
|
|
1960
|
+
* @example
|
|
1961
|
+
* ```ts
|
|
1962
|
+
* import { contracts } from "@d9-network/spec";
|
|
1963
|
+
* import { buildSelectorMap } from "@d9-network/ink";
|
|
1964
|
+
*
|
|
1965
|
+
* const selectors = buildSelectorMap(contracts.usdt.metadata);
|
|
1966
|
+
* const info = selectors.get("db20f9f5");
|
|
1967
|
+
* if (info) {
|
|
1968
|
+
* console.log(info.label); // "PSP22::transfer"
|
|
1969
|
+
* }
|
|
1970
|
+
* ```
|
|
1971
|
+
*/
|
|
1972
|
+
function buildSelectorMap(metadata) {
|
|
1973
|
+
const map$1 = /* @__PURE__ */ new Map();
|
|
1974
|
+
const messages = metadata.spec.messages;
|
|
1975
|
+
for (const message of messages) {
|
|
1976
|
+
const selectorHex = message.selector.startsWith("0x") ? message.selector.slice(2).toLowerCase() : message.selector.toLowerCase();
|
|
1977
|
+
const selector = hexToBytes(selectorHex);
|
|
1978
|
+
map$1.set(selectorHex, {
|
|
1979
|
+
label: message.label,
|
|
1980
|
+
selector,
|
|
1981
|
+
selectorHex,
|
|
1982
|
+
mutates: message.mutates,
|
|
1983
|
+
payable: message.payable
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
return map$1;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Build a reverse lookup map (label -> selector info)
|
|
1990
|
+
*
|
|
1991
|
+
* @param metadata - Contract metadata
|
|
1992
|
+
* @returns Map of label -> SelectorInfo
|
|
1993
|
+
*
|
|
1994
|
+
* @example
|
|
1995
|
+
* ```ts
|
|
1996
|
+
* const labelMap = buildLabelMap(contracts.usdt.metadata);
|
|
1997
|
+
* const info = labelMap.get("PSP22::transfer");
|
|
1998
|
+
* console.log(info?.selectorHex); // "db20f9f5"
|
|
1999
|
+
* ```
|
|
2000
|
+
*/
|
|
2001
|
+
function buildLabelMap(metadata) {
|
|
2002
|
+
const map$1 = /* @__PURE__ */ new Map();
|
|
2003
|
+
const selectorMap = buildSelectorMap(metadata);
|
|
2004
|
+
for (const info of selectorMap.values()) map$1.set(info.label, info);
|
|
2005
|
+
return map$1;
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Get selector for a message label
|
|
2009
|
+
*
|
|
2010
|
+
* @param metadata - Contract metadata
|
|
2011
|
+
* @param label - Message label (e.g., "PSP22::transfer")
|
|
2012
|
+
* @returns Selector info or null if not found
|
|
2013
|
+
*
|
|
2014
|
+
* @example
|
|
2015
|
+
* ```ts
|
|
2016
|
+
* const info = getSelectorForLabel(contracts.usdt.metadata, "PSP22::transfer");
|
|
2017
|
+
* console.log(info?.selectorHex); // "db20f9f5"
|
|
2018
|
+
* ```
|
|
2019
|
+
*/
|
|
2020
|
+
function getSelectorForLabel(metadata, label) {
|
|
2021
|
+
return buildLabelMap(metadata).get(label) ?? null;
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Get message label for a selector
|
|
2025
|
+
*
|
|
2026
|
+
* @param metadata - Contract metadata
|
|
2027
|
+
* @param selector - Selector as Uint8Array or hex string (with or without 0x)
|
|
2028
|
+
* @returns Selector info or null if not found
|
|
2029
|
+
*
|
|
2030
|
+
* @example
|
|
2031
|
+
* ```ts
|
|
2032
|
+
* const info = getLabelForSelector(contracts.usdt.metadata, "db20f9f5");
|
|
2033
|
+
* console.log(info?.label); // "PSP22::transfer"
|
|
2034
|
+
*
|
|
2035
|
+
* // Also works with Uint8Array
|
|
2036
|
+
* const info2 = getLabelForSelector(contracts.usdt.metadata, callData.slice(0, 4));
|
|
2037
|
+
* ```
|
|
2038
|
+
*/
|
|
2039
|
+
function getLabelForSelector(metadata, selector) {
|
|
2040
|
+
const selectorHex = normalizeSelector(selector);
|
|
2041
|
+
return buildSelectorMap(metadata).get(selectorHex) ?? null;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Check if call data matches a specific message
|
|
2045
|
+
*
|
|
2046
|
+
* @param metadata - Contract metadata
|
|
2047
|
+
* @param callData - Call data bytes or hex string
|
|
2048
|
+
* @param label - Message label to check
|
|
2049
|
+
* @returns True if the call data selector matches the label
|
|
2050
|
+
*
|
|
2051
|
+
* @example
|
|
2052
|
+
* ```ts
|
|
2053
|
+
* if (isMessageCall(contracts.usdt.metadata, callData, "PSP22::transfer")) {
|
|
2054
|
+
* // This is a transfer call
|
|
2055
|
+
* }
|
|
2056
|
+
* ```
|
|
2057
|
+
*/
|
|
2058
|
+
function isMessageCall(metadata, callData, label) {
|
|
2059
|
+
const bytes = typeof callData === "string" ? hexToBytes(callData) : callData;
|
|
2060
|
+
if (bytes.length < 4) return false;
|
|
2061
|
+
const callSelectorHex = bytesToHex(bytes.slice(0, 4));
|
|
2062
|
+
const info = getSelectorForLabel(metadata, label);
|
|
2063
|
+
return info !== null && info.selectorHex === callSelectorHex;
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Check if call data matches a PSP22 method
|
|
2067
|
+
*
|
|
2068
|
+
* @param callData - Call data bytes or hex string
|
|
2069
|
+
* @param method - PSP22 method name (e.g., "transfer", "transferFrom")
|
|
2070
|
+
* @returns True if the call data selector matches the PSP22 method
|
|
2071
|
+
*
|
|
2072
|
+
* @example
|
|
2073
|
+
* ```ts
|
|
2074
|
+
* if (isPSP22Call(callData, "transfer")) {
|
|
2075
|
+
* // This is a PSP22::transfer call
|
|
2076
|
+
* }
|
|
2077
|
+
* ```
|
|
2078
|
+
*/
|
|
2079
|
+
function isPSP22Call(callData, method) {
|
|
2080
|
+
const bytes = typeof callData === "string" ? hexToBytes(callData) : callData;
|
|
2081
|
+
if (bytes.length < 4) return false;
|
|
2082
|
+
return bytesToHex(bytes.slice(0, 4)) === PSP22_SELECTORS[method];
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Get all selectors from contract metadata
|
|
2086
|
+
*
|
|
2087
|
+
* @param metadata - Contract metadata
|
|
2088
|
+
* @returns Array of SelectorInfo for all messages
|
|
2089
|
+
*/
|
|
2090
|
+
function getAllSelectors(metadata) {
|
|
2091
|
+
const selectorMap = buildSelectorMap(metadata);
|
|
2092
|
+
return Array.from(selectorMap.values());
|
|
2093
|
+
}
|
|
2094
|
+
function normalizeSelector(selector) {
|
|
2095
|
+
if (selector instanceof Uint8Array) return bytesToHex(selector);
|
|
2096
|
+
return selector.startsWith("0x") ? selector.slice(2).toLowerCase() : selector.toLowerCase();
|
|
2097
|
+
}
|
|
2098
|
+
function hexToBytes(hex) {
|
|
2099
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
2100
|
+
const bytes = new Uint8Array(clean.length / 2);
|
|
2101
|
+
for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
2102
|
+
return bytes;
|
|
2103
|
+
}
|
|
2104
|
+
function bytesToHex(bytes) {
|
|
2105
|
+
let hex = "";
|
|
2106
|
+
for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
|
|
2107
|
+
return hex;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
//#endregion
|
|
2111
|
+
//#region src/extrinsic-parser.ts
|
|
2112
|
+
/**
|
|
2113
|
+
* Parser for extracting contract calls from extrinsics
|
|
2114
|
+
*
|
|
2115
|
+
* This class provides a high-level API for indexers to parse contract
|
|
2116
|
+
* interactions from blockchain extrinsics without manual hex manipulation.
|
|
2117
|
+
*
|
|
2118
|
+
* @typeParam S - The storage descriptor type
|
|
2119
|
+
* @typeParam M - The InkCallableDescriptor type (message definitions)
|
|
2120
|
+
* @typeParam C - The constructors descriptor type
|
|
2121
|
+
* @typeParam E - The events type
|
|
2122
|
+
*
|
|
2123
|
+
* @example
|
|
2124
|
+
* ```ts
|
|
2125
|
+
* import { ExtrinsicParser } from "@d9-network/ink";
|
|
2126
|
+
* import { contracts } from "@d9-network/spec";
|
|
2127
|
+
*
|
|
2128
|
+
* // Create parser for USDT contract
|
|
2129
|
+
* const parser = new ExtrinsicParser(contracts.usdt, {
|
|
2130
|
+
* contractAddresses: [USDT_ADDRESS],
|
|
2131
|
+
* });
|
|
2132
|
+
*
|
|
2133
|
+
* // Parse extrinsics from a block
|
|
2134
|
+
* for (const extrinsic of block.extrinsics) {
|
|
2135
|
+
* const parsed = parser.parseExtrinsic(extrinsic);
|
|
2136
|
+
* if (parsed?.call.type === "PSP22::transfer") {
|
|
2137
|
+
* console.log(`Transfer: ${parsed.call.args.value} to ${parsed.call.args.to}`);
|
|
2138
|
+
* }
|
|
2139
|
+
* }
|
|
2140
|
+
* ```
|
|
2141
|
+
*/
|
|
2142
|
+
var ExtrinsicParser = class {
|
|
2143
|
+
callParser;
|
|
2144
|
+
options;
|
|
2145
|
+
constructor(descriptor, options = {}) {
|
|
2146
|
+
this.callParser = new ContractCallParser(descriptor);
|
|
2147
|
+
this.options = options;
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Parse a single extrinsic for contract calls
|
|
2151
|
+
*
|
|
2152
|
+
* @param extrinsic - Raw extrinsic data
|
|
2153
|
+
* @returns Parsed contract extrinsic or null if not a valid contract call
|
|
2154
|
+
*/
|
|
2155
|
+
parseExtrinsic(extrinsic) {
|
|
2156
|
+
if (!this.isContractCall(extrinsic)) return null;
|
|
2157
|
+
const extracted = this.extractCallData(extrinsic);
|
|
2158
|
+
if (!extracted) return null;
|
|
2159
|
+
const { data, contractAddress, value } = extracted;
|
|
2160
|
+
if (this.options.contractAddresses && this.options.contractAddresses.length > 0 && !this.options.contractAddresses.includes(contractAddress)) return null;
|
|
2161
|
+
const raw = {
|
|
2162
|
+
data,
|
|
2163
|
+
contractAddress,
|
|
2164
|
+
txHash: extrinsic.hash,
|
|
2165
|
+
blockNumber: extrinsic.blockNumber,
|
|
2166
|
+
blockHash: extrinsic.blockHash
|
|
2167
|
+
};
|
|
2168
|
+
const call = this.callParser.parseCall(raw);
|
|
2169
|
+
if (!call) return null;
|
|
2170
|
+
if (this.options.messageLabels && this.options.messageLabels.length > 0 && !this.options.messageLabels.includes(call.type)) return null;
|
|
2171
|
+
return {
|
|
2172
|
+
call,
|
|
2173
|
+
extrinsic: {
|
|
2174
|
+
hash: extrinsic.hash ?? "",
|
|
2175
|
+
blockNumber: extrinsic.blockNumber ?? 0,
|
|
2176
|
+
blockHash: extrinsic.blockHash ?? "",
|
|
2177
|
+
index: extrinsic.index ?? 0,
|
|
2178
|
+
signer: String(extrinsic.signer ?? "")
|
|
2179
|
+
},
|
|
2180
|
+
contractAddress,
|
|
2181
|
+
value
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* Parse multiple extrinsics and filter contract calls
|
|
2186
|
+
*
|
|
2187
|
+
* @param extrinsics - Array of raw extrinsics
|
|
2188
|
+
* @returns Array of parsed contract extrinsics
|
|
2189
|
+
*/
|
|
2190
|
+
parseExtrinsics(extrinsics) {
|
|
2191
|
+
const results = [];
|
|
2192
|
+
for (const extrinsic of extrinsics) {
|
|
2193
|
+
const parsed = this.parseExtrinsic(extrinsic);
|
|
2194
|
+
if (parsed) results.push(parsed);
|
|
2195
|
+
}
|
|
2196
|
+
return results;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Filter extrinsics by message type with type narrowing
|
|
2200
|
+
*
|
|
2201
|
+
* @param extrinsics - Array of raw extrinsics
|
|
2202
|
+
* @param label - Message label to filter by
|
|
2203
|
+
* @returns Filtered and typed extrinsics
|
|
2204
|
+
*
|
|
2205
|
+
* @example
|
|
2206
|
+
* ```ts
|
|
2207
|
+
* const transfers = parser.filterByMessageType(extrinsics, "PSP22::transfer");
|
|
2208
|
+
* for (const tx of transfers) {
|
|
2209
|
+
* // tx.call.args is fully typed
|
|
2210
|
+
* console.log(tx.call.args.to, tx.call.args.value);
|
|
2211
|
+
* }
|
|
2212
|
+
* ```
|
|
2213
|
+
*/
|
|
2214
|
+
filterByMessageType(extrinsics, label) {
|
|
2215
|
+
return this.parseExtrinsics(extrinsics).filter((tx) => tx.call.type === label);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Check if an extrinsic is a contract call
|
|
2219
|
+
*
|
|
2220
|
+
* @param extrinsic - Raw extrinsic
|
|
2221
|
+
* @returns True if this is a Contracts.call extrinsic
|
|
2222
|
+
*/
|
|
2223
|
+
isContractCall(extrinsic) {
|
|
2224
|
+
const call = extrinsic.call;
|
|
2225
|
+
if (!call) return false;
|
|
2226
|
+
if (call.type === "Contracts") {
|
|
2227
|
+
const innerCall = call.value;
|
|
2228
|
+
return innerCall?.type === "call" || innerCall?.type === "call_old_weight" || innerCall?.type === "instantiate_with_code" || innerCall?.type === "instantiate";
|
|
2229
|
+
}
|
|
2230
|
+
return false;
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* Extract call data from an extrinsic structure
|
|
2234
|
+
*
|
|
2235
|
+
* Handles various extrinsic formats (polkadot-api, subsquid, etc.)
|
|
2236
|
+
*
|
|
2237
|
+
* @param extrinsic - Raw extrinsic
|
|
2238
|
+
* @returns Extracted call data or null
|
|
2239
|
+
*/
|
|
2240
|
+
extractCallData(extrinsic) {
|
|
2241
|
+
const call = extrinsic.call;
|
|
2242
|
+
if (!call?.value?.value) return null;
|
|
2243
|
+
const contractsCall = call.value.value;
|
|
2244
|
+
const contractAddress = this.extractContractAddress(contractsCall.dest);
|
|
2245
|
+
if (!contractAddress) return null;
|
|
2246
|
+
const data = this.extractBytes(contractsCall.data);
|
|
2247
|
+
if (!data || data.length < 4) return null;
|
|
2248
|
+
return {
|
|
2249
|
+
data,
|
|
2250
|
+
contractAddress,
|
|
2251
|
+
value: this.extractBigInt(contractsCall.value)
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Get access to the underlying call parser
|
|
2256
|
+
*/
|
|
2257
|
+
getCallParser() {
|
|
2258
|
+
return this.callParser;
|
|
2259
|
+
}
|
|
2260
|
+
extractContractAddress(dest) {
|
|
2261
|
+
if (!dest) return null;
|
|
2262
|
+
if (typeof dest === "string") return dest;
|
|
2263
|
+
if (typeof dest === "object" && "type" in dest && "value" in dest) return dest.value;
|
|
2264
|
+
if (typeof dest === "object" && "Id" in dest) return dest.Id;
|
|
2265
|
+
return null;
|
|
2266
|
+
}
|
|
2267
|
+
extractBytes(data) {
|
|
2268
|
+
if (!data) return null;
|
|
2269
|
+
if (data instanceof Uint8Array) return data;
|
|
2270
|
+
if (typeof data === "object" && "asBytes" in data && typeof data.asBytes === "function") return data.asBytes();
|
|
2271
|
+
return null;
|
|
2272
|
+
}
|
|
2273
|
+
extractBigInt(value) {
|
|
2274
|
+
if (value === void 0 || value === null) return 0n;
|
|
2275
|
+
if (typeof value === "bigint") return value;
|
|
2276
|
+
if (typeof value === "number") return BigInt(value);
|
|
2277
|
+
if (typeof value === "string") try {
|
|
2278
|
+
return BigInt(value);
|
|
2279
|
+
} catch {
|
|
2280
|
+
return 0n;
|
|
2281
|
+
}
|
|
2282
|
+
return 0n;
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
/**
|
|
2286
|
+
* Create an extrinsic parser from a contract descriptor
|
|
2287
|
+
*
|
|
2288
|
+
* @param descriptor - Contract descriptor
|
|
2289
|
+
* @param options - Parser options
|
|
2290
|
+
* @returns ExtrinsicParser instance
|
|
2291
|
+
*
|
|
2292
|
+
* @example
|
|
2293
|
+
* ```ts
|
|
2294
|
+
* const parser = createExtrinsicParser(contracts.usdt, {
|
|
2295
|
+
* contractAddresses: [USDT_ADDRESS],
|
|
2296
|
+
* });
|
|
2297
|
+
* ```
|
|
2298
|
+
*/
|
|
2299
|
+
function createExtrinsicParser(descriptor, options) {
|
|
2300
|
+
return new ExtrinsicParser(descriptor, options);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
1706
2303
|
//#endregion
|
|
1707
2304
|
//#region src/utils/fees.ts
|
|
1708
2305
|
const WEIGHT_REF_TIME_PER_SECOND = 1000000000000n;
|
|
@@ -1857,6 +2454,7 @@ Object.defineProperty(exports, 'EncodeError', {
|
|
|
1857
2454
|
return _d9_network_spec.EncodeError;
|
|
1858
2455
|
}
|
|
1859
2456
|
});
|
|
2457
|
+
exports.ExtrinsicParser = ExtrinsicParser;
|
|
1860
2458
|
exports.InkCodecs = InkCodecs;
|
|
1861
2459
|
Object.defineProperty(exports, 'LangError', {
|
|
1862
2460
|
enumerable: true,
|
|
@@ -1876,6 +2474,7 @@ Object.defineProperty(exports, 'NetworkError', {
|
|
|
1876
2474
|
return _d9_network_spec.NetworkError;
|
|
1877
2475
|
}
|
|
1878
2476
|
});
|
|
2477
|
+
exports.PSP22_SELECTORS = PSP22_SELECTORS;
|
|
1879
2478
|
Object.defineProperty(exports, 'SignerError', {
|
|
1880
2479
|
enumerable: true,
|
|
1881
2480
|
get: function () {
|
|
@@ -1898,13 +2497,16 @@ exports.applyGasMargin = applyGasMargin;
|
|
|
1898
2497
|
exports.buildAllEventDecoders = buildAllEventDecoders;
|
|
1899
2498
|
exports.buildAllMessageDecoders = buildAllMessageDecoders;
|
|
1900
2499
|
exports.buildEventDecoder = buildEventDecoder;
|
|
2500
|
+
exports.buildLabelMap = buildLabelMap;
|
|
1901
2501
|
exports.buildMessageDecoder = buildMessageDecoder;
|
|
2502
|
+
exports.buildSelectorMap = buildSelectorMap;
|
|
1902
2503
|
exports.compareGasWeight = compareGasWeight;
|
|
1903
2504
|
exports.createAsciiEventTopic = createAsciiEventTopic;
|
|
1904
2505
|
exports.createCodecRegistry = createCodecRegistry;
|
|
1905
2506
|
exports.createContractEventStream = createContractEventStream;
|
|
1906
2507
|
exports.createD9InkContract = createD9InkContract;
|
|
1907
2508
|
exports.createD9InkSdk = createD9InkSdk;
|
|
2509
|
+
exports.createExtrinsicParser = createExtrinsicParser;
|
|
1908
2510
|
exports.createMessageBuilder = createMessageBuilder;
|
|
1909
2511
|
exports.createNativeTransferStream = createNativeTransferStream;
|
|
1910
2512
|
exports.createPSP22TransferStream = createPSP22TransferStream;
|
|
@@ -1915,10 +2517,18 @@ exports.encodeCall = encodeCall;
|
|
|
1915
2517
|
exports.encodeContractCall = encodeContractCall;
|
|
1916
2518
|
exports.encodeContractCallWithLimits = encodeContractCallWithLimits;
|
|
1917
2519
|
exports.estimateTransactionCost = estimateTransactionCost;
|
|
2520
|
+
exports.extractContractEmittedData = extractContractEmittedData;
|
|
1918
2521
|
exports.formatGasInfo = formatGasInfo;
|
|
1919
2522
|
exports.gasExceedsLimit = gasExceedsLimit;
|
|
2523
|
+
exports.getAllSelectors = getAllSelectors;
|
|
1920
2524
|
exports.getEventSignature = getEventSignature;
|
|
2525
|
+
exports.getLabelForSelector = getLabelForSelector;
|
|
2526
|
+
exports.getSelectorForLabel = getSelectorForLabel;
|
|
2527
|
+
exports.isApplyExtrinsicPhase = isApplyExtrinsicPhase;
|
|
2528
|
+
exports.isBalancesTransferEvent = isBalancesTransferEvent;
|
|
1921
2529
|
exports.isCallType = isCallType;
|
|
2530
|
+
exports.isContractCalledEvent = isContractCalledEvent;
|
|
2531
|
+
exports.isContractEmittedEvent = isContractEmittedEvent;
|
|
1922
2532
|
Object.defineProperty(exports, 'isContractError', {
|
|
1923
2533
|
enumerable: true,
|
|
1924
2534
|
get: function () {
|
|
@@ -1933,5 +2543,7 @@ Object.defineProperty(exports, 'isErrorType', {
|
|
|
1933
2543
|
});
|
|
1934
2544
|
exports.isEventType = isEventType;
|
|
1935
2545
|
exports.isLangError = isLangError;
|
|
2546
|
+
exports.isMessageCall = isMessageCall;
|
|
2547
|
+
exports.isPSP22Call = isPSP22Call;
|
|
1936
2548
|
exports.unwrapInkResult = unwrapInkResult;
|
|
1937
2549
|
//# sourceMappingURL=index.cjs.map
|