Skip to content

Conversation

@fengtality
Copy link
Contributor

@fengtality fengtality commented Dec 18, 2025

Summary

Adds wallet management capabilities to Gateway:

  1. Client-side wallet generation script - Generate Solana or Ethereum keypairs locally
  2. Server-side wallet API endpoints - Create wallets, show private keys, and send transactions via API

New API Endpoints

POST /wallet/create

Create a new wallet server-side and store it encrypted in Gateway.

{
  "chain": "solana",
  "setDefault": true
}

POST /wallet/show-private-key

Retrieve the private key for a wallet. Requires explicit passphrase verification for security.

{
  "chain": "solana",
  "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "passphrase": "<gateway-passphrase>"
}

POST /wallet/send

Send native tokens (SOL/ETH) or SPL/ERC20 tokens to another address.

{
  "chain": "solana",
  "network": "mainnet-beta",
  "address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "toAddress": "<recipient-address>",
  "amount": "0.1",
  "token": "SOL"
}

Client-Side Wallet Generation Script

Security Model

┌─────────────────────────────────────────────────────────────┐
│  1. Keypair generated LOCALLY on user's machine             │
│  2. Address + Private Key displayed with backup warnings    │
│  3. User must type "yes" to confirm backup                  │
│  4. User must type "yes" to add to Gateway                  │
│  5. Only then is key sent to /wallet/add (encrypted)        │
└─────────────────────────────────────────────────────────────┘

Usage

# Solana wallet (default)
pnpm wallet:create

# Ethereum wallet
pnpm wallet:create -- --chain ethereum

# Generate only, don't add to Gateway
pnpm wallet:create -- --no-add

# Verify a saved private key
pnpm wallet:create -- --verify

Supported Chains

Chain Private Key Format Address Format
Solana Base58 (88 chars) Base58 (32-44 chars)
Ethereum Hex 0x prefix (66 chars) 0x + 40 hex chars

Files Changed

New Files

  • scripts/create-wallet.ts - Client-side wallet generation script
  • src/wallet/routes/createWallet.ts - POST /wallet/create endpoint
  • src/wallet/routes/showPrivateKey.ts - POST /wallet/show-private-key endpoint
  • src/wallet/routes/sendTransaction.ts - POST /wallet/send endpoint
  • test/wallet/wallet-new-routes.test.ts - Tests for new endpoints (21 test cases)

Modified Files

  • package.json - Added wallet:create npm script
  • src/services/config-manager-cert-passphrase.ts - Simplified to use passphrase only
  • src/wallet/schemas.ts - Added schemas for new endpoints
  • src/wallet/utils.ts - Added utility functions for new endpoints
  • src/wallet/wallet.routes.ts - Registered @fastify/sensible and new routes

QA Manual Testing Instructions

Prerequisites

  • Node.js 20+
  • Gateway repo cloned and dependencies installed (pnpm install)
  • Gateway running with passphrase (pnpm start --passphrase=test --dev)

Test 1: Create wallet via API

curl -X POST http://localhost:15888/wallet/create \
  -H "Content-Type: application/json" \
  -d '{"chain": "solana", "setDefault": true}'

Expected: Returns {"address": "...", "chain": "solana"}

Test 2: Show private key via API

curl -X POST http://localhost:15888/wallet/show-private-key \
  -H "Content-Type: application/json" \
  -d '{"chain": "solana", "address": "<address-from-test-1>", "passphrase": "test"}'

Expected: Returns {"address": "...", "chain": "solana", "privateKey": "..."}

Test 3: Show private key with wrong passphrase

curl -X POST http://localhost:15888/wallet/show-private-key \
  -H "Content-Type: application/json" \
  -d '{"chain": "solana", "address": "<address>", "passphrase": "wrong"}'

Expected: Returns 401 Unauthorized

Test 4: Send transaction via API

curl -X POST http://localhost:15888/wallet/send \
  -H "Content-Type: application/json" \
  -d '{"chain": "solana", "network": "mainnet-beta", "address": "<sender>", "toAddress": "<recipient>", "amount": "0.001"}'

Expected: Returns transaction signature and status

Test 5: Generate wallet client-side

pnpm wallet:create -- --no-add

Expected: Displays address and private key with security warnings

Test 6: Verify private key

pnpm wallet:create -- --verify

Expected: Prompts for private key and shows derived address


🤖 Generated with Claude Code

