diff --git a/advanced/dapps/react-dapp-v2/package.json b/advanced/dapps/react-dapp-v2/package.json index dda7e9c79..a4ef588ed 100644 --- a/advanced/dapps/react-dapp-v2/package.json +++ b/advanced/dapps/react-dapp-v2/package.json @@ -18,6 +18,7 @@ "@kadena/types": "^0.6.0", "@multiversx/sdk-core": "12.18.0", "@multiversx/sdk-wallet": "4.2.0", + "@noble/curves": "^1.6.0", "@polkadot/util-crypto": "^10.1.2", "@solana/web3.js": "^1.36.0", "@walletconnect/encoding": "^1.0.1", @@ -26,6 +27,8 @@ "@walletconnect/utils": "2.16.1", "@web3modal/standalone": "2.4.3", "axios": "^1.0.0", + "bitcoinjs-lib": "^6.1.5", + "bitcoinjs-message": "^2.2.0", "blockies-ts": "^1.0.0", "bs58": "^5.0.0", "cosmos-wallet": "^1.2.0", diff --git a/advanced/dapps/react-dapp-v2/public/assets/btc-testnet.png b/advanced/dapps/react-dapp-v2/public/assets/btc-testnet.png new file mode 100644 index 000000000..b991e4b73 Binary files /dev/null and b/advanced/dapps/react-dapp-v2/public/assets/btc-testnet.png differ diff --git a/advanced/dapps/react-dapp-v2/src/chains/bip122.ts b/advanced/dapps/react-dapp-v2/src/chains/bip122.ts new file mode 100644 index 000000000..88077f660 --- /dev/null +++ b/advanced/dapps/react-dapp-v2/src/chains/bip122.ts @@ -0,0 +1,41 @@ +import { NamespaceMetadata, ChainMetadata, ChainsMap } from "../helpers"; + +export const BIP122_MAINNET = "000000000019d6689c085ae165831e93"; +export const BIP122_TESTNET = "000000000933ea01ad0ee984209779ba"; + +export const BtcChainData: ChainsMap = { + [BIP122_MAINNET]: { + id: `bip122:${BIP122_MAINNET}`, + name: "BTC Mainnet", + rpc: [], + slip44: 0, + testnet: false, + }, + [BIP122_TESTNET]: { + id: `bip122:${BIP122_TESTNET}`, + name: "BTC Testnet", + rpc: [], + slip44: 501, + testnet: true, + }, +}; + +export const BtcMetadata: NamespaceMetadata = { + [BIP122_MAINNET]: { + logo: "/assets/btc-testnet.png", + rgb: "247, 147, 25", + }, + [BIP122_TESTNET]: { + logo: "/assets/btc-testnet.png", + rgb: "247, 147, 25", + }, +}; + +export function getChainMetadata(chainId: string): ChainMetadata { + const reference = chainId.split(":")[1]; + const metadata = BtcMetadata[reference]; + if (typeof metadata === "undefined") { + throw new Error(`No chain metadata found for chainId: ${chainId}`); + } + return metadata; +} diff --git a/advanced/dapps/react-dapp-v2/src/chains/index.ts b/advanced/dapps/react-dapp-v2/src/chains/index.ts index 47878f76d..ce02b603e 100644 --- a/advanced/dapps/react-dapp-v2/src/chains/index.ts +++ b/advanced/dapps/react-dapp-v2/src/chains/index.ts @@ -9,6 +9,7 @@ import * as multiversx from "./multiversx"; import * as tron from "./tron"; import * as tezos from "./tezos"; import * as kadena from "./kadena"; +import * as bip122 from "./bip122"; import { ChainMetadata, ChainRequestRender } from "../helpers"; @@ -33,6 +34,8 @@ export function getChainMetadata(chainId: string): ChainMetadata { return tron.getChainMetadata(chainId); case "tezos": return tezos.getChainMetadata(chainId); + case "bip122": + return bip122.getChainMetadata(chainId); default: throw new Error(`No metadata handler for namespace ${namespace}`); } diff --git a/advanced/dapps/react-dapp-v2/src/components/Asset.tsx b/advanced/dapps/react-dapp-v2/src/components/Asset.tsx index 9e726b408..c74990211 100644 --- a/advanced/dapps/react-dapp-v2/src/components/Asset.tsx +++ b/advanced/dapps/react-dapp-v2/src/components/Asset.tsx @@ -10,6 +10,9 @@ import { getChainMetadata } from "../chains"; const xdaiLogo = getChainMetadata("eip155:100").logo; const maticLogo = getChainMetadata("eip155:137").logo; const kadenaLogo = getChainMetadata("kadena:testnet04").logo; +const btcLogo = getChainMetadata( + "bip122:000000000933ea01ad0ee984209779ba" +).logo; const SAsset = styled.div` width: 100%; @@ -48,6 +51,8 @@ function getAssetIcon(asset: AssetData): JSX.Element { return ; case "kda": return ; + case "btc": + return ; default: return ; } diff --git a/advanced/dapps/react-dapp-v2/src/constants/default.ts b/advanced/dapps/react-dapp-v2/src/constants/default.ts index 50fa43af4..91139a41b 100644 --- a/advanced/dapps/react-dapp-v2/src/constants/default.ts +++ b/advanced/dapps/react-dapp-v2/src/constants/default.ts @@ -19,6 +19,7 @@ export const DEFAULT_MAIN_CHAINS = [ "tron:0x2b6653dc", "tezos:mainnet", "kadena:mainnet01", + "bip122:000000000019d6689c085ae165831e93", ]; export const DEFAULT_TEST_CHAINS = [ @@ -38,6 +39,7 @@ export const DEFAULT_TEST_CHAINS = [ "tron:0xcd8690dc", "tezos:testnet", "kadena:testnet04", + "bip122:000000000933ea01ad0ee984209779ba", ]; export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS]; @@ -272,6 +274,18 @@ export enum DEFAULT_KADENA_METHODS { } export enum DEFAULT_KADENA_EVENTS {} +/** + * BITCOIN + */ +export enum DEFAULT_BIP122_METHODS { + BIP122_SEND_TRANSACTION = "sendTransfer", + BIP122_GET_ACCOUNT_ADDRESSES = "getAccountAddresses", + BIP122_SIGN_MESSAGE = "signMessage", + BIP122_SIGN_PSBT = "signPsbt", +} +export enum DEFAULT_BIP122_EVENTS { + BIP122_ADDRESS_CHANGED = "bip122_addressesChanged", +} export const REGIONALIZED_RELAYER_ENDPOINTS: RelayerType[] = [ { diff --git a/advanced/dapps/react-dapp-v2/src/contexts/ChainDataContext.tsx b/advanced/dapps/react-dapp-v2/src/contexts/ChainDataContext.tsx index 8b86d9471..4c138451a 100644 --- a/advanced/dapps/react-dapp-v2/src/contexts/ChainDataContext.tsx +++ b/advanced/dapps/react-dapp-v2/src/contexts/ChainDataContext.tsx @@ -16,6 +16,7 @@ import { CosmosChainData } from "../chains/cosmos"; import { EIP155ChainData } from "../chains/eip155"; import { TezosChainData } from "../chains/tezos"; import { KadenaChainData } from "../chains/kadena"; +import { BtcChainData } from "../chains/bip122"; /** * Types @@ -73,6 +74,9 @@ export function ChainDataContextProvider({ case "kadena": chains = KadenaChainData; break; + case "bip122": + chains = BtcChainData; + break; default: console.error("Unknown chain namespace: ", namespace); } diff --git a/advanced/dapps/react-dapp-v2/src/contexts/ClientContext.tsx b/advanced/dapps/react-dapp-v2/src/contexts/ClientContext.tsx index 0ca8990d1..28f1b2872 100644 --- a/advanced/dapps/react-dapp-v2/src/contexts/ClientContext.tsx +++ b/advanced/dapps/react-dapp-v2/src/contexts/ClientContext.tsx @@ -49,6 +49,7 @@ interface IContext { setChains: any; setRelayerRegion: any; origin: string; + setAccounts: any; } /** @@ -63,6 +64,16 @@ const web3Modal = new Web3Modal({ projectId: DEFAULT_PROJECT_ID, themeMode: "light", walletConnectVersion: 2, + mobileWallets: [ + { + id: "bifrost", + name: "Bifrost Wallet", + links: { + native: "bifrostwallet://", + universal: "https://bifrostwallet.com", + }, + }, + ], }); /** @@ -106,7 +117,6 @@ export function ClientContextProvider({ const [namespace, reference, address] = account.split(":"); const chainId = `${namespace}:${reference}`; const assets = await apiGetAccountBalance(address, chainId); - return { account, assets: [assets] }; }) ); @@ -123,6 +133,11 @@ export function ClientContextProvider({ } }; + useMemo(() => { + if (!accounts.length) return; + getAccountBalances(accounts); + }, [accounts]); + const onSessionConnected = useCallback( async (_session: SessionTypes.Struct) => { const allNamespaceAccounts = Object.values(_session.namespaces) @@ -147,26 +162,17 @@ export function ClientContextProvider({ } console.log("connect, pairing topic is:", pairing?.topic); try { - const requiredNamespaces = getRequiredNamespaces(chains); - console.log( - "requiredNamespaces config for connect:", - requiredNamespaces - ); - const optionalNamespaces = getOptionalNamespaces(chains); - console.log( - "optionalNamespaces config for connect:", - optionalNamespaces - ); + const namespacesToRequest = getRequiredNamespaces(chains); const { uri, approval } = await client.connect({ pairingTopic: pairing?.topic, - requiredNamespaces, - optionalNamespaces, + requiredNamespaces: {}, + optionalNamespaces: namespacesToRequest, }); // Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing). if (uri) { // Create a flat array of all requested chains across namespaces. - const standaloneChains = Object.values(requiredNamespaces) + const standaloneChains = Object.values(namespacesToRequest) .map((namespace) => namespace.chains) .flat() as string[]; @@ -375,6 +381,7 @@ export function ClientContextProvider({ setChains, setRelayerRegion, origin, + setAccounts, }), [ pairings, @@ -392,6 +399,7 @@ export function ClientContextProvider({ setChains, setRelayerRegion, origin, + setAccounts, ] ); diff --git a/advanced/dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx b/advanced/dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx index fdec84c1a..eb8d3631f 100644 --- a/advanced/dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx +++ b/advanced/dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx @@ -3,6 +3,8 @@ import { createContext, ReactNode, useContext, useState } from "react"; import * as encoding from "@walletconnect/encoding"; import { Transaction as EthTransaction } from "@ethereumjs/tx"; import { recoverTransaction } from "@celo/wallet-base"; +import * as bitcoin from "bitcoinjs-lib"; + import { formatDirectSignDoc, stringifySignDocValues, @@ -28,6 +30,7 @@ import { } from "@kadena/client"; import { PactNumber } from "@kadena/pactjs"; import { + IUTXO, KadenaAccount, eip712, formatTestBatchCall, @@ -54,6 +57,7 @@ import { SendCallsParams, GetCapabilitiesResult, GetCallsResult, + DEFAULT_BIP122_METHODS, DEFAULT_EIP7715_METHODS, WalletGrantPermissionsParameters, WalletGrantPermissionsReturnType, @@ -69,8 +73,15 @@ import { SignableMessage, } from "@multiversx/sdk-core"; import { UserVerifier } from "@multiversx/sdk-wallet/out/userVerifier"; -import { SignClient } from "@walletconnect/sign-client/dist/types/client"; import { parseEther } from "ethers/lib/utils"; +import { + apiGetAddressUtxos, + calculateChange, + getAvailableBalanceFromUtxos, + isOrdinalAddress, + isValidBip122Signature, +} from "../helpers/bip122"; +import { getAddressFromAccount } from "@walletconnect/utils"; /** * Types @@ -137,6 +148,12 @@ interface IContext { testSign: TRpcRequestCallback; testQuicksign: TRpcRequestCallback; }; + bip122Rpc: { + testGetAccountAddresses: TRpcRequestCallback; + testSignMessage: TRpcRequestCallback; + testSendTransaction: TRpcRequestCallback; + testSignPsbt: TRpcRequestCallback; + }; rpcResult?: IFormattedRpcResponse | null; isRpcRequestPending: boolean; isTestnet: boolean; @@ -164,7 +181,7 @@ export function JsonRpcContextProvider({ null ); - const { client, session, accounts, balances, solanaPublicKeys } = + const { client, session, accounts, balances, solanaPublicKeys, setAccounts } = useWalletConnectClient(); const { chainData } = useChainData(); @@ -872,7 +889,7 @@ export function JsonRpcContextProvider({ })), transaction: transaction .serialize({ verifySignatures: false }) - .toString('base64'), + .toString("base64"), }, }, }); @@ -1640,6 +1657,231 @@ export function JsonRpcContextProvider({ ), }; + const bip122Rpc = { + testSignMessage: _createJsonRpcRequestHandler( + async ( + chainId: string, + address: string + ): Promise => { + console.log("testSignMessage", chainId, address); + const method = DEFAULT_BIP122_METHODS.BIP122_SIGN_MESSAGE; + const message = "This is a message to be signed for BIP122"; + const shouldAddAddress = address !== getAddressFromAccount(accounts[0]); + const result = await client!.request<{ + signature: string; + address: string; + }>({ + topic: session!.topic, + chainId: chainId, + request: { + method, + params: { + message, + account: getAddressFromAccount(accounts[0]), + address: shouldAddAddress ? address : undefined, + }, + }, + }); + + return { + method, + address: address, + valid: await isValidBip122Signature( + address, + result.signature, + message + ), + result: result.signature, + }; + } + ), + testSendTransaction: _createJsonRpcRequestHandler( + async ( + chainId: string, + address: string + ): Promise => { + const method = DEFAULT_BIP122_METHODS.BIP122_SEND_TRANSACTION; + + const utxos = await apiGetAddressUtxos(address, chainId); + console.log("utxos", utxos); + const availableBalance = getAvailableBalanceFromUtxos(utxos); // in satoshis + console.log("availableBalance", availableBalance); + const req = { + account: address, + recipientAddress: address, + amount: "550", + }; + console.log("request", { + method, + params: req, + chainId, + }); + + const result = await client!.request<{ txid: string }>({ + topic: session!.topic, + chainId: chainId, + request: { + method, + params: req, + }, + }); + console.log("result", result); + return { + method, + address: address, + valid: true, + result: result?.txid, + }; + } + ), + testSignPsbt: _createJsonRpcRequestHandler( + async ( + chainId: string, + address: string + ): Promise => { + const method = DEFAULT_BIP122_METHODS.BIP122_SIGN_PSBT; + + const utxos = (await apiGetAddressUtxos(address, chainId)) as IUTXO[]; + if (!utxos || utxos.length === 0) { + throw new Error("No UTXOs found for address: " + address); + } + + const availableBalance = getAvailableBalanceFromUtxos(utxos); // in satoshis + const satoshisToTransfer = 550; + if (availableBalance < satoshisToTransfer) { + throw new Error( + "Insufficient balance: " + availableBalance + " satoshis" + ); + } + const network = bitcoin.networks.testnet; + const psbt = new bitcoin.Psbt({ network }); + + const utxosToSpend: any[] = []; + let utxosValue = 0; + utxos.forEach((utxo) => { + utxosValue += utxo.value; + utxosToSpend.push(utxo); + if (utxosValue >= satoshisToTransfer) { + return; + } + }); + const signInputs: unknown[] = []; + + utxosToSpend.forEach((utxo, index) => { + psbt.addInput({ + hash: utxo.txid, + index: utxo.vout, + witnessUtxo: { + script: bitcoin.address.toOutputScript(address, network), + value: utxo.value, + }, + }); + signInputs.push({ + address, + index, + sighashTypes: [bitcoin.Transaction.SIGHASH_ALL], + }); + }); + + const change = calculateChange(utxosToSpend, satoshisToTransfer, 5); + if (change > 0) { + psbt.addOutput({ + address: address, + value: change, + }); + } + + psbt.addOutput({ + address: address, + value: satoshisToTransfer, + }); + + const transaction = psbt.toBase64(); + + console.log("availableBalance", availableBalance); + const req = { + account: address, + psbt: transaction, + signInputs, + broadcast: false, + }; + console.log("signPsbt", { + method, + params: req, + chainId, + }); + + const result = await client!.request<{ psbt: string }>({ + topic: session!.topic, + chainId: chainId, + request: { + method, + params: req, + }, + }); + console.log("result", result); + const reconstructed = bitcoin.Psbt.fromBase64(result.psbt, { network }); + return { + method, + address: address, + valid: true, + result: reconstructed.extractTransaction().toHex(), + }; + } + ), + testGetAccountAddresses: _createJsonRpcRequestHandler( + async ( + chainId: string, + address: string + ): Promise => { + const method = DEFAULT_BIP122_METHODS.BIP122_GET_ACCOUNT_ADDRESSES; + const isOrdinal = isOrdinalAddress(address); + const req = { + account: address, + intentions: isOrdinal ? ["ordinal"] : ["payment"], + }; + const addresses = + session?.sessionProperties?.[ + `bip122_${DEFAULT_BIP122_METHODS.BIP122_GET_ACCOUNT_ADDRESSES}` + ]; + let result; + if (addresses) { + console.log("cached addresses", addresses); + const parsed = JSON.parse(addresses); + result = isOrdinal ? parsed.ordinal : parsed.payment; + console.log("parsed", result); + } else { + console.log("request", { + method, + params: req, + chainId, + }); + + result = await client!.request({ + topic: session!.topic, + chainId: chainId, + request: { + method, + params: req, + }, + }); + + console.log("result", result); + } + + const accounts = result.map((r: any) => `${chainId}:${r.address}`); + setAccounts((prev: string[]) => [...new Set([...prev, ...accounts])]); + + return { + method, + address: address, + valid: true, + result: result.map((r: any) => r.address).join(", "), + }; + } + ), + }; + return ( {children} diff --git a/advanced/dapps/react-dapp-v2/src/helpers/api.ts b/advanced/dapps/react-dapp-v2/src/helpers/api.ts index 59f304cad..ad3b4e6a7 100644 --- a/advanced/dapps/react-dapp-v2/src/helpers/api.ts +++ b/advanced/dapps/react-dapp-v2/src/helpers/api.ts @@ -3,6 +3,7 @@ import { apiGetKadenaAccountBalance } from "./kadena"; import { AssetData } from "./types"; import { PactCommand } from "@kadena/client"; +import { apiGetBip122AccountBalance } from "./bip122"; export type RpcProvidersByChainId = Record< number, @@ -155,6 +156,10 @@ export async function apiGetAccountBalance( ); } + if (namespace === "bip122") { + return apiGetBip122AccountBalance(address, networkId as string); + } + if (namespace !== "eip155") { return { balance: "", symbol: "", name: "" }; } diff --git a/advanced/dapps/react-dapp-v2/src/helpers/bip122.ts b/advanced/dapps/react-dapp-v2/src/helpers/bip122.ts new file mode 100644 index 000000000..8f178f1be --- /dev/null +++ b/advanced/dapps/react-dapp-v2/src/helpers/bip122.ts @@ -0,0 +1,78 @@ +import { schnorr } from "@noble/secp256k1"; +import * as bitcoin from "bitcoinjs-lib"; +import BitcoinMessage from "bitcoinjs-message"; +import { convertHexToBase64 } from "./utilities"; +import { IUTXO } from "./types"; +import { BIP122_TESTNET } from "../chains/bip122"; +export async function apiGetBip122AccountBalance( + address: string, + chainId: string +) { + const utxo = await apiGetAddressUtxos(address, chainId); + const balanceInSatoshis = getAvailableBalanceFromUtxos(utxo); + const balanceInBtc = balanceInSatoshis * 0.00000001; + return { balance: balanceInBtc.toString(), symbol: "BTC", name: "BTC" }; +} + +export async function apiGetAddressUtxos(address: string, chainId: string) { + const isTestnet = chainId.includes(BIP122_TESTNET); + return await ( + await fetch( + `https://mempool.space${ + isTestnet ? "/testnet" : "" + }/api/address/${address}/utxo` + ) + ).json(); +} + +export function getAvailableBalanceFromUtxos(utxos: IUTXO[]) { + if (!utxos || !utxos.length) { + return 0; + } + return utxos.reduce((acc, { value }) => acc + value, 0); +} + +export function calculateChange( + utxos: IUTXO[], + amount: number, + feeRate: number +): number { + const inputSum = utxos.reduce((sum, utxo) => sum + utxo.value, 0); // Sum of all UTXO values + const estimatedSize = 10 + 148 * utxos.length + 34 * 2; // Rough estimate of transaction size + const fee = estimatedSize * feeRate; // Transaction fee + const change = inputSum - amount - fee; // Calculate change + return change; +} + +export async function isValidBip122Signature( + address: string, + signature: string, + message: string +) { + // if taproot address + if (address.startsWith("bc1p") || address.startsWith("tb1p")) { + // Convert the Ordinals address (Taproot) to the internal public key + const decoded = bitcoin.address.fromBech32(address); + if (decoded.version !== 1 || decoded.data.length !== 32) { + throw new Error("Invalid Taproot address"); + } + + const publicKey = decoded.data; // The 32-byte internal public key (X coordinate of pubkey) + + // Hash the message using SHA256 (standard Bitcoin message hashing) + const messageHash = bitcoin.crypto.sha256(Buffer.from(message)); + + // Verify the Schnorr signature using tiny-secp256k1 + return await schnorr.verify( + Buffer.from(signature, "hex"), + messageHash, + publicKey + ); + } + + return BitcoinMessage.verify(message, address, convertHexToBase64(signature)); +} + +export function isOrdinalAddress(address: string) { + return address.startsWith("tb1p"); +} diff --git a/advanced/dapps/react-dapp-v2/src/helpers/namespaces.ts b/advanced/dapps/react-dapp-v2/src/helpers/namespaces.ts index 553f7b53d..f5363c51b 100644 --- a/advanced/dapps/react-dapp-v2/src/helpers/namespaces.ts +++ b/advanced/dapps/react-dapp-v2/src/helpers/namespaces.ts @@ -19,6 +19,8 @@ import { DEFAULT_TEZOS_METHODS, DEFAULT_TEZOS_EVENTS, DEFAULT_OPTIONAL_METHODS, + DEFAULT_BIP122_METHODS, + DEFAULT_BIP122_EVENTS, } from "../constants"; export const getNamespacesFromChains = (chains: string[]) => { @@ -53,6 +55,8 @@ export const getSupportedRequiredMethodsByNamespace = (namespace: string) => { return Object.values(DEFAULT_TEZOS_METHODS); case "kadena": return Object.values(DEFAULT_KADENA_METHODS); + case "bip122": + return Object.values(DEFAULT_BIP122_METHODS); default: throw new Error( `No default required methods for namespace: ${namespace}` @@ -72,6 +76,7 @@ export const getSupportedOptionalMethodsByNamespace = (namespace: string) => { case "tron": case "tezos": case "kadena": + case "bip122": return []; default: throw new Error( @@ -100,6 +105,8 @@ export const getSupportedEventsByNamespace = (namespace: string) => { return Object.values(DEFAULT_TEZOS_EVENTS); case "kadena": return Object.values(DEFAULT_KADENA_EVENTS); + case "bip122": + return Object.values(DEFAULT_BIP122_EVENTS); default: throw new Error(`No default events for namespace: ${namespace}`); } diff --git a/advanced/dapps/react-dapp-v2/src/helpers/types.ts b/advanced/dapps/react-dapp-v2/src/helpers/types.ts index 795318ef2..5c4f54f9f 100644 --- a/advanced/dapps/react-dapp-v2/src/helpers/types.ts +++ b/advanced/dapps/react-dapp-v2/src/helpers/types.ts @@ -163,3 +163,15 @@ export interface KadenaAccount { account: string; // Kadena account chainId: ChainId; // Kadena ChainId } + +export interface IUTXO { + txid: string; + vout: number; + value: number; + status: { + confirmed: boolean; + block_height: number; + block_hash: string; + block_time: number; + }; +} diff --git a/advanced/dapps/react-dapp-v2/src/helpers/utilities.ts b/advanced/dapps/react-dapp-v2/src/helpers/utilities.ts index 776b32c0e..e25fa569d 100644 --- a/advanced/dapps/react-dapp-v2/src/helpers/utilities.ts +++ b/advanced/dapps/react-dapp-v2/src/helpers/utilities.ts @@ -182,6 +182,13 @@ export function convertHexToUtf8(hex: string) { return hex; } } +export function convertHexToBase64(hex: string) { + try { + return encoding.hexToBuffer(hex).toString("base64"); + } catch (e) { + return hex; + } +} export const sanitizeDecimals = (value: string, decimals = 18): string => { const [integer, fractional] = value.split("."); @@ -196,7 +203,11 @@ export const toWad = (amount: string, decimals = 18): BigNumber => { }; export const fromWad = (wad: BigNumberish, decimals = 18): string => { - return sanitizeDecimals(utils.formatUnits(wad, decimals), decimals); + try { + return sanitizeDecimals(utils.formatUnits(wad, decimals), decimals); + } catch (e) { + return wad?.toString(); + } }; export const LOCALSTORAGE_KEY_TESTNET = "TESTNET"; diff --git a/advanced/dapps/react-dapp-v2/src/pages/index.tsx b/advanced/dapps/react-dapp-v2/src/pages/index.tsx index 6827e2f7d..b8d844fed 100644 --- a/advanced/dapps/react-dapp-v2/src/pages/index.tsx +++ b/advanced/dapps/react-dapp-v2/src/pages/index.tsx @@ -23,6 +23,7 @@ import { DEFAULT_EIP155_OPTIONAL_METHODS, DEFAULT_EIP5792_METHODS, GetCapabilitiesResult, + DEFAULT_BIP122_METHODS, DEFAULT_EIP7715_METHODS, } from "../constants"; import { AccountAction, setLocaleStorageTestnetFlag } from "../helpers"; @@ -79,6 +80,7 @@ const Home: NextPage = () => { setChains, setRelayerRegion, origin, + setAccounts, } = useWalletConnectClient(); // Use `JsonRpcContext` to provide us with relevant RPC methods and states. @@ -93,6 +95,7 @@ const Home: NextPage = () => { tronRpc, tezosRpc, kadenaRpc, + bip122Rpc, isRpcRequestPending, rpcResult, isTestnet, @@ -476,6 +479,43 @@ const Home: NextPage = () => { ]; }; + const getBip122Actions = (): AccountAction[] => { + const onSignMessage = async (chainId: string, address: string) => { + openRequestModal(); + await bip122Rpc.testSignMessage(chainId, address); + }; + const onGetAccountAddresses = async (chainId: string, address: string) => { + openRequestModal(); + await bip122Rpc.testGetAccountAddresses(chainId, address); + }; + const onSendTransaction = async (chainId: string, address: string) => { + openRequestModal(); + await bip122Rpc.testSendTransaction(chainId, address); + }; + const onSignPsbt = async (chainId: string, address: string) => { + openRequestModal(); + await bip122Rpc.testSignPsbt(chainId, address); + }; + return [ + { + method: DEFAULT_BIP122_METHODS.BIP122_SEND_TRANSACTION, + callback: onSendTransaction, + }, + { + method: DEFAULT_BIP122_METHODS.BIP122_GET_ACCOUNT_ADDRESSES, + callback: onGetAccountAddresses, + }, + { + method: DEFAULT_BIP122_METHODS.BIP122_SIGN_MESSAGE, + callback: onSignMessage, + }, + { + method: DEFAULT_BIP122_METHODS.BIP122_SIGN_PSBT, + callback: onSignPsbt, + }, + ]; + }; + const getBlockchainActions = (account: string) => { const [namespace, chainId, address] = account.split(":"); switch (namespace) { @@ -497,6 +537,8 @@ const Home: NextPage = () => { return getTezosActions(); case "kadena": return getKadenaActions(); + case "bip122": + return getBip122Actions(); default: break; } diff --git a/advanced/dapps/react-dapp-v2/tsconfig.json b/advanced/dapps/react-dapp-v2/tsconfig.json index 99710e857..bad4c604c 100644 --- a/advanced/dapps/react-dapp-v2/tsconfig.json +++ b/advanced/dapps/react-dapp-v2/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2015", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, diff --git a/advanced/dapps/react-dapp-v2/yarn.lock b/advanced/dapps/react-dapp-v2/yarn.lock index 2ead6ccf3..674a5a904 100644 --- a/advanced/dapps/react-dapp-v2/yarn.lock +++ b/advanced/dapps/react-dapp-v2/yarn.lock @@ -2367,6 +2367,13 @@ dependencies: "@noble/hashes" "1.4.0" +"@noble/curves@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b" + integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ== + dependencies: + "@noble/hashes" "1.5.0" + "@noble/ed25519@1.7.3": version "1.7.3" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" @@ -2392,6 +2399,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== +"@noble/hashes@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": version "1.3.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" @@ -4967,7 +4979,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bech32@1.1.4, bech32@^1.1.4: +bech32@1.1.4, bech32@^1.1.3, bech32@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== @@ -5031,6 +5043,11 @@ bindings@^1.3.0, bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bip174@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.1.tgz#ef3e968cf76de234a546962bcf572cc150982f9f" + integrity sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ== + bip39@3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.2.tgz#2baf42ff3071fc9ddd5103de92e8f80d9257ee32" @@ -5048,6 +5065,37 @@ bip39@^3.0.2: dependencies: "@noble/hashes" "^1.2.0" +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== + dependencies: + safe-buffer "^5.0.1" + +bitcoinjs-lib@^6.1.5: + version "6.1.6" + resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.6.tgz#f57c17c82511f860f11946d784c18da39f8618a8" + integrity sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA== + dependencies: + "@noble/hashes" "^1.2.0" + bech32 "^2.0.0" + bip174 "^2.1.1" + bs58check "^3.0.1" + typeforce "^1.11.3" + varuint-bitcoin "^1.1.2" + +bitcoinjs-message@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bitcoinjs-message/-/bitcoinjs-message-2.2.0.tgz#8116ec7f447f9889e23030fc15c5286a6ae5503b" + integrity sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA== + dependencies: + bech32 "^1.1.3" + bs58check "^2.1.2" + buffer-equals "^1.0.3" + create-hash "^1.1.2" + secp256k1 "^3.0.1" + varuint-bitcoin "^1.0.1" + blake2b-wasm@^1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz#e4d075da10068e5d4c3ec1fb9accc4d186c55d81" @@ -5201,7 +5249,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -5305,6 +5353,14 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-3.0.1.tgz#2094d13720a28593de1cba1d8c4e48602fdd841c" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -5312,6 +5368,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-equals@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/buffer-equals/-/buffer-equals-1.0.4.tgz#0353b54fd07fd9564170671ae6f66b9cf10d27f5" + integrity sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -6830,6 +6891,15 @@ dotenv@8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g== + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + duplexer@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -11344,6 +11414,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.14.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" + integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== + nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" @@ -14126,6 +14201,20 @@ scryptsy@2.1.0: resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== +secp256k1@^3.0.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" + integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + secp256k1@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" @@ -15465,6 +15554,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typeforce@^1.11.3: + version "1.18.0" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + typescript@^4.7.4: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -15848,6 +15942,13 @@ varint@^5.0.0: resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== +varuint-bitcoin@^1.0.1, varuint-bitcoin@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz#e76c138249d06138b480d4c5b40ef53693e24e92" + integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== + dependencies: + safe-buffer "^5.1.1" + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" diff --git a/advanced/wallets/react-wallet-v2/next.config.js b/advanced/wallets/react-wallet-v2/next.config.js index e1f3bae45..ebacdf340 100644 --- a/advanced/wallets/react-wallet-v2/next.config.js +++ b/advanced/wallets/react-wallet-v2/next.config.js @@ -5,7 +5,12 @@ module.exports = { ...config.resolve.fallback, fs: false } - + // needed for tiny-secp256k1 package + config.experiments.asyncWebAssembly = true + config.module.rules.push({ + test: /\.wasm$/, + type: 'webassembly/async' + }) return config }, async headers() { diff --git a/advanced/wallets/react-wallet-v2/package.json b/advanced/wallets/react-wallet-v2/package.json index 3e49268e8..bd77bd224 100644 --- a/advanced/wallets/react-wallet-v2/package.json +++ b/advanced/wallets/react-wallet-v2/package.json @@ -26,23 +26,28 @@ "@multiversx/sdk-wallet": "4.2.0", "@near-wallet-selector/wallet-utils": "^8.0.0", "@nextui-org/react": "1.0.8-beta.5", + "@noble/curves": "^1.6.0", "@polkadot/keyring": "^10.1.2", "@polkadot/types": "^9.3.3", + "@reown/walletkit": "1.0.0", "@reown/appkit-experimental": "1.1.5", "@rhinestone/module-sdk": "0.1.25", "@solana/web3.js": "1.89.2", "@taquito/signer": "^15.1.0", "@taquito/taquito": "^15.1.0", "@types/semver": "^7.5.8", - "@reown/walletkit": "1.0.0", "@zerodev/ecdsa-validator": "5.3.0", "@zerodev/presets": "5.3.0", "@zerodev/sdk": "5.3.1", "@zerodev/session-key": "5.4.0", "@zerodev/weighted-ecdsa-validator": "5.3.0", + "bip32": "^4.0.0", + "bitcoinjs-lib": "^6.1.5", + "bitcoinjs-message": "^2.2.0", "borsh": "^1.0.0", "bs58": "6.0.0", "cosmos-wallet": "1.2.0", + "ecpair": "^2.1.0", "ethers": "5.7.2", "framer-motion": "6.5.1", "graphql": "^16.8.2", @@ -54,7 +59,8 @@ "react-dom": "17.0.2", "react-hot-toast": "^2.4.1", "react-qr-reader-es6": "2.2.1-2", - "solady": "^0.0.234", + "solana-wallet": "^1.0.2", + "tiny-secp256k1": "^2.2.3", "tronweb": "^4.4.0", "valtio": "1.13.2", "viem": "2.17.8", @@ -71,4 +77,4 @@ "prettier": "2.6.2", "typescript": "5.2.2" } -} \ No newline at end of file +} diff --git a/advanced/wallets/react-wallet-v2/public/chain-logos/btc-testnet.png b/advanced/wallets/react-wallet-v2/public/chain-logos/btc-testnet.png new file mode 100644 index 000000000..b991e4b73 Binary files /dev/null and b/advanced/wallets/react-wallet-v2/public/chain-logos/btc-testnet.png differ diff --git a/advanced/wallets/react-wallet-v2/src/components/Modal.tsx b/advanced/wallets/react-wallet-v2/src/components/Modal.tsx index f8fba7876..1f81fc8a3 100644 --- a/advanced/wallets/react-wallet-v2/src/components/Modal.tsx +++ b/advanced/wallets/react-wallet-v2/src/components/Modal.tsx @@ -18,7 +18,10 @@ import { useSnapshot } from 'valtio' import { useCallback, useMemo } from 'react' import LoadingModal from '@/views/LoadingModal' import SessionAuthenticateModal from '@/views/SessionAuthenticateModal' +import SessionSignBip122Modal from '@/views/SessionSignBip122Modal' +import SessionSendTransactionBip122Modal from '@/views/SessionSendTransactionBip122Modal' import SessionGrantPermissionsModal from '@/views/SessionGrantPermissionsModal' +import SessionGetBip122AddressesModal from '@/views/SessionGetBip122AddressesModal' export default function Modal() { const { open, view } = useSnapshot(ModalStore.state) @@ -65,6 +68,12 @@ export default function Modal() { return case 'SessionAuthenticateModal': return + case 'SessionSignBip122Modal': + return + case 'SessionGetBip122AddressesModal': + return + case 'SessionSendTransactionBip122Modal': + return default: return null } diff --git a/advanced/wallets/react-wallet-v2/src/data/Bip122Data.ts b/advanced/wallets/react-wallet-v2/src/data/Bip122Data.ts new file mode 100644 index 000000000..e5d09f6d6 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/data/Bip122Data.ts @@ -0,0 +1,60 @@ +/** + * Chains + */ +export const BIP122_NAMESPACE = 'bip122' + +export const BIP122_MAINNET_ID = '000000000019d6689c085ae165831e93' +export const BIP122_TESTNET_ID = '000000000933ea01ad0ee984209779ba' +export const BIP122_MAINNET_CAIP2 = `${BIP122_NAMESPACE}:${BIP122_MAINNET_ID}` +export const BIP122_TESTNET_CAIP2 = `${BIP122_NAMESPACE}:${BIP122_TESTNET_ID}` + +export type IBip122ChainId = typeof BIP122_MAINNET_CAIP2 | typeof BIP122_TESTNET_CAIP2 + +export const BITCOIN_MAINNET = { + [BIP122_MAINNET_CAIP2]: { + chainId: BIP122_MAINNET_ID, + name: 'BTC Mainnet', + logo: '/chain-logos/btc-testnet.png', + rgb: '107, 111, 147', + rpc: '', + coinType: '0', + caip2: BIP122_MAINNET_CAIP2 as IBip122ChainId, + namespace: BIP122_NAMESPACE + } +} +export const BITCOIN_TESTNET = { + [BIP122_TESTNET_CAIP2]: { + chainId: BIP122_TESTNET_ID, + name: 'BTC Testnet', + logo: '/chain-logos/btc-testnet.png', + rgb: '247, 147, 25', + rpc: '', + coinType: '1', + caip2: BIP122_TESTNET_CAIP2 as IBip122ChainId, + namespace: BIP122_NAMESPACE + } +} + +export const BIP122_CHAINS = { ...BITCOIN_MAINNET, ...BITCOIN_TESTNET } as Record< + IBip122ChainId, + typeof BITCOIN_MAINNET[typeof BIP122_MAINNET_CAIP2] & + typeof BITCOIN_TESTNET[typeof BIP122_TESTNET_CAIP2] +> + +/** + * Methods + */ +export const BIP122_SIGNING_METHODS = { + BIP122_SIGN_MESSAGE: 'signMessage', + BIP122_GET_ACCOUNT_ADDRESSES: 'getAccountAddresses', + BIP122_SEND_TRANSACTION: 'sendTransfer', + BIP122_SIGN_PSBT: 'signPsbt' +} + +/** + * Events + */ + +export const BIP122_EVENTS = { + BIP122_ADDRESSES_CHANGED: 'bip122_addressesChanged' +} diff --git a/advanced/wallets/react-wallet-v2/src/data/chainsUtil.ts b/advanced/wallets/react-wallet-v2/src/data/chainsUtil.ts index 6ebea5fd2..f002f3d8f 100644 --- a/advanced/wallets/react-wallet-v2/src/data/chainsUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/data/chainsUtil.ts @@ -1,3 +1,4 @@ +import { BIP122_CHAINS } from './Bip122Data' import * as viemChains from 'viem/chains' import { COSMOS_MAINNET_CHAINS } from './COSMOSData' import { EIP155_CHAINS } from './EIP155Data' @@ -18,7 +19,8 @@ export const ALL_CHAINS = { ...POLKADOT_CHAINS, ...SOLANA_CHAINS, ...TEZOS_CHAINS, - ...TRON_CHAINS + ...TRON_CHAINS, + ...BIP122_CHAINS } export function getChainData(chainId?: string) { diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useInitialization.ts b/advanced/wallets/react-wallet-v2/src/hooks/useInitialization.ts index f0f56ea3a..2599b7728 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useInitialization.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useInitialization.ts @@ -12,6 +12,7 @@ import { createOrRestoreKadenaWallet } from '@/utils/KadenaWalletUtil' import { useCallback, useEffect, useRef, useState } from 'react' import { useSnapshot } from 'valtio' import useSmartAccounts from './useSmartAccounts' +import { createOrRestoreBip122Wallet } from '@/utils/Bip122WalletUtil' export default function useInitialization() { const [initialized, setInitialized] = useState(false) @@ -31,6 +32,7 @@ export default function useInitialization() { const { tronAddresses } = await createOrRestoreTronWallet() const { tezosAddresses } = await createOrRestoreTezosWallet() const { kadenaAddresses } = await createOrRestoreKadenaWallet() + const { bip122Addresses } = await createOrRestoreBip122Wallet() await initializeSmartAccounts(eip155Wallets[eip155Addresses[0]].getPrivateKey()) SettingsStore.setEIP155Address(eip155Addresses[0]) @@ -42,6 +44,7 @@ export default function useInitialization() { SettingsStore.setTronAddress(tronAddresses[0]) SettingsStore.setTezosAddress(tezosAddresses[0]) SettingsStore.setKadenaAddress(kadenaAddresses[0]) + SettingsStore.setbip122Address(bip122Addresses[0]) await createWalletKit(relayerRegionURL) setInitialized(true) } catch (err: unknown) { diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts b/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts index 4cf2d9665..c7b5821f6 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts @@ -18,6 +18,7 @@ import { formatJsonRpcError } from '@json-rpc-tools/utils' import { approveEIP5792Request } from '@/utils/EIP5792RequestHandlerUtils' import EIP155Lib from '@/lib/EIP155Lib' import { getWallet } from '@/utils/EIP155WalletUtil' +import { BIP122_SIGNING_METHODS } from '@/data/Bip122Data' import { EIP7715_METHOD } from '@/data/EIP7715Data' import { refreshSessionsList } from '@/pages/wc' @@ -140,6 +141,16 @@ export default function useWalletConnectEventsManager(initialized: boolean) { case KADENA_SIGNING_METHODS.KADENA_SIGN: case KADENA_SIGNING_METHODS.KADENA_QUICKSIGN: return ModalStore.open('SessionSignKadenaModal', { requestEvent, requestSession }) + case BIP122_SIGNING_METHODS.BIP122_SIGN_MESSAGE: + return ModalStore.open('SessionSignBip122Modal', { requestEvent, requestSession }) + case BIP122_SIGNING_METHODS.BIP122_GET_ACCOUNT_ADDRESSES: + return ModalStore.open('SessionGetBip122AddressesModal', { requestEvent, requestSession }) + case BIP122_SIGNING_METHODS.BIP122_SIGN_PSBT: + case BIP122_SIGNING_METHODS.BIP122_SEND_TRANSACTION: + return ModalStore.open('SessionSendTransactionBip122Modal', { + requestEvent, + requestSession + }) default: return ModalStore.open('SessionUnsuportedMethodModal', { requestEvent, requestSession }) } diff --git a/advanced/wallets/react-wallet-v2/src/lib/Bip122Lib.ts b/advanced/wallets/react-wallet-v2/src/lib/Bip122Lib.ts new file mode 100644 index 000000000..eed33445b --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/lib/Bip122Lib.ts @@ -0,0 +1,439 @@ +import ECPairFactory from 'ecpair' +import * as bitcoin from 'bitcoinjs-lib' +import * as ecc from 'tiny-secp256k1' +import * as bip39 from 'bip39' +import BIP32Factory, { BIP32Interface } from 'bip32' +import bitcoinMessage from 'bitcoinjs-message' +import { schnorr } from '@noble/secp256k1' +import { BIP122_CHAINS, BIP122_TESTNET_ID, IBip122ChainId } from '@/data/Bip122Data' +bitcoin.initEccLib(ecc) + +const ECPair = ECPairFactory(ecc) +const bip32 = BIP32Factory(ecc) +interface IInitArguments { + privateKey?: string +} + +interface IUTXO { + txid: string + vout: number + value: number + status: { + confirmed: boolean + block_height: number + block_hash: string + block_time: number + } +} + +interface ICreateTransaction { + network: bitcoin.Network + recipientAddress: string + amount: number + changeAddress: string + memo?: string + utxos: IUTXO[] + privateKeyWIF: string + feeRate: number +} + +interface IAddressData { + address: string + path: string + publicKey: string +} + +interface IPsbtInput { + address: string + index: number + sighashTypes: number[] +} + +interface ISignPsbt { + account: string + psbt: string + signInputs: IPsbtInput[] + broadcast: boolean + chainId: IBip122ChainId +} + +const validator = (pubkey: Buffer, msghash: Buffer, signature: Buffer): boolean => { + return ECPair.fromPublicKey(pubkey).verify(msghash, signature) +} + +/** + * Library + */ +export default class Bip122Lib { + private account: BIP32Interface + private mnemonic: string + private addresses = {} as Record> + private ordinals = {} as Record> + private keys = {} as Record< + IBip122ChainId, + Map + > + + constructor(key?: string) { + this.keys = Object.values(BIP122_CHAINS).reduce((acc, chain) => { + acc[chain.caip2] = new Map() + return acc + }, this.keys) + this.addresses = Object.values(BIP122_CHAINS).reduce((acc, chain) => { + acc[chain.caip2] = new Map() + return acc + }, this.addresses) + this.ordinals = Object.values(BIP122_CHAINS).reduce((acc, chain) => { + acc[chain.caip2] = new Map() + return acc + }, this.ordinals) + + this.mnemonic = key ? key : bip39.generateMnemonic() + const seed = bip39.mnemonicToSeedSync(this.mnemonic) + const root = bip32.fromSeed(seed) + this.account = bip32.fromBase58(root.toBase58()) + const addressIndex = (localStorage.getItem(`${seed}_index`) || 0) as number + this.loadAddresses(addressIndex) + } + + static async init({ privateKey }: IInitArguments) { + return new Bip122Lib(privateKey) + } + + public getAddress(chainId: IBip122ChainId) { + return Array.from(this.addresses[chainId].values())[0].address + } + + public getOrdinalsAddress(chainId: IBip122ChainId) { + return Array.from(this.ordinals[chainId].values())[0].address + } + + public getPrivateKey() { + return this.mnemonic + } + + public getAddresses(chainId: IBip122ChainId, intentions?: string[]) { + if (intentions && intentions[0] === 'ordinal') { + return this.ordinals[chainId] + } + return this.addresses[chainId] + } + + public async signMessage({ + message, + address, + protocol, + chainId + }: { + message: string + address: string + protocol?: string + chainId: IBip122ChainId + }) { + if (protocol && protocol !== 'ecdsa') { + throw new Error(`Supported signing protols: ecdsa, received: ${protocol}`) + } + const addressData = this.getAddressData(address, chainId) + if (!addressData) { + throw new Error(`Unkown address: ${address}`) + } + const keyData = this.keys[chainId].get(address)! + var keyPair = ECPair.fromWIF(keyData.wif) + var privateKey = keyPair.privateKey! + + let signature + if (this.isOrdinal(address, chainId)) { + const messageHash = bitcoin.crypto.sha256(Buffer.from(message)) + + const sig = await schnorr.sign(messageHash, privateKey) + signature = Buffer.from(sig) + } else { + signature = bitcoinMessage.sign(message, privateKey, keyPair.compressed, { + segwitType: 'p2wpkh' + }) + } + + return { + signature: signature.toString('hex').replace('0x', ''), + address + } + } + + public async sendTransfer(params: { + account: string + recipientAddress: string + amount: string + changeAddress?: string + memo?: string + chainId: IBip122ChainId + }) { + const { account, recipientAddress, amount, changeAddress, memo, chainId } = params + const satoshis = parseInt(amount) + + const addressData = this.getAddressData(account, chainId) + if (!addressData) { + throw new Error(`Unkown address: ${account}`) + } + + if (satoshis < 0) { + throw new Error(`Invalid amount: ${amount}`) + } + + const utxos = (await this.getUTXOs(account, chainId)) as IUTXO[] + if (!utxos || utxos.length === 0) { + throw new Error(`No UTXOs found for address: ${account}`) + } + + let utxosValue = 0 + const utxosToSpend: IUTXO[] = [] + utxos.forEach(utxo => { + utxosValue += utxo.value + utxosToSpend.push(utxo) + if (utxosValue >= satoshis) { + return + } + }) + + const keyData = this.keys[chainId].get(account)! + const transaction = await this.createTransaction({ + network: keyData.network, + recipientAddress, + amount: satoshis, + changeAddress: changeAddress || account, + utxos: utxosToSpend, + privateKeyWIF: keyData.wif, + memo, + feeRate: await this.getFeeRate() + }) + return await this.broadcastTransaction(transaction, chainId) + } + + async getUTXOs(address: string, chainId: IBip122ChainId): Promise { + const isTestnet = this.isTestnet(chainId) + // make chain dynamic + return await ( + await fetch(`https://mempool.space${isTestnet ? '/testnet' : ''}/api/address/${address}/utxo`) + ).json() + } + + async broadcastTransaction(transaction: string, chainId: IBip122ChainId) { + const isTestnet = this.isTestnet(chainId) + const result = await fetch(`https://mempool.space${isTestnet ? '/testnet' : ''}/api/tx`, { + method: 'POST', + body: transaction + }) + + if (result.ok) { + return await result.text() + } + throw new Error('Error broadcasting transaction: ' + (await result.text())) + } + + getAvailableBalance(utxos: IUTXO[]) { + return utxos.reduce((acc, { value }) => acc + value, 0) + } + + private async getFeeRate() { + const defaultFeeRate = 2 + try { + const response = await fetch('https://mempool.space/api/v1/fees/recommended') + if (response.ok) { + const data = await response.json() + return parseInt(data?.economyFee ?? defaultFeeRate) + } + } catch (e) { + console.error('Error fetching fee rate', e) + } + return defaultFeeRate + } + + private generateAddress({ + index, + coinType, + chainId, + change = false, + taproot = false + }: { + index: number + coinType: string + chainId: IBip122ChainId + change?: boolean + taproot?: boolean + }) { + const network = this.getNetwork(coinType) + const path = `m/84'/${coinType}'/0'/${change ? 1 : 0}/${index}` + const child = this.account.derivePath(path) + let address + if (taproot) { + address = bitcoin.payments.p2tr({ + pubkey: child.publicKey.slice(1), + network + }).address! + } else { + address = bitcoin.payments.p2wpkh({ + pubkey: child.publicKey, + network + }).address! + } + const wif = child.toWIF() + this.keys[chainId].set(address, { wif, network }) + return { address, path, publicKey: child.publicKey.toString('hex') } + } + + private loadAddresses(startIndex = 0) { + console.log('Loading addresses...') + console.log('Keys:', this.keys) + console.log('Addresses:', this.addresses) + console.log('Ordinals:', this.ordinals) + Object.keys(this.keys).forEach(chainId => { + const data = BIP122_CHAINS[chainId as IBip122ChainId] + const addressesToLoad = startIndex + 20 + + for (let i = startIndex; i < addressesToLoad; i++) { + const addressParams = { + index: i, + chainId: data.caip2, + coinType: data.coinType + } + // payment addresses + const addressData = this.generateAddress(addressParams) + this.addresses[data.caip2].set(addressData.address, addressData) + // ordinals + const taprootAddress = this.generateAddress({ + ...addressParams, + taproot: true + }) + this.ordinals[data.caip2].set(taprootAddress.address, taprootAddress) + } + console.log('Loaded addresses:', this.addresses, this.ordinals) + }) + } + + private getNetwork(coinType: string) { + if (coinType === '0') { + return bitcoin.networks.bitcoin + } else if (coinType === '1') { + return bitcoin.networks.testnet + } + throw new Error(`Unsupported coin type: ${coinType}`) + } + + private async createTransaction({ + network, + recipientAddress, + amount, + changeAddress, + memo, + utxos, + privateKeyWIF, + feeRate + }: ICreateTransaction) { + const psbt = new bitcoin.Psbt({ network }) + const keyPair = ECPair.fromWIF(privateKeyWIF) + const payment = bitcoin.payments.p2wpkh({ + pubkey: keyPair.publicKey, + network: bitcoin.networks.testnet + }) + + utxos.forEach(utxo => { + psbt.addInput({ + hash: utxo.txid, + index: utxo.vout, + witnessUtxo: { + script: Buffer.from(payment.output?.toString('hex')!, 'hex'), + value: utxo.value + } + }) + }) + + psbt.addOutput({ + address: recipientAddress, + value: amount + }) + const change = this.calculateChange(utxos, amount, feeRate) + + if (change > 0) { + psbt.addOutput({ + address: changeAddress, + value: change + }) + } + + if (memo) { + const data = Buffer.from(memo, 'utf8') + const embed = bitcoin.payments.embed({ data: [data] }) + psbt.addOutput({ + script: embed.output!, + value: 0 + }) + } + + psbt.signAllInputs(keyPair) + + psbt.validateSignaturesOfInput(0, validator) + + psbt.finalizeAllInputs() + + const tx = psbt.extractTransaction() + + return tx.toHex() + } + + public async signPsbt({ account, psbt, signInputs, broadcast = false, chainId }: ISignPsbt) { + const keyData = this.keys[chainId].get(account)! + const keyPair = ECPair.fromWIF(keyData.wif) + const transaction = bitcoin.Psbt.fromBase64(psbt, { network: keyData.network }) + signInputs.forEach(({ address, index, sighashTypes }) => { + let keyPairToSignWith = keyPair + if (address !== account) { + const keyData = this.keys[chainId].get(address)! + keyPairToSignWith = ECPair.fromWIF(keyData.wif) + } + transaction.signInput(index, keyPairToSignWith, sighashTypes) + }) + transaction.validateSignaturesOfInput(0, validator) + transaction.finalizeAllInputs() + + if (!broadcast) { + return { + psbt: transaction.toBase64() + } + } + + const tx = transaction.extractTransaction() + const txId = await this.broadcastTransaction(tx.toHex(), chainId) + return { + psbt: transaction.toBase64(), + txid: txId + } + } + + // Helper function to calculate change + private calculateChange(utxos: IUTXO[], amount: number, feeRate: number): number { + const inputSum = utxos.reduce((sum, utxo) => sum + utxo.value, 0) + /** + * 10 bytes: This is an estimated fixed overhead for the transaction. + * 148 bytes: This is the average size of each input (UTXO). + * 34 bytes: This is the size of each output. + * The multiplication by 2 indicates that there are usually two outputs in a typical transaction (one for the recipient and one for change) + */ + const estimatedSize = 10 + 148 * utxos.length + 34 * 2 + const fee = estimatedSize * feeRate + const change = inputSum - amount - fee + return change + } + + private getAddressData(address: string, chainId: IBip122ChainId) { + const addressData = this.addresses[chainId].get(address) + if (addressData) return addressData + return this.ordinals[chainId].get(address) + } + + private isOrdinal(address: string, chainId: IBip122ChainId) { + return this.ordinals[chainId].has(address) + } + + private isTestnet(chainId: IBip122ChainId) { + return chainId.includes(BIP122_TESTNET_ID) + } +} diff --git a/advanced/wallets/react-wallet-v2/src/pages/index.tsx b/advanced/wallets/react-wallet-v2/src/pages/index.tsx index 48346431e..5442640fa 100644 --- a/advanced/wallets/react-wallet-v2/src/pages/index.tsx +++ b/advanced/wallets/react-wallet-v2/src/pages/index.tsx @@ -15,6 +15,7 @@ import { Text } from '@nextui-org/react' import { Fragment } from 'react' import { useSnapshot } from 'valtio' import useSmartAccounts from '@/hooks/useSmartAccounts' +import { BIP122_CHAINS } from '@/data/Bip122Data' import { useRouter } from 'next/router' import ChainAbstractionBalanceCard from '@/components/ChainAbstractionBalanceCard' @@ -30,6 +31,7 @@ export default function HomePage() { tronAddress, tezosAddress, kadenaAddress, + bip122Address, smartAccountEnabled, chainAbstractionEnabled } = useSnapshot(SettingsStore.state) @@ -132,6 +134,17 @@ export default function HomePage() { data-testid={'chain-card-' + caip10.toString()} /> ))} + {Object.entries(BIP122_CHAINS).map(([caip10, { name, logo, rgb }]) => ( + + ))} {testNets ? ( diff --git a/advanced/wallets/react-wallet-v2/src/store/ModalStore.ts b/advanced/wallets/react-wallet-v2/src/store/ModalStore.ts index 909c34a35..3b0b6bed6 100644 --- a/advanced/wallets/react-wallet-v2/src/store/ModalStore.ts +++ b/advanced/wallets/react-wallet-v2/src/store/ModalStore.ts @@ -33,6 +33,9 @@ interface State { | 'SessionSignKadenaModal' | 'SessionAuthenticateModal' | 'LoadingModal' + | 'SessionSignBip122Modal' + | 'SessionGetBip122AddressesModal' + | 'SessionSendTransactionBip122Modal' data?: ModalData } diff --git a/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts b/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts index 6d70cbb06..706cc9fe8 100644 --- a/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts +++ b/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts @@ -31,6 +31,7 @@ interface State { tronAddress: string tezosAddress: string kadenaAddress: string + bip122Address: string kernelSmartAccountAddress: string safeSmartAccountAddress: string biconomySmartAccountAddress: string @@ -66,6 +67,7 @@ const state = proxy({ tronAddress: '', tezosAddress: '', kadenaAddress: '', + bip122Address: '', kernelSmartAccountAddress: '', safeSmartAccountAddress: '', biconomySmartAccountAddress: '', @@ -127,6 +129,9 @@ const SettingsStore = { setKadenaAddress(kadenaAddress: string) { state.kadenaAddress = kadenaAddress }, + setbip122Address(bip122Address: string) { + state.bip122Address = bip122Address + }, setRelayerRegionURL(relayerRegionURL: string) { state.relayerRegionURL = relayerRegionURL }, diff --git a/advanced/wallets/react-wallet-v2/src/utils/Bip122RequestHandlerUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/Bip122RequestHandlerUtil.ts new file mode 100644 index 000000000..1323b35dd --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/utils/Bip122RequestHandlerUtil.ts @@ -0,0 +1,92 @@ +import { KADENA_SIGNING_METHODS } from '@/data/KadenaData' +import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils' +import { SignClientTypes } from '@walletconnect/types' +import { getSdkError } from '@walletconnect/utils' +import { getWalletAddressFromParams } from './HelperUtil' +import { BIP122_SIGNING_METHODS, IBip122ChainId } from '@/data/Bip122Data' +import { bip122Addresses, bip122Wallet } from './Bip122WalletUtil' + +export async function approveBip122Request( + requestEvent: SignClientTypes.EventArguments['session_request'] +) { + const { params, id } = requestEvent + const { request } = params + const chainId = params.chainId as IBip122ChainId + const account = request.params.account + const wallet = bip122Wallet + console.log('wallet:', wallet, bip122Wallet) + console.log('account:', account) + console.log('request:', request.method) + switch (request.method) { + case BIP122_SIGNING_METHODS.BIP122_SIGN_MESSAGE: + const message = request.params.message + const address = request.params.address + const protocol = request.params.protocol + console.log( + 'signing message:', + message, + 'with address:', + address || account, + 'chainId:', + params + ) + const signature = await wallet.signMessage({ + message, + address: address || account, + protocol, + chainId + }) + return formatJsonRpcResult(id, signature) + case BIP122_SIGNING_METHODS.BIP122_SEND_TRANSACTION: + const transactionParams = request.params + console.log('signing transaction:', transactionParams, 'with account:', account) + const txid = await wallet.sendTransfer({ + account: transactionParams.account, + recipientAddress: transactionParams.recipientAddress, + amount: transactionParams.amount, + changeAddress: transactionParams.changeAddress, + memo: transactionParams.memo, + chainId + }) + console.log('signed transaction:', txid) + return formatJsonRpcResult(id, { txid }) + case BIP122_SIGNING_METHODS.BIP122_GET_ACCOUNT_ADDRESSES: + console.log('getting addresses for account:', account) + const addresses = wallet.getAddresses(chainId) + return formatJsonRpcResult(id, Array.from(addresses.values())) + case BIP122_SIGNING_METHODS.BIP122_SIGN_PSBT: + const psbt = request.params.psbt + const signInputs = request.params.signInputs + const broadcast = request.params.broadcast + console.log( + 'signing psbt:', + psbt, + 'with account:', + account, + 'inputs:', + signInputs, + 'broadcast:', + broadcast + ) + const result = await wallet.signPsbt({ + account, + psbt, + signInputs, + broadcast, + chainId + }) + console.log('signed psbt:', result) + return formatJsonRpcResult(id, result) + default: + throw new Error(getSdkError('UNSUPPORTED_METHODS').message) + } +} + +export function rejectBip122Request( + request: SignClientTypes.EventArguments['session_request'], + message?: string +) { + const { id } = request + + return formatJsonRpcError(id, message || getSdkError('USER_REJECTED_METHODS').message) +} diff --git a/advanced/wallets/react-wallet-v2/src/utils/Bip122WalletUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/Bip122WalletUtil.ts new file mode 100644 index 000000000..ee1121589 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/utils/Bip122WalletUtil.ts @@ -0,0 +1,41 @@ +import { BIP122_MAINNET_CAIP2, BIP122_TESTNET_CAIP2 } from '@/data/Bip122Data' +import BitcoinLib from '@/lib/Bip122Lib' + +export let wallet1: BitcoinLib +export let wallet2: BitcoinLib +export let bip122Wallet: BitcoinLib +export let bip122Addresses: string[] + +/** + * Utilities + */ +export async function createOrRestoreBip122Wallet() { + const privateKey1 = localStorage.getItem('BITCOIN_PRIVATE_KEY_1') + + if (privateKey1) { + wallet1 = await BitcoinLib.init({ privateKey: privateKey1 }) + // wallet2 = await BitcoinLib.init({ privateKey: privateKey2 }) + } else { + wallet1 = await BitcoinLib.init({}) + // Don't store private keys in local storage in a production project! + localStorage.setItem('BITCOIN_PRIVATE_KEY_1', wallet1.getPrivateKey()) + console.log('BITCOIN_PRIVATE_KEY_1', wallet1.getPrivateKey()) + } + + const mainnetAddress = wallet1.getAddress(BIP122_MAINNET_CAIP2) + + console.log('address1', { mainnetAddress, privateKey1 }, mainnetAddress) + + bip122Wallet = wallet1 + bip122Addresses = [ + `${BIP122_MAINNET_CAIP2}:${wallet1.getAddress(BIP122_MAINNET_CAIP2)}`, + `${BIP122_MAINNET_CAIP2}:${wallet1.getOrdinalsAddress(BIP122_MAINNET_CAIP2)}`, + `${BIP122_TESTNET_CAIP2}:${wallet1.getAddress(BIP122_TESTNET_CAIP2)}`, + `${BIP122_TESTNET_CAIP2}:${wallet1.getOrdinalsAddress(BIP122_TESTNET_CAIP2)}` + ] + + return { + bip122Wallet, + bip122Addresses + } +} diff --git a/advanced/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts index 2f053cb38..539866e5e 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/WalletConnectUtil.ts @@ -6,7 +6,7 @@ export async function createWalletKit(relayerRegionURL: string) { const core = new Core({ projectId: process.env.NEXT_PUBLIC_PROJECT_ID, relayUrl: relayerRegionURL ?? process.env.NEXT_PUBLIC_RELAY_URL, - logger: 'trace' + logger: 'error' }) walletkit = await WalletKit.init({ core, @@ -15,6 +15,9 @@ export async function createWalletKit(relayerRegionURL: string) { description: 'React Wallet for WalletConnect', url: 'https://walletconnect.com/', icons: ['https://avatars.githubusercontent.com/u/37784886'] + }, + signConfig: { + disableRequestQueue: true } }) diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionGetBip122AddressesModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionGetBip122AddressesModal.tsx new file mode 100644 index 000000000..13cf14958 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/views/SessionGetBip122AddressesModal.tsx @@ -0,0 +1,92 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { Col, Divider, Row, Text } from '@nextui-org/react' + +import RequestDataCard from '@/components/RequestDataCard' +import ModalStore from '@/store/ModalStore' +import { styledToast } from '@/utils/HelperUtil' +import { walletkit } from '@/utils/WalletConnectUtil' +import RequestModal from '../components/RequestModal' +import { useCallback, useState } from 'react' +import { approveBip122Request, rejectBip122Request } from '@/utils/Bip122RequestHandlerUtil' +import { bip122Wallet } from '@/utils/Bip122WalletUtil' + +export default function SessionGetBip122AddressesModal() { + // Get request and wallet data from store + const requestEvent = ModalStore.state.data?.requestEvent + const requestSession = ModalStore.state.data?.requestSession + const [isLoadingApprove, setIsLoadingApprove] = useState(false) + const [isLoadingReject, setIsLoadingReject] = useState(false) + + // Ensure request and wallet are defined + if (!requestEvent || !requestSession) { + return Missing request data + } + + const { topic, params } = requestEvent + const { request, chainId } = params + const account = request.params.account + const intentions = request.params.intentions + const addresses = bip122Wallet.getAddresses(intentions) + // Handle approve action (logic varies based on request method) + const onApprove = useCallback(async () => { + if (requestEvent) { + const response = await approveBip122Request(requestEvent) + try { + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingApprove(false) + styledToast((e as Error).message, 'error') + return + } + setIsLoadingApprove(false) + ModalStore.close() + } + }, [requestEvent, topic]) + + // Handle reject action + const onReject = useCallback(async () => { + if (requestEvent) { + setIsLoadingReject(true) + const response = rejectBip122Request(requestEvent) + try { + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingReject(false) + styledToast((e as Error).message, 'error') + return + } + setIsLoadingReject(false) + ModalStore.close() + } + }, [requestEvent, topic]) + + return ( + + {account && ( + <> + + + Addresses for account + {account} + + + + + )} + + + ) +} diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx index 0686dda94..837b5e98d 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx @@ -36,6 +36,13 @@ import usePriorityAccounts from '@/hooks/usePriorityAccounts' import useSmartAccounts from '@/hooks/useSmartAccounts' import { EIP5792_METHODS } from '@/data/EIP5792Data' import { getWalletCapabilities } from '@/utils/EIP5792WalletUtil' +import { bip122Addresses, bip122Wallet } from '@/utils/Bip122WalletUtil' +import { + BIP122_CHAINS, + BIP122_EVENTS, + BIP122_SIGNING_METHODS, + IBip122ChainId +} from '@/data/Bip122Data' import { EIP7715_METHOD } from '@/data/EIP7715Data' import { useRouter } from 'next/router' @@ -105,6 +112,11 @@ export default function SessionProposalModal() { const tronChains = Object.keys(TRON_CHAINS) const tronMethods = Object.values(TRON_SIGNING_METHODS) + // bip122 + const bip122Chains = Object.keys(BIP122_CHAINS) + const bip122Methods = Object.values(BIP122_SIGNING_METHODS) + const bip122Events = Object.values(BIP122_EVENTS) + return { eip155: { chains: eip155Chains, @@ -181,9 +193,15 @@ export default function SessionProposalModal() { accounts: tronChains .map(chain => tronAddresses.map(address => `${chain}:${address}`)) .flat() + }, + bip122: { + chains: bip122Chains, + methods: bip122Methods, + events: bip122Events, + accounts: bip122Addresses } } - }, []) + }, [addressesToApprove]) console.log('supportedNamespaces', supportedNamespaces, eip155Addresses) const requestedChains = useMemo(() => { @@ -238,6 +256,7 @@ export default function SessionProposalModal() { }, [proposal, supportedChains]) console.log('notSupportedChains', { notSupportedChains, supportedChains }) const getAddress = useCallback((namespace?: string) => { + console.log('getAddress', namespace) if (!namespace) return 'N/A' switch (namespace) { case 'eip155': @@ -258,6 +277,8 @@ export default function SessionProposalModal() { return tezosAddresses[0] case 'tron': return tronAddresses[0] + case 'bip122': + return bip122Addresses[0] } }, []) @@ -268,7 +289,9 @@ export default function SessionProposalModal() { proposal: proposal.params, supportedNamespaces }) - } catch (e) {} + } catch (e) { + console.error('Error building approved namespaces', e) + } }, [proposal.params, supportedNamespaces]) const reorderedEip155Accounts = usePriorityAccounts({ namespaces }) @@ -276,6 +299,7 @@ export default function SessionProposalModal() { // Hanlde approve action, construct session namespace const onApprove = useCallback(async () => { + console.log('onApprove', { proposal, namespaces }) try { if (proposal && namespaces) { setIsLoadingApprove(true) @@ -285,8 +309,19 @@ export default function SessionProposalModal() { } //get capabilities for all reorderedEip155Accounts in wallet const capabilities = getWalletCapabilities(reorderedEip155Accounts) - const sessionProperties = { capabilities: JSON.stringify(capabilities) } - + let sessionProperties = { + capabilities: JSON.stringify(capabilities) + } as any + if (namespaces.bip122) { + const bip122Chain = namespaces.bip122.chains?.[0]! + sessionProperties.bip122_getAccountAddresses = JSON.stringify({ + payment: Array.from(bip122Wallet.getAddresses(bip122Chain as IBip122ChainId).values()), + ordinal: Array.from( + bip122Wallet.getAddresses(bip122Chain as IBip122ChainId, ['ordinal']).values() + ) + }) + } + console.log('sessionProperties', sessionProperties) await walletkit.approveSession({ id: proposal.id, namespaces, @@ -323,6 +358,7 @@ export default function SessionProposalModal() { ModalStore.close() }, [proposal]) console.log('notSupportedChains', notSupportedChains) + console.log('supportedChains', supportedChains) return ( {(supportedChains.length > 0 && supportedChains.map((chain, i) => { + console.log('chain', chain) if (!chain) { return <> } diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionSendTransactionBip122Modal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionSendTransactionBip122Modal.tsx new file mode 100644 index 000000000..19d41b83f --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/views/SessionSendTransactionBip122Modal.tsx @@ -0,0 +1,88 @@ +import { useCallback, useState } from 'react' +import { Divider, Text } from '@nextui-org/react' + +import RequestDataCard from '@/components/RequestDataCard' +import RequesDetailsCard from '@/components/RequestDetalilsCard' +import RequestMethodCard from '@/components/RequestMethodCard' +import ModalStore from '@/store/ModalStore' +import { styledToast } from '@/utils/HelperUtil' +import { walletkit } from '@/utils/WalletConnectUtil' +import RequestModal from '../components/RequestModal' +import { approveBip122Request, rejectBip122Request } from '@/utils/Bip122RequestHandlerUtil' +import { JsonRpcResponse } from '@json-rpc-tools/utils' + +export default function SessionSendTransactionBip122Modal() { + const [isLoadingApprove, setIsLoadingApprove] = useState(false) + const [isLoadingReject, setIsLoadingReject] = useState(false) + + // Get request and wallet data from store + const requestEvent = ModalStore.state.data?.requestEvent + const requestSession = ModalStore.state.data?.requestSession + + const topic = requestEvent?.topic + const params = requestEvent?.params + const chainId = params?.chainId + const request = params?.request + const transaction = request?.params + + // Handle reject action + const onReject = useCallback( + async (rejection?: JsonRpcResponse) => { + if (requestEvent && topic) { + setIsLoadingReject(true) + const response = rejection || rejectBip122Request(requestEvent) + try { + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingReject(false) + styledToast((e as Error).message, 'error') + return + } + setIsLoadingReject(false) + ModalStore.close() + } + }, + [requestEvent, topic] + ) + // Handle approve action + const onApprove = useCallback(async () => { + if (requestEvent && topic) { + setIsLoadingApprove(true) + try { + const response = await approveBip122Request(requestEvent) + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingApprove(false) + styledToast((e as Error).message, 'error') + onReject(rejectBip122Request(requestEvent, (e as Error).message)) + } + setIsLoadingApprove(false) + ModalStore.close() + } + }, [onReject, requestEvent, topic]) + + return request && requestSession ? ( + + + + + + + + ) : ( + Request not found + ) +} diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionSignBip122Modal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionSignBip122Modal.tsx new file mode 100644 index 000000000..9cb899b46 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/views/SessionSignBip122Modal.tsx @@ -0,0 +1,98 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { Col, Divider, Row, Text } from '@nextui-org/react' + +import RequestDataCard from '@/components/RequestDataCard' +import RequestDetailsCard from '@/components/RequestDetalilsCard' +import ModalStore from '@/store/ModalStore' +import { styledToast } from '@/utils/HelperUtil' +import { walletkit } from '@/utils/WalletConnectUtil' +import RequestModal from '../components/RequestModal' +import { useCallback, useState } from 'react' +import { approveBip122Request, rejectBip122Request } from '@/utils/Bip122RequestHandlerUtil' + +export default function SessionSignBip122Modal() { + // Get request and wallet data from store + const requestEvent = ModalStore.state.data?.requestEvent + const requestSession = ModalStore.state.data?.requestSession + const [isLoadingApprove, setIsLoadingApprove] = useState(false) + const [isLoadingReject, setIsLoadingReject] = useState(false) + + // Ensure request and wallet are defined + if (!requestEvent || !requestSession) { + return Missing request data + } + + const { topic, params } = requestEvent + const { request, chainId } = params + const message = request.params.message + const account = request.params.account + const address = request.params.address + // Handle approve action (logic varies based on request method) + const onApprove = useCallback(async () => { + if (requestEvent) { + const response = await approveBip122Request(requestEvent) + try { + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingApprove(false) + styledToast((e as Error).message, 'error') + return + } + setIsLoadingApprove(false) + ModalStore.close() + } + }, [requestEvent, topic]) + + // Handle reject action + const onReject = useCallback(async () => { + if (requestEvent) { + setIsLoadingReject(true) + const response = rejectBip122Request(requestEvent) + try { + await walletkit.respondSessionRequest({ + topic, + response + }) + } catch (e) { + setIsLoadingReject(false) + styledToast((e as Error).message, 'error') + return + } + setIsLoadingReject(false) + ModalStore.close() + } + }, [requestEvent, topic]) + + return ( + + + {message && ( + <> + + + Message + {message} + + + + + + To sign with address + {address || account} + + + + )} + + ) +} diff --git a/advanced/wallets/react-wallet-v2/yarn.lock b/advanced/wallets/react-wallet-v2/yarn.lock index a8951305c..65c44d161 100644 --- a/advanced/wallets/react-wallet-v2/yarn.lock +++ b/advanced/wallets/react-wallet-v2/yarn.lock @@ -87,6 +87,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.25.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.6.2": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" @@ -1266,7 +1273,7 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@1.6.0", "@noble/curves@~1.6.0": +"@noble/curves@1.6.0", "@noble/curves@^1.4.2", "@noble/curves@^1.6.0", "@noble/curves@~1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b" integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ== @@ -2255,6 +2262,11 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@^1.1.1", "@scure/base@~1.1.7", "@scure/base@~1.1.8": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== + "@scure/base@~1.1.4": version "1.1.5" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" @@ -2265,11 +2277,6 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== -"@scure/base@~1.1.7", "@scure/base@~1.1.8": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" - integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== - "@scure/bip32@1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" @@ -2349,6 +2356,27 @@ rpc-websockets "^7.5.1" superstruct "^0.14.2" +"@solana/web3.js@^1.66.2": + version "1.95.4" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.95.4.tgz#771603f60d75cf7556ad867e1fd2efae32f9ad09" + integrity sha512-sdewnNEA42ZSMxqkzdwEWi6fDgzwtJHaQa5ndUGEJYtoOnM6X5cvPmjoTUp7/k7bRrVAxfBgDnvQQHD6yhlLYw== + dependencies: + "@babel/runtime" "^7.25.0" + "@noble/curves" "^1.4.2" + "@noble/hashes" "^1.4.0" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.1" + node-fetch "^2.7.0" + rpc-websockets "^9.0.2" + superstruct "^2.0.2" + "@stablelib/aead@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" @@ -2548,6 +2576,13 @@ dependencies: tslib "^2.4.0" +"@swc/helpers@^0.5.11": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.13.tgz#33e63ff3cd0cade557672bd7888a39ce7d115a8c" + integrity sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w== + dependencies: + tslib "^2.4.0" + "@taquito/http-utils@^15.1.0": version "15.1.0" resolved "https://registry.yarnpkg.com/@taquito/http-utils/-/http-utils-15.1.0.tgz#66f3ce220c483e33d6b31bca6e0c76b5b895ed9b" @@ -2773,6 +2808,11 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -2780,6 +2820,13 @@ dependencies: "@types/node" "*" +"@types/ws@^8.2.2": + version "8.5.12" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== + dependencies: + "@types/node" "*" + "@typescript-eslint/parser@^5.21.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" @@ -3416,6 +3463,11 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + base-x@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" @@ -3426,7 +3478,7 @@ base64-js@^1.3.0, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bech32@1.1.4, bech32@^1.1.4: +bech32@1.1.4, bech32@^1.1.3, bech32@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== @@ -3458,13 +3510,28 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.3.0: +bindings@^1.3.0, bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" +bip174@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.1.tgz#ef3e968cf76de234a546962bcf572cc150982f9f" + integrity sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ== + +bip32@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/bip32/-/bip32-4.0.0.tgz#7fac3c05072188d2d355a4d6596b37188f06aa2f" + integrity sha512-aOGy88DDlVUhspIXJN+dVEtclhIsfAUppD43V0j40cPTld3pv/0X/MlrZSZ6jowIaQQzFwP8M6rFU2z2mVYjDQ== + dependencies: + "@noble/hashes" "^1.2.0" + "@scure/base" "^1.1.1" + typeforce "^1.11.5" + wif "^2.0.6" + bip39@3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.2.tgz#2baf42ff3071fc9ddd5103de92e8f80d9257ee32" @@ -3482,6 +3549,37 @@ bip39@^3.0.2, bip39@^3.0.4: dependencies: "@noble/hashes" "^1.2.0" +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== + dependencies: + safe-buffer "^5.0.1" + +bitcoinjs-lib@^6.1.5: + version "6.1.6" + resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.6.tgz#f57c17c82511f860f11946d784c18da39f8618a8" + integrity sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA== + dependencies: + "@noble/hashes" "^1.2.0" + bech32 "^2.0.0" + bip174 "^2.1.1" + bs58check "^3.0.1" + typeforce "^1.11.3" + varuint-bitcoin "^1.1.2" + +bitcoinjs-message@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bitcoinjs-message/-/bitcoinjs-message-2.2.0.tgz#8116ec7f447f9889e23030fc15c5286a6ae5503b" + integrity sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA== + dependencies: + bech32 "^1.1.3" + bs58check "^2.1.2" + buffer-equals "^1.0.3" + create-hash "^1.1.2" + secp256k1 "^3.0.1" + varuint-bitcoin "^1.0.1" + blake2b-wasm@^1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz#e4d075da10068e5d4c3ec1fb9accc4d186c55d81" @@ -3556,6 +3654,18 @@ brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== +browserify-aes@^1.0.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + bs58@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8" @@ -3570,7 +3680,14 @@ bs58@^4.0.0, bs58@^4.0.1: dependencies: base-x "^3.0.2" -bs58check@^2.1.2: +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58check@<3.0.0, bs58check@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== @@ -3579,11 +3696,29 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +bs58check@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-3.0.1.tgz#2094d13720a28593de1cba1d8c4e48602fdd841c" + integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^5.0.0" + +buffer-equals@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/buffer-equals/-/buffer-equals-1.0.4.tgz#0353b54fd07fd9564170671ae6f66b9cf10d27f5" + integrity sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA== + buffer-reverse@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3680,7 +3815,7 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -cipher-base@^1.0.1, cipher-base@^1.0.3: +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== @@ -3818,7 +3953,7 @@ cosmos-wallet@1.2.0: "@cosmjs/amino" "^0.25.4" "@cosmjs/proto-signing" "^0.25.4" -create-hash@^1.1.0, create-hash@^1.1.2: +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== @@ -4055,6 +4190,15 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g== + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + duplexify@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" @@ -4065,6 +4209,15 @@ duplexify@^4.1.2: readable-stream "^3.1.1" stream-shift "^1.0.0" +ecpair@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ecpair/-/ecpair-2.1.0.tgz#673f826b1d80d5eb091b8e2010c6b588e8d2cb45" + integrity sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw== + dependencies: + randombytes "^2.1.0" + typeforce "^1.18.0" + wif "^2.0.6" + ed25519-hd-key@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ed25519-hd-key/-/ed25519-hd-key-1.1.2.tgz#168dcf08419694be7bba3319e7d64e4a5cfe5d44" @@ -4559,11 +4712,24 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@3.3.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -5432,6 +5598,24 @@ jayson@^4.1.0: uuid "^8.3.2" ws "^7.4.5" +jayson@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.2.tgz#443c26a8658703e0b2e881117b09395d88b6982e" + integrity sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.5.10" + jiti@^1.20.0: version "1.21.0" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" @@ -5925,6 +6109,11 @@ mustache@^4.0.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== +nan@^2.14.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" + integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== + nanoassert@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d" @@ -6722,6 +6911,22 @@ rpc-websockets@^7.5.1: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +rpc-websockets@^9.0.2: + version "9.0.4" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.0.4.tgz#9d8ee82533b5d1e13d9ded729e3e38d0d8fa083f" + integrity sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ== + dependencies: + "@swc/helpers" "^0.5.11" + "@types/uuid" "^8.3.4" + "@types/ws" "^8.2.2" + buffer "^6.0.3" + eventemitter3 "^5.0.1" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -6753,7 +6958,7 @@ safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6790,6 +6995,20 @@ scryptsy@2.1.0: resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== +secp256k1@^3.0.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.1.tgz#b62a62a882d6b16f9b51fe599c6b3a861e36c59f" + integrity sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.7" + nan "^2.14.0" + safe-buffer "^5.1.2" + semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -6892,16 +7111,20 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -solady@^0.0.234: - version "0.0.234" - resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.234.tgz#99ddd59e38f1987683b465e5f97d6808c2c50a32" - integrity sha512-twY/0NtBbOZ7fGFIqp7Ebuvfgw2yD6A44HKbEpS05RK03VH+bv81/Mg6kYI/7EK/NuATlvrACDdLYfp/c1+d4A== - solady@^0.0.235: version "0.0.235" resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.235.tgz#50ab6e403ed6935012df2c16803fcd58045f9a0e" integrity sha512-JUEXLDG7ag3HmqUnrDG7ilhafH6R9bFPpwV63O2kH4UbnS2+gRGEOqqy4k01O7tHjo3MWkDD0cpG+UY9pjy/fQ== +solana-wallet@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/solana-wallet/-/solana-wallet-1.0.2.tgz#1b8157974a0ec5228ac45d8414c656dcb3a94d47" + integrity sha512-oZnLJvwBFnQ0Hf0vTuAUFizq59AhxDfoMpdDUuqo02seNsV7AbYl3QGJZBJ1uCr36cRJnXFr2NqI3RM2IDq62Q== + dependencies: + "@solana/web3.js" "^1.66.2" + bs58 "^5.0.0" + tweetnacl "^1.0.3" + sonic-boom@^2.2.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" @@ -7111,6 +7334,11 @@ superstruct@^0.14.2: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== +superstruct@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54" + integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -7152,6 +7380,13 @@ thread-stream@^0.15.1: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-secp256k1@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-2.2.3.tgz#fe1dde11a64fcee2091157d4b78bcb300feb9b65" + integrity sha512-SGcL07SxcPN2nGKHTCvRMkQLYPSoeFcvArUSCYtjVARiFAWU44cCIqYS0mYAU6nY7XfvwURuTIGo2Omt3ZQr0Q== + dependencies: + uint8array-tools "0.0.7" + tiny-warning@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" @@ -7298,6 +7533,11 @@ typedarray-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a" integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ== +typeforce@^1.11.3, typeforce@^1.11.5, typeforce@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + typescript@5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" @@ -7313,6 +7553,11 @@ ufo@^1.3.0, ufo@^1.3.1, ufo@^1.3.2: resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.2.tgz#c7d719d0628a1c80c006d2240e0d169f6e3c0496" integrity sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA== +uint8array-tools@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/uint8array-tools/-/uint8array-tools-0.0.7.tgz#a7a2bb5d8836eae2fade68c771454e6a438b390d" + integrity sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ== + uint8arrays@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.0.tgz#8186b8eafce68f28bd29bd29d683a311778901e2" @@ -7455,6 +7700,13 @@ valtio@1.13.2: proxy-compare "2.6.0" use-sync-external-store "1.2.0" +varuint-bitcoin@^1.0.1, varuint-bitcoin@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz#e76c138249d06138b480d4c5b40ef53693e24e92" + integrity sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw== + dependencies: + safe-buffer "^5.1.1" + viem@2.17.8: version "2.17.8" resolved "https://registry.yarnpkg.com/viem/-/viem-2.17.8.tgz#79da50ef86fb429d3b36d4ef2f49be5b2999420f" @@ -7589,6 +7841,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wif@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" + integrity sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ== + dependencies: + bs58check "<3.0.0" + wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" @@ -7628,6 +7887,11 @@ ws@^7.4.5, ws@^7.5.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + ws@^8.5.0: version "8.15.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997"