diff --git a/packages/transactions/.eslintrc.js b/packages/transactions/.eslintrc.js new file mode 100644 index 00000000..9b81937b --- /dev/null +++ b/packages/transactions/.eslintrc.js @@ -0,0 +1,17 @@ +/* eslint-env node */ + +// @ts-check + +const { defineConfig } = require("eslint-define-config"); + +module.exports = defineConfig({ + extends: ["../../.eslintrc.js"], + overrides: [ + { + files: ["tsup.config.ts"], + rules: { + "import/no-extraneous-dependencies": "off", + }, + }, + ], +}); \ No newline at end of file diff --git a/packages/transactions/README.md b/packages/transactions/README.md new file mode 100644 index 00000000..be1e669e --- /dev/null +++ b/packages/transactions/README.md @@ -0,0 +1,353 @@ +# @phantom/transactions + +A multi-chain transaction builder package that simplifies transaction creation for dApp development with Phantom Wallet SDK. Build transactions for Solana, Ethereum, and other blockchains with a unified API. + +## Installation + +```bash +npm install @phantom/transactions +# or +yarn add @phantom/transactions +``` + +## Overview + +The `@phantom/transactions` package provides a simple, unified interface for creating transactions across multiple blockchains. It automatically handles chain-specific logic while providing a consistent developer experience. + +### Supported Networks + +- **Solana**: Native SOL transfers and SPL tokens +- **Ethereum/EVM**: Native ETH transfers and ERC-20 tokens + - Ethereum, Polygon, Arbitrum, Optimism, Base, BSC, Avalanche +- **Bitcoin**: Native BTC transfers (coming soon) +- **Sui**: Native SUI transfers and tokens (coming soon) + +## Quick Start + +```typescript +import { createSendTokenTransaction } from "@phantom/transactions"; +import { useSignAndSendTransaction, NetworkId } from "@phantom/react-sdk"; + +function SendToken() { + const { signAndSendTransaction } = useSignAndSendTransaction(); + + const handleSend = async () => { + // Create transaction + const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.SOLANA_MAINNET, + from: "sender-address", + to: "recipient-address", + amount: "1.5", // 1.5 SOL + }); + + if (error) { + console.error("Transaction creation failed:", error); + return; + } + + // Sign and send with Phantom SDK + const signature = await signAndSendTransaction({ + transaction, + networkId: NetworkId.SOLANA_MAINNET, + }); + }; + + return ; +} +``` + +## API Reference + +### createSendTokenTransaction(params) + +Creates a transaction for sending tokens on any supported blockchain. + +**Parameters:** +- `networkId` (NetworkId) - The target network identifier +- `from` (string) - Sender address +- `to` (string) - Recipient address +- `amount` (string | number | bigint) - Amount to send +- `token?` (string) - Token contract address (optional for native transfers) +- `decimals?` (number) - Token decimals (auto-detected if not provided) + +**Returns:** +- `Promise` with: + - `transaction` - The blockchain-specific transaction object + - `error?` - Error message if transaction creation failed + +## Usage Examples + +### Native Token Transfers + +```typescript +import { createSendTokenTransaction, NetworkId } from "@phantom/transactions"; + +// Send SOL on Solana +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.SOLANA_MAINNET, + from: "sender-solana-address", + to: "recipient-solana-address", + amount: "0.1", // 0.1 SOL +}); + +// Send ETH on Ethereum +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.ETHEREUM_MAINNET, + from: "0x...", // sender address + to: "0x...", // recipient address + amount: "0.05", // 0.05 ETH +}); + +// Send MATIC on Polygon +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.POLYGON_MAINNET, + from: "0x...", + to: "0x...", + amount: "10", // 10 MATIC +}); +``` + +### Token Transfers + +```typescript +// Send USDC on Solana (SPL Token) +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.SOLANA_MAINNET, + from: "sender-address", + to: "recipient-address", + amount: "100", // 100 USDC + token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint address + decimals: 6, // USDC has 6 decimals +}); + +// Send USDC on Ethereum (ERC-20) +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.ETHEREUM_MAINNET, + from: "0x...", + to: "0x...", + amount: "50", + token: "0xA0b86a33E6441E52bB62c678b9431B57e4FB3b3A", // USDC contract address + decimals: 6, +}); +``` + +### Convenience Functions + +```typescript +import { createNativeTransfer, createTokenTransfer } from "@phantom/transactions"; + +// Native transfer (convenience function) +const { transaction } = await createNativeTransfer({ + networkId: NetworkId.SOLANA_DEVNET, + from: "sender", + to: "recipient", + amount: "2.0", +}); + +// Token transfer (convenience function) +const { transaction } = await createTokenTransfer({ + networkId: NetworkId.ETHEREUM_MAINNET, + from: "0x...", + to: "0x...", + amount: "1000", + token: "0x...", // Required for token transfers + decimals: 18, +}); +``` + +## RPC Configuration + +Configure custom RPC endpoints for different networks: + +```typescript +import { setRPCConfig, getRPCConfig } from "@phantom/transactions"; + +// Set custom RPC endpoints +setRPCConfig({ + solana: { + mainnet: "https://your-solana-rpc.com", + devnet: "https://your-solana-devnet.com", + }, + ethereum: { + mainnet: "https://your-ethereum-rpc.com", + sepolia: "https://your-sepolia-rpc.com", + }, + polygon: { + mainnet: "https://your-polygon-rpc.com", + }, +}); + +// Get current configuration +const config = getRPCConfig(); +console.log(config.solana?.mainnet); +``` + +### Environment Variables + +You can also configure RPC endpoints via environment variables: + +```bash +# Solana +SOLANA_MAINNET_RPC=https://api.mainnet-beta.solana.com +SOLANA_DEVNET_RPC=https://api.devnet.solana.com + +# Ethereum +ETHEREUM_MAINNET_RPC=https://mainnet.infura.io/v3/YOUR_KEY +ETHEREUM_SEPOLIA_RPC=https://sepolia.infura.io/v3/YOUR_KEY + +# Polygon +POLYGON_MAINNET_RPC=https://polygon-mainnet.infura.io/v3/YOUR_KEY + +# Other networks... +``` + +## Integration with Phantom SDKs + +### React SDK Integration + +```typescript +import { createSendTokenTransaction } from "@phantom/transactions"; +import { useSignAndSendTransaction, NetworkId } from "@phantom/react-sdk"; + +function TokenSender() { + const { signAndSendTransaction, loading } = useSignAndSendTransaction(); + + const sendUSDC = async () => { + const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.SOLANA_MAINNET, + from: "your-address", + to: "recipient-address", + amount: "10", + token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + }); + + if (error) { + alert(error); + return; + } + + try { + const signature = await signAndSendTransaction({ + transaction, + networkId: NetworkId.SOLANA_MAINNET, + }); + console.log("Transaction sent:", signature); + } catch (err) { + console.error("Failed to send:", err); + } + }; + + return ( + + ); +} +``` + +### Server SDK Integration + +```typescript +import { createSendTokenTransaction } from "@phantom/transactions"; +import { ServerSDK, NetworkId } from "@phantom/server-sdk"; + +const sdk = new ServerSDK({ + organizationId: process.env.ORGANIZATION_ID!, + apiPrivateKey: process.env.PRIVATE_KEY!, + apiBaseUrl: process.env.API_URL!, +}); + +async function serverSideTransfer(walletId: string) { + // Create transaction + const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.ETHEREUM_MAINNET, + from: "wallet-address", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: "0.1", // 0.1 ETH + }); + + if (error) { + throw new Error(error); + } + + // Sign and send with server SDK + const result = await sdk.signAndSendTransaction({ + walletId, + transaction, + networkId: NetworkId.ETHEREUM_MAINNET, + }); + + return result; +} +``` + +## Error Handling + +```typescript +const { transaction, error } = await createSendTokenTransaction({ + networkId: NetworkId.SOLANA_MAINNET, + from: "invalid-address", + to: "recipient", + amount: "1.0", +}); + +if (error) { + console.error("Transaction creation failed:", error); + // Handle specific error cases + if (error.includes("Invalid address")) { + // Handle invalid address + } else if (error.includes("Insufficient")) { + // Handle insufficient balance + } + return; +} + +// Transaction created successfully +console.log("Transaction ready:", transaction); +``` + +## Network Support + +| Network | Native Token | Custom Tokens | Status | +|---------|-------------|---------------|---------| +| Solana | ✅ SOL | 🚧 SPL Tokens | Partial | +| Ethereum | ✅ ETH | 🚧 ERC-20 | Partial | +| Polygon | ✅ MATIC | 🚧 ERC-20 | Partial | +| Arbitrum | ✅ ETH | 🚧 ERC-20 | Partial | +| Optimism | ✅ ETH | 🚧 ERC-20 | Partial | +| Base | ✅ ETH | 🚧 ERC-20 | Partial | +| BSC | ✅ BNB | 🚧 BEP-20 | Partial | +| Avalanche | ✅ AVAX | 🚧 ERC-20 | Partial | +| Bitcoin | 🚧 BTC | ❌ | Coming Soon | +| Sui | 🚧 SUI | 🚧 Sui Tokens | Coming Soon | + +## Development Status + +This package is in active development. Current limitations: + +- **SPL Token transfers**: Need @solana/spl-token implementation +- **ERC-20 transfers**: Need proper contract call encoding +- **Bitcoin**: Full implementation needed +- **Sui tokens**: Token-specific logic needed + +## Contributing + +This package is part of the Phantom Wallet SDK monorepo. Contributions are welcome! + +```bash +# Install dependencies +yarn install + +# Build the package +yarn workspace @phantom/transactions build + +# Run tests +yarn workspace @phantom/transactions test + +# Lint code +yarn workspace @phantom/transactions lint +``` + +## License + +MIT License - see LICENSE file for details. \ No newline at end of file diff --git a/packages/transactions/jest.config.js b/packages/transactions/jest.config.js new file mode 100644 index 00000000..6bde9e0b --- /dev/null +++ b/packages/transactions/jest.config.js @@ -0,0 +1,19 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/src'], + testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts', + '!src/**/*.test.ts', + ], + moduleNameMapping: { + '^@phantom/(.*)$': '/../$1/src', + }, + testTimeout: 10000, +}; \ No newline at end of file diff --git a/packages/transactions/package.json b/packages/transactions/package.json new file mode 100644 index 00000000..9bb964ee --- /dev/null +++ b/packages/transactions/package.json @@ -0,0 +1,64 @@ +{ + "name": "@phantom/transactions", + "version": "0.0.1", + "description": "Transaction builders for multi-chain dApp development with Phantom Wallet SDK", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "files": [ + "dist" + ], + "scripts": { + "?pack-release": "When https://github.com/changesets/changesets/issues/432 has a solution we can remove this trick", + "pack-release": "rimraf ./_release && yarn pack && mkdir ./_release && tar zxvf ./package.tgz --directory ./_release && rm ./package.tgz", + "build": "rimraf ./dist && tsup", + "dev": "rimraf ./dist && tsup --watch", + "clean": "rm -rf dist", + "test": "jest", + "test:watch": "jest --watch", + "lint": "tsc --noEmit && eslint --cache . --ext .ts", + "prettier": "prettier --write \"src/**/*.{ts}\"" + }, + "dependencies": { + "@mysten/sui.js": "^0.54.1", + "@phantom/client": "workspace:^", + "@solana/web3.js": "^1.87.6", + "bitcoinjs-lib": "^6.1.5", + "buffer": "^6.0.3", + "viem": "^2.21.1" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "^20.11.0", + "eslint": "8.53.0", + "jest": "^29.7.0", + "prettier": "^3.5.2", + "rimraf": "^6.0.1", + "ts-jest": "^29.1.2", + "tsup": "^6.7.0", + "typescript": "^5.0.4" + }, + "keywords": [ + "phantom", + "transactions", + "solana", + "ethereum", + "bitcoin", + "sui", + "web3", + "dapp", + "blockchain", + "multi-chain" + ], + "license": "MIT", + "publishConfig": { + "directory": "_release/package" + } +} diff --git a/packages/transactions/src/builders/bitcoin.ts b/packages/transactions/src/builders/bitcoin.ts new file mode 100644 index 00000000..70aa97ec --- /dev/null +++ b/packages/transactions/src/builders/bitcoin.ts @@ -0,0 +1,33 @@ +import type { BitcoinTransactionBuilder, SendTokenTransactionParams, TransactionResult } from "../types"; +import { getRPCUrl } from "../config"; + +export class BitcoinTransactionBuilderImpl implements BitcoinTransactionBuilder { + async createSendTokenTransaction(params: SendTokenTransactionParams): Promise { + try { + const rpcUrl = getRPCUrl(params.networkId); + + // This is a placeholder implementation + // In a real implementation, you would: + // 1. Use bitcoinjs-lib to create transactions + // 2. Fetch UTXOs from the Bitcoin network + // 3. Calculate fees + // 4. Create and sign the transaction + + // Bitcoin doesn't have native token support, but you could handle: + // - Native BTC transfers + // - Lightning Network payments + // - Ordinals/Inscriptions + // - BRC-20 tokens (via Ordinals) + + return { + transaction: null, + error: "Bitcoin transaction creation not yet implemented. Please add bitcoinjs-lib implementation.", + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create Bitcoin transaction: ${error.message}`, + }; + } + } +} \ No newline at end of file diff --git a/packages/transactions/src/builders/evm.ts b/packages/transactions/src/builders/evm.ts new file mode 100644 index 00000000..5a8c7ebe --- /dev/null +++ b/packages/transactions/src/builders/evm.ts @@ -0,0 +1,149 @@ +import { createPublicClient, http, parseEther, parseUnits, formatUnits, getContract } from "viem"; +import { mainnet, sepolia, polygon, polygonMumbai, arbitrum, arbitrumSepolia, optimism, optimismSepolia, base, baseSepolia, bsc, bscTestnet, avalanche, avalancheFuji } from "viem/chains"; +import type { EVMTransactionBuilder, SendTokenTransactionParams, TransactionResult } from "../types"; +import { getRPCUrl } from "../config"; + +// ERC-20 ABI for token transfers +const ERC20_ABI = [ + { + constant: true, + inputs: [], + name: "decimals", + outputs: [{ name: "", type: "uint8" }], + type: "function", + }, + { + constant: false, + inputs: [ + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ name: "", type: "bool" }], + type: "function", + }, +] as const; + +export class EVMTransactionBuilderImpl implements EVMTransactionBuilder { + private getChain(networkId: string) { + const [, network] = networkId.toLowerCase().split(':'); + + switch (network) { + case '1': return mainnet; + case '11155111': return sepolia; + case '5': return sepolia; // Goerli deprecated, fallback to Sepolia + case '137': return polygon; + case '80001': return polygonMumbai; + case '42161': return arbitrum; + case '421614': return arbitrumSepolia; + case '10': return optimism; + case '11155420': return optimismSepolia; + case '8453': return base; + case '84532': return baseSepolia; + case '56': return bsc; + case '97': return bscTestnet; + case '43114': return avalanche; + case '43113': return avalancheFuji; + default: return mainnet; + } + } + + private getPublicClient(networkId: string) { + const chain = this.getChain(networkId); + const rpcUrl = getRPCUrl(networkId); + + return createPublicClient({ + chain, + transport: http(rpcUrl), + }); + } + + async createSendTokenTransaction(params: SendTokenTransactionParams): Promise { + try { + const publicClient = this.getPublicClient(params.networkId); + + // Handle native token transfer (ETH, MATIC, etc.) + if (!params.token) { + return this.createNativeTransfer(params); + } + + // Handle ERC-20 token transfer + return await this.createERC20Transfer(publicClient, params); + } catch (error: any) { + return { + transaction: null, + error: `Failed to create EVM transaction: ${error.message}`, + }; + } + } + + private createNativeTransfer(params: SendTokenTransactionParams): TransactionResult { + try { + // Convert amount to wei + const value = typeof params.amount === 'string' + ? parseEther(params.amount) + : typeof params.amount === 'bigint' + ? params.amount + : parseEther(params.amount.toString()); + + const transaction = { + to: params.to as `0x${string}`, + value, + // Gas limit and gas price will be estimated by the wallet + }; + + return { + transaction, + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create native transfer: ${error.message}`, + }; + } + } + + private async createERC20Transfer(publicClient: any, params: SendTokenTransactionParams): Promise { + try { + const tokenContract = getContract({ + address: params.token as `0x${string}`, + abi: ERC20_ABI, + client: publicClient, + }); + + // Get token decimals if not provided + let decimals = params.decimals; + if (!decimals) { + try { + decimals = await tokenContract.read.decimals(); + } catch (error) { + decimals = 18; // Default to 18 decimals + } + } + + // Convert amount to token units + const value = typeof params.amount === 'string' + ? parseUnits(params.amount, decimals) + : typeof params.amount === 'bigint' + ? params.amount + : parseUnits(params.amount.toString(), decimals); + + // Create transfer transaction data + const transaction = { + to: params.token as `0x${string}`, + data: tokenContract.write.transfer.toString(), // This needs proper encoding + // Note: This is a simplified version. In practice, you'd need to properly encode the function call + }; + + return { + transaction: null, + error: "ERC-20 token transfers need proper function encoding. Please implement contract call encoding.", + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create ERC-20 transfer: ${error.message}`, + }; + } + } +} \ No newline at end of file diff --git a/packages/transactions/src/builders/solana.ts b/packages/transactions/src/builders/solana.ts new file mode 100644 index 00000000..d5c6406d --- /dev/null +++ b/packages/transactions/src/builders/solana.ts @@ -0,0 +1,97 @@ +import { Connection, PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; +import type { SolanaTransactionBuilder, SendTokenTransactionParams, TransactionResult } from "../types"; +import { getRPCUrl } from "../config"; + +export class SolanaTransactionBuilderImpl implements SolanaTransactionBuilder { + private getConnection(networkId: string): Connection { + const rpcUrl = getRPCUrl(networkId); + return new Connection(rpcUrl, 'confirmed'); + } + + async createSendTokenTransaction(params: SendTokenTransactionParams): Promise { + try { + const connection = this.getConnection(params.networkId); + const fromPubkey = new PublicKey(params.from); + const toPubkey = new PublicKey(params.to); + + // Handle native SOL transfer + if (!params.token) { + return this.createNativeSOLTransfer(connection, fromPubkey, toPubkey, params.amount); + } + + // Handle SPL token transfer + return this.createSPLTokenTransfer(connection, fromPubkey, toPubkey, params.token, params.amount, params.decimals); + } catch (error: any) { + return { + transaction: null, + error: `Failed to create Solana transaction: ${error.message}`, + }; + } + } + + private async createNativeSOLTransfer( + connection: Connection, + fromPubkey: PublicKey, + toPubkey: PublicKey, + amount: string | number | bigint + ): Promise { + try { + // Convert amount to lamports + const lamports = typeof amount === 'string' + ? Math.floor(parseFloat(amount) * LAMPORTS_PER_SOL) + : typeof amount === 'bigint' + ? Number(amount) + : Math.floor(amount * LAMPORTS_PER_SOL); + + const transaction = new Transaction().add( + SystemProgram.transfer({ + fromPubkey, + toPubkey, + lamports, + }) + ); + + // Get recent blockhash + const { blockhash } = await connection.getLatestBlockhash(); + transaction.recentBlockhash = blockhash; + transaction.feePayer = fromPubkey; + + return { + transaction, + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create native SOL transfer: ${error.message}`, + }; + } + } + + private async createSPLTokenTransfer( + connection: Connection, + fromPubkey: PublicKey, + toPubkey: PublicKey, + tokenAddress: string, + amount: string | number | bigint, + decimals?: number + ): Promise { + try { + // This is a placeholder for SPL token transfer + // In a real implementation, you would: + // 1. Import @solana/spl-token + // 2. Get or create associated token accounts + // 3. Create transfer instruction + // 4. Handle token decimals properly + + return { + transaction: null, + error: "SPL token transfers not yet implemented. Please add @solana/spl-token dependency and implement token transfer logic.", + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create SPL token transfer: ${error.message}`, + }; + } + } +} \ No newline at end of file diff --git a/packages/transactions/src/builders/sui.ts b/packages/transactions/src/builders/sui.ts new file mode 100644 index 00000000..29f35b44 --- /dev/null +++ b/packages/transactions/src/builders/sui.ts @@ -0,0 +1,76 @@ +import { SuiClient, getFullnodeUrl } from "@mysten/sui.js/client"; +import { TransactionBlock } from "@mysten/sui.js/transactions"; +import type { SuiTransactionBuilder, SendTokenTransactionParams, TransactionResult } from "../types"; +import { getRPCUrl } from "../config"; + +export class SuiTransactionBuilderImpl implements SuiTransactionBuilder { + private getSuiClient(networkId: string): SuiClient { + const rpcUrl = getRPCUrl(networkId); + return new SuiClient({ url: rpcUrl }); + } + + async createSendTokenTransaction(params: SendTokenTransactionParams): Promise { + try { + const client = this.getSuiClient(params.networkId); + + // Handle native SUI transfer + if (!params.token) { + return this.createNativeSuiTransfer(params); + } + + // Handle Sui token transfer + return this.createSuiTokenTransfer(client, params); + } catch (error: any) { + return { + transaction: null, + error: `Failed to create Sui transaction: ${error.message}`, + }; + } + } + + private createNativeSuiTransfer(params: SendTokenTransactionParams): TransactionResult { + try { + const tx = new TransactionBlock(); + + // Convert amount to MIST (1 SUI = 1,000,000,000 MIST) + const amount = typeof params.amount === 'string' + ? Math.floor(parseFloat(params.amount) * 1_000_000_000) + : typeof params.amount === 'bigint' + ? Number(params.amount) + : Math.floor(params.amount * 1_000_000_000); + + // Split coins and transfer + const [coin] = tx.splitCoins(tx.gas, [tx.pure(amount)]); + tx.transferObjects([coin], tx.pure(params.to)); + + return { + transaction: tx, + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create native SUI transfer: ${error.message}`, + }; + } + } + + private async createSuiTokenTransfer(client: SuiClient, params: SendTokenTransactionParams): Promise { + try { + // This is a placeholder for Sui token transfers + // In a real implementation, you would: + // 1. Query for coin objects of the specified type + // 2. Create appropriate transfer transactions + // 3. Handle token-specific logic + + return { + transaction: null, + error: "Sui token transfers not yet fully implemented. Please add token-specific logic.", + }; + } catch (error: any) { + return { + transaction: null, + error: `Failed to create Sui token transfer: ${error.message}`, + }; + } + } +} \ No newline at end of file diff --git a/packages/transactions/src/config.ts b/packages/transactions/src/config.ts new file mode 100644 index 00000000..06a8e9b8 --- /dev/null +++ b/packages/transactions/src/config.ts @@ -0,0 +1,128 @@ +import type { RPCConfig } from "./types"; + +// Safe environment variable access for isomorphic support +const getEnvVar = (name: string): string | undefined => { + if (typeof process !== 'undefined' && process.env) { + return process.env[name]; + } + return undefined; +}; + +// Default RPC endpoints - can be overridden via environment variables or config +const DEFAULT_RPC_CONFIG: RPCConfig = { + solana: { + mainnet: getEnvVar('SOLANA_MAINNET_RPC') || "https://api.mainnet-beta.solana.com", + devnet: getEnvVar('SOLANA_DEVNET_RPC') || "https://api.devnet.solana.com", + testnet: getEnvVar('SOLANA_TESTNET_RPC') || "https://api.testnet.solana.com", + }, + ethereum: { + mainnet: getEnvVar('ETHEREUM_MAINNET_RPC') || "https://mainnet.infura.io/v3/YOUR_INFURA_KEY", + sepolia: getEnvVar('ETHEREUM_SEPOLIA_RPC') || "https://sepolia.infura.io/v3/YOUR_INFURA_KEY", + goerli: getEnvVar('ETHEREUM_GOERLI_RPC') || "https://goerli.infura.io/v3/YOUR_INFURA_KEY", + }, + polygon: { + mainnet: getEnvVar('POLYGON_MAINNET_RPC') || "https://polygon-mainnet.infura.io/v3/YOUR_INFURA_KEY", + mumbai: getEnvVar('POLYGON_MUMBAI_RPC') || "https://polygon-mumbai.infura.io/v3/YOUR_INFURA_KEY", + }, + arbitrum: { + mainnet: getEnvVar('ARBITRUM_MAINNET_RPC') || "https://arbitrum-mainnet.infura.io/v3/YOUR_INFURA_KEY", + sepolia: getEnvVar('ARBITRUM_SEPOLIA_RPC') || "https://arbitrum-sepolia.infura.io/v3/YOUR_INFURA_KEY", + }, + optimism: { + mainnet: getEnvVar('OPTIMISM_MAINNET_RPC') || "https://optimism-mainnet.infura.io/v3/YOUR_INFURA_KEY", + sepolia: getEnvVar('OPTIMISM_SEPOLIA_RPC') || "https://optimism-sepolia.infura.io/v3/YOUR_INFURA_KEY", + }, + base: { + mainnet: getEnvVar('BASE_MAINNET_RPC') || "https://mainnet.base.org", + sepolia: getEnvVar('BASE_SEPOLIA_RPC') || "https://sepolia.base.org", + }, + bsc: { + mainnet: getEnvVar('BSC_MAINNET_RPC') || "https://bsc-dataseed.binance.org", + testnet: getEnvVar('BSC_TESTNET_RPC') || "https://data-seed-prebsc-1-s1.binance.org:8545", + }, + avalanche: { + mainnet: getEnvVar('AVALANCHE_MAINNET_RPC') || "https://api.avax.network/ext/bc/C/rpc", + fuji: getEnvVar('AVALANCHE_FUJI_RPC') || "https://api.avax-test.network/ext/bc/C/rpc", + }, + bitcoin: { + mainnet: getEnvVar('BITCOIN_MAINNET_RPC') || "https://blockstream.info/api", + testnet: getEnvVar('BITCOIN_TESTNET_RPC') || "https://blockstream.info/testnet/api", + }, + sui: { + mainnet: getEnvVar('SUI_MAINNET_RPC') || "https://fullnode.mainnet.sui.io:443", + testnet: getEnvVar('SUI_TESTNET_RPC') || "https://fullnode.testnet.sui.io:443", + devnet: getEnvVar('SUI_DEVNET_RPC') || "https://fullnode.devnet.sui.io:443", + }, +}; + +let currentConfig: RPCConfig = { ...DEFAULT_RPC_CONFIG }; + +export function getRPCConfig(): RPCConfig { + return currentConfig; +} + +export function setRPCConfig(config: Partial): void { + currentConfig = { + ...currentConfig, + ...config, + // Deep merge for nested objects + solana: { ...currentConfig.solana, ...config.solana }, + ethereum: { ...currentConfig.ethereum, ...config.ethereum }, + polygon: { ...currentConfig.polygon, ...config.polygon }, + arbitrum: { ...currentConfig.arbitrum, ...config.arbitrum }, + optimism: { ...currentConfig.optimism, ...config.optimism }, + base: { ...currentConfig.base, ...config.base }, + bsc: { ...currentConfig.bsc, ...config.bsc }, + avalanche: { ...currentConfig.avalanche, ...config.avalanche }, + bitcoin: { ...currentConfig.bitcoin, ...config.bitcoin }, + sui: { ...currentConfig.sui, ...config.sui }, + }; +} + +export function getRPCUrl(networkId: string): string { + const config = getRPCConfig(); + const [chain, network] = networkId.toLowerCase().split(':'); + + switch (chain) { + case 'solana': + return config.solana?.[network as keyof typeof config.solana] || config.solana?.mainnet || DEFAULT_RPC_CONFIG.solana!.mainnet!; + + case 'ethereum': + case 'eip155': + const ethNetwork = network === '1' ? 'mainnet' : network === '11155111' ? 'sepolia' : network === '5' ? 'goerli' : 'mainnet'; + return config.ethereum?.[ethNetwork as keyof typeof config.ethereum] || config.ethereum?.mainnet || DEFAULT_RPC_CONFIG.ethereum!.mainnet!; + + case 'polygon': + const polygonNetwork = network === '137' ? 'mainnet' : network === '80001' ? 'mumbai' : 'mainnet'; + return config.polygon?.[polygonNetwork as keyof typeof config.polygon] || config.polygon?.mainnet || DEFAULT_RPC_CONFIG.polygon!.mainnet!; + + case 'arbitrum': + const arbitrumNetwork = network === '42161' ? 'mainnet' : network === '421614' ? 'sepolia' : 'mainnet'; + return config.arbitrum?.[arbitrumNetwork as keyof typeof config.arbitrum] || config.arbitrum?.mainnet || DEFAULT_RPC_CONFIG.arbitrum!.mainnet!; + + case 'optimism': + const optimismNetwork = network === '10' ? 'mainnet' : network === '11155420' ? 'sepolia' : 'mainnet'; + return config.optimism?.[optimismNetwork as keyof typeof config.optimism] || config.optimism?.mainnet || DEFAULT_RPC_CONFIG.optimism!.mainnet!; + + case 'base': + const baseNetwork = network === '8453' ? 'mainnet' : network === '84532' ? 'sepolia' : 'mainnet'; + return config.base?.[baseNetwork as keyof typeof config.base] || config.base?.mainnet || DEFAULT_RPC_CONFIG.base!.mainnet!; + + case 'bsc': + const bscNetwork = network === '56' ? 'mainnet' : network === '97' ? 'testnet' : 'mainnet'; + return config.bsc?.[bscNetwork as keyof typeof config.bsc] || config.bsc?.mainnet || DEFAULT_RPC_CONFIG.bsc!.mainnet!; + + case 'avalanche': + const avalancheNetwork = network === '43114' ? 'mainnet' : network === '43113' ? 'fuji' : 'mainnet'; + return config.avalanche?.[avalancheNetwork as keyof typeof config.avalanche] || config.avalanche?.mainnet || DEFAULT_RPC_CONFIG.avalanche!.mainnet!; + + case 'bitcoin': + return config.bitcoin?.[network as keyof typeof config.bitcoin] || config.bitcoin?.mainnet || DEFAULT_RPC_CONFIG.bitcoin!.mainnet!; + + case 'sui': + return config.sui?.[network as keyof typeof config.sui] || config.sui?.mainnet || DEFAULT_RPC_CONFIG.sui!.mainnet!; + + default: + throw new Error(`Unsupported network: ${networkId}`); + } +} \ No newline at end of file diff --git a/packages/transactions/src/index.ts b/packages/transactions/src/index.ts new file mode 100644 index 00000000..2565838d --- /dev/null +++ b/packages/transactions/src/index.ts @@ -0,0 +1,105 @@ +import type { SendTokenTransactionParams, TransactionResult } from "./types"; +import { SolanaTransactionBuilderImpl } from "./builders/solana"; +import { EVMTransactionBuilderImpl } from "./builders/evm"; +import { BitcoinTransactionBuilderImpl } from "./builders/bitcoin"; +import { SuiTransactionBuilderImpl } from "./builders/sui"; + +// Export types +export type * from "./types"; + +// Export configuration functions +export { getRPCConfig, setRPCConfig, getRPCUrl } from "./config"; + +// Builder instances +const solanaBuilder = new SolanaTransactionBuilderImpl(); +const evmBuilder = new EVMTransactionBuilderImpl(); +const bitcoinBuilder = new BitcoinTransactionBuilderImpl(); +const suiBuilder = new SuiTransactionBuilderImpl(); + +/** + * Create a send token transaction for any supported blockchain + * + * @param params - Transaction parameters including networkId, addresses, amount, and optional token + * @returns Promise - Contains the transaction object or error message + * + * @example + * ```typescript + * import { createSendTokenTransaction } from "@phantom/transactions"; + * import { NetworkId } from "@phantom/client"; + * + * // Native token transfer (SOL, ETH, etc.) + * const { transaction, error } = await createSendTokenTransaction({ + * networkId: NetworkId.SOLANA_MAINNET, + * from: "sender-address", + * to: "recipient-address", + * amount: "1.5", // 1.5 SOL + * }); + * + * // Token transfer + * const { transaction, error } = await createSendTokenTransaction({ + * networkId: NetworkId.ETHEREUM_MAINNET, + * from: "0x...", + * to: "0x...", + * amount: "100", + * token: "0x...", // USDC contract address + * decimals: 6, + * }); + * + * if (error) { + * console.error("Transaction creation failed:", error); + * return; + * } + * + * // Use with Phantom SDK + * const signature = await signAndSendTransaction({ + * transaction, + * networkId, + * }); + * ``` + */ +export async function createSendTokenTransaction(params: SendTokenTransactionParams): Promise { + const [chain] = params.networkId.toLowerCase().split(':'); + + switch (chain) { + case 'solana': + return solanaBuilder.createSendTokenTransaction(params); + + case 'ethereum': + case 'eip155': + case 'polygon': + case 'arbitrum': + case 'optimism': + case 'base': + case 'bsc': + case 'avalanche': + return evmBuilder.createSendTokenTransaction(params); + + case 'bitcoin': + return bitcoinBuilder.createSendTokenTransaction(params); + + case 'sui': + return suiBuilder.createSendTokenTransaction(params); + + default: + return { + transaction: null, + error: `Unsupported network: ${params.networkId}. Supported networks: Solana, Ethereum/EVM chains, Bitcoin, Sui`, + }; + } +} + +/** + * Create a native token transfer (SOL, ETH, BTC, SUI, etc.) + * Convenience function for sending native blockchain tokens + */ +export async function createNativeTransfer(params: Omit): Promise { + return createSendTokenTransaction({ ...params, token: undefined }); +} + +/** + * Create a token transfer (SPL, ERC-20, etc.) + * Convenience function for sending custom tokens + */ +export async function createTokenTransfer(params: Required> & SendTokenTransactionParams): Promise { + return createSendTokenTransaction(params); +} \ No newline at end of file diff --git a/packages/transactions/src/transactions.test.ts b/packages/transactions/src/transactions.test.ts new file mode 100644 index 00000000..7bc99980 --- /dev/null +++ b/packages/transactions/src/transactions.test.ts @@ -0,0 +1,160 @@ +import { createSendTokenTransaction, createNativeTransfer, setRPCConfig, getRPCConfig } from "./index"; + +// Mock NetworkId for testing +const mockNetworkIds = { + SOLANA_MAINNET: "solana:mainnet-beta", + ETHEREUM_MAINNET: "ethereum:1", + POLYGON_MAINNET: "polygon:137", + BITCOIN_MAINNET: "bitcoin:mainnet", + SUI_MAINNET: "sui:mainnet", +} as const; + +describe("@phantom/transactions", () => { + describe("RPC Configuration", () => { + it("should have default RPC configuration", () => { + const config = getRPCConfig(); + expect(config.solana?.mainnet).toBeDefined(); + expect(config.ethereum?.mainnet).toBeDefined(); + expect(config.polygon?.mainnet).toBeDefined(); + expect(config.bitcoin?.mainnet).toBeDefined(); + expect(config.sui?.mainnet).toBeDefined(); + }); + + it("should allow updating RPC configuration", () => { + const customRPC = "https://custom-rpc.example.com"; + + setRPCConfig({ + solana: { + mainnet: customRPC, + }, + }); + + const config = getRPCConfig(); + expect(config.solana?.mainnet).toBe(customRPC); + }); + }); + + describe("createSendTokenTransaction", () => { + it("should return error for unsupported network", async () => { + const result = await createSendTokenTransaction({ + networkId: "unsupported:network" as any, + from: "test-from", + to: "test-to", + amount: "1.0", + }); + + expect(result.transaction).toBeNull(); + expect(result.error).toContain("Unsupported network"); + }); + + it("should create Solana native transfer", async () => { + // Use a custom RPC for testing to avoid hitting real endpoints + setRPCConfig({ + solana: { + mainnet: "https://localhost:8899", // Local test RPC that doesn't exist + }, + }); + + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.SOLANA_MAINNET, + from: "11111111111111111111111111111112", // Valid Solana address format + to: "11111111111111111111111111111113", + amount: "1.0", + }); + + // Since we don't have real RPC connection in tests, we expect an error + // but it should be a connection error, not a parsing error + expect(result.error).toBeDefined(); + expect(result.error).not.toContain("Unsupported network"); + expect(result.error).toContain("Failed to create Solana transaction"); + }, 15000); + + it("should handle EVM networks", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.ETHEREUM_MAINNET, + from: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: "0.1", + }); + + // EVM native transfers should work without RPC connection + expect(result.transaction).toBeDefined(); + expect(result.transaction.to).toBe("0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E"); + expect(result.transaction.value).toBeDefined(); + }); + + it("should return error for Bitcoin (not implemented)", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.BITCOIN_MAINNET, + from: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + to: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + amount: "0.001", + }); + + expect(result.transaction).toBeNull(); + expect(result.error).toContain("not yet implemented"); + }); + + it("should return error for Sui (partially implemented)", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.SUI_MAINNET, + from: "0x123", + to: "0x456", + amount: "1.0", + }); + + // Sui native transfers should create a transaction + expect(result.transaction).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + }); + + describe("createNativeTransfer", () => { + it("should create native transfer without token parameter", async () => { + const result = await createNativeTransfer({ + networkId: mockNetworkIds.ETHEREUM_MAINNET, + from: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: "0.5", + }); + + expect(result.transaction).toBeDefined(); + expect(result.error).toBeUndefined(); + }); + }); + + describe("Amount handling", () => { + it("should handle string amounts", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.ETHEREUM_MAINNET, + from: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: "1.5", // string + }); + + expect(result.transaction).toBeDefined(); + }); + + it("should handle number amounts", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.ETHEREUM_MAINNET, + from: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: 2.5, // number + }); + + expect(result.transaction).toBeDefined(); + }); + + it("should handle bigint amounts", async () => { + const result = await createSendTokenTransaction({ + networkId: mockNetworkIds.ETHEREUM_MAINNET, + from: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", + amount: 1000000000000000000n, // 1 ETH in wei + }); + + expect(result.transaction).toBeDefined(); + }); + }); +}); \ No newline at end of file diff --git a/packages/transactions/src/types.ts b/packages/transactions/src/types.ts new file mode 100644 index 00000000..3ef5d6a2 --- /dev/null +++ b/packages/transactions/src/types.ts @@ -0,0 +1,79 @@ +import type { NetworkId } from "@phantom/client"; + +// Base transaction parameters +export interface BaseTransactionParams { + networkId: NetworkId; + from: string; + to: string; + amount: string | number | bigint; +} + +// Send token transaction parameters +export interface SendTokenTransactionParams extends BaseTransactionParams { + token?: string; // Token address (optional for native token transfers) + decimals?: number; // Token decimals (auto-detected if not provided) +} + +// Transaction result +export interface TransactionResult { + transaction: any; // The blockchain-specific transaction object + error?: string; +} + +// RPC configuration +export interface RPCConfig { + solana?: { + mainnet?: string; + devnet?: string; + testnet?: string; + }; + ethereum?: { + mainnet?: string; + sepolia?: string; + goerli?: string; + }; + polygon?: { + mainnet?: string; + mumbai?: string; + }; + arbitrum?: { + mainnet?: string; + sepolia?: string; + }; + optimism?: { + mainnet?: string; + sepolia?: string; + }; + base?: { + mainnet?: string; + sepolia?: string; + }; + bsc?: { + mainnet?: string; + testnet?: string; + }; + avalanche?: { + mainnet?: string; + fuji?: string; + }; + bitcoin?: { + mainnet?: string; + testnet?: string; + }; + sui?: { + mainnet?: string; + testnet?: string; + devnet?: string; + }; +} + +// Transaction builder interface +export interface TransactionBuilder { + createSendTokenTransaction(params: SendTokenTransactionParams): Promise; +} + +// Chain-specific transaction builders +export interface SolanaTransactionBuilder extends TransactionBuilder {} +export interface EVMTransactionBuilder extends TransactionBuilder {} +export interface BitcoinTransactionBuilder extends TransactionBuilder {} +export interface SuiTransactionBuilder extends TransactionBuilder {} \ No newline at end of file diff --git a/packages/transactions/tsconfig.json b/packages/transactions/tsconfig.json new file mode 100644 index 00000000..3b539eef --- /dev/null +++ b/packages/transactions/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "types": ["jest", "node"], + "moduleResolution": "node" + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} \ No newline at end of file diff --git a/packages/transactions/tsup.config.ts b/packages/transactions/tsup.config.ts new file mode 100644 index 00000000..b8cfe65a --- /dev/null +++ b/packages/transactions/tsup.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: true, + external: ["@phantom/client"], +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 215f7984..f206ca81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,31 @@ __metadata: version: 8 cacheKey: 10c0 +"@0no-co/graphql.web@npm:^1.0.5": + version: 1.2.0 + resolution: "@0no-co/graphql.web@npm:1.2.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + graphql: + optional: true + checksum: 10c0/4eed600962bfab42afb49cddcfb31a47b00502f59707609cf160559920ce0f5cf8874791e4cafc465ede30ae291992f3f892bc757b2a989e80e50e358f71c518 + languageName: node + linkType: hard + +"@0no-co/graphqlsp@npm:^1.12.13": + version: 1.15.0 + resolution: "@0no-co/graphqlsp@npm:1.15.0" + dependencies: + "@gql.tada/internal": "npm:^1.0.0" + graphql: "npm:^15.5.0 || ^16.0.0 || ^17.0.0" + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + checksum: 10c0/0805fec594fc7329194a047e9bd0a4e72387268ccf8042ec6b830048f971c48e6a168ad99572e4a5e6d5556b807f3fc1461fcdcc041cdf75ce42e549a6b44108 + languageName: node + linkType: hard + "@adraffy/ens-normalize@npm:^1.11.0": version: 1.11.0 resolution: "@adraffy/ens-normalize@npm:1.11.0" @@ -1544,6 +1569,49 @@ __metadata: languageName: node linkType: hard +"@gql.tada/cli-utils@npm:1.7.0": + version: 1.7.0 + resolution: "@gql.tada/cli-utils@npm:1.7.0" + dependencies: + "@0no-co/graphqlsp": "npm:^1.12.13" + "@gql.tada/internal": "npm:1.0.8" + graphql: "npm:^15.5.0 || ^16.0.0 || ^17.0.0" + peerDependencies: + "@0no-co/graphqlsp": ^1.12.13 + "@gql.tada/svelte-support": 1.0.1 + "@gql.tada/vue-support": 1.0.1 + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + peerDependenciesMeta: + "@gql.tada/svelte-support": + optional: true + "@gql.tada/vue-support": + optional: true + checksum: 10c0/2e861dcaf153903aaea300dc25025b318caea081ecdcea486170872eeeb64a3a0d3a06b6e1a33e2c6c2ed0efc374bfe819ed74ba8009309db61517eebf405b89 + languageName: node + linkType: hard + +"@gql.tada/internal@npm:1.0.8, @gql.tada/internal@npm:^1.0.0": + version: 1.0.8 + resolution: "@gql.tada/internal@npm:1.0.8" + dependencies: + "@0no-co/graphql.web": "npm:^1.0.5" + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + typescript: ^5.0.0 + checksum: 10c0/dd9b34296160f7eb4374b1791dd9353fd4147f4d7e2a3a8e2974c2319dc6165a4458c19d9b517a3f7764da4e7a4401c099fdf1936841de02c0e565db95d64a5d + languageName: node + linkType: hard + +"@graphql-typed-document-node/core@npm:^3.2.0": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.13": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -2224,6 +2292,35 @@ __metadata: languageName: node linkType: hard +"@mysten/bcs@npm:0.11.1": + version: 0.11.1 + resolution: "@mysten/bcs@npm:0.11.1" + dependencies: + bs58: "npm:^5.0.0" + checksum: 10c0/aca312c07ef1a2bdfbeef677d1636dfd89b306d0ac04c1c57e5c6386bdea067acbd500d6d50347afe9c87f41fa7c78a6deb3e3bb211a313fbdc7ac2651d33de7 + languageName: node + linkType: hard + +"@mysten/sui.js@npm:^0.54.1": + version: 0.54.1 + resolution: "@mysten/sui.js@npm:0.54.1" + dependencies: + "@graphql-typed-document-node/core": "npm:^3.2.0" + "@mysten/bcs": "npm:0.11.1" + "@noble/curves": "npm:^1.1.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/bip32": "npm:^1.3.1" + "@scure/bip39": "npm:^1.2.1" + "@suchipi/femver": "npm:^1.0.0" + bech32: "npm:^2.0.0" + gql.tada: "npm:^1.7.0" + graphql: "npm:^16.8.1" + superstruct: "npm:^1.0.3" + tweetnacl: "npm:^1.0.3" + checksum: 10c0/3a983b298b0db4eae49448612dcf689ce479912a12c8c65a83eb1d1e66e9a224bc46ca83920d32d255716c11d529c743dff7240257952962bdb0b722e04050db + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:^0.2.11": version: 0.2.12 resolution: "@napi-rs/wasm-runtime@npm:0.2.12" @@ -2262,6 +2359,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.1.0": + version: 1.9.6 + resolution: "@noble/curves@npm:1.9.6" + dependencies: + "@noble/hashes": "npm:1.8.0" + checksum: 10c0/e462875ad752d2cdffc3c7b27b6de3adcff5fae0731e94138bd9e452c5f9b7aaf4c01ea6c62d3c0544b4e7419662535bb2ef1103311de48d51885c053206e118 + languageName: node + linkType: hard + "@noble/curves@npm:^1.4.2": version: 1.9.1 resolution: "@noble/curves@npm:1.9.1" @@ -2280,7 +2386,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.8.0, @noble/hashes@npm:~1.8.0": +"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.8.0, @noble/hashes@npm:~1.8.0": version: 1.8.0 resolution: "@noble/hashes@npm:1.8.0" checksum: 10c0/06a0b52c81a6fa7f04d67762e08b2c476a00285858150caeaaff4037356dd5e119f45b2a530f638b77a5eeca013168ec1b655db41bae3236cb2e9d511484fc77 @@ -2656,6 +2762,28 @@ __metadata: languageName: unknown linkType: soft +"@phantom/transactions@workspace:packages/transactions": + version: 0.0.0-use.local + resolution: "@phantom/transactions@workspace:packages/transactions" + dependencies: + "@mysten/sui.js": "npm:^0.54.1" + "@phantom/client": "workspace:^" + "@solana/web3.js": "npm:^1.87.6" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.11.0" + bitcoinjs-lib: "npm:^6.1.5" + buffer: "npm:^6.0.3" + eslint: "npm:8.53.0" + jest: "npm:^29.7.0" + prettier: "npm:^3.5.2" + rimraf: "npm:^6.0.1" + ts-jest: "npm:^29.1.2" + tsup: "npm:^6.7.0" + typescript: "npm:^5.0.4" + viem: "npm:^2.21.1" + languageName: unknown + linkType: soft + "@phantom/wallet-sdk@workspace:*, @phantom/wallet-sdk@workspace:packages/browser-embedded-sdk": version: 0.0.0-use.local resolution: "@phantom/wallet-sdk@workspace:packages/browser-embedded-sdk" @@ -2842,7 +2970,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.7.0, @scure/bip32@npm:^1.7.0": +"@scure/bip32@npm:1.7.0, @scure/bip32@npm:^1.3.1, @scure/bip32@npm:^1.7.0": version: 1.7.0 resolution: "@scure/bip32@npm:1.7.0" dependencies: @@ -2853,7 +2981,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.6.0, @scure/bip39@npm:^1.6.0": +"@scure/bip39@npm:1.6.0, @scure/bip39@npm:^1.2.1, @scure/bip39@npm:^1.6.0": version: 1.6.0 resolution: "@scure/bip39@npm:1.6.0" dependencies: @@ -3477,6 +3605,13 @@ __metadata: languageName: node linkType: hard +"@suchipi/femver@npm:^1.0.0": + version: 1.0.0 + resolution: "@suchipi/femver@npm:1.0.0" + checksum: 10c0/558c15aadeb1153c36e2c00581ba0e04b94ff9a294213e715f377151d66f6ab9704f10f6542230d5e71fdf402d62ee671556dba116ce0ce50876664581942750 + languageName: node + linkType: hard + "@swc/helpers@npm:^0.5.11": version: 0.5.17 resolution: "@swc/helpers@npm:0.5.17" @@ -4804,6 +4939,13 @@ __metadata: languageName: node linkType: hard +"bech32@npm:^2.0.0": + version: 2.0.0 + resolution: "bech32@npm:2.0.0" + checksum: 10c0/45e7cc62758c9b26c05161b4483f40ea534437cf68ef785abadc5b62a2611319b878fef4f86ddc14854f183b645917a19addebc9573ab890e19194bc8f521942 + languageName: node + linkType: hard + "better-path-resolve@npm:1.0.0": version: 1.0.0 resolution: "better-path-resolve@npm:1.0.0" @@ -4820,6 +4962,27 @@ __metadata: languageName: node linkType: hard +"bip174@npm:^2.1.1": + version: 2.1.1 + resolution: "bip174@npm:2.1.1" + checksum: 10c0/d92e142fca85fa4f621dbc9131dafe1da7d69fa7cae03137fa4745d66ffa50561f85ff8c49ca41da8ed1ca65e642415b13dc046531412dfebe6ff03c275e71ae + languageName: node + linkType: hard + +"bitcoinjs-lib@npm:^6.1.5": + version: 6.1.7 + resolution: "bitcoinjs-lib@npm:6.1.7" + dependencies: + "@noble/hashes": "npm:^1.2.0" + bech32: "npm:^2.0.0" + bip174: "npm:^2.1.1" + bs58check: "npm:^3.0.1" + typeforce: "npm:^1.11.3" + varuint-bitcoin: "npm:^1.1.2" + checksum: 10c0/1ce7415cc967bfc5ea7cba0032e73eaba8e3dd7683dffcc90c020d0dba92de6cf1dc92359733c9efa186a4c3ae9547cc379fa4b4a7fb106c0b126c226dec2a40 + languageName: node + linkType: hard + "bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": version: 5.2.2 resolution: "bn.js@npm:5.2.2" @@ -4916,6 +5079,16 @@ __metadata: languageName: node linkType: hard +"bs58check@npm:^3.0.1": + version: 3.0.1 + resolution: "bs58check@npm:3.0.1" + dependencies: + "@noble/hashes": "npm:^1.2.0" + bs58: "npm:^5.0.0" + checksum: 10c0/a01f62351d17cea5f6607f75f6b4b79d3473d018c52f1dfa6f449751062bb079ebfd556ea81c453de657102ab8c5a6b78620161f21ae05f0e5a43543e0447700 + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -7015,6 +7188,23 @@ __metadata: languageName: node linkType: hard +"gql.tada@npm:^1.7.0": + version: 1.8.12 + resolution: "gql.tada@npm:1.8.12" + dependencies: + "@0no-co/graphql.web": "npm:^1.0.5" + "@0no-co/graphqlsp": "npm:^1.12.13" + "@gql.tada/cli-utils": "npm:1.7.0" + "@gql.tada/internal": "npm:1.0.8" + peerDependencies: + typescript: ^5.0.0 + bin: + gql-tada: bin/cli.js + gql.tada: bin/cli.js + checksum: 10c0/011c50a3e02e4b836ce69b01d5a523c1cf9526a031cd8f0d5e14a7505814381922f7ed3f21ae1b4508051fe1f868f1bd836bbea96fcec30c48cc383351a31d8e + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -7029,6 +7219,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^15.5.0 || ^16.0.0 || ^17.0.0, graphql@npm:^16.8.1": + version: 16.11.0 + resolution: "graphql@npm:16.11.0" + checksum: 10c0/124da7860a2292e9acf2fed0c71fc0f6a9b9ca865d390d112bdd563c1f474357141501c12891f4164fe984315764736ad67f705219c62f7580681d431a85db88 + languageName: node + linkType: hard + "has-bigints@npm:^1.0.2": version: 1.1.0 resolution: "has-bigints@npm:1.1.0" @@ -9631,6 +9828,27 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.8.6": + version: 0.8.6 + resolution: "ox@npm:0.8.6" + dependencies: + "@adraffy/ens-normalize": "npm:^1.11.0" + "@noble/ciphers": "npm:^1.3.0" + "@noble/curves": "npm:^1.9.1" + "@noble/hashes": "npm:^1.8.0" + "@scure/bip32": "npm:^1.7.0" + "@scure/bip39": "npm:^1.6.0" + abitype: "npm:^1.0.8" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/025c638966c6e569bb38a78a6f9f84bbeb9f5b0a594ef011ab3f81414edcaf5b20d9ea7d8f90471ddafdcb3f21c61f7a2255d35d2032e8daafbc8b20d98d27cf + languageName: node + linkType: hard + "p-filter@npm:^2.1.0": version: 2.1.0 resolution: "p-filter@npm:2.1.0" @@ -10527,7 +10745,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:^5.0.1": +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.1": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 @@ -11017,6 +11235,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^1.0.3": + version: 1.0.4 + resolution: "superstruct@npm:1.0.4" + checksum: 10c0/d355f1a96fa314e9df217aa371e8f22854644e7b600b7b0faa36860a8e50f61a60a6f1189ecf166171bf438aa6581bbd0d3adae1a65f03a3c43c62fd843e925c + languageName: node + linkType: hard + "superstruct@npm:^2.0.2": version: 2.0.2 resolution: "superstruct@npm:2.0.2" @@ -11553,6 +11778,13 @@ __metadata: languageName: node linkType: hard +"typeforce@npm:^1.11.3": + version: 1.18.0 + resolution: "typeforce@npm:1.18.0" + checksum: 10c0/011f57effd9ae6d3dd8bb249e09b4ecadb2c2a3f803b27f977ac8b7782834855930bff971ba549bcd5a8cedc8136d8a977c0b7e050cc67deded948181b7ba3e8 + languageName: node + linkType: hard + "typescript@npm:^5.0.2, typescript@npm:^5.0.4, typescript@npm:^5.2.0, typescript@npm:^5.8.3, typescript@npm:~5.8.3": version: 5.8.3 resolution: "typescript@npm:5.8.3" @@ -11847,6 +12079,36 @@ __metadata: languageName: node linkType: hard +"varuint-bitcoin@npm:^1.1.2": + version: 1.1.2 + resolution: "varuint-bitcoin@npm:1.1.2" + dependencies: + safe-buffer: "npm:^5.1.1" + checksum: 10c0/3d38f8de8192b7a4fc00abea01ed189f1e1e6aee1ebc4192040c5717d2483e0a6a73873fcf6b3c1910d947d338b671470505705fe40c765dc832255dfa2d4210 + languageName: node + linkType: hard + +"viem@npm:^2.21.1": + version: 2.33.2 + resolution: "viem@npm:2.33.2" + dependencies: + "@noble/curves": "npm:1.9.2" + "@noble/hashes": "npm:1.8.0" + "@scure/bip32": "npm:1.7.0" + "@scure/bip39": "npm:1.6.0" + abitype: "npm:1.0.8" + isows: "npm:1.0.7" + ox: "npm:0.8.6" + ws: "npm:8.18.2" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/4b0cdbb322617897f71f6ffa9efc90f8cfad202f9a90d2275181db79e806a962f5d458e3d8b5f861a81e628c05b9a4ebf7853ebce26283291aec3f54a03a4df3 + languageName: node + linkType: hard + "viem@npm:^2.33.1": version: 2.33.1 resolution: "viem@npm:2.33.1"