fengtality and others added 5 commits December 18, 2025 11:23
Add scripts/create-wallet.ts for secure client-side wallet generation:
- Generates Solana keypair locally using @solana/web3.js
- Displays address and private key with backup warnings
- Requires user confirmation before adding to Gateway
- Supports --no-add flag for generate-only mode
- Supports --gateway flag for custom Gateway URL

Usage: pnpm wallet:create

Security: Private key is generated locally and only sent to Gateway
after user explicitly confirms backup and chooses to add it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add --verify flag to verify saved private keys
- Verification shows derived address and can match against expected
- Update warning to emphasize this is the ONLY time key is displayed
- Suggest password manager instead of writing down (key is long)
- Remove hardware wallet suggestion
- Add hint to use --verify for later addition to Gateway

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add detailed COMMANDS section with all flags explained
- Add SECURITY NOTES section
- Add ADDING WALLET TO GATEWAY MANUALLY section
- Simplify --verify mode to just validate key and show address

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add --chain flag to specify solana (default) or ethereum
- Support Ethereum key generation using ethers.js Wallet.createRandom()
- Support Ethereum key verification (hex format with 0x prefix)
- Update documentation header with both chain examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…ncryption

Add a dedicated environment variable (GATEWAY_WALLET_KEY) for wallet
encryption that is separate from the server passphrase. This provides
better security because:

- Environment variables are not visible in `ps aux` (unlike --passphrase)
- Not stored in shell history
- Can be set separately from the shared server passphrase

The system maintains backward compatibility by falling back to the
existing passphrase if GATEWAY_WALLET_KEY is not set.

Usage: export GATEWAY_WALLET_KEY=your-secret-key

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@fengtality fengtality marked this pull request as ready for review December 18, 2025 19:47
@fengtality fengtality added this to the v2.12 milestone Dec 18, 2025
@fengtality fengtality requested review from nikspz and rapcmia December 18, 2025 19:47
@fengtality fengtality changed the title feat: add local Solana wallet generation script feat: add local wallet generation script Dec 18, 2025
@rapcmia rapcmia moved this to Under Review in Pull Request Board Dec 19, 2025
@rapcmia
Copy link
Contributor

rapcmia commented Dec 19, 2025

Commit e9920b0

  • Tested on linux and macOs with gateway defaults
  • By default, User is prompted to save to gateway
    • If yes, User is prompt if need to add to gateway
      image
    • If no, nothing happens
      image
  • Gateway must be active all the time in order to add wallets ✅
    image
    • Confirmed wallet is available and registered on gateway ✅
      image
    • Tested with gateway offline ✅
      image

Manual functional testing of CLI commands using pnpm

  • pnpm wallet:create (solana by default) ✅
  • pnpm wallet:create -- --chain solana ✅
  • pnpm wallet:create -- --chain solana --no-add ✅
    image
  • pnpm wallet:create -- --verify ✅
    • Added wallet created and matched the wallet address ✅
    • Added incorrect or incomplete private keys, responses are correct and related to input ✅
      image
  • pnpm wallet:create -- --verify --chain solana ✅
    • Added a pkey ethereum and did not accepted, returns Non-base58 character
      image

The only issue I found is when prompted to enter values, the input form is not immediately visible. The form only appears after typing a character and then pressing backspace ❌

image

rapcmia
rapcmia previously approved these changes Dec 19, 2025
Copy link
Contributor

@nikspz nikspz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • commit e9920b0
    • pnpm wallet:create --chain ethereum

      • Need to install the following packages:
      • [email protected]
      • proceed? yes
        • failed  ELIFECYCLE  Command failed with exit code 1.
    • sudo apt-get install -y nodejs

    • pnpm install

    • pnpm wallet:create --chain ethereum ✅

      • review created wallet address and private key successully
    • imported created private key successfully ✅

    • pnpm wallet:create -- --verify --chain ethereum ✅

      • wallet verified ✅

      • private key showed when type/paste ❌

        image.png

    • pnpm wallet:create -- --chain ethereum

      • add as default ✅
      • reviewed wallet added as default for ethereum
    • pnpm wallet:create -- --chain ethereum --no-add

      • review wallet created and not added to gateway
    • export GATEWAY_WALLET_KEY=Randompass

    • pnpm wallet:create -- --chain ethereum

    • wallet shown in gateway

    • unset GATEWAY_WALLET_KEY

    • restart and

    • gateway pnpm start --passphrase testpass --dev

    • Expected:

      • Wallet cannot be decrypted
      • Clear error in logs (e.g. “failed to decrypt wallet”)
    • Actual:

      • no failed to decrypt wallet showed

