@sip-protocol/sdk 0.7.1 → 0.7.3
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/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2926 -341
- package/dist/browser.mjs +48 -2
- package/dist/chunk-2XIVXWHA.mjs +1930 -0
- package/dist/chunk-3M3HNQCW.mjs +18253 -0
- package/dist/chunk-7RFRWDCW.mjs +1504 -0
- package/dist/chunk-F6F73W35.mjs +16166 -0
- package/dist/chunk-OFDBEIEK.mjs +16166 -0
- package/dist/chunk-SF7YSLF5.mjs +1515 -0
- package/dist/chunk-WWUSGOXE.mjs +17129 -0
- package/dist/index-8MQz13eJ.d.mts +13746 -0
- package/dist/index-B71aXVzk.d.ts +13264 -0
- package/dist/index-DIBZHOOQ.d.ts +13746 -0
- package/dist/index-pOIIuwfV.d.mts +13264 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2911 -326
- package/dist/index.mjs +48 -2
- package/dist/solana-4O4K45VU.mjs +46 -0
- package/dist/solana-NDABAZ6P.mjs +56 -0
- package/dist/solana-ZYO63LY5.mjs +46 -0
- package/package.json +3 -3
- package/src/chains/solana/index.ts +24 -0
- package/src/chains/solana/providers/generic.ts +160 -0
- package/src/chains/solana/providers/helius.ts +249 -0
- package/src/chains/solana/providers/index.ts +54 -0
- package/src/chains/solana/providers/interface.ts +178 -0
- package/src/chains/solana/providers/webhook.ts +519 -0
- package/src/chains/solana/scan.ts +88 -8
- package/src/chains/solana/types.ts +20 -1
- package/src/compliance/index.ts +14 -0
- package/src/compliance/range-sas.ts +591 -0
- package/src/index.ts +99 -0
- package/src/privacy-backends/index.ts +86 -0
- package/src/privacy-backends/interface.ts +263 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +460 -0
- package/src/privacy-backends/registry.ts +278 -0
- package/src/privacy-backends/router.ts +346 -0
- package/src/privacy-backends/sip-native.ts +253 -0
- package/src/proofs/noir.ts +1 -1
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
package/dist/index.js
CHANGED
|
@@ -1552,7 +1552,8 @@ async function scanForPayments(params) {
|
|
|
1552
1552
|
spendingPublicKey,
|
|
1553
1553
|
fromSlot,
|
|
1554
1554
|
toSlot,
|
|
1555
|
-
limit = 100
|
|
1555
|
+
limit = 100,
|
|
1556
|
+
provider
|
|
1556
1557
|
} = params;
|
|
1557
1558
|
const results = [];
|
|
1558
1559
|
const memoProgram = new import_web32.PublicKey(MEMO_PROGRAM_ID);
|
|
@@ -1596,12 +1597,26 @@ async function scanForPayments(params) {
|
|
|
1596
1597
|
if (isOurs) {
|
|
1597
1598
|
const transferInfo = parseTokenTransfer(tx);
|
|
1598
1599
|
if (transferInfo) {
|
|
1600
|
+
let amount = transferInfo.amount;
|
|
1601
|
+
const tokenSymbol = getTokenSymbol(transferInfo.mint);
|
|
1602
|
+
if (provider && announcement.stealthAddress) {
|
|
1603
|
+
try {
|
|
1604
|
+
const balance = await provider.getTokenBalance(
|
|
1605
|
+
announcement.stealthAddress,
|
|
1606
|
+
transferInfo.mint
|
|
1607
|
+
);
|
|
1608
|
+
if (balance > 0n) {
|
|
1609
|
+
amount = balance;
|
|
1610
|
+
}
|
|
1611
|
+
} catch {
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1599
1614
|
results.push({
|
|
1600
1615
|
stealthAddress: announcement.stealthAddress || "",
|
|
1601
1616
|
ephemeralPublicKey: announcement.ephemeralPublicKey,
|
|
1602
|
-
amount
|
|
1617
|
+
amount,
|
|
1603
1618
|
mint: transferInfo.mint,
|
|
1604
|
-
tokenSymbol
|
|
1619
|
+
tokenSymbol,
|
|
1605
1620
|
txSignature: sigInfo.signature,
|
|
1606
1621
|
slot: sigInfo.slot,
|
|
1607
1622
|
timestamp: sigInfo.blockTime || 0
|
|
@@ -1644,8 +1659,19 @@ async function claimStealthPayment(params) {
|
|
|
1644
1659
|
);
|
|
1645
1660
|
const stealthPrivKeyBytes = (0, import_utils9.hexToBytes)(recovery.privateKey.slice(2));
|
|
1646
1661
|
const stealthPubkey = new import_web32.PublicKey(stealthAddress);
|
|
1662
|
+
const expectedPubKeyBytes = stealthPubkey.toBytes();
|
|
1663
|
+
const scalarBigInt = bytesToBigIntLE2(stealthPrivKeyBytes);
|
|
1664
|
+
const ED25519_ORDER2 = 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
1665
|
+
let validScalar = scalarBigInt % ED25519_ORDER2;
|
|
1666
|
+
if (validScalar === 0n) validScalar = 1n;
|
|
1667
|
+
const derivedPubKeyBytes = import_ed255192.ed25519.ExtendedPoint.BASE.multiply(validScalar).toRawBytes();
|
|
1668
|
+
if (!derivedPubKeyBytes.every((b, i) => b === expectedPubKeyBytes[i])) {
|
|
1669
|
+
throw new Error(
|
|
1670
|
+
"Stealth key derivation failed: derived private key does not produce expected public key. This may indicate incorrect spending/viewing keys or corrupted announcement data."
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1647
1673
|
const stealthKeypair = import_web32.Keypair.fromSecretKey(
|
|
1648
|
-
new Uint8Array([...stealthPrivKeyBytes, ...
|
|
1674
|
+
new Uint8Array([...stealthPrivKeyBytes, ...expectedPubKeyBytes])
|
|
1649
1675
|
);
|
|
1650
1676
|
const stealthATA = await (0, import_spl_token2.getAssociatedTokenAddress)(
|
|
1651
1677
|
mint,
|
|
@@ -1696,7 +1722,13 @@ async function claimStealthPayment(params) {
|
|
|
1696
1722
|
explorerUrl: getExplorerUrl(txSignature, cluster)
|
|
1697
1723
|
};
|
|
1698
1724
|
}
|
|
1699
|
-
async function getStealthBalance(connection, stealthAddress, mint) {
|
|
1725
|
+
async function getStealthBalance(connection, stealthAddress, mint, provider) {
|
|
1726
|
+
if (provider) {
|
|
1727
|
+
try {
|
|
1728
|
+
return await provider.getTokenBalance(stealthAddress, mint.toBase58());
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1700
1732
|
try {
|
|
1701
1733
|
const stealthPubkey = new import_web32.PublicKey(stealthAddress);
|
|
1702
1734
|
const ata = await (0, import_spl_token2.getAssociatedTokenAddress)(mint, stealthPubkey, true);
|
|
@@ -1746,7 +1778,14 @@ function detectCluster2(endpoint) {
|
|
|
1746
1778
|
}
|
|
1747
1779
|
return "mainnet-beta";
|
|
1748
1780
|
}
|
|
1749
|
-
|
|
1781
|
+
function bytesToBigIntLE2(bytes) {
|
|
1782
|
+
let result = 0n;
|
|
1783
|
+
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
1784
|
+
result = result << 8n | BigInt(bytes[i]);
|
|
1785
|
+
}
|
|
1786
|
+
return result;
|
|
1787
|
+
}
|
|
1788
|
+
var import_web32, import_spl_token2, import_utils9, import_ed255192;
|
|
1750
1789
|
var init_scan = __esm({
|
|
1751
1790
|
"src/chains/solana/scan.ts"() {
|
|
1752
1791
|
"use strict";
|
|
@@ -1756,6 +1795,428 @@ var init_scan = __esm({
|
|
|
1756
1795
|
init_types();
|
|
1757
1796
|
init_constants();
|
|
1758
1797
|
import_utils9 = require("@noble/hashes/utils");
|
|
1798
|
+
import_ed255192 = require("@noble/curves/ed25519");
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
|
|
1802
|
+
// src/chains/solana/providers/helius.ts
|
|
1803
|
+
var HeliusProvider;
|
|
1804
|
+
var init_helius = __esm({
|
|
1805
|
+
"src/chains/solana/providers/helius.ts"() {
|
|
1806
|
+
"use strict";
|
|
1807
|
+
HeliusProvider = class {
|
|
1808
|
+
name = "helius";
|
|
1809
|
+
apiKey;
|
|
1810
|
+
cluster;
|
|
1811
|
+
rpcUrl;
|
|
1812
|
+
restUrl;
|
|
1813
|
+
constructor(config) {
|
|
1814
|
+
if (!config.apiKey) {
|
|
1815
|
+
throw new Error("Helius API key is required. Get one at https://dev.helius.xyz");
|
|
1816
|
+
}
|
|
1817
|
+
this.apiKey = config.apiKey;
|
|
1818
|
+
this.cluster = config.cluster ?? "mainnet-beta";
|
|
1819
|
+
this.rpcUrl = this.cluster === "devnet" ? `https://devnet.helius-rpc.com/?api-key=${this.apiKey}` : `https://mainnet.helius-rpc.com/?api-key=${this.apiKey}`;
|
|
1820
|
+
this.restUrl = this.cluster === "devnet" ? `https://api-devnet.helius.xyz/v0` : `https://api.helius.xyz/v0`;
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Get all token assets owned by an address using DAS API
|
|
1824
|
+
*
|
|
1825
|
+
* Uses getAssetsByOwner for comprehensive asset information including
|
|
1826
|
+
* NFTs and fungible tokens with metadata.
|
|
1827
|
+
*/
|
|
1828
|
+
async getAssetsByOwner(owner) {
|
|
1829
|
+
const assets = [];
|
|
1830
|
+
let page = 1;
|
|
1831
|
+
const limit = 1e3;
|
|
1832
|
+
let hasMore = true;
|
|
1833
|
+
while (hasMore) {
|
|
1834
|
+
const response = await fetch(this.rpcUrl, {
|
|
1835
|
+
method: "POST",
|
|
1836
|
+
headers: { "Content-Type": "application/json" },
|
|
1837
|
+
body: JSON.stringify({
|
|
1838
|
+
jsonrpc: "2.0",
|
|
1839
|
+
id: `sip-${Date.now()}`,
|
|
1840
|
+
method: "getAssetsByOwner",
|
|
1841
|
+
params: {
|
|
1842
|
+
ownerAddress: owner,
|
|
1843
|
+
page,
|
|
1844
|
+
limit,
|
|
1845
|
+
displayOptions: {
|
|
1846
|
+
showFungible: true,
|
|
1847
|
+
showNativeBalance: false
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
})
|
|
1851
|
+
});
|
|
1852
|
+
if (!response.ok) {
|
|
1853
|
+
throw new Error(`Helius API error: ${response.status} ${response.statusText}`);
|
|
1854
|
+
}
|
|
1855
|
+
const data = await response.json();
|
|
1856
|
+
if (data.error) {
|
|
1857
|
+
throw new Error(`Helius RPC error: ${data.error.message} (code: ${data.error.code})`);
|
|
1858
|
+
}
|
|
1859
|
+
if (data.result?.items) {
|
|
1860
|
+
for (const item of data.result.items) {
|
|
1861
|
+
if (item.interface !== "FungibleToken" && item.interface !== "FungibleAsset") {
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
const tokenInfo = item.token_info;
|
|
1865
|
+
if (!tokenInfo?.balance) continue;
|
|
1866
|
+
const balanceValue = typeof tokenInfo.balance === "string" ? BigInt(tokenInfo.balance) : BigInt(Math.floor(tokenInfo.balance));
|
|
1867
|
+
assets.push({
|
|
1868
|
+
mint: item.id,
|
|
1869
|
+
amount: balanceValue,
|
|
1870
|
+
decimals: tokenInfo.decimals ?? 0,
|
|
1871
|
+
symbol: tokenInfo.symbol ?? item.content?.metadata?.symbol,
|
|
1872
|
+
name: item.content?.metadata?.name,
|
|
1873
|
+
logoUri: item.content?.links?.image
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
hasMore = data.result?.items?.length === limit;
|
|
1878
|
+
page++;
|
|
1879
|
+
if (page > 100) {
|
|
1880
|
+
console.warn("[HeliusProvider] Reached page limit (100), stopping pagination");
|
|
1881
|
+
break;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
return assets;
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Get token balance for a specific mint using Balances API
|
|
1888
|
+
*
|
|
1889
|
+
* More efficient than getAssetsByOwner when you only need one token's balance.
|
|
1890
|
+
*/
|
|
1891
|
+
async getTokenBalance(owner, mint) {
|
|
1892
|
+
try {
|
|
1893
|
+
const url = `${this.restUrl}/addresses/${owner}/balances?api-key=${this.apiKey}`;
|
|
1894
|
+
const response = await fetch(url);
|
|
1895
|
+
if (!response.ok) {
|
|
1896
|
+
const assets = await this.getAssetsByOwner(owner);
|
|
1897
|
+
const asset = assets.find((a) => a.mint === mint);
|
|
1898
|
+
return asset?.amount ?? 0n;
|
|
1899
|
+
}
|
|
1900
|
+
const data = await response.json();
|
|
1901
|
+
const token = data.tokens?.find((t) => t.mint === mint);
|
|
1902
|
+
return token ? BigInt(token.amount) : 0n;
|
|
1903
|
+
} catch (error) {
|
|
1904
|
+
console.warn("[HeliusProvider] getTokenBalance error, falling back to DAS:", error);
|
|
1905
|
+
const assets = await this.getAssetsByOwner(owner);
|
|
1906
|
+
const asset = assets.find((a) => a.mint === mint);
|
|
1907
|
+
return asset?.amount ?? 0n;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* Check if provider supports real-time subscriptions
|
|
1912
|
+
*
|
|
1913
|
+
* Helius supports webhooks for real-time notifications,
|
|
1914
|
+
* but that requires server-side setup. Client-side subscriptions
|
|
1915
|
+
* are not directly supported.
|
|
1916
|
+
*/
|
|
1917
|
+
supportsSubscriptions() {
|
|
1918
|
+
return false;
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1924
|
+
// src/chains/solana/providers/generic.ts
|
|
1925
|
+
function validateSolanaAddress(address, paramName) {
|
|
1926
|
+
try {
|
|
1927
|
+
return new import_web33.PublicKey(address);
|
|
1928
|
+
} catch {
|
|
1929
|
+
throw new Error(`Invalid Solana address for ${paramName}: ${address}`);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
var import_web33, import_spl_token3, CLUSTER_ENDPOINTS, GenericProvider;
|
|
1933
|
+
var init_generic = __esm({
|
|
1934
|
+
"src/chains/solana/providers/generic.ts"() {
|
|
1935
|
+
"use strict";
|
|
1936
|
+
import_web33 = require("@solana/web3.js");
|
|
1937
|
+
import_spl_token3 = require("@solana/spl-token");
|
|
1938
|
+
CLUSTER_ENDPOINTS = {
|
|
1939
|
+
"mainnet-beta": "https://api.mainnet-beta.solana.com",
|
|
1940
|
+
devnet: "https://api.devnet.solana.com",
|
|
1941
|
+
testnet: "https://api.testnet.solana.com"
|
|
1942
|
+
};
|
|
1943
|
+
GenericProvider = class {
|
|
1944
|
+
name = "generic";
|
|
1945
|
+
connection;
|
|
1946
|
+
constructor(config) {
|
|
1947
|
+
if (config.connection) {
|
|
1948
|
+
this.connection = config.connection;
|
|
1949
|
+
} else {
|
|
1950
|
+
const endpoint = config.endpoint ?? CLUSTER_ENDPOINTS[config.cluster ?? "mainnet-beta"];
|
|
1951
|
+
this.connection = new import_web33.Connection(endpoint, "confirmed");
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Get all token assets owned by an address using getParsedTokenAccountsByOwner
|
|
1956
|
+
*
|
|
1957
|
+
* Note: This is less efficient than Helius DAS API for large wallets,
|
|
1958
|
+
* but works with any RPC endpoint.
|
|
1959
|
+
*/
|
|
1960
|
+
async getAssetsByOwner(owner) {
|
|
1961
|
+
const ownerPubkey = validateSolanaAddress(owner, "owner");
|
|
1962
|
+
const accounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
1963
|
+
ownerPubkey,
|
|
1964
|
+
{ programId: import_spl_token3.TOKEN_PROGRAM_ID }
|
|
1965
|
+
);
|
|
1966
|
+
const assets = [];
|
|
1967
|
+
for (const { account } of accounts.value) {
|
|
1968
|
+
const parsed = account.data.parsed;
|
|
1969
|
+
if (parsed.type !== "account") continue;
|
|
1970
|
+
const info = parsed.info;
|
|
1971
|
+
const amount = BigInt(info.tokenAmount.amount);
|
|
1972
|
+
if (amount === 0n) continue;
|
|
1973
|
+
assets.push({
|
|
1974
|
+
mint: info.mint,
|
|
1975
|
+
amount,
|
|
1976
|
+
decimals: info.tokenAmount.decimals,
|
|
1977
|
+
// Generic RPC doesn't provide symbol/name, those need metadata lookup
|
|
1978
|
+
symbol: void 0,
|
|
1979
|
+
name: void 0,
|
|
1980
|
+
logoUri: void 0
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
return assets;
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Get token balance for a specific mint
|
|
1987
|
+
*
|
|
1988
|
+
* Uses getAccount on the associated token address.
|
|
1989
|
+
*/
|
|
1990
|
+
async getTokenBalance(owner, mint) {
|
|
1991
|
+
const ownerPubkey = validateSolanaAddress(owner, "owner");
|
|
1992
|
+
const mintPubkey = validateSolanaAddress(mint, "mint");
|
|
1993
|
+
try {
|
|
1994
|
+
const ata = await (0, import_spl_token3.getAssociatedTokenAddress)(
|
|
1995
|
+
mintPubkey,
|
|
1996
|
+
ownerPubkey,
|
|
1997
|
+
true
|
|
1998
|
+
// allowOwnerOffCurve for PDAs
|
|
1999
|
+
);
|
|
2000
|
+
const account = await (0, import_spl_token3.getAccount)(this.connection, ata);
|
|
2001
|
+
return account.amount;
|
|
2002
|
+
} catch {
|
|
2003
|
+
return 0n;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Check if provider supports real-time subscriptions
|
|
2008
|
+
*
|
|
2009
|
+
* Generic RPC supports WebSocket subscriptions but they're not
|
|
2010
|
+
* efficient for monitoring token transfers. Returns false.
|
|
2011
|
+
*/
|
|
2012
|
+
supportsSubscriptions() {
|
|
2013
|
+
return false;
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Get the underlying Connection object
|
|
2017
|
+
*
|
|
2018
|
+
* Useful for advanced operations that need direct RPC access.
|
|
2019
|
+
*/
|
|
2020
|
+
getConnection() {
|
|
2021
|
+
return this.connection;
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
|
|
2027
|
+
// src/chains/solana/providers/interface.ts
|
|
2028
|
+
function createProvider(type, config) {
|
|
2029
|
+
switch (type) {
|
|
2030
|
+
case "helius":
|
|
2031
|
+
return new HeliusProvider(config);
|
|
2032
|
+
case "generic":
|
|
2033
|
+
return new GenericProvider(config);
|
|
2034
|
+
case "quicknode":
|
|
2035
|
+
case "triton":
|
|
2036
|
+
throw new Error(
|
|
2037
|
+
`Provider '${type}' is not yet implemented. Use 'helius' or 'generic' for now. See https://github.com/sip-protocol/sip-protocol/issues/${type === "quicknode" ? "494" : "495"}`
|
|
2038
|
+
);
|
|
2039
|
+
default:
|
|
2040
|
+
throw new Error(`Unknown provider type: ${type}`);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
var init_interface = __esm({
|
|
2044
|
+
"src/chains/solana/providers/interface.ts"() {
|
|
2045
|
+
"use strict";
|
|
2046
|
+
init_helius();
|
|
2047
|
+
init_generic();
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
// src/chains/solana/providers/webhook.ts
|
|
2052
|
+
function createWebhookHandler(config) {
|
|
2053
|
+
const { viewingPrivateKey, spendingPublicKey, onPaymentFound, onError } = config;
|
|
2054
|
+
if (!viewingPrivateKey || !viewingPrivateKey.startsWith("0x")) {
|
|
2055
|
+
throw new ValidationError("viewingPrivateKey must be a valid hex string starting with 0x", "viewingPrivateKey");
|
|
2056
|
+
}
|
|
2057
|
+
if (!spendingPublicKey || !spendingPublicKey.startsWith("0x")) {
|
|
2058
|
+
throw new ValidationError("spendingPublicKey must be a valid hex string starting with 0x", "spendingPublicKey");
|
|
2059
|
+
}
|
|
2060
|
+
if (typeof onPaymentFound !== "function") {
|
|
2061
|
+
throw new ValidationError("onPaymentFound callback is required", "onPaymentFound");
|
|
2062
|
+
}
|
|
2063
|
+
return async (payload) => {
|
|
2064
|
+
const transactions = Array.isArray(payload) ? payload : [payload];
|
|
2065
|
+
const results = [];
|
|
2066
|
+
for (const tx of transactions) {
|
|
2067
|
+
try {
|
|
2068
|
+
if (isRawTransaction(tx)) {
|
|
2069
|
+
const result = await processRawTransaction(
|
|
2070
|
+
tx,
|
|
2071
|
+
viewingPrivateKey,
|
|
2072
|
+
spendingPublicKey,
|
|
2073
|
+
onPaymentFound
|
|
2074
|
+
);
|
|
2075
|
+
results.push(result);
|
|
2076
|
+
} else {
|
|
2077
|
+
results.push({
|
|
2078
|
+
found: false,
|
|
2079
|
+
signature: tx.signature
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
} catch (error) {
|
|
2083
|
+
onError?.(error, isRawTransaction(tx) ? tx : void 0);
|
|
2084
|
+
results.push({
|
|
2085
|
+
found: false,
|
|
2086
|
+
signature: getSignature(tx)
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
return results;
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
async function processRawTransaction(tx, viewingPrivateKey, spendingPublicKey, onPaymentFound) {
|
|
2094
|
+
const signature = tx.transaction?.signatures?.[0] ?? "unknown";
|
|
2095
|
+
if (tx.meta?.err) {
|
|
2096
|
+
return { found: false, signature };
|
|
2097
|
+
}
|
|
2098
|
+
if (!tx.meta?.logMessages) {
|
|
2099
|
+
return { found: false, signature };
|
|
2100
|
+
}
|
|
2101
|
+
for (const log2 of tx.meta.logMessages) {
|
|
2102
|
+
if (!log2.includes(SIP_MEMO_PREFIX)) continue;
|
|
2103
|
+
const memoMatch = log2.match(/Program log: (.+)/);
|
|
2104
|
+
if (!memoMatch) continue;
|
|
2105
|
+
const memoContent = memoMatch[1];
|
|
2106
|
+
const announcement = parseAnnouncement(memoContent);
|
|
2107
|
+
if (!announcement) continue;
|
|
2108
|
+
const ephemeralPubKeyHex = solanaAddressToEd25519PublicKey(
|
|
2109
|
+
announcement.ephemeralPublicKey
|
|
2110
|
+
);
|
|
2111
|
+
const viewTagNumber = parseInt(announcement.viewTag, 16);
|
|
2112
|
+
if (!Number.isFinite(viewTagNumber) || viewTagNumber < 0 || viewTagNumber > 255) {
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
const stealthAddressToCheck = {
|
|
2116
|
+
address: announcement.stealthAddress ? solanaAddressToEd25519PublicKey(announcement.stealthAddress) : "0x" + "00".repeat(32),
|
|
2117
|
+
ephemeralPublicKey: ephemeralPubKeyHex,
|
|
2118
|
+
viewTag: viewTagNumber
|
|
2119
|
+
};
|
|
2120
|
+
let isOurs = false;
|
|
2121
|
+
try {
|
|
2122
|
+
isOurs = checkEd25519StealthAddress(
|
|
2123
|
+
stealthAddressToCheck,
|
|
2124
|
+
viewingPrivateKey,
|
|
2125
|
+
spendingPublicKey
|
|
2126
|
+
);
|
|
2127
|
+
} catch {
|
|
2128
|
+
continue;
|
|
2129
|
+
}
|
|
2130
|
+
if (isOurs) {
|
|
2131
|
+
const transferInfo = parseTokenTransferFromWebhook(tx);
|
|
2132
|
+
const payment = {
|
|
2133
|
+
stealthAddress: announcement.stealthAddress || "",
|
|
2134
|
+
ephemeralPublicKey: announcement.ephemeralPublicKey,
|
|
2135
|
+
amount: transferInfo?.amount ?? 0n,
|
|
2136
|
+
mint: transferInfo?.mint ?? "",
|
|
2137
|
+
tokenSymbol: transferInfo?.mint ? getTokenSymbol2(transferInfo.mint) : void 0,
|
|
2138
|
+
txSignature: signature,
|
|
2139
|
+
slot: tx.slot,
|
|
2140
|
+
timestamp: tx.blockTime
|
|
2141
|
+
};
|
|
2142
|
+
try {
|
|
2143
|
+
await onPaymentFound(payment);
|
|
2144
|
+
} catch {
|
|
2145
|
+
}
|
|
2146
|
+
return { found: true, payment, signature };
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
return { found: false, signature };
|
|
2150
|
+
}
|
|
2151
|
+
function parseTokenTransferFromWebhook(tx) {
|
|
2152
|
+
const { preTokenBalances, postTokenBalances } = tx.meta;
|
|
2153
|
+
if (!postTokenBalances || !preTokenBalances) {
|
|
2154
|
+
return null;
|
|
2155
|
+
}
|
|
2156
|
+
for (const post of postTokenBalances) {
|
|
2157
|
+
const pre = preTokenBalances.find(
|
|
2158
|
+
(p) => p.accountIndex === post.accountIndex
|
|
2159
|
+
);
|
|
2160
|
+
const postAmount = BigInt(post.uiTokenAmount.amount);
|
|
2161
|
+
const preAmount = pre ? BigInt(pre.uiTokenAmount.amount) : 0n;
|
|
2162
|
+
if (postAmount > preAmount) {
|
|
2163
|
+
return {
|
|
2164
|
+
mint: post.mint,
|
|
2165
|
+
amount: postAmount - preAmount
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return null;
|
|
2170
|
+
}
|
|
2171
|
+
function getTokenSymbol2(mint) {
|
|
2172
|
+
for (const [symbol, address] of Object.entries(SOLANA_TOKEN_MINTS)) {
|
|
2173
|
+
if (address === mint) {
|
|
2174
|
+
return symbol;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
return void 0;
|
|
2178
|
+
}
|
|
2179
|
+
function isRawTransaction(tx) {
|
|
2180
|
+
return typeof tx === "object" && tx !== null && "meta" in tx && "transaction" in tx && Array.isArray(tx.transaction?.signatures);
|
|
2181
|
+
}
|
|
2182
|
+
function getSignature(tx) {
|
|
2183
|
+
if ("signature" in tx && typeof tx.signature === "string") {
|
|
2184
|
+
return tx.signature;
|
|
2185
|
+
}
|
|
2186
|
+
if (isRawTransaction(tx)) {
|
|
2187
|
+
return tx.transaction?.signatures?.[0] ?? "unknown";
|
|
2188
|
+
}
|
|
2189
|
+
return "unknown";
|
|
2190
|
+
}
|
|
2191
|
+
async function processWebhookTransaction(transaction, viewingPrivateKey, spendingPublicKey) {
|
|
2192
|
+
const result = await processRawTransaction(
|
|
2193
|
+
transaction,
|
|
2194
|
+
viewingPrivateKey,
|
|
2195
|
+
spendingPublicKey,
|
|
2196
|
+
() => {
|
|
2197
|
+
}
|
|
2198
|
+
// No-op callback
|
|
2199
|
+
);
|
|
2200
|
+
return result.found ? result.payment ?? null : null;
|
|
2201
|
+
}
|
|
2202
|
+
var init_webhook = __esm({
|
|
2203
|
+
"src/chains/solana/providers/webhook.ts"() {
|
|
2204
|
+
"use strict";
|
|
2205
|
+
init_stealth();
|
|
2206
|
+
init_types();
|
|
2207
|
+
init_constants();
|
|
2208
|
+
init_errors();
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
|
|
2212
|
+
// src/chains/solana/providers/index.ts
|
|
2213
|
+
var init_providers = __esm({
|
|
2214
|
+
"src/chains/solana/providers/index.ts"() {
|
|
2215
|
+
"use strict";
|
|
2216
|
+
init_interface();
|
|
2217
|
+
init_helius();
|
|
2218
|
+
init_generic();
|
|
2219
|
+
init_webhook();
|
|
1759
2220
|
}
|
|
1760
2221
|
});
|
|
1761
2222
|
|
|
@@ -1764,6 +2225,8 @@ var solana_exports = {};
|
|
|
1764
2225
|
__export(solana_exports, {
|
|
1765
2226
|
ATA_RENT_LAMPORTS: () => ATA_RENT_LAMPORTS,
|
|
1766
2227
|
ESTIMATED_TX_FEE_LAMPORTS: () => ESTIMATED_TX_FEE_LAMPORTS,
|
|
2228
|
+
GenericProvider: () => GenericProvider,
|
|
2229
|
+
HeliusProvider: () => HeliusProvider,
|
|
1767
2230
|
MEMO_PROGRAM_ID: () => MEMO_PROGRAM_ID,
|
|
1768
2231
|
SIP_MEMO_PREFIX: () => SIP_MEMO_PREFIX,
|
|
1769
2232
|
SOLANA_EXPLORER_URLS: () => SOLANA_EXPLORER_URLS,
|
|
@@ -1772,6 +2235,8 @@ __export(solana_exports, {
|
|
|
1772
2235
|
SOLANA_TOKEN_MINTS: () => SOLANA_TOKEN_MINTS,
|
|
1773
2236
|
claimStealthPayment: () => claimStealthPayment,
|
|
1774
2237
|
createAnnouncementMemo: () => createAnnouncementMemo,
|
|
2238
|
+
createProvider: () => createProvider,
|
|
2239
|
+
createWebhookHandler: () => createWebhookHandler,
|
|
1775
2240
|
estimatePrivateTransferFee: () => estimatePrivateTransferFee,
|
|
1776
2241
|
getExplorerUrl: () => getExplorerUrl,
|
|
1777
2242
|
getStealthBalance: () => getStealthBalance,
|
|
@@ -1779,6 +2244,7 @@ __export(solana_exports, {
|
|
|
1779
2244
|
getTokenMint: () => getTokenMint,
|
|
1780
2245
|
hasTokenAccount: () => hasTokenAccount,
|
|
1781
2246
|
parseAnnouncement: () => parseAnnouncement,
|
|
2247
|
+
processWebhookTransaction: () => processWebhookTransaction,
|
|
1782
2248
|
scanForPayments: () => scanForPayments,
|
|
1783
2249
|
sendPrivateSPLTransfer: () => sendPrivateSPLTransfer
|
|
1784
2250
|
});
|
|
@@ -1789,6 +2255,8 @@ var init_solana = __esm({
|
|
|
1789
2255
|
init_types();
|
|
1790
2256
|
init_transfer();
|
|
1791
2257
|
init_scan();
|
|
2258
|
+
init_providers();
|
|
2259
|
+
init_providers();
|
|
1792
2260
|
}
|
|
1793
2261
|
});
|
|
1794
2262
|
|
|
@@ -4947,6 +5415,8 @@ var index_exports = {};
|
|
|
4947
5415
|
__export(index_exports, {
|
|
4948
5416
|
ATTESTATION_VERSION: () => ATTESTATION_VERSION,
|
|
4949
5417
|
AptosStealthService: () => AptosStealthService,
|
|
5418
|
+
AttestationGatedDisclosure: () => AttestationGatedDisclosure,
|
|
5419
|
+
AttestationSchema: () => AttestationSchema,
|
|
4950
5420
|
AuditorKeyDerivation: () => AuditorKeyDerivation,
|
|
4951
5421
|
AuditorType: () => AuditorType,
|
|
4952
5422
|
BaseWalletAdapter: () => BaseWalletAdapter,
|
|
@@ -4964,11 +5434,14 @@ __export(index_exports, {
|
|
|
4964
5434
|
ErrorCode: () => ErrorCode,
|
|
4965
5435
|
EthereumChainId: () => EthereumChainId,
|
|
4966
5436
|
EthereumWalletAdapter: () => EthereumWalletAdapter,
|
|
5437
|
+
GenericProvider: () => GenericProvider,
|
|
4967
5438
|
HardwareErrorCode: () => HardwareErrorCode,
|
|
4968
5439
|
HardwareWalletError: () => HardwareWalletError,
|
|
5440
|
+
HeliusProvider: () => HeliusProvider,
|
|
4969
5441
|
IntentBuilder: () => IntentBuilder,
|
|
4970
5442
|
IntentError: () => IntentError,
|
|
4971
|
-
IntentStatus: () =>
|
|
5443
|
+
IntentStatus: () => import_types59.IntentStatus,
|
|
5444
|
+
KNOWN_EXCHANGES: () => KNOWN_EXCHANGES,
|
|
4972
5445
|
LedgerWalletAdapter: () => LedgerWalletAdapter,
|
|
4973
5446
|
MEMO_PROGRAM_ID: () => MEMO_PROGRAM_ID,
|
|
4974
5447
|
MockEthereumAdapter: () => MockEthereumAdapter,
|
|
@@ -4978,30 +5451,33 @@ __export(index_exports, {
|
|
|
4978
5451
|
MockSolver: () => MockSolver,
|
|
4979
5452
|
MockTrezorAdapter: () => MockTrezorAdapter,
|
|
4980
5453
|
MockWalletAdapter: () => MockWalletAdapter,
|
|
4981
|
-
NATIVE_TOKENS: () =>
|
|
5454
|
+
NATIVE_TOKENS: () => import_types59.NATIVE_TOKENS,
|
|
4982
5455
|
NEARIntentsAdapter: () => NEARIntentsAdapter,
|
|
4983
5456
|
NEARIntentsBackend: () => NEARIntentsBackend,
|
|
4984
5457
|
NetworkError: () => NetworkError,
|
|
4985
5458
|
ORACLE_DOMAIN: () => ORACLE_DOMAIN,
|
|
4986
5459
|
OneClickClient: () => OneClickClient,
|
|
4987
|
-
OneClickDepositMode: () =>
|
|
4988
|
-
OneClickErrorCode: () =>
|
|
4989
|
-
OneClickSwapStatus: () =>
|
|
4990
|
-
OneClickSwapType: () =>
|
|
5460
|
+
OneClickDepositMode: () => import_types63.OneClickDepositMode,
|
|
5461
|
+
OneClickErrorCode: () => import_types63.OneClickErrorCode,
|
|
5462
|
+
OneClickSwapStatus: () => import_types63.OneClickSwapStatus,
|
|
5463
|
+
OneClickSwapType: () => import_types63.OneClickSwapType,
|
|
4991
5464
|
PaymentBuilder: () => PaymentBuilder,
|
|
4992
|
-
PaymentStatus: () =>
|
|
4993
|
-
|
|
5465
|
+
PaymentStatus: () => import_types60.PaymentStatus,
|
|
5466
|
+
PrivacyBackendRegistry: () => PrivacyBackendRegistry,
|
|
5467
|
+
PrivacyLevel: () => import_types59.PrivacyLevel,
|
|
5468
|
+
PrivacySmartRouter: () => SmartRouter2,
|
|
4994
5469
|
PrivateNFT: () => PrivateNFT,
|
|
4995
5470
|
PrivateVoting: () => PrivateVoting,
|
|
4996
5471
|
ProofError: () => ProofError,
|
|
4997
5472
|
ProofGenerationError: () => ProofGenerationError,
|
|
4998
5473
|
ProofNotImplementedError: () => ProofNotImplementedError,
|
|
4999
|
-
ProposalStatus: () =>
|
|
5000
|
-
ReportStatus: () =>
|
|
5474
|
+
ProposalStatus: () => import_types61.ProposalStatus,
|
|
5475
|
+
ReportStatus: () => import_types62.ReportStatus,
|
|
5001
5476
|
SIP: () => SIP,
|
|
5002
5477
|
SIPError: () => SIPError,
|
|
5478
|
+
SIPNativeBackend: () => SIPNativeBackend,
|
|
5003
5479
|
SIP_MEMO_PREFIX: () => SIP_MEMO_PREFIX,
|
|
5004
|
-
SIP_VERSION: () =>
|
|
5480
|
+
SIP_VERSION: () => import_types59.SIP_VERSION,
|
|
5005
5481
|
SOLANA_EXPLORER_URLS: () => SOLANA_EXPLORER_URLS,
|
|
5006
5482
|
SOLANA_RPC_ENDPOINTS: () => SOLANA_RPC_ENDPOINTS,
|
|
5007
5483
|
SOLANA_TOKEN_DECIMALS: () => SOLANA_TOKEN_DECIMALS,
|
|
@@ -5016,14 +5492,15 @@ __export(index_exports, {
|
|
|
5016
5492
|
SolanaSameChainExecutor: () => SolanaSameChainExecutor,
|
|
5017
5493
|
SolanaWalletAdapter: () => SolanaWalletAdapter,
|
|
5018
5494
|
SuiStealthService: () => SuiStealthService,
|
|
5495
|
+
SurveillanceAnalyzer: () => SurveillanceAnalyzer,
|
|
5019
5496
|
SwapStatus: () => SwapStatus,
|
|
5020
5497
|
ThresholdViewingKey: () => ThresholdViewingKey,
|
|
5021
5498
|
Treasury: () => Treasury,
|
|
5022
5499
|
TrezorWalletAdapter: () => TrezorWalletAdapter,
|
|
5023
5500
|
ValidationError: () => ValidationError,
|
|
5024
5501
|
WalletError: () => WalletError,
|
|
5025
|
-
WalletErrorCode: () =>
|
|
5026
|
-
ZcashErrorCode: () =>
|
|
5502
|
+
WalletErrorCode: () => import_types58.WalletErrorCode,
|
|
5503
|
+
ZcashErrorCode: () => import_types64.ZcashErrorCode,
|
|
5027
5504
|
ZcashNativeBackend: () => ZcashNativeBackend,
|
|
5028
5505
|
ZcashRPCClient: () => ZcashRPCClient,
|
|
5029
5506
|
ZcashRPCError: () => ZcashRPCError,
|
|
@@ -5032,11 +5509,15 @@ __export(index_exports, {
|
|
|
5032
5509
|
addBlindings: () => addBlindings,
|
|
5033
5510
|
addCommitments: () => addCommitments,
|
|
5034
5511
|
addOracle: () => addOracle,
|
|
5512
|
+
analyzeAddressReuse: () => analyzeAddressReuse,
|
|
5513
|
+
analyzeTemporalPatterns: () => analyzeTemporalPatterns,
|
|
5035
5514
|
aptosAddressToAuthKey: () => aptosAddressToAuthKey,
|
|
5036
5515
|
attachProofs: () => attachProofs,
|
|
5037
5516
|
base58ToHex: () => base58ToHex,
|
|
5038
|
-
browserBytesToHex: () =>
|
|
5517
|
+
browserBytesToHex: () => bytesToHex11,
|
|
5039
5518
|
browserHexToBytes: () => hexToBytes10,
|
|
5519
|
+
calculatePrivacyScore: () => calculatePrivacyScore,
|
|
5520
|
+
calculateSIPComparison: () => calculateSIPComparison,
|
|
5040
5521
|
checkAptosStealthAddress: () => checkAptosStealthAddress,
|
|
5041
5522
|
checkEd25519StealthAddress: () => checkEd25519StealthAddress,
|
|
5042
5523
|
checkStealthAddress: () => checkStealthAddress,
|
|
@@ -5051,6 +5532,7 @@ __export(index_exports, {
|
|
|
5051
5532
|
createEthereumAdapter: () => createEthereumAdapter,
|
|
5052
5533
|
createKeySpendOnlyOutput: () => createKeySpendOnlyOutput,
|
|
5053
5534
|
createLedgerAdapter: () => createLedgerAdapter,
|
|
5535
|
+
createMockAttestation: () => createMockAttestation,
|
|
5054
5536
|
createMockEthereumAdapter: () => createMockEthereumAdapter,
|
|
5055
5537
|
createMockEthereumProvider: () => createMockEthereumProvider,
|
|
5056
5538
|
createMockLedgerAdapter: () => createMockLedgerAdapter,
|
|
@@ -5065,6 +5547,7 @@ __export(index_exports, {
|
|
|
5065
5547
|
createPrivateOwnership: () => createPrivateOwnership,
|
|
5066
5548
|
createPrivateVoting: () => createPrivateVoting,
|
|
5067
5549
|
createProductionSIP: () => createProductionSIP,
|
|
5550
|
+
createProvider: () => createProvider,
|
|
5068
5551
|
createSIP: () => createSIP,
|
|
5069
5552
|
createSameChainExecutor: () => createSameChainExecutor,
|
|
5070
5553
|
createSealedBidAuction: () => createSealedBidAuction,
|
|
@@ -5072,9 +5555,11 @@ __export(index_exports, {
|
|
|
5072
5555
|
createShieldedPayment: () => createShieldedPayment,
|
|
5073
5556
|
createSmartRouter: () => createSmartRouter,
|
|
5074
5557
|
createSolanaAdapter: () => createSolanaAdapter,
|
|
5558
|
+
createSurveillanceAnalyzer: () => createSurveillanceAnalyzer,
|
|
5075
5559
|
createTaprootOutput: () => createTaprootOutput,
|
|
5076
5560
|
createTrezorAdapter: () => createTrezorAdapter,
|
|
5077
5561
|
createWalletFactory: () => createWalletFactory,
|
|
5562
|
+
createWebhookHandler: () => createWebhookHandler,
|
|
5078
5563
|
createZcashClient: () => createZcashClient,
|
|
5079
5564
|
createZcashNativeBackend: () => createZcashNativeBackend,
|
|
5080
5565
|
createZcashShieldedService: () => createZcashShieldedService,
|
|
@@ -5083,6 +5568,7 @@ __export(index_exports, {
|
|
|
5083
5568
|
decodeTaprootAddress: () => decodeTaprootAddress,
|
|
5084
5569
|
decryptMemo: () => decryptMemo,
|
|
5085
5570
|
decryptWithViewing: () => decryptWithViewing,
|
|
5571
|
+
defaultRegistry: () => defaultRegistry,
|
|
5086
5572
|
deriveAptosStealthPrivateKey: () => deriveAptosStealthPrivateKey,
|
|
5087
5573
|
deriveEd25519StealthPrivateKey: () => deriveEd25519StealthPrivateKey,
|
|
5088
5574
|
deriveOracleId: () => deriveOracleId,
|
|
@@ -5092,7 +5578,9 @@ __export(index_exports, {
|
|
|
5092
5578
|
deserializeAttestationMessage: () => deserializeAttestationMessage,
|
|
5093
5579
|
deserializeIntent: () => deserializeIntent,
|
|
5094
5580
|
deserializePayment: () => deserializePayment,
|
|
5581
|
+
detectClusters: () => detectClusters,
|
|
5095
5582
|
detectEthereumWallets: () => detectEthereumWallets,
|
|
5583
|
+
detectExchangeExposure: () => detectExchangeExposure,
|
|
5096
5584
|
detectSolanaWallets: () => detectSolanaWallets,
|
|
5097
5585
|
ed25519PublicKeyToAptosAddress: () => ed25519PublicKeyToAptosAddress,
|
|
5098
5586
|
ed25519PublicKeyToNearAddress: () => ed25519PublicKeyToNearAddress,
|
|
@@ -5102,6 +5590,7 @@ __export(index_exports, {
|
|
|
5102
5590
|
encryptForViewing: () => encryptForViewing,
|
|
5103
5591
|
estimatePrivateTransferFee: () => estimatePrivateTransferFee,
|
|
5104
5592
|
featureNotSupportedError: () => featureNotSupportedError,
|
|
5593
|
+
fetchAttestation: () => fetchAttestation,
|
|
5105
5594
|
formatStablecoinAmount: () => formatStablecoinAmount,
|
|
5106
5595
|
fromHex: () => fromHex,
|
|
5107
5596
|
fromStablecoinUnits: () => fromStablecoinUnits,
|
|
@@ -5157,7 +5646,7 @@ __export(index_exports, {
|
|
|
5157
5646
|
isExpired: () => isExpired,
|
|
5158
5647
|
isNonNegativeAmount: () => isNonNegativeAmount,
|
|
5159
5648
|
isPaymentExpired: () => isPaymentExpired,
|
|
5160
|
-
isPrivate: () =>
|
|
5649
|
+
isPrivate: () => import_types59.isPrivate,
|
|
5161
5650
|
isPrivateWalletAdapter: () => isPrivateWalletAdapter,
|
|
5162
5651
|
isSIPError: () => isSIPError,
|
|
5163
5652
|
isSameChainSupported: () => isSameChainSupported,
|
|
@@ -5186,6 +5675,7 @@ __export(index_exports, {
|
|
|
5186
5675
|
normalizeSuiAddress: () => normalizeSuiAddress,
|
|
5187
5676
|
notConnectedError: () => notConnectedError,
|
|
5188
5677
|
parseAnnouncement: () => parseAnnouncement,
|
|
5678
|
+
processWebhookTransaction: () => processWebhookTransaction,
|
|
5189
5679
|
proveOwnership: () => proveOwnership,
|
|
5190
5680
|
publicKeyToEthAddress: () => publicKeyToEthAddress,
|
|
5191
5681
|
registerWallet: () => registerWallet,
|
|
@@ -5208,7 +5698,7 @@ __export(index_exports, {
|
|
|
5208
5698
|
subtractBlindings: () => subtractBlindings,
|
|
5209
5699
|
subtractCommitments: () => subtractCommitments,
|
|
5210
5700
|
supportsSharedArrayBuffer: () => supportsSharedArrayBuffer,
|
|
5211
|
-
supportsViewingKey: () =>
|
|
5701
|
+
supportsViewingKey: () => import_types59.supportsViewingKey,
|
|
5212
5702
|
supportsWebBluetooth: () => supportsWebBluetooth,
|
|
5213
5703
|
supportsWebHID: () => supportsWebHID,
|
|
5214
5704
|
supportsWebUSB: () => supportsWebUSB,
|
|
@@ -5226,6 +5716,7 @@ __export(index_exports, {
|
|
|
5226
5716
|
validateScalar: () => validateScalar,
|
|
5227
5717
|
validateViewingKey: () => validateViewingKey,
|
|
5228
5718
|
verifyAttestation: () => verifyAttestation,
|
|
5719
|
+
verifyAttestationSignature: () => verifyAttestationSignature,
|
|
5229
5720
|
verifyCommitment: () => verifyCommitment,
|
|
5230
5721
|
verifyOpening: () => verifyOpening,
|
|
5231
5722
|
verifyOracleSignature: () => verifyOracleSignature,
|
|
@@ -5239,7 +5730,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5239
5730
|
init_errors();
|
|
5240
5731
|
|
|
5241
5732
|
// src/sip.ts
|
|
5242
|
-
var
|
|
5733
|
+
var import_types8 = require("@sip-protocol/types");
|
|
5243
5734
|
|
|
5244
5735
|
// src/intent.ts
|
|
5245
5736
|
var import_types = require("@sip-protocol/types");
|
|
@@ -7158,7 +7649,7 @@ var SIP = class {
|
|
|
7158
7649
|
this.config = {
|
|
7159
7650
|
...config,
|
|
7160
7651
|
mode: config.mode ?? "demo",
|
|
7161
|
-
defaultPrivacy: config.defaultPrivacy ??
|
|
7652
|
+
defaultPrivacy: config.defaultPrivacy ?? import_types8.PrivacyLevel.SHIELDED
|
|
7162
7653
|
};
|
|
7163
7654
|
this.proofProvider = config.proofProvider;
|
|
7164
7655
|
if (config.intentsAdapter) {
|
|
@@ -7363,8 +7854,8 @@ var SIP = class {
|
|
|
7363
7854
|
"intentsAdapter"
|
|
7364
7855
|
);
|
|
7365
7856
|
}
|
|
7366
|
-
const metaAddr = recipientMetaAddress ?? (params.privacy !==
|
|
7367
|
-
if (params.privacy !==
|
|
7857
|
+
const metaAddr = recipientMetaAddress ?? (params.privacy !== import_types8.PrivacyLevel.TRANSPARENT ? this.stealthKeys?.metaAddress : void 0);
|
|
7858
|
+
if (params.privacy !== import_types8.PrivacyLevel.TRANSPARENT && !metaAddr) {
|
|
7368
7859
|
throw new ValidationError(
|
|
7369
7860
|
"Stealth meta-address required for privacy modes. Call generateStealthKeys() or provide recipientMetaAddress.",
|
|
7370
7861
|
"recipientMetaAddress"
|
|
@@ -7455,10 +7946,10 @@ var SIP = class {
|
|
|
7455
7946
|
}
|
|
7456
7947
|
);
|
|
7457
7948
|
this.pendingSwaps.delete(quote.intentId);
|
|
7458
|
-
const isSuccess = finalStatus.status ===
|
|
7949
|
+
const isSuccess = finalStatus.status === import_types8.OneClickSwapStatus.SUCCESS;
|
|
7459
7950
|
return {
|
|
7460
7951
|
intentId: intent.intentId,
|
|
7461
|
-
status: isSuccess ?
|
|
7952
|
+
status: isSuccess ? import_types8.IntentStatus.FULFILLED : import_types8.IntentStatus.FAILED,
|
|
7462
7953
|
outputAmount: quote.outputAmount,
|
|
7463
7954
|
txHash: finalStatus.settlementTxHash ?? depositTxHash,
|
|
7464
7955
|
fulfilledAt: Math.floor(Date.now() / 1e3),
|
|
@@ -7525,9 +8016,9 @@ var SIP = class {
|
|
|
7525
8016
|
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
7526
8017
|
return {
|
|
7527
8018
|
intentId: intent.intentId,
|
|
7528
|
-
status:
|
|
8019
|
+
status: import_types8.IntentStatus.FULFILLED,
|
|
7529
8020
|
outputAmount: quote.outputAmount,
|
|
7530
|
-
txHash: intent.privacyLevel ===
|
|
8021
|
+
txHash: intent.privacyLevel === import_types8.PrivacyLevel.TRANSPARENT ? `0x${Date.now().toString(16)}` : void 0,
|
|
7531
8022
|
fulfilledAt: Math.floor(Date.now() / 1e3)
|
|
7532
8023
|
};
|
|
7533
8024
|
}
|
|
@@ -7569,7 +8060,7 @@ var SIP = class {
|
|
|
7569
8060
|
}
|
|
7570
8061
|
const { sendPrivateSPLTransfer: sendPrivateSPLTransfer2 } = await Promise.resolve().then(() => (init_solana(), solana_exports));
|
|
7571
8062
|
const { PublicKey: SolanaPublicKey } = await import("@solana/web3.js");
|
|
7572
|
-
const { getAssociatedTokenAddress:
|
|
8063
|
+
const { getAssociatedTokenAddress: getAssociatedTokenAddress4 } = await import("@solana/spl-token");
|
|
7573
8064
|
const { SOLANA_TOKEN_MINTS: SOLANA_TOKEN_MINTS2 } = await Promise.resolve().then(() => (init_constants(), constants_exports));
|
|
7574
8065
|
let mint;
|
|
7575
8066
|
if (params.token in SOLANA_TOKEN_MINTS2) {
|
|
@@ -7577,7 +8068,7 @@ var SIP = class {
|
|
|
7577
8068
|
} else {
|
|
7578
8069
|
mint = new SolanaPublicKey(params.token);
|
|
7579
8070
|
}
|
|
7580
|
-
const senderTokenAccount = await
|
|
8071
|
+
const senderTokenAccount = await getAssociatedTokenAddress4(
|
|
7581
8072
|
mint,
|
|
7582
8073
|
params.sender
|
|
7583
8074
|
);
|
|
@@ -8140,7 +8631,7 @@ function hexToBytes10(hex) {
|
|
|
8140
8631
|
}
|
|
8141
8632
|
return bytes;
|
|
8142
8633
|
}
|
|
8143
|
-
function
|
|
8634
|
+
function bytesToHex11(bytes) {
|
|
8144
8635
|
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8145
8636
|
}
|
|
8146
8637
|
function isBrowser() {
|
|
@@ -8203,7 +8694,7 @@ var CHAIN_NUMERIC_IDS = {
|
|
|
8203
8694
|
};
|
|
8204
8695
|
|
|
8205
8696
|
// src/oracle/verification.ts
|
|
8206
|
-
var
|
|
8697
|
+
var import_ed255193 = require("@noble/curves/ed25519");
|
|
8207
8698
|
var import_sha25610 = require("@noble/hashes/sha256");
|
|
8208
8699
|
var import_utils14 = require("@noble/hashes/utils");
|
|
8209
8700
|
|
|
@@ -8369,7 +8860,7 @@ function verifyAttestation(attestation, registry) {
|
|
|
8369
8860
|
const signatureBytes = (0, import_utils14.hexToBytes)(
|
|
8370
8861
|
sig.signature.startsWith("0x") ? sig.signature.slice(2) : sig.signature
|
|
8371
8862
|
);
|
|
8372
|
-
const isValid =
|
|
8863
|
+
const isValid = import_ed255193.ed25519.verify(signatureBytes, messageHash, publicKeyBytes);
|
|
8373
8864
|
if (isValid) {
|
|
8374
8865
|
validCount++;
|
|
8375
8866
|
validOracles.push(sig.oracleId);
|
|
@@ -8397,14 +8888,14 @@ function verifyOracleSignature(signature, messageHash, oracle) {
|
|
|
8397
8888
|
const signatureBytes = (0, import_utils14.hexToBytes)(
|
|
8398
8889
|
signature.signature.startsWith("0x") ? signature.signature.slice(2) : signature.signature
|
|
8399
8890
|
);
|
|
8400
|
-
return
|
|
8891
|
+
return import_ed255193.ed25519.verify(signatureBytes, messageHash, publicKeyBytes);
|
|
8401
8892
|
} catch {
|
|
8402
8893
|
return false;
|
|
8403
8894
|
}
|
|
8404
8895
|
}
|
|
8405
8896
|
function signAttestationMessage(messageHash, privateKey) {
|
|
8406
|
-
const signature =
|
|
8407
|
-
const publicKey =
|
|
8897
|
+
const signature = import_ed255193.ed25519.sign(messageHash, privateKey);
|
|
8898
|
+
const publicKey = import_ed255193.ed25519.getPublicKey(privateKey);
|
|
8408
8899
|
const oracleId = deriveOracleId(publicKey);
|
|
8409
8900
|
return {
|
|
8410
8901
|
oracleId,
|
|
@@ -8460,13 +8951,13 @@ function hasEnoughOracles(registry) {
|
|
|
8460
8951
|
}
|
|
8461
8952
|
|
|
8462
8953
|
// src/index.ts
|
|
8463
|
-
var import_types58 = require("@sip-protocol/types");
|
|
8464
8954
|
var import_types59 = require("@sip-protocol/types");
|
|
8465
8955
|
var import_types60 = require("@sip-protocol/types");
|
|
8466
8956
|
var import_types61 = require("@sip-protocol/types");
|
|
8957
|
+
var import_types62 = require("@sip-protocol/types");
|
|
8467
8958
|
|
|
8468
8959
|
// src/solver/mock-solver.ts
|
|
8469
|
-
var
|
|
8960
|
+
var import_types11 = require("@sip-protocol/types");
|
|
8470
8961
|
var import_utils15 = require("@noble/hashes/utils");
|
|
8471
8962
|
var MockSolver = class {
|
|
8472
8963
|
info;
|
|
@@ -8592,7 +9083,7 @@ var MockSolver = class {
|
|
|
8592
9083
|
status.error = "Simulated failure for testing";
|
|
8593
9084
|
return {
|
|
8594
9085
|
intentId: intent.intentId,
|
|
8595
|
-
status:
|
|
9086
|
+
status: import_types11.IntentStatus.FAILED,
|
|
8596
9087
|
fulfilledAt: Math.floor(Date.now() / 1e3),
|
|
8597
9088
|
error: status.error
|
|
8598
9089
|
};
|
|
@@ -8602,7 +9093,7 @@ var MockSolver = class {
|
|
|
8602
9093
|
status.txHash = txHash;
|
|
8603
9094
|
return {
|
|
8604
9095
|
intentId: intent.intentId,
|
|
8605
|
-
status:
|
|
9096
|
+
status: import_types11.IntentStatus.FULFILLED,
|
|
8606
9097
|
outputAmount: quote.outputAmount,
|
|
8607
9098
|
txHash: intent.privacyLevel === "transparent" ? txHash : void 0,
|
|
8608
9099
|
fulfillmentProof: {
|
|
@@ -8648,7 +9139,7 @@ function createMockSolver(config) {
|
|
|
8648
9139
|
}
|
|
8649
9140
|
|
|
8650
9141
|
// src/index.ts
|
|
8651
|
-
var
|
|
9142
|
+
var import_types63 = require("@sip-protocol/types");
|
|
8652
9143
|
|
|
8653
9144
|
// src/settlement/interface.ts
|
|
8654
9145
|
var SwapStatus = /* @__PURE__ */ ((SwapStatus2) => {
|
|
@@ -9055,21 +9546,21 @@ function createSmartRouter(registry) {
|
|
|
9055
9546
|
}
|
|
9056
9547
|
|
|
9057
9548
|
// src/settlement/backends/near-intents.ts
|
|
9058
|
-
var
|
|
9549
|
+
var import_types12 = require("@sip-protocol/types");
|
|
9059
9550
|
init_errors();
|
|
9060
9551
|
function mapOneClickStatus(status) {
|
|
9061
9552
|
switch (status) {
|
|
9062
|
-
case
|
|
9553
|
+
case import_types12.OneClickSwapStatus.PENDING_DEPOSIT:
|
|
9063
9554
|
return "pending_deposit" /* PENDING_DEPOSIT */;
|
|
9064
|
-
case
|
|
9555
|
+
case import_types12.OneClickSwapStatus.PROCESSING:
|
|
9065
9556
|
return "in_progress" /* IN_PROGRESS */;
|
|
9066
|
-
case
|
|
9557
|
+
case import_types12.OneClickSwapStatus.SUCCESS:
|
|
9067
9558
|
return "success" /* SUCCESS */;
|
|
9068
|
-
case
|
|
9559
|
+
case import_types12.OneClickSwapStatus.FAILED:
|
|
9069
9560
|
return "failed" /* FAILED */;
|
|
9070
|
-
case
|
|
9561
|
+
case import_types12.OneClickSwapStatus.INCOMPLETE_DEPOSIT:
|
|
9071
9562
|
return "failed" /* FAILED */;
|
|
9072
|
-
case
|
|
9563
|
+
case import_types12.OneClickSwapStatus.REFUNDED:
|
|
9073
9564
|
return "refunded" /* REFUNDED */;
|
|
9074
9565
|
default:
|
|
9075
9566
|
return "pending_deposit" /* PENDING_DEPOSIT */;
|
|
@@ -9107,9 +9598,9 @@ var NEARIntentsBackend = class {
|
|
|
9107
9598
|
"zcash"
|
|
9108
9599
|
],
|
|
9109
9600
|
supportedPrivacyLevels: [
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9601
|
+
import_types12.PrivacyLevel.TRANSPARENT,
|
|
9602
|
+
import_types12.PrivacyLevel.SHIELDED,
|
|
9603
|
+
import_types12.PrivacyLevel.COMPLIANT
|
|
9113
9604
|
],
|
|
9114
9605
|
supportsCancellation: false,
|
|
9115
9606
|
supportsRefunds: true,
|
|
@@ -9366,13 +9857,13 @@ var NEARIntentsBackend = class {
|
|
|
9366
9857
|
if (!params.privacyLevel) {
|
|
9367
9858
|
throw new ValidationError("privacyLevel is required", "privacyLevel");
|
|
9368
9859
|
}
|
|
9369
|
-
if (params.privacyLevel !==
|
|
9860
|
+
if (params.privacyLevel !== import_types12.PrivacyLevel.TRANSPARENT && !params.recipientMetaAddress) {
|
|
9370
9861
|
throw new ValidationError(
|
|
9371
9862
|
"recipientMetaAddress is required for shielded/compliant privacy modes",
|
|
9372
9863
|
"recipientMetaAddress"
|
|
9373
9864
|
);
|
|
9374
9865
|
}
|
|
9375
|
-
if (params.privacyLevel ===
|
|
9866
|
+
if (params.privacyLevel === import_types12.PrivacyLevel.TRANSPARENT && !params.senderAddress) {
|
|
9376
9867
|
throw new ValidationError(
|
|
9377
9868
|
"senderAddress is required for transparent mode",
|
|
9378
9869
|
"senderAddress"
|
|
@@ -9411,7 +9902,7 @@ function createNEARIntentsBackend(config) {
|
|
|
9411
9902
|
}
|
|
9412
9903
|
|
|
9413
9904
|
// src/settlement/backends/zcash-native.ts
|
|
9414
|
-
var
|
|
9905
|
+
var import_types13 = require("@sip-protocol/types");
|
|
9415
9906
|
init_errors();
|
|
9416
9907
|
var ZcashNativeBackend = class {
|
|
9417
9908
|
name = "zcash-native";
|
|
@@ -9430,9 +9921,9 @@ var ZcashNativeBackend = class {
|
|
|
9430
9921
|
supportedSourceChains: ["zcash"],
|
|
9431
9922
|
supportedDestinationChains: ["zcash"],
|
|
9432
9923
|
supportedPrivacyLevels: [
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9924
|
+
import_types13.PrivacyLevel.TRANSPARENT,
|
|
9925
|
+
import_types13.PrivacyLevel.SHIELDED,
|
|
9926
|
+
import_types13.PrivacyLevel.COMPLIANT
|
|
9436
9927
|
],
|
|
9437
9928
|
supportsCancellation: false,
|
|
9438
9929
|
supportsRefunds: false,
|
|
@@ -9471,7 +9962,7 @@ var ZcashNativeBackend = class {
|
|
|
9471
9962
|
const minAmountOut = amountOut * BigInt(99) / BigInt(100);
|
|
9472
9963
|
let depositAddress;
|
|
9473
9964
|
let recipientAddress;
|
|
9474
|
-
if (privacyLevel ===
|
|
9965
|
+
if (privacyLevel === import_types13.PrivacyLevel.SHIELDED || privacyLevel === import_types13.PrivacyLevel.COMPLIANT) {
|
|
9475
9966
|
if (typeof recipientMetaAddress === "string") {
|
|
9476
9967
|
recipientAddress = recipientMetaAddress;
|
|
9477
9968
|
} else if (recipientMetaAddress) {
|
|
@@ -9756,7 +10247,7 @@ function createZcashNativeBackend(config) {
|
|
|
9756
10247
|
}
|
|
9757
10248
|
|
|
9758
10249
|
// src/settlement/backends/direct-chain.ts
|
|
9759
|
-
var
|
|
10250
|
+
var import_types14 = require("@sip-protocol/types");
|
|
9760
10251
|
init_stealth();
|
|
9761
10252
|
init_errors();
|
|
9762
10253
|
var import_utils16 = require("@noble/hashes/utils");
|
|
@@ -9798,7 +10289,7 @@ var DEFAULT_GAS_FEES = {
|
|
|
9798
10289
|
};
|
|
9799
10290
|
|
|
9800
10291
|
// src/zcash/rpc-client.ts
|
|
9801
|
-
var
|
|
10292
|
+
var import_types15 = require("@sip-protocol/types");
|
|
9802
10293
|
init_errors();
|
|
9803
10294
|
var DEFAULT_CONFIG = {
|
|
9804
10295
|
host: "127.0.0.1",
|
|
@@ -9819,19 +10310,19 @@ var ZcashRPCError = class extends Error {
|
|
|
9819
10310
|
* Check if error is due to insufficient funds
|
|
9820
10311
|
*/
|
|
9821
10312
|
isInsufficientFunds() {
|
|
9822
|
-
return this.code ===
|
|
10313
|
+
return this.code === import_types15.ZcashErrorCode.WALLET_INSUFFICIENT_FUNDS;
|
|
9823
10314
|
}
|
|
9824
10315
|
/**
|
|
9825
10316
|
* Check if error is due to invalid address
|
|
9826
10317
|
*/
|
|
9827
10318
|
isInvalidAddress() {
|
|
9828
|
-
return this.code ===
|
|
10319
|
+
return this.code === import_types15.ZcashErrorCode.INVALID_ADDRESS_OR_KEY;
|
|
9829
10320
|
}
|
|
9830
10321
|
/**
|
|
9831
10322
|
* Check if error is due to wallet being locked
|
|
9832
10323
|
*/
|
|
9833
10324
|
isWalletLocked() {
|
|
9834
|
-
return this.code ===
|
|
10325
|
+
return this.code === import_types15.ZcashErrorCode.WALLET_UNLOCK_NEEDED;
|
|
9835
10326
|
}
|
|
9836
10327
|
};
|
|
9837
10328
|
var ZcashRPCClient = class {
|
|
@@ -10218,7 +10709,7 @@ function createZcashClient(config) {
|
|
|
10218
10709
|
}
|
|
10219
10710
|
|
|
10220
10711
|
// src/zcash/shielded-service.ts
|
|
10221
|
-
var
|
|
10712
|
+
var import_types16 = require("@sip-protocol/types");
|
|
10222
10713
|
init_errors();
|
|
10223
10714
|
var ZcashShieldedService = class _ZcashShieldedService {
|
|
10224
10715
|
client;
|
|
@@ -10405,7 +10896,7 @@ var ZcashShieldedService = class _ZcashShieldedService {
|
|
|
10405
10896
|
* Higher-level method that handles privacy level mapping.
|
|
10406
10897
|
*/
|
|
10407
10898
|
async sendWithPrivacy(to, amount, privacyLevel, memo) {
|
|
10408
|
-
if (privacyLevel ===
|
|
10899
|
+
if (privacyLevel === import_types16.PrivacyLevel.TRANSPARENT) {
|
|
10409
10900
|
throw new ValidationError(
|
|
10410
10901
|
"Transparent mode not supported for Zcash shielded service. Use standard RPC client.",
|
|
10411
10902
|
"privacyLevel",
|
|
@@ -10499,7 +10990,7 @@ var ZcashShieldedService = class _ZcashShieldedService {
|
|
|
10499
10990
|
const viewingKey = await this.exportViewingKey();
|
|
10500
10991
|
return {
|
|
10501
10992
|
viewingKey,
|
|
10502
|
-
privacyLevel:
|
|
10993
|
+
privacyLevel: import_types16.PrivacyLevel.COMPLIANT,
|
|
10503
10994
|
disclaimer: "This viewing key provides read-only access to transaction history. It cannot be used to spend funds. Share only with authorized auditors."
|
|
10504
10995
|
};
|
|
10505
10996
|
}
|
|
@@ -10585,11 +11076,11 @@ var ZcashShieldedService = class _ZcashShieldedService {
|
|
|
10585
11076
|
*/
|
|
10586
11077
|
mapPrivacyLevelToPolicy(level) {
|
|
10587
11078
|
switch (level) {
|
|
10588
|
-
case
|
|
11079
|
+
case import_types16.PrivacyLevel.TRANSPARENT:
|
|
10589
11080
|
return "NoPrivacy";
|
|
10590
|
-
case
|
|
11081
|
+
case import_types16.PrivacyLevel.SHIELDED:
|
|
10591
11082
|
return "FullPrivacy";
|
|
10592
|
-
case
|
|
11083
|
+
case import_types16.PrivacyLevel.COMPLIANT:
|
|
10593
11084
|
return "FullPrivacy";
|
|
10594
11085
|
default:
|
|
10595
11086
|
return "FullPrivacy";
|
|
@@ -10642,7 +11133,7 @@ function createZcashShieldedService(config) {
|
|
|
10642
11133
|
}
|
|
10643
11134
|
|
|
10644
11135
|
// src/zcash/swap-service.ts
|
|
10645
|
-
var
|
|
11136
|
+
var import_types17 = require("@sip-protocol/types");
|
|
10646
11137
|
init_errors();
|
|
10647
11138
|
var MOCK_PRICES = {
|
|
10648
11139
|
ETH: 2500,
|
|
@@ -10756,7 +11247,7 @@ var ZcashSwapService = class {
|
|
|
10756
11247
|
validUntil,
|
|
10757
11248
|
depositAddress: this.generateMockDepositAddress(sourceChain),
|
|
10758
11249
|
estimatedTime: this.getEstimatedTime(sourceChain),
|
|
10759
|
-
privacyLevel:
|
|
11250
|
+
privacyLevel: import_types17.PrivacyLevel.SHIELDED
|
|
10760
11251
|
};
|
|
10761
11252
|
this.quotes.set(quoteId, quote);
|
|
10762
11253
|
return quote;
|
|
@@ -10799,7 +11290,7 @@ var ZcashSwapService = class {
|
|
|
10799
11290
|
depositAddress: "",
|
|
10800
11291
|
// Will be set by bridge
|
|
10801
11292
|
estimatedTime: this.getEstimatedTime(params.sourceChain),
|
|
10802
|
-
privacyLevel:
|
|
11293
|
+
privacyLevel: import_types17.PrivacyLevel.SHIELDED
|
|
10803
11294
|
};
|
|
10804
11295
|
this.quotes.set(quote.quoteId, quote);
|
|
10805
11296
|
return quote;
|
|
@@ -11082,10 +11573,10 @@ function createZcashSwapService(config) {
|
|
|
11082
11573
|
init_errors();
|
|
11083
11574
|
|
|
11084
11575
|
// src/zcash/index.ts
|
|
11085
|
-
var
|
|
11576
|
+
var import_types18 = require("@sip-protocol/types");
|
|
11086
11577
|
|
|
11087
11578
|
// src/index.ts
|
|
11088
|
-
var
|
|
11579
|
+
var import_types64 = require("@sip-protocol/types");
|
|
11089
11580
|
|
|
11090
11581
|
// src/bitcoin/taproot.ts
|
|
11091
11582
|
var import_secp256k14 = require("@noble/curves/secp256k1");
|
|
@@ -11380,7 +11871,7 @@ init_errors();
|
|
|
11380
11871
|
init_validation();
|
|
11381
11872
|
|
|
11382
11873
|
// src/payment/payment.ts
|
|
11383
|
-
var
|
|
11874
|
+
var import_types19 = require("@sip-protocol/types");
|
|
11384
11875
|
var import_sha25613 = require("@noble/hashes/sha256");
|
|
11385
11876
|
var import_utils19 = require("@noble/hashes/utils");
|
|
11386
11877
|
var import_chacha2 = require("@noble/ciphers/chacha.js");
|
|
@@ -11571,7 +12062,7 @@ var PaymentBuilder = class {
|
|
|
11571
12062
|
_amount;
|
|
11572
12063
|
_recipientMetaAddress;
|
|
11573
12064
|
_recipientAddress;
|
|
11574
|
-
_privacy =
|
|
12065
|
+
_privacy = import_types19.PrivacyLevel.SHIELDED;
|
|
11575
12066
|
_viewingKey;
|
|
11576
12067
|
_sourceChain;
|
|
11577
12068
|
_destinationChain;
|
|
@@ -11854,7 +12345,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11854
12345
|
} else {
|
|
11855
12346
|
resolvedToken = token;
|
|
11856
12347
|
}
|
|
11857
|
-
if (privacy !==
|
|
12348
|
+
if (privacy !== import_types19.PrivacyLevel.TRANSPARENT && !recipientMetaAddress) {
|
|
11858
12349
|
throw new ValidationError(
|
|
11859
12350
|
"recipientMetaAddress is required for shielded/compliant privacy modes",
|
|
11860
12351
|
"recipientMetaAddress",
|
|
@@ -11862,7 +12353,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11862
12353
|
"SIP_2008" /* MISSING_REQUIRED */
|
|
11863
12354
|
);
|
|
11864
12355
|
}
|
|
11865
|
-
if (privacy ===
|
|
12356
|
+
if (privacy === import_types19.PrivacyLevel.TRANSPARENT && !recipientAddress) {
|
|
11866
12357
|
throw new ValidationError(
|
|
11867
12358
|
"recipientAddress is required for transparent mode",
|
|
11868
12359
|
"recipientAddress",
|
|
@@ -11870,7 +12361,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11870
12361
|
"SIP_2008" /* MISSING_REQUIRED */
|
|
11871
12362
|
);
|
|
11872
12363
|
}
|
|
11873
|
-
if (privacy ===
|
|
12364
|
+
if (privacy === import_types19.PrivacyLevel.COMPLIANT && !viewingKey) {
|
|
11874
12365
|
throw new ValidationError(
|
|
11875
12366
|
"viewingKey is required for compliant mode",
|
|
11876
12367
|
"viewingKey",
|
|
@@ -11892,7 +12383,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11892
12383
|
const now = Math.floor(Date.now() / 1e3);
|
|
11893
12384
|
const payment = {
|
|
11894
12385
|
paymentId,
|
|
11895
|
-
version:
|
|
12386
|
+
version: import_types19.SIP_VERSION,
|
|
11896
12387
|
privacyLevel: privacy,
|
|
11897
12388
|
createdAt: now,
|
|
11898
12389
|
expiry: now + ttl,
|
|
@@ -11903,7 +12394,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11903
12394
|
purpose,
|
|
11904
12395
|
viewingKeyHash
|
|
11905
12396
|
};
|
|
11906
|
-
if (privacy !==
|
|
12397
|
+
if (privacy !== import_types19.PrivacyLevel.TRANSPARENT && recipientMetaAddress) {
|
|
11907
12398
|
const metaAddress = decodeStealthMetaAddress(recipientMetaAddress);
|
|
11908
12399
|
const { stealthAddress } = generateStealthAddress(metaAddress);
|
|
11909
12400
|
payment.recipientStealth = stealthAddress;
|
|
@@ -11920,7 +12411,7 @@ async function createShieldedPayment(params, options) {
|
|
|
11920
12411
|
payment.recipientAddress = recipientAddress;
|
|
11921
12412
|
payment.memo = memo;
|
|
11922
12413
|
}
|
|
11923
|
-
if (privacy !==
|
|
12414
|
+
if (privacy !== import_types19.PrivacyLevel.TRANSPARENT && proofProvider?.isReady) {
|
|
11924
12415
|
const hexToUint8 = (hex) => {
|
|
11925
12416
|
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
11926
12417
|
return (0, import_utils19.hexToBytes)(cleanHex);
|
|
@@ -11986,7 +12477,7 @@ function decryptMemo(encryptedMemo, viewingKey) {
|
|
|
11986
12477
|
function trackPayment(payment) {
|
|
11987
12478
|
return {
|
|
11988
12479
|
...payment,
|
|
11989
|
-
status:
|
|
12480
|
+
status: import_types19.PaymentStatus.DRAFT
|
|
11990
12481
|
};
|
|
11991
12482
|
}
|
|
11992
12483
|
function isPaymentExpired(payment) {
|
|
@@ -12019,7 +12510,7 @@ function getPaymentSummary(payment) {
|
|
|
12019
12510
|
}
|
|
12020
12511
|
|
|
12021
12512
|
// src/treasury/treasury.ts
|
|
12022
|
-
var
|
|
12513
|
+
var import_types20 = require("@sip-protocol/types");
|
|
12023
12514
|
var import_secp256k16 = require("@noble/curves/secp256k1");
|
|
12024
12515
|
var import_sha25614 = require("@noble/hashes/sha256");
|
|
12025
12516
|
var import_utils20 = require("@noble/hashes/utils");
|
|
@@ -12055,7 +12546,7 @@ var Treasury = class _Treasury {
|
|
|
12055
12546
|
...m,
|
|
12056
12547
|
addedAt: now
|
|
12057
12548
|
})),
|
|
12058
|
-
defaultPrivacy: params.defaultPrivacy ??
|
|
12549
|
+
defaultPrivacy: params.defaultPrivacy ?? import_types20.PrivacyLevel.SHIELDED,
|
|
12059
12550
|
masterViewingKey,
|
|
12060
12551
|
dailyLimit: params.dailyLimit,
|
|
12061
12552
|
transactionLimit: params.transactionLimit,
|
|
@@ -12134,7 +12625,7 @@ var Treasury = class _Treasury {
|
|
|
12134
12625
|
proposalId,
|
|
12135
12626
|
treasuryId: this.config.treasuryId,
|
|
12136
12627
|
type: "payment",
|
|
12137
|
-
status:
|
|
12628
|
+
status: import_types20.ProposalStatus.PENDING,
|
|
12138
12629
|
proposer: "",
|
|
12139
12630
|
// Should be set by caller
|
|
12140
12631
|
title: params.title,
|
|
@@ -12167,7 +12658,7 @@ var Treasury = class _Treasury {
|
|
|
12167
12658
|
proposalId,
|
|
12168
12659
|
treasuryId: this.config.treasuryId,
|
|
12169
12660
|
type: "batch_payment",
|
|
12170
|
-
status:
|
|
12661
|
+
status: import_types20.ProposalStatus.PENDING,
|
|
12171
12662
|
proposer: "",
|
|
12172
12663
|
title: params.title,
|
|
12173
12664
|
description: params.description,
|
|
@@ -12201,7 +12692,7 @@ var Treasury = class _Treasury {
|
|
|
12201
12692
|
* Get pending proposals
|
|
12202
12693
|
*/
|
|
12203
12694
|
getPendingProposals() {
|
|
12204
|
-
return this.getAllProposals().filter((p) => p.status ===
|
|
12695
|
+
return this.getAllProposals().filter((p) => p.status === import_types20.ProposalStatus.PENDING);
|
|
12205
12696
|
}
|
|
12206
12697
|
/**
|
|
12207
12698
|
* Sign a proposal
|
|
@@ -12232,7 +12723,7 @@ var Treasury = class _Treasury {
|
|
|
12232
12723
|
"SIP_2001" /* INVALID_INPUT */
|
|
12233
12724
|
);
|
|
12234
12725
|
}
|
|
12235
|
-
if (proposal.status !==
|
|
12726
|
+
if (proposal.status !== import_types20.ProposalStatus.PENDING) {
|
|
12236
12727
|
throw new ValidationError(
|
|
12237
12728
|
`proposal is not pending: ${proposal.status}`,
|
|
12238
12729
|
"proposalId",
|
|
@@ -12242,7 +12733,7 @@ var Treasury = class _Treasury {
|
|
|
12242
12733
|
}
|
|
12243
12734
|
const now = Math.floor(Date.now() / 1e3);
|
|
12244
12735
|
if (now > proposal.expiresAt) {
|
|
12245
|
-
proposal.status =
|
|
12736
|
+
proposal.status = import_types20.ProposalStatus.EXPIRED;
|
|
12246
12737
|
throw new ValidationError(
|
|
12247
12738
|
"proposal has expired",
|
|
12248
12739
|
"proposalId",
|
|
@@ -12274,9 +12765,9 @@ var Treasury = class _Treasury {
|
|
|
12274
12765
|
const approvals = proposal.signatures.filter((s) => s.approved).length;
|
|
12275
12766
|
const rejections = proposal.signatures.filter((s) => !s.approved).length;
|
|
12276
12767
|
if (approvals >= proposal.requiredSignatures) {
|
|
12277
|
-
proposal.status =
|
|
12768
|
+
proposal.status = import_types20.ProposalStatus.APPROVED;
|
|
12278
12769
|
} else if (rejections > this.config.totalSigners - proposal.requiredSignatures) {
|
|
12279
|
-
proposal.status =
|
|
12770
|
+
proposal.status = import_types20.ProposalStatus.REJECTED;
|
|
12280
12771
|
}
|
|
12281
12772
|
return proposal;
|
|
12282
12773
|
}
|
|
@@ -12293,7 +12784,7 @@ var Treasury = class _Treasury {
|
|
|
12293
12784
|
"SIP_2001" /* INVALID_INPUT */
|
|
12294
12785
|
);
|
|
12295
12786
|
}
|
|
12296
|
-
if (proposal.status !==
|
|
12787
|
+
if (proposal.status !== import_types20.ProposalStatus.APPROVED) {
|
|
12297
12788
|
throw new ValidationError(
|
|
12298
12789
|
`proposal is not approved: ${proposal.status}`,
|
|
12299
12790
|
"proposalId",
|
|
@@ -12306,8 +12797,8 @@ var Treasury = class _Treasury {
|
|
|
12306
12797
|
const payment = await createShieldedPayment({
|
|
12307
12798
|
token: proposal.payment.token,
|
|
12308
12799
|
amount: proposal.payment.amount,
|
|
12309
|
-
recipientMetaAddress: proposal.payment.privacy !==
|
|
12310
|
-
recipientAddress: proposal.payment.privacy ===
|
|
12800
|
+
recipientMetaAddress: proposal.payment.privacy !== import_types20.PrivacyLevel.TRANSPARENT ? proposal.payment.recipient : void 0,
|
|
12801
|
+
recipientAddress: proposal.payment.privacy === import_types20.PrivacyLevel.TRANSPARENT ? proposal.payment.recipient : void 0,
|
|
12311
12802
|
privacy: proposal.payment.privacy,
|
|
12312
12803
|
viewingKey: this.config.masterViewingKey?.key,
|
|
12313
12804
|
sourceChain: this.config.chain,
|
|
@@ -12320,8 +12811,8 @@ var Treasury = class _Treasury {
|
|
|
12320
12811
|
const payment = await createShieldedPayment({
|
|
12321
12812
|
token: proposal.batchPayment.token,
|
|
12322
12813
|
amount: recipient.amount,
|
|
12323
|
-
recipientMetaAddress: proposal.batchPayment.privacy !==
|
|
12324
|
-
recipientAddress: proposal.batchPayment.privacy ===
|
|
12814
|
+
recipientMetaAddress: proposal.batchPayment.privacy !== import_types20.PrivacyLevel.TRANSPARENT ? recipient.address : void 0,
|
|
12815
|
+
recipientAddress: proposal.batchPayment.privacy === import_types20.PrivacyLevel.TRANSPARENT ? recipient.address : void 0,
|
|
12325
12816
|
privacy: proposal.batchPayment.privacy,
|
|
12326
12817
|
viewingKey: this.config.masterViewingKey?.key,
|
|
12327
12818
|
sourceChain: this.config.chain,
|
|
@@ -12331,7 +12822,7 @@ var Treasury = class _Treasury {
|
|
|
12331
12822
|
payments.push(payment);
|
|
12332
12823
|
}
|
|
12333
12824
|
}
|
|
12334
|
-
proposal.status =
|
|
12825
|
+
proposal.status = import_types20.ProposalStatus.EXECUTED;
|
|
12335
12826
|
proposal.executedAt = Math.floor(Date.now() / 1e3);
|
|
12336
12827
|
proposal.resultPayments = payments;
|
|
12337
12828
|
return payments;
|
|
@@ -12360,7 +12851,7 @@ var Treasury = class _Treasury {
|
|
|
12360
12851
|
"SIP_2001" /* INVALID_INPUT */
|
|
12361
12852
|
);
|
|
12362
12853
|
}
|
|
12363
|
-
if (proposal.status !==
|
|
12854
|
+
if (proposal.status !== import_types20.ProposalStatus.PENDING) {
|
|
12364
12855
|
throw new ValidationError(
|
|
12365
12856
|
`proposal is not pending: ${proposal.status}`,
|
|
12366
12857
|
"proposalId",
|
|
@@ -12368,7 +12859,7 @@ var Treasury = class _Treasury {
|
|
|
12368
12859
|
"SIP_2001" /* INVALID_INPUT */
|
|
12369
12860
|
);
|
|
12370
12861
|
}
|
|
12371
|
-
proposal.status =
|
|
12862
|
+
proposal.status = import_types20.ProposalStatus.CANCELLED;
|
|
12372
12863
|
return proposal;
|
|
12373
12864
|
}
|
|
12374
12865
|
// ─── Auditor Access ──────────────────────────────────────────────────────────
|
|
@@ -12465,7 +12956,7 @@ var Treasury = class _Treasury {
|
|
|
12465
12956
|
getCommittedAmount(token) {
|
|
12466
12957
|
let committed = 0n;
|
|
12467
12958
|
for (const proposal of this.proposals.values()) {
|
|
12468
|
-
if (proposal.status !==
|
|
12959
|
+
if (proposal.status !== import_types20.ProposalStatus.PENDING) continue;
|
|
12469
12960
|
if (proposal.type === "payment" && proposal.payment) {
|
|
12470
12961
|
if (proposal.payment.token.symbol === token.symbol && proposal.payment.token.chain === token.chain) {
|
|
12471
12962
|
committed += proposal.payment.amount;
|
|
@@ -12708,7 +13199,7 @@ function validateBatchProposalParams(params, config) {
|
|
|
12708
13199
|
}
|
|
12709
13200
|
|
|
12710
13201
|
// src/compliance/compliance-manager.ts
|
|
12711
|
-
var
|
|
13202
|
+
var import_types21 = require("@sip-protocol/types");
|
|
12712
13203
|
var import_utils21 = require("@noble/hashes/utils");
|
|
12713
13204
|
init_errors();
|
|
12714
13205
|
var DEFAULTS2 = {
|
|
@@ -13077,7 +13568,7 @@ var ComplianceManager = class _ComplianceManager {
|
|
|
13077
13568
|
title: params.title,
|
|
13078
13569
|
description: params.description,
|
|
13079
13570
|
format: params.format,
|
|
13080
|
-
status:
|
|
13571
|
+
status: import_types21.ReportStatus.GENERATING,
|
|
13081
13572
|
requestedBy,
|
|
13082
13573
|
requestedAt: now,
|
|
13083
13574
|
startDate: params.startDate,
|
|
@@ -13106,10 +13597,10 @@ var ComplianceManager = class _ComplianceManager {
|
|
|
13106
13597
|
} else if (params.format === "csv") {
|
|
13107
13598
|
report.content = this.generateCSV(transactions);
|
|
13108
13599
|
}
|
|
13109
|
-
report.status =
|
|
13600
|
+
report.status = import_types21.ReportStatus.COMPLETED;
|
|
13110
13601
|
report.generatedAt = Math.floor(Date.now() / 1e3);
|
|
13111
13602
|
} catch (error) {
|
|
13112
|
-
report.status =
|
|
13603
|
+
report.status = import_types21.ReportStatus.FAILED;
|
|
13113
13604
|
report.error = error instanceof Error ? error.message : "Unknown error";
|
|
13114
13605
|
}
|
|
13115
13606
|
this.addAuditLog(requestedBy, "report_generated", {
|
|
@@ -15180,54 +15671,359 @@ var AuditorKeyDerivation = class {
|
|
|
15180
15671
|
}
|
|
15181
15672
|
};
|
|
15182
15673
|
|
|
15183
|
-
// src/
|
|
15674
|
+
// src/compliance/range-sas.ts
|
|
15675
|
+
var import_sha25620 = require("@noble/hashes/sha256");
|
|
15676
|
+
var import_hmac3 = require("@noble/hashes/hmac");
|
|
15677
|
+
var import_sha5124 = require("@noble/hashes/sha512");
|
|
15184
15678
|
var import_utils27 = require("@noble/hashes/utils");
|
|
15185
15679
|
init_errors();
|
|
15186
|
-
|
|
15680
|
+
init_secure_memory();
|
|
15681
|
+
var AttestationSchema = /* @__PURE__ */ ((AttestationSchema2) => {
|
|
15682
|
+
AttestationSchema2["RANGE_KYC_V1"] = "range-kyc-v1";
|
|
15683
|
+
AttestationSchema2["RANGE_ACCREDITED_INVESTOR"] = "range-accredited-investor";
|
|
15684
|
+
AttestationSchema2["RANGE_INSTITUTIONAL"] = "range-institutional";
|
|
15685
|
+
AttestationSchema2["RANGE_REGULATOR"] = "range-regulator";
|
|
15686
|
+
AttestationSchema2["CUSTOM"] = "custom";
|
|
15687
|
+
return AttestationSchema2;
|
|
15688
|
+
})(AttestationSchema || {});
|
|
15689
|
+
var DEFAULT_MAX_CACHE_SIZE = 1e3;
|
|
15690
|
+
var AttestationGatedDisclosure = class {
|
|
15691
|
+
config;
|
|
15692
|
+
derivedKeys = /* @__PURE__ */ new Map();
|
|
15693
|
+
cacheOrder = [];
|
|
15694
|
+
// LRU tracking
|
|
15187
15695
|
/**
|
|
15188
|
-
* Create a
|
|
15189
|
-
*
|
|
15190
|
-
* Generates a cryptographically binding commitment to a bid amount.
|
|
15191
|
-
* The commitment can be published publicly without revealing the bid.
|
|
15696
|
+
* Create a new attestation-gated disclosure manager
|
|
15192
15697
|
*
|
|
15193
|
-
*
|
|
15194
|
-
|
|
15698
|
+
* @param config - Configuration options
|
|
15699
|
+
*/
|
|
15700
|
+
constructor(config) {
|
|
15701
|
+
if (!config.masterViewingKey) {
|
|
15702
|
+
throw new ValidationError(
|
|
15703
|
+
"masterViewingKey is required",
|
|
15704
|
+
"masterViewingKey",
|
|
15705
|
+
void 0,
|
|
15706
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
15707
|
+
);
|
|
15708
|
+
}
|
|
15709
|
+
this.config = {
|
|
15710
|
+
masterViewingKey: config.masterViewingKey,
|
|
15711
|
+
allowedSchemas: config.allowedSchemas ?? [],
|
|
15712
|
+
allowedIssuers: config.allowedIssuers ?? [],
|
|
15713
|
+
verifyOnChain: config.verifyOnChain ?? false,
|
|
15714
|
+
rangeApiEndpoint: config.rangeApiEndpoint ?? "https://api.range.org/v1",
|
|
15715
|
+
minAttestationAge: config.minAttestationAge ?? 0,
|
|
15716
|
+
maxAttestationAge: config.maxAttestationAge ?? 0,
|
|
15717
|
+
// 0 = no limit
|
|
15718
|
+
maxCacheSize: config.maxCacheSize ?? DEFAULT_MAX_CACHE_SIZE,
|
|
15719
|
+
customVerifier: config.customVerifier ?? (async () => true)
|
|
15720
|
+
};
|
|
15721
|
+
}
|
|
15722
|
+
/**
|
|
15723
|
+
* Derive a viewing key for a verified auditor
|
|
15195
15724
|
*
|
|
15196
|
-
* @param
|
|
15197
|
-
* @
|
|
15198
|
-
* @
|
|
15725
|
+
* @param attestation - The auditor's Range SAS attestation
|
|
15726
|
+
* @param scope - Optional scope restrictions for the viewing key
|
|
15727
|
+
* @returns Derivation result with viewing key if granted
|
|
15199
15728
|
*
|
|
15200
15729
|
* @example
|
|
15201
15730
|
* ```typescript
|
|
15202
|
-
* const
|
|
15203
|
-
*
|
|
15204
|
-
*
|
|
15205
|
-
* const receipt = auction.createBid({
|
|
15206
|
-
* auctionId: 'auction-xyz',
|
|
15207
|
-
* amount: 50n * 10n**18n,
|
|
15208
|
-
* })
|
|
15209
|
-
*
|
|
15210
|
-
* // Public data (safe to publish)
|
|
15211
|
-
* console.log({
|
|
15212
|
-
* auctionId: receipt.auctionId,
|
|
15213
|
-
* commitment: receipt.commitment,
|
|
15214
|
-
* timestamp: receipt.timestamp,
|
|
15731
|
+
* const result = await disclosure.deriveViewingKeyForAuditor(attestation, {
|
|
15732
|
+
* startTime: Date.now() / 1000 - 30 * 24 * 60 * 60, // Last 30 days
|
|
15733
|
+
* endTime: Date.now() / 1000,
|
|
15215
15734
|
* })
|
|
15216
15735
|
*
|
|
15217
|
-
*
|
|
15218
|
-
*
|
|
15219
|
-
*
|
|
15220
|
-
* salt: receipt.salt,
|
|
15221
|
-
* })
|
|
15736
|
+
* if (result.granted) {
|
|
15737
|
+
* // Share result.viewingKey with auditor
|
|
15738
|
+
* }
|
|
15222
15739
|
* ```
|
|
15223
15740
|
*/
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15741
|
+
async deriveViewingKeyForAuditor(attestation, scope) {
|
|
15742
|
+
const verification = await this.verifyAttestation(attestation);
|
|
15743
|
+
if (!verification.valid) {
|
|
15744
|
+
return {
|
|
15745
|
+
granted: false,
|
|
15746
|
+
reason: verification.errors.join("; ")
|
|
15747
|
+
};
|
|
15748
|
+
}
|
|
15749
|
+
const cacheKey = this.getCacheKey(attestation);
|
|
15750
|
+
const cached = this.derivedKeys.get(cacheKey);
|
|
15751
|
+
if (cached) {
|
|
15752
|
+
this.updateCacheOrder(cacheKey);
|
|
15753
|
+
return {
|
|
15754
|
+
granted: true,
|
|
15755
|
+
viewingKey: cached,
|
|
15756
|
+
scope,
|
|
15757
|
+
expiresAt: attestation.expiresAt
|
|
15758
|
+
// 0 = never expires, undefined = not set
|
|
15759
|
+
};
|
|
15760
|
+
}
|
|
15761
|
+
const viewingKey = this.deriveKeyFromAttestation(attestation);
|
|
15762
|
+
this.cacheKey(cacheKey, viewingKey);
|
|
15763
|
+
return {
|
|
15764
|
+
granted: true,
|
|
15765
|
+
viewingKey,
|
|
15766
|
+
scope,
|
|
15767
|
+
expiresAt: attestation.expiresAt
|
|
15768
|
+
// 0 = never expires, undefined = not set
|
|
15769
|
+
};
|
|
15770
|
+
}
|
|
15771
|
+
/**
|
|
15772
|
+
* Verify a Range SAS attestation
|
|
15773
|
+
*
|
|
15774
|
+
* @param attestation - The attestation to verify
|
|
15775
|
+
* @returns Verification result
|
|
15776
|
+
*/
|
|
15777
|
+
async verifyAttestation(attestation) {
|
|
15778
|
+
const errors = [];
|
|
15779
|
+
if (!attestation || typeof attestation !== "object") {
|
|
15780
|
+
return {
|
|
15781
|
+
valid: false,
|
|
15782
|
+
errors: ["Attestation must be an object"]
|
|
15783
|
+
};
|
|
15784
|
+
}
|
|
15785
|
+
if (!attestation.uid || typeof attestation.uid !== "string" || attestation.uid.trim() === "") {
|
|
15786
|
+
errors.push("Attestation uid is required and must be a non-empty string");
|
|
15787
|
+
}
|
|
15788
|
+
if (!attestation.subject || typeof attestation.subject !== "string" || attestation.subject.trim() === "") {
|
|
15789
|
+
errors.push("Attestation subject is required and must be a non-empty string");
|
|
15790
|
+
}
|
|
15791
|
+
if (!attestation.schema || typeof attestation.schema !== "string" || attestation.schema.trim() === "") {
|
|
15792
|
+
errors.push("Attestation schema is required and must be a non-empty string");
|
|
15793
|
+
}
|
|
15794
|
+
if (!attestation.issuer || typeof attestation.issuer !== "string" || attestation.issuer.trim() === "") {
|
|
15795
|
+
errors.push("Attestation issuer is required and must be a non-empty string");
|
|
15796
|
+
}
|
|
15797
|
+
if (errors.length > 0) {
|
|
15798
|
+
return { valid: false, errors };
|
|
15799
|
+
}
|
|
15800
|
+
if (attestation.revoked) {
|
|
15801
|
+
errors.push("Attestation has been revoked");
|
|
15802
|
+
}
|
|
15803
|
+
const now = Date.now() / 1e3;
|
|
15804
|
+
if (attestation.expiresAt > 0 && attestation.expiresAt < now) {
|
|
15805
|
+
errors.push("Attestation has expired");
|
|
15806
|
+
}
|
|
15807
|
+
const age = now - attestation.timestamp;
|
|
15808
|
+
if (age < this.config.minAttestationAge) {
|
|
15809
|
+
errors.push(`Attestation too new (age: ${age}s, required: ${this.config.minAttestationAge}s)`);
|
|
15810
|
+
}
|
|
15811
|
+
if (this.config.maxAttestationAge > 0 && age > this.config.maxAttestationAge) {
|
|
15812
|
+
errors.push(`Attestation too old (age: ${Math.floor(age)}s, max: ${this.config.maxAttestationAge}s)`);
|
|
15813
|
+
}
|
|
15814
|
+
if (this.config.allowedSchemas.length > 0) {
|
|
15815
|
+
if (!this.config.allowedSchemas.includes(attestation.schema)) {
|
|
15816
|
+
errors.push(`Schema '${attestation.schema}' not in allowed list`);
|
|
15817
|
+
}
|
|
15818
|
+
}
|
|
15819
|
+
if (this.config.allowedIssuers.length > 0) {
|
|
15820
|
+
if (!this.config.allowedIssuers.includes(attestation.issuer)) {
|
|
15821
|
+
errors.push(`Issuer '${attestation.issuer}' not in allowed list`);
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
if (errors.length === 0 && this.config.customVerifier) {
|
|
15825
|
+
try {
|
|
15826
|
+
const customValid = await this.config.customVerifier(attestation);
|
|
15827
|
+
if (!customValid) {
|
|
15828
|
+
errors.push("Custom verification failed");
|
|
15829
|
+
}
|
|
15830
|
+
} catch (e) {
|
|
15831
|
+
errors.push(`Custom verification error: ${e instanceof Error ? e.message : "unknown"}`);
|
|
15832
|
+
}
|
|
15833
|
+
}
|
|
15834
|
+
return {
|
|
15835
|
+
valid: errors.length === 0,
|
|
15836
|
+
errors,
|
|
15837
|
+
metadata: {
|
|
15838
|
+
verificationMethod: this.config.verifyOnChain ? "on-chain" : "api",
|
|
15839
|
+
schemaVersion: attestation.schema
|
|
15840
|
+
}
|
|
15841
|
+
};
|
|
15842
|
+
}
|
|
15843
|
+
/**
|
|
15844
|
+
* Revoke a previously derived viewing key
|
|
15845
|
+
*
|
|
15846
|
+
* @param attestation - The attestation whose key should be revoked
|
|
15847
|
+
* @returns Whether revocation was successful
|
|
15848
|
+
*/
|
|
15849
|
+
revokeViewingKey(attestation) {
|
|
15850
|
+
const key = this.getCacheKey(attestation);
|
|
15851
|
+
const deleted = this.derivedKeys.delete(key);
|
|
15852
|
+
if (deleted) {
|
|
15853
|
+
const index = this.cacheOrder.indexOf(key);
|
|
15854
|
+
if (index !== -1) {
|
|
15855
|
+
this.cacheOrder.splice(index, 1);
|
|
15856
|
+
}
|
|
15857
|
+
}
|
|
15858
|
+
return deleted;
|
|
15859
|
+
}
|
|
15860
|
+
/**
|
|
15861
|
+
* Check if a viewing key has been derived for an attestation
|
|
15862
|
+
*
|
|
15863
|
+
* @param attestation - The attestation to check
|
|
15864
|
+
* @returns Whether a key exists
|
|
15865
|
+
*/
|
|
15866
|
+
hasViewingKey(attestation) {
|
|
15867
|
+
const key = this.getCacheKey(attestation);
|
|
15868
|
+
return this.derivedKeys.has(key);
|
|
15869
|
+
}
|
|
15870
|
+
/**
|
|
15871
|
+
* Get the current cache size
|
|
15872
|
+
*
|
|
15873
|
+
* @returns Number of cached viewing keys
|
|
15874
|
+
*/
|
|
15875
|
+
getCacheSize() {
|
|
15876
|
+
return this.derivedKeys.size;
|
|
15877
|
+
}
|
|
15878
|
+
/**
|
|
15879
|
+
* Clear all cached viewing keys
|
|
15880
|
+
*/
|
|
15881
|
+
clearCache() {
|
|
15882
|
+
this.derivedKeys.clear();
|
|
15883
|
+
this.cacheOrder.length = 0;
|
|
15884
|
+
}
|
|
15885
|
+
// ─── Private Methods ────────────────────────────────────────────────────────
|
|
15886
|
+
/**
|
|
15887
|
+
* Add a key to cache with LRU eviction
|
|
15888
|
+
*/
|
|
15889
|
+
cacheKey(key, viewingKey) {
|
|
15890
|
+
while (this.derivedKeys.size >= this.config.maxCacheSize && this.cacheOrder.length > 0) {
|
|
15891
|
+
const oldest = this.cacheOrder.shift();
|
|
15892
|
+
if (oldest) {
|
|
15893
|
+
this.derivedKeys.delete(oldest);
|
|
15894
|
+
}
|
|
15895
|
+
}
|
|
15896
|
+
this.derivedKeys.set(key, viewingKey);
|
|
15897
|
+
this.cacheOrder.push(key);
|
|
15898
|
+
}
|
|
15899
|
+
/**
|
|
15900
|
+
* Update LRU order for a cache key (move to end)
|
|
15901
|
+
*/
|
|
15902
|
+
updateCacheOrder(key) {
|
|
15903
|
+
const index = this.cacheOrder.indexOf(key);
|
|
15904
|
+
if (index !== -1) {
|
|
15905
|
+
this.cacheOrder.splice(index, 1);
|
|
15906
|
+
this.cacheOrder.push(key);
|
|
15907
|
+
}
|
|
15908
|
+
}
|
|
15909
|
+
/**
|
|
15910
|
+
* Derive a viewing key from an attestation
|
|
15911
|
+
*/
|
|
15912
|
+
deriveKeyFromAttestation(attestation) {
|
|
15913
|
+
const masterKeyHex = this.config.masterViewingKey.key.startsWith("0x") ? this.config.masterViewingKey.key.slice(2) : this.config.masterViewingKey.key;
|
|
15914
|
+
const masterKeyBytes = (0, import_utils27.hexToBytes)(masterKeyHex);
|
|
15915
|
+
const derivationData = (0, import_utils27.utf8ToBytes)(
|
|
15916
|
+
`SIP-RANGE-SAS:${attestation.uid}:${attestation.subject}:${attestation.schema}:${attestation.signature}`
|
|
15917
|
+
);
|
|
15918
|
+
const derived = (0, import_hmac3.hmac)(import_sha5124.sha512, masterKeyBytes, derivationData);
|
|
15919
|
+
const keyBytes = derived.slice(0, 32);
|
|
15920
|
+
try {
|
|
15921
|
+
const key = `0x${(0, import_utils27.bytesToHex)(keyBytes)}`;
|
|
15922
|
+
const hashBytes = (0, import_sha25620.sha256)(keyBytes);
|
|
15923
|
+
const hash2 = `0x${(0, import_utils27.bytesToHex)(hashBytes)}`;
|
|
15924
|
+
return {
|
|
15925
|
+
key,
|
|
15926
|
+
path: `${this.config.masterViewingKey.path}/sas/${attestation.uid.slice(0, 8)}`,
|
|
15927
|
+
hash: hash2
|
|
15928
|
+
};
|
|
15929
|
+
} finally {
|
|
15930
|
+
secureWipe(masterKeyBytes);
|
|
15931
|
+
secureWipe(derived);
|
|
15932
|
+
secureWipe(keyBytes);
|
|
15933
|
+
}
|
|
15934
|
+
}
|
|
15935
|
+
/**
|
|
15936
|
+
* Get cache key for an attestation
|
|
15937
|
+
*
|
|
15938
|
+
* Includes schema and issuer to prevent cache poisoning attacks where
|
|
15939
|
+
* an attacker could evict legitimate cache entries with same uid:subject.
|
|
15940
|
+
*/
|
|
15941
|
+
getCacheKey(attestation) {
|
|
15942
|
+
return `${attestation.uid}:${attestation.subject}:${attestation.schema}:${attestation.issuer}`;
|
|
15943
|
+
}
|
|
15944
|
+
};
|
|
15945
|
+
function createMockAttestation(overrides = {}) {
|
|
15946
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
15947
|
+
return {
|
|
15948
|
+
uid: `sas_${Math.random().toString(36).slice(2, 10)}`,
|
|
15949
|
+
schema: "range-kyc-v1" /* RANGE_KYC_V1 */,
|
|
15950
|
+
issuer: "range-protocol",
|
|
15951
|
+
subject: "11111111111111111111111111111112",
|
|
15952
|
+
// System program (placeholder)
|
|
15953
|
+
data: {
|
|
15954
|
+
level: "institutional",
|
|
15955
|
+
jurisdiction: "US",
|
|
15956
|
+
verifiedAt: now
|
|
15957
|
+
},
|
|
15958
|
+
timestamp: now,
|
|
15959
|
+
expiresAt: now + 365 * 24 * 60 * 60,
|
|
15960
|
+
// 1 year
|
|
15961
|
+
signature: "0x" + "00".repeat(64),
|
|
15962
|
+
revoked: false,
|
|
15963
|
+
...overrides
|
|
15964
|
+
};
|
|
15965
|
+
}
|
|
15966
|
+
async function verifyAttestationSignature(_attestation) {
|
|
15967
|
+
console.warn(
|
|
15968
|
+
"[Range SAS] verifyAttestationSignature is a STUB - always returns true. Implement real Ed25519 signature verification before production use."
|
|
15969
|
+
);
|
|
15970
|
+
return true;
|
|
15971
|
+
}
|
|
15972
|
+
async function fetchAttestation(uid, apiEndpoint = "https://api.range.org/v1") {
|
|
15973
|
+
console.warn(
|
|
15974
|
+
`[Range SAS] fetchAttestation is a STUB - returning null for ${uid}. Would fetch from ${apiEndpoint}. Implement Range API integration before production use.`
|
|
15975
|
+
);
|
|
15976
|
+
return null;
|
|
15977
|
+
}
|
|
15978
|
+
|
|
15979
|
+
// src/auction/sealed-bid.ts
|
|
15980
|
+
var import_utils28 = require("@noble/hashes/utils");
|
|
15981
|
+
init_errors();
|
|
15982
|
+
var SealedBidAuction = class {
|
|
15983
|
+
/**
|
|
15984
|
+
* Create a sealed bid for an auction
|
|
15985
|
+
*
|
|
15986
|
+
* Generates a cryptographically binding commitment to a bid amount.
|
|
15987
|
+
* The commitment can be published publicly without revealing the bid.
|
|
15988
|
+
*
|
|
15989
|
+
* **Important:** Keep the returned `BidReceipt` secret! It contains the bid
|
|
15990
|
+
* amount and salt needed to reveal the bid later. Only publish the commitment.
|
|
15991
|
+
*
|
|
15992
|
+
* @param params - Bid creation parameters
|
|
15993
|
+
* @returns Complete bid receipt (keep secret!) and sealed bid (publish this)
|
|
15994
|
+
* @throws {ValidationError} If auctionId is empty, amount is non-positive, or salt is invalid
|
|
15995
|
+
*
|
|
15996
|
+
* @example
|
|
15997
|
+
* ```typescript
|
|
15998
|
+
* const auction = new SealedBidAuction()
|
|
15999
|
+
*
|
|
16000
|
+
* // Create a bid for 50 ETH
|
|
16001
|
+
* const receipt = auction.createBid({
|
|
16002
|
+
* auctionId: 'auction-xyz',
|
|
16003
|
+
* amount: 50n * 10n**18n,
|
|
16004
|
+
* })
|
|
16005
|
+
*
|
|
16006
|
+
* // Public data (safe to publish)
|
|
16007
|
+
* console.log({
|
|
16008
|
+
* auctionId: receipt.auctionId,
|
|
16009
|
+
* commitment: receipt.commitment,
|
|
16010
|
+
* timestamp: receipt.timestamp,
|
|
16011
|
+
* })
|
|
16012
|
+
*
|
|
16013
|
+
* // Secret data (store securely, needed for reveal)
|
|
16014
|
+
* secureStorage.save({
|
|
16015
|
+
* amount: receipt.amount,
|
|
16016
|
+
* salt: receipt.salt,
|
|
16017
|
+
* })
|
|
16018
|
+
* ```
|
|
16019
|
+
*/
|
|
16020
|
+
createBid(params) {
|
|
16021
|
+
if (typeof params.auctionId !== "string" || params.auctionId.length === 0) {
|
|
16022
|
+
throw new ValidationError(
|
|
16023
|
+
"auctionId must be a non-empty string",
|
|
16024
|
+
"auctionId",
|
|
16025
|
+
{ received: params.auctionId }
|
|
16026
|
+
);
|
|
15231
16027
|
}
|
|
15232
16028
|
if (typeof params.amount !== "bigint") {
|
|
15233
16029
|
throw new ValidationError(
|
|
@@ -15259,7 +16055,7 @@ var SealedBidAuction = class {
|
|
|
15259
16055
|
);
|
|
15260
16056
|
}
|
|
15261
16057
|
}
|
|
15262
|
-
const salt = params.salt ?? (0,
|
|
16058
|
+
const salt = params.salt ?? (0, import_utils28.randomBytes)(32);
|
|
15263
16059
|
const { commitment, blinding } = commit(params.amount, salt);
|
|
15264
16060
|
const sealedBid = {
|
|
15265
16061
|
auctionId: params.auctionId,
|
|
@@ -15403,7 +16199,7 @@ var SealedBidAuction = class {
|
|
|
15403
16199
|
* ```
|
|
15404
16200
|
*/
|
|
15405
16201
|
revealBid(bid, amount, salt) {
|
|
15406
|
-
const saltHex = `0x${(0,
|
|
16202
|
+
const saltHex = `0x${(0, import_utils28.bytesToHex)(salt)}`;
|
|
15407
16203
|
const isValid = this.verifyBid({
|
|
15408
16204
|
commitment: bid.commitment,
|
|
15409
16205
|
amount,
|
|
@@ -15888,9 +16684,9 @@ function createSealedBidAuction() {
|
|
|
15888
16684
|
}
|
|
15889
16685
|
|
|
15890
16686
|
// src/governance/private-vote.ts
|
|
15891
|
-
var
|
|
16687
|
+
var import_sha25621 = require("@noble/hashes/sha256");
|
|
15892
16688
|
var import_hkdf3 = require("@noble/hashes/hkdf");
|
|
15893
|
-
var
|
|
16689
|
+
var import_utils29 = require("@noble/hashes/utils");
|
|
15894
16690
|
var import_chacha4 = require("@noble/ciphers/chacha.js");
|
|
15895
16691
|
init_errors();
|
|
15896
16692
|
init_secure_memory();
|
|
@@ -15930,7 +16726,7 @@ var PrivateVoting = class {
|
|
|
15930
16726
|
const { proposalId, choice, weight, encryptionKey, voter = "anonymous" } = params;
|
|
15931
16727
|
const derivedKey = this.deriveEncryptionKey(encryptionKey, proposalId);
|
|
15932
16728
|
try {
|
|
15933
|
-
const nonce = (0,
|
|
16729
|
+
const nonce = (0, import_utils29.randomBytes)(NONCE_SIZE2);
|
|
15934
16730
|
const voteData = {
|
|
15935
16731
|
proposalId,
|
|
15936
16732
|
choice,
|
|
@@ -15938,14 +16734,14 @@ var PrivateVoting = class {
|
|
|
15938
16734
|
voter,
|
|
15939
16735
|
timestamp: Date.now()
|
|
15940
16736
|
};
|
|
15941
|
-
const plaintext = (0,
|
|
16737
|
+
const plaintext = (0, import_utils29.utf8ToBytes)(JSON.stringify(voteData));
|
|
15942
16738
|
const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
|
|
15943
16739
|
const ciphertext = cipher.encrypt(plaintext);
|
|
15944
|
-
const keyHash = (0,
|
|
16740
|
+
const keyHash = (0, import_sha25621.sha256)((0, import_utils29.hexToBytes)(encryptionKey.slice(2)));
|
|
15945
16741
|
return {
|
|
15946
|
-
ciphertext: `0x${(0,
|
|
15947
|
-
nonce: `0x${(0,
|
|
15948
|
-
encryptionKeyHash: `0x${(0,
|
|
16742
|
+
ciphertext: `0x${(0, import_utils29.bytesToHex)(ciphertext)}`,
|
|
16743
|
+
nonce: `0x${(0, import_utils29.bytesToHex)(nonce)}`,
|
|
16744
|
+
encryptionKeyHash: `0x${(0, import_utils29.bytesToHex)(keyHash)}`,
|
|
15949
16745
|
proposalId,
|
|
15950
16746
|
voter,
|
|
15951
16747
|
timestamp: voteData.timestamp
|
|
@@ -15991,8 +16787,8 @@ var PrivateVoting = class {
|
|
|
15991
16787
|
}
|
|
15992
16788
|
const derivedKey = this.deriveEncryptionKey(decryptionKey, vote.proposalId);
|
|
15993
16789
|
try {
|
|
15994
|
-
const keyHash = (0,
|
|
15995
|
-
const expectedKeyHash = `0x${(0,
|
|
16790
|
+
const keyHash = (0, import_sha25621.sha256)((0, import_utils29.hexToBytes)(decryptionKey.slice(2)));
|
|
16791
|
+
const expectedKeyHash = `0x${(0, import_utils29.bytesToHex)(keyHash)}`;
|
|
15996
16792
|
if (vote.encryptionKeyHash !== expectedKeyHash) {
|
|
15997
16793
|
throw new CryptoError(
|
|
15998
16794
|
"Decryption key hash mismatch - this key cannot decrypt this vote",
|
|
@@ -16001,9 +16797,9 @@ var PrivateVoting = class {
|
|
|
16001
16797
|
);
|
|
16002
16798
|
}
|
|
16003
16799
|
const nonceHex = vote.nonce.startsWith("0x") ? vote.nonce.slice(2) : vote.nonce;
|
|
16004
|
-
const nonce = (0,
|
|
16800
|
+
const nonce = (0, import_utils29.hexToBytes)(nonceHex);
|
|
16005
16801
|
const ciphertextHex = vote.ciphertext.startsWith("0x") ? vote.ciphertext.slice(2) : vote.ciphertext;
|
|
16006
|
-
const ciphertext = (0,
|
|
16802
|
+
const ciphertext = (0, import_utils29.hexToBytes)(ciphertextHex);
|
|
16007
16803
|
const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
|
|
16008
16804
|
let plaintext;
|
|
16009
16805
|
try {
|
|
@@ -16092,11 +16888,11 @@ var PrivateVoting = class {
|
|
|
16092
16888
|
*/
|
|
16093
16889
|
deriveEncryptionKey(key, proposalId) {
|
|
16094
16890
|
const keyHex = key.startsWith("0x") ? key.slice(2) : key;
|
|
16095
|
-
const keyBytes = (0,
|
|
16891
|
+
const keyBytes = (0, import_utils29.hexToBytes)(keyHex);
|
|
16096
16892
|
try {
|
|
16097
|
-
const salt = (0,
|
|
16098
|
-
const info = (0,
|
|
16099
|
-
return (0, import_hkdf3.hkdf)(
|
|
16893
|
+
const salt = (0, import_utils29.utf8ToBytes)(VOTE_ENCRYPTION_DOMAIN);
|
|
16894
|
+
const info = (0, import_utils29.utf8ToBytes)(proposalId);
|
|
16895
|
+
return (0, import_hkdf3.hkdf)(import_sha25621.sha256, keyBytes, salt, info, 32);
|
|
16100
16896
|
} finally {
|
|
16101
16897
|
secureWipe(keyBytes);
|
|
16102
16898
|
}
|
|
@@ -16241,21 +17037,21 @@ var PrivateVoting = class {
|
|
|
16241
17037
|
const blindings = {};
|
|
16242
17038
|
for (const [choice, weights] of Object.entries(votesByChoice)) {
|
|
16243
17039
|
const totalWeight = weights.reduce((sum, w) => sum + w, 0n);
|
|
16244
|
-
const { commitment, blinding } = commit(totalWeight, (0,
|
|
17040
|
+
const { commitment, blinding } = commit(totalWeight, (0, import_utils29.hexToBytes)(generateBlinding().slice(2)));
|
|
16245
17041
|
tallies[choice] = commitment;
|
|
16246
17042
|
blindings[choice] = blinding;
|
|
16247
17043
|
}
|
|
16248
17044
|
const encryptedBlindings = {};
|
|
16249
17045
|
for (const [choice, blinding] of Object.entries(blindings)) {
|
|
16250
|
-
const nonce = (0,
|
|
17046
|
+
const nonce = (0, import_utils29.randomBytes)(NONCE_SIZE2);
|
|
16251
17047
|
const derivedKey = this.deriveEncryptionKey(decryptionKey, `${proposalId}-tally-${choice}`);
|
|
16252
17048
|
try {
|
|
16253
17049
|
const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonce);
|
|
16254
|
-
const blindingBytes = (0,
|
|
17050
|
+
const blindingBytes = (0, import_utils29.hexToBytes)(blinding.slice(2));
|
|
16255
17051
|
const ciphertext = cipher.encrypt(blindingBytes);
|
|
16256
17052
|
encryptedBlindings[choice] = {
|
|
16257
|
-
ciphertext: `0x${(0,
|
|
16258
|
-
nonce: `0x${(0,
|
|
17053
|
+
ciphertext: `0x${(0, import_utils29.bytesToHex)(ciphertext)}`,
|
|
17054
|
+
nonce: `0x${(0, import_utils29.bytesToHex)(nonce)}`
|
|
16259
17055
|
};
|
|
16260
17056
|
} finally {
|
|
16261
17057
|
secureWipe(derivedKey);
|
|
@@ -16348,9 +17144,9 @@ var PrivateVoting = class {
|
|
|
16348
17144
|
}
|
|
16349
17145
|
let reconstructedKey = null;
|
|
16350
17146
|
try {
|
|
16351
|
-
reconstructedKey = (0,
|
|
17147
|
+
reconstructedKey = (0, import_utils29.hexToBytes)(decryptionShares[0].share.slice(2));
|
|
16352
17148
|
for (let i = 1; i < decryptionShares.length; i++) {
|
|
16353
|
-
const shareBytes = (0,
|
|
17149
|
+
const shareBytes = (0, import_utils29.hexToBytes)(decryptionShares[i].share.slice(2));
|
|
16354
17150
|
if (shareBytes.length !== reconstructedKey.length) {
|
|
16355
17151
|
throw new ValidationError(
|
|
16356
17152
|
"all decryption shares must have the same length",
|
|
@@ -16363,7 +17159,7 @@ var PrivateVoting = class {
|
|
|
16363
17159
|
reconstructedKey[j] ^= shareBytes[j];
|
|
16364
17160
|
}
|
|
16365
17161
|
}
|
|
16366
|
-
const reconstructedKeyHex = `0x${(0,
|
|
17162
|
+
const reconstructedKeyHex = `0x${(0, import_utils29.bytesToHex)(reconstructedKey)}`;
|
|
16367
17163
|
const results = {};
|
|
16368
17164
|
for (const [choice, commitmentPoint] of Object.entries(tally.tallies)) {
|
|
16369
17165
|
const encBlinding = tally.encryptedBlindings[choice];
|
|
@@ -16380,11 +17176,11 @@ var PrivateVoting = class {
|
|
|
16380
17176
|
);
|
|
16381
17177
|
let blindingFactor;
|
|
16382
17178
|
try {
|
|
16383
|
-
const nonceBytes = (0,
|
|
16384
|
-
const ciphertextBytes = (0,
|
|
17179
|
+
const nonceBytes = (0, import_utils29.hexToBytes)(encBlinding.nonce.slice(2));
|
|
17180
|
+
const ciphertextBytes = (0, import_utils29.hexToBytes)(encBlinding.ciphertext.slice(2));
|
|
16385
17181
|
const cipher = (0, import_chacha4.xchacha20poly1305)(derivedKey, nonceBytes);
|
|
16386
17182
|
const blindingBytes = cipher.decrypt(ciphertextBytes);
|
|
16387
|
-
blindingFactor = `0x${(0,
|
|
17183
|
+
blindingFactor = `0x${(0, import_utils29.bytesToHex)(blindingBytes)}`;
|
|
16388
17184
|
} catch (e) {
|
|
16389
17185
|
throw new CryptoError(
|
|
16390
17186
|
"failed to decrypt blinding factor",
|
|
@@ -16404,7 +17200,7 @@ var PrivateVoting = class {
|
|
|
16404
17200
|
try {
|
|
16405
17201
|
const { commitment: testCommit } = commit(
|
|
16406
17202
|
value,
|
|
16407
|
-
(0,
|
|
17203
|
+
(0, import_utils29.hexToBytes)(blindingFactor.slice(2))
|
|
16408
17204
|
);
|
|
16409
17205
|
if (testCommit === commitmentPoint) {
|
|
16410
17206
|
results[choice] = value;
|
|
@@ -16604,9 +17400,9 @@ function createPrivateVoting() {
|
|
|
16604
17400
|
}
|
|
16605
17401
|
|
|
16606
17402
|
// src/nft/private-nft.ts
|
|
16607
|
-
var
|
|
17403
|
+
var import_sha25622 = require("@noble/hashes/sha256");
|
|
16608
17404
|
var import_secp256k18 = require("@noble/curves/secp256k1");
|
|
16609
|
-
var
|
|
17405
|
+
var import_utils30 = require("@noble/hashes/utils");
|
|
16610
17406
|
init_stealth();
|
|
16611
17407
|
init_errors();
|
|
16612
17408
|
init_validation();
|
|
@@ -16695,23 +17491,23 @@ var PrivateNFT = class {
|
|
|
16695
17491
|
const { ownership, challenge, stealthPrivateKey } = params;
|
|
16696
17492
|
try {
|
|
16697
17493
|
const message = this.createProofMessage(ownership, challenge);
|
|
16698
|
-
const messageHash = (0,
|
|
16699
|
-
const privateKeyBytes = (0,
|
|
17494
|
+
const messageHash = (0, import_sha25622.sha256)(new TextEncoder().encode(message));
|
|
17495
|
+
const privateKeyBytes = (0, import_utils30.hexToBytes)(stealthPrivateKey.slice(2));
|
|
16700
17496
|
const signature = import_secp256k18.secp256k1.sign(messageHash, privateKeyBytes);
|
|
16701
17497
|
const zkProof = {
|
|
16702
17498
|
type: "ownership",
|
|
16703
|
-
proof: `0x${(0,
|
|
17499
|
+
proof: `0x${(0, import_utils30.bytesToHex)(signature.toCompactRawBytes())}`,
|
|
16704
17500
|
publicInputs: [
|
|
16705
|
-
`0x${(0,
|
|
17501
|
+
`0x${(0, import_utils30.bytesToHex)(messageHash)}`
|
|
16706
17502
|
]
|
|
16707
17503
|
};
|
|
16708
|
-
const stealthHashBytes = (0,
|
|
17504
|
+
const stealthHashBytes = (0, import_sha25622.sha256)((0, import_utils30.hexToBytes)(ownership.ownerStealth.address.slice(2)));
|
|
16709
17505
|
return {
|
|
16710
17506
|
nftContract: ownership.nftContract,
|
|
16711
17507
|
tokenId: ownership.tokenId,
|
|
16712
17508
|
challenge,
|
|
16713
17509
|
proof: zkProof,
|
|
16714
|
-
stealthHash: `0x${(0,
|
|
17510
|
+
stealthHash: `0x${(0, import_utils30.bytesToHex)(stealthHashBytes)}`,
|
|
16715
17511
|
timestamp: Date.now()
|
|
16716
17512
|
};
|
|
16717
17513
|
} catch (e) {
|
|
@@ -16753,9 +17549,9 @@ var PrivateNFT = class {
|
|
|
16753
17549
|
verifyOwnership(proof) {
|
|
16754
17550
|
try {
|
|
16755
17551
|
this.validateOwnershipProof(proof);
|
|
16756
|
-
const signatureBytes = (0,
|
|
17552
|
+
const signatureBytes = (0, import_utils30.hexToBytes)(proof.proof.proof.slice(2));
|
|
16757
17553
|
const signature = import_secp256k18.secp256k1.Signature.fromCompact(signatureBytes);
|
|
16758
|
-
const messageHash = (0,
|
|
17554
|
+
const messageHash = (0, import_utils30.hexToBytes)(proof.proof.publicInputs[0].slice(2));
|
|
16759
17555
|
if (signatureBytes.length !== 64) {
|
|
16760
17556
|
return {
|
|
16761
17557
|
valid: false,
|
|
@@ -16852,12 +17648,12 @@ var PrivateNFT = class {
|
|
|
16852
17648
|
chain: nft.chain,
|
|
16853
17649
|
timestamp: Date.now()
|
|
16854
17650
|
};
|
|
16855
|
-
const previousOwnerHashBytes = (0,
|
|
17651
|
+
const previousOwnerHashBytes = (0, import_sha25622.sha256)((0, import_utils30.hexToBytes)(nft.ownerStealth.address.slice(2)));
|
|
16856
17652
|
const transfer = {
|
|
16857
17653
|
nftContract: nft.nftContract,
|
|
16858
17654
|
tokenId: nft.tokenId,
|
|
16859
17655
|
newOwnerStealth,
|
|
16860
|
-
previousOwnerHash: `0x${(0,
|
|
17656
|
+
previousOwnerHash: `0x${(0, import_utils30.bytesToHex)(previousOwnerHashBytes)}`,
|
|
16861
17657
|
chain: nft.chain,
|
|
16862
17658
|
timestamp: Date.now()
|
|
16863
17659
|
};
|
|
@@ -16924,8 +17720,8 @@ var PrivateNFT = class {
|
|
|
16924
17720
|
);
|
|
16925
17721
|
}
|
|
16926
17722
|
const ownedNFTs = [];
|
|
16927
|
-
const scanKeyHex = `0x${(0,
|
|
16928
|
-
const viewingKeyHex = `0x${(0,
|
|
17723
|
+
const scanKeyHex = `0x${(0, import_utils30.bytesToHex)(scanKey)}`;
|
|
17724
|
+
const viewingKeyHex = `0x${(0, import_utils30.bytesToHex)(viewingKey)}`;
|
|
16929
17725
|
for (const transfer of transfers) {
|
|
16930
17726
|
try {
|
|
16931
17727
|
if (!transfer || typeof transfer !== "object") {
|
|
@@ -17143,10 +17939,10 @@ function verifyOwnership(proof) {
|
|
|
17143
17939
|
|
|
17144
17940
|
// src/wallet/errors.ts
|
|
17145
17941
|
init_errors();
|
|
17146
|
-
var
|
|
17942
|
+
var import_types22 = require("@sip-protocol/types");
|
|
17147
17943
|
var WalletError = class extends SIPError {
|
|
17148
17944
|
walletCode;
|
|
17149
|
-
constructor(message, walletCode =
|
|
17945
|
+
constructor(message, walletCode = import_types22.WalletErrorCode.UNKNOWN, options) {
|
|
17150
17946
|
super(message, "SIP_7000" /* WALLET_ERROR */, options);
|
|
17151
17947
|
this.walletCode = walletCode;
|
|
17152
17948
|
this.name = "WalletError";
|
|
@@ -17156,10 +17952,10 @@ var WalletError = class extends SIPError {
|
|
|
17156
17952
|
*/
|
|
17157
17953
|
isConnectionError() {
|
|
17158
17954
|
const codes = [
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17955
|
+
import_types22.WalletErrorCode.NOT_INSTALLED,
|
|
17956
|
+
import_types22.WalletErrorCode.CONNECTION_REJECTED,
|
|
17957
|
+
import_types22.WalletErrorCode.CONNECTION_FAILED,
|
|
17958
|
+
import_types22.WalletErrorCode.NOT_CONNECTED
|
|
17163
17959
|
];
|
|
17164
17960
|
return codes.includes(this.walletCode);
|
|
17165
17961
|
}
|
|
@@ -17168,9 +17964,9 @@ var WalletError = class extends SIPError {
|
|
|
17168
17964
|
*/
|
|
17169
17965
|
isSigningError() {
|
|
17170
17966
|
const codes = [
|
|
17171
|
-
|
|
17172
|
-
|
|
17173
|
-
|
|
17967
|
+
import_types22.WalletErrorCode.SIGNING_REJECTED,
|
|
17968
|
+
import_types22.WalletErrorCode.SIGNING_FAILED,
|
|
17969
|
+
import_types22.WalletErrorCode.INVALID_MESSAGE
|
|
17174
17970
|
];
|
|
17175
17971
|
return codes.includes(this.walletCode);
|
|
17176
17972
|
}
|
|
@@ -17179,10 +17975,10 @@ var WalletError = class extends SIPError {
|
|
|
17179
17975
|
*/
|
|
17180
17976
|
isTransactionError() {
|
|
17181
17977
|
const codes = [
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17978
|
+
import_types22.WalletErrorCode.INSUFFICIENT_FUNDS,
|
|
17979
|
+
import_types22.WalletErrorCode.TRANSACTION_REJECTED,
|
|
17980
|
+
import_types22.WalletErrorCode.TRANSACTION_FAILED,
|
|
17981
|
+
import_types22.WalletErrorCode.INVALID_TRANSACTION
|
|
17186
17982
|
];
|
|
17187
17983
|
return codes.includes(this.walletCode);
|
|
17188
17984
|
}
|
|
@@ -17191,9 +17987,9 @@ var WalletError = class extends SIPError {
|
|
|
17191
17987
|
*/
|
|
17192
17988
|
isPrivacyError() {
|
|
17193
17989
|
const codes = [
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17990
|
+
import_types22.WalletErrorCode.STEALTH_NOT_SUPPORTED,
|
|
17991
|
+
import_types22.WalletErrorCode.VIEWING_KEY_NOT_SUPPORTED,
|
|
17992
|
+
import_types22.WalletErrorCode.SHIELDED_NOT_SUPPORTED
|
|
17197
17993
|
];
|
|
17198
17994
|
return codes.includes(this.walletCode);
|
|
17199
17995
|
}
|
|
@@ -17202,10 +17998,10 @@ var WalletError = class extends SIPError {
|
|
|
17202
17998
|
*/
|
|
17203
17999
|
isUserRejection() {
|
|
17204
18000
|
const codes = [
|
|
17205
|
-
|
|
17206
|
-
|
|
17207
|
-
|
|
17208
|
-
|
|
18001
|
+
import_types22.WalletErrorCode.CONNECTION_REJECTED,
|
|
18002
|
+
import_types22.WalletErrorCode.SIGNING_REJECTED,
|
|
18003
|
+
import_types22.WalletErrorCode.TRANSACTION_REJECTED,
|
|
18004
|
+
import_types22.WalletErrorCode.CHAIN_SWITCH_REJECTED
|
|
17209
18005
|
];
|
|
17210
18006
|
return codes.includes(this.walletCode);
|
|
17211
18007
|
}
|
|
@@ -17213,15 +18009,15 @@ var WalletError = class extends SIPError {
|
|
|
17213
18009
|
function notConnectedError() {
|
|
17214
18010
|
return new WalletError(
|
|
17215
18011
|
"Wallet not connected. Call connect() first.",
|
|
17216
|
-
|
|
18012
|
+
import_types22.WalletErrorCode.NOT_CONNECTED
|
|
17217
18013
|
);
|
|
17218
18014
|
}
|
|
17219
|
-
function featureNotSupportedError(feature, code =
|
|
18015
|
+
function featureNotSupportedError(feature, code = import_types22.WalletErrorCode.UNKNOWN) {
|
|
17220
18016
|
return new WalletError(`${feature} is not supported by this wallet`, code);
|
|
17221
18017
|
}
|
|
17222
18018
|
|
|
17223
18019
|
// src/wallet/base-adapter.ts
|
|
17224
|
-
var
|
|
18020
|
+
var import_types23 = require("@sip-protocol/types");
|
|
17225
18021
|
var BaseWalletAdapter = class {
|
|
17226
18022
|
_address = "";
|
|
17227
18023
|
_publicKey = "";
|
|
@@ -17384,12 +18180,12 @@ var MockWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17384
18180
|
this._connectionState = "connecting";
|
|
17385
18181
|
if (this.shouldFailConnect) {
|
|
17386
18182
|
this.setError(
|
|
17387
|
-
|
|
18183
|
+
import_types23.WalletErrorCode.CONNECTION_FAILED,
|
|
17388
18184
|
"Mock connection failure"
|
|
17389
18185
|
);
|
|
17390
18186
|
throw new WalletError(
|
|
17391
18187
|
"Mock connection failure",
|
|
17392
|
-
|
|
18188
|
+
import_types23.WalletErrorCode.CONNECTION_FAILED
|
|
17393
18189
|
);
|
|
17394
18190
|
}
|
|
17395
18191
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -17401,7 +18197,7 @@ var MockWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17401
18197
|
async signMessage(message) {
|
|
17402
18198
|
this.requireConnected();
|
|
17403
18199
|
if (this.shouldFailSign) {
|
|
17404
|
-
throw new WalletError("Mock signing failure",
|
|
18200
|
+
throw new WalletError("Mock signing failure", import_types23.WalletErrorCode.SIGNING_FAILED);
|
|
17405
18201
|
}
|
|
17406
18202
|
const mockSig = new Uint8Array(64);
|
|
17407
18203
|
for (let i = 0; i < 64; i++) {
|
|
@@ -17416,7 +18212,7 @@ var MockWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17416
18212
|
async signTransaction(tx) {
|
|
17417
18213
|
this.requireConnected();
|
|
17418
18214
|
if (this.shouldFailSign) {
|
|
17419
|
-
throw new WalletError("Mock signing failure",
|
|
18215
|
+
throw new WalletError("Mock signing failure", import_types23.WalletErrorCode.SIGNING_FAILED);
|
|
17420
18216
|
}
|
|
17421
18217
|
const signature = await this.signMessage(
|
|
17422
18218
|
new TextEncoder().encode(JSON.stringify(tx.data))
|
|
@@ -17598,7 +18394,7 @@ function isPrivateWalletAdapter(adapter) {
|
|
|
17598
18394
|
}
|
|
17599
18395
|
|
|
17600
18396
|
// src/wallet/solana/adapter.ts
|
|
17601
|
-
var
|
|
18397
|
+
var import_types24 = require("@sip-protocol/types");
|
|
17602
18398
|
|
|
17603
18399
|
// src/wallet/solana/types.ts
|
|
17604
18400
|
function getSolanaProvider(wallet = "phantom") {
|
|
@@ -17719,19 +18515,19 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17719
18515
|
}
|
|
17720
18516
|
if (!this.provider) {
|
|
17721
18517
|
this.setError(
|
|
17722
|
-
|
|
18518
|
+
import_types24.WalletErrorCode.NOT_INSTALLED,
|
|
17723
18519
|
`${this.walletName} wallet is not installed`
|
|
17724
18520
|
);
|
|
17725
18521
|
throw new WalletError(
|
|
17726
18522
|
`${this.walletName} wallet is not installed`,
|
|
17727
|
-
|
|
18523
|
+
import_types24.WalletErrorCode.NOT_INSTALLED
|
|
17728
18524
|
);
|
|
17729
18525
|
}
|
|
17730
18526
|
const { publicKey } = await this.provider.connect();
|
|
17731
18527
|
if (!publicKey) {
|
|
17732
18528
|
throw new WalletError(
|
|
17733
18529
|
"No public key returned from wallet",
|
|
17734
|
-
|
|
18530
|
+
import_types24.WalletErrorCode.CONNECTION_FAILED
|
|
17735
18531
|
);
|
|
17736
18532
|
}
|
|
17737
18533
|
this.setupEventHandlers();
|
|
@@ -17741,11 +18537,11 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17741
18537
|
} catch (error) {
|
|
17742
18538
|
const message = error instanceof Error ? error.message : "Connection failed";
|
|
17743
18539
|
if (message.includes("User rejected") || message.includes("rejected")) {
|
|
17744
|
-
this.setError(
|
|
17745
|
-
throw new WalletError(message,
|
|
18540
|
+
this.setError(import_types24.WalletErrorCode.CONNECTION_REJECTED, message);
|
|
18541
|
+
throw new WalletError(message, import_types24.WalletErrorCode.CONNECTION_REJECTED);
|
|
17746
18542
|
}
|
|
17747
|
-
this.setError(
|
|
17748
|
-
throw error instanceof WalletError ? error : new WalletError(message,
|
|
18543
|
+
this.setError(import_types24.WalletErrorCode.CONNECTION_FAILED, message);
|
|
18544
|
+
throw error instanceof WalletError ? error : new WalletError(message, import_types24.WalletErrorCode.CONNECTION_FAILED, { cause: error });
|
|
17749
18545
|
}
|
|
17750
18546
|
}
|
|
17751
18547
|
/**
|
|
@@ -17768,7 +18564,7 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17768
18564
|
async signMessage(message) {
|
|
17769
18565
|
this.requireConnected();
|
|
17770
18566
|
if (!this.provider) {
|
|
17771
|
-
throw new WalletError("Provider not available",
|
|
18567
|
+
throw new WalletError("Provider not available", import_types24.WalletErrorCode.NOT_CONNECTED);
|
|
17772
18568
|
}
|
|
17773
18569
|
try {
|
|
17774
18570
|
const { signature } = await this.provider.signMessage(message);
|
|
@@ -17779,9 +18575,9 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17779
18575
|
} catch (error) {
|
|
17780
18576
|
const message2 = error instanceof Error ? error.message : "Signing failed";
|
|
17781
18577
|
if (message2.includes("User rejected") || message2.includes("rejected")) {
|
|
17782
|
-
throw new WalletError(message2,
|
|
18578
|
+
throw new WalletError(message2, import_types24.WalletErrorCode.SIGNING_REJECTED);
|
|
17783
18579
|
}
|
|
17784
|
-
throw new WalletError(message2,
|
|
18580
|
+
throw new WalletError(message2, import_types24.WalletErrorCode.SIGNING_FAILED, {
|
|
17785
18581
|
cause: error
|
|
17786
18582
|
});
|
|
17787
18583
|
}
|
|
@@ -17794,7 +18590,7 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17794
18590
|
async signTransaction(tx) {
|
|
17795
18591
|
this.requireConnected();
|
|
17796
18592
|
if (!this.provider) {
|
|
17797
|
-
throw new WalletError("Provider not available",
|
|
18593
|
+
throw new WalletError("Provider not available", import_types24.WalletErrorCode.NOT_CONNECTED);
|
|
17798
18594
|
}
|
|
17799
18595
|
try {
|
|
17800
18596
|
const solTx = tx.data;
|
|
@@ -17813,9 +18609,9 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17813
18609
|
} catch (error) {
|
|
17814
18610
|
const message = error instanceof Error ? error.message : "Signing failed";
|
|
17815
18611
|
if (message.includes("User rejected") || message.includes("rejected")) {
|
|
17816
|
-
throw new WalletError(message,
|
|
18612
|
+
throw new WalletError(message, import_types24.WalletErrorCode.SIGNING_REJECTED);
|
|
17817
18613
|
}
|
|
17818
|
-
throw new WalletError(message,
|
|
18614
|
+
throw new WalletError(message, import_types24.WalletErrorCode.SIGNING_FAILED, {
|
|
17819
18615
|
cause: error
|
|
17820
18616
|
});
|
|
17821
18617
|
}
|
|
@@ -17826,7 +18622,7 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17826
18622
|
async signAndSendTransaction(tx) {
|
|
17827
18623
|
this.requireConnected();
|
|
17828
18624
|
if (!this.provider) {
|
|
17829
|
-
throw new WalletError("Provider not available",
|
|
18625
|
+
throw new WalletError("Provider not available", import_types24.WalletErrorCode.NOT_CONNECTED);
|
|
17830
18626
|
}
|
|
17831
18627
|
try {
|
|
17832
18628
|
const solTx = tx.data;
|
|
@@ -17841,12 +18637,12 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17841
18637
|
} catch (error) {
|
|
17842
18638
|
const message = error instanceof Error ? error.message : "Transaction failed";
|
|
17843
18639
|
if (message.includes("User rejected") || message.includes("rejected")) {
|
|
17844
|
-
throw new WalletError(message,
|
|
18640
|
+
throw new WalletError(message, import_types24.WalletErrorCode.TRANSACTION_REJECTED);
|
|
17845
18641
|
}
|
|
17846
18642
|
if (message.includes("insufficient") || message.includes("Insufficient")) {
|
|
17847
|
-
throw new WalletError(message,
|
|
18643
|
+
throw new WalletError(message, import_types24.WalletErrorCode.INSUFFICIENT_FUNDS);
|
|
17848
18644
|
}
|
|
17849
|
-
throw new WalletError(message,
|
|
18645
|
+
throw new WalletError(message, import_types24.WalletErrorCode.TRANSACTION_FAILED, {
|
|
17850
18646
|
cause: error
|
|
17851
18647
|
});
|
|
17852
18648
|
}
|
|
@@ -17859,16 +18655,16 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17859
18655
|
async signAllTransactions(transactions) {
|
|
17860
18656
|
this.requireConnected();
|
|
17861
18657
|
if (!this.provider) {
|
|
17862
|
-
throw new WalletError("Provider not available",
|
|
18658
|
+
throw new WalletError("Provider not available", import_types24.WalletErrorCode.NOT_CONNECTED);
|
|
17863
18659
|
}
|
|
17864
18660
|
try {
|
|
17865
18661
|
return await this.provider.signAllTransactions(transactions);
|
|
17866
18662
|
} catch (error) {
|
|
17867
18663
|
const message = error instanceof Error ? error.message : "Signing failed";
|
|
17868
18664
|
if (message.includes("User rejected") || message.includes("rejected")) {
|
|
17869
|
-
throw new WalletError(message,
|
|
18665
|
+
throw new WalletError(message, import_types24.WalletErrorCode.SIGNING_REJECTED);
|
|
17870
18666
|
}
|
|
17871
|
-
throw new WalletError(message,
|
|
18667
|
+
throw new WalletError(message, import_types24.WalletErrorCode.SIGNING_FAILED, {
|
|
17872
18668
|
cause: error
|
|
17873
18669
|
});
|
|
17874
18670
|
}
|
|
@@ -17889,7 +18685,7 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17889
18685
|
} catch (error) {
|
|
17890
18686
|
throw new WalletError(
|
|
17891
18687
|
"Failed to get balance",
|
|
17892
|
-
|
|
18688
|
+
import_types24.WalletErrorCode.UNKNOWN,
|
|
17893
18689
|
{ cause: error }
|
|
17894
18690
|
);
|
|
17895
18691
|
}
|
|
@@ -17902,7 +18698,7 @@ var SolanaWalletAdapter = class extends BaseWalletAdapter {
|
|
|
17902
18698
|
if (asset.chain !== "solana") {
|
|
17903
18699
|
throw new WalletError(
|
|
17904
18700
|
`Asset chain ${asset.chain} not supported by Solana adapter`,
|
|
17905
|
-
|
|
18701
|
+
import_types24.WalletErrorCode.UNSUPPORTED_CHAIN
|
|
17906
18702
|
);
|
|
17907
18703
|
}
|
|
17908
18704
|
if (!asset.address) {
|
|
@@ -18020,7 +18816,7 @@ function createSolanaAdapter(config = {}) {
|
|
|
18020
18816
|
}
|
|
18021
18817
|
|
|
18022
18818
|
// src/wallet/solana/mock.ts
|
|
18023
|
-
var
|
|
18819
|
+
var import_types26 = require("@sip-protocol/types");
|
|
18024
18820
|
var MockPublicKey = class {
|
|
18025
18821
|
base58;
|
|
18026
18822
|
bytes;
|
|
@@ -18086,8 +18882,8 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18086
18882
|
this._connectionState = "connecting";
|
|
18087
18883
|
await this.simulateLatency();
|
|
18088
18884
|
if (this.shouldFailConnect) {
|
|
18089
|
-
this.setError(
|
|
18090
|
-
throw new WalletError("Mock connection failure",
|
|
18885
|
+
this.setError(import_types26.WalletErrorCode.CONNECTION_FAILED, "Mock connection failure");
|
|
18886
|
+
throw new WalletError("Mock connection failure", import_types26.WalletErrorCode.CONNECTION_FAILED);
|
|
18091
18887
|
}
|
|
18092
18888
|
const hexPubKey = "0x" + Buffer.from(this.mockPublicKey.toBytes()).toString("hex");
|
|
18093
18889
|
this.setConnected(this.mockAddress, hexPubKey);
|
|
@@ -18106,7 +18902,7 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18106
18902
|
this.requireConnected();
|
|
18107
18903
|
await this.simulateLatency();
|
|
18108
18904
|
if (this.shouldFailSign) {
|
|
18109
|
-
throw new WalletError("Mock signing failure",
|
|
18905
|
+
throw new WalletError("Mock signing failure", import_types26.WalletErrorCode.SIGNING_REJECTED);
|
|
18110
18906
|
}
|
|
18111
18907
|
const mockSig = new Uint8Array(64);
|
|
18112
18908
|
for (let i = 0; i < 64; i++) {
|
|
@@ -18124,7 +18920,7 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18124
18920
|
this.requireConnected();
|
|
18125
18921
|
await this.simulateLatency();
|
|
18126
18922
|
if (this.shouldFailSign) {
|
|
18127
|
-
throw new WalletError("Mock signing failure",
|
|
18923
|
+
throw new WalletError("Mock signing failure", import_types26.WalletErrorCode.SIGNING_REJECTED);
|
|
18128
18924
|
}
|
|
18129
18925
|
const solTx = tx.data;
|
|
18130
18926
|
this.signedTransactions.push(solTx);
|
|
@@ -18144,10 +18940,10 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18144
18940
|
this.requireConnected();
|
|
18145
18941
|
await this.simulateLatency();
|
|
18146
18942
|
if (this.shouldFailSign) {
|
|
18147
|
-
throw new WalletError("Mock signing failure",
|
|
18943
|
+
throw new WalletError("Mock signing failure", import_types26.WalletErrorCode.SIGNING_REJECTED);
|
|
18148
18944
|
}
|
|
18149
18945
|
if (this.shouldFailTransaction) {
|
|
18150
|
-
throw new WalletError("Mock transaction failure",
|
|
18946
|
+
throw new WalletError("Mock transaction failure", import_types26.WalletErrorCode.TRANSACTION_FAILED);
|
|
18151
18947
|
}
|
|
18152
18948
|
const txSig = `mock_tx_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
18153
18949
|
this.sentTransactions.push(txSig);
|
|
@@ -18167,7 +18963,7 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18167
18963
|
this.requireConnected();
|
|
18168
18964
|
await this.simulateLatency();
|
|
18169
18965
|
if (this.shouldFailSign) {
|
|
18170
|
-
throw new WalletError("Mock signing failure",
|
|
18966
|
+
throw new WalletError("Mock signing failure", import_types26.WalletErrorCode.SIGNING_REJECTED);
|
|
18171
18967
|
}
|
|
18172
18968
|
this.signedTransactions.push(...transactions);
|
|
18173
18969
|
return transactions.map((tx) => {
|
|
@@ -18194,7 +18990,7 @@ var MockSolanaAdapter = class extends BaseWalletAdapter {
|
|
|
18194
18990
|
if (asset.chain !== "solana") {
|
|
18195
18991
|
throw new WalletError(
|
|
18196
18992
|
`Asset chain ${asset.chain} not supported by Solana adapter`,
|
|
18197
|
-
|
|
18993
|
+
import_types26.WalletErrorCode.UNSUPPORTED_CHAIN
|
|
18198
18994
|
);
|
|
18199
18995
|
}
|
|
18200
18996
|
if (!asset.address) {
|
|
@@ -18363,7 +19159,7 @@ function createMockSolanaAdapter(config = {}) {
|
|
|
18363
19159
|
}
|
|
18364
19160
|
|
|
18365
19161
|
// src/wallet/ethereum/adapter.ts
|
|
18366
|
-
var
|
|
19162
|
+
var import_types28 = require("@sip-protocol/types");
|
|
18367
19163
|
|
|
18368
19164
|
// src/wallet/ethereum/types.ts
|
|
18369
19165
|
var EthereumChainId = {
|
|
@@ -18505,7 +19301,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18505
19301
|
this._connectionState = "error";
|
|
18506
19302
|
throw new WalletError(
|
|
18507
19303
|
`${this.walletType} wallet not found. Please install the extension.`,
|
|
18508
|
-
|
|
19304
|
+
import_types28.WalletErrorCode.NOT_INSTALLED
|
|
18509
19305
|
);
|
|
18510
19306
|
}
|
|
18511
19307
|
const accounts = await this.provider.request({
|
|
@@ -18515,7 +19311,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18515
19311
|
this._connectionState = "error";
|
|
18516
19312
|
throw new WalletError(
|
|
18517
19313
|
"No accounts returned from wallet",
|
|
18518
|
-
|
|
19314
|
+
import_types28.WalletErrorCode.CONNECTION_REJECTED
|
|
18519
19315
|
);
|
|
18520
19316
|
}
|
|
18521
19317
|
const address = normalizeAddress(accounts[0]);
|
|
@@ -18535,12 +19331,12 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18535
19331
|
if (rpcError.code === 4001) {
|
|
18536
19332
|
throw new WalletError(
|
|
18537
19333
|
"User rejected connection request",
|
|
18538
|
-
|
|
19334
|
+
import_types28.WalletErrorCode.CONNECTION_REJECTED
|
|
18539
19335
|
);
|
|
18540
19336
|
}
|
|
18541
19337
|
throw new WalletError(
|
|
18542
19338
|
`Failed to connect: ${rpcError.message || String(error)}`,
|
|
18543
|
-
|
|
19339
|
+
import_types28.WalletErrorCode.CONNECTION_FAILED
|
|
18544
19340
|
);
|
|
18545
19341
|
}
|
|
18546
19342
|
}
|
|
@@ -18560,7 +19356,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18560
19356
|
if (!this.provider) {
|
|
18561
19357
|
throw new WalletError(
|
|
18562
19358
|
"Provider not available",
|
|
18563
|
-
|
|
19359
|
+
import_types28.WalletErrorCode.NOT_CONNECTED
|
|
18564
19360
|
);
|
|
18565
19361
|
}
|
|
18566
19362
|
try {
|
|
@@ -18578,12 +19374,12 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18578
19374
|
if (rpcError.code === 4001) {
|
|
18579
19375
|
throw new WalletError(
|
|
18580
19376
|
"User rejected signing request",
|
|
18581
|
-
|
|
19377
|
+
import_types28.WalletErrorCode.SIGNING_REJECTED
|
|
18582
19378
|
);
|
|
18583
19379
|
}
|
|
18584
19380
|
throw new WalletError(
|
|
18585
19381
|
`Failed to sign message: ${rpcError.message || String(error)}`,
|
|
18586
|
-
|
|
19382
|
+
import_types28.WalletErrorCode.SIGNING_FAILED
|
|
18587
19383
|
);
|
|
18588
19384
|
}
|
|
18589
19385
|
}
|
|
@@ -18595,7 +19391,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18595
19391
|
if (!this.provider) {
|
|
18596
19392
|
throw new WalletError(
|
|
18597
19393
|
"Provider not available",
|
|
18598
|
-
|
|
19394
|
+
import_types28.WalletErrorCode.NOT_CONNECTED
|
|
18599
19395
|
);
|
|
18600
19396
|
}
|
|
18601
19397
|
try {
|
|
@@ -18612,12 +19408,12 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18612
19408
|
if (rpcError.code === 4001) {
|
|
18613
19409
|
throw new WalletError(
|
|
18614
19410
|
"User rejected signing request",
|
|
18615
|
-
|
|
19411
|
+
import_types28.WalletErrorCode.SIGNING_REJECTED
|
|
18616
19412
|
);
|
|
18617
19413
|
}
|
|
18618
19414
|
throw new WalletError(
|
|
18619
19415
|
`Failed to sign typed data: ${rpcError.message || String(error)}`,
|
|
18620
|
-
|
|
19416
|
+
import_types28.WalletErrorCode.SIGNING_FAILED
|
|
18621
19417
|
);
|
|
18622
19418
|
}
|
|
18623
19419
|
}
|
|
@@ -18629,7 +19425,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18629
19425
|
if (!this.provider) {
|
|
18630
19426
|
throw new WalletError(
|
|
18631
19427
|
"Provider not available",
|
|
18632
|
-
|
|
19428
|
+
import_types28.WalletErrorCode.NOT_CONNECTED
|
|
18633
19429
|
);
|
|
18634
19430
|
}
|
|
18635
19431
|
try {
|
|
@@ -18657,7 +19453,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18657
19453
|
if (rpcError.code === 4001) {
|
|
18658
19454
|
throw new WalletError(
|
|
18659
19455
|
"User rejected transaction signing",
|
|
18660
|
-
|
|
19456
|
+
import_types28.WalletErrorCode.SIGNING_REJECTED
|
|
18661
19457
|
);
|
|
18662
19458
|
}
|
|
18663
19459
|
if (rpcError.code === -32601 || rpcError.message?.includes("not supported")) {
|
|
@@ -18675,7 +19471,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18675
19471
|
}
|
|
18676
19472
|
throw new WalletError(
|
|
18677
19473
|
`Failed to sign transaction: ${rpcError.message || String(error)}`,
|
|
18678
|
-
|
|
19474
|
+
import_types28.WalletErrorCode.TRANSACTION_FAILED
|
|
18679
19475
|
);
|
|
18680
19476
|
}
|
|
18681
19477
|
}
|
|
@@ -18687,7 +19483,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18687
19483
|
if (!this.provider) {
|
|
18688
19484
|
throw new WalletError(
|
|
18689
19485
|
"Provider not available",
|
|
18690
|
-
|
|
19486
|
+
import_types28.WalletErrorCode.NOT_CONNECTED
|
|
18691
19487
|
);
|
|
18692
19488
|
}
|
|
18693
19489
|
try {
|
|
@@ -18709,12 +19505,12 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18709
19505
|
if (rpcError.code === 4001) {
|
|
18710
19506
|
throw new WalletError(
|
|
18711
19507
|
"User rejected transaction",
|
|
18712
|
-
|
|
19508
|
+
import_types28.WalletErrorCode.TRANSACTION_REJECTED
|
|
18713
19509
|
);
|
|
18714
19510
|
}
|
|
18715
19511
|
throw new WalletError(
|
|
18716
19512
|
`Failed to send transaction: ${rpcError.message || String(error)}`,
|
|
18717
|
-
|
|
19513
|
+
import_types28.WalletErrorCode.TRANSACTION_FAILED
|
|
18718
19514
|
);
|
|
18719
19515
|
}
|
|
18720
19516
|
}
|
|
@@ -18749,7 +19545,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18749
19545
|
} catch (error) {
|
|
18750
19546
|
throw new WalletError(
|
|
18751
19547
|
`Failed to fetch balance: ${String(error)}`,
|
|
18752
|
-
|
|
19548
|
+
import_types28.WalletErrorCode.UNKNOWN
|
|
18753
19549
|
);
|
|
18754
19550
|
}
|
|
18755
19551
|
}
|
|
@@ -18761,7 +19557,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18761
19557
|
if (asset.chain !== "ethereum") {
|
|
18762
19558
|
throw new WalletError(
|
|
18763
19559
|
`Asset chain ${asset.chain} not supported by Ethereum adapter`,
|
|
18764
|
-
|
|
19560
|
+
import_types28.WalletErrorCode.UNSUPPORTED_CHAIN
|
|
18765
19561
|
);
|
|
18766
19562
|
}
|
|
18767
19563
|
if (!asset.address) {
|
|
@@ -18786,7 +19582,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18786
19582
|
} catch (error) {
|
|
18787
19583
|
throw new WalletError(
|
|
18788
19584
|
`Failed to fetch token balance: ${String(error)}`,
|
|
18789
|
-
|
|
19585
|
+
import_types28.WalletErrorCode.UNKNOWN
|
|
18790
19586
|
);
|
|
18791
19587
|
}
|
|
18792
19588
|
}
|
|
@@ -18798,7 +19594,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18798
19594
|
if (!this.provider) {
|
|
18799
19595
|
throw new WalletError(
|
|
18800
19596
|
"Provider not available",
|
|
18801
|
-
|
|
19597
|
+
import_types28.WalletErrorCode.NOT_CONNECTED
|
|
18802
19598
|
);
|
|
18803
19599
|
}
|
|
18804
19600
|
try {
|
|
@@ -18813,18 +19609,18 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18813
19609
|
if (rpcError.code === 4001) {
|
|
18814
19610
|
throw new WalletError(
|
|
18815
19611
|
"User rejected chain switch",
|
|
18816
|
-
|
|
19612
|
+
import_types28.WalletErrorCode.CHAIN_SWITCH_REJECTED
|
|
18817
19613
|
);
|
|
18818
19614
|
}
|
|
18819
19615
|
if (rpcError.code === 4902) {
|
|
18820
19616
|
throw new WalletError(
|
|
18821
19617
|
`Chain ${chainId} not added to wallet`,
|
|
18822
|
-
|
|
19618
|
+
import_types28.WalletErrorCode.UNSUPPORTED_CHAIN
|
|
18823
19619
|
);
|
|
18824
19620
|
}
|
|
18825
19621
|
throw new WalletError(
|
|
18826
19622
|
`Failed to switch chain: ${rpcError.message || String(error)}`,
|
|
18827
|
-
|
|
19623
|
+
import_types28.WalletErrorCode.CHAIN_SWITCH_FAILED
|
|
18828
19624
|
);
|
|
18829
19625
|
}
|
|
18830
19626
|
}
|
|
@@ -18860,7 +19656,7 @@ var EthereumWalletAdapter = class extends BaseWalletAdapter {
|
|
|
18860
19656
|
}
|
|
18861
19657
|
throw new WalletError(
|
|
18862
19658
|
`Transaction ${txHash} not confirmed after ${maxAttempts * 5} seconds`,
|
|
18863
|
-
|
|
19659
|
+
import_types28.WalletErrorCode.TRANSACTION_FAILED
|
|
18864
19660
|
);
|
|
18865
19661
|
}
|
|
18866
19662
|
/**
|
|
@@ -18934,7 +19730,7 @@ function createEthereumAdapter(config) {
|
|
|
18934
19730
|
}
|
|
18935
19731
|
|
|
18936
19732
|
// src/wallet/ethereum/mock.ts
|
|
18937
|
-
var
|
|
19733
|
+
var import_types30 = require("@sip-protocol/types");
|
|
18938
19734
|
var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
18939
19735
|
chain = "ethereum";
|
|
18940
19736
|
name = "mock-ethereum";
|
|
@@ -18975,7 +19771,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
18975
19771
|
this._connectionState = "error";
|
|
18976
19772
|
throw new WalletError(
|
|
18977
19773
|
"Mock connection rejected",
|
|
18978
|
-
|
|
19774
|
+
import_types30.WalletErrorCode.CONNECTION_REJECTED
|
|
18979
19775
|
);
|
|
18980
19776
|
}
|
|
18981
19777
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -18987,7 +19783,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
18987
19783
|
this._connectionState = "error";
|
|
18988
19784
|
throw new WalletError(
|
|
18989
19785
|
`Mock connection failed: ${String(error)}`,
|
|
18990
|
-
|
|
19786
|
+
import_types30.WalletErrorCode.CONNECTION_FAILED
|
|
18991
19787
|
);
|
|
18992
19788
|
}
|
|
18993
19789
|
}
|
|
@@ -19005,7 +19801,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
19005
19801
|
if (this._shouldFailSign) {
|
|
19006
19802
|
throw new WalletError(
|
|
19007
19803
|
"Mock signing rejected",
|
|
19008
|
-
|
|
19804
|
+
import_types30.WalletErrorCode.SIGNING_REJECTED
|
|
19009
19805
|
);
|
|
19010
19806
|
}
|
|
19011
19807
|
const msgHex = Buffer.from(message).toString("hex");
|
|
@@ -19023,7 +19819,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
19023
19819
|
if (this._shouldFailSign) {
|
|
19024
19820
|
throw new WalletError(
|
|
19025
19821
|
"Mock signing rejected",
|
|
19026
|
-
|
|
19822
|
+
import_types30.WalletErrorCode.SIGNING_REJECTED
|
|
19027
19823
|
);
|
|
19028
19824
|
}
|
|
19029
19825
|
const mockSig = `0x${"1".repeat(130)}`;
|
|
@@ -19040,7 +19836,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
19040
19836
|
if (this._shouldFailSign) {
|
|
19041
19837
|
throw new WalletError(
|
|
19042
19838
|
"Mock signing rejected",
|
|
19043
|
-
|
|
19839
|
+
import_types30.WalletErrorCode.SIGNING_REJECTED
|
|
19044
19840
|
);
|
|
19045
19841
|
}
|
|
19046
19842
|
this._signedTransactions.push(tx);
|
|
@@ -19065,7 +19861,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
19065
19861
|
if (this._shouldFailTransaction) {
|
|
19066
19862
|
throw new WalletError(
|
|
19067
19863
|
"Mock transaction failed",
|
|
19068
|
-
|
|
19864
|
+
import_types30.WalletErrorCode.TRANSACTION_FAILED
|
|
19069
19865
|
);
|
|
19070
19866
|
}
|
|
19071
19867
|
this._signedTransactions.push(tx);
|
|
@@ -19095,7 +19891,7 @@ var MockEthereumAdapter = class extends BaseWalletAdapter {
|
|
|
19095
19891
|
if (asset.chain !== "ethereum") {
|
|
19096
19892
|
throw new WalletError(
|
|
19097
19893
|
`Asset chain ${asset.chain} not supported by Ethereum adapter`,
|
|
19098
|
-
|
|
19894
|
+
import_types30.WalletErrorCode.UNSUPPORTED_CHAIN
|
|
19099
19895
|
);
|
|
19100
19896
|
}
|
|
19101
19897
|
if (!asset.address) {
|
|
@@ -19300,28 +20096,28 @@ function createMockEthereumProvider(config = {}) {
|
|
|
19300
20096
|
}
|
|
19301
20097
|
|
|
19302
20098
|
// src/wallet/cosmos/adapter.ts
|
|
19303
|
-
var
|
|
20099
|
+
var import_types33 = require("@sip-protocol/types");
|
|
19304
20100
|
|
|
19305
20101
|
// src/wallet/cosmos/mock.ts
|
|
19306
|
-
var
|
|
20102
|
+
var import_types35 = require("@sip-protocol/types");
|
|
19307
20103
|
|
|
19308
20104
|
// src/wallet/bitcoin/adapter.ts
|
|
19309
|
-
var
|
|
20105
|
+
var import_types38 = require("@sip-protocol/types");
|
|
19310
20106
|
|
|
19311
20107
|
// src/wallet/bitcoin/mock.ts
|
|
19312
|
-
var
|
|
20108
|
+
var import_types40 = require("@sip-protocol/types");
|
|
19313
20109
|
|
|
19314
20110
|
// src/wallet/aptos/adapter.ts
|
|
19315
|
-
var
|
|
20111
|
+
var import_types43 = require("@sip-protocol/types");
|
|
19316
20112
|
|
|
19317
20113
|
// src/wallet/aptos/mock.ts
|
|
19318
|
-
var
|
|
20114
|
+
var import_types45 = require("@sip-protocol/types");
|
|
19319
20115
|
|
|
19320
20116
|
// src/wallet/sui/adapter.ts
|
|
19321
|
-
var
|
|
20117
|
+
var import_types47 = require("@sip-protocol/types");
|
|
19322
20118
|
|
|
19323
20119
|
// src/wallet/sui/mock.ts
|
|
19324
|
-
var
|
|
20120
|
+
var import_types49 = require("@sip-protocol/types");
|
|
19325
20121
|
|
|
19326
20122
|
// src/wallet/hardware/types.ts
|
|
19327
20123
|
var DerivationPath = {
|
|
@@ -19401,7 +20197,7 @@ function getAvailableTransports() {
|
|
|
19401
20197
|
|
|
19402
20198
|
// src/wallet/hardware/ledger.ts
|
|
19403
20199
|
var import_rlp = require("@ethereumjs/rlp");
|
|
19404
|
-
var
|
|
20200
|
+
var import_types51 = require("@sip-protocol/types");
|
|
19405
20201
|
var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
19406
20202
|
chain;
|
|
19407
20203
|
name = "ledger";
|
|
@@ -19554,7 +20350,7 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
|
19554
20350
|
async getBalance() {
|
|
19555
20351
|
throw new WalletError(
|
|
19556
20352
|
"Hardware wallets do not track balances. Use an RPC provider.",
|
|
19557
|
-
|
|
20353
|
+
import_types51.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
19558
20354
|
);
|
|
19559
20355
|
}
|
|
19560
20356
|
/**
|
|
@@ -19565,7 +20361,7 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
|
19565
20361
|
async getTokenBalance(_asset) {
|
|
19566
20362
|
throw new WalletError(
|
|
19567
20363
|
"Hardware wallets do not track balances. Use an RPC provider.",
|
|
19568
|
-
|
|
20364
|
+
import_types51.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
19569
20365
|
);
|
|
19570
20366
|
}
|
|
19571
20367
|
// ─── Account Management ─────────────────────────────────────────────────────
|
|
@@ -19792,7 +20588,7 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
|
19792
20588
|
* @see https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
|
|
19793
20589
|
*/
|
|
19794
20590
|
buildRawEthereumTx(tx) {
|
|
19795
|
-
const
|
|
20591
|
+
const hexToBytes25 = (hex) => {
|
|
19796
20592
|
if (!hex || hex === "0x" || hex === "0x0" || hex === "0x00") {
|
|
19797
20593
|
return new Uint8Array(0);
|
|
19798
20594
|
}
|
|
@@ -19809,21 +20605,21 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
|
19809
20605
|
const isEIP1559 = tx.maxFeePerGas !== void 0 && tx.maxPriorityFeePerGas !== void 0;
|
|
19810
20606
|
if (isEIP1559) {
|
|
19811
20607
|
const txData = [
|
|
19812
|
-
|
|
20608
|
+
hexToBytes25(`0x${tx.chainId.toString(16)}`),
|
|
19813
20609
|
// chainId
|
|
19814
|
-
|
|
20610
|
+
hexToBytes25(tx.nonce),
|
|
19815
20611
|
// nonce
|
|
19816
|
-
|
|
20612
|
+
hexToBytes25(tx.maxPriorityFeePerGas),
|
|
19817
20613
|
// maxPriorityFeePerGas
|
|
19818
|
-
|
|
20614
|
+
hexToBytes25(tx.maxFeePerGas),
|
|
19819
20615
|
// maxFeePerGas
|
|
19820
|
-
|
|
20616
|
+
hexToBytes25(tx.gasLimit),
|
|
19821
20617
|
// gasLimit
|
|
19822
|
-
|
|
20618
|
+
hexToBytes25(tx.to),
|
|
19823
20619
|
// to
|
|
19824
|
-
|
|
20620
|
+
hexToBytes25(tx.value),
|
|
19825
20621
|
// value
|
|
19826
|
-
|
|
20622
|
+
hexToBytes25(tx.data),
|
|
19827
20623
|
// data
|
|
19828
20624
|
[]
|
|
19829
20625
|
// accessList (empty)
|
|
@@ -19842,19 +20638,19 @@ var LedgerWalletAdapter = class extends BaseWalletAdapter {
|
|
|
19842
20638
|
);
|
|
19843
20639
|
}
|
|
19844
20640
|
const txData = [
|
|
19845
|
-
|
|
20641
|
+
hexToBytes25(tx.nonce),
|
|
19846
20642
|
// nonce
|
|
19847
|
-
|
|
20643
|
+
hexToBytes25(tx.gasPrice),
|
|
19848
20644
|
// gasPrice
|
|
19849
|
-
|
|
20645
|
+
hexToBytes25(tx.gasLimit),
|
|
19850
20646
|
// gasLimit
|
|
19851
|
-
|
|
20647
|
+
hexToBytes25(tx.to),
|
|
19852
20648
|
// to
|
|
19853
|
-
|
|
20649
|
+
hexToBytes25(tx.value),
|
|
19854
20650
|
// value
|
|
19855
|
-
|
|
20651
|
+
hexToBytes25(tx.data),
|
|
19856
20652
|
// data
|
|
19857
|
-
|
|
20653
|
+
hexToBytes25(`0x${tx.chainId.toString(16)}`),
|
|
19858
20654
|
// v (chainId for EIP-155)
|
|
19859
20655
|
new Uint8Array(0),
|
|
19860
20656
|
// r (empty for unsigned)
|
|
@@ -19931,7 +20727,7 @@ function createLedgerAdapter(config) {
|
|
|
19931
20727
|
}
|
|
19932
20728
|
|
|
19933
20729
|
// src/wallet/hardware/trezor.ts
|
|
19934
|
-
var
|
|
20730
|
+
var import_types53 = require("@sip-protocol/types");
|
|
19935
20731
|
var TrezorWalletAdapter = class extends BaseWalletAdapter {
|
|
19936
20732
|
chain;
|
|
19937
20733
|
name = "trezor";
|
|
@@ -20077,7 +20873,7 @@ var TrezorWalletAdapter = class extends BaseWalletAdapter {
|
|
|
20077
20873
|
async getBalance() {
|
|
20078
20874
|
throw new WalletError(
|
|
20079
20875
|
"Hardware wallets do not track balances. Use an RPC provider.",
|
|
20080
|
-
|
|
20876
|
+
import_types53.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20081
20877
|
);
|
|
20082
20878
|
}
|
|
20083
20879
|
/**
|
|
@@ -20088,7 +20884,7 @@ var TrezorWalletAdapter = class extends BaseWalletAdapter {
|
|
|
20088
20884
|
async getTokenBalance(_asset) {
|
|
20089
20885
|
throw new WalletError(
|
|
20090
20886
|
"Hardware wallets do not track balances. Use an RPC provider.",
|
|
20091
|
-
|
|
20887
|
+
import_types53.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20092
20888
|
);
|
|
20093
20889
|
}
|
|
20094
20890
|
// ─── Account Management ─────────────────────────────────────────────────────
|
|
@@ -20369,8 +21165,8 @@ function createTrezorAdapter(config) {
|
|
|
20369
21165
|
}
|
|
20370
21166
|
|
|
20371
21167
|
// src/wallet/hardware/mock.ts
|
|
20372
|
-
var
|
|
20373
|
-
var
|
|
21168
|
+
var import_types55 = require("@sip-protocol/types");
|
|
21169
|
+
var import_utils31 = require("@noble/hashes/utils");
|
|
20374
21170
|
var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
20375
21171
|
chain;
|
|
20376
21172
|
name = "mock-ledger";
|
|
@@ -20517,7 +21313,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
20517
21313
|
async getBalance() {
|
|
20518
21314
|
throw new WalletError(
|
|
20519
21315
|
"Hardware wallets do not track balances",
|
|
20520
|
-
|
|
21316
|
+
import_types55.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20521
21317
|
);
|
|
20522
21318
|
}
|
|
20523
21319
|
/**
|
|
@@ -20526,7 +21322,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
20526
21322
|
async getTokenBalance(_asset) {
|
|
20527
21323
|
throw new WalletError(
|
|
20528
21324
|
"Hardware wallets do not track balances",
|
|
20529
|
-
|
|
21325
|
+
import_types55.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20530
21326
|
);
|
|
20531
21327
|
}
|
|
20532
21328
|
/**
|
|
@@ -20615,15 +21411,15 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
20615
21411
|
}
|
|
20616
21412
|
}
|
|
20617
21413
|
generateMockAddress(index) {
|
|
20618
|
-
const bytes = (0,
|
|
21414
|
+
const bytes = (0, import_utils31.randomBytes)(20);
|
|
20619
21415
|
bytes[0] = index;
|
|
20620
|
-
return `0x${(0,
|
|
21416
|
+
return `0x${(0, import_utils31.bytesToHex)(bytes)}`;
|
|
20621
21417
|
}
|
|
20622
21418
|
generateMockPublicKey(index) {
|
|
20623
|
-
const bytes = (0,
|
|
21419
|
+
const bytes = (0, import_utils31.randomBytes)(33);
|
|
20624
21420
|
bytes[0] = 2;
|
|
20625
21421
|
bytes[1] = index;
|
|
20626
|
-
return `0x${(0,
|
|
21422
|
+
return `0x${(0, import_utils31.bytesToHex)(bytes)}`;
|
|
20627
21423
|
}
|
|
20628
21424
|
generateMockSignature(data) {
|
|
20629
21425
|
const sig = new Uint8Array(65);
|
|
@@ -20632,7 +21428,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
20632
21428
|
sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 11;
|
|
20633
21429
|
}
|
|
20634
21430
|
sig[64] = 27;
|
|
20635
|
-
return `0x${(0,
|
|
21431
|
+
return `0x${(0, import_utils31.bytesToHex)(sig)}`;
|
|
20636
21432
|
}
|
|
20637
21433
|
delay(ms) {
|
|
20638
21434
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -20756,13 +21552,13 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
|
|
|
20756
21552
|
async getBalance() {
|
|
20757
21553
|
throw new WalletError(
|
|
20758
21554
|
"Hardware wallets do not track balances",
|
|
20759
|
-
|
|
21555
|
+
import_types55.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20760
21556
|
);
|
|
20761
21557
|
}
|
|
20762
21558
|
async getTokenBalance(_asset) {
|
|
20763
21559
|
throw new WalletError(
|
|
20764
21560
|
"Hardware wallets do not track balances",
|
|
20765
|
-
|
|
21561
|
+
import_types55.WalletErrorCode.UNSUPPORTED_OPERATION
|
|
20766
21562
|
);
|
|
20767
21563
|
}
|
|
20768
21564
|
async getAccounts(startIndex = 0, count = 5) {
|
|
@@ -20821,15 +21617,15 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
|
|
|
20821
21617
|
}
|
|
20822
21618
|
}
|
|
20823
21619
|
generateMockAddress(index) {
|
|
20824
|
-
const bytes = (0,
|
|
21620
|
+
const bytes = (0, import_utils31.randomBytes)(20);
|
|
20825
21621
|
bytes[0] = index + 100;
|
|
20826
|
-
return `0x${(0,
|
|
21622
|
+
return `0x${(0, import_utils31.bytesToHex)(bytes)}`;
|
|
20827
21623
|
}
|
|
20828
21624
|
generateMockPublicKey(index) {
|
|
20829
|
-
const bytes = (0,
|
|
21625
|
+
const bytes = (0, import_utils31.randomBytes)(33);
|
|
20830
21626
|
bytes[0] = 3;
|
|
20831
21627
|
bytes[1] = index + 100;
|
|
20832
|
-
return `0x${(0,
|
|
21628
|
+
return `0x${(0, import_utils31.bytesToHex)(bytes)}`;
|
|
20833
21629
|
}
|
|
20834
21630
|
generateMockSignature(data) {
|
|
20835
21631
|
const sig = new Uint8Array(65);
|
|
@@ -20838,7 +21634,7 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
|
|
|
20838
21634
|
sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 17;
|
|
20839
21635
|
}
|
|
20840
21636
|
sig[64] = 28;
|
|
20841
|
-
return `0x${(0,
|
|
21637
|
+
return `0x${(0, import_utils31.bytesToHex)(sig)}`;
|
|
20842
21638
|
}
|
|
20843
21639
|
delay(ms) {
|
|
20844
21640
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -20852,7 +21648,7 @@ function createMockTrezorAdapter(config) {
|
|
|
20852
21648
|
}
|
|
20853
21649
|
|
|
20854
21650
|
// src/wallet/index.ts
|
|
20855
|
-
var
|
|
21651
|
+
var import_types58 = require("@sip-protocol/types");
|
|
20856
21652
|
|
|
20857
21653
|
// src/index.ts
|
|
20858
21654
|
init_solana();
|
|
@@ -20905,10 +21701,1778 @@ function isSameChainSupported(chain) {
|
|
|
20905
21701
|
function getSupportedSameChainChains() {
|
|
20906
21702
|
return ["solana"];
|
|
20907
21703
|
}
|
|
21704
|
+
|
|
21705
|
+
// src/privacy-backends/registry.ts
|
|
21706
|
+
var DEFAULT_PRIORITY = 50;
|
|
21707
|
+
var PrivacyBackendRegistry = class {
|
|
21708
|
+
backends = /* @__PURE__ */ new Map();
|
|
21709
|
+
/**
|
|
21710
|
+
* Register a privacy backend
|
|
21711
|
+
*
|
|
21712
|
+
* @param backend - Backend instance to register
|
|
21713
|
+
* @param options - Registration options
|
|
21714
|
+
* @throws Error if backend with same name exists and override is false
|
|
21715
|
+
*
|
|
21716
|
+
* @example
|
|
21717
|
+
* ```typescript
|
|
21718
|
+
* registry.register(new SIPNativeBackend())
|
|
21719
|
+
* registry.register(new PrivacyCashBackend(), { priority: 100 })
|
|
21720
|
+
* ```
|
|
21721
|
+
*/
|
|
21722
|
+
register(backend, options = {}) {
|
|
21723
|
+
const { override = false, priority = DEFAULT_PRIORITY, enabled = true } = options;
|
|
21724
|
+
if (this.backends.has(backend.name) && !override) {
|
|
21725
|
+
throw new Error(
|
|
21726
|
+
`Backend '${backend.name}' is already registered. Use { override: true } to replace it.`
|
|
21727
|
+
);
|
|
21728
|
+
}
|
|
21729
|
+
this.backends.set(backend.name, {
|
|
21730
|
+
backend,
|
|
21731
|
+
priority,
|
|
21732
|
+
enabled,
|
|
21733
|
+
registeredAt: Date.now()
|
|
21734
|
+
});
|
|
21735
|
+
}
|
|
21736
|
+
/**
|
|
21737
|
+
* Unregister a backend by name
|
|
21738
|
+
*
|
|
21739
|
+
* @param name - Backend name to unregister
|
|
21740
|
+
* @returns true if backend was removed, false if not found
|
|
21741
|
+
*/
|
|
21742
|
+
unregister(name) {
|
|
21743
|
+
return this.backends.delete(name);
|
|
21744
|
+
}
|
|
21745
|
+
/**
|
|
21746
|
+
* Get a backend by name
|
|
21747
|
+
*
|
|
21748
|
+
* @param name - Backend name
|
|
21749
|
+
* @returns Backend instance or undefined if not found
|
|
21750
|
+
*/
|
|
21751
|
+
get(name) {
|
|
21752
|
+
const entry = this.backends.get(name);
|
|
21753
|
+
return entry?.enabled ? entry.backend : void 0;
|
|
21754
|
+
}
|
|
21755
|
+
/**
|
|
21756
|
+
* Check if a backend is registered
|
|
21757
|
+
*
|
|
21758
|
+
* @param name - Backend name
|
|
21759
|
+
* @returns true if registered (regardless of enabled state)
|
|
21760
|
+
*/
|
|
21761
|
+
has(name) {
|
|
21762
|
+
return this.backends.has(name);
|
|
21763
|
+
}
|
|
21764
|
+
/**
|
|
21765
|
+
* Get all enabled backends sorted by priority
|
|
21766
|
+
*
|
|
21767
|
+
* @returns Array of backends (highest priority first)
|
|
21768
|
+
*/
|
|
21769
|
+
getAll() {
|
|
21770
|
+
return Array.from(this.backends.values()).filter((entry) => entry.enabled).sort((a, b) => b.priority - a.priority).map((entry) => entry.backend);
|
|
21771
|
+
}
|
|
21772
|
+
/**
|
|
21773
|
+
* Get all registered entries (including disabled)
|
|
21774
|
+
*
|
|
21775
|
+
* @returns Array of registered backend entries
|
|
21776
|
+
*/
|
|
21777
|
+
getAllEntries() {
|
|
21778
|
+
return Array.from(this.backends.values()).sort((a, b) => b.priority - a.priority);
|
|
21779
|
+
}
|
|
21780
|
+
/**
|
|
21781
|
+
* Get backends supporting a specific chain
|
|
21782
|
+
*
|
|
21783
|
+
* @param chain - Chain type to filter by
|
|
21784
|
+
* @returns Array of backends supporting the chain
|
|
21785
|
+
*/
|
|
21786
|
+
getByChain(chain) {
|
|
21787
|
+
return this.getAll().filter(
|
|
21788
|
+
(backend) => backend.chains.includes(chain)
|
|
21789
|
+
);
|
|
21790
|
+
}
|
|
21791
|
+
/**
|
|
21792
|
+
* Get backends of a specific type
|
|
21793
|
+
*
|
|
21794
|
+
* @param type - Backend type to filter by
|
|
21795
|
+
* @returns Array of backends of the specified type
|
|
21796
|
+
*/
|
|
21797
|
+
getByType(type) {
|
|
21798
|
+
return this.getAll().filter(
|
|
21799
|
+
(backend) => backend.type === type || backend.type === "both"
|
|
21800
|
+
);
|
|
21801
|
+
}
|
|
21802
|
+
/**
|
|
21803
|
+
* Get backends that support compliance (viewing keys)
|
|
21804
|
+
*
|
|
21805
|
+
* @returns Array of compliance-supporting backends
|
|
21806
|
+
*/
|
|
21807
|
+
getCompliant() {
|
|
21808
|
+
return this.getAll().filter(
|
|
21809
|
+
(backend) => backend.getCapabilities().complianceSupport
|
|
21810
|
+
);
|
|
21811
|
+
}
|
|
21812
|
+
/**
|
|
21813
|
+
* Find available backends for a transfer
|
|
21814
|
+
*
|
|
21815
|
+
* @param params - Transfer parameters
|
|
21816
|
+
* @returns Array of available backends with availability info
|
|
21817
|
+
*/
|
|
21818
|
+
async findAvailable(params) {
|
|
21819
|
+
const chainBackends = this.getByChain(params.chain);
|
|
21820
|
+
const results = [];
|
|
21821
|
+
for (const backend of chainBackends) {
|
|
21822
|
+
const availability = await backend.checkAvailability(params);
|
|
21823
|
+
if (availability.available) {
|
|
21824
|
+
results.push({ backend, availability });
|
|
21825
|
+
}
|
|
21826
|
+
}
|
|
21827
|
+
return results;
|
|
21828
|
+
}
|
|
21829
|
+
/**
|
|
21830
|
+
* Enable a backend
|
|
21831
|
+
*
|
|
21832
|
+
* @param name - Backend name
|
|
21833
|
+
* @returns true if backend was enabled, false if not found
|
|
21834
|
+
*/
|
|
21835
|
+
enable(name) {
|
|
21836
|
+
const entry = this.backends.get(name);
|
|
21837
|
+
if (entry) {
|
|
21838
|
+
entry.enabled = true;
|
|
21839
|
+
return true;
|
|
21840
|
+
}
|
|
21841
|
+
return false;
|
|
21842
|
+
}
|
|
21843
|
+
/**
|
|
21844
|
+
* Disable a backend
|
|
21845
|
+
*
|
|
21846
|
+
* @param name - Backend name
|
|
21847
|
+
* @returns true if backend was disabled, false if not found
|
|
21848
|
+
*/
|
|
21849
|
+
disable(name) {
|
|
21850
|
+
const entry = this.backends.get(name);
|
|
21851
|
+
if (entry) {
|
|
21852
|
+
entry.enabled = false;
|
|
21853
|
+
return true;
|
|
21854
|
+
}
|
|
21855
|
+
return false;
|
|
21856
|
+
}
|
|
21857
|
+
/**
|
|
21858
|
+
* Set backend priority
|
|
21859
|
+
*
|
|
21860
|
+
* @param name - Backend name
|
|
21861
|
+
* @param priority - New priority value
|
|
21862
|
+
* @returns true if priority was set, false if not found
|
|
21863
|
+
*/
|
|
21864
|
+
setPriority(name, priority) {
|
|
21865
|
+
const entry = this.backends.get(name);
|
|
21866
|
+
if (entry) {
|
|
21867
|
+
entry.priority = priority;
|
|
21868
|
+
return true;
|
|
21869
|
+
}
|
|
21870
|
+
return false;
|
|
21871
|
+
}
|
|
21872
|
+
/**
|
|
21873
|
+
* Get count of registered backends
|
|
21874
|
+
*
|
|
21875
|
+
* @param enabledOnly - If true, only count enabled backends
|
|
21876
|
+
* @returns Number of backends
|
|
21877
|
+
*/
|
|
21878
|
+
count(enabledOnly = false) {
|
|
21879
|
+
if (enabledOnly) {
|
|
21880
|
+
return Array.from(this.backends.values()).filter((e) => e.enabled).length;
|
|
21881
|
+
}
|
|
21882
|
+
return this.backends.size;
|
|
21883
|
+
}
|
|
21884
|
+
/**
|
|
21885
|
+
* Clear all registered backends
|
|
21886
|
+
*/
|
|
21887
|
+
clear() {
|
|
21888
|
+
this.backends.clear();
|
|
21889
|
+
}
|
|
21890
|
+
/**
|
|
21891
|
+
* Get backend names
|
|
21892
|
+
*
|
|
21893
|
+
* @param enabledOnly - If true, only return enabled backend names
|
|
21894
|
+
* @returns Array of backend names
|
|
21895
|
+
*/
|
|
21896
|
+
getNames(enabledOnly = false) {
|
|
21897
|
+
if (enabledOnly) {
|
|
21898
|
+
return Array.from(this.backends.entries()).filter(([, entry]) => entry.enabled).map(([name]) => name);
|
|
21899
|
+
}
|
|
21900
|
+
return Array.from(this.backends.keys());
|
|
21901
|
+
}
|
|
21902
|
+
};
|
|
21903
|
+
var defaultRegistry = new PrivacyBackendRegistry();
|
|
21904
|
+
|
|
21905
|
+
// src/privacy-backends/sip-native.ts
|
|
21906
|
+
var SUPPORTED_CHAINS = [
|
|
21907
|
+
"solana",
|
|
21908
|
+
"ethereum",
|
|
21909
|
+
"near",
|
|
21910
|
+
"polygon",
|
|
21911
|
+
"arbitrum",
|
|
21912
|
+
"optimism",
|
|
21913
|
+
"base",
|
|
21914
|
+
"avalanche",
|
|
21915
|
+
"bsc"
|
|
21916
|
+
];
|
|
21917
|
+
var SIP_NATIVE_CAPABILITIES = {
|
|
21918
|
+
hiddenAmount: true,
|
|
21919
|
+
hiddenSender: true,
|
|
21920
|
+
hiddenRecipient: true,
|
|
21921
|
+
hiddenCompute: false,
|
|
21922
|
+
complianceSupport: true,
|
|
21923
|
+
anonymitySet: void 0,
|
|
21924
|
+
// Not pool-based
|
|
21925
|
+
setupRequired: false,
|
|
21926
|
+
latencyEstimate: "fast",
|
|
21927
|
+
supportedTokens: "all",
|
|
21928
|
+
minAmount: void 0,
|
|
21929
|
+
maxAmount: void 0
|
|
21930
|
+
};
|
|
21931
|
+
var SIPNativeBackend = class {
|
|
21932
|
+
name = "sip-native";
|
|
21933
|
+
type = "transaction";
|
|
21934
|
+
chains;
|
|
21935
|
+
config;
|
|
21936
|
+
/**
|
|
21937
|
+
* Create a new SIP Native backend
|
|
21938
|
+
*
|
|
21939
|
+
* @param config - Backend configuration
|
|
21940
|
+
*/
|
|
21941
|
+
constructor(config = {}) {
|
|
21942
|
+
this.chains = config.chains ?? SUPPORTED_CHAINS;
|
|
21943
|
+
this.config = {
|
|
21944
|
+
chains: this.chains,
|
|
21945
|
+
requireViewingKey: config.requireViewingKey ?? false,
|
|
21946
|
+
minAmount: config.minAmount ?? BigInt(0),
|
|
21947
|
+
maxAmount: config.maxAmount ?? BigInt(Number.MAX_SAFE_INTEGER)
|
|
21948
|
+
};
|
|
21949
|
+
}
|
|
21950
|
+
/**
|
|
21951
|
+
* Check if backend is available for given parameters
|
|
21952
|
+
*/
|
|
21953
|
+
async checkAvailability(params) {
|
|
21954
|
+
if (!this.chains.includes(params.chain)) {
|
|
21955
|
+
return {
|
|
21956
|
+
available: false,
|
|
21957
|
+
reason: `Chain '${params.chain}' not supported by SIP Native backend`
|
|
21958
|
+
};
|
|
21959
|
+
}
|
|
21960
|
+
if (this.config.requireViewingKey && !params.viewingKey) {
|
|
21961
|
+
return {
|
|
21962
|
+
available: false,
|
|
21963
|
+
reason: "Viewing key required for SIP Native backend"
|
|
21964
|
+
};
|
|
21965
|
+
}
|
|
21966
|
+
if (params.amount < this.config.minAmount) {
|
|
21967
|
+
return {
|
|
21968
|
+
available: false,
|
|
21969
|
+
reason: `Amount ${params.amount} below minimum ${this.config.minAmount}`
|
|
21970
|
+
};
|
|
21971
|
+
}
|
|
21972
|
+
if (params.amount > this.config.maxAmount) {
|
|
21973
|
+
return {
|
|
21974
|
+
available: false,
|
|
21975
|
+
reason: `Amount ${params.amount} above maximum ${this.config.maxAmount}`
|
|
21976
|
+
};
|
|
21977
|
+
}
|
|
21978
|
+
const estimatedCost = this.getEstimatedCostForChain(params.chain);
|
|
21979
|
+
return {
|
|
21980
|
+
available: true,
|
|
21981
|
+
estimatedCost,
|
|
21982
|
+
estimatedTime: 1e3
|
|
21983
|
+
// ~1 second for stealth address operations
|
|
21984
|
+
};
|
|
21985
|
+
}
|
|
21986
|
+
/**
|
|
21987
|
+
* Get backend capabilities
|
|
21988
|
+
*/
|
|
21989
|
+
getCapabilities() {
|
|
21990
|
+
return {
|
|
21991
|
+
...SIP_NATIVE_CAPABILITIES,
|
|
21992
|
+
minAmount: this.config.minAmount > BigInt(0) ? this.config.minAmount : void 0,
|
|
21993
|
+
maxAmount: this.config.maxAmount < BigInt(Number.MAX_SAFE_INTEGER) ? this.config.maxAmount : void 0
|
|
21994
|
+
};
|
|
21995
|
+
}
|
|
21996
|
+
/**
|
|
21997
|
+
* Execute a privacy-preserving transfer
|
|
21998
|
+
*
|
|
21999
|
+
* This creates a stealth address transfer with:
|
|
22000
|
+
* - Ephemeral keypair generation
|
|
22001
|
+
* - Stealth address derivation
|
|
22002
|
+
* - Pedersen commitment for amount
|
|
22003
|
+
* - Optional viewing key encryption
|
|
22004
|
+
*/
|
|
22005
|
+
async execute(params) {
|
|
22006
|
+
const availability = await this.checkAvailability(params);
|
|
22007
|
+
if (!availability.available) {
|
|
22008
|
+
return {
|
|
22009
|
+
success: false,
|
|
22010
|
+
error: availability.reason,
|
|
22011
|
+
backend: this.name
|
|
22012
|
+
};
|
|
22013
|
+
}
|
|
22014
|
+
try {
|
|
22015
|
+
const simulatedSignature = `sim_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
22016
|
+
return {
|
|
22017
|
+
success: true,
|
|
22018
|
+
signature: simulatedSignature,
|
|
22019
|
+
backend: this.name,
|
|
22020
|
+
metadata: {
|
|
22021
|
+
chain: params.chain,
|
|
22022
|
+
amount: params.amount.toString(),
|
|
22023
|
+
hasViewingKey: !!params.viewingKey,
|
|
22024
|
+
timestamp: Date.now()
|
|
22025
|
+
}
|
|
22026
|
+
};
|
|
22027
|
+
} catch (error) {
|
|
22028
|
+
return {
|
|
22029
|
+
success: false,
|
|
22030
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
22031
|
+
backend: this.name
|
|
22032
|
+
};
|
|
22033
|
+
}
|
|
22034
|
+
}
|
|
22035
|
+
/**
|
|
22036
|
+
* Estimate cost for a transfer
|
|
22037
|
+
*/
|
|
22038
|
+
async estimateCost(params) {
|
|
22039
|
+
return this.getEstimatedCostForChain(params.chain);
|
|
22040
|
+
}
|
|
22041
|
+
/**
|
|
22042
|
+
* Get estimated cost based on chain
|
|
22043
|
+
*/
|
|
22044
|
+
getEstimatedCostForChain(chain) {
|
|
22045
|
+
const costMap = {
|
|
22046
|
+
solana: BigInt(5e3),
|
|
22047
|
+
// ~0.000005 SOL
|
|
22048
|
+
ethereum: BigInt("50000000000000"),
|
|
22049
|
+
// ~0.00005 ETH
|
|
22050
|
+
near: BigInt("1000000000000000000000"),
|
|
22051
|
+
// ~0.001 NEAR
|
|
22052
|
+
polygon: BigInt("50000000000000"),
|
|
22053
|
+
// ~0.00005 MATIC
|
|
22054
|
+
arbitrum: BigInt("50000000000000"),
|
|
22055
|
+
optimism: BigInt("50000000000000"),
|
|
22056
|
+
base: BigInt("50000000000000"),
|
|
22057
|
+
avalanche: BigInt("50000000000000"),
|
|
22058
|
+
bsc: BigInt("50000000000000")
|
|
22059
|
+
};
|
|
22060
|
+
return costMap[chain] ?? BigInt(0);
|
|
22061
|
+
}
|
|
22062
|
+
};
|
|
22063
|
+
|
|
22064
|
+
// src/privacy-backends/privacycash-types.ts
|
|
22065
|
+
var SOL_POOL_SIZES = {
|
|
22066
|
+
/** 0.1 SOL pool */
|
|
22067
|
+
SMALL: BigInt(1e8),
|
|
22068
|
+
/** 1 SOL pool */
|
|
22069
|
+
MEDIUM: BigInt(1e9),
|
|
22070
|
+
/** 10 SOL pool */
|
|
22071
|
+
LARGE: BigInt(1e10),
|
|
22072
|
+
/** 100 SOL pool */
|
|
22073
|
+
WHALE: BigInt(1e11)
|
|
22074
|
+
};
|
|
22075
|
+
var USDC_POOL_SIZES = {
|
|
22076
|
+
/** 10 USDC pool */
|
|
22077
|
+
SMALL: BigInt(1e7),
|
|
22078
|
+
/** 100 USDC pool */
|
|
22079
|
+
MEDIUM: BigInt(1e8),
|
|
22080
|
+
/** 1,000 USDC pool */
|
|
22081
|
+
LARGE: BigInt(1e9),
|
|
22082
|
+
/** 10,000 USDC pool */
|
|
22083
|
+
WHALE: BigInt(1e10)
|
|
22084
|
+
};
|
|
22085
|
+
var USDT_POOL_SIZES = {
|
|
22086
|
+
/** 10 USDT pool */
|
|
22087
|
+
SMALL: BigInt(1e7),
|
|
22088
|
+
/** 100 USDT pool */
|
|
22089
|
+
MEDIUM: BigInt(1e8),
|
|
22090
|
+
/** 1,000 USDT pool */
|
|
22091
|
+
LARGE: BigInt(1e9),
|
|
22092
|
+
/** 10,000 USDT pool */
|
|
22093
|
+
WHALE: BigInt(1e10)
|
|
22094
|
+
};
|
|
22095
|
+
var SOL_POOL_AMOUNTS = [
|
|
22096
|
+
SOL_POOL_SIZES.SMALL,
|
|
22097
|
+
SOL_POOL_SIZES.MEDIUM,
|
|
22098
|
+
SOL_POOL_SIZES.LARGE,
|
|
22099
|
+
SOL_POOL_SIZES.WHALE
|
|
22100
|
+
];
|
|
22101
|
+
var SPL_POOL_AMOUNTS = [
|
|
22102
|
+
USDC_POOL_SIZES.SMALL,
|
|
22103
|
+
USDC_POOL_SIZES.MEDIUM,
|
|
22104
|
+
USDC_POOL_SIZES.LARGE,
|
|
22105
|
+
USDC_POOL_SIZES.WHALE
|
|
22106
|
+
];
|
|
22107
|
+
|
|
22108
|
+
// src/privacy-backends/privacycash.ts
|
|
22109
|
+
var DEFAULT_ANONYMITY_SET = 50;
|
|
22110
|
+
var BASE_COST_LAMPORTS = BigInt(1e7);
|
|
22111
|
+
var PRIVACYCASH_CAPABILITIES = {
|
|
22112
|
+
hiddenAmount: false,
|
|
22113
|
+
// Fixed pool sizes, amount is known
|
|
22114
|
+
hiddenSender: true,
|
|
22115
|
+
// Pool mixing hides sender
|
|
22116
|
+
hiddenRecipient: true,
|
|
22117
|
+
// Withdrawal to fresh address
|
|
22118
|
+
hiddenCompute: false,
|
|
22119
|
+
// No compute privacy
|
|
22120
|
+
complianceSupport: false,
|
|
22121
|
+
// No viewing keys
|
|
22122
|
+
anonymitySet: DEFAULT_ANONYMITY_SET,
|
|
22123
|
+
setupRequired: false,
|
|
22124
|
+
// No setup needed
|
|
22125
|
+
latencyEstimate: "medium",
|
|
22126
|
+
// Need to wait for pool
|
|
22127
|
+
supportedTokens: "all",
|
|
22128
|
+
// SOL + USDC/USDT
|
|
22129
|
+
minAmount: SOL_POOL_AMOUNTS[0],
|
|
22130
|
+
// Smallest pool
|
|
22131
|
+
maxAmount: SOL_POOL_AMOUNTS[SOL_POOL_AMOUNTS.length - 1]
|
|
22132
|
+
// Largest pool
|
|
22133
|
+
};
|
|
22134
|
+
|
|
22135
|
+
// src/privacy-backends/router.ts
|
|
22136
|
+
var DEFAULT_CONFIG2 = {
|
|
22137
|
+
prioritize: "privacy",
|
|
22138
|
+
requireViewingKeys: false,
|
|
22139
|
+
allowComputePrivacy: true
|
|
22140
|
+
};
|
|
22141
|
+
var PRIORITY_WEIGHTS = {
|
|
22142
|
+
privacy: {
|
|
22143
|
+
hiddenAmount: 25,
|
|
22144
|
+
hiddenSender: 25,
|
|
22145
|
+
hiddenRecipient: 25,
|
|
22146
|
+
hiddenCompute: 10,
|
|
22147
|
+
anonymitySet: 15
|
|
22148
|
+
},
|
|
22149
|
+
speed: {
|
|
22150
|
+
fast: 40,
|
|
22151
|
+
medium: 25,
|
|
22152
|
+
slow: 10,
|
|
22153
|
+
setupRequired: -20
|
|
22154
|
+
},
|
|
22155
|
+
cost: {
|
|
22156
|
+
baseCost: 50,
|
|
22157
|
+
estimatedCost: 50
|
|
22158
|
+
},
|
|
22159
|
+
compliance: {
|
|
22160
|
+
complianceSupport: 60,
|
|
22161
|
+
hiddenAmount: 15,
|
|
22162
|
+
hiddenSender: 15,
|
|
22163
|
+
hiddenRecipient: 10
|
|
22164
|
+
}
|
|
22165
|
+
};
|
|
22166
|
+
var SmartRouter2 = class {
|
|
22167
|
+
registry;
|
|
22168
|
+
/**
|
|
22169
|
+
* Create a new SmartRouter
|
|
22170
|
+
*
|
|
22171
|
+
* @param registry - Backend registry to use for selection
|
|
22172
|
+
*/
|
|
22173
|
+
constructor(registry) {
|
|
22174
|
+
this.registry = registry;
|
|
22175
|
+
}
|
|
22176
|
+
/**
|
|
22177
|
+
* Select the best backend for a transfer
|
|
22178
|
+
*
|
|
22179
|
+
* @param params - Transfer parameters
|
|
22180
|
+
* @param config - Router configuration
|
|
22181
|
+
* @returns Selection result with backend and reasoning
|
|
22182
|
+
* @throws Error if no suitable backend is found
|
|
22183
|
+
*/
|
|
22184
|
+
async selectBackend(params, config = {}) {
|
|
22185
|
+
const fullConfig = { ...DEFAULT_CONFIG2, ...config };
|
|
22186
|
+
const chainBackends = this.registry.getByChain(params.chain);
|
|
22187
|
+
if (chainBackends.length === 0) {
|
|
22188
|
+
throw new Error(
|
|
22189
|
+
`No backends available for chain '${params.chain}'. Register a backend that supports this chain.`
|
|
22190
|
+
);
|
|
22191
|
+
}
|
|
22192
|
+
const scoredBackends = [];
|
|
22193
|
+
for (const backend of chainBackends) {
|
|
22194
|
+
if (fullConfig.excludeBackends?.includes(backend.name)) {
|
|
22195
|
+
continue;
|
|
22196
|
+
}
|
|
22197
|
+
const availability = await backend.checkAvailability(params);
|
|
22198
|
+
if (!availability.available) {
|
|
22199
|
+
continue;
|
|
22200
|
+
}
|
|
22201
|
+
const capabilities = backend.getCapabilities();
|
|
22202
|
+
if (fullConfig.requireViewingKeys && !capabilities.complianceSupport) {
|
|
22203
|
+
continue;
|
|
22204
|
+
}
|
|
22205
|
+
if (fullConfig.minAnonymitySet && capabilities.anonymitySet !== void 0 && capabilities.anonymitySet < fullConfig.minAnonymitySet) {
|
|
22206
|
+
continue;
|
|
22207
|
+
}
|
|
22208
|
+
if (!fullConfig.allowComputePrivacy && backend.type === "compute") {
|
|
22209
|
+
continue;
|
|
22210
|
+
}
|
|
22211
|
+
if (fullConfig.maxCost && availability.estimatedCost) {
|
|
22212
|
+
if (availability.estimatedCost > fullConfig.maxCost) {
|
|
22213
|
+
continue;
|
|
22214
|
+
}
|
|
22215
|
+
}
|
|
22216
|
+
if (fullConfig.maxLatency && availability.estimatedTime) {
|
|
22217
|
+
if (availability.estimatedTime > fullConfig.maxLatency) {
|
|
22218
|
+
continue;
|
|
22219
|
+
}
|
|
22220
|
+
}
|
|
22221
|
+
const { score, reason } = this.scoreBackend(
|
|
22222
|
+
backend,
|
|
22223
|
+
availability,
|
|
22224
|
+
fullConfig
|
|
22225
|
+
);
|
|
22226
|
+
scoredBackends.push({
|
|
22227
|
+
backend,
|
|
22228
|
+
availability,
|
|
22229
|
+
score,
|
|
22230
|
+
reason
|
|
22231
|
+
});
|
|
22232
|
+
}
|
|
22233
|
+
if (scoredBackends.length === 0) {
|
|
22234
|
+
throw new Error(
|
|
22235
|
+
`No backends meet the requirements for this transfer. Check your router configuration and registered backends.`
|
|
22236
|
+
);
|
|
22237
|
+
}
|
|
22238
|
+
scoredBackends.sort((a, b) => b.score - a.score);
|
|
22239
|
+
if (fullConfig.preferredBackend) {
|
|
22240
|
+
const preferredIndex = scoredBackends.findIndex(
|
|
22241
|
+
(s) => s.backend.name === fullConfig.preferredBackend
|
|
22242
|
+
);
|
|
22243
|
+
if (preferredIndex > 0) {
|
|
22244
|
+
const preferred = scoredBackends[preferredIndex];
|
|
22245
|
+
const leader = scoredBackends[0];
|
|
22246
|
+
if (leader.score - preferred.score <= 10) {
|
|
22247
|
+
scoredBackends.splice(preferredIndex, 1);
|
|
22248
|
+
scoredBackends.unshift(preferred);
|
|
22249
|
+
preferred.reason = `Preferred backend (within 10pts of optimal)`;
|
|
22250
|
+
}
|
|
22251
|
+
}
|
|
22252
|
+
}
|
|
22253
|
+
const selected = scoredBackends[0];
|
|
22254
|
+
const alternatives = scoredBackends.slice(1).map((s) => ({
|
|
22255
|
+
backend: s.backend,
|
|
22256
|
+
score: s.score,
|
|
22257
|
+
reason: s.reason
|
|
22258
|
+
}));
|
|
22259
|
+
return {
|
|
22260
|
+
backend: selected.backend,
|
|
22261
|
+
reason: selected.reason,
|
|
22262
|
+
alternatives,
|
|
22263
|
+
score: selected.score
|
|
22264
|
+
};
|
|
22265
|
+
}
|
|
22266
|
+
/**
|
|
22267
|
+
* Execute a transfer using the best available backend
|
|
22268
|
+
*
|
|
22269
|
+
* @param params - Transfer parameters
|
|
22270
|
+
* @param config - Router configuration
|
|
22271
|
+
* @returns Transaction result
|
|
22272
|
+
*/
|
|
22273
|
+
async execute(params, config = {}) {
|
|
22274
|
+
const selection = await this.selectBackend(params, config);
|
|
22275
|
+
return selection.backend.execute(params);
|
|
22276
|
+
}
|
|
22277
|
+
/**
|
|
22278
|
+
* Get available backends for a transfer (without selecting)
|
|
22279
|
+
*
|
|
22280
|
+
* @param params - Transfer parameters
|
|
22281
|
+
* @returns Array of available backends with scores
|
|
22282
|
+
*/
|
|
22283
|
+
async getAvailableBackends(params) {
|
|
22284
|
+
return this.registry.findAvailable(params);
|
|
22285
|
+
}
|
|
22286
|
+
/**
|
|
22287
|
+
* Score a backend based on configuration priority
|
|
22288
|
+
*/
|
|
22289
|
+
scoreBackend(backend, availability, config) {
|
|
22290
|
+
const capabilities = backend.getCapabilities();
|
|
22291
|
+
let score = 0;
|
|
22292
|
+
const reasons = [];
|
|
22293
|
+
switch (config.prioritize) {
|
|
22294
|
+
case "privacy":
|
|
22295
|
+
if (capabilities.hiddenAmount) {
|
|
22296
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenAmount;
|
|
22297
|
+
reasons.push("hidden amounts");
|
|
22298
|
+
}
|
|
22299
|
+
if (capabilities.hiddenSender) {
|
|
22300
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenSender;
|
|
22301
|
+
reasons.push("hidden sender");
|
|
22302
|
+
}
|
|
22303
|
+
if (capabilities.hiddenRecipient) {
|
|
22304
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenRecipient;
|
|
22305
|
+
reasons.push("hidden recipient");
|
|
22306
|
+
}
|
|
22307
|
+
if (capabilities.hiddenCompute) {
|
|
22308
|
+
score += PRIORITY_WEIGHTS.privacy.hiddenCompute;
|
|
22309
|
+
reasons.push("private compute");
|
|
22310
|
+
}
|
|
22311
|
+
if (capabilities.anonymitySet && capabilities.anonymitySet >= 100) {
|
|
22312
|
+
score += PRIORITY_WEIGHTS.privacy.anonymitySet;
|
|
22313
|
+
reasons.push(`anonymity set: ${capabilities.anonymitySet}`);
|
|
22314
|
+
}
|
|
22315
|
+
break;
|
|
22316
|
+
case "speed":
|
|
22317
|
+
score += PRIORITY_WEIGHTS.speed[capabilities.latencyEstimate];
|
|
22318
|
+
reasons.push(`${capabilities.latencyEstimate} latency`);
|
|
22319
|
+
if (capabilities.setupRequired) {
|
|
22320
|
+
score += PRIORITY_WEIGHTS.speed.setupRequired;
|
|
22321
|
+
reasons.push("setup required");
|
|
22322
|
+
}
|
|
22323
|
+
break;
|
|
22324
|
+
case "cost":
|
|
22325
|
+
if (availability.estimatedCost !== void 0) {
|
|
22326
|
+
const logCost = availability.estimatedCost > BigInt(0) ? Math.log10(Number(availability.estimatedCost)) : 0;
|
|
22327
|
+
const costScore = Math.max(0, 70 - logCost * 5);
|
|
22328
|
+
score += costScore;
|
|
22329
|
+
reasons.push(`low cost`);
|
|
22330
|
+
}
|
|
22331
|
+
break;
|
|
22332
|
+
case "compliance":
|
|
22333
|
+
if (capabilities.complianceSupport) {
|
|
22334
|
+
score += PRIORITY_WEIGHTS.compliance.complianceSupport;
|
|
22335
|
+
reasons.push("viewing key support");
|
|
22336
|
+
}
|
|
22337
|
+
if (capabilities.hiddenAmount) {
|
|
22338
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenAmount;
|
|
22339
|
+
}
|
|
22340
|
+
if (capabilities.hiddenSender) {
|
|
22341
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenSender;
|
|
22342
|
+
}
|
|
22343
|
+
if (capabilities.hiddenRecipient) {
|
|
22344
|
+
score += PRIORITY_WEIGHTS.compliance.hiddenRecipient;
|
|
22345
|
+
}
|
|
22346
|
+
break;
|
|
22347
|
+
}
|
|
22348
|
+
score = Math.min(100, Math.max(0, score));
|
|
22349
|
+
return {
|
|
22350
|
+
score,
|
|
22351
|
+
reason: reasons.length > 0 ? reasons.join(", ") : "default selection"
|
|
22352
|
+
};
|
|
22353
|
+
}
|
|
22354
|
+
};
|
|
22355
|
+
|
|
22356
|
+
// src/surveillance/algorithms/address-reuse.ts
|
|
22357
|
+
var MAX_DEDUCTION = 25;
|
|
22358
|
+
var DEDUCTION_PER_REUSE = 2;
|
|
22359
|
+
var REUSE_THRESHOLD = 1;
|
|
22360
|
+
function analyzeAddressReuse(transactions, walletAddress) {
|
|
22361
|
+
const receiveAddresses = /* @__PURE__ */ new Map();
|
|
22362
|
+
const sendAddresses = /* @__PURE__ */ new Map();
|
|
22363
|
+
for (const tx of transactions) {
|
|
22364
|
+
if (!tx.success) continue;
|
|
22365
|
+
if (tx.recipient === walletAddress) {
|
|
22366
|
+
const count = receiveAddresses.get(walletAddress) ?? 0;
|
|
22367
|
+
receiveAddresses.set(walletAddress, count + 1);
|
|
22368
|
+
}
|
|
22369
|
+
if (tx.sender === walletAddress) {
|
|
22370
|
+
const count = sendAddresses.get(walletAddress) ?? 0;
|
|
22371
|
+
sendAddresses.set(walletAddress, count + 1);
|
|
22372
|
+
}
|
|
22373
|
+
for (const addr of tx.involvedAddresses) {
|
|
22374
|
+
if (addr === walletAddress) continue;
|
|
22375
|
+
if (tx.sender === walletAddress) {
|
|
22376
|
+
const count = sendAddresses.get(addr) ?? 0;
|
|
22377
|
+
sendAddresses.set(addr, count + 1);
|
|
22378
|
+
}
|
|
22379
|
+
if (tx.recipient === walletAddress) {
|
|
22380
|
+
const count = receiveAddresses.get(addr) ?? 0;
|
|
22381
|
+
receiveAddresses.set(addr, count + 1);
|
|
22382
|
+
}
|
|
22383
|
+
}
|
|
22384
|
+
}
|
|
22385
|
+
let receiveReuseCount = 0;
|
|
22386
|
+
let sendReuseCount = 0;
|
|
22387
|
+
const reusedAddresses = [];
|
|
22388
|
+
for (const [address, count] of Array.from(receiveAddresses.entries())) {
|
|
22389
|
+
if (count > REUSE_THRESHOLD) {
|
|
22390
|
+
const reuseCount = count - REUSE_THRESHOLD;
|
|
22391
|
+
receiveReuseCount += reuseCount;
|
|
22392
|
+
const existing = reusedAddresses.find((r) => r.address === address);
|
|
22393
|
+
if (existing) {
|
|
22394
|
+
existing.useCount = Math.max(existing.useCount, count);
|
|
22395
|
+
existing.type = "both";
|
|
22396
|
+
} else {
|
|
22397
|
+
reusedAddresses.push({
|
|
22398
|
+
address,
|
|
22399
|
+
useCount: count,
|
|
22400
|
+
type: "receive"
|
|
22401
|
+
});
|
|
22402
|
+
}
|
|
22403
|
+
}
|
|
22404
|
+
}
|
|
22405
|
+
for (const [address, count] of Array.from(sendAddresses.entries())) {
|
|
22406
|
+
if (count > REUSE_THRESHOLD) {
|
|
22407
|
+
const reuseCount = count - REUSE_THRESHOLD;
|
|
22408
|
+
sendReuseCount += reuseCount;
|
|
22409
|
+
const existing = reusedAddresses.find((r) => r.address === address);
|
|
22410
|
+
if (existing) {
|
|
22411
|
+
existing.useCount = Math.max(existing.useCount, count);
|
|
22412
|
+
existing.type = "both";
|
|
22413
|
+
} else {
|
|
22414
|
+
reusedAddresses.push({
|
|
22415
|
+
address,
|
|
22416
|
+
useCount: count,
|
|
22417
|
+
type: "send"
|
|
22418
|
+
});
|
|
22419
|
+
}
|
|
22420
|
+
}
|
|
22421
|
+
}
|
|
22422
|
+
const totalReuseCount = receiveReuseCount + sendReuseCount;
|
|
22423
|
+
const rawDeduction = totalReuseCount * DEDUCTION_PER_REUSE;
|
|
22424
|
+
const scoreDeduction = Math.min(rawDeduction, MAX_DEDUCTION);
|
|
22425
|
+
reusedAddresses.sort((a, b) => b.useCount - a.useCount);
|
|
22426
|
+
return {
|
|
22427
|
+
receiveReuseCount,
|
|
22428
|
+
sendReuseCount,
|
|
22429
|
+
totalReuseCount,
|
|
22430
|
+
scoreDeduction,
|
|
22431
|
+
reusedAddresses: reusedAddresses.slice(0, 10)
|
|
22432
|
+
// Top 10 most reused
|
|
22433
|
+
};
|
|
22434
|
+
}
|
|
22435
|
+
|
|
22436
|
+
// src/surveillance/algorithms/cluster.ts
|
|
22437
|
+
var MAX_DEDUCTION2 = 25;
|
|
22438
|
+
var DEDUCTION_PER_LINK = 3;
|
|
22439
|
+
var MIN_LINK_THRESHOLD = 2;
|
|
22440
|
+
var UnionFind = class {
|
|
22441
|
+
parent;
|
|
22442
|
+
rank;
|
|
22443
|
+
constructor() {
|
|
22444
|
+
this.parent = /* @__PURE__ */ new Map();
|
|
22445
|
+
this.rank = /* @__PURE__ */ new Map();
|
|
22446
|
+
}
|
|
22447
|
+
find(x) {
|
|
22448
|
+
if (!this.parent.has(x)) {
|
|
22449
|
+
this.parent.set(x, x);
|
|
22450
|
+
this.rank.set(x, 0);
|
|
22451
|
+
}
|
|
22452
|
+
if (this.parent.get(x) !== x) {
|
|
22453
|
+
this.parent.set(x, this.find(this.parent.get(x)));
|
|
22454
|
+
}
|
|
22455
|
+
return this.parent.get(x);
|
|
22456
|
+
}
|
|
22457
|
+
union(x, y) {
|
|
22458
|
+
const rootX = this.find(x);
|
|
22459
|
+
const rootY = this.find(y);
|
|
22460
|
+
if (rootX === rootY) return;
|
|
22461
|
+
const rankX = this.rank.get(rootX) ?? 0;
|
|
22462
|
+
const rankY = this.rank.get(rootY) ?? 0;
|
|
22463
|
+
if (rankX < rankY) {
|
|
22464
|
+
this.parent.set(rootX, rootY);
|
|
22465
|
+
} else if (rankX > rankY) {
|
|
22466
|
+
this.parent.set(rootY, rootX);
|
|
22467
|
+
} else {
|
|
22468
|
+
this.parent.set(rootY, rootX);
|
|
22469
|
+
this.rank.set(rootX, rankX + 1);
|
|
22470
|
+
}
|
|
22471
|
+
}
|
|
22472
|
+
getClusters() {
|
|
22473
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
22474
|
+
for (const addr of Array.from(this.parent.keys())) {
|
|
22475
|
+
const root = this.find(addr);
|
|
22476
|
+
if (!clusters.has(root)) {
|
|
22477
|
+
clusters.set(root, []);
|
|
22478
|
+
}
|
|
22479
|
+
clusters.get(root).push(addr);
|
|
22480
|
+
}
|
|
22481
|
+
return clusters;
|
|
22482
|
+
}
|
|
22483
|
+
};
|
|
22484
|
+
function detectClusters(transactions, walletAddress) {
|
|
22485
|
+
const uf = new UnionFind();
|
|
22486
|
+
const linkCounts = /* @__PURE__ */ new Map();
|
|
22487
|
+
const linkTypes = /* @__PURE__ */ new Map();
|
|
22488
|
+
const txCountPerPair = /* @__PURE__ */ new Map();
|
|
22489
|
+
uf.find(walletAddress);
|
|
22490
|
+
for (const tx of transactions) {
|
|
22491
|
+
if (!tx.success) continue;
|
|
22492
|
+
const involvedWithWallet = tx.involvedAddresses.filter(
|
|
22493
|
+
(addr) => addr !== walletAddress
|
|
22494
|
+
);
|
|
22495
|
+
if (tx.sender === walletAddress && involvedWithWallet.length > 0) {
|
|
22496
|
+
for (const addr of involvedWithWallet) {
|
|
22497
|
+
const pairKey = [walletAddress, addr].sort().join(":");
|
|
22498
|
+
const count = (txCountPerPair.get(pairKey) ?? 0) + 1;
|
|
22499
|
+
txCountPerPair.set(pairKey, count);
|
|
22500
|
+
if (count >= MIN_LINK_THRESHOLD) {
|
|
22501
|
+
uf.union(walletAddress, addr);
|
|
22502
|
+
linkCounts.set(addr, count);
|
|
22503
|
+
linkTypes.set(addr, "common-input");
|
|
22504
|
+
}
|
|
22505
|
+
}
|
|
22506
|
+
}
|
|
22507
|
+
if (tx.sender === walletAddress && tx.recipient !== walletAddress) {
|
|
22508
|
+
const otherRecipients = involvedWithWallet.filter(
|
|
22509
|
+
(addr) => addr !== tx.recipient
|
|
22510
|
+
);
|
|
22511
|
+
for (const addr of otherRecipients) {
|
|
22512
|
+
const pairKey = [walletAddress, addr].sort().join(":");
|
|
22513
|
+
const count = (txCountPerPair.get(pairKey) ?? 0) + 1;
|
|
22514
|
+
txCountPerPair.set(pairKey, count);
|
|
22515
|
+
if (count >= MIN_LINK_THRESHOLD) {
|
|
22516
|
+
uf.union(walletAddress, addr);
|
|
22517
|
+
if (!linkTypes.has(addr)) {
|
|
22518
|
+
linkTypes.set(addr, "change-address");
|
|
22519
|
+
}
|
|
22520
|
+
linkCounts.set(addr, count);
|
|
22521
|
+
}
|
|
22522
|
+
}
|
|
22523
|
+
}
|
|
22524
|
+
if (tx.recipient === walletAddress && involvedWithWallet.length > 1) {
|
|
22525
|
+
for (let i = 0; i < involvedWithWallet.length; i++) {
|
|
22526
|
+
for (let j = i + 1; j < involvedWithWallet.length; j++) {
|
|
22527
|
+
const addr1 = involvedWithWallet[i];
|
|
22528
|
+
const addr2 = involvedWithWallet[j];
|
|
22529
|
+
const pairKey2 = [addr1, addr2].sort().join(":");
|
|
22530
|
+
const count2 = (txCountPerPair.get(pairKey2) ?? 0) + 1;
|
|
22531
|
+
txCountPerPair.set(pairKey2, count2);
|
|
22532
|
+
if (count2 >= MIN_LINK_THRESHOLD) {
|
|
22533
|
+
uf.union(addr1, addr2);
|
|
22534
|
+
linkTypes.set(addr1, "consolidation");
|
|
22535
|
+
linkTypes.set(addr2, "consolidation");
|
|
22536
|
+
}
|
|
22537
|
+
}
|
|
22538
|
+
const pairKey = [walletAddress, involvedWithWallet[i]].sort().join(":");
|
|
22539
|
+
const count = (txCountPerPair.get(pairKey) ?? 0) + 1;
|
|
22540
|
+
txCountPerPair.set(pairKey, count);
|
|
22541
|
+
if (count >= MIN_LINK_THRESHOLD) {
|
|
22542
|
+
uf.union(walletAddress, involvedWithWallet[i]);
|
|
22543
|
+
linkCounts.set(involvedWithWallet[i], count);
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
}
|
|
22547
|
+
}
|
|
22548
|
+
const allClusters = uf.getClusters();
|
|
22549
|
+
const walletRoot = uf.find(walletAddress);
|
|
22550
|
+
const walletCluster = allClusters.get(walletRoot) ?? [walletAddress];
|
|
22551
|
+
const linkedAddresses = walletCluster.filter((addr) => addr !== walletAddress);
|
|
22552
|
+
const linkedAddressCount = linkedAddresses.length;
|
|
22553
|
+
const totalLinkTxs = Array.from(linkCounts.values()).reduce((a, b) => a + b, 0);
|
|
22554
|
+
const confidence = Math.min(totalLinkTxs / (linkedAddressCount * 5), 1);
|
|
22555
|
+
const rawDeduction = linkedAddressCount * DEDUCTION_PER_LINK;
|
|
22556
|
+
const scoreDeduction = Math.min(rawDeduction, MAX_DEDUCTION2);
|
|
22557
|
+
const clusters = [];
|
|
22558
|
+
if (linkedAddressCount > 0) {
|
|
22559
|
+
const byType = /* @__PURE__ */ new Map();
|
|
22560
|
+
for (const addr of linkedAddresses) {
|
|
22561
|
+
const type = linkTypes.get(addr) ?? "common-input";
|
|
22562
|
+
if (!byType.has(type)) {
|
|
22563
|
+
byType.set(type, []);
|
|
22564
|
+
}
|
|
22565
|
+
byType.get(type).push(addr);
|
|
22566
|
+
}
|
|
22567
|
+
for (const [type, addresses] of Array.from(byType.entries())) {
|
|
22568
|
+
const txCount = addresses.reduce(
|
|
22569
|
+
(sum, addr) => sum + (linkCounts.get(addr) ?? 0),
|
|
22570
|
+
0
|
|
22571
|
+
);
|
|
22572
|
+
clusters.push({
|
|
22573
|
+
addresses: [walletAddress, ...addresses],
|
|
22574
|
+
linkType: type,
|
|
22575
|
+
transactionCount: txCount
|
|
22576
|
+
});
|
|
22577
|
+
}
|
|
22578
|
+
}
|
|
22579
|
+
return {
|
|
22580
|
+
linkedAddressCount,
|
|
22581
|
+
confidence,
|
|
22582
|
+
scoreDeduction,
|
|
22583
|
+
clusters
|
|
22584
|
+
};
|
|
22585
|
+
}
|
|
22586
|
+
|
|
22587
|
+
// src/surveillance/algorithms/exchange.ts
|
|
22588
|
+
var MAX_DEDUCTION3 = 20;
|
|
22589
|
+
var DEDUCTION_PER_CEX = 8;
|
|
22590
|
+
var DEDUCTION_PER_DEX = 2;
|
|
22591
|
+
var KNOWN_EXCHANGES = [
|
|
22592
|
+
// Centralized Exchanges (KYC Required)
|
|
22593
|
+
{
|
|
22594
|
+
name: "Binance",
|
|
22595
|
+
addresses: [
|
|
22596
|
+
"5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
|
|
22597
|
+
"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
|
|
22598
|
+
"AC5RDfQFmDS1deWZos921JfqscXdByf8BKHs5ACWjtW2"
|
|
22599
|
+
],
|
|
22600
|
+
type: "cex",
|
|
22601
|
+
kycRequired: true
|
|
22602
|
+
},
|
|
22603
|
+
{
|
|
22604
|
+
name: "Coinbase",
|
|
22605
|
+
addresses: [
|
|
22606
|
+
"H8sMJSCQxfKiFTCfDR3DUMLPwcRbM61LGFJ8N4dK3WjS",
|
|
22607
|
+
"2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S",
|
|
22608
|
+
"GJRs4FwHtemZ5ZE9x3FNvJ8TMwitKTh21yxdRPqn7npE"
|
|
22609
|
+
],
|
|
22610
|
+
type: "cex",
|
|
22611
|
+
kycRequired: true
|
|
22612
|
+
},
|
|
22613
|
+
{
|
|
22614
|
+
name: "Kraken",
|
|
22615
|
+
addresses: [
|
|
22616
|
+
"krakenmRKej41L9sX8N8Z2mhjZ8UpVHHBMzkKzfBh54"
|
|
22617
|
+
],
|
|
22618
|
+
type: "cex",
|
|
22619
|
+
kycRequired: true
|
|
22620
|
+
},
|
|
22621
|
+
{
|
|
22622
|
+
name: "FTX (Defunct)",
|
|
22623
|
+
addresses: [
|
|
22624
|
+
"FTXkd8cjuYGRLzPVdvqxNxNNNYBfFPPjrF3vW2Yq8p7"
|
|
22625
|
+
],
|
|
22626
|
+
type: "cex",
|
|
22627
|
+
kycRequired: true
|
|
22628
|
+
},
|
|
22629
|
+
{
|
|
22630
|
+
name: "KuCoin",
|
|
22631
|
+
addresses: [
|
|
22632
|
+
"BmFdpraQhkiDQE6SnfG5omcA1VwzqfXrwtNYBwWTymy6"
|
|
22633
|
+
],
|
|
22634
|
+
type: "cex",
|
|
22635
|
+
kycRequired: true
|
|
22636
|
+
},
|
|
22637
|
+
{
|
|
22638
|
+
name: "OKX",
|
|
22639
|
+
addresses: [
|
|
22640
|
+
"GGztQqQ6pCPaJQnNpXBgELr5cs3WwDakRbh1iEMzjgSJ"
|
|
22641
|
+
],
|
|
22642
|
+
type: "cex",
|
|
22643
|
+
kycRequired: true
|
|
22644
|
+
},
|
|
22645
|
+
{
|
|
22646
|
+
name: "Bybit",
|
|
22647
|
+
addresses: [
|
|
22648
|
+
"AC5RDfQFmDS1deWZos921JfqscXdByf8BKHs5ACWjtW3"
|
|
22649
|
+
],
|
|
22650
|
+
type: "cex",
|
|
22651
|
+
kycRequired: true
|
|
22652
|
+
},
|
|
22653
|
+
{
|
|
22654
|
+
name: "Gate.io",
|
|
22655
|
+
addresses: [
|
|
22656
|
+
"u6PJ8DtQuPFnfmwHbGFULQ4u4EgjDiyYKjVEsynXq2w"
|
|
22657
|
+
],
|
|
22658
|
+
type: "cex",
|
|
22659
|
+
kycRequired: true
|
|
22660
|
+
},
|
|
22661
|
+
// Decentralized Exchanges (No KYC but traceable)
|
|
22662
|
+
{
|
|
22663
|
+
name: "Jupiter",
|
|
22664
|
+
addresses: [
|
|
22665
|
+
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
|
22666
|
+
"JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB"
|
|
22667
|
+
],
|
|
22668
|
+
type: "dex",
|
|
22669
|
+
kycRequired: false
|
|
22670
|
+
},
|
|
22671
|
+
{
|
|
22672
|
+
name: "Raydium",
|
|
22673
|
+
addresses: [
|
|
22674
|
+
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
|
|
22675
|
+
"5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1"
|
|
22676
|
+
],
|
|
22677
|
+
type: "dex",
|
|
22678
|
+
kycRequired: false
|
|
22679
|
+
},
|
|
22680
|
+
{
|
|
22681
|
+
name: "Orca",
|
|
22682
|
+
addresses: [
|
|
22683
|
+
"9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP",
|
|
22684
|
+
"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"
|
|
22685
|
+
],
|
|
22686
|
+
type: "dex",
|
|
22687
|
+
kycRequired: false
|
|
22688
|
+
},
|
|
22689
|
+
{
|
|
22690
|
+
name: "Marinade",
|
|
22691
|
+
addresses: [
|
|
22692
|
+
"MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD"
|
|
22693
|
+
],
|
|
22694
|
+
type: "dex",
|
|
22695
|
+
kycRequired: false
|
|
22696
|
+
},
|
|
22697
|
+
{
|
|
22698
|
+
name: "Phantom Swap",
|
|
22699
|
+
addresses: [
|
|
22700
|
+
"PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY"
|
|
22701
|
+
],
|
|
22702
|
+
type: "dex",
|
|
22703
|
+
kycRequired: false
|
|
22704
|
+
}
|
|
22705
|
+
];
|
|
22706
|
+
function buildExchangeLookup(exchanges) {
|
|
22707
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
22708
|
+
for (const exchange of exchanges) {
|
|
22709
|
+
for (const address of exchange.addresses) {
|
|
22710
|
+
lookup.set(address, exchange);
|
|
22711
|
+
}
|
|
22712
|
+
}
|
|
22713
|
+
return lookup;
|
|
22714
|
+
}
|
|
22715
|
+
function detectExchangeExposure(transactions, walletAddress, customExchanges) {
|
|
22716
|
+
const exchanges = customExchanges ? [...KNOWN_EXCHANGES, ...customExchanges] : KNOWN_EXCHANGES;
|
|
22717
|
+
const lookup = buildExchangeLookup(exchanges);
|
|
22718
|
+
const exchangeStats = /* @__PURE__ */ new Map();
|
|
22719
|
+
for (const tx of transactions) {
|
|
22720
|
+
if (!tx.success) continue;
|
|
22721
|
+
for (const addr of tx.involvedAddresses) {
|
|
22722
|
+
const exchange = lookup.get(addr);
|
|
22723
|
+
if (!exchange) continue;
|
|
22724
|
+
if (!exchangeStats.has(exchange.name)) {
|
|
22725
|
+
exchangeStats.set(exchange.name, {
|
|
22726
|
+
exchange,
|
|
22727
|
+
deposits: 0,
|
|
22728
|
+
withdrawals: 0,
|
|
22729
|
+
firstInteraction: tx.timestamp,
|
|
22730
|
+
lastInteraction: tx.timestamp
|
|
22731
|
+
});
|
|
22732
|
+
}
|
|
22733
|
+
const stats = exchangeStats.get(exchange.name);
|
|
22734
|
+
stats.firstInteraction = Math.min(stats.firstInteraction, tx.timestamp);
|
|
22735
|
+
stats.lastInteraction = Math.max(stats.lastInteraction, tx.timestamp);
|
|
22736
|
+
if (tx.sender === walletAddress && tx.recipient === addr) {
|
|
22737
|
+
stats.deposits++;
|
|
22738
|
+
} else if (tx.sender === addr && tx.recipient === walletAddress) {
|
|
22739
|
+
stats.withdrawals++;
|
|
22740
|
+
} else if (tx.sender === walletAddress) {
|
|
22741
|
+
stats.deposits++;
|
|
22742
|
+
}
|
|
22743
|
+
}
|
|
22744
|
+
}
|
|
22745
|
+
let totalDeposits = 0;
|
|
22746
|
+
let totalWithdrawals = 0;
|
|
22747
|
+
let cexCount = 0;
|
|
22748
|
+
let dexCount = 0;
|
|
22749
|
+
const exchangeResults = [];
|
|
22750
|
+
for (const [name, stats] of Array.from(exchangeStats.entries())) {
|
|
22751
|
+
totalDeposits += stats.deposits;
|
|
22752
|
+
totalWithdrawals += stats.withdrawals;
|
|
22753
|
+
if (stats.exchange.type === "cex") {
|
|
22754
|
+
cexCount++;
|
|
22755
|
+
} else {
|
|
22756
|
+
dexCount++;
|
|
22757
|
+
}
|
|
22758
|
+
exchangeResults.push({
|
|
22759
|
+
name,
|
|
22760
|
+
type: stats.exchange.type,
|
|
22761
|
+
kycRequired: stats.exchange.kycRequired,
|
|
22762
|
+
deposits: stats.deposits,
|
|
22763
|
+
withdrawals: stats.withdrawals,
|
|
22764
|
+
firstInteraction: stats.firstInteraction,
|
|
22765
|
+
lastInteraction: stats.lastInteraction
|
|
22766
|
+
});
|
|
22767
|
+
}
|
|
22768
|
+
exchangeResults.sort(
|
|
22769
|
+
(a, b) => b.deposits + b.withdrawals - (a.deposits + a.withdrawals)
|
|
22770
|
+
);
|
|
22771
|
+
const cexDeduction = cexCount * DEDUCTION_PER_CEX;
|
|
22772
|
+
const dexDeduction = dexCount * DEDUCTION_PER_DEX;
|
|
22773
|
+
const rawDeduction = cexDeduction + dexDeduction;
|
|
22774
|
+
const scoreDeduction = Math.min(rawDeduction, MAX_DEDUCTION3);
|
|
22775
|
+
return {
|
|
22776
|
+
exchangeCount: exchangeStats.size,
|
|
22777
|
+
depositCount: totalDeposits,
|
|
22778
|
+
withdrawalCount: totalWithdrawals,
|
|
22779
|
+
scoreDeduction,
|
|
22780
|
+
exchanges: exchangeResults
|
|
22781
|
+
};
|
|
22782
|
+
}
|
|
22783
|
+
|
|
22784
|
+
// src/surveillance/algorithms/temporal.ts
|
|
22785
|
+
var MAX_DEDUCTION4 = 15;
|
|
22786
|
+
var DEDUCTION_PER_PATTERN = 5;
|
|
22787
|
+
var MIN_TRANSACTIONS_FOR_PATTERN = 5;
|
|
22788
|
+
var DAY_REGULARITY_THRESHOLD = 0.3;
|
|
22789
|
+
var HOUR_REGULARITY_THRESHOLD = 0.25;
|
|
22790
|
+
var TIMEZONES = {
|
|
22791
|
+
"UTC-12": -12,
|
|
22792
|
+
"UTC-11": -11,
|
|
22793
|
+
"HST": -10,
|
|
22794
|
+
"AKST": -9,
|
|
22795
|
+
"PST": -8,
|
|
22796
|
+
"MST": -7,
|
|
22797
|
+
"CST": -6,
|
|
22798
|
+
"EST": -5,
|
|
22799
|
+
"AST": -4,
|
|
22800
|
+
"BRT": -3,
|
|
22801
|
+
"UTC-2": -2,
|
|
22802
|
+
"UTC-1": -1,
|
|
22803
|
+
"UTC": 0,
|
|
22804
|
+
"CET": 1,
|
|
22805
|
+
"EET": 2,
|
|
22806
|
+
"MSK": 3,
|
|
22807
|
+
"GST": 4,
|
|
22808
|
+
"PKT": 5,
|
|
22809
|
+
"BST": 6,
|
|
22810
|
+
"ICT": 7,
|
|
22811
|
+
"CST_ASIA": 8,
|
|
22812
|
+
"JST": 9,
|
|
22813
|
+
"AEST": 10,
|
|
22814
|
+
"AEDT": 11,
|
|
22815
|
+
"NZST": 12
|
|
22816
|
+
};
|
|
22817
|
+
function analyzeTemporalPatterns(transactions) {
|
|
22818
|
+
const patterns = [];
|
|
22819
|
+
if (transactions.length < MIN_TRANSACTIONS_FOR_PATTERN) {
|
|
22820
|
+
return {
|
|
22821
|
+
patterns: [],
|
|
22822
|
+
scoreDeduction: 0
|
|
22823
|
+
};
|
|
22824
|
+
}
|
|
22825
|
+
const txTimes = transactions.filter((tx) => tx.success && tx.timestamp > 0).map((tx) => new Date(tx.timestamp * 1e3));
|
|
22826
|
+
if (txTimes.length < MIN_TRANSACTIONS_FOR_PATTERN) {
|
|
22827
|
+
return {
|
|
22828
|
+
patterns: [],
|
|
22829
|
+
scoreDeduction: 0
|
|
22830
|
+
};
|
|
22831
|
+
}
|
|
22832
|
+
const dayOfWeekPattern = analyzeDayOfWeekPattern(txTimes);
|
|
22833
|
+
if (dayOfWeekPattern) {
|
|
22834
|
+
patterns.push(dayOfWeekPattern);
|
|
22835
|
+
}
|
|
22836
|
+
const hourPattern = analyzeHourPattern(txTimes);
|
|
22837
|
+
if (hourPattern) {
|
|
22838
|
+
patterns.push(hourPattern);
|
|
22839
|
+
}
|
|
22840
|
+
const inferredTimezone = inferTimezone(txTimes);
|
|
22841
|
+
const burstPattern = detectActivityBursts(transactions);
|
|
22842
|
+
if (burstPattern) {
|
|
22843
|
+
patterns.push(burstPattern);
|
|
22844
|
+
}
|
|
22845
|
+
const rawDeduction = patterns.length * DEDUCTION_PER_PATTERN;
|
|
22846
|
+
const scoreDeduction = Math.min(rawDeduction, MAX_DEDUCTION4);
|
|
22847
|
+
return {
|
|
22848
|
+
patterns,
|
|
22849
|
+
inferredTimezone,
|
|
22850
|
+
scoreDeduction
|
|
22851
|
+
};
|
|
22852
|
+
}
|
|
22853
|
+
function analyzeDayOfWeekPattern(times) {
|
|
22854
|
+
const dayCount = new Array(7).fill(0);
|
|
22855
|
+
for (const time of times) {
|
|
22856
|
+
dayCount[time.getUTCDay()]++;
|
|
22857
|
+
}
|
|
22858
|
+
const total = times.length;
|
|
22859
|
+
const dominantDays = [];
|
|
22860
|
+
for (let day = 0; day < 7; day++) {
|
|
22861
|
+
const percentage = dayCount[day] / total;
|
|
22862
|
+
if (percentage >= DAY_REGULARITY_THRESHOLD) {
|
|
22863
|
+
dominantDays.push(day);
|
|
22864
|
+
}
|
|
22865
|
+
}
|
|
22866
|
+
if (dominantDays.length === 0 || dominantDays.length > 3) {
|
|
22867
|
+
return null;
|
|
22868
|
+
}
|
|
22869
|
+
const dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
22870
|
+
const dominantDayNames = dominantDays.map((d) => dayNames[d]).join(", ");
|
|
22871
|
+
return {
|
|
22872
|
+
type: "regular-schedule",
|
|
22873
|
+
description: `Most transactions occur on ${dominantDayNames}`,
|
|
22874
|
+
confidence: Math.max(...dominantDays.map((d) => dayCount[d] / total)),
|
|
22875
|
+
evidence: {
|
|
22876
|
+
dayOfWeek: dominantDays
|
|
22877
|
+
}
|
|
22878
|
+
};
|
|
22879
|
+
}
|
|
22880
|
+
function analyzeHourPattern(times) {
|
|
22881
|
+
const hourCount = new Array(24).fill(0);
|
|
22882
|
+
for (const time of times) {
|
|
22883
|
+
hourCount[time.getUTCHours()]++;
|
|
22884
|
+
}
|
|
22885
|
+
const total = times.length;
|
|
22886
|
+
const activeHours = [];
|
|
22887
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
22888
|
+
const windowCount = hourCount[hour] + hourCount[(hour + 1) % 24] + hourCount[(hour + 2) % 24];
|
|
22889
|
+
const windowPercentage = windowCount / total;
|
|
22890
|
+
if (windowPercentage >= HOUR_REGULARITY_THRESHOLD) {
|
|
22891
|
+
if (!activeHours.includes(hour)) {
|
|
22892
|
+
activeHours.push(hour);
|
|
22893
|
+
}
|
|
22894
|
+
}
|
|
22895
|
+
}
|
|
22896
|
+
if (activeHours.length === 0 || activeHours.length > 8) {
|
|
22897
|
+
return null;
|
|
22898
|
+
}
|
|
22899
|
+
const activeHourRange = Math.max(...activeHours) - Math.min(...activeHours);
|
|
22900
|
+
if (activeHourRange <= 6) {
|
|
22901
|
+
const startHour = Math.min(...activeHours);
|
|
22902
|
+
const endHour = Math.max(...activeHours) + 2;
|
|
22903
|
+
return {
|
|
22904
|
+
type: "timezone-inference",
|
|
22905
|
+
description: `Activity concentrated between ${startHour}:00-${endHour}:00 UTC`,
|
|
22906
|
+
confidence: 0.7,
|
|
22907
|
+
evidence: {
|
|
22908
|
+
hourOfDay: activeHours
|
|
22909
|
+
}
|
|
22910
|
+
};
|
|
22911
|
+
}
|
|
22912
|
+
return null;
|
|
22913
|
+
}
|
|
22914
|
+
function inferTimezone(times) {
|
|
22915
|
+
const hourCount = new Array(24).fill(0);
|
|
22916
|
+
for (const time of times) {
|
|
22917
|
+
hourCount[time.getUTCHours()]++;
|
|
22918
|
+
}
|
|
22919
|
+
let maxActivity = 0;
|
|
22920
|
+
let bestStartHour = 0;
|
|
22921
|
+
for (let start = 0; start < 24; start++) {
|
|
22922
|
+
let windowActivity = 0;
|
|
22923
|
+
for (let i = 0; i < 8; i++) {
|
|
22924
|
+
windowActivity += hourCount[(start + i) % 24];
|
|
22925
|
+
}
|
|
22926
|
+
if (windowActivity > maxActivity) {
|
|
22927
|
+
maxActivity = windowActivity;
|
|
22928
|
+
bestStartHour = start;
|
|
22929
|
+
}
|
|
22930
|
+
}
|
|
22931
|
+
const totalActivity = times.length;
|
|
22932
|
+
if (maxActivity / totalActivity < 0.6) {
|
|
22933
|
+
return void 0;
|
|
22934
|
+
}
|
|
22935
|
+
const assumedLocalNoon = 12;
|
|
22936
|
+
const peakMidpoint = (bestStartHour + 4) % 24;
|
|
22937
|
+
const inferredOffset = (assumedLocalNoon - peakMidpoint + 24) % 24;
|
|
22938
|
+
const normalizedOffset = inferredOffset > 12 ? inferredOffset - 24 : inferredOffset;
|
|
22939
|
+
for (const [name, offset] of Object.entries(TIMEZONES)) {
|
|
22940
|
+
if (Math.abs(offset - normalizedOffset) <= 1) {
|
|
22941
|
+
return name;
|
|
22942
|
+
}
|
|
22943
|
+
}
|
|
22944
|
+
return `UTC${normalizedOffset >= 0 ? "+" : ""}${normalizedOffset}`;
|
|
22945
|
+
}
|
|
22946
|
+
function detectActivityBursts(transactions) {
|
|
22947
|
+
if (transactions.length < 10) {
|
|
22948
|
+
return null;
|
|
22949
|
+
}
|
|
22950
|
+
const sorted = [...transactions].filter((tx) => tx.timestamp > 0).sort((a, b) => a.timestamp - b.timestamp);
|
|
22951
|
+
const gaps = [];
|
|
22952
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
22953
|
+
gaps.push(sorted[i].timestamp - sorted[i - 1].timestamp);
|
|
22954
|
+
}
|
|
22955
|
+
const avgGap = gaps.reduce((a, b) => a + b, 0) / gaps.length;
|
|
22956
|
+
const burstThreshold = avgGap * 0.1;
|
|
22957
|
+
let burstCount = 0;
|
|
22958
|
+
for (const gap of gaps) {
|
|
22959
|
+
if (gap < burstThreshold && gap < 3600) {
|
|
22960
|
+
burstCount++;
|
|
22961
|
+
}
|
|
22962
|
+
}
|
|
22963
|
+
const burstPercentage = burstCount / gaps.length;
|
|
22964
|
+
if (burstPercentage > 0.2) {
|
|
22965
|
+
return {
|
|
22966
|
+
type: "activity-burst",
|
|
22967
|
+
description: `${Math.round(burstPercentage * 100)}% of transactions occur in rapid succession`,
|
|
22968
|
+
confidence: burstPercentage,
|
|
22969
|
+
evidence: {
|
|
22970
|
+
frequency: `${burstCount} bursts detected`
|
|
22971
|
+
}
|
|
22972
|
+
};
|
|
22973
|
+
}
|
|
22974
|
+
return null;
|
|
22975
|
+
}
|
|
22976
|
+
|
|
22977
|
+
// src/surveillance/scoring.ts
|
|
22978
|
+
var MAX_POINTS = {
|
|
22979
|
+
addressReuse: 25,
|
|
22980
|
+
clusterExposure: 25,
|
|
22981
|
+
exchangeExposure: 20,
|
|
22982
|
+
temporalPatterns: 15,
|
|
22983
|
+
socialLinks: 15
|
|
22984
|
+
};
|
|
22985
|
+
var TOTAL_MAX_SCORE = Object.values(MAX_POINTS).reduce((a, b) => a + b, 0);
|
|
22986
|
+
var RISK_THRESHOLDS = {
|
|
22987
|
+
critical: 30,
|
|
22988
|
+
high: 50,
|
|
22989
|
+
medium: 70,
|
|
22990
|
+
low: 100
|
|
22991
|
+
};
|
|
22992
|
+
function calculatePrivacyScore(addressReuse, cluster, exchangeExposure, temporalPatterns, socialLinks, walletAddress) {
|
|
22993
|
+
const breakdown = {
|
|
22994
|
+
addressReuse: MAX_POINTS.addressReuse - addressReuse.scoreDeduction,
|
|
22995
|
+
clusterExposure: MAX_POINTS.clusterExposure - cluster.scoreDeduction,
|
|
22996
|
+
exchangeExposure: MAX_POINTS.exchangeExposure - exchangeExposure.scoreDeduction,
|
|
22997
|
+
temporalPatterns: MAX_POINTS.temporalPatterns - temporalPatterns.scoreDeduction,
|
|
22998
|
+
socialLinks: MAX_POINTS.socialLinks - socialLinks.scoreDeduction
|
|
22999
|
+
};
|
|
23000
|
+
const totalScore = breakdown.addressReuse + breakdown.clusterExposure + breakdown.exchangeExposure + breakdown.temporalPatterns + breakdown.socialLinks;
|
|
23001
|
+
const overall = Math.round(totalScore / TOTAL_MAX_SCORE * 100);
|
|
23002
|
+
let risk = "low";
|
|
23003
|
+
if (overall < RISK_THRESHOLDS.critical) {
|
|
23004
|
+
risk = "critical";
|
|
23005
|
+
} else if (overall < RISK_THRESHOLDS.high) {
|
|
23006
|
+
risk = "high";
|
|
23007
|
+
} else if (overall < RISK_THRESHOLDS.medium) {
|
|
23008
|
+
risk = "medium";
|
|
23009
|
+
}
|
|
23010
|
+
const recommendations = generateRecommendations(
|
|
23011
|
+
addressReuse,
|
|
23012
|
+
cluster,
|
|
23013
|
+
exchangeExposure,
|
|
23014
|
+
temporalPatterns,
|
|
23015
|
+
socialLinks
|
|
23016
|
+
);
|
|
23017
|
+
return {
|
|
23018
|
+
overall,
|
|
23019
|
+
breakdown,
|
|
23020
|
+
risk,
|
|
23021
|
+
recommendations,
|
|
23022
|
+
analyzedAt: Date.now(),
|
|
23023
|
+
walletAddress
|
|
23024
|
+
};
|
|
23025
|
+
}
|
|
23026
|
+
function generateRecommendations(addressReuse, cluster, exchangeExposure, temporalPatterns, socialLinks) {
|
|
23027
|
+
const recommendations = [];
|
|
23028
|
+
if (addressReuse.scoreDeduction > 0) {
|
|
23029
|
+
const severity = getSeverity(addressReuse.scoreDeduction, MAX_POINTS.addressReuse);
|
|
23030
|
+
recommendations.push({
|
|
23031
|
+
id: "address-reuse-001",
|
|
23032
|
+
severity,
|
|
23033
|
+
category: "addressReuse",
|
|
23034
|
+
title: `Address reused ${addressReuse.totalReuseCount} times`,
|
|
23035
|
+
description: "Reusing the same address for multiple transactions creates linkability between your transactions, allowing observers to track your activity.",
|
|
23036
|
+
action: "Use SIP stealth addresses for each transaction. Each payment uses a unique one-time address that cannot be linked to your main address.",
|
|
23037
|
+
potentialGain: addressReuse.scoreDeduction
|
|
23038
|
+
});
|
|
23039
|
+
}
|
|
23040
|
+
if (cluster.scoreDeduction > 0) {
|
|
23041
|
+
const severity = getSeverity(cluster.scoreDeduction, MAX_POINTS.clusterExposure);
|
|
23042
|
+
recommendations.push({
|
|
23043
|
+
id: "cluster-001",
|
|
23044
|
+
severity,
|
|
23045
|
+
category: "clusterExposure",
|
|
23046
|
+
title: `${cluster.linkedAddressCount} addresses linked to your wallet`,
|
|
23047
|
+
description: "Transaction analysis has linked multiple addresses to your wallet through common input ownership patterns. This expands your privacy exposure.",
|
|
23048
|
+
action: "Use SIP for all transactions to prevent cluster analysis. Stealth addresses break the link between your spending and receiving addresses.",
|
|
23049
|
+
potentialGain: cluster.scoreDeduction
|
|
23050
|
+
});
|
|
23051
|
+
}
|
|
23052
|
+
if (exchangeExposure.scoreDeduction > 0) {
|
|
23053
|
+
const cexes = exchangeExposure.exchanges.filter((e) => e.kycRequired);
|
|
23054
|
+
const severity = getSeverity(exchangeExposure.scoreDeduction, MAX_POINTS.exchangeExposure);
|
|
23055
|
+
if (cexes.length > 0) {
|
|
23056
|
+
recommendations.push({
|
|
23057
|
+
id: "exchange-cex-001",
|
|
23058
|
+
severity,
|
|
23059
|
+
category: "exchangeExposure",
|
|
23060
|
+
title: `Interacted with ${cexes.length} KYC exchange(s)`,
|
|
23061
|
+
description: `Deposits to ${cexes.map((e) => e.name).join(", ")} link your on-chain activity to your verified identity. This is one of the biggest privacy risks.`,
|
|
23062
|
+
action: "Use SIP viewing keys for selective disclosure. You can prove compliance to exchanges without exposing your full transaction history.",
|
|
23063
|
+
potentialGain: Math.min(cexes.length * 8, MAX_POINTS.exchangeExposure)
|
|
23064
|
+
});
|
|
23065
|
+
}
|
|
23066
|
+
const dexes = exchangeExposure.exchanges.filter((e) => !e.kycRequired);
|
|
23067
|
+
if (dexes.length > 0) {
|
|
23068
|
+
recommendations.push({
|
|
23069
|
+
id: "exchange-dex-001",
|
|
23070
|
+
severity: "low",
|
|
23071
|
+
category: "exchangeExposure",
|
|
23072
|
+
title: `Used ${dexes.length} DEX(es) without privacy`,
|
|
23073
|
+
description: "DEX swaps are public and can be traced. While no KYC is required, your swap patterns can reveal trading strategies.",
|
|
23074
|
+
action: "Use SIP for private swaps. Amounts and swap details are hidden while still using your preferred DEX.",
|
|
23075
|
+
potentialGain: Math.min(dexes.length * 2, 6)
|
|
23076
|
+
});
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
if (temporalPatterns.scoreDeduction > 0) {
|
|
23080
|
+
const severity = getSeverity(temporalPatterns.scoreDeduction, MAX_POINTS.temporalPatterns);
|
|
23081
|
+
for (const pattern of temporalPatterns.patterns) {
|
|
23082
|
+
if (pattern.type === "regular-schedule") {
|
|
23083
|
+
recommendations.push({
|
|
23084
|
+
id: "temporal-schedule-001",
|
|
23085
|
+
severity,
|
|
23086
|
+
category: "temporalPatterns",
|
|
23087
|
+
title: "Regular transaction schedule detected",
|
|
23088
|
+
description: `${pattern.description}. Predictable patterns make your activity easier to track and attribute.`,
|
|
23089
|
+
action: "Vary your transaction timing. Consider using scheduled private transactions through SIP to obscure timing patterns.",
|
|
23090
|
+
potentialGain: 5
|
|
23091
|
+
});
|
|
23092
|
+
}
|
|
23093
|
+
if (pattern.type === "timezone-inference") {
|
|
23094
|
+
recommendations.push({
|
|
23095
|
+
id: "temporal-timezone-001",
|
|
23096
|
+
severity: "medium",
|
|
23097
|
+
category: "temporalPatterns",
|
|
23098
|
+
title: "Timezone can be inferred from activity",
|
|
23099
|
+
description: `${pattern.description}. This narrows down your geographic location based on when you transact.`,
|
|
23100
|
+
action: "Use time-delayed transactions or vary your active hours. SIP can queue transactions for random future execution.",
|
|
23101
|
+
potentialGain: 5
|
|
23102
|
+
});
|
|
23103
|
+
}
|
|
23104
|
+
}
|
|
23105
|
+
}
|
|
23106
|
+
if (socialLinks.scoreDeduction > 0) {
|
|
23107
|
+
const severity = getSeverity(socialLinks.scoreDeduction, MAX_POINTS.socialLinks);
|
|
23108
|
+
if (socialLinks.isDoxxed) {
|
|
23109
|
+
recommendations.push({
|
|
23110
|
+
id: "social-doxxed-001",
|
|
23111
|
+
severity: "critical",
|
|
23112
|
+
category: "socialLinks",
|
|
23113
|
+
title: "Wallet publicly linked to your identity",
|
|
23114
|
+
description: "Your wallet address is publicly associated with your real identity through ENS/SNS names or social profiles. All transactions are attributable to you.",
|
|
23115
|
+
action: "Use a fresh wallet with SIP for private transactions. Your viewing keys let you prove ownership when needed without constant exposure.",
|
|
23116
|
+
potentialGain: 15
|
|
23117
|
+
});
|
|
23118
|
+
} else if (socialLinks.partialExposure) {
|
|
23119
|
+
recommendations.push({
|
|
23120
|
+
id: "social-partial-001",
|
|
23121
|
+
severity,
|
|
23122
|
+
category: "socialLinks",
|
|
23123
|
+
title: "Partial identity exposure detected",
|
|
23124
|
+
description: "Some identifying information is linked to your wallet, such as ENS names or labeled addresses on block explorers.",
|
|
23125
|
+
action: "Consider using a separate wallet for private activities. SIP stealth addresses prevent linking to your main identity.",
|
|
23126
|
+
potentialGain: socialLinks.scoreDeduction
|
|
23127
|
+
});
|
|
23128
|
+
}
|
|
23129
|
+
}
|
|
23130
|
+
recommendations.sort((a, b) => b.potentialGain - a.potentialGain);
|
|
23131
|
+
return recommendations;
|
|
23132
|
+
}
|
|
23133
|
+
function getSeverity(deduction, maxPoints) {
|
|
23134
|
+
const percentage = deduction / maxPoints;
|
|
23135
|
+
if (percentage >= 0.8) return "critical";
|
|
23136
|
+
if (percentage >= 0.5) return "high";
|
|
23137
|
+
if (percentage >= 0.25) return "medium";
|
|
23138
|
+
return "low";
|
|
23139
|
+
}
|
|
23140
|
+
function calculateSIPComparison(currentScore) {
|
|
23141
|
+
const improvements = [];
|
|
23142
|
+
const addressReuseImprovement = MAX_POINTS.addressReuse - currentScore.breakdown.addressReuse;
|
|
23143
|
+
if (addressReuseImprovement > 0) {
|
|
23144
|
+
improvements.push({
|
|
23145
|
+
category: "addressReuse",
|
|
23146
|
+
currentScore: currentScore.breakdown.addressReuse,
|
|
23147
|
+
projectedScore: MAX_POINTS.addressReuse,
|
|
23148
|
+
reason: "Stealth addresses prevent any address reuse"
|
|
23149
|
+
});
|
|
23150
|
+
}
|
|
23151
|
+
const clusterImprovement = MAX_POINTS.clusterExposure - currentScore.breakdown.clusterExposure;
|
|
23152
|
+
if (clusterImprovement > 0) {
|
|
23153
|
+
improvements.push({
|
|
23154
|
+
category: "clusterExposure",
|
|
23155
|
+
currentScore: currentScore.breakdown.clusterExposure,
|
|
23156
|
+
projectedScore: MAX_POINTS.clusterExposure,
|
|
23157
|
+
reason: "Stealth addresses cannot be linked via common input analysis"
|
|
23158
|
+
});
|
|
23159
|
+
}
|
|
23160
|
+
const exchangeImprovement = Math.min(
|
|
23161
|
+
(MAX_POINTS.exchangeExposure - currentScore.breakdown.exchangeExposure) * 0.5,
|
|
23162
|
+
10
|
|
23163
|
+
);
|
|
23164
|
+
if (exchangeImprovement > 0) {
|
|
23165
|
+
improvements.push({
|
|
23166
|
+
category: "exchangeExposure",
|
|
23167
|
+
currentScore: currentScore.breakdown.exchangeExposure,
|
|
23168
|
+
projectedScore: currentScore.breakdown.exchangeExposure + Math.round(exchangeImprovement),
|
|
23169
|
+
reason: "Viewing keys allow selective disclosure without exposing full history"
|
|
23170
|
+
});
|
|
23171
|
+
}
|
|
23172
|
+
const temporalImprovement = Math.min(
|
|
23173
|
+
(MAX_POINTS.temporalPatterns - currentScore.breakdown.temporalPatterns) * 0.3,
|
|
23174
|
+
5
|
|
23175
|
+
);
|
|
23176
|
+
if (temporalImprovement > 0) {
|
|
23177
|
+
improvements.push({
|
|
23178
|
+
category: "temporalPatterns",
|
|
23179
|
+
currentScore: currentScore.breakdown.temporalPatterns,
|
|
23180
|
+
projectedScore: currentScore.breakdown.temporalPatterns + Math.round(temporalImprovement),
|
|
23181
|
+
reason: "Private transactions reduce pattern correlation"
|
|
23182
|
+
});
|
|
23183
|
+
}
|
|
23184
|
+
const totalImprovement = improvements.reduce(
|
|
23185
|
+
(sum, imp) => sum + (imp.projectedScore - imp.currentScore),
|
|
23186
|
+
0
|
|
23187
|
+
);
|
|
23188
|
+
const projectedTotal = currentScore.overall / 100 * TOTAL_MAX_SCORE + totalImprovement;
|
|
23189
|
+
const projectedScore = Math.min(Math.round(projectedTotal / TOTAL_MAX_SCORE * 100), 100);
|
|
23190
|
+
return {
|
|
23191
|
+
currentScore: currentScore.overall,
|
|
23192
|
+
projectedScore,
|
|
23193
|
+
improvement: projectedScore - currentScore.overall,
|
|
23194
|
+
improvements
|
|
23195
|
+
};
|
|
23196
|
+
}
|
|
23197
|
+
|
|
23198
|
+
// src/surveillance/analyzer.ts
|
|
23199
|
+
var SurveillanceAnalyzer = class _SurveillanceAnalyzer {
|
|
23200
|
+
config;
|
|
23201
|
+
heliusUrl;
|
|
23202
|
+
constructor(config) {
|
|
23203
|
+
if (!config.heliusApiKey) {
|
|
23204
|
+
throw new Error(
|
|
23205
|
+
"Helius API key is required. Get one at https://dev.helius.xyz"
|
|
23206
|
+
);
|
|
23207
|
+
}
|
|
23208
|
+
this.config = {
|
|
23209
|
+
heliusApiKey: config.heliusApiKey,
|
|
23210
|
+
cluster: config.cluster ?? "mainnet-beta",
|
|
23211
|
+
maxTransactions: config.maxTransactions ?? 1e3,
|
|
23212
|
+
includeSocialLinks: config.includeSocialLinks ?? false,
|
|
23213
|
+
customExchangeAddresses: config.customExchangeAddresses ?? []
|
|
23214
|
+
};
|
|
23215
|
+
this.heliusUrl = this.config.cluster === "devnet" ? `https://api-devnet.helius.xyz/v0` : `https://api.helius.xyz/v0`;
|
|
23216
|
+
}
|
|
23217
|
+
/**
|
|
23218
|
+
* Perform full privacy analysis on a wallet
|
|
23219
|
+
*
|
|
23220
|
+
* @param walletAddress - Solana wallet address to analyze
|
|
23221
|
+
* @returns Complete analysis result with all details
|
|
23222
|
+
*/
|
|
23223
|
+
async analyze(walletAddress) {
|
|
23224
|
+
const startTime = Date.now();
|
|
23225
|
+
const transactions = await this.fetchTransactionHistory(walletAddress);
|
|
23226
|
+
const addressReuse = analyzeAddressReuse(transactions, walletAddress);
|
|
23227
|
+
const cluster = detectClusters(transactions, walletAddress);
|
|
23228
|
+
const exchangeExposure = detectExchangeExposure(
|
|
23229
|
+
transactions,
|
|
23230
|
+
walletAddress,
|
|
23231
|
+
this.config.customExchangeAddresses
|
|
23232
|
+
);
|
|
23233
|
+
const temporalPatterns = analyzeTemporalPatterns(transactions);
|
|
23234
|
+
const socialLinks = await this.analyzeSocialLinks(walletAddress);
|
|
23235
|
+
const privacyScore = calculatePrivacyScore(
|
|
23236
|
+
addressReuse,
|
|
23237
|
+
cluster,
|
|
23238
|
+
exchangeExposure,
|
|
23239
|
+
temporalPatterns,
|
|
23240
|
+
socialLinks,
|
|
23241
|
+
walletAddress
|
|
23242
|
+
);
|
|
23243
|
+
const sipComparison = calculateSIPComparison(privacyScore);
|
|
23244
|
+
const analysisDurationMs = Date.now() - startTime;
|
|
23245
|
+
return {
|
|
23246
|
+
privacyScore,
|
|
23247
|
+
addressReuse,
|
|
23248
|
+
cluster,
|
|
23249
|
+
exchangeExposure,
|
|
23250
|
+
temporalPatterns,
|
|
23251
|
+
socialLinks,
|
|
23252
|
+
sipComparison,
|
|
23253
|
+
transactionCount: transactions.length,
|
|
23254
|
+
analysisDurationMs
|
|
23255
|
+
};
|
|
23256
|
+
}
|
|
23257
|
+
/**
|
|
23258
|
+
* Fetch transaction history using Helius Enhanced Transactions API
|
|
23259
|
+
*/
|
|
23260
|
+
async fetchTransactionHistory(walletAddress) {
|
|
23261
|
+
const transactions = [];
|
|
23262
|
+
let beforeSignature;
|
|
23263
|
+
let hasMore = true;
|
|
23264
|
+
while (hasMore && transactions.length < this.config.maxTransactions) {
|
|
23265
|
+
const url = new URL(`${this.heliusUrl}/addresses/${walletAddress}/transactions`);
|
|
23266
|
+
url.searchParams.set("api-key", this.config.heliusApiKey);
|
|
23267
|
+
url.searchParams.set("limit", "100");
|
|
23268
|
+
if (beforeSignature) {
|
|
23269
|
+
url.searchParams.set("before", beforeSignature);
|
|
23270
|
+
}
|
|
23271
|
+
const controller = new AbortController();
|
|
23272
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
23273
|
+
let response;
|
|
23274
|
+
try {
|
|
23275
|
+
response = await fetch(url.toString(), {
|
|
23276
|
+
signal: controller.signal
|
|
23277
|
+
});
|
|
23278
|
+
} catch (error) {
|
|
23279
|
+
clearTimeout(timeoutId);
|
|
23280
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
23281
|
+
throw new Error("Helius API request timed out after 30 seconds");
|
|
23282
|
+
}
|
|
23283
|
+
throw error;
|
|
23284
|
+
}
|
|
23285
|
+
clearTimeout(timeoutId);
|
|
23286
|
+
if (!response.ok) {
|
|
23287
|
+
throw new Error(
|
|
23288
|
+
`Helius API error: ${response.status} ${response.statusText}`
|
|
23289
|
+
);
|
|
23290
|
+
}
|
|
23291
|
+
const data = await response.json();
|
|
23292
|
+
if (data.length === 0) {
|
|
23293
|
+
hasMore = false;
|
|
23294
|
+
break;
|
|
23295
|
+
}
|
|
23296
|
+
for (const tx of data) {
|
|
23297
|
+
const analyzable = this.parseTransaction(tx, walletAddress);
|
|
23298
|
+
if (analyzable) {
|
|
23299
|
+
transactions.push(analyzable);
|
|
23300
|
+
}
|
|
23301
|
+
}
|
|
23302
|
+
beforeSignature = data[data.length - 1]?.signature;
|
|
23303
|
+
hasMore = data.length === 100;
|
|
23304
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
23305
|
+
}
|
|
23306
|
+
return transactions;
|
|
23307
|
+
}
|
|
23308
|
+
/**
|
|
23309
|
+
* Parse Helius transaction into analyzable format
|
|
23310
|
+
*/
|
|
23311
|
+
parseTransaction(tx, walletAddress) {
|
|
23312
|
+
const involvedAddresses = /* @__PURE__ */ new Set();
|
|
23313
|
+
if (tx.feePayer) {
|
|
23314
|
+
involvedAddresses.add(tx.feePayer);
|
|
23315
|
+
}
|
|
23316
|
+
let sender = null;
|
|
23317
|
+
let recipient = null;
|
|
23318
|
+
let amount = BigInt(0);
|
|
23319
|
+
if (tx.nativeTransfers && tx.nativeTransfers.length > 0) {
|
|
23320
|
+
for (const transfer of tx.nativeTransfers) {
|
|
23321
|
+
involvedAddresses.add(transfer.fromUserAccount);
|
|
23322
|
+
involvedAddresses.add(transfer.toUserAccount);
|
|
23323
|
+
if (transfer.fromUserAccount === walletAddress) {
|
|
23324
|
+
sender = walletAddress;
|
|
23325
|
+
recipient = transfer.toUserAccount;
|
|
23326
|
+
amount = BigInt(transfer.amount);
|
|
23327
|
+
} else if (transfer.toUserAccount === walletAddress) {
|
|
23328
|
+
sender = transfer.fromUserAccount;
|
|
23329
|
+
recipient = walletAddress;
|
|
23330
|
+
amount = BigInt(transfer.amount);
|
|
23331
|
+
}
|
|
23332
|
+
}
|
|
23333
|
+
}
|
|
23334
|
+
let mint = null;
|
|
23335
|
+
if (tx.tokenTransfers && tx.tokenTransfers.length > 0) {
|
|
23336
|
+
for (const transfer of tx.tokenTransfers) {
|
|
23337
|
+
if (transfer.fromUserAccount) {
|
|
23338
|
+
involvedAddresses.add(transfer.fromUserAccount);
|
|
23339
|
+
}
|
|
23340
|
+
if (transfer.toUserAccount) {
|
|
23341
|
+
involvedAddresses.add(transfer.toUserAccount);
|
|
23342
|
+
}
|
|
23343
|
+
if (transfer.fromUserAccount === walletAddress) {
|
|
23344
|
+
sender = walletAddress;
|
|
23345
|
+
recipient = transfer.toUserAccount;
|
|
23346
|
+
amount = BigInt(Math.floor(transfer.tokenAmount));
|
|
23347
|
+
mint = transfer.mint;
|
|
23348
|
+
} else if (transfer.toUserAccount === walletAddress) {
|
|
23349
|
+
sender = transfer.fromUserAccount;
|
|
23350
|
+
recipient = walletAddress;
|
|
23351
|
+
amount = BigInt(Math.floor(transfer.tokenAmount));
|
|
23352
|
+
mint = transfer.mint;
|
|
23353
|
+
}
|
|
23354
|
+
}
|
|
23355
|
+
}
|
|
23356
|
+
if (tx.accountData) {
|
|
23357
|
+
for (const account of tx.accountData) {
|
|
23358
|
+
involvedAddresses.add(account.account);
|
|
23359
|
+
}
|
|
23360
|
+
}
|
|
23361
|
+
let type = "other";
|
|
23362
|
+
if (tx.type === "SWAP" || tx.events?.swap) {
|
|
23363
|
+
type = "swap";
|
|
23364
|
+
} else if (tx.type === "TRANSFER" || tx.nativeTransfers?.length || tx.tokenTransfers?.length) {
|
|
23365
|
+
type = "transfer";
|
|
23366
|
+
} else if (tx.type?.includes("STAKE")) {
|
|
23367
|
+
type = "stake";
|
|
23368
|
+
}
|
|
23369
|
+
return {
|
|
23370
|
+
signature: tx.signature,
|
|
23371
|
+
slot: tx.slot,
|
|
23372
|
+
timestamp: tx.timestamp,
|
|
23373
|
+
sender,
|
|
23374
|
+
recipient,
|
|
23375
|
+
amount,
|
|
23376
|
+
mint,
|
|
23377
|
+
fee: BigInt(tx.fee || 0),
|
|
23378
|
+
involvedAddresses: Array.from(involvedAddresses),
|
|
23379
|
+
type,
|
|
23380
|
+
success: true
|
|
23381
|
+
// Helius only returns successful transactions
|
|
23382
|
+
};
|
|
23383
|
+
}
|
|
23384
|
+
/**
|
|
23385
|
+
* Analyze social links (placeholder for external API integration)
|
|
23386
|
+
*
|
|
23387
|
+
* In production, this would query:
|
|
23388
|
+
* - SNS (Solana Name Service)
|
|
23389
|
+
* - Arkham Intelligence
|
|
23390
|
+
* - 0xppl API
|
|
23391
|
+
* - Other identity providers
|
|
23392
|
+
*/
|
|
23393
|
+
async analyzeSocialLinks(walletAddress) {
|
|
23394
|
+
if (!this.config.includeSocialLinks) {
|
|
23395
|
+
return {
|
|
23396
|
+
isDoxxed: false,
|
|
23397
|
+
partialExposure: false,
|
|
23398
|
+
scoreDeduction: 0,
|
|
23399
|
+
links: []
|
|
23400
|
+
};
|
|
23401
|
+
}
|
|
23402
|
+
try {
|
|
23403
|
+
const snsResult = await this.checkSNS(walletAddress);
|
|
23404
|
+
if (snsResult) {
|
|
23405
|
+
return {
|
|
23406
|
+
isDoxxed: false,
|
|
23407
|
+
partialExposure: true,
|
|
23408
|
+
scoreDeduction: 7,
|
|
23409
|
+
links: [snsResult]
|
|
23410
|
+
};
|
|
23411
|
+
}
|
|
23412
|
+
} catch {
|
|
23413
|
+
}
|
|
23414
|
+
return {
|
|
23415
|
+
isDoxxed: false,
|
|
23416
|
+
partialExposure: false,
|
|
23417
|
+
scoreDeduction: 0,
|
|
23418
|
+
links: []
|
|
23419
|
+
};
|
|
23420
|
+
}
|
|
23421
|
+
/**
|
|
23422
|
+
* Check Solana Name Service for domain names
|
|
23423
|
+
*/
|
|
23424
|
+
async checkSNS(walletAddress) {
|
|
23425
|
+
try {
|
|
23426
|
+
const url = `${this.heliusUrl}/addresses/${walletAddress}/names?api-key=${this.config.heliusApiKey}`;
|
|
23427
|
+
const response = await fetch(url);
|
|
23428
|
+
if (!response.ok) {
|
|
23429
|
+
return null;
|
|
23430
|
+
}
|
|
23431
|
+
const data = await response.json();
|
|
23432
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
23433
|
+
return {
|
|
23434
|
+
platform: "sns",
|
|
23435
|
+
identifier: data[0].name || data[0],
|
|
23436
|
+
confidence: 1
|
|
23437
|
+
};
|
|
23438
|
+
}
|
|
23439
|
+
} catch {
|
|
23440
|
+
}
|
|
23441
|
+
return null;
|
|
23442
|
+
}
|
|
23443
|
+
/**
|
|
23444
|
+
* Get quick privacy score without full analysis
|
|
23445
|
+
*
|
|
23446
|
+
* Performs a lighter analysis suitable for real-time display.
|
|
23447
|
+
* Uses only the most recent transactions (100 max).
|
|
23448
|
+
*/
|
|
23449
|
+
async quickScore(walletAddress) {
|
|
23450
|
+
const quickAnalyzer = new _SurveillanceAnalyzer({
|
|
23451
|
+
heliusApiKey: this.config.heliusApiKey,
|
|
23452
|
+
cluster: this.config.cluster,
|
|
23453
|
+
maxTransactions: 100,
|
|
23454
|
+
includeSocialLinks: false,
|
|
23455
|
+
// Skip social links for speed
|
|
23456
|
+
customExchangeAddresses: this.config.customExchangeAddresses
|
|
23457
|
+
});
|
|
23458
|
+
const result = await quickAnalyzer.analyze(walletAddress);
|
|
23459
|
+
const topRecommendation = result.privacyScore.recommendations[0];
|
|
23460
|
+
return {
|
|
23461
|
+
score: result.privacyScore.overall,
|
|
23462
|
+
risk: result.privacyScore.risk,
|
|
23463
|
+
topIssue: topRecommendation?.title ?? null
|
|
23464
|
+
};
|
|
23465
|
+
}
|
|
23466
|
+
};
|
|
23467
|
+
function createSurveillanceAnalyzer(config) {
|
|
23468
|
+
return new SurveillanceAnalyzer(config);
|
|
23469
|
+
}
|
|
20908
23470
|
// Annotate the CommonJS export names for ESM import in node:
|
|
20909
23471
|
0 && (module.exports = {
|
|
20910
23472
|
ATTESTATION_VERSION,
|
|
20911
23473
|
AptosStealthService,
|
|
23474
|
+
AttestationGatedDisclosure,
|
|
23475
|
+
AttestationSchema,
|
|
20912
23476
|
AuditorKeyDerivation,
|
|
20913
23477
|
AuditorType,
|
|
20914
23478
|
BaseWalletAdapter,
|
|
@@ -20926,11 +23490,14 @@ function getSupportedSameChainChains() {
|
|
|
20926
23490
|
ErrorCode,
|
|
20927
23491
|
EthereumChainId,
|
|
20928
23492
|
EthereumWalletAdapter,
|
|
23493
|
+
GenericProvider,
|
|
20929
23494
|
HardwareErrorCode,
|
|
20930
23495
|
HardwareWalletError,
|
|
23496
|
+
HeliusProvider,
|
|
20931
23497
|
IntentBuilder,
|
|
20932
23498
|
IntentError,
|
|
20933
23499
|
IntentStatus,
|
|
23500
|
+
KNOWN_EXCHANGES,
|
|
20934
23501
|
LedgerWalletAdapter,
|
|
20935
23502
|
MEMO_PROGRAM_ID,
|
|
20936
23503
|
MockEthereumAdapter,
|
|
@@ -20952,7 +23519,9 @@ function getSupportedSameChainChains() {
|
|
|
20952
23519
|
OneClickSwapType,
|
|
20953
23520
|
PaymentBuilder,
|
|
20954
23521
|
PaymentStatus,
|
|
23522
|
+
PrivacyBackendRegistry,
|
|
20955
23523
|
PrivacyLevel,
|
|
23524
|
+
PrivacySmartRouter,
|
|
20956
23525
|
PrivateNFT,
|
|
20957
23526
|
PrivateVoting,
|
|
20958
23527
|
ProofError,
|
|
@@ -20962,6 +23531,7 @@ function getSupportedSameChainChains() {
|
|
|
20962
23531
|
ReportStatus,
|
|
20963
23532
|
SIP,
|
|
20964
23533
|
SIPError,
|
|
23534
|
+
SIPNativeBackend,
|
|
20965
23535
|
SIP_MEMO_PREFIX,
|
|
20966
23536
|
SIP_VERSION,
|
|
20967
23537
|
SOLANA_EXPLORER_URLS,
|
|
@@ -20978,6 +23548,7 @@ function getSupportedSameChainChains() {
|
|
|
20978
23548
|
SolanaSameChainExecutor,
|
|
20979
23549
|
SolanaWalletAdapter,
|
|
20980
23550
|
SuiStealthService,
|
|
23551
|
+
SurveillanceAnalyzer,
|
|
20981
23552
|
SwapStatus,
|
|
20982
23553
|
ThresholdViewingKey,
|
|
20983
23554
|
Treasury,
|
|
@@ -20994,11 +23565,15 @@ function getSupportedSameChainChains() {
|
|
|
20994
23565
|
addBlindings,
|
|
20995
23566
|
addCommitments,
|
|
20996
23567
|
addOracle,
|
|
23568
|
+
analyzeAddressReuse,
|
|
23569
|
+
analyzeTemporalPatterns,
|
|
20997
23570
|
aptosAddressToAuthKey,
|
|
20998
23571
|
attachProofs,
|
|
20999
23572
|
base58ToHex,
|
|
21000
23573
|
browserBytesToHex,
|
|
21001
23574
|
browserHexToBytes,
|
|
23575
|
+
calculatePrivacyScore,
|
|
23576
|
+
calculateSIPComparison,
|
|
21002
23577
|
checkAptosStealthAddress,
|
|
21003
23578
|
checkEd25519StealthAddress,
|
|
21004
23579
|
checkStealthAddress,
|
|
@@ -21013,6 +23588,7 @@ function getSupportedSameChainChains() {
|
|
|
21013
23588
|
createEthereumAdapter,
|
|
21014
23589
|
createKeySpendOnlyOutput,
|
|
21015
23590
|
createLedgerAdapter,
|
|
23591
|
+
createMockAttestation,
|
|
21016
23592
|
createMockEthereumAdapter,
|
|
21017
23593
|
createMockEthereumProvider,
|
|
21018
23594
|
createMockLedgerAdapter,
|
|
@@ -21027,6 +23603,7 @@ function getSupportedSameChainChains() {
|
|
|
21027
23603
|
createPrivateOwnership,
|
|
21028
23604
|
createPrivateVoting,
|
|
21029
23605
|
createProductionSIP,
|
|
23606
|
+
createProvider,
|
|
21030
23607
|
createSIP,
|
|
21031
23608
|
createSameChainExecutor,
|
|
21032
23609
|
createSealedBidAuction,
|
|
@@ -21034,9 +23611,11 @@ function getSupportedSameChainChains() {
|
|
|
21034
23611
|
createShieldedPayment,
|
|
21035
23612
|
createSmartRouter,
|
|
21036
23613
|
createSolanaAdapter,
|
|
23614
|
+
createSurveillanceAnalyzer,
|
|
21037
23615
|
createTaprootOutput,
|
|
21038
23616
|
createTrezorAdapter,
|
|
21039
23617
|
createWalletFactory,
|
|
23618
|
+
createWebhookHandler,
|
|
21040
23619
|
createZcashClient,
|
|
21041
23620
|
createZcashNativeBackend,
|
|
21042
23621
|
createZcashShieldedService,
|
|
@@ -21045,6 +23624,7 @@ function getSupportedSameChainChains() {
|
|
|
21045
23624
|
decodeTaprootAddress,
|
|
21046
23625
|
decryptMemo,
|
|
21047
23626
|
decryptWithViewing,
|
|
23627
|
+
defaultRegistry,
|
|
21048
23628
|
deriveAptosStealthPrivateKey,
|
|
21049
23629
|
deriveEd25519StealthPrivateKey,
|
|
21050
23630
|
deriveOracleId,
|
|
@@ -21054,7 +23634,9 @@ function getSupportedSameChainChains() {
|
|
|
21054
23634
|
deserializeAttestationMessage,
|
|
21055
23635
|
deserializeIntent,
|
|
21056
23636
|
deserializePayment,
|
|
23637
|
+
detectClusters,
|
|
21057
23638
|
detectEthereumWallets,
|
|
23639
|
+
detectExchangeExposure,
|
|
21058
23640
|
detectSolanaWallets,
|
|
21059
23641
|
ed25519PublicKeyToAptosAddress,
|
|
21060
23642
|
ed25519PublicKeyToNearAddress,
|
|
@@ -21064,6 +23646,7 @@ function getSupportedSameChainChains() {
|
|
|
21064
23646
|
encryptForViewing,
|
|
21065
23647
|
estimatePrivateTransferFee,
|
|
21066
23648
|
featureNotSupportedError,
|
|
23649
|
+
fetchAttestation,
|
|
21067
23650
|
formatStablecoinAmount,
|
|
21068
23651
|
fromHex,
|
|
21069
23652
|
fromStablecoinUnits,
|
|
@@ -21148,6 +23731,7 @@ function getSupportedSameChainChains() {
|
|
|
21148
23731
|
normalizeSuiAddress,
|
|
21149
23732
|
notConnectedError,
|
|
21150
23733
|
parseAnnouncement,
|
|
23734
|
+
processWebhookTransaction,
|
|
21151
23735
|
proveOwnership,
|
|
21152
23736
|
publicKeyToEthAddress,
|
|
21153
23737
|
registerWallet,
|
|
@@ -21188,6 +23772,7 @@ function getSupportedSameChainChains() {
|
|
|
21188
23772
|
validateScalar,
|
|
21189
23773
|
validateViewingKey,
|
|
21190
23774
|
verifyAttestation,
|
|
23775
|
+
verifyAttestationSignature,
|
|
21191
23776
|
verifyCommitment,
|
|
21192
23777
|
verifyOpening,
|
|
21193
23778
|
verifyOracleSignature,
|