-
Notifications
You must be signed in to change notification settings - Fork 44
feat: Transactions package #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
rafinskipg
wants to merge
1
commit into
main
Choose a base branch
from
feat/transactions-package
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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", | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <button onClick={handleSend}>Send 1.5 SOL</button>; | ||
| } | ||
| ``` | ||
|
|
||
| ## 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<TransactionResult>` 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 ( | ||
| <button onClick={sendUSDC} disabled={loading}> | ||
| Send 10 USDC | ||
| </button> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ### 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** @type {import('jest').Config} */ | ||
| module.exports = { | ||
| preset: 'ts-jest', | ||
| testEnvironment: 'node', | ||
| roots: ['<rootDir>/src'], | ||
| testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], | ||
| transform: { | ||
| '^.+\\.ts$': 'ts-jest', | ||
| }, | ||
| collectCoverageFrom: [ | ||
| 'src/**/*.ts', | ||
| '!src/**/*.d.ts', | ||
| '!src/**/*.test.ts', | ||
| ], | ||
| moduleNameMapping: { | ||
| '^@phantom/(.*)$': '<rootDir>/../$1/src', | ||
| }, | ||
| testTimeout: 10000, | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we strictly need to be turning off this rule globally?