- Add POST /wallet/create endpoint for server-side wallet generation
- Add POST /wallet/show-private-key endpoint with explicit passphrase verification
- Add POST /wallet/send endpoint for native and token transfers
- Remove GATEWAY_WALLET_KEY env var, use passphrase only for wallet encryption
- Register @fastify/sensible plugin for proper HTTP error responses
- Add comprehensive tests for new wallet endpoints
- Update Swagger examples to use configured default wallet addresses

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@fengtality fengtality changed the title feat: add local wallet generation script feat: add wallet generation script and wallet API endpoints Dec 23, 2025
fengtality and others added 2 commits December 22, 2025 19:39
Detect token program by checking mint account owner and use the correct
program ID (TOKEN_PROGRAM_ID or TOKEN_2022_PROGRAM_ID) for ATA derivation
and transfer instructions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Config values may be empty in CI environment, causing test failures.
Use generated Solana keypair and fixed Ethereum test address instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@rapcmia rapcmia dismissed their stale review December 23, 2025 07:33

for retest

@rapcmia
Copy link
Contributor

rapcmia commented Dec 23, 2025

Commit 0ab0660

  • Test on solana chain
  • When setDefault is set to false, no wallet is marked as the default across all test cases

Test gateway client-side scripts

  • pnpm wallet:create ✅
  • pnpm wallet:create —noadd ✅
  • pnpm wallet:create —verify ✅
    • Accepts valid and return response when invalid key

Test new wallet endpoints

  • /wallet/create ✅
    curl -sS -X POST http://localhost:15888/wallet/create -H 'Content-Type: application/json' -d '{"chain":"solana"}' | jq
    {
      "address": "FkesyV1BasRFQtTZBedtrjuTswNjpQJoc5t5QgzgkLYA",
      "chain": "solana"
    }
    logs: 2025-12-23 01:49:48 | info | 	Creating new wallet for chain: solana
    
    • Test with invalid chain returns validation error 400, must be equal to one of the allowed values
    • When checking wallet addresses, already added on the list
  • /wallet/show-private-key ✅
    curl -sS -X POST http://localhost:15888/wallet/show-private-key -H 'Content-Type: application/json' -d '{"chain":"solana","address":"FkesyV1BasRFQtTZBedtrjuTswNjpQJoc5t5QgzgkLYA","passphrase":"XXX"}' | jq
    {
      "address": "FkesyV1BasRFQtTZBedtrjuTswNjpQJoc5t5QgzgkLYA",
      "chain": "solana",
      "privateKey": "XXX"
    }
    logs: 2025-12-23 03:02:36 | info | 	Show private key requested for chain: solana, address: FkesyV1BasRFQtTZBedtrjuTswNjpQJoc5t5QgzgkLYA
    
    • Requires wallet address and passphrase to complete
    • Test with invalid passphrase ❗
      • Response is correct "Invalid passphrase" but gateway logs return different ❌
        2025-12-23 03:02:16 | info | 	Show private key requested for chain: solana, address: FkesyV1BasRFQtTZBedtrjuTswNjpQJoc5t5QgzgkLYA
        
    • Test with invalid wallet address
      • Response 500 with Error: Invalid Solana address format: invalid
  • /wallet/send ❌
    • Send to another QA wallet address, from Main wallet to E1z9B8ynMszys7jiXGS1qk956xxc2pT5qeZzQSSCW9fr
    • Tried to send JUP and USDC equivalent to 3usd but kept on failing ❌
      curl -sS -X POST http://localhost:15888/wallet/send -H 'Content-Type: application/json' -d '{"chain":"solana","network":"mainnet-beta","address":"GiooMWkHziPuXhfRar2ChBxU6VhQSrAh7xJTG2zHcxWV","toAddress":"E1z9B8ynMszys7jiXGS1qk956xxc2pT5qeZzQSSCW9fr","amount":"3","token":"USDC"}' | jq
      {
        "statusCode": 500,
        "error": "InternalServerError",
        "message": "Transaction failed: Transaction failed to confirm after 10 attempts"
      }
      
      #### gateway logs 
      2025-12-23 03:16:08 | info |    Sending transaction with 0.01 lamports/CU priority fee and total priority fee of 0.000002 SOL
      2025-12-23 03:16:09 | info |    Sending transaction via sendRawTransaction
      2025-12-23 03:16:09 | info |    🚀 Sent transaction BeJSXpyVYSKamRyf3wG3NFdwdbYius8jvPGxfPMPVf5GKdopiMCCjBqH25wARCyDAaBJ9ZQwZDHkhtr1mxgb1TA, polling for confirmation...
      2025-12-23 03:16:11 | error |   ❌ Transaction BeJSXpyVYSKamRyf3wG3NFdwdbYius8jvPGxfPMPVf5GKdopiMCCjBqH25wARCyDAaBJ9ZQwZDHkhtr1mxgb1TA failed with error: {
        "InstructionError": [
          0,
          "InvalidAccountData"
        ]
      }
      2025-12-23 03:16:11 | error |   Solana send failed: Transaction failed to confirm after 10 attempts
      
      • I tried to change nodeURLs(default, helius and quicknode) and minPriorityFeePerCU (1) but still failed
      • Tried to send another wallet, still failed

See attached for the test and gateway logs: 12232025a.zip

- Create recipient's Associated Token Account (ATA) if it doesn't exist before SPL/Token2022 transfers
- Add warning log when invalid passphrase is provided for show-private-key
- Fixes InvalidAccountData error when sending tokens to wallets without ATAs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@rapcmia
Copy link
Contributor

rapcmia commented Dec 24, 2025

Commit 4e2b361

  • Test /wallet/show-private-key ✅
    curl -sS -X POST http://localhost:15888/wallet/show-private-key -H 'Content-Type: application/json' -d '{"chain":"solana","address":"GiooMWkHziPuXhfRar2ChBxU6VhQSrAh7xJTG2zHcxWV","passphrase":"XXX"}' | jq
    {
      "statusCode": 401,
      "error": "UnauthorizedError",
      "message": "Invalid passphrase"
    }
    logs: 2025-12-23 20:27:57 | warn | 	Invalid passphrase provided for show-private-key request on solana
    
    • Now gateway logs with invalid passphrase provided
    • Test with wallets I added via wallet/add and still shows pkey
  • Test /wallet/send
    curl -sS -X POST http://localhost:15888/wallet/send -H 'Content-Type: application/json' -d '{"chain":"solana","network":"mainnet-beta","address":"GiooMWkHziPuXhfRar2ChBxU6VhQSrAh7xJTG2zHcxWV","toAddress":"E1z9B8ynMszys7jiXGS1qk956xxc2pT5qeZzQSSCW9fr","amount":"3","token":"USDC"}' | jq
    {
      "signature": "4x2GpULizQS8vJvYfTmd3CoseAHdTeS97CTMFikcsmSZfaPM34HF8uH1PNXHHEKGCdLeJgGEQfpZmp6vc8roNh2a",
      "status": 1,
      "amount": "3",
      "token": "USDC",
      "toAddress": "E1z9B8ynMszys7jiXGS1qk956xxc2pT5qeZzQSSCW9fr",
      "fee": 0.00024095800000000002
    }
    logs: 2025-12-23 20:34:20 | info | 	Sending 3 USDC from GiooMWkHziPuXhfRar2ChBxU6VhQSrAh7xJTG2zHcxWV to E1z9B8ynMszys7jiXGS1qk956xxc2pT5qeZzQSSCW9fr
    

  • Built docker image locally and do all endpoint tests, all ok ✅
  • Incase needed, attached is test and gateway logs 12242025.zip

Copy link
Contributor

@rapcmia rapcmia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved 1/2

  • Test for solana chain ✅

Copy link
Contributor

@nikspz nikspz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ethereum
    • POST /wallet/create ✅
    • POST /wallet/show-private-key ✅
      • invalid passphrase: ok
        • {
          "statusCode":401,
          "error":"UnauthorizedError",
          "message":"Invalid passphrase"
          }
      • correct passphrase ✅
        • showed pkey
    • pnpm wallet:create -- --no-add ✅
      • created wallet
    • pnpm wallet:create -- --no-add --chain ethereum ✅
      • created wallet
    • pnpm wallet:create -- --verify --chain ethereum ✅
      • returns correct address
    • POST /wallet/send ✅

@cardosofede cardosofede merged commit c71c6ab into development Dec 31, 2025
5 checks passed
@cardosofede cardosofede deleted the feat/wallet-create-script branch December 31, 2025 11:17
@rapcmia rapcmia moved this from Under Review to Development v2.12.0 in Pull Request Board Jan 5, 2026
@rapcmia rapcmia moved this from Development v2.12.0 to Release v2.11.0 in Pull Request Board Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Release v2.12.0

Development

Successfully merging this pull request may close these issues.

5 participants