diff --git a/.auto-claude-security.json b/.auto-claude-security.json new file mode 100644 index 0000000..d8961d3 --- /dev/null +++ b/.auto-claude-security.json @@ -0,0 +1,225 @@ +{ + "base_commands": [ + ".", + "[", + "[[", + "ag", + "awk", + "basename", + "bash", + "bc", + "break", + "cat", + "cd", + "chmod", + "clear", + "cmp", + "column", + "comm", + "command", + "continue", + "cp", + "curl", + "cut", + "date", + "df", + "diff", + "dig", + "dirname", + "du", + "echo", + "egrep", + "env", + "eval", + "exec", + "exit", + "expand", + "export", + "expr", + "false", + "fd", + "fgrep", + "file", + "find", + "fmt", + "fold", + "gawk", + "gh", + "git", + "grep", + "gunzip", + "gzip", + "head", + "help", + "host", + "iconv", + "id", + "jobs", + "join", + "jq", + "kill", + "killall", + "less", + "let", + "ln", + "ls", + "lsof", + "man", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "paste", + "pgrep", + "ping", + "pkill", + "popd", + "printenv", + "printf", + "ps", + "pushd", + "pwd", + "read", + "readlink", + "realpath", + "reset", + "return", + "rev", + "rg", + "rm", + "rmdir", + "sed", + "seq", + "set", + "sh", + "shuf", + "sleep", + "sort", + "source", + "split", + "stat", + "tail", + "tar", + "tee", + "test", + "time", + "timeout", + "touch", + "tr", + "tree", + "true", + "type", + "uname", + "unexpand", + "uniq", + "unset", + "unzip", + "watch", + "wc", + "wget", + "whereis", + "which", + "whoami", + "xargs", + "yes", + "yq", + "zip", + "zsh" + ], + "stack_commands": [ + "createdb", + "createuser", + "dive", + "docker", + "docker-buildx", + "docker-compose", + "dockerfile", + "dropdb", + "dropuser", + "eslint", + "initdb", + "jest", + "nest", + "node", + "npm", + "npx", + "nvm", + "pg_ctl", + "pg_dump", + "pg_dumpall", + "pg_isready", + "pg_restore", + "postgres", + "prettier", + "psql", + "ts-node", + "tsc", + "tsx", + "turbo", + "yarn" + ], + "script_commands": [ + "bun", + "npm", + "pnpm", + "yarn" + ], + "custom_commands": [], + "detected_stack": { + "languages": [ + "javascript", + "typescript" + ], + "package_managers": [ + "yarn" + ], + "frameworks": [ + "nestjs", + "turbo", + "jest", + "eslint", + "prettier" + ], + "databases": [ + "postgresql" + ], + "infrastructure": [ + "docker" + ], + "cloud_providers": [], + "code_quality_tools": [], + "version_managers": [ + "nvm" + ] + }, + "custom_scripts": { + "npm_scripts": [ + "build", + "dev", + "start", + "start:dev", + "test", + "lint", + "lint:fix", + "format", + "format:fix", + "docker:build", + "docker:up", + "docker:down", + "db:generate", + "db:migrate", + "db:push", + "clean", + "referral-rewards" + ], + "make_targets": [], + "poetry_scripts": [], + "cargo_aliases": [], + "shell_scripts": [] + }, + "project_dir": "/Users/mini/Projects/microservices", + "created_at": "2026-01-18T02:17:30.535095", + "project_hash": "defd094cee780bd44e5ea01103f5125e", + "inherited_from": "/Users/mini/Projects/microservices" +} \ No newline at end of file diff --git a/.auto-claude-status b/.auto-claude-status new file mode 100644 index 0000000..04cd65b --- /dev/null +++ b/.auto-claude-status @@ -0,0 +1,25 @@ +{ + "active": true, + "spec": "001-i-would-like-to-do-a-poc", + "state": "building", + "subtasks": { + "completed": 24, + "total": 29, + "in_progress": 1, + "failed": 0 + }, + "phase": { + "current": "Integration", + "id": null, + "total": 5 + }, + "workers": { + "active": 0, + "max": 1 + }, + "session": { + "number": 25, + "started_at": "2026-01-18T02:37:10.520239" + }, + "last_update": "2026-01-18T03:24:40.380937" +} \ No newline at end of file diff --git a/.claude_settings.json b/.claude_settings.json new file mode 100644 index 0000000..f461a0c --- /dev/null +++ b/.claude_settings.json @@ -0,0 +1,39 @@ +{ + "sandbox": { + "enabled": true, + "autoAllowBashIfSandboxed": true + }, + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Read(./**)", + "Write(./**)", + "Edit(./**)", + "Glob(./**)", + "Grep(./**)", + "Read(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/**)", + "Write(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/**)", + "Edit(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/**)", + "Glob(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/**)", + "Grep(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/**)", + "Read(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/.auto-claude/specs/001-i-would-like-to-do-a-poc/**)", + "Write(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/.auto-claude/specs/001-i-would-like-to-do-a-poc/**)", + "Edit(/Users/mini/Projects/microservices/.auto-claude/worktrees/tasks/001-i-would-like-to-do-a-poc/.auto-claude/specs/001-i-would-like-to-do-a-poc/**)", + "Read(/Users/mini/Projects/microservices/.auto-claude/**)", + "Write(/Users/mini/Projects/microservices/.auto-claude/**)", + "Edit(/Users/mini/Projects/microservices/.auto-claude/**)", + "Glob(/Users/mini/Projects/microservices/.auto-claude/**)", + "Grep(/Users/mini/Projects/microservices/.auto-claude/**)", + "Bash(*)", + "WebFetch(*)", + "WebSearch(*)", + "mcp__context7__resolve-library-id(*)", + "mcp__context7__get-library-docs(*)", + "mcp__graphiti-memory__search_nodes(*)", + "mcp__graphiti-memory__search_facts(*)", + "mcp__graphiti-memory__add_episode(*)", + "mcp__graphiti-memory__get_episodes(*)", + "mcp__graphiti-memory__get_entity_edge(*)" + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c076ebc..c49e0e8 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,7 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json Pulumi.*.yaml /generated/prisma -*.db \ No newline at end of file +*.db + +# Auto Claude data directory +.auto-claude/ diff --git a/apps/send-swap-service/.env.example b/apps/send-swap-service/.env.example new file mode 100644 index 0000000..9e8ad3a --- /dev/null +++ b/apps/send-swap-service/.env.example @@ -0,0 +1,71 @@ +# Wallet Configuration (CRITICAL - keep secure) +MNEMONIC= +WALLET_PASSPHRASE= + +# Database Configuration +DATABASE_URL="postgresql://postgres:password@send-swap-db:5432/send_swap_service" + +# Swapper APIs (Backend - no VITE_ prefix) +CHAINFLIP_NETWORK="mainnet" +CHAINFLIP_API_URL="https://chainflip-broker.io" +NEAR_INTENTS_API_KEY= +JUPITER_API_URL="https://quote-api.jup.ag/v6" +RELAY_API_URL="https://api.relay.link" + +# Unchained API URLs (Production) +UNCHAINED_ETHEREUM_HTTP_URL="https://api.ethereum.shapeshift.com" +UNCHAINED_ETHEREUM_WS_URL="wss://api.ethereum.shapeshift.com" +UNCHAINED_AVALANCHE_HTTP_URL="https://api.avalanche.shapeshift.com" +UNCHAINED_AVALANCHE_WS_URL="wss://api.avalanche.shapeshift.com" +UNCHAINED_OPTIMISM_HTTP_URL="https://api.optimism.shapeshift.com" +UNCHAINED_OPTIMISM_WS_URL="wss://api.optimism.shapeshift.com" +UNCHAINED_BNBSMARTCHAIN_HTTP_URL="https://api.bnbsmartchain.shapeshift.com" +UNCHAINED_BNBSMARTCHAIN_WS_URL="wss://api.bnbsmartchain.shapeshift.com" +UNCHAINED_POLYGON_HTTP_URL="https://api.polygon.shapeshift.com" +UNCHAINED_POLYGON_WS_URL="wss://api.polygon.shapeshift.com" +UNCHAINED_GNOSIS_HTTP_URL="https://api.gnosis.shapeshift.com" +UNCHAINED_GNOSIS_WS_URL="wss://api.gnosis.shapeshift.com" +UNCHAINED_ARBITRUM_HTTP_URL="https://api.arbitrum.shapeshift.com" +UNCHAINED_ARBITRUM_WS_URL="wss://api.arbitrum.shapeshift.com" +UNCHAINED_ARBITRUM_NOVA_HTTP_URL="https://api.arbitrum-nova.shapeshift.com" +UNCHAINED_ARBITRUM_NOVA_WS_URL="wss://api.arbitrum-nova.shapeshift.com" +UNCHAINED_BASE_HTTP_URL="https://api.base.shapeshift.com" +UNCHAINED_BASE_WS_URL="wss://api.base.shapeshift.com" +UNCHAINED_BITCOIN_HTTP_URL="https://api.bitcoin.shapeshift.com" +UNCHAINED_BITCOIN_WS_URL="wss://api.bitcoin.shapeshift.com" +UNCHAINED_DOGECOIN_HTTP_URL="https://api.dogecoin.shapeshift.com" +UNCHAINED_DOGECOIN_WS_URL="wss://api.dogecoin.shapeshift.com" +UNCHAINED_LITECOIN_HTTP_URL="https://api.litecoin.shapeshift.com" +UNCHAINED_LITECOIN_WS_URL="wss://api.litecoin.shapeshift.com" +UNCHAINED_BITCOINCASH_HTTP_URL="https://api.bitcoincash.shapeshift.com" +UNCHAINED_BITCOINCASH_WS_URL="wss://api.bitcoincash.shapeshift.com" +UNCHAINED_COSMOS_HTTP_URL="https://api.cosmos.shapeshift.com" +UNCHAINED_COSMOS_WS_URL="wss://api.cosmos.shapeshift.com" +UNCHAINED_THORCHAIN_HTTP_URL="https://api.thorchain.shapeshift.com" +UNCHAINED_THORCHAIN_WS_URL="wss://api.thorchain.shapeshift.com" +UNCHAINED_MAYACHAIN_HTTP_URL="https://api.mayachain.shapeshift.com" +UNCHAINED_MAYACHAIN_WS_URL="wss://api.mayachain.shapeshift.com" +UNCHAINED_SOLANA_HTTP_URL="https://api.solana.shapeshift.com" +UNCHAINED_SOLANA_WS_URL="wss://api.solana.shapeshift.com" + +# Node URLs (Production) +ETHEREUM_NODE_URL="https://api.ethereum.shapeshift.com/api/v1/jsonrpc" +AVALANCHE_NODE_URL="https://api.avalanche.shapeshift.com/api/v1/jsonrpc" +OPTIMISM_NODE_URL="https://api.optimism.shapeshift.com/api/v1/jsonrpc" +BNBSMARTCHAIN_NODE_URL="https://api.bnbsmartchain.shapeshift.com/api/v1/jsonrpc" +POLYGON_NODE_URL="https://api.polygon.shapeshift.com/api/v1/jsonrpc" +GNOSIS_NODE_URL="https://api.gnosis.shapeshift.com/api/v1/jsonrpc" +ARBITRUM_NODE_URL="https://api.arbitrum.shapeshift.com/api/v1/jsonrpc" +ARBITRUM_NOVA_NODE_URL="https://api.arbitrum-nova.shapeshift.com/api/v1/jsonrpc" +BASE_NODE_URL="https://api.base.shapeshift.com/api/v1/jsonrpc" +THORCHAIN_NODE_URL="https://api.thorchain.shapeshift.com/lcd" +MAYACHAIN_NODE_URL="https://api.mayachain.shapeshift.com/lcd" +SOLANA_NODE_URL="https://api.solana.shapeshift.com/api/v1/jsonrpc" + +# Midgard URLs (Production) +THORCHAIN_MIDGARD_URL="https://api.thorchain.shapeshift.com/midgard/v2" +MAYACHAIN_MIDGARD_URL="https://api.mayachain.shapeshift.com/midgard/v2" + +# Service URLs +SWAP_SERVICE_URL="http://swap-service:3001" +NOTIFICATIONS_SERVICE_URL="http://notifications-service:3003" diff --git a/apps/send-swap-service/jest.config.js b/apps/send-swap-service/jest.config.js new file mode 100644 index 0000000..870177f --- /dev/null +++ b/apps/send-swap-service/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: 'src', + testRegex: '.*\\.spec\\.ts$', + transform: { + '^.+\\.(t|j)s$': 'ts-jest', + }, + collectCoverageFrom: ['**/*.(t|j)s'], + coverageDirectory: '../coverage', + testEnvironment: 'node', + moduleNameMapper: { + '^@/(.*)$': '/$1', + }, +}; diff --git a/apps/send-swap-service/nest-cli.json b/apps/send-swap-service/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/apps/send-swap-service/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/apps/send-swap-service/package.json b/apps/send-swap-service/package.json new file mode 100644 index 0000000..e95306e --- /dev/null +++ b/apps/send-swap-service/package.json @@ -0,0 +1,37 @@ +{ + "name": "@shapeshift/send-swap-service", + "version": "0.0.1", + "description": "Send swap microservice", + "main": "dist/main.js", + "scripts": { + "clean": "rm -rf .turbo dist node_modules", + "build": "yarn run -T nest build", + "start": "yarn run -T nest start", + "start:dev": "yarn run -T nest start --watch", + "start:debug": "yarn run -T nest start --debug --watch", + "start:prod": "node dist/main.js", + "test": "yarn run -T jest", + "test:watch": "yarn run -T jest --watch", + "test:cov": "yarn run -T jest --coverage", + "test:api": "./scripts/test-quotes-api.sh", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate deploy", + "db:push": "prisma db push", + "db:studio": "prisma studio" + }, + "dependencies": { + "@prisma/client": "6.13.0", + "@shapeshift/shared-types": "workspace:*", + "@shapeshift/shared-utils": "workspace:*", + "@shapeshiftoss/hdwallet-core": "^1.62.21", + "@shapeshiftoss/hdwallet-native": "^1.62.21", + "axios": "^1.6.2", + "prisma": "6.13.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "ts-jest": "^29.1.2" + } +} diff --git a/apps/send-swap-service/prisma.config.ts b/apps/send-swap-service/prisma.config.ts new file mode 100644 index 0000000..10743aa --- /dev/null +++ b/apps/send-swap-service/prisma.config.ts @@ -0,0 +1,7 @@ +import 'dotenv/config'; + +import type { PrismaConfig } from 'prisma'; + +export default { + schema: 'prisma/schema.prisma', +} satisfies PrismaConfig; diff --git a/apps/send-swap-service/prisma/schema.prisma b/apps/send-swap-service/prisma/schema.prisma new file mode 100644 index 0000000..0b27c43 --- /dev/null +++ b/apps/send-swap-service/prisma/schema.prisma @@ -0,0 +1,60 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgres" + url = env("DATABASE_URL") +} + +model Quote { + id String @id @default(cuid()) + quoteId String @unique + status QuoteStatus @default(ACTIVE) + + // Assets + sellAsset Json + buyAsset Json + sellAmountCryptoBaseUnit String + expectedBuyAmountCryptoBaseUnit String + + // Addresses + depositAddress String // Service wallet address for deposits + receiveAddress String // User's destination address + + // Swapper info + swapperName String + swapperType SwapperType // DIRECT or SERVICE_WALLET + + // Gas overhead (for service-wallet swappers) + gasOverheadBaseUnit String? + + // Timing + expiresAt DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // Execution + depositTxHash String? + executionTxHash String? + executedAt DateTime? + + @@index([depositAddress]) + @@index([quoteId]) + @@index([status]) + @@map("quotes") +} + +enum QuoteStatus { + ACTIVE + EXPIRED + DEPOSIT_RECEIVED + EXECUTING + COMPLETED + FAILED +} + +enum SwapperType { + DIRECT + SERVICE_WALLET +} diff --git a/apps/send-swap-service/scripts/test-quotes-api.sh b/apps/send-swap-service/scripts/test-quotes-api.sh new file mode 100755 index 0000000..eb7fb75 --- /dev/null +++ b/apps/send-swap-service/scripts/test-quotes-api.sh @@ -0,0 +1,165 @@ +#!/bin/bash +# Test script for Quote Creation API endpoint +# This script tests the POST /quotes endpoint with the correct request body + +set -e + +BASE_URL="${BASE_URL:-http://localhost:3004}" + +echo "=== Testing Quote Creation API ===" +echo "Base URL: ${BASE_URL}" +echo "" + +# Test 1: Create a quote with Chainflip (DIRECT swapper) +echo "Test 1: Creating quote with Chainflip (ETH -> BTC)" +echo "--------------------------------------------------------" + +RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/quotes" \ + -H "Content-Type: application/json" \ + -d '{ + "sellAssetId": "eip155:1/slip44:60", + "buyAssetId": "bip122:000000000019d6689c085ae165831e93/slip44:0", + "sellAmountCryptoBaseUnit": "1000000000000000000", + "receiveAddress": "bc1qtest123", + "swapperName": "Chainflip", + "expectedBuyAmountCryptoBaseUnit": "3000000", + "sellAsset": { "symbol": "ETH", "name": "Ethereum", "precision": 18 }, + "buyAsset": { "symbol": "BTC", "name": "Bitcoin", "precision": 8 } + }') + +HTTP_STATUS=$(echo "$RESPONSE" | tail -n1) +BODY=$(echo "$RESPONSE" | sed '$d') + +echo "HTTP Status: ${HTTP_STATUS}" +echo "Response Body: ${BODY}" | head -c 500 +echo "" + +if [ "$HTTP_STATUS" -eq 201 ]; then + echo "PASS: Quote created successfully (201)" +else + echo "FAIL: Expected 201, got ${HTTP_STATUS}" + exit 1 +fi + +echo "" + +# Test 2: Create a quote with THORChain (SERVICE_WALLET swapper) +echo "Test 2: Creating quote with THORChain (ETH -> BTC)" +echo "--------------------------------------------------------" + +RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/quotes" \ + -H "Content-Type: application/json" \ + -d '{ + "sellAssetId": "eip155:1/slip44:60", + "buyAssetId": "bip122:000000000019d6689c085ae165831e93/slip44:0", + "sellAmountCryptoBaseUnit": "1000000000000000000", + "receiveAddress": "bc1qtest123", + "swapperName": "THORChain", + "expectedBuyAmountCryptoBaseUnit": "2900000", + "sellAsset": { "symbol": "ETH", "name": "Ethereum", "precision": 18 }, + "buyAsset": { "symbol": "BTC", "name": "Bitcoin", "precision": 8 } + }') + +HTTP_STATUS=$(echo "$RESPONSE" | tail -n1) +BODY=$(echo "$RESPONSE" | sed '$d') + +echo "HTTP Status: ${HTTP_STATUS}" +echo "Response Body: ${BODY}" | head -c 500 +echo "" + +if [ "$HTTP_STATUS" -eq 201 ]; then + echo "PASS: Quote created successfully (201)" + + # Verify SERVICE_WALLET quote has gas overhead + if echo "$BODY" | grep -q "gasOverheadBaseUnit"; then + echo "PASS: Response includes gasOverheadBaseUnit" + fi +else + echo "FAIL: Expected 201, got ${HTTP_STATUS}" + exit 1 +fi + +echo "" + +# Test 3: Get a quote by ID +echo "Test 3: Getting quote by ID" +echo "--------------------------------------------------------" + +# Extract quote ID from previous response +QUOTE_ID=$(echo "$BODY" | grep -o '"quoteId":"[^"]*"' | cut -d'"' -f4) + +if [ -n "$QUOTE_ID" ]; then + RESPONSE=$(curl -s -w "\n%{http_code}" -X GET "${BASE_URL}/quotes/${QUOTE_ID}") + + HTTP_STATUS=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + echo "HTTP Status: ${HTTP_STATUS}" + echo "Response Body: ${BODY}" | head -c 500 + echo "" + + if [ "$HTTP_STATUS" -eq 200 ]; then + echo "PASS: Quote retrieved successfully (200)" + else + echo "FAIL: Expected 200, got ${HTTP_STATUS}" + exit 1 + fi +else + echo "SKIP: Could not extract quote ID from previous response" +fi + +echo "" + +# Test 4: Verify qrData in response +echo "Test 4: Verifying qrData format" +echo "--------------------------------------------------------" + +RESPONSE=$(curl -s -X POST "${BASE_URL}/quotes" \ + -H "Content-Type: application/json" \ + -d '{ + "sellAssetId": "eip155:1/slip44:60", + "buyAssetId": "bip122:000000000019d6689c085ae165831e93/slip44:0", + "sellAmountCryptoBaseUnit": "2000000000000000000", + "receiveAddress": "bc1qtest456", + "swapperName": "Chainflip", + "expectedBuyAmountCryptoBaseUnit": "6000000", + "sellAsset": { "symbol": "ETH", "name": "Ethereum" }, + "buyAsset": { "symbol": "BTC", "name": "Bitcoin" } + }') + +if echo "$RESPONSE" | grep -q '"qrData":"ethereum:'; then + echo "PASS: qrData contains ethereum: URI scheme" +else + echo "FAIL: qrData missing or incorrect format" + exit 1 +fi + +echo "" + +# Test 5: Invalid swapper should fail +echo "Test 5: Testing invalid swapper rejection" +echo "--------------------------------------------------------" + +RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/quotes" \ + -H "Content-Type: application/json" \ + -d '{ + "sellAssetId": "eip155:1/slip44:60", + "buyAssetId": "bip122:000000000019d6689c085ae165831e93/slip44:0", + "sellAmountCryptoBaseUnit": "1000000000000000000", + "receiveAddress": "bc1qtest123", + "swapperName": "Zrx", + "expectedBuyAmountCryptoBaseUnit": "3000000", + "sellAsset": { "symbol": "ETH" }, + "buyAsset": { "symbol": "BTC" } + }') + +HTTP_STATUS=$(echo "$RESPONSE" | tail -n1) + +if [ "$HTTP_STATUS" -eq 400 ]; then + echo "PASS: Invalid swapper correctly rejected (400)" +else + echo "FAIL: Expected 400 for invalid swapper, got ${HTTP_STATUS}" +fi + +echo "" +echo "=== All Tests Completed ===" diff --git a/apps/send-swap-service/scripts/verify-startup.sh b/apps/send-swap-service/scripts/verify-startup.sh new file mode 100755 index 0000000..32313d8 --- /dev/null +++ b/apps/send-swap-service/scripts/verify-startup.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Verification Script: Service Startup with All Chain Wallets +# +# This script helps verify that the send-swap-service starts successfully +# with all chain wallets properly initialized. +# +# Prerequisites: +# 1. Set MNEMONIC environment variable in the root .env file +# 2. Dependencies installed (yarn install from monorepo root) +# 3. Database available (docker compose up send-swap-db) +# +# Expected Startup Logs (in order): +# =============================== +# 1. "Initializing wallets..." +# 2. "Initializing HD wallet..." +# 3. "HD wallet initialized successfully (device: xxx)" +# 4. "Verifying address generation for all chain types..." +# 5. "EVM deposit address: 0x..." (Ethereum/EVM chains) +# 6. "BTC deposit address: bc1..." (Bitcoin - SegWit format) +# 7. "ATOM deposit address: cosmos1..." (Cosmos SDK chains) +# 8. "Solana deposit address: ..." (Solana) +# 9. "Address generation verified for all chain types" +# 10. "All wallets initialized successfully" +# 11. "Send-swap service is running on: http://localhost:3004" +# +# Expected Cron Job Logs (every 30 seconds): +# ========================================== +# - "[DepositMonitorService] Starting deposit check..." +# - "[DepositMonitorService] No quotes to monitor" (if no active quotes) +# OR "[DepositMonitorService] Found X quotes to monitor for deposits" +# - "[DepositMonitorService] Deposit check completed" +# +# These logs confirm the deposit monitoring cron job is running correctly. +# +# Verification Checklist: +# ====================== +# [ ] Service starts without errors +# [ ] EVM address generated (0x prefix, 42 characters) +# [ ] BTC address generated (bc1 prefix for SegWit) +# [ ] ATOM address generated (cosmos1 prefix) +# [ ] Solana address generated (base58 encoded) +# [ ] Health endpoint responds: curl http://localhost:3004/health +# [ ] Cron job logs appear every 30 seconds (Starting deposit check...) +# [ ] Quote monitoring status logged (No quotes or Found X quotes) +# + +set -e + +echo "==========================================" +echo "Send-Swap-Service Startup Verification" +echo "==========================================" +echo "" + +# Check if we're in the right directory +if [[ ! -f "package.json" ]] || [[ ! -d "src/wallet" ]]; then + echo "Error: Please run this script from apps/send-swap-service directory" + exit 1 +fi + +# Check for MNEMONIC in root .env +if [[ -f "../../.env" ]]; then + if grep -q "^MNEMONIC=" ../../.env && ! grep -q "^MNEMONIC=$" ../../.env; then + echo "[OK] MNEMONIC found in root .env" + else + echo "[WARNING] MNEMONIC is not set or empty in ../../.env" + echo " Please set MNEMONIC=" + fi +else + echo "[WARNING] Root .env file not found" + echo " Create ../../.env with MNEMONIC=" +fi + +echo "" +echo "Starting service with wallet verification..." +echo "Press Ctrl+C to stop" +echo "" +echo "Look for these key log messages:" +echo " - 'HD wallet initialized successfully'" +echo " - 'EVM deposit address:'" +echo " - 'BTC deposit address:'" +echo " - 'ATOM deposit address:'" +echo " - 'Solana deposit address:'" +echo " - 'All wallets initialized successfully'" +echo "" +echo "After ~30 seconds, look for cron job logs:" +echo " - 'Starting deposit check...'" +echo " - 'No quotes to monitor' or 'Found X quotes to monitor'" +echo " - 'Deposit check completed'" +echo "" +echo "==========================================" +echo "" + +# Start the service +yarn start:dev diff --git a/apps/send-swap-service/src/app.module.ts b/apps/send-swap-service/src/app.module.ts new file mode 100644 index 0000000..485dd64 --- /dev/null +++ b/apps/send-swap-service/src/app.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { ScheduleModule } from '@nestjs/schedule'; +import { HttpModule } from '@nestjs/axios'; +import { ConfigModule } from '@nestjs/config'; +import { PrismaService } from './prisma/prisma.service'; +import { WalletModule } from './wallet/wallet.module'; +import { QuotesModule } from './quotes/quotes.module'; +import { MonitoringModule } from './monitoring/monitoring.module'; +import { SwappersModule } from './swappers/swappers.module'; + +@Module({ + imports: [ + ScheduleModule.forRoot(), + HttpModule, + ConfigModule.forRoot({ + envFilePath: '../../.env', + }), + WalletModule, + QuotesModule, + MonitoringModule, + SwappersModule, + ], + controllers: [], + providers: [PrismaService], +}) +export class AppModule {} diff --git a/apps/send-swap-service/src/execution/execution.module.ts b/apps/send-swap-service/src/execution/execution.module.ts new file mode 100644 index 0000000..8f12d44 --- /dev/null +++ b/apps/send-swap-service/src/execution/execution.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common'; +import { HttpModule } from '@nestjs/axios'; +import { ConfigModule } from '@nestjs/config'; +import { SwapExecutorService } from './swap-executor.service'; + +/** + * ExecutionModule provides swap execution functionality for send-swap operations. + * + * This module: + * - Routes swap execution based on swapper type (DIRECT vs SERVICE_WALLET) + * - Handles swap status checking for DIRECT swappers + * - Initiates swap transactions for SERVICE_WALLET swappers + * - Provides retry functionality for failed swaps + * + * Dependencies: + * - HttpModule: HTTP client for swapper API queries + * - ConfigModule: Access to swapper API URLs and credentials + */ +@Module({ + imports: [ + HttpModule, + ConfigModule, + ], + providers: [SwapExecutorService], + exports: [SwapExecutorService], +}) +export class ExecutionModule {} diff --git a/apps/send-swap-service/src/execution/swap-executor.service.ts b/apps/send-swap-service/src/execution/swap-executor.service.ts new file mode 100644 index 0000000..d07f1c9 --- /dev/null +++ b/apps/send-swap-service/src/execution/swap-executor.service.ts @@ -0,0 +1,558 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { HttpService } from '@nestjs/axios'; +import { Quote, QuoteStatus } from '@prisma/client'; +import { firstValueFrom } from 'rxjs'; +import { SwapperType, SwapperName } from '../swappers/swapper.types'; +import { isDirectSwapper, isServiceWalletSwapper } from '../swappers/swapper-config'; + +/** + * Execution result returned by swap executors + */ +export interface SwapExecutionResult { + success: boolean; + executionTxHash?: string; + error?: string; + swapperName: string; + swapperType: SwapperType; + metadata?: Record; +} + +/** + * Chainflip deposit channel status from API + */ +interface ChainflipChannelStatus { + status: string; + swapId?: string; + depositAmount?: string; + expectedOutputAmount?: string; + outputTxHash?: string; +} + +/** + * NEAR Intents swap status from API + */ +interface NearIntentsSwapStatus { + status: string; + swapId?: string; + outputTxHash?: string; +} + +/** + * SwapExecutorService handles the execution of swaps after deposits are detected. + * + * This service differentiates between two execution flows: + * + * 1. DIRECT swappers (Chainflip, NEAR Intents): + * - The deposit address belongs to the swapper's infrastructure + * - Swap execution happens automatically on their end + * - We only need to monitor the swap status + * + * 2. SERVICE_WALLET swappers (THORChain, Jupiter, etc.): + * - The deposit goes to our service wallet + * - We must initiate the swap transaction ourselves + * - Requires signing and broadcasting via the wallet manager + */ +@Injectable() +export class SwapExecutorService { + private readonly logger = new Logger(SwapExecutorService.name); + + constructor( + private configService: ConfigService, + private httpService: HttpService, + ) {} + + /** + * Execute a swap for a quote that has received a deposit. + * + * Routes to the appropriate execution flow based on swapper type: + * - DIRECT: Monitor swapper for completion (they handle execution) + * - SERVICE_WALLET: Initiate and broadcast swap transaction + * + * @param quote - The quote with confirmed deposit + * @returns Execution result with success status and tx hash + */ + async executeSwap(quote: Quote): Promise { + const swapperName = quote.swapperName as SwapperName; + const swapperType = quote.swapperType === 'DIRECT' ? SwapperType.DIRECT : SwapperType.SERVICE_WALLET; + + this.logger.log( + `Executing swap for quote ${quote.quoteId} via ${swapperName} (${swapperType})`, + ); + + try { + // Route to appropriate execution flow based on swapper type + if (isDirectSwapper(swapperName)) { + return this.executeDirectSwap(quote, swapperName); + } else if (isServiceWalletSwapper(swapperName)) { + return this.executeServiceWalletSwap(quote, swapperName); + } else { + // Unknown swapper - should not happen if validation is correct + this.logger.error(`Unknown swapper type for ${swapperName}`); + return { + success: false, + error: `Unsupported swapper: ${swapperName}`, + swapperName, + swapperType, + }; + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.logger.error( + `Swap execution failed for quote ${quote.quoteId}: ${errorMessage}`, + error instanceof Error ? error.stack : undefined, + ); + + return { + success: false, + error: errorMessage, + swapperName, + swapperType, + }; + } + } + + /** + * Execute a swap using a DIRECT swapper. + * + * For DIRECT swappers (Chainflip, NEAR Intents), the swap execution + * is handled by the swapper infrastructure. When a user deposits to + * the swapper's deposit address, the swap is automatically initiated. + * + * Our responsibility is to: + * 1. Check the swap status on the swapper's API + * 2. Return the execution tx hash once complete + * + * @param quote - The quote with confirmed deposit + * @param swapperName - The direct swapper name + * @returns Execution result + */ + private async executeDirectSwap( + quote: Quote, + swapperName: SwapperName, + ): Promise { + this.logger.log( + `DIRECT swap for quote ${quote.quoteId}: swapper ${swapperName} handles execution`, + ); + + switch (swapperName) { + case SwapperName.Chainflip: + return this.checkChainflipSwapStatus(quote); + + case SwapperName.NearIntents: + return this.checkNearIntentsSwapStatus(quote); + + default: + return { + success: false, + error: `Unsupported DIRECT swapper: ${swapperName}`, + swapperName, + swapperType: SwapperType.DIRECT, + }; + } + } + + /** + * Check Chainflip swap status. + * + * Chainflip automatically executes swaps when deposits are received + * at their deposit channel addresses. We query their status API to + * get the output transaction hash. + * + * @param quote - The quote to check + * @returns Execution result with status + */ + private async checkChainflipSwapStatus(quote: Quote): Promise { + const chainflipApiUrl = this.configService.get('CHAINFLIP_API_URL'); + + if (!chainflipApiUrl) { + this.logger.warn('CHAINFLIP_API_URL not configured'); + // For POC, return pending status + return { + success: false, + error: 'Chainflip API URL not configured - swap may still be processing', + swapperName: SwapperName.Chainflip, + swapperType: SwapperType.DIRECT, + metadata: { pendingExternalCheck: true }, + }; + } + + try { + // Query Chainflip status endpoint using deposit channel/address + // Note: In production, we'd track the deposit channel ID from quote creation + const response = await firstValueFrom( + this.httpService.get( + `${chainflipApiUrl}/swaps/status`, + { + params: { + depositAddress: quote.depositAddress, + }, + timeout: 15000, + }, + ), + ); + + const status = response.data; + + if (status.status === 'complete' && status.outputTxHash) { + this.logger.log( + `Chainflip swap complete for quote ${quote.quoteId}: ${status.outputTxHash}`, + ); + + return { + success: true, + executionTxHash: status.outputTxHash, + swapperName: SwapperName.Chainflip, + swapperType: SwapperType.DIRECT, + metadata: { + swapId: status.swapId, + depositAmount: status.depositAmount, + outputAmount: status.expectedOutputAmount, + }, + }; + } + + // Swap still in progress + this.logger.debug( + `Chainflip swap pending for quote ${quote.quoteId}: status=${status.status}`, + ); + + return { + success: false, + error: `Swap still processing: ${status.status}`, + swapperName: SwapperName.Chainflip, + swapperType: SwapperType.DIRECT, + metadata: { status: status.status, swapId: status.swapId }, + }; + } catch (error) { + this.logger.debug( + `Could not check Chainflip status for quote ${quote.quoteId}`, + error, + ); + + return { + success: false, + error: 'Unable to check Chainflip swap status', + swapperName: SwapperName.Chainflip, + swapperType: SwapperType.DIRECT, + metadata: { pendingExternalCheck: true }, + }; + } + } + + /** + * Check NEAR Intents swap status. + * + * NEAR Intents executes swaps via their 1Click API when deposits + * are received. We query their status endpoint for completion. + * + * @param quote - The quote to check + * @returns Execution result with status + */ + private async checkNearIntentsSwapStatus(quote: Quote): Promise { + const nearIntentsApiUrl = this.configService.get('NEAR_INTENTS_API_URL'); + + if (!nearIntentsApiUrl) { + this.logger.warn('NEAR_INTENTS_API_URL not configured'); + return { + success: false, + error: 'NEAR Intents API URL not configured - swap may still be processing', + swapperName: SwapperName.NearIntents, + swapperType: SwapperType.DIRECT, + metadata: { pendingExternalCheck: true }, + }; + } + + try { + const jwtToken = this.configService.get('NEAR_INTENTS_API_KEY'); + + const response = await firstValueFrom( + this.httpService.get( + `${nearIntentsApiUrl}/swap/status`, + { + params: { + depositAddress: quote.depositAddress, + }, + headers: jwtToken ? { Authorization: `Bearer ${jwtToken}` } : {}, + timeout: 15000, + }, + ), + ); + + const status = response.data; + + if (status.status === 'complete' && status.outputTxHash) { + this.logger.log( + `NEAR Intents swap complete for quote ${quote.quoteId}: ${status.outputTxHash}`, + ); + + return { + success: true, + executionTxHash: status.outputTxHash, + swapperName: SwapperName.NearIntents, + swapperType: SwapperType.DIRECT, + metadata: { swapId: status.swapId }, + }; + } + + return { + success: false, + error: `Swap still processing: ${status.status}`, + swapperName: SwapperName.NearIntents, + swapperType: SwapperType.DIRECT, + metadata: { status: status.status, swapId: status.swapId }, + }; + } catch (error) { + this.logger.debug( + `Could not check NEAR Intents status for quote ${quote.quoteId}`, + error, + ); + + return { + success: false, + error: 'Unable to check NEAR Intents swap status', + swapperName: SwapperName.NearIntents, + swapperType: SwapperType.DIRECT, + metadata: { pendingExternalCheck: true }, + }; + } + } + + /** + * Execute a swap using a SERVICE_WALLET swapper. + * + * For SERVICE_WALLET swappers, we must: + * 1. Build the swap transaction (memo, calldata, etc.) + * 2. Sign it with our service wallet + * 3. Broadcast to the network + * 4. Return the execution tx hash + * + * @param quote - The quote with confirmed deposit + * @param swapperName - The service wallet swapper name + * @returns Execution result + */ + private async executeServiceWalletSwap( + quote: Quote, + swapperName: SwapperName, + ): Promise { + this.logger.log( + `SERVICE_WALLET swap for quote ${quote.quoteId}: initiating ${swapperName} execution`, + ); + + switch (swapperName) { + case SwapperName.THORChain: + return this.executeThorChainSwap(quote); + + case SwapperName.Jupiter: + return this.executeJupiterSwap(quote); + + case SwapperName.Relay: + return this.executeRelaySwap(quote); + + case SwapperName.Mayachain: + return this.executeMayachainSwap(quote); + + case SwapperName.ButterSwap: + return this.executeButterSwapSwap(quote); + + case SwapperName.Bebop: + return this.executeBebopSwap(quote); + + default: + return { + success: false, + error: `Unsupported SERVICE_WALLET swapper: ${swapperName}`, + swapperName, + swapperType: SwapperType.SERVICE_WALLET, + }; + } + } + + /** + * Execute a THORChain swap. + * + * THORChain uses memo-based routing. We need to: + * 1. Get the inbound address from THORChain API + * 2. Build a transaction with the swap memo + * 3. Sign and broadcast + * + * Memo format: =:ASSET.ASSET:destination_address:limit + */ + private async executeThorChainSwap(quote: Quote): Promise { + this.logger.log(`Executing THORChain swap for quote ${quote.quoteId}`); + + // TODO: Implement THORChain swap execution + // For POC, return placeholder indicating this needs implementation + // In production: + // 1. Query THORChain /thorchain/inbound_addresses for inbound vault + // 2. Build transaction with swap memo + // 3. Sign with wallet manager + // 4. Broadcast to network + // 5. Return tx hash + + return { + success: false, + error: 'THORChain swap execution not yet implemented', + swapperName: SwapperName.THORChain, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Execute a Jupiter swap (Solana only). + * + * Jupiter provides swap routes for Solana tokens. + * We need to: + * 1. Get swap route from Jupiter API + * 2. Build and sign the Solana transaction + * 3. Broadcast to Solana network + */ + private async executeJupiterSwap(quote: Quote): Promise { + this.logger.log(`Executing Jupiter swap for quote ${quote.quoteId}`); + + // TODO: Implement Jupiter swap execution + // For POC, return placeholder + + return { + success: false, + error: 'Jupiter swap execution not yet implemented', + swapperName: SwapperName.Jupiter, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Execute a Relay swap (cross-chain bridging). + */ + private async executeRelaySwap(quote: Quote): Promise { + this.logger.log(`Executing Relay swap for quote ${quote.quoteId}`); + + // TODO: Implement Relay swap execution + + return { + success: false, + error: 'Relay swap execution not yet implemented', + swapperName: SwapperName.Relay, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Execute a Mayachain swap. + * + * Similar to THORChain, uses memo-based routing. + */ + private async executeMayachainSwap(quote: Quote): Promise { + this.logger.log(`Executing Mayachain swap for quote ${quote.quoteId}`); + + // TODO: Implement Mayachain swap execution + + return { + success: false, + error: 'Mayachain swap execution not yet implemented', + swapperName: SwapperName.Mayachain, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Execute a ButterSwap swap. + */ + private async executeButterSwapSwap(quote: Quote): Promise { + this.logger.log(`Executing ButterSwap swap for quote ${quote.quoteId}`); + + // TODO: Implement ButterSwap swap execution + + return { + success: false, + error: 'ButterSwap swap execution not yet implemented', + swapperName: SwapperName.ButterSwap, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Execute a Bebop swap (intent-based). + */ + private async executeBebopSwap(quote: Quote): Promise { + this.logger.log(`Executing Bebop swap for quote ${quote.quoteId}`); + + // TODO: Implement Bebop swap execution + + return { + success: false, + error: 'Bebop swap execution not yet implemented', + swapperName: SwapperName.Bebop, + swapperType: SwapperType.SERVICE_WALLET, + metadata: { + receiveAddress: quote.receiveAddress, + sellAmount: quote.sellAmountCryptoBaseUnit, + needsImplementation: true, + }, + }; + } + + /** + * Check if a swap is still pending execution. + * + * For DIRECT swappers, this queries the swapper's status API. + * For SERVICE_WALLET swappers, this checks the blockchain for tx confirmation. + * + * @param quote - The quote to check + * @returns True if swap is still pending + */ + async isSwapPending(quote: Quote): Promise { + // If quote already has execution tx hash, it's not pending + if (quote.executionTxHash) { + return false; + } + + // If quote status indicates completion or failure, it's not pending + if ( + quote.status === QuoteStatus.COMPLETED || + quote.status === QuoteStatus.FAILED || + quote.status === QuoteStatus.EXPIRED + ) { + return false; + } + + // Quote is still pending + return true; + } + + /** + * Retry a failed swap execution. + * + * @param quote - The quote to retry + * @returns Execution result from retry attempt + */ + async retrySwap(quote: Quote): Promise { + this.logger.log(`Retrying swap execution for quote ${quote.quoteId}`); + return this.executeSwap(quote); + } +} diff --git a/apps/send-swap-service/src/main.ts b/apps/send-swap-service/src/main.ts new file mode 100644 index 0000000..a38ab79 --- /dev/null +++ b/apps/send-swap-service/src/main.ts @@ -0,0 +1,32 @@ +import { NestFactory } from '@nestjs/core'; +import type { Response } from 'express'; +import { AppModule } from './app.module'; +import { WalletInitService } from './wallet/wallet-init.service'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + // Initialize wallets before starting HTTP listener + const walletInitService = app.get(WalletInitService); + await walletInitService.initializeWallets(); + + // Enable CORS + app.enableCors({ + origin: process.env.ALLOWED_ORIGINS?.split(',') || [ + 'http://localhost:3000', + /\.shapeshift\.com$/, + ], + credentials: true, + }); + + app.getHttpAdapter().get('/health', (_, res: Response) => { + res.status(200).json({ status: 'ok' }); + }); + + const port = process.env.PORT || 3004; + await app.listen(port); + + console.log(`Send-swap service is running on: http://localhost:${port}`); +} + +bootstrap(); diff --git a/apps/send-swap-service/src/monitoring/deposit-monitor.service.ts b/apps/send-swap-service/src/monitoring/deposit-monitor.service.ts new file mode 100644 index 0000000..1166126 --- /dev/null +++ b/apps/send-swap-service/src/monitoring/deposit-monitor.service.ts @@ -0,0 +1,817 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { ConfigService } from '@nestjs/config'; +import { HttpService } from '@nestjs/axios'; +import { QuotesService } from '../quotes/quotes.service'; +import { SwapExecutorService, SwapExecutionResult } from '../execution/swap-executor.service'; +import { Quote, QuoteStatus } from '@prisma/client'; +import { firstValueFrom } from 'rxjs'; + +/** + * Chain family types for deposit detection + */ +type ChainFamily = 'EVM' | 'UTXO' | 'COSMOS' | 'SOLANA' | 'UNKNOWN'; + +/** + * Result of a deposit check for a specific address + */ +interface DepositCheckResult { + found: boolean; + txHash?: string; + amount?: string; + confirmations?: number; + isPartial?: boolean; + isOverDeposit?: boolean; +} + +/** + * Transaction info from blockchain query + */ +interface TransactionInfo { + txHash: string; + amount: string; + confirmations: number; + timestamp: number; +} + +/** + * Minimum confirmations required per chain family + */ +const MIN_CONFIRMATIONS: Record = { + EVM: 12, // ~3 minutes on Ethereum + UTXO: 3, // ~30 minutes for BTC + COSMOS: 1, // Near instant finality + SOLANA: 32, // Solana finality + UNKNOWN: 1, +}; + +/** + * Tolerance percentage for amount matching (allows for minor fee variations) + * Set to 0.1% tolerance + */ +const AMOUNT_TOLERANCE_PERCENT = 0.1; + +/** + * DepositMonitorService monitors deposit addresses for incoming funds. + * + * This service runs a cron job every 30 seconds to: + * - Fetch active quotes that need deposit monitoring + * - Check deposit addresses for incoming transactions + * - Update quote status when deposits are detected + * + * The monitoring process is chain-aware and handles different + * blockchain families (EVM, UTXO, Cosmos, Solana) appropriately. + */ +@Injectable() +export class DepositMonitorService { + private readonly logger = new Logger(DepositMonitorService.name); + + constructor( + private quotesService: QuotesService, + private configService: ConfigService, + private httpService: HttpService, + private swapExecutor: SwapExecutorService, + ) {} + + /** + * Check for deposits on active quote deposit addresses. + * Runs every 30 seconds via cron scheduler. + * + * This method: + * 1. Fetches all active quotes that need monitoring + * 2. Checks each deposit address for incoming transactions + * 3. Updates quote status when deposits are detected + * 4. Handles errors gracefully to prevent job failures + */ + @Cron(CronExpression.EVERY_30_SECONDS) + async checkDeposits(): Promise { + try { + this.logger.log('Starting deposit check...'); + + // Get quotes that need deposit monitoring + const quotesToMonitor = await this.quotesService.getQuotesToMonitor(); + + if (quotesToMonitor.length === 0) { + this.logger.log('No quotes to monitor'); + return; + } + + this.logger.log(`Found ${quotesToMonitor.length} quotes to monitor for deposits`); + + // Process each quote for deposit checking + for (const quote of quotesToMonitor) { + try { + // Only check ACTIVE quotes for new deposits + if (quote.status === QuoteStatus.ACTIVE) { + await this.checkDepositForQuote(quote); + } + // For DEPOSIT_RECEIVED quotes, check if swap execution is complete + // This is mainly for DIRECT swappers where execution happens externally + else if (quote.status === QuoteStatus.DEPOSIT_RECEIVED) { + await this.checkPendingSwapExecution(quote); + } + } catch (error) { + this.logger.error( + `Failed to check deposit for quote ${quote.quoteId}:`, + error, + ); + } + } + + this.logger.log('Deposit check completed'); + } catch (error) { + this.logger.error('Failed to check deposits:', error); + } + } + + /** + * Check deposit for a specific quote. + * Matches deposits by address AND amount, with chain-aware confirmation handling. + * + * @param quote - The quote to check for deposits + */ + private async checkDepositForQuote(quote: Quote): Promise { + const { quoteId, depositAddress, sellAmountCryptoBaseUnit } = quote; + + this.logger.debug( + `Checking deposit for quote ${quoteId} at address ${depositAddress} for amount ${sellAmountCryptoBaseUnit}`, + ); + + // Determine chain family from deposit address + const chainFamily = this.getChainFamily(depositAddress); + if (chainFamily === 'UNKNOWN') { + this.logger.warn(`Unknown chain family for address ${depositAddress}`); + return; + } + + // Check for deposit matching address + amount + const depositResult = await this.checkDepositReceived( + depositAddress, + sellAmountCryptoBaseUnit, + chainFamily, + ); + + if (depositResult.found && depositResult.txHash) { + // Verify sufficient confirmations + const minConfirmations = MIN_CONFIRMATIONS[chainFamily]; + const hasEnoughConfirmations = (depositResult.confirmations ?? 0) >= minConfirmations; + + if (!hasEnoughConfirmations) { + this.logger.debug( + `Deposit found for quote ${quoteId} but needs more confirmations ` + + `(${depositResult.confirmations}/${minConfirmations})`, + ); + return; + } + + // Handle partial deposits - log but don't process yet + if (depositResult.isPartial) { + this.logger.warn( + `Partial deposit detected for quote ${quoteId}: ` + + `received ${depositResult.amount}, expected ${sellAmountCryptoBaseUnit}`, + ); + // TODO: In future, implement partial deposit handling (wait for more or refund) + return; + } + + // Handle over-deposits - process but log for potential refund + if (depositResult.isOverDeposit) { + this.logger.warn( + `Over-deposit detected for quote ${quoteId}: ` + + `received ${depositResult.amount}, expected ${sellAmountCryptoBaseUnit}`, + ); + // TODO: In future, implement excess amount refund logic + } + + // Acquire lock on quote before processing to prevent race conditions + // Uses optimistic locking: atomically transitions ACTIVE → EXECUTING + const lockedQuote = await this.quotesService.lockQuote(quoteId); + + if (!lockedQuote) { + // Quote is already being processed by another iteration or status changed + this.logger.debug( + `Skipping deposit for quote ${quoteId}: could not acquire lock (already processing)`, + ); + return; + } + + // Mark deposit as received (quote is now locked in EXECUTING state) + this.logger.log( + `Deposit confirmed for quote ${quoteId}: ${depositResult.txHash} ` + + `(${depositResult.confirmations} confirmations)`, + ); + + await this.quotesService.markDepositReceived(quoteId, depositResult.txHash); + + // Execute the swap + await this.executeSwapForQuote(lockedQuote, depositResult.txHash); + } + } + + /** + * Execute swap for a quote after deposit is confirmed. + * + * Routes to the appropriate execution flow based on swapper type: + * - DIRECT: Swapper handles execution, we monitor for completion + * - SERVICE_WALLET: We initiate and broadcast the swap transaction + * + * @param quote - The quote with confirmed deposit + * @param depositTxHash - The deposit transaction hash + */ + private async executeSwapForQuote(quote: Quote, depositTxHash: string): Promise { + const { quoteId } = quote; + + try { + this.logger.log(`Initiating swap execution for quote ${quoteId}`); + + // Execute the swap via appropriate swapper flow + const executionResult = await this.swapExecutor.executeSwap(quote); + + // Update quote status based on execution result + await this.updateQuoteAfterExecution(quoteId, executionResult); + } catch (error) { + this.logger.error( + `Swap execution failed for quote ${quoteId}:`, + error, + ); + + // Mark quote as failed + await this.quotesService.markFailed(quoteId); + } + } + + /** + * Check pending swap execution for DEPOSIT_RECEIVED quotes. + * + * For DIRECT swappers, the swap execution happens on the swapper's infrastructure. + * This method re-checks the swap status to see if it has completed. + * + * @param quote - The quote with DEPOSIT_RECEIVED status + */ + private async checkPendingSwapExecution(quote: Quote): Promise { + const { quoteId } = quote; + + this.logger.debug(`Checking pending swap execution for quote ${quoteId}`); + + try { + // Check if swap is still pending + const isPending = await this.swapExecutor.isSwapPending(quote); + + if (!isPending) { + // Swap already completed or failed + return; + } + + // Re-execute/check the swap (for DIRECT swappers, this checks status) + const executionResult = await this.swapExecutor.executeSwap(quote); + + // Update quote status based on result + await this.updateQuoteAfterExecution(quoteId, executionResult); + } catch (error) { + this.logger.error( + `Failed to check pending swap execution for quote ${quoteId}:`, + error, + ); + } + } + + /** + * Update quote status after swap execution attempt. + * + * This method handles the status transition after swap execution: + * - Success: Updates status to COMPLETED with execution tx hash + * - Failure: Updates status to FAILED with error logging + * - Pending: Keeps current status for retry in next monitoring cycle + * + * Status transitions handled: + * - DEPOSIT_RECEIVED → COMPLETED (successful swap) + * - DEPOSIT_RECEIVED → FAILED (swap error) + * - DEPOSIT_RECEIVED → DEPOSIT_RECEIVED (pending external check) + * + * @param quoteId - The quote identifier + * @param result - The execution result from SwapExecutorService + */ + private async updateQuoteAfterExecution( + quoteId: string, + result: SwapExecutionResult, + ): Promise { + if (result.success && result.executionTxHash) { + // Swap completed successfully - transition to COMPLETED status + await this.updateQuoteStatus(quoteId, 'COMPLETED', result.executionTxHash); + } else if (result.metadata?.pendingExternalCheck) { + // For DIRECT swappers, the swap may still be processing on their end + // Keep quote in DEPOSIT_RECEIVED status for next monitoring cycle + this.logger.log( + `Swap pending external check for quote ${quoteId} (${result.swapperName})`, + ); + // Quote remains in DEPOSIT_RECEIVED - will be checked again next cycle + } else if (result.metadata?.needsImplementation) { + // SERVICE_WALLET swapper not yet implemented + this.logger.warn( + `Swap execution not implemented for ${result.swapperName}, quote ${quoteId}`, + ); + // For POC, keep in DEPOSIT_RECEIVED for manual handling + } else { + // Swap failed - transition to FAILED status + await this.updateQuoteStatus(quoteId, 'FAILED', undefined, result.error); + } + } + + /** + * Update quote status to COMPLETED or FAILED. + * + * Centralizes status update logic with consistent logging and error handling. + * Only handles terminal states (COMPLETED, FAILED) as these are final outcomes. + * + * @param quoteId - The quote identifier + * @param status - The target status ('COMPLETED' or 'FAILED') + * @param executionTxHash - Transaction hash for COMPLETED status + * @param errorMessage - Error message for FAILED status + */ + private async updateQuoteStatus( + quoteId: string, + status: 'COMPLETED' | 'FAILED', + executionTxHash?: string, + errorMessage?: string, + ): Promise { + try { + if (status === 'COMPLETED' && executionTxHash) { + this.logger.log( + `Quote ${quoteId} status → COMPLETED: ${executionTxHash}`, + ); + await this.quotesService.markCompleted(quoteId, executionTxHash); + } else if (status === 'FAILED') { + this.logger.error( + `Quote ${quoteId} status → FAILED: ${errorMessage || 'Unknown error'}`, + ); + await this.quotesService.markFailed(quoteId); + } + } catch (error) { + this.logger.error( + `Failed to update quote ${quoteId} status to ${status}:`, + error, + ); + // Rethrow to ensure caller is aware of the failure + throw error; + } + } + + /** + * Check if a deposit has been received at the given address matching the expected amount. + * + * This method queries the appropriate blockchain based on the chain family. + * + * @param depositAddress - The deposit address to check + * @param expectedAmount - The expected deposit amount in base units + * @param chainFamily - The blockchain family (EVM, UTXO, COSMOS, SOLANA) + * @returns Deposit check result with match status and transaction details + */ + async checkDepositReceived( + depositAddress: string, + expectedAmount: string, + chainFamily: ChainFamily, + ): Promise { + try { + // Get transactions for the deposit address + const transactions = await this.getAddressTransactions(depositAddress, chainFamily); + + if (transactions.length === 0) { + return { found: false }; + } + + // Find a transaction matching the expected amount + for (const tx of transactions) { + const matchResult = this.matchAmount(tx.amount, expectedAmount); + + if (matchResult.matches) { + return { + found: true, + txHash: tx.txHash, + amount: tx.amount, + confirmations: tx.confirmations, + isPartial: matchResult.isPartial, + isOverDeposit: matchResult.isOverDeposit, + }; + } + } + + // Check if there's a partial deposit (received less than expected) + const totalReceived = transactions.reduce( + (sum, tx) => BigInt(sum) + BigInt(tx.amount), + BigInt(0), + ); + + const expectedBigInt = BigInt(expectedAmount); + if (totalReceived > BigInt(0) && totalReceived < expectedBigInt) { + // Return the largest transaction as a partial indicator + const largestTx = transactions.reduce((max, tx) => + BigInt(tx.amount) > BigInt(max.amount) ? tx : max, + ); + + return { + found: true, + txHash: largestTx.txHash, + amount: totalReceived.toString(), + confirmations: largestTx.confirmations, + isPartial: true, + }; + } + + return { found: false }; + } catch (error) { + this.logger.error( + `Failed to check deposit at ${depositAddress}:`, + error, + ); + return { found: false }; + } + } + + /** + * Match a received amount against an expected amount with tolerance. + * + * @param receivedAmount - The amount received in base units + * @param expectedAmount - The expected amount in base units + * @returns Match result indicating exact match, partial, or over-deposit + */ + private matchAmount( + receivedAmount: string, + expectedAmount: string, + ): { matches: boolean; isPartial?: boolean; isOverDeposit?: boolean } { + const received = BigInt(receivedAmount); + const expected = BigInt(expectedAmount); + + // Calculate tolerance (0.1% of expected) + const tolerance = (expected * BigInt(Math.floor(AMOUNT_TOLERANCE_PERCENT * 10))) / BigInt(1000); + + // Exact match (within tolerance) + const lowerBound = expected - tolerance; + const upperBound = expected + tolerance; + + if (received >= lowerBound && received <= upperBound) { + return { matches: true }; + } + + // Over-deposit (more than expected + tolerance) + if (received > upperBound) { + return { matches: true, isOverDeposit: true }; + } + + // Partial deposit (less than expected - tolerance) + if (received > BigInt(0) && received < lowerBound) { + return { matches: false, isPartial: true }; + } + + return { matches: false }; + } + + /** + * Determine the chain family from a deposit address format. + * + * @param address - The deposit address + * @returns The chain family identifier + */ + getChainFamily(address: string): ChainFamily { + // EVM addresses start with 0x and are 42 characters + if (address.startsWith('0x') && address.length === 42) { + return 'EVM'; + } + + // Bitcoin - native segwit (bc1), legacy (1 or 3) + if ( + address.startsWith('bc1') || + (address.startsWith('1') && address.length >= 26 && address.length <= 35) || + (address.startsWith('3') && address.length >= 26 && address.length <= 35) + ) { + return 'UTXO'; + } + + // Litecoin - segwit (ltc1), legacy (L or M) + if ( + address.startsWith('ltc1') || + address.startsWith('L') || + address.startsWith('M') + ) { + return 'UTXO'; + } + + // Dogecoin - starts with D + if (address.startsWith('D') && address.length >= 26 && address.length <= 35) { + return 'UTXO'; + } + + // Bitcoin Cash - starts with q or bitcoincash: + if (address.startsWith('q') || address.startsWith('bitcoincash:')) { + return 'UTXO'; + } + + // Cosmos addresses - start with cosmos + if (address.startsWith('cosmos')) { + return 'COSMOS'; + } + + // Osmosis addresses - start with osmo + if (address.startsWith('osmo')) { + return 'COSMOS'; + } + + // Solana - base58 encoded, typically 32-44 characters + if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) { + return 'SOLANA'; + } + + return 'UNKNOWN'; + } + + /** + * Get transactions for an address from the appropriate blockchain. + * + * This method queries Unchained APIs for transaction data. + * In a production system, this would integrate with the actual blockchain APIs. + * + * @param address - The address to query + * @param chainFamily - The blockchain family + * @returns Array of transactions for the address + */ + private async getAddressTransactions( + address: string, + chainFamily: ChainFamily, + ): Promise { + try { + switch (chainFamily) { + case 'EVM': + return this.getEvmTransactions(address); + case 'UTXO': + return this.getUtxoTransactions(address); + case 'COSMOS': + return this.getCosmosTransactions(address); + case 'SOLANA': + return this.getSolanaTransactions(address); + default: + return []; + } + } catch (error) { + this.logger.error( + `Failed to get transactions for ${address} on ${chainFamily}:`, + error, + ); + return []; + } + } + + /** + * Get EVM transactions for an address. + * Queries Unchained Ethereum API for incoming transactions. + * + * @param address - The EVM address + * @returns Array of incoming transactions + */ + private async getEvmTransactions(address: string): Promise { + const unchainedUrl = this.configService.get('UNCHAINED_ETHEREUM_HTTP_URL'); + + if (!unchainedUrl) { + this.logger.debug('UNCHAINED_ETHEREUM_HTTP_URL not configured, skipping EVM check'); + return []; + } + + try { + const response = await firstValueFrom( + this.httpService.get(`${unchainedUrl}/api/v1/account/${address}/txs`, { + params: { pageSize: 10 }, + timeout: 10000, + }), + ); + + const txs = response.data?.txs || []; + + return txs + .filter((tx: Record) => { + // Filter for incoming transactions to this address + const transfers = (tx.transfers as Array<{ type: string; to: string; value: string }>) || []; + return transfers.some( + (t) => t.type === 'receive' && t.to.toLowerCase() === address.toLowerCase(), + ); + }) + .map((tx: Record) => { + const transfers = (tx.transfers as Array<{ type: string; to: string; value: string }>) || []; + const incomingTransfer = transfers.find( + (t) => t.type === 'receive' && t.to.toLowerCase() === address.toLowerCase(), + ); + + return { + txHash: tx.txid as string, + amount: incomingTransfer?.value || '0', + confirmations: (tx.confirmations as number) || 0, + timestamp: (tx.timestamp as number) || 0, + }; + }); + } catch { + this.logger.debug(`No EVM transactions found for ${address}`); + return []; + } + } + + /** + * Get UTXO transactions for an address. + * Queries Unchained Bitcoin API for incoming transactions. + * + * @param address - The UTXO address + * @returns Array of incoming transactions + */ + private async getUtxoTransactions(address: string): Promise { + // Determine which Unchained endpoint to use based on address format + let unchainedUrl: string | undefined; + + if (address.startsWith('bc1') || address.startsWith('1') || address.startsWith('3')) { + unchainedUrl = this.configService.get('UNCHAINED_BITCOIN_HTTP_URL'); + } else if (address.startsWith('ltc1') || address.startsWith('L') || address.startsWith('M')) { + unchainedUrl = this.configService.get('UNCHAINED_LITECOIN_HTTP_URL'); + } else if (address.startsWith('D')) { + unchainedUrl = this.configService.get('UNCHAINED_DOGECOIN_HTTP_URL'); + } else if (address.startsWith('q') || address.startsWith('bitcoincash:')) { + unchainedUrl = this.configService.get('UNCHAINED_BITCOINCASH_HTTP_URL'); + } + + if (!unchainedUrl) { + this.logger.debug('Unchained URL not configured for UTXO address, skipping check'); + return []; + } + + try { + const response = await firstValueFrom( + this.httpService.get(`${unchainedUrl}/api/v1/account/${address}/txs`, { + params: { pageSize: 10 }, + timeout: 10000, + }), + ); + + const txs = response.data?.txs || []; + + return txs + .filter((tx: Record) => { + // Filter for transactions with outputs to this address + const vout = (tx.vout as Array<{ addresses: string[]; value: string }>) || []; + return vout.some((output) => + output.addresses?.some((a) => a.toLowerCase() === address.toLowerCase()), + ); + }) + .map((tx: Record) => { + const vout = (tx.vout as Array<{ addresses: string[]; value: string }>) || []; + const receivedOutput = vout.find((output) => + output.addresses?.some((a) => a.toLowerCase() === address.toLowerCase()), + ); + + return { + txHash: tx.txid as string, + amount: receivedOutput?.value || '0', + confirmations: (tx.confirmations as number) || 0, + timestamp: (tx.timestamp as number) || 0, + }; + }); + } catch { + this.logger.debug(`No UTXO transactions found for ${address}`); + return []; + } + } + + /** + * Get Cosmos transactions for an address. + * Queries Unchained Cosmos API for incoming transactions. + * + * @param address - The Cosmos address + * @returns Array of incoming transactions + */ + private async getCosmosTransactions(address: string): Promise { + let unchainedUrl: string | undefined; + + if (address.startsWith('cosmos')) { + unchainedUrl = this.configService.get('UNCHAINED_COSMOS_HTTP_URL'); + } else if (address.startsWith('osmo')) { + unchainedUrl = this.configService.get('UNCHAINED_OSMOSIS_HTTP_URL'); + } + + if (!unchainedUrl) { + this.logger.debug('Unchained URL not configured for Cosmos address, skipping check'); + return []; + } + + try { + const response = await firstValueFrom( + this.httpService.get(`${unchainedUrl}/api/v1/account/${address}/txs`, { + params: { pageSize: 10 }, + timeout: 10000, + }), + ); + + const txs = response.data?.txs || []; + + return txs + .filter((tx: Record) => { + // Filter for incoming bank transfers + const messages = (tx.messages as Array<{ type: string; to: string; value: { amount: string } }>) || []; + return messages.some( + (msg) => + msg.type === 'cosmos-sdk/MsgSend' && + msg.to?.toLowerCase() === address.toLowerCase(), + ); + }) + .map((tx: Record) => { + const messages = (tx.messages as Array<{ type: string; to: string; value: { amount: string } }>) || []; + const incomingMsg = messages.find( + (msg) => + msg.type === 'cosmos-sdk/MsgSend' && + msg.to?.toLowerCase() === address.toLowerCase(), + ); + + return { + txHash: tx.txid as string, + amount: incomingMsg?.value?.amount || '0', + confirmations: (tx.confirmations as number) || 1, // Cosmos has near-instant finality + timestamp: (tx.timestamp as number) || 0, + }; + }); + } catch { + this.logger.debug(`No Cosmos transactions found for ${address}`); + return []; + } + } + + /** + * Get Solana transactions for an address. + * Queries Solana RPC for incoming transactions. + * + * @param address - The Solana address + * @returns Array of incoming transactions + */ + private async getSolanaTransactions(address: string): Promise { + const rpcUrl = this.configService.get('SOLANA_RPC_URL'); + + if (!rpcUrl) { + this.logger.debug('SOLANA_RPC_URL not configured, skipping Solana check'); + return []; + } + + try { + // Get recent signatures for the address + const signaturesResponse = await firstValueFrom( + this.httpService.post( + rpcUrl, + { + jsonrpc: '2.0', + id: 1, + method: 'getSignaturesForAddress', + params: [address, { limit: 10 }], + }, + { timeout: 10000 }, + ), + ); + + const signatures = signaturesResponse.data?.result || []; + + const transactions: TransactionInfo[] = []; + + for (const sig of signatures) { + try { + // Get transaction details + const txResponse = await firstValueFrom( + this.httpService.post( + rpcUrl, + { + jsonrpc: '2.0', + id: 1, + method: 'getTransaction', + params: [sig.signature, { encoding: 'jsonParsed', maxSupportedTransactionVersion: 0 }], + }, + { timeout: 10000 }, + ), + ); + + const tx = txResponse.data?.result; + if (!tx) continue; + + // Check for incoming SOL transfers + const preBalance = tx.meta?.preBalances?.[0] || 0; + const postBalance = tx.meta?.postBalances?.[0] || 0; + const receivedAmount = postBalance - preBalance; + + if (receivedAmount > 0) { + transactions.push({ + txHash: sig.signature, + amount: receivedAmount.toString(), + confirmations: tx.slot ? 32 : 0, // Solana finality + timestamp: tx.blockTime || 0, + }); + } + } catch { + // Skip failed transaction fetches + continue; + } + } + + return transactions; + } catch { + this.logger.debug(`No Solana transactions found for ${address}`); + return []; + } + } +} diff --git a/apps/send-swap-service/src/monitoring/monitoring.module.ts b/apps/send-swap-service/src/monitoring/monitoring.module.ts new file mode 100644 index 0000000..7def758 --- /dev/null +++ b/apps/send-swap-service/src/monitoring/monitoring.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { HttpModule } from '@nestjs/axios'; +import { ConfigModule } from '@nestjs/config'; +import { DepositMonitorService } from './deposit-monitor.service'; +import { QuotesModule } from '../quotes/quotes.module'; +import { ExecutionModule } from '../execution/execution.module'; + +/** + * MonitoringModule provides deposit monitoring functionality for send-swap operations. + * + * This module: + * - Runs periodic cron jobs to check for deposits + * - Monitors active quote deposit addresses + * - Updates quote status when deposits are detected + * - Queries blockchain APIs for transaction detection + * - Executes swaps via SwapExecutorService after deposit confirmation + * + * Dependencies: + * - QuotesModule: Access to quotes for monitoring and status updates + * - ExecutionModule: Swap execution after deposit detection + * - HttpModule: HTTP client for blockchain API queries + * - ConfigModule: Access to Unchained URLs and other configuration + * - ScheduleModule: Must be imported in AppModule for cron functionality + */ +@Module({ + imports: [ + QuotesModule, + ExecutionModule, + HttpModule, + ConfigModule, + ], + providers: [DepositMonitorService], + exports: [DepositMonitorService], +}) +export class MonitoringModule {} diff --git a/apps/send-swap-service/src/prisma/prisma.service.ts b/apps/send-swap-service/src/prisma/prisma.service.ts new file mode 100644 index 0000000..bb6565f --- /dev/null +++ b/apps/send-swap-service/src/prisma/prisma.service.ts @@ -0,0 +1,13 @@ +import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + async onModuleInit() { + await this.$connect(); + } + + async onModuleDestroy() { + await this.$disconnect(); + } +} diff --git a/apps/send-swap-service/src/quotes/quotes.controller.spec.ts b/apps/send-swap-service/src/quotes/quotes.controller.spec.ts new file mode 100644 index 0000000..e049dda --- /dev/null +++ b/apps/send-swap-service/src/quotes/quotes.controller.spec.ts @@ -0,0 +1,121 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { QuotesController } from './quotes.controller'; +import { QuotesService, CreateQuoteDto, QuoteResponse } from './quotes.service'; +import { QuoteStatus } from '@prisma/client'; +import { SwapperType, SwapperName } from '../swappers/swapper.types'; + +describe('QuotesController', () => { + let controller: QuotesController; + let quotesService: jest.Mocked; + + const mockQuoteResponse: QuoteResponse = { + quoteId: 'quote_abc123', + status: QuoteStatus.ACTIVE, + depositAddress: '0x1234567890abcdef1234567890abcdef12345678', + receiveAddress: 'bc1qtest123', + sellAsset: { symbol: 'ETH', name: 'Ethereum' }, + buyAsset: { symbol: 'BTC', name: 'Bitcoin' }, + sellAmountCryptoBaseUnit: '1000000000000000000', + expectedBuyAmountCryptoBaseUnit: '3000000', + swapperName: SwapperName.Chainflip, + swapperType: SwapperType.DIRECT, + gasOverheadBaseUnit: null, + expiresAt: new Date(Date.now() + 30 * 60 * 1000), + createdAt: new Date(), + qrData: 'ethereum:0x1234567890abcdef1234567890abcdef12345678?value=1000000000000000000', + }; + + beforeEach(async () => { + const mockQuotesService = { + createQuote: jest.fn(), + getQuote: jest.fn(), + }; + + const module: TestingModule = await Test.createTestingModule({ + controllers: [QuotesController], + providers: [ + { + provide: QuotesService, + useValue: mockQuotesService, + }, + ], + }).compile(); + + controller = module.get(QuotesController); + quotesService = module.get(QuotesService); + }); + + describe('createQuote', () => { + it('should create a quote and return 201', async () => { + const createQuoteDto: CreateQuoteDto = { + sellAssetId: 'eip155:1/slip44:60', + buyAssetId: 'bip122:000000000019d6689c085ae165831e93/slip44:0', + sellAmountCryptoBaseUnit: '1000000000000000000', + receiveAddress: 'bc1qtest123', + swapperName: SwapperName.Chainflip, + expectedBuyAmountCryptoBaseUnit: '3000000', + sellAsset: { symbol: 'ETH', name: 'Ethereum' }, + buyAsset: { symbol: 'BTC', name: 'Bitcoin' }, + }; + + quotesService.createQuote.mockResolvedValue(mockQuoteResponse); + + const result = await controller.createQuote(createQuoteDto); + + expect(result).toEqual(mockQuoteResponse); + expect(quotesService.createQuote).toHaveBeenCalledWith(createQuoteDto); + }); + + it('should return a quote with qrData', async () => { + const createQuoteDto: CreateQuoteDto = { + sellAssetId: 'eip155:1/slip44:60', + buyAssetId: 'bip122:000000000019d6689c085ae165831e93/slip44:0', + sellAmountCryptoBaseUnit: '1000000000000000000', + receiveAddress: 'bc1qtest123', + swapperName: SwapperName.Chainflip, + expectedBuyAmountCryptoBaseUnit: '3000000', + sellAsset: { symbol: 'ETH', name: 'Ethereum' }, + buyAsset: { symbol: 'BTC', name: 'Bitcoin' }, + }; + + quotesService.createQuote.mockResolvedValue(mockQuoteResponse); + + const result = await controller.createQuote(createQuoteDto); + + expect(result.qrData).toBeDefined(); + expect(result.qrData).toContain('ethereum:'); + }); + + it('should return a quote with 30-minute expiration', async () => { + const createQuoteDto: CreateQuoteDto = { + sellAssetId: 'eip155:1/slip44:60', + buyAssetId: 'bip122:000000000019d6689c085ae165831e93/slip44:0', + sellAmountCryptoBaseUnit: '1000000000000000000', + receiveAddress: 'bc1qtest123', + swapperName: SwapperName.Chainflip, + expectedBuyAmountCryptoBaseUnit: '3000000', + sellAsset: { symbol: 'ETH', name: 'Ethereum' }, + buyAsset: { symbol: 'BTC', name: 'Bitcoin' }, + }; + + quotesService.createQuote.mockResolvedValue(mockQuoteResponse); + + const result = await controller.createQuote(createQuoteDto); + + expect(result.expiresAt).toBeDefined(); + expect(result.expiresAt.getTime()).toBeGreaterThan(Date.now()); + }); + }); + + describe('getQuote', () => { + it('should get a quote by id', async () => { + const quoteId = 'quote_abc123'; + quotesService.getQuote.mockResolvedValue(mockQuoteResponse); + + const result = await controller.getQuote(quoteId); + + expect(result).toEqual(mockQuoteResponse); + expect(quotesService.getQuote).toHaveBeenCalledWith(quoteId); + }); + }); +}); diff --git a/apps/send-swap-service/src/quotes/quotes.controller.ts b/apps/send-swap-service/src/quotes/quotes.controller.ts new file mode 100644 index 0000000..958ff6c --- /dev/null +++ b/apps/send-swap-service/src/quotes/quotes.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Post, Get, Param, Body } from '@nestjs/common'; +import { QuotesService, CreateQuoteDto, QuoteResponse } from './quotes.service'; + +/** + * QuotesController handles HTTP endpoints for quote generation and retrieval. + * + * Endpoints: + * - POST /quotes: Create a new quote for a send-swap operation + * - GET /quotes/:id: Get a quote by its unique identifier + */ +@Controller('quotes') +export class QuotesController { + constructor(private quotesService: QuotesService) {} + + /** + * Create a new quote for a send-swap operation. + * + * @param data - Quote creation parameters including assets, amounts, and addresses + * @returns The created quote with deposit address, expiration, and QR data + */ + @Post() + async createQuote(@Body() data: CreateQuoteDto): Promise { + return this.quotesService.createQuote(data); + } + + /** + * Get a quote by its unique identifier. + * + * @param id - The quote ID (e.g., "quote_abc123") + * @returns The quote with current status and details + */ + @Get(':id') + async getQuote(@Param('id') id: string): Promise { + return this.quotesService.getQuote(id); + } +} diff --git a/apps/send-swap-service/src/quotes/quotes.module.ts b/apps/send-swap-service/src/quotes/quotes.module.ts new file mode 100644 index 0000000..bbb9589 --- /dev/null +++ b/apps/send-swap-service/src/quotes/quotes.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { QuotesController } from './quotes.controller'; +import { QuotesService } from './quotes.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { WalletModule } from '../wallet/wallet.module'; +import { SwappersModule } from '../swappers/swappers.module'; + +/** + * QuotesModule provides quote generation and management functionality. + * + * Dependencies: + * - PrismaService: Database access for quote persistence + * - WalletModule: Deposit address generation + * - SwappersModule: Swapper type classification and gas calculation + */ +@Module({ + imports: [WalletModule, SwappersModule], + controllers: [QuotesController], + providers: [QuotesService, PrismaService], + exports: [QuotesService], +}) +export class QuotesModule {} diff --git a/apps/send-swap-service/src/quotes/quotes.service.ts b/apps/send-swap-service/src/quotes/quotes.service.ts new file mode 100644 index 0000000..b2493b6 --- /dev/null +++ b/apps/send-swap-service/src/quotes/quotes.service.ts @@ -0,0 +1,753 @@ +import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { WalletManagerService, EvmChain, UtxoChain, CosmosChain } from '../wallet/wallet-manager.service'; +import { SwapperManagerService } from '../swappers/swapper-manager.service'; +import { GasCalculatorService, ChainId } from '../swappers/gas-calculator.service'; +import { SwapperType, SwapperName } from '../swappers/swapper.types'; +import { Quote, QuoteStatus, Prisma } from '@prisma/client'; +import { randomUUID } from 'crypto'; + +/** + * Quote expiration time in minutes (30-minute TTL) + * Quotes automatically expire after this duration and cannot accept deposits. + */ +const QUOTE_EXPIRATION_MINUTES = 30; + +/** + * Quote expiration time in milliseconds (1800000ms = 30 minutes) + * Used for precise time calculations and comparisons. + */ +const QUOTE_EXPIRATION_MS = 1800000; + +/** + * Chain family identifiers for deposit address generation + */ +type ChainFamily = 'EVM' | 'UTXO' | 'COSMOS' | 'SOLANA'; + +/** + * Chain info extracted from asset ID + */ +interface ChainInfo { + family: ChainFamily; + chainId: ChainId; + evmChain?: EvmChain; + utxoChain?: UtxoChain; + cosmosChain?: CosmosChain; +} + +/** + * DTO for creating a new quote + */ +export interface CreateQuoteDto { + sellAssetId: string; + buyAssetId: string; + sellAmountCryptoBaseUnit: string; + receiveAddress: string; + swapperName: SwapperName; + expectedBuyAmountCryptoBaseUnit: string; + sellAsset: Record; + buyAsset: Record; +} + +/** + * DTO for quote response + */ +export interface QuoteResponse { + quoteId: string; + status: QuoteStatus; + depositAddress: string; + receiveAddress: string; + sellAsset: Record; + buyAsset: Record; + sellAmountCryptoBaseUnit: string; + expectedBuyAmountCryptoBaseUnit: string; + swapperName: string; + swapperType: SwapperType; + gasOverheadBaseUnit: string | null; + expiresAt: Date; + createdAt: Date; + qrData: string; +} + +/** + * QuotesService handles quote generation and management for send-swap operations. + * + * Key responsibilities: + * - Generate quotes with 30-minute expiration + * - Generate deposit addresses based on sell asset chain + * - Calculate gas overhead for service-wallet swappers + * - Manage quote lifecycle (active, expired, completed, etc.) + */ +@Injectable() +export class QuotesService { + private readonly logger = new Logger(QuotesService.name); + + constructor( + private prisma: PrismaService, + private walletManager: WalletManagerService, + private swapperManager: SwapperManagerService, + private gasCalculator: GasCalculatorService, + ) {} + + /** + * Create a new quote for a send-swap operation. + * + * @param data - Quote creation parameters + * @returns The created quote with deposit address and expiration + */ + async createQuote(data: CreateQuoteDto): Promise { + try { + // Validate swapper is valid for send-swap + if (!this.swapperManager.isValidSwapper(data.swapperName)) { + throw new BadRequestException( + `Swapper ${data.swapperName} is not supported for send-swap operations`, + ); + } + + // Get swapper type + const swapperType = this.swapperManager.getSwapperType(data.swapperName); + + // Extract chain info from sell asset + const chainInfo = this.extractChainInfo(data.sellAssetId); + if (!chainInfo) { + throw new BadRequestException( + `Unsupported chain for asset: ${data.sellAssetId}`, + ); + } + + // Generate unique quote ID + const quoteId = `quote_${randomUUID().replace(/-/g, '').substring(0, 16)}`; + + // Get the next available address index for unique deposit address + const addressIndex = await this.getNextAddressIndex(); + + // Generate deposit address based on chain family + const depositAddress = await this.generateDepositAddress(chainInfo, addressIndex); + + // Calculate gas overhead for service-wallet swappers + let gasOverheadBaseUnit: string | null = null; + if (swapperType === SwapperType.SERVICE_WALLET) { + gasOverheadBaseUnit = this.gasCalculator.calculateGasOverhead( + chainInfo.chainId, + swapperType, + ); + this.logger.debug( + `Gas overhead for ${data.swapperName} on ${chainInfo.chainId}: ${gasOverheadBaseUnit}`, + ); + } + + // Calculate expiration time (30 minutes from now) + const expiresAt = new Date(); + expiresAt.setMinutes(expiresAt.getMinutes() + QUOTE_EXPIRATION_MINUTES); + + // Create quote in database + const quote = await this.prisma.quote.create({ + data: { + quoteId, + status: QuoteStatus.ACTIVE, + sellAsset: data.sellAsset as Prisma.InputJsonValue, + buyAsset: data.buyAsset as Prisma.InputJsonValue, + sellAmountCryptoBaseUnit: data.sellAmountCryptoBaseUnit, + expectedBuyAmountCryptoBaseUnit: data.expectedBuyAmountCryptoBaseUnit, + depositAddress, + receiveAddress: data.receiveAddress, + swapperName: data.swapperName, + swapperType: swapperType === SwapperType.DIRECT ? 'DIRECT' : 'SERVICE_WALLET', + gasOverheadBaseUnit, + expiresAt, + }, + }); + + this.logger.log( + `Quote created: ${quoteId}, swapper: ${data.swapperName}, type: ${swapperType}, expires: ${expiresAt.toISOString()}`, + ); + + return this.formatQuoteResponse(quote); + } catch (error) { + this.logger.error('Failed to create quote', error); + throw error; + } + } + + /** + * Get a quote by its unique ID. + * + * @param quoteId - The quote identifier + * @returns The quote with current status + */ + async getQuote(quoteId: string): Promise { + const quote = await this.prisma.quote.findUnique({ + where: { quoteId }, + }); + + if (!quote) { + throw new NotFoundException(`Quote not found: ${quoteId}`); + } + + // Check and update expiration status if needed + const updatedQuote = await this.checkAndUpdateExpiration(quote); + + return this.formatQuoteResponse(updatedQuote); + } + + /** + * Get a quote by deposit address. + * + * @param depositAddress - The deposit address to search for + * @returns The quote if found + */ + async getQuoteByDepositAddress(depositAddress: string): Promise { + const quote = await this.prisma.quote.findFirst({ + where: { + depositAddress, + status: QuoteStatus.ACTIVE, + }, + }); + + if (quote) { + return this.checkAndUpdateExpiration(quote); + } + + return null; + } + + /** + * Get all active (non-expired) quotes. + * + * @returns List of active quotes + */ + async getActiveQuotes(): Promise { + const now = new Date(); + + const quotes = await this.prisma.quote.findMany({ + where: { + status: QuoteStatus.ACTIVE, + expiresAt: { + gt: now, + }, + }, + orderBy: { createdAt: 'desc' }, + }); + + return quotes; + } + + /** + * Get quotes that need expiration check. + * Used by the deposit monitor to efficiently query active quotes. + * + * @returns List of quotes to monitor + */ + async getQuotesToMonitor(): Promise { + const now = new Date(); + + return this.prisma.quote.findMany({ + where: { + status: { + in: [QuoteStatus.ACTIVE, QuoteStatus.DEPOSIT_RECEIVED], + }, + expiresAt: { + gt: now, + }, + }, + }); + } + + /** + * Lock a quote for processing using optimistic locking. + * + * This method atomically transitions a quote from ACTIVE to EXECUTING status, + * preventing race conditions when multiple deposit monitor iterations might + * detect the same deposit simultaneously. + * + * Uses a database-level atomic update with status guard to ensure only one + * process can acquire the lock. + * + * @param quoteId - The quote identifier to lock + * @returns The locked quote, or null if lock could not be acquired + */ + async lockQuote(quoteId: string): Promise { + try { + // Optimistic locking: atomically update ONLY if status is ACTIVE + // This prevents race conditions where multiple processes try to lock + // the same quote simultaneously + const updatedQuote = await this.prisma.quote.updateMany({ + where: { + quoteId, + status: QuoteStatus.ACTIVE, + expiresAt: { gt: new Date() }, // Also ensure not expired + }, + data: { + status: QuoteStatus.EXECUTING, + }, + }); + + // If no rows were updated, the quote was already locked or not in ACTIVE state + if (updatedQuote.count === 0) { + this.logger.debug( + `Failed to lock quote ${quoteId}: not in ACTIVE state or already locked`, + ); + return null; + } + + this.logger.log(`Quote ${quoteId} locked for execution`); + + // Fetch and return the locked quote + return this.prisma.quote.findUnique({ where: { quoteId } }); + } catch (error) { + this.logger.error(`Failed to lock quote ${quoteId}:`, error); + return null; + } + } + + /** + * Update quote status when deposit is received. + * Accepts quotes in either ACTIVE or EXECUTING status. + * + * When called after lockQuote(), the quote will be in EXECUTING status. + * For backwards compatibility, also accepts ACTIVE status for direct calls. + * + * @param quoteId - The quote identifier + * @param depositTxHash - The deposit transaction hash + * @returns Updated quote + */ + async markDepositReceived(quoteId: string, depositTxHash: string): Promise { + const quote = await this.prisma.quote.findUnique({ + where: { quoteId }, + }); + + if (!quote) { + throw new NotFoundException(`Quote not found: ${quoteId}`); + } + + // Check if quote is expired + if (quote.expiresAt < new Date()) { + throw new BadRequestException(`Quote ${quoteId} has expired`); + } + + // Check if quote is in valid state for deposit + // Accept both ACTIVE (legacy/direct calls) and EXECUTING (after lockQuote) + if (quote.status !== QuoteStatus.ACTIVE && quote.status !== QuoteStatus.EXECUTING) { + throw new BadRequestException( + `Quote ${quoteId} is not in ACTIVE or EXECUTING status (current: ${quote.status})`, + ); + } + + const updatedQuote = await this.prisma.quote.update({ + where: { quoteId }, + data: { + status: QuoteStatus.DEPOSIT_RECEIVED, + depositTxHash, + }, + }); + + this.logger.log(`Deposit received for quote ${quoteId}: ${depositTxHash}`); + + return updatedQuote; + } + + /** + * Update quote status to executing. + * + * @param quoteId - The quote identifier + * @returns Updated quote + */ + async markExecuting(quoteId: string): Promise { + const updatedQuote = await this.prisma.quote.update({ + where: { quoteId }, + data: { + status: QuoteStatus.EXECUTING, + }, + }); + + this.logger.log(`Quote ${quoteId} is now executing`); + + return updatedQuote; + } + + /** + * Mark quote as completed after successful swap execution. + * + * @param quoteId - The quote identifier + * @param executionTxHash - The swap execution transaction hash + * @returns Updated quote + */ + async markCompleted(quoteId: string, executionTxHash: string): Promise { + const updatedQuote = await this.prisma.quote.update({ + where: { quoteId }, + data: { + status: QuoteStatus.COMPLETED, + executionTxHash, + executedAt: new Date(), + }, + }); + + this.logger.log(`Quote ${quoteId} completed: ${executionTxHash}`); + + return updatedQuote; + } + + /** + * Mark quote as failed. + * + * @param quoteId - The quote identifier + * @returns Updated quote + */ + async markFailed(quoteId: string): Promise { + const updatedQuote = await this.prisma.quote.update({ + where: { quoteId }, + data: { + status: QuoteStatus.FAILED, + }, + }); + + this.logger.log(`Quote ${quoteId} marked as failed`); + + return updatedQuote; + } + + /** + * Expire all quotes that have passed their expiration time. + * Called by cron job to clean up stale quotes. + * + * @returns Number of quotes expired + */ + async expireStaleQuotes(): Promise { + const now = new Date(); + + const result = await this.prisma.quote.updateMany({ + where: { + status: QuoteStatus.ACTIVE, + expiresAt: { + lte: now, + }, + }, + data: { + status: QuoteStatus.EXPIRED, + }, + }); + + if (result.count > 0) { + this.logger.log(`Expired ${result.count} stale quotes`); + } + + return result.count; + } + + /** + * Extract chain information from a CAIP-19 asset ID. + * Maps asset IDs to chain families and specific chain identifiers. + * + * @param assetId - CAIP-19 asset identifier (e.g., "eip155:1/slip44:60") + * @returns Chain information or undefined if unsupported + */ + private extractChainInfo(assetId: string): ChainInfo | undefined { + const chainPart = assetId.split('/')[0]; + + if (!chainPart) { + return undefined; + } + + // EVM chains + if (chainPart.startsWith('eip155:')) { + const chainId = chainPart as ChainId; + const evmChainMap: Record = { + 'eip155:1': 'ETH', + 'eip155:43114': 'AVAX', + 'eip155:56': 'BSC', + 'eip155:137': 'POLYGON', + 'eip155:10': 'OPTIMISM', + 'eip155:42161': 'ARBITRUM', + 'eip155:8453': 'BASE', + 'eip155:100': 'GNOSIS', + }; + + const evmChain = evmChainMap[chainPart]; + if (evmChain) { + return { family: 'EVM', chainId, evmChain }; + } + } + + // UTXO chains + if (chainPart.startsWith('bip122:')) { + const chainId = chainPart as ChainId; + const utxoChainMap: Record = { + 'bip122:000000000019d6689c085ae165831e93': 'BTC', + 'bip122:12a765e31ffd4059bada1e25190f6e98': 'LTC', + 'bip122:1a91e3dace36e2be3bf030a65679fe82': 'DOGE', + 'bip122:000000000000000000651ef99cb9fcbe': 'BCH', + }; + + const utxoChain = utxoChainMap[chainPart]; + if (utxoChain) { + return { family: 'UTXO', chainId, utxoChain }; + } + } + + // Cosmos-SDK chains + if (chainPart.startsWith('cosmos:')) { + const chainId = chainPart as ChainId; + const cosmosChainMap: Record = { + 'cosmos:cosmoshub-4': 'ATOM', + 'cosmos:osmosis-1': 'OSMO', + }; + + const cosmosChain = cosmosChainMap[chainPart]; + if (cosmosChain) { + return { family: 'COSMOS', chainId, cosmosChain }; + } + } + + // Solana + if (chainPart === 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') { + return { family: 'SOLANA', chainId: ChainId.SOL }; + } + + return undefined; + } + + /** + * Generate a deposit address based on the chain family. + * + * @param chainInfo - Chain information + * @param addressIndex - Index for unique address generation + * @returns The deposit address + */ + private async generateDepositAddress( + chainInfo: ChainInfo, + addressIndex: number, + ): Promise { + switch (chainInfo.family) { + case 'EVM': + return this.walletManager.getEvmDepositAddress( + chainInfo.evmChain!, + addressIndex, + ); + + case 'UTXO': + return this.walletManager.getUtxoDepositAddress( + chainInfo.utxoChain!, + addressIndex, + ); + + case 'COSMOS': + return this.walletManager.getCosmosDepositAddress( + chainInfo.cosmosChain!, + addressIndex, + ); + + case 'SOLANA': + return this.walletManager.getSolanaDepositAddress(addressIndex); + + default: + throw new BadRequestException( + `Unsupported chain family: ${chainInfo.family}`, + ); + } + } + + /** + * Get the next available address index for unique deposit address generation. + * Uses the total quote count as a simple incrementing index. + * + * @returns The next address index + */ + private async getNextAddressIndex(): Promise { + const count = await this.prisma.quote.count(); + return count; + } + + /** + * Check if a quote has expired and update its status if needed. + * Uses the 30-minute TTL defined by QUOTE_EXPIRATION_MINUTES. + * + * @param quote - The quote to check + * @returns The quote with updated status if expired + */ + private async checkAndUpdateExpiration(quote: Quote): Promise { + if (quote.status === QuoteStatus.ACTIVE && this.isQuoteExpired(quote)) { + this.logger.log(`Quote ${quote.quoteId} has expired (30-minute TTL exceeded)`); + return this.prisma.quote.update({ + where: { id: quote.id }, + data: { status: QuoteStatus.EXPIRED }, + }); + } + return quote; + } + + /** + * Check if a quote has expired based on its expiresAt timestamp. + * Quotes have a 30-minute TTL from creation. + * + * @param quote - The quote to check + * @returns True if the quote has expired + */ + isQuoteExpired(quote: Quote): boolean { + return quote.expiresAt < new Date(); + } + + /** + * Get the remaining time in milliseconds before a quote expires. + * Returns 0 if the quote has already expired. + * + * @param quote - The quote to check + * @returns Remaining time in milliseconds (max 1800000ms for 30 minutes) + */ + getRemainingTimeMs(quote: Quote): number { + const remaining = quote.expiresAt.getTime() - Date.now(); + return Math.max(0, remaining); + } + + /** + * Check if a quote can still accept deposits. + * A quote can accept deposits only if it's ACTIVE and not expired. + * + * @param quote - The quote to check + * @returns True if the quote can accept deposits + */ + canAcceptDeposit(quote: Quote): boolean { + return quote.status === QuoteStatus.ACTIVE && !this.isQuoteExpired(quote); + } + + /** + * Get the expiration duration in milliseconds (30 minutes = 1800000ms). + * Useful for clients that need to know the TTL duration. + * + * @returns Expiration duration in milliseconds + */ + getExpirationDurationMs(): number { + return QUOTE_EXPIRATION_MS; + } + + /** + * Format a quote entity into a response DTO with QR data. + * + * @param quote - The quote entity + * @returns Formatted quote response + */ + private formatQuoteResponse(quote: Quote): QuoteResponse { + // Generate QR-friendly data for deposit + // Format depends on the chain (ethereum: for EVM, bitcoin: for BTC, etc.) + const qrData = this.generateQrData(quote); + + return { + quoteId: quote.quoteId, + status: quote.status, + depositAddress: quote.depositAddress, + receiveAddress: quote.receiveAddress, + sellAsset: quote.sellAsset as Record, + buyAsset: quote.buyAsset as Record, + sellAmountCryptoBaseUnit: quote.sellAmountCryptoBaseUnit, + expectedBuyAmountCryptoBaseUnit: quote.expectedBuyAmountCryptoBaseUnit, + swapperName: quote.swapperName, + swapperType: quote.swapperType === 'DIRECT' ? SwapperType.DIRECT : SwapperType.SERVICE_WALLET, + gasOverheadBaseUnit: quote.gasOverheadBaseUnit, + expiresAt: quote.expiresAt, + createdAt: quote.createdAt, + qrData, + }; + } + + /** + * Generate QR-friendly data for a deposit address. + * Uses standard URI schemes for different chains. + * + * @param quote - The quote to generate QR data for + * @returns QR-friendly URI string + */ + private generateQrData(quote: Quote): string { + const { depositAddress, sellAmountCryptoBaseUnit } = quote; + + // Determine chain type from swapper type or deposit address format + // EVM addresses start with 0x + if (depositAddress.startsWith('0x')) { + return `ethereum:${depositAddress}?value=${sellAmountCryptoBaseUnit}`; + } + + // Bitcoin addresses - native segwit starts with bc1, legacy with 1 or 3 + if ( + depositAddress.startsWith('bc1') || + depositAddress.startsWith('1') || + depositAddress.startsWith('3') + ) { + return `bitcoin:${depositAddress}?amount=${this.satoshiToBtc(sellAmountCryptoBaseUnit)}`; + } + + // Litecoin - segwit starts with ltc1, legacy with L or M + if ( + depositAddress.startsWith('ltc1') || + depositAddress.startsWith('L') || + depositAddress.startsWith('M') + ) { + return `litecoin:${depositAddress}?amount=${this.satoshiToBtc(sellAmountCryptoBaseUnit)}`; + } + + // Dogecoin - starts with D + if (depositAddress.startsWith('D')) { + return `dogecoin:${depositAddress}?amount=${this.satoshiToBtc(sellAmountCryptoBaseUnit)}`; + } + + // Bitcoin Cash - starts with q or bitcoincash: + if ( + depositAddress.startsWith('q') || + depositAddress.startsWith('bitcoincash:') + ) { + const bchAddress = depositAddress.replace('bitcoincash:', ''); + return `bitcoincash:${bchAddress}?amount=${this.satoshiToBtc(sellAmountCryptoBaseUnit)}`; + } + + // Cosmos addresses - start with cosmos + if (depositAddress.startsWith('cosmos')) { + return `cosmos:${depositAddress}?amount=${this.microToUnit(sellAmountCryptoBaseUnit)}`; + } + + // Osmosis addresses - start with osmo + if (depositAddress.startsWith('osmo')) { + return `osmosis:${depositAddress}?amount=${this.microToUnit(sellAmountCryptoBaseUnit)}`; + } + + // Solana - base58 encoded, typically 32-44 characters + if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(depositAddress)) { + return `solana:${depositAddress}?amount=${this.lamportsToSol(sellAmountCryptoBaseUnit)}`; + } + + // Default fallback - just return the address + return depositAddress; + } + + /** + * Convert satoshi to BTC for QR amount formatting + */ + private satoshiToBtc(satoshi: string): string { + const btc = BigInt(satoshi) / BigInt(100000000); + const remainder = BigInt(satoshi) % BigInt(100000000); + if (remainder === BigInt(0)) { + return btc.toString(); + } + return `${btc}.${remainder.toString().padStart(8, '0').replace(/0+$/, '')}`; + } + + /** + * Convert micro units to base units (e.g., uatom to ATOM) + */ + private microToUnit(micro: string): string { + const unit = BigInt(micro) / BigInt(1000000); + const remainder = BigInt(micro) % BigInt(1000000); + if (remainder === BigInt(0)) { + return unit.toString(); + } + return `${unit}.${remainder.toString().padStart(6, '0').replace(/0+$/, '')}`; + } + + /** + * Convert lamports to SOL + */ + private lamportsToSol(lamports: string): string { + const sol = BigInt(lamports) / BigInt(1000000000); + const remainder = BigInt(lamports) % BigInt(1000000000); + if (remainder === BigInt(0)) { + return sol.toString(); + } + return `${sol}.${remainder.toString().padStart(9, '0').replace(/0+$/, '')}`; + } +} diff --git a/apps/send-swap-service/src/swappers/gas-calculator.service.ts b/apps/send-swap-service/src/swappers/gas-calculator.service.ts new file mode 100644 index 0000000..4c0fb78 --- /dev/null +++ b/apps/send-swap-service/src/swappers/gas-calculator.service.ts @@ -0,0 +1,460 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { SwapperType } from './swapper.types'; + +/** + * Chain identifiers for gas overhead calculation + * Using CAIP-2 chain ID prefixes where applicable + */ +export enum ChainId { + // EVM Chains + ETH = 'eip155:1', + AVAX = 'eip155:43114', + BSC = 'eip155:56', + POLYGON = 'eip155:137', + OPTIMISM = 'eip155:10', + ARBITRUM = 'eip155:42161', + BASE = 'eip155:8453', + GNOSIS = 'eip155:100', + + // UTXO Chains + BTC = 'bip122:000000000019d6689c085ae165831e93', + LTC = 'bip122:12a765e31ffd4059bada1e25190f6e98', + DOGE = 'bip122:1a91e3dace36e2be3bf030a65679fe82', + BCH = 'bip122:000000000000000000651ef99cb9fcbe', + + // Cosmos-SDK Chains + ATOM = 'cosmos:cosmoshub-4', + OSMO = 'cosmos:osmosis-1', + + // Solana + SOL = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', +} + +/** + * Chain family types for different gas calculation strategies + */ +export enum ChainFamily { + EVM = 'EVM', + UTXO = 'UTXO', + COSMOS = 'COSMOS', + SOLANA = 'SOLANA', +} + +/** + * Gas overhead configuration for a specific chain + */ +export interface GasOverheadConfig { + chainId: ChainId; + chainFamily: ChainFamily; + /** Base gas overhead in the chain's native base unit (wei, satoshi, lamports, etc.) */ + baseOverhead: string; + /** Percentage buffer to add for price volatility (e.g., 1.1 = 10% buffer) */ + volatilityBuffer: number; + /** Human-readable description of the overhead estimation */ + description: string; +} + +/** + * Gas overhead configuration by chain + * + * Values are estimated based on typical swap transaction costs: + * - EVM: Approve + swap transaction gas costs + * - UTXO: Transaction size-based fees + * - Cosmos: Fixed gas costs per transaction type + * - Solana: Compute unit costs + * + * All values are in the chain's smallest denomination (base units): + * - ETH/EVM: wei (1 ETH = 10^18 wei) + * - BTC/UTXO: satoshi (1 BTC = 10^8 satoshi) + * - Cosmos: uatom/uosmo (1 ATOM = 10^6 uatom) + * - Solana: lamports (1 SOL = 10^9 lamports) + */ +export const GAS_OVERHEAD_BY_CHAIN: ReadonlyMap = new Map([ + // EVM Chains - estimated at ~300k gas with typical gas prices + [ + ChainId.ETH, + { + chainId: ChainId.ETH, + chainFamily: ChainFamily.EVM, + // ~0.003 ETH at 10 gwei for 300k gas + baseOverhead: '3000000000000000', // 0.003 ETH in wei + volatilityBuffer: 1.2, // 20% buffer for ETH gas volatility + description: 'Ethereum mainnet - high gas fees, volatile', + }, + ], + [ + ChainId.AVAX, + { + chainId: ChainId.AVAX, + chainFamily: ChainFamily.EVM, + // ~0.005 AVAX at 25 nAVAX for 200k gas + baseOverhead: '5000000000000000', // 0.005 AVAX in wei + volatilityBuffer: 1.15, + description: 'Avalanche C-Chain - moderate gas fees', + }, + ], + [ + ChainId.BSC, + { + chainId: ChainId.BSC, + chainFamily: ChainFamily.EVM, + // ~0.0003 BNB at 3 gwei for 100k gas + baseOverhead: '300000000000000', // 0.0003 BNB in wei + volatilityBuffer: 1.1, + description: 'BNB Smart Chain - low gas fees', + }, + ], + [ + ChainId.POLYGON, + { + chainId: ChainId.POLYGON, + chainFamily: ChainFamily.EVM, + // ~0.01 MATIC at 50 gwei for 200k gas + baseOverhead: '10000000000000000', // 0.01 MATIC in wei + volatilityBuffer: 1.15, + description: 'Polygon PoS - low gas fees, can spike', + }, + ], + [ + ChainId.OPTIMISM, + { + chainId: ChainId.OPTIMISM, + chainFamily: ChainFamily.EVM, + // ~0.0002 ETH for L2 transaction + baseOverhead: '200000000000000', // 0.0002 ETH in wei + volatilityBuffer: 1.1, + description: 'Optimism L2 - low gas fees', + }, + ], + [ + ChainId.ARBITRUM, + { + chainId: ChainId.ARBITRUM, + chainFamily: ChainFamily.EVM, + // ~0.0002 ETH for L2 transaction + baseOverhead: '200000000000000', // 0.0002 ETH in wei + volatilityBuffer: 1.1, + description: 'Arbitrum One L2 - low gas fees', + }, + ], + [ + ChainId.BASE, + { + chainId: ChainId.BASE, + chainFamily: ChainFamily.EVM, + // ~0.00015 ETH for L2 transaction + baseOverhead: '150000000000000', // 0.00015 ETH in wei + volatilityBuffer: 1.1, + description: 'Base L2 - very low gas fees', + }, + ], + [ + ChainId.GNOSIS, + { + chainId: ChainId.GNOSIS, + chainFamily: ChainFamily.EVM, + // ~0.0001 xDAI for transaction + baseOverhead: '100000000000000', // 0.0001 xDAI in wei + volatilityBuffer: 1.1, + description: 'Gnosis Chain - very low gas fees', + }, + ], + + // UTXO Chains - based on typical transaction sizes + [ + ChainId.BTC, + { + chainId: ChainId.BTC, + chainFamily: ChainFamily.UTXO, + // ~10,000 satoshi for 250-byte tx at 40 sat/vbyte + baseOverhead: '10000', // 0.0001 BTC in satoshi + volatilityBuffer: 1.3, // 30% buffer for BTC fee volatility + description: 'Bitcoin - fees vary by mempool congestion', + }, + ], + [ + ChainId.LTC, + { + chainId: ChainId.LTC, + chainFamily: ChainFamily.UTXO, + // ~2,000 litoshi for typical transaction + baseOverhead: '2000', // 0.00002 LTC in litoshi + volatilityBuffer: 1.1, + description: 'Litecoin - low fees', + }, + ], + [ + ChainId.DOGE, + { + chainId: ChainId.DOGE, + chainFamily: ChainFamily.UTXO, + // ~100,000,000 (1 DOGE) minimum fee + baseOverhead: '100000000', // 1 DOGE in koinu + volatilityBuffer: 1.1, + description: 'Dogecoin - 1 DOGE minimum fee', + }, + ], + [ + ChainId.BCH, + { + chainId: ChainId.BCH, + chainFamily: ChainFamily.UTXO, + // ~500 satoshi for typical transaction + baseOverhead: '500', // 0.000005 BCH in satoshi + volatilityBuffer: 1.1, + description: 'Bitcoin Cash - very low fees', + }, + ], + + // Cosmos-SDK Chains + [ + ChainId.ATOM, + { + chainId: ChainId.ATOM, + chainFamily: ChainFamily.COSMOS, + // ~5,000 uatom (0.005 ATOM) for typical transaction + baseOverhead: '5000', // 0.005 ATOM in uatom + volatilityBuffer: 1.1, + description: 'Cosmos Hub - fixed gas prices', + }, + ], + [ + ChainId.OSMO, + { + chainId: ChainId.OSMO, + chainFamily: ChainFamily.COSMOS, + // ~2,500 uosmo (0.0025 OSMO) for typical transaction + baseOverhead: '2500', // 0.0025 OSMO in uosmo + volatilityBuffer: 1.1, + description: 'Osmosis - low gas fees', + }, + ], + + // Solana + [ + ChainId.SOL, + { + chainId: ChainId.SOL, + chainFamily: ChainFamily.SOLANA, + // ~5000 lamports base + priority fee estimate + baseOverhead: '10000000', // 0.01 SOL in lamports (includes priority fee buffer) + volatilityBuffer: 1.15, + description: 'Solana - low fees, may need priority fees', + }, + ], +]); + +/** + * Default gas overhead for unknown chains + */ +const DEFAULT_GAS_OVERHEAD: GasOverheadConfig = { + chainId: ChainId.ETH, // Placeholder + chainFamily: ChainFamily.EVM, + baseOverhead: '5000000000000000', // 0.005 ETH equivalent - conservative default + volatilityBuffer: 1.25, + description: 'Default overhead for unknown chains - conservative estimate', +}; + +/** + * GasCalculatorService calculates chain-specific gas overhead for service-wallet swappers. + * + * This service is used to add gas fee buffers to quotes when the service needs to + * custody user funds and execute swaps on their behalf. This ensures the service + * doesn't lose money on gas fees. + * + * Key responsibilities: + * - Calculate gas overhead for a specific chain + * - Apply volatility buffers for networks with variable gas prices + * - Support multiple chain families (EVM, UTXO, Cosmos, Solana) + */ +@Injectable() +export class GasCalculatorService { + private readonly logger = new Logger(GasCalculatorService.name); + + /** + * Get gas overhead configuration for a chain + */ + getGasOverheadConfig(chainId: ChainId): GasOverheadConfig { + const config = GAS_OVERHEAD_BY_CHAIN.get(chainId); + + if (!config) { + this.logger.warn( + `No gas overhead config for chain ${chainId}, using default`, + ); + return DEFAULT_GAS_OVERHEAD; + } + + return config; + } + + /** + * Calculate gas overhead for a swap on a specific chain + * + * @param chainId - The chain identifier + * @param swapperType - The swapper type (DIRECT swappers don't need overhead) + * @returns Gas overhead in base units (wei, satoshi, lamports, etc.) + */ + calculateGasOverhead(chainId: ChainId, swapperType: SwapperType): string { + // Direct swappers don't need gas overhead - they handle their own execution + if (swapperType === SwapperType.DIRECT) { + this.logger.debug( + `Skipping gas overhead for DIRECT swapper on ${chainId}`, + ); + return '0'; + } + + const config = this.getGasOverheadConfig(chainId); + + // Apply volatility buffer to base overhead + const baseOverhead = BigInt(config.baseOverhead); + const bufferedOverhead = + (baseOverhead * BigInt(Math.round(config.volatilityBuffer * 100))) / + BigInt(100); + + this.logger.debug( + `Gas overhead for ${chainId}: ${bufferedOverhead.toString()} (base: ${config.baseOverhead}, buffer: ${config.volatilityBuffer}x)`, + ); + + return bufferedOverhead.toString(); + } + + /** + * Calculate gas overhead from a CAIP-2 formatted asset ID + * + * Asset IDs follow format: chainNamespace:chainReference/assetNamespace:assetReference + * e.g., "eip155:1/slip44:60" for ETH on Ethereum mainnet + * + * @param assetId - CAIP-19 asset identifier + * @param swapperType - The swapper type + * @returns Gas overhead in base units + */ + calculateGasOverheadFromAssetId( + assetId: string, + swapperType: SwapperType, + ): string { + const chainId = this.extractChainIdFromAssetId(assetId); + + if (!chainId) { + this.logger.warn( + `Could not extract chain ID from asset: ${assetId}, using default overhead`, + ); + return this.calculateGasOverheadWithConfig( + DEFAULT_GAS_OVERHEAD, + swapperType, + ); + } + + return this.calculateGasOverhead(chainId, swapperType); + } + + /** + * Extract ChainId from a CAIP-19 asset identifier + * + * @param assetId - Full asset identifier (e.g., "eip155:1/slip44:60") + * @returns ChainId enum value or undefined + */ + extractChainIdFromAssetId(assetId: string): ChainId | undefined { + // Extract chain part from asset ID (everything before the "/") + const chainPart = assetId.split('/')[0]; + + if (!chainPart) { + return undefined; + } + + // Check if it matches any known chain ID + for (const chainId of Object.values(ChainId)) { + if (chainId === chainPart) { + return chainId; + } + } + + return undefined; + } + + /** + * Calculate gas overhead with a specific config + * Internal helper for applying volatility buffer + */ + private calculateGasOverheadWithConfig( + config: GasOverheadConfig, + swapperType: SwapperType, + ): string { + if (swapperType === SwapperType.DIRECT) { + return '0'; + } + + const baseOverhead = BigInt(config.baseOverhead); + const bufferedOverhead = + (baseOverhead * BigInt(Math.round(config.volatilityBuffer * 100))) / + BigInt(100); + + return bufferedOverhead.toString(); + } + + /** + * Get the chain family for a chain ID + */ + getChainFamily(chainId: ChainId): ChainFamily { + const config = GAS_OVERHEAD_BY_CHAIN.get(chainId); + return config?.chainFamily ?? ChainFamily.EVM; + } + + /** + * Check if a chain is supported for gas estimation + */ + isSupportedChain(chainId: ChainId): boolean { + return GAS_OVERHEAD_BY_CHAIN.has(chainId); + } + + /** + * Get all supported chain IDs + */ + getSupportedChains(): ChainId[] { + return Array.from(GAS_OVERHEAD_BY_CHAIN.keys()); + } + + /** + * Get gas overhead summary for logging/debugging + */ + getGasOverheadSummary(): Record { + const summary: Record = {}; + + for (const [chainId, config] of GAS_OVERHEAD_BY_CHAIN.entries()) { + summary[chainId] = { + overhead: config.baseOverhead, + family: config.chainFamily, + }; + } + + return summary; + } + + /** + * Log gas overhead configuration (useful for debugging at startup) + */ + logGasOverheadConfig(): void { + this.logger.log('=== Gas Overhead Configuration ==='); + + const byFamily: Record = { + [ChainFamily.EVM]: [], + [ChainFamily.UTXO]: [], + [ChainFamily.COSMOS]: [], + [ChainFamily.SOLANA]: [], + }; + + for (const [chainId, config] of GAS_OVERHEAD_BY_CHAIN.entries()) { + byFamily[config.chainFamily].push( + `${chainId}: ${config.baseOverhead} (${config.volatilityBuffer}x buffer)`, + ); + } + + for (const [family, chains] of Object.entries(byFamily)) { + if (chains.length > 0) { + this.logger.log(`${family} chains:`); + for (const chain of chains) { + this.logger.log(` - ${chain}`); + } + } + } + } +} diff --git a/apps/send-swap-service/src/swappers/swapper-config.ts b/apps/send-swap-service/src/swappers/swapper-config.ts new file mode 100644 index 0000000..900ad70 --- /dev/null +++ b/apps/send-swap-service/src/swappers/swapper-config.ts @@ -0,0 +1,285 @@ +import { SwapperName, SwapperType, SwapperConfig } from './swapper.types'; + +/** + * Swapper Configuration Constants + * + * This file centralizes all swapper configuration for the send-swap-service. + * It defines which swappers are supported, excluded, and their classifications. + * + * Based on spec.md Swapper Classification Reference and isCrossAccountTradeSupported + * function from ShapeShift web repo. + */ + +/** + * Excluded swappers - these don't support destination addresses + * CRITICAL: These swappers MUST be excluded - they don't support external destination addresses + */ +export const EXCLUDED_SWAPPERS: readonly SwapperName[] = [ + SwapperName.Zrx, // No receiveAddress support + SwapperName.CowSwap, // No receiveAddress support + SwapperName.ArbitrumBridge, // Disabled for simplicity + SwapperName.Portals, // No receiveAddress support + SwapperName.Cetus, // No receiveAddress support + SwapperName.Sunio, // No receiveAddress support + SwapperName.Avnu, // No receiveAddress support + SwapperName.Stonfi, // No receiveAddress support +] as const; + +/** + * Set of excluded swappers for O(1) lookup + */ +export const EXCLUDED_SWAPPERS_SET: ReadonlySet = new Set(EXCLUDED_SWAPPERS); + +/** + * Direct execution swappers - these provide their own deposit addresses + * and handle swaps natively (no service custody required) + */ +export const DIRECT_SWAPPERS: readonly SwapperName[] = [ + SwapperName.Chainflip, + SwapperName.NearIntents, +] as const; + +/** + * Set of direct swappers for O(1) lookup + */ +export const DIRECT_SWAPPERS_SET: ReadonlySet = new Set(DIRECT_SWAPPERS); + +/** + * Service-wallet swappers that support destination addresses + * These require the service to receive funds first and execute on behalf of user + */ +export const SERVICE_WALLET_SWAPPERS: readonly SwapperName[] = [ + SwapperName.THORChain, + SwapperName.Jupiter, + SwapperName.Relay, + SwapperName.Mayachain, + SwapperName.ButterSwap, + SwapperName.Bebop, +] as const; + +/** + * Set of service-wallet swappers for O(1) lookup + */ +export const SERVICE_WALLET_SWAPPERS_SET: ReadonlySet = new Set(SERVICE_WALLET_SWAPPERS); + +/** + * All valid swappers (both direct and service-wallet that support destination addresses) + */ +export const VALID_SWAPPERS: readonly SwapperName[] = [ + ...DIRECT_SWAPPERS, + ...SERVICE_WALLET_SWAPPERS, +] as const; + +/** + * Set of valid swappers for O(1) lookup + */ +export const VALID_SWAPPERS_SET: ReadonlySet = new Set(VALID_SWAPPERS); + +/** + * Check if a swapper is excluded (doesn't support destination addresses) + */ +export function isExcludedSwapper(swapperName: SwapperName): boolean { + return EXCLUDED_SWAPPERS_SET.has(swapperName); +} + +/** + * Check if a swapper is valid for send-swap operations + */ +export function isValidSwapper(swapperName: SwapperName): boolean { + return VALID_SWAPPERS_SET.has(swapperName); +} + +/** + * Check if a swapper is a direct execution swapper + */ +export function isDirectSwapper(swapperName: SwapperName): boolean { + return DIRECT_SWAPPERS_SET.has(swapperName); +} + +/** + * Check if a swapper is a service-wallet swapper (that supports destination addresses) + */ +export function isServiceWalletSwapper(swapperName: SwapperName): boolean { + return SERVICE_WALLET_SWAPPERS_SET.has(swapperName); +} + +/** + * Filter a list of swapper names to only include valid ones + * (excludes swappers that don't support destination addresses) + */ +export function filterValidSwappers(swapperNames: SwapperName[]): SwapperName[] { + return swapperNames.filter((name) => !isExcludedSwapper(name)); +} + +/** + * Get the swapper type for a given swapper name + */ +export function getSwapperTypeFromConfig(swapperName: SwapperName): SwapperType { + if (DIRECT_SWAPPERS_SET.has(swapperName)) { + return SwapperType.DIRECT; + } + return SwapperType.SERVICE_WALLET; +} + +/** + * Full swapper configuration map + * Provides detailed configuration for each swapper + */ +export const SWAPPER_CONFIGS: ReadonlyMap = new Map([ + // Direct execution swappers - provide their own deposit addresses + [ + SwapperName.Chainflip, + { + name: SwapperName.Chainflip, + type: SwapperType.DIRECT, + supportsDestinationAddress: true, + description: 'Uses requestDepositAddressV2() API with fillOrKillParams', + }, + ], + [ + SwapperName.NearIntents, + { + name: SwapperName.NearIntents, + type: SwapperType.DIRECT, + supportsDestinationAddress: true, + description: 'Uses 1Click REST API with JWT authentication', + }, + ], + + // Service-wallet swappers - require service to custody and execute + [ + SwapperName.THORChain, + { + name: SwapperName.THORChain, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Memo-based routing, rate limited 1 req/sec on /quote', + }, + ], + [ + SwapperName.Jupiter, + { + name: SwapperName.Jupiter, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Solana swap execution (Solana only)', + }, + ], + [ + SwapperName.Relay, + { + name: SwapperName.Relay, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Cross-chain bridging', + }, + ], + [ + SwapperName.Mayachain, + { + name: SwapperName.Mayachain, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Maya protocol swaps', + }, + ], + [ + SwapperName.ButterSwap, + { + name: SwapperName.ButterSwap, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Multi-chain swaps', + }, + ], + [ + SwapperName.Bebop, + { + name: SwapperName.Bebop, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: true, + description: 'Intent-based swaps', + }, + ], + + // Excluded swappers - no destination address support + [ + SwapperName.Zrx, + { + name: SwapperName.Zrx, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.CowSwap, + { + name: SwapperName.CowSwap, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.ArbitrumBridge, + { + name: SwapperName.ArbitrumBridge, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'Disabled for simplicity - EXCLUDED', + }, + ], + [ + SwapperName.Portals, + { + name: SwapperName.Portals, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.Cetus, + { + name: SwapperName.Cetus, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.Sunio, + { + name: SwapperName.Sunio, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.Avnu, + { + name: SwapperName.Avnu, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], + [ + SwapperName.Stonfi, + { + name: SwapperName.Stonfi, + type: SwapperType.SERVICE_WALLET, + supportsDestinationAddress: false, + description: 'No receiveAddress support - EXCLUDED', + }, + ], +]); + +/** + * Get swapper configuration by name + */ +export function getSwapperConfig(swapperName: SwapperName): SwapperConfig | undefined { + return SWAPPER_CONFIGS.get(swapperName); +} diff --git a/apps/send-swap-service/src/swappers/swapper-manager.service.ts b/apps/send-swap-service/src/swappers/swapper-manager.service.ts new file mode 100644 index 0000000..40900f8 --- /dev/null +++ b/apps/send-swap-service/src/swappers/swapper-manager.service.ts @@ -0,0 +1,234 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { + SwapperType, + SwapperName, + SwapperConfig, + AvailableSwapper, + SwapperQuoteRequest, +} from './swapper.types'; +import { + SWAPPER_CONFIGS, + EXCLUDED_SWAPPERS, + VALID_SWAPPERS, + DIRECT_SWAPPERS, + SERVICE_WALLET_SWAPPERS, + isExcludedSwapper, + isValidSwapper, + filterValidSwappers, + getSwapperConfig, + getSwapperTypeFromConfig, +} from './swapper-config'; + +/** + * SwapperManagerService handles swapper classification, filtering, and management. + * + * Key responsibilities: + * - Classify swappers as DIRECT or SERVICE_WALLET + * - Filter out swappers that don't support destination addresses + * - Provide swapper configuration for quote generation + * + * Configuration is externalized to swapper-config.ts for easy updates. + */ +@Injectable() +export class SwapperManagerService { + private readonly logger = new Logger(SwapperManagerService.name); + + /** + * Get swapper type classification (DIRECT or SERVICE_WALLET) + */ + getSwapperType(swapperName: SwapperName): SwapperType { + const config = getSwapperConfig(swapperName); + if (!config) { + this.logger.warn( + `Unknown swapper: ${swapperName}, defaulting to SERVICE_WALLET`, + ); + return SwapperType.SERVICE_WALLET; + } + return config.type; + } + + /** + * Check if a swapper is a direct execution swapper + */ + isDirectSwapper(swapperName: SwapperName): boolean { + return this.getSwapperType(swapperName) === SwapperType.DIRECT; + } + + /** + * Check if a swapper is a service-wallet swapper + */ + isServiceWalletSwapper(swapperName: SwapperName): boolean { + return this.getSwapperType(swapperName) === SwapperType.SERVICE_WALLET; + } + + /** + * Check if a swapper supports destination addresses (required for send-swap) + */ + supportsDestinationAddress(swapperName: SwapperName): boolean { + const config = getSwapperConfig(swapperName); + if (!config) { + this.logger.warn( + `Unknown swapper: ${swapperName}, assuming no destination address support`, + ); + return false; + } + return config.supportsDestinationAddress; + } + + /** + * Check if a swapper is excluded (doesn't support destination addresses) + */ + isExcludedSwapper(swapperName: SwapperName): boolean { + return isExcludedSwapper(swapperName); + } + + /** + * Check if a swapper is valid for send-swap operations + */ + isValidSwapper(swapperName: SwapperName): boolean { + return isValidSwapper(swapperName); + } + + /** + * Get configuration for a specific swapper + */ + getSwapperConfig(swapperName: SwapperName): SwapperConfig | undefined { + return getSwapperConfig(swapperName); + } + + /** + * Get all valid swappers for send-swap (filters out excluded ones) + */ + getValidSwappers(): AvailableSwapper[] { + const validSwappers: AvailableSwapper[] = []; + + for (const [name, config] of SWAPPER_CONFIGS.entries()) { + if (config.supportsDestinationAddress) { + validSwappers.push({ + name, + type: config.type, + config, + }); + } + } + + this.logger.debug( + `Found ${validSwappers.length} valid swappers for send-swap`, + ); + return validSwappers; + } + + /** + * Get all direct swappers + */ + getDirectSwappers(): AvailableSwapper[] { + return this.getValidSwappers().filter( + (s) => s.type === SwapperType.DIRECT, + ); + } + + /** + * Get all service-wallet swappers (that support destination addresses) + */ + getServiceWalletSwappers(): AvailableSwapper[] { + return this.getValidSwappers().filter( + (s) => s.type === SwapperType.SERVICE_WALLET, + ); + } + + /** + * Get excluded swappers (for logging/debugging) + */ + getExcludedSwappers(): SwapperConfig[] { + const excluded: SwapperConfig[] = []; + + for (const config of SWAPPER_CONFIGS.values()) { + if (!config.supportsDestinationAddress) { + excluded.push(config); + } + } + + return excluded; + } + + /** + * Get list of excluded swapper names + */ + getExcludedSwapperNames(): readonly SwapperName[] { + return EXCLUDED_SWAPPERS; + } + + /** + * Get list of valid swapper names + */ + getValidSwapperNames(): readonly SwapperName[] { + return VALID_SWAPPERS; + } + + /** + * Get list of direct swapper names + */ + getDirectSwapperNames(): readonly SwapperName[] { + return DIRECT_SWAPPERS; + } + + /** + * Get list of service-wallet swapper names + */ + getServiceWalletSwapperNames(): readonly SwapperName[] { + return SERVICE_WALLET_SWAPPERS; + } + + /** + * Filter a list of swapper names to only include valid ones + */ + filterValidSwapperNames(swapperNames: SwapperName[]): SwapperName[] { + return filterValidSwappers(swapperNames); + } + + /** + * Log swapper classification summary (useful for debugging at startup) + */ + logSwapperSummary(): void { + const directSwappers = this.getDirectSwappers(); + const serviceWalletSwappers = this.getServiceWalletSwappers(); + const excludedSwappers = this.getExcludedSwappers(); + + this.logger.log('=== Swapper Classification Summary ==='); + this.logger.log( + `Direct swappers (${directSwappers.length}): ${directSwappers.map((s) => s.name).join(', ')}`, + ); + this.logger.log( + `Service-wallet swappers (${serviceWalletSwappers.length}): ${serviceWalletSwappers.map((s) => s.name).join(', ')}`, + ); + this.logger.log( + `Excluded swappers (${excludedSwappers.length}): ${excludedSwappers.map((s) => s.name).join(', ')}`, + ); + } + + /** + * Validate a quote request can be processed by the given swapper + */ + validateSwapperForQuote( + swapperName: SwapperName, + _request: SwapperQuoteRequest, + ): { valid: boolean; reason?: string } { + const config = getSwapperConfig(swapperName); + + if (!config) { + return { valid: false, reason: `Unknown swapper: ${swapperName}` }; + } + + if (!config.supportsDestinationAddress) { + return { + valid: false, + reason: `${swapperName} does not support destination addresses`, + }; + } + + // Additional validation can be added here based on asset pairs, + // chain support, etc. + + return { valid: true }; + } +} diff --git a/apps/send-swap-service/src/swappers/swapper.types.ts b/apps/send-swap-service/src/swappers/swapper.types.ts new file mode 100644 index 0000000..c65d0dc --- /dev/null +++ b/apps/send-swap-service/src/swappers/swapper.types.ts @@ -0,0 +1,97 @@ +/** + * Swapper type classification for send-swap-service + * + * DIRECT: Swappers that provide their own deposit addresses and handle swaps natively. + * The service doesn't need to custody funds - user deposits go directly to the swapper. + * Examples: Chainflip, NEAR Intents + * + * SERVICE_WALLET: Swappers that require the service to receive funds first and execute + * the swap on behalf of the user. Requires gas fee overhead calculation. + * Examples: THORChain, Jupiter, Relay, Mayachain + */ +export enum SwapperType { + DIRECT = 'DIRECT', + SERVICE_WALLET = 'SERVICE_WALLET', +} + +/** + * Swapper names as constants for type safety + * Based on @shapeshiftoss/swapper SwapperName enum + */ +export enum SwapperName { + // Direct execution swappers + Chainflip = 'Chainflip', + NearIntents = 'NearIntents', + + // Service-wallet swappers + THORChain = 'THORChain', + Jupiter = 'Jupiter', + Relay = 'Relay', + Mayachain = 'Mayachain', + ButterSwap = 'ButterSwap', + Bebop = 'Bebop', + + // Excluded swappers (no destination address support) + Zrx = 'Zrx', + CowSwap = 'CowSwap', + ArbitrumBridge = 'ArbitrumBridge', + Portals = 'Portals', + Cetus = 'Cetus', + Sunio = 'Sunio', + Avnu = 'Avnu', + Stonfi = 'Stonfi', +} + +/** + * Swapper configuration interface + */ +export interface SwapperConfig { + name: SwapperName; + type: SwapperType; + supportsDestinationAddress: boolean; + description: string; +} + +/** + * Deposit address information returned by direct swappers + */ +export interface DirectSwapperDepositInfo { + depositAddress: string; + depositMemo?: string; + expiresAt?: Date; + depositChannel?: string; +} + +/** + * Quote request interface for swapper manager + */ +export interface SwapperQuoteRequest { + sellAssetId: string; + buyAssetId: string; + sellAmountCryptoBaseUnit: string; + receiveAddress: string; + slippageTolerancePercentage?: number; +} + +/** + * Swapper quote response interface + */ +export interface SwapperQuote { + swapperName: SwapperName; + swapperType: SwapperType; + depositAddress: string; + depositMemo?: string; + expectedBuyAmountCryptoBaseUnit: string; + expiresAt: Date; + gasOverheadBaseUnit?: string; + metadata?: Record; +} + +/** + * Result of getting available swappers for a pair + */ +export interface AvailableSwapper { + name: SwapperName; + type: SwapperType; + config: SwapperConfig; +} diff --git a/apps/send-swap-service/src/swappers/swappers.module.ts b/apps/send-swap-service/src/swappers/swappers.module.ts new file mode 100644 index 0000000..95bed18 --- /dev/null +++ b/apps/send-swap-service/src/swappers/swappers.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { SwapperManagerService } from './swapper-manager.service'; +import { GasCalculatorService } from './gas-calculator.service'; + +@Module({ + imports: [ConfigModule], + providers: [SwapperManagerService, GasCalculatorService], + exports: [SwapperManagerService, GasCalculatorService], +}) +export class SwappersModule {} diff --git a/apps/send-swap-service/src/wallet/wallet-init.service.ts b/apps/send-swap-service/src/wallet/wallet-init.service.ts new file mode 100644 index 0000000..d4aca00 --- /dev/null +++ b/apps/send-swap-service/src/wallet/wallet-init.service.ts @@ -0,0 +1,83 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { WalletManagerService } from './wallet-manager.service'; + +/** + * WalletInitService handles explicit wallet initialization at startup. + * This service should be called from main.ts before starting the HTTP listener + * to ensure all wallets are ready to generate deposit addresses. + */ +@Injectable() +export class WalletInitService { + private readonly logger = new Logger(WalletInitService.name); + + constructor(private walletManagerService: WalletManagerService) {} + + /** + * Initialize all wallet services and verify they are ready for use. + * This method should be called from main.ts before app.listen(). + * + * The initialization process: + * 1. Ensures the HD wallet is initialized from the mnemonic + * 2. Verifies wallet can generate addresses for all supported chains + * 3. Logs the primary deposit addresses for each chain type + */ + async initializeWallets(): Promise { + this.logger.log('Initializing wallets...'); + + try { + // Ensure wallet is initialized (may already be done via OnModuleInit) + await this.walletManagerService.initializeWallet(); + + // Verify wallet is ready by checking initialization status + if (!this.walletManagerService.isInitialized()) { + throw new Error('Wallet initialization failed - wallet not ready'); + } + + // Generate and log primary deposit addresses for verification + await this.verifyAddressGeneration(); + + this.logger.log('All wallets initialized successfully'); + } catch (error) { + this.logger.error('Failed to initialize wallets:', error); + throw error; + } + } + + /** + * Verify wallet can generate addresses for all supported chain types. + * This is a startup health check to ensure the wallet is properly configured. + */ + private async verifyAddressGeneration(): Promise { + this.logger.log('Verifying address generation for all chain types...'); + + try { + // Verify EVM address generation + const evmAddress = await this.walletManagerService.getEvmAddress('ETH', 0, 0); + this.logger.log(`EVM deposit address: ${evmAddress.address}`); + + // Verify UTXO address generation (BTC as primary) + const btcAddress = await this.walletManagerService.getUtxoAddress('BTC', 0, 0); + this.logger.log(`BTC deposit address: ${btcAddress.address}`); + + // Verify Cosmos address generation (ATOM as primary) + const atomAddress = await this.walletManagerService.getCosmosAddress('ATOM', 0, 0); + this.logger.log(`ATOM deposit address: ${atomAddress.address}`); + + // Verify Solana address generation + const solanaAddress = await this.walletManagerService.getSolanaAddress(0); + this.logger.log(`Solana deposit address: ${solanaAddress.address}`); + + this.logger.log('Address generation verified for all chain types'); + } catch (error) { + this.logger.error('Address generation verification failed:', error); + throw error; + } + } + + /** + * Get the wallet manager service instance. + */ + getWalletManager(): WalletManagerService { + return this.walletManagerService; + } +} diff --git a/apps/send-swap-service/src/wallet/wallet-manager.service.ts b/apps/send-swap-service/src/wallet/wallet-manager.service.ts new file mode 100644 index 0000000..f8cb466 --- /dev/null +++ b/apps/send-swap-service/src/wallet/wallet-manager.service.ts @@ -0,0 +1,635 @@ +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { + Keyring, + bip32ToAddressNList, + ETHGetAddress, + BTCGetAddress, + BTCInputScriptType, + CosmosGetAddress, + SolanaGetAddress, +} from '@shapeshiftoss/hdwallet-core'; +import { NativeHDWallet, NativeAdapter } from '@shapeshiftoss/hdwallet-native'; + +/** + * Supported EVM chains and their identifiers. + * All EVM chains use the same SLIP44 coin type (60) since they share the same + * address derivation scheme. + */ +export type EvmChain = 'ETH' | 'AVAX' | 'BSC' | 'POLYGON' | 'OPTIMISM' | 'ARBITRUM' | 'BASE' | 'GNOSIS'; + +/** + * EVM address result with metadata + */ +export interface EvmAddressResult { + address: string; + chain: EvmChain; + derivationPath: string; + accountIndex: number; +} + +/** + * Supported UTXO chains and their identifiers. + * Each UTXO chain has its own SLIP44 coin type and address format. + */ +export type UtxoChain = 'BTC' | 'LTC' | 'DOGE' | 'BCH'; + +/** + * UTXO address result with metadata + */ +export interface UtxoAddressResult { + address: string; + chain: UtxoChain; + derivationPath: string; + accountIndex: number; + scriptType: BTCInputScriptType; +} + +/** + * UTXO chain configuration including SLIP44 coin type and preferred script type. + * - BTC/LTC: Use SegWit (P2WPKH) with BIP84 derivation for modern address format + * - DOGE/BCH: Use Legacy (P2PKH) with BIP44 derivation as they don't support SegWit + */ +const UTXO_CHAIN_CONFIG: Record< + UtxoChain, + { coinName: string; scriptType: BTCInputScriptType; purpose: number } +> = { + BTC: { + coinName: 'Bitcoin', + scriptType: BTCInputScriptType.SpendWitness, // Native SegWit (bech32) + purpose: 84, // BIP84 for native SegWit + }, + LTC: { + coinName: 'Litecoin', + scriptType: BTCInputScriptType.SpendWitness, // Native SegWit (bech32) + purpose: 84, // BIP84 for native SegWit + }, + DOGE: { + coinName: 'Dogecoin', + scriptType: BTCInputScriptType.SpendAddress, // Legacy P2PKH + purpose: 44, // BIP44 for legacy + }, + BCH: { + coinName: 'BitcoinCash', + scriptType: BTCInputScriptType.SpendAddress, // Legacy P2PKH + purpose: 44, // BIP44 for legacy + }, +}; + +/** + * Supported Cosmos-SDK chains and their identifiers. + * Each Cosmos-SDK chain has its own address prefix (bech32) but shares + * the same SLIP44 coin type (118) for most chains. + */ +export type CosmosChain = 'ATOM' | 'OSMO'; + +/** + * Cosmos-SDK address result with metadata + */ +export interface CosmosAddressResult { + address: string; + chain: CosmosChain; + derivationPath: string; + accountIndex: number; +} + +/** + * Cosmos-SDK chain configuration including coin name for SLIP44 lookup. + * - ATOM: Cosmos Hub with 'cosmos' bech32 prefix, SLIP44 coin type 118 + * - OSMO: Osmosis with 'osmo' bech32 prefix, also uses SLIP44 coin type 118 + * + * Both chains use BIP44 standard derivation: m/44'/118'/account'/0/addressIndex + */ +const COSMOS_CHAIN_CONFIG: Record = { + ATOM: { + coinName: 'Atom', // Maps to SLIP44 coin type 118 + }, + OSMO: { + coinName: 'Osmo', // Osmosis also uses SLIP44 coin type 118 + }, +}; + +/** + * Solana address result with metadata + */ +export interface SolanaAddressResult { + address: string; + derivationPath: string; + accountIndex: number; +} + +/** + * Solana uses SLIP44 coin type 501. + * Standard derivation path: m/44'/501'/account'/0' + * Note: Solana uses a slightly different derivation scheme than most BIP44 chains, + * with the hardened address index at the end. + */ +const SOLANA_COIN_NAME = 'Solana'; // Maps to SLIP44 coin type 501 + +/** + * WalletManagerService manages the HD wallet for all supported chains. + * It initializes a native HD wallet from a mnemonic stored in environment variables + * and provides access to the wallet for address generation and signing. + */ +@Injectable() +export class WalletManagerService implements OnModuleInit { + private readonly logger = new Logger(WalletManagerService.name); + private keyring: Keyring; + private wallet: NativeHDWallet | null = null; + private initialized = false; + + constructor(private configService: ConfigService) { + this.keyring = new Keyring(); + } + + async onModuleInit() { + await this.initializeWallet(); + } + + /** + * Initialize the HD wallet from the mnemonic stored in environment variables. + * This must be called before any wallet operations. + */ + async initializeWallet(): Promise { + if (this.initialized) { + this.logger.debug('Wallet already initialized'); + return; + } + + this.logger.log('Initializing HD wallet...'); + + try { + const mnemonic = this.configService.get('MNEMONIC'); + const passphrase = this.configService.get('WALLET_PASSPHRASE') || ''; + + if (!mnemonic) { + throw new Error('MNEMONIC environment variable is not set'); + } + + // Create native adapter for the keyring + const nativeAdapter = NativeAdapter.useKeyring(this.keyring); + await nativeAdapter.initialize(); + + // Create and load the wallet with the mnemonic + // Generate a unique device ID for this wallet instance + const deviceId = 'shapeshift-send-swap-service'; + const wallet = await nativeAdapter.pairDevice(deviceId); + if (!wallet) { + throw new Error('Failed to pair native wallet device'); + } + + // Load the mnemonic into the wallet + // Note: passphrase parameter indicates whether to use a passphrase, not the passphrase itself + await wallet.loadDevice({ + mnemonic, + passphrase: passphrase.length > 0, // true if passphrase is provided + label: 'ShapeShift Send Swap Service', + }); + + this.wallet = wallet; + this.initialized = true; + + // Log success without exposing sensitive data + const walletId = await wallet.getDeviceID(); + this.logger.log(`HD wallet initialized successfully (device: ${walletId})`); + } catch (error) { + this.logger.error('Failed to initialize HD wallet:', error); + throw error; + } + } + + /** + * Get the initialized HD wallet instance. + * @throws Error if wallet is not initialized + */ + getWallet(): NativeHDWallet { + if (!this.wallet || !this.initialized) { + throw new Error('Wallet not initialized. Call initializeWallet() first.'); + } + return this.wallet; + } + + /** + * Get the keyring instance for managing wallet connections. + */ + getKeyring(): Keyring { + return this.keyring; + } + + /** + * Check if the wallet is initialized and ready for use. + */ + isInitialized(): boolean { + return this.initialized && this.wallet !== null; + } + + /** + * Get the BIP44 derivation path for EVM chains. + * Standard path format: m/44'/60'/account'/0/addressIndex + * All EVM chains use coin type 60 (ETH) as they share the same address format. + * + * @param accountIndex - Account index (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The derivation path string + */ + private getEvmDerivationPath(accountIndex = 0, addressIndex = 0): string { + const coinType = 60; // SLIP44 coin type for Ethereum (used by all EVM chains) + return `m/44'/${coinType}'/${accountIndex}'/0/${addressIndex}`; + } + + /** + * Generate an EVM-compatible wallet address for deposit purposes. + * All EVM chains (ETH, AVAX, BSC, Polygon, etc.) use the same address derivation + * since they're Ethereum-compatible and share the 0x address format. + * + * @param chain - The EVM chain identifier (for metadata purposes) + * @param accountIndex - Account index for address derivation (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The generated address with metadata + * @throws Error if wallet is not initialized + */ + async getEvmAddress( + chain: EvmChain = 'ETH', + accountIndex = 0, + addressIndex = 0, + ): Promise { + const wallet = this.getWallet(); + const derivationPath = this.getEvmDerivationPath(accountIndex, addressIndex); + + this.logger.debug(`Generating ${chain} address at path: ${derivationPath}`); + + const addressNList = bip32ToAddressNList(derivationPath); + + const params: ETHGetAddress = { + addressNList, + showDisplay: false, // Don't require user confirmation (server-side) + }; + + const address = await wallet.ethGetAddress(params); + + if (!address) { + throw new Error(`Failed to generate ${chain} address at path ${derivationPath}`); + } + + return { + address, + chain, + derivationPath, + accountIndex, + }; + } + + /** + * Generate deposit addresses for all supported EVM chains. + * Useful for initializing deposit addresses at startup. + * + * @param accountIndex - Account index for address derivation (default: 0) + * @returns Array of addresses for all EVM chains + */ + async getAllEvmAddresses(accountIndex = 0): Promise { + const evmChains: EvmChain[] = [ + 'ETH', + 'AVAX', + 'BSC', + 'POLYGON', + 'OPTIMISM', + 'ARBITRUM', + 'BASE', + 'GNOSIS', + ]; + + const addresses: EvmAddressResult[] = []; + + for (const chain of evmChains) { + try { + const result = await this.getEvmAddress(chain, accountIndex); + addresses.push(result); + this.logger.debug(`Generated ${chain} address: ${result.address}`); + } catch (error) { + this.logger.error(`Failed to generate address for ${chain}:`, error); + throw error; + } + } + + // All EVM addresses should be identical since they use the same derivation + const uniqueAddresses = new Set(addresses.map((a) => a.address)); + if (uniqueAddresses.size === 1) { + this.logger.log( + `Generated EVM deposit address for ${evmChains.length} chains: ${addresses[0].address}`, + ); + } + + return addresses; + } + + /** + * Get EVM address for a specific quote. + * Uses addressIndex to generate unique deposit addresses per quote. + * + * @param chain - The EVM chain + * @param quoteIndex - Index to derive unique address for this quote + * @returns The generated address + */ + async getEvmDepositAddress(chain: EvmChain, quoteIndex: number): Promise { + const result = await this.getEvmAddress(chain, 0, quoteIndex); + return result.address; + } + + /** + * Get the BIP32 derivation path for UTXO chains. + * - BIP84 (m/84'/coin'/account'/0/index) for SegWit chains (BTC, LTC) + * - BIP44 (m/44'/coin'/account'/0/index) for legacy chains (DOGE, BCH) + * + * @param chain - The UTXO chain + * @param accountIndex - Account index (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The derivation path string + */ + private getUtxoDerivationPath( + chain: UtxoChain, + accountIndex = 0, + addressIndex = 0, + ): string { + const config = UTXO_CHAIN_CONFIG[chain]; + // SLIP44 coin types: BTC=0, LTC=2, DOGE=3, BCH=145 + const coinTypes: Record = { + BTC: 0, + LTC: 2, + DOGE: 3, + BCH: 145, + }; + const coinType = coinTypes[chain]; + return `m/${config.purpose}'/${coinType}'/${accountIndex}'/0/${addressIndex}`; + } + + /** + * Generate a UTXO-chain wallet address for deposit purposes. + * Each UTXO chain uses its own SLIP44 coin type and may use different + * address formats (SegWit for BTC/LTC, Legacy for DOGE/BCH). + * + * @param chain - The UTXO chain identifier + * @param accountIndex - Account index for address derivation (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The generated address with metadata + * @throws Error if wallet is not initialized + */ + async getUtxoAddress( + chain: UtxoChain, + accountIndex = 0, + addressIndex = 0, + ): Promise { + const wallet = this.getWallet(); + const config = UTXO_CHAIN_CONFIG[chain]; + const derivationPath = this.getUtxoDerivationPath(chain, accountIndex, addressIndex); + + this.logger.debug(`Generating ${chain} address at path: ${derivationPath}`); + + const addressNList = bip32ToAddressNList(derivationPath); + + const params: BTCGetAddress = { + addressNList, + coin: config.coinName, + scriptType: config.scriptType, + showDisplay: false, // Don't require user confirmation (server-side) + }; + + const address = await wallet.btcGetAddress(params); + + if (!address) { + throw new Error(`Failed to generate ${chain} address at path ${derivationPath}`); + } + + return { + address, + chain, + derivationPath, + accountIndex, + scriptType: config.scriptType, + }; + } + + /** + * Generate deposit addresses for all supported UTXO chains. + * Useful for initializing deposit addresses at startup. + * + * @param accountIndex - Account index for address derivation (default: 0) + * @returns Array of addresses for all UTXO chains + */ + async getAllUtxoAddresses(accountIndex = 0): Promise { + const utxoChains: UtxoChain[] = ['BTC', 'LTC', 'DOGE', 'BCH']; + + const addresses: UtxoAddressResult[] = []; + + for (const chain of utxoChains) { + try { + const result = await this.getUtxoAddress(chain, accountIndex); + addresses.push(result); + this.logger.debug(`Generated ${chain} address: ${result.address}`); + } catch (error) { + this.logger.error(`Failed to generate address for ${chain}:`, error); + throw error; + } + } + + this.logger.log( + `Generated UTXO deposit addresses: BTC=${addresses[0]?.address}, LTC=${addresses[1]?.address}, DOGE=${addresses[2]?.address}, BCH=${addresses[3]?.address}`, + ); + + return addresses; + } + + /** + * Get UTXO address for a specific quote. + * Uses addressIndex to generate unique deposit addresses per quote. + * + * @param chain - The UTXO chain + * @param quoteIndex - Index to derive unique address for this quote + * @returns The generated address + */ + async getUtxoDepositAddress(chain: UtxoChain, quoteIndex: number): Promise { + const result = await this.getUtxoAddress(chain, 0, quoteIndex); + return result.address; + } + + /** + * Get the BIP44 derivation path for Cosmos-SDK chains. + * Standard path format: m/44'/118'/account'/0/addressIndex + * Both ATOM and OSMO use coin type 118 (Cosmos). + * + * @param chain - The Cosmos-SDK chain + * @param accountIndex - Account index (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The derivation path string + */ + private getCosmosDerivationPath( + chain: CosmosChain, + accountIndex = 0, + addressIndex = 0, + ): string { + const coinType = 118; // SLIP44 coin type for Cosmos (used by both ATOM and OSMO) + return `m/44'/${coinType}'/${accountIndex}'/0/${addressIndex}`; + } + + /** + * Generate a Cosmos-SDK wallet address for deposit purposes. + * Each Cosmos-SDK chain uses its own bech32 address prefix: + * - ATOM: cosmos1... + * - OSMO: osmo1... + * + * @param chain - The Cosmos-SDK chain identifier + * @param accountIndex - Account index for address derivation (default: 0) + * @param addressIndex - Address index within the account (default: 0) + * @returns The generated address with metadata + * @throws Error if wallet is not initialized + */ + async getCosmosAddress( + chain: CosmosChain, + accountIndex = 0, + addressIndex = 0, + ): Promise { + const wallet = this.getWallet(); + const derivationPath = this.getCosmosDerivationPath(chain, accountIndex, addressIndex); + + this.logger.debug(`Generating ${chain} address at path: ${derivationPath}`); + + const addressNList = bip32ToAddressNList(derivationPath); + + const params: CosmosGetAddress = { + addressNList, + showDisplay: false, // Don't require user confirmation (server-side) + }; + + const address = await wallet.cosmosGetAddress(params); + + if (!address) { + throw new Error(`Failed to generate ${chain} address at path ${derivationPath}`); + } + + return { + address, + chain, + derivationPath, + accountIndex, + }; + } + + /** + * Generate deposit addresses for all supported Cosmos-SDK chains. + * Useful for initializing deposit addresses at startup. + * + * @param accountIndex - Account index for address derivation (default: 0) + * @returns Array of addresses for all Cosmos-SDK chains + */ + async getAllCosmosAddresses(accountIndex = 0): Promise { + const cosmosChains: CosmosChain[] = ['ATOM', 'OSMO']; + + const addresses: CosmosAddressResult[] = []; + + for (const chain of cosmosChains) { + try { + const result = await this.getCosmosAddress(chain, accountIndex); + addresses.push(result); + this.logger.debug(`Generated ${chain} address: ${result.address}`); + } catch (error) { + this.logger.error(`Failed to generate address for ${chain}:`, error); + throw error; + } + } + + this.logger.log( + `Generated Cosmos-SDK deposit addresses: ATOM=${addresses[0]?.address}, OSMO=${addresses[1]?.address}`, + ); + + return addresses; + } + + /** + * Get Cosmos-SDK address for a specific quote. + * Uses addressIndex to generate unique deposit addresses per quote. + * + * @param chain - The Cosmos-SDK chain + * @param quoteIndex - Index to derive unique address for this quote + * @returns The generated address + */ + async getCosmosDepositAddress(chain: CosmosChain, quoteIndex: number): Promise { + const result = await this.getCosmosAddress(chain, 0, quoteIndex); + return result.address; + } + + /** + * Get the BIP44 derivation path for Solana. + * Standard path format: m/44'/501'/account'/0' + * Solana uses SLIP44 coin type 501 with a hardened address index. + * + * @param accountIndex - Account index (default: 0) + * @returns The derivation path string + */ + private getSolanaDerivationPath(accountIndex = 0): string { + const coinType = 501; // SLIP44 coin type for Solana + return `m/44'/${coinType}'/${accountIndex}'/0'`; + } + + /** + * Generate a Solana wallet address for deposit purposes. + * Solana addresses are base58-encoded public keys derived using Ed25519. + * + * @param accountIndex - Account index for address derivation (default: 0) + * @returns The generated address with metadata + * @throws Error if wallet is not initialized + */ + async getSolanaAddress(accountIndex = 0): Promise { + const wallet = this.getWallet(); + const derivationPath = this.getSolanaDerivationPath(accountIndex); + + this.logger.debug(`Generating Solana address at path: ${derivationPath}`); + + const addressNList = bip32ToAddressNList(derivationPath); + + const params: SolanaGetAddress = { + addressNList, + showDisplay: false, // Don't require user confirmation (server-side) + }; + + const address = await wallet.solanaGetAddress(params); + + if (!address) { + throw new Error(`Failed to generate Solana address at path ${derivationPath}`); + } + + return { + address, + derivationPath, + accountIndex, + }; + } + + /** + * Generate a Solana deposit address. + * Useful for initializing deposit address at startup. + * + * @param accountIndex - Account index for address derivation (default: 0) + * @returns The generated Solana address with metadata + */ + async getSolanaDepositAddressInfo(accountIndex = 0): Promise { + try { + const result = await this.getSolanaAddress(accountIndex); + this.logger.log(`Generated Solana deposit address: ${result.address}`); + return result; + } catch (error) { + this.logger.error('Failed to generate Solana address:', error); + throw error; + } + } + + /** + * Get Solana address for a specific quote. + * Uses accountIndex to generate unique deposit addresses per quote. + * + * @param quoteIndex - Index to derive unique address for this quote + * @returns The generated address + */ + async getSolanaDepositAddress(quoteIndex: number): Promise { + const result = await this.getSolanaAddress(quoteIndex); + return result.address; + } +} diff --git a/apps/send-swap-service/src/wallet/wallet.module.ts b/apps/send-swap-service/src/wallet/wallet.module.ts new file mode 100644 index 0000000..d4192db --- /dev/null +++ b/apps/send-swap-service/src/wallet/wallet.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { WalletManagerService } from './wallet-manager.service'; +import { WalletInitService } from './wallet-init.service'; + +@Module({ + imports: [ConfigModule], + providers: [WalletManagerService, WalletInitService], + exports: [WalletManagerService, WalletInitService], +}) +export class WalletModule {} diff --git a/apps/send-swap-service/tsconfig.json b/apps/send-swap-service/tsconfig.json new file mode 100644 index 0000000..402edb7 --- /dev/null +++ b/apps/send-swap-service/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["jest", "node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/docker-compose.yml b/docker-compose.yml index cb00af5..13306ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -88,6 +88,23 @@ services: notifications-db: condition: service_healthy + send-swap-service: + <<: *service-base + working_dir: /workspace/apps/send-swap-service + command: sh -c "yarn db:push && yarn start:dev" + ports: + - "3004:3004" + env_file: + - ./apps/send-swap-service/.env + environment: + - NODE_ENV=development + - PORT=3004 + depends_on: + packages: + condition: service_started + send-swap-db: + condition: service_healthy + user-db: <<: *db-base environment: @@ -121,7 +138,19 @@ services: volumes: - notifications_db_data:/var/lib/postgresql/data + send-swap-db: + <<: *db-base + environment: + - POSTGRES_DB=send_swap_service + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + ports: + - "5435:5432" + volumes: + - send_swap_db_data:/var/lib/postgresql/data + volumes: user_db_data: swap_db_data: notifications_db_data: + send_swap_db_data: diff --git a/packages/shared-types/src/index.ts b/packages/shared-types/src/index.ts index baf6642..4a2a294 100644 --- a/packages/shared-types/src/index.ts +++ b/packages/shared-types/src/index.ts @@ -134,3 +134,79 @@ export interface VerifySwapAffiliateDto { protocol: string; txHash?: string; } + +// Quote types for send-swap-service +export type QuoteStatus = 'ACTIVE' | 'EXPIRED' | 'DEPOSIT_RECEIVED' | 'EXECUTING' | 'COMPLETED' | 'FAILED'; +export type SwapperType = 'DIRECT' | 'SERVICE_WALLET'; + +export interface Quote { + id: string; + quoteId: string; + status: QuoteStatus; + + // Assets + sellAsset: Asset; + buyAsset: Asset; + sellAmountCryptoBaseUnit: string; + expectedBuyAmountCryptoBaseUnit: string; + + // Addresses + depositAddress: string; + receiveAddress: string; + + // Swapper info + swapperName: string; + swapperType: SwapperType; + + // Gas overhead (for service-wallet swappers) + gasOverheadBaseUnit?: string; + + // Timing + expiresAt: Date; + createdAt: Date; + updatedAt: Date; + + // Execution + depositTxHash?: string; + executionTxHash?: string; + executedAt?: Date; +} + +export interface CreateQuoteDto { + sellAssetId: string; + buyAssetId: string; + sellAmountCryptoBaseUnit: string; + receiveAddress: string; + preferredSwapper?: string; +} + +export interface QuoteResponse { + quoteId: string; + depositAddress: string; + sellAmount: string; + sellAsset: Asset; + expectedBuyAmount: string; + buyAsset: Asset; + expiresAt: string; + qrData: string; + gasOverhead?: string; + swapperName: string; + swapperType: SwapperType; +} + +export interface QuoteStatusResponse { + quoteId: string; + status: QuoteStatus; + depositTxHash?: string; + executionTxHash?: string; + statusMessage: string; + executedAt?: string; +} + +export interface UpdateQuoteStatusDto { + quoteId: string; + status: QuoteStatus; + depositTxHash?: string; + executionTxHash?: string; + statusMessage?: string; +} diff --git a/yarn.lock b/yarn.lock index 3af4574..395f896 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37,7 +37,7 @@ __metadata: languageName: node linkType: hard -"@adraffy/ens-normalize@npm:^1.11.0": +"@adraffy/ens-normalize@npm:^1.10.1, @adraffy/ens-normalize@npm:^1.11.0": version: 1.11.1 resolution: "@adraffy/ens-normalize@npm:1.11.1" checksum: 10/dd19274d9fcaf99bf08a62b64e54f4748de11b235767addbd3f7385ae1b7777bd704d17ff003ffaa3295a0b9d035929381cf3b38329c96260bff96aab8ad7b37 @@ -655,6 +655,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/code-frame@npm:7.28.6" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.28.5" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10/93e7ed9e039e3cb661bdb97c26feebafacc6ec13d745881dae5c7e2708f579475daebe7a3b5d23b183bb940b30744f52f4a5bcb65b4df03b79d82fcb38495784 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.27.2": version: 7.28.5 resolution: "@babel/compat-data@npm:7.28.5" @@ -662,6 +673,36 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/compat-data@npm:7.28.6" + checksum: 10/dc17dfb55711a15f006e34c4610c49b7335fc11b23e192f9e5f625e8ea0f48805e61a57b6b4f5550879332782c93af0b5d6952825fffbb8d4e604b14d698249f + languageName: node + linkType: hard + +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": + version: 7.28.6 + resolution: "@babel/core@npm:7.28.6" + dependencies: + "@babel/code-frame": "npm:^7.28.6" + "@babel/generator": "npm:^7.28.6" + "@babel/helper-compilation-targets": "npm:^7.28.6" + "@babel/helper-module-transforms": "npm:^7.28.6" + "@babel/helpers": "npm:^7.28.6" + "@babel/parser": "npm:^7.28.6" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10/1a150a69c547daf13c457be1fdaf1a0935d02b94605e777e049537ec2f279b4bb442ffbe1c2d8ff62c688878b1d5530a5784daf72ece950d1917fb78717f51d2 + languageName: node + linkType: hard + "@babel/core@npm:^7.23.9, @babel/core@npm:^7.27.4": version: 7.28.5 resolution: "@babel/core@npm:7.28.5" @@ -698,6 +739,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.28.6, @babel/generator@npm:^7.7.2": + version: 7.28.6 + resolution: "@babel/generator@npm:7.28.6" + dependencies: + "@babel/parser": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10/ef2af927e8e0985d02ec4321a242da761a934e927539147c59fdd544034dc7f0e9846f6bf86209aca7a28aee2243ed0fad668adccd48f96d7d6866215173f9af + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" @@ -711,6 +765,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-compilation-targets@npm:7.28.6" + dependencies: + "@babel/compat-data": "npm:^7.28.6" + "@babel/helper-validator-option": "npm:^7.27.1" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10/f512a5aeee4dfc6ea8807f521d085fdca8d66a7d068a6dd5e5b37da10a6081d648c0bbf66791a081e4e8e6556758da44831b331540965dfbf4f5275f3d0a8788 + languageName: node + linkType: hard + "@babel/helper-globals@npm:^7.28.0": version: 7.28.0 resolution: "@babel/helper-globals@npm:7.28.0" @@ -728,6 +795,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-module-imports@npm:7.28.6" + dependencies: + "@babel/traverse": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + checksum: 10/64b1380d74425566a3c288074d7ce4dea56d775d2d3325a3d4a6df1dca702916c1d268133b6f385de9ba5b822b3c6e2af5d3b11ac88e5453d5698d77264f0ec0 + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.28.3": version: 7.28.3 resolution: "@babel/helper-module-transforms@npm:7.28.3" @@ -741,6 +818,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-module-transforms@npm:7.28.6" + dependencies: + "@babel/helper-module-imports": "npm:^7.28.6" + "@babel/helper-validator-identifier": "npm:^7.28.5" + "@babel/traverse": "npm:^7.28.6" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/2e421c7db743249819ee51e83054952709dc2e197c7d5d415b4bdddc718580195704bfcdf38544b3f674efc2eccd4d29a65d38678fc827ed3934a7690984cd8b + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" @@ -748,6 +838,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-plugin-utils@npm:7.28.6" + checksum: 10/21c853bbc13dbdddf03309c9a0477270124ad48989e1ad6524b83e83a77524b333f92edd2caae645c5a7ecf264ec6d04a9ebe15aeb54c7f33c037b71ec521e4a + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-string-parser@npm:7.27.1" @@ -779,6 +876,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helpers@npm:7.28.6" + dependencies: + "@babel/template": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + checksum: 10/213485cdfffc4deb81fc1bf2cefed61bc825049322590ef69690e223faa300a2a4d1e7d806c723bb1f1f538226b9b1b6c356ca94eb47fa7c6d9e9f251ee425e6 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" @@ -790,6 +897,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/parser@npm:7.28.6" + dependencies: + "@babel/types": "npm:^7.28.6" + bin: + parser: ./bin/babel-parser.js + checksum: 10/483a6fb5f9876ec9cbbb98816f2c94f39ae4d1158d35f87e1c4bf19a1f56027c96a1a3962ff0c8c46e8322a6d9e1c80d26b7f9668410df13d5b5769d9447b010 + languageName: node + linkType: hard + "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -878,6 +996,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.7.2": + version: 7.28.6 + resolution: "@babel/plugin-syntax-jsx@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.28.6" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/572e38f5c1bb4b8124300e7e3dd13e82ae84a21f90d3f0786c98cd05e63c78ca1f32d1cfe462dfbaf5e7d5102fa7cd8fd741dfe4f3afc2e01a3b2877dcc8c866 + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -977,6 +1106,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.7.2": + version: 7.28.6 + resolution: "@babel/plugin-syntax-typescript@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.28.6" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/5c55f9c63bd36cf3d7e8db892294c8f85000f9c1526c3a1cc310d47d1e174f5c6f6605e5cc902c4636d885faba7a9f3d5e5edc6b35e4f3b1fd4c2d58d0304fa5 + languageName: node + linkType: hard + "@babel/runtime@npm:7.26.10": version: 7.26.10 resolution: "@babel/runtime@npm:7.26.10" @@ -1004,6 +1144,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.28.6, @babel/template@npm:^7.3.3": + version: 7.28.6 + resolution: "@babel/template@npm:7.28.6" + dependencies: + "@babel/code-frame": "npm:^7.28.6" + "@babel/parser": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + checksum: 10/0ad6e32bf1e7e31bf6b52c20d15391f541ddd645cbd488a77fe537a15b280ee91acd3a777062c52e03eedbc2e1f41548791f6a3697c02476ec5daf49faa38533 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.5": version: 7.28.5 resolution: "@babel/traverse@npm:7.28.5" @@ -1019,6 +1170,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/traverse@npm:7.28.6" + dependencies: + "@babel/code-frame": "npm:^7.28.6" + "@babel/generator": "npm:^7.28.6" + "@babel/helper-globals": "npm:^7.28.0" + "@babel/parser": "npm:^7.28.6" + "@babel/template": "npm:^7.28.6" + "@babel/types": "npm:^7.28.6" + debug: "npm:^4.3.1" + checksum: 10/dd71efe9412433169b805d5c346a6473e539ce30f605752a0d40a0733feba37259bd72bb4ad2ab591e2eaff1ee56633de160c1e98efdc8f373cf33a4a8660275 + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" @@ -1029,6 +1195,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.28.6, @babel/types@npm:^7.3.3": + version: 7.28.6 + resolution: "@babel/types@npm:7.28.6" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.28.5" + checksum: 10/f9c6e52b451065aae5654686ecfc7de2d27dd0fbbc204ee2bd912a71daa359521a32f378981b1cf333ace6c8f86928814452cb9f388a7da59ad468038deb6b5f + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -1045,6 +1221,30 @@ __metadata: languageName: node linkType: hard +"@bithighlander/bitcoin-cash-js-lib@npm:^5.2.1": + version: 5.2.1 + resolution: "@bithighlander/bitcoin-cash-js-lib@npm:5.2.1" + dependencies: + bech32: "npm:^1.1.2" + big-integer: "npm:^1.6.44" + bip174: "npm:^2.0.1" + bip32: "npm:^2.0.4" + bip66: "npm:^1.1.0" + bitcoin-ops: "npm:^1.4.0" + bs58check: "npm:^2.0.0" + create-hash: "npm:^1.1.0" + create-hmac: "npm:^1.1.3" + merkle-lib: "npm:^2.0.10" + pushdata-bitcoin: "npm:^1.0.1" + randombytes: "npm:^2.0.1" + tiny-secp256k1: "npm:^1.1.1" + typeforce: "npm:^1.11.3" + varuint-bitcoin: "npm:^1.0.4" + wif: "npm:^2.0.1" + checksum: 10/0f8891b0d653aa89a2a0396e2c783c3dc35bc8f8562fdf8edb66c5d5eff8994bde0cfad2b8e4ac14663ccbbab62071c4a4182542f4ccf4404cf1aeb40ca2398a + languageName: node + linkType: hard + "@borewit/text-codec@npm:^0.1.0": version: 0.1.1 resolution: "@borewit/text-codec@npm:0.1.1" @@ -1340,6 +1540,13 @@ __metadata: languageName: node linkType: hard +"@eivifj/dot@npm:^1.0.1": + version: 1.0.3 + resolution: "@eivifj/dot@npm:1.0.3" + checksum: 10/896ab27b9f00ba2c1ef9128e0f1453903463f2f652f09023cdd0f5301f31b8a519a72b107359adae5843bea3dc52f3e03d9d876c7a3325fac38769f501aca0a1 + languageName: node + linkType: hard + "@emnapi/core@npm:^1.4.3": version: 1.7.0 resolution: "@emnapi/core@npm:1.7.0" @@ -2311,6 +2518,25 @@ __metadata: languageName: node linkType: hard +"@fioprotocol/fiojs@npm:1.0.1": + version: 1.0.1 + resolution: "@fioprotocol/fiojs@npm:1.0.1" + dependencies: + ajv: "npm:^6.10.2" + babel-runtime: "npm:6.26.0" + bigi: "npm:^1.4.2" + browserify-aes: "npm:^1.2.0" + bs58: "npm:^4.0.1" + create-hash: "npm:^1.2.0" + create-hmac: "npm:^1.1.7" + ecurve: "npm:^1.0.6" + long: "npm:^4.0.0" + randombytes: "npm:^2.1.0" + text-encoding: "npm:0.7.0" + checksum: 10/2106c9d7a4b318e39df84363a1b892c5fc05fa6fab2d0be33bb4443bba9b259c7d299c2e266a5ca249c9a4f05b09898ecfd5c88dd2c8d50d5e42fd7bec0b9a90 + languageName: node + linkType: hard + "@gql.tada/cli-utils@npm:1.7.2": version: 1.7.2 resolution: "@gql.tada/cli-utils@npm:1.7.2" @@ -2759,6 +2985,20 @@ __metadata: languageName: node linkType: hard +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 10/4a80c750e8a31f344233cb9951dee9b77bf6b89377cb131f8b3cde07ff218f504370133a5963f6a786af4d2ce7f85642db206ff7a15f99fe58df4c38ac04899e + languageName: node + linkType: hard + "@jest/core@npm:30.2.0": version: 30.2.0 resolution: "@jest/core@npm:30.2.0" @@ -2800,6 +3040,47 @@ __metadata: languageName: node linkType: hard +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/reporters": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-changed-files: "npm:^29.7.0" + jest-config: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-resolve-dependencies: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-ansi: "npm:^6.0.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 10/ab6ac2e562d083faac7d8152ec1cc4eccc80f62e9579b69ed40aedf7211a6b2d57024a6cd53c4e35fd051c39a236e86257d1d99ebdb122291969a0a04563b51e + languageName: node + linkType: hard + "@jest/diff-sequences@npm:30.0.1": version: 30.0.1 resolution: "@jest/diff-sequences@npm:30.0.1" @@ -2819,6 +3100,18 @@ __metadata: languageName: node linkType: hard +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" + dependencies: + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + checksum: 10/90b5844a9a9d8097f2cf107b1b5e57007c552f64315da8c1f51217eeb0a9664889d3f145cdf8acf23a84f4d8309a6675e27d5b059659a004db0ea9546d1c81a8 + languageName: node + linkType: hard + "@jest/expect-utils@npm:30.2.0": version: 30.2.0 resolution: "@jest/expect-utils@npm:30.2.0" @@ -2828,6 +3121,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + checksum: 10/ef8d379778ef574a17bde2801a6f4469f8022a46a5f9e385191dc73bb1fc318996beaed4513fbd7055c2847227a1bed2469977821866534593a6e52a281499ee + languageName: node + linkType: hard + "@jest/expect@npm:30.2.0": version: 30.2.0 resolution: "@jest/expect@npm:30.2.0" @@ -2838,6 +3140,16 @@ __metadata: languageName: node linkType: hard +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" + dependencies: + expect: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + checksum: 10/fea6c3317a8da5c840429d90bfe49d928e89c9e89fceee2149b93a11b7e9c73d2f6e4d7cdf647163da938fc4e2169e4490be6bae64952902bc7a701033fd4880 + languageName: node + linkType: hard + "@jest/fake-timers@npm:30.2.0": version: 30.2.0 resolution: "@jest/fake-timers@npm:30.2.0" @@ -2852,6 +3164,20 @@ __metadata: languageName: node linkType: hard +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@sinonjs/fake-timers": "npm:^10.0.2" + "@types/node": "npm:*" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/9b394e04ffc46f91725ecfdff34c4e043eb7a16e1d78964094c9db3fde0b1c8803e45943a980e8c740d0a3d45661906de1416ca5891a538b0660481a3a828c27 + languageName: node + linkType: hard + "@jest/get-type@npm:30.1.0": version: 30.1.0 resolution: "@jest/get-type@npm:30.1.0" @@ -2871,6 +3197,18 @@ __metadata: languageName: node linkType: hard +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + jest-mock: "npm:^29.7.0" + checksum: 10/97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 + languageName: node + linkType: hard + "@jest/pattern@npm:30.0.1": version: 30.0.1 resolution: "@jest/pattern@npm:30.0.1" @@ -2917,6 +3255,43 @@ __metadata: languageName: node linkType: hard +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" + dependencies: + "@bcoe/v8-coverage": "npm:^0.2.3" + "@jest/console": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + collect-v8-coverage: "npm:^1.0.0" + exit: "npm:^0.1.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + istanbul-lib-coverage: "npm:^3.0.0" + istanbul-lib-instrument: "npm:^6.0.0" + istanbul-lib-report: "npm:^3.0.0" + istanbul-lib-source-maps: "npm:^4.0.0" + istanbul-reports: "npm:^3.1.3" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + slash: "npm:^3.0.0" + string-length: "npm:^4.0.1" + strip-ansi: "npm:^6.0.0" + v8-to-istanbul: "npm:^9.0.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 10/a17d1644b26dea14445cedd45567f4ba7834f980be2ef74447204e14238f121b50d8b858fde648083d2cd8f305f81ba434ba49e37a5f4237a6f2a61180cc73dc + languageName: node + linkType: hard + "@jest/schemas@npm:30.0.5": version: 30.0.5 resolution: "@jest/schemas@npm:30.0.5" @@ -2926,6 +3301,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: 10/910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 + languageName: node + linkType: hard + "@jest/snapshot-utils@npm:30.2.0": version: 30.2.0 resolution: "@jest/snapshot-utils@npm:30.2.0" @@ -2949,6 +3333,17 @@ __metadata: languageName: node linkType: hard +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.18" + callsites: "npm:^3.0.0" + graceful-fs: "npm:^4.2.9" + checksum: 10/bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb + languageName: node + linkType: hard + "@jest/test-result@npm:30.2.0": version: 30.2.0 resolution: "@jest/test-result@npm:30.2.0" @@ -2961,6 +3356,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + collect-v8-coverage: "npm:^1.0.0" + checksum: 10/c073ab7dfe3c562bff2b8fee6cc724ccc20aa96bcd8ab48ccb2aa309b4c0c1923a9e703cea386bd6ae9b71133e92810475bb9c7c22328fc63f797ad3324ed189 + languageName: node + linkType: hard + "@jest/test-sequencer@npm:30.2.0": version: 30.2.0 resolution: "@jest/test-sequencer@npm:30.2.0" @@ -2973,6 +3380,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 10/4420c26a0baa7035c5419b0892ff8ffe9a41b1583ec54a10db3037cd46a7e29dd3d7202f8aa9d376e9e53be5f8b1bc0d16e1de6880a6d319b033b01dc4c8f639 + languageName: node + linkType: hard + "@jest/transform@npm:30.2.0": version: 30.2.0 resolution: "@jest/transform@npm:30.2.0" @@ -2996,6 +3415,29 @@ __metadata: languageName: node linkType: hard +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + babel-plugin-istanbul: "npm:^6.1.1" + chalk: "npm:^4.0.0" + convert-source-map: "npm:^2.0.0" + fast-json-stable-stringify: "npm:^2.1.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pirates: "npm:^4.0.4" + slash: "npm:^3.0.0" + write-file-atomic: "npm:^4.0.2" + checksum: 10/30f42293545ab037d5799c81d3e12515790bb58513d37f788ce32d53326d0d72ebf5b40f989e6896739aa50a5f77be44686e510966370d58511d5ad2637c68c1 + languageName: node + linkType: hard + "@jest/types@npm:30.2.0": version: 30.2.0 resolution: "@jest/types@npm:30.2.0" @@ -3011,6 +3453,20 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" + dependencies: + "@jest/schemas": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + "@types/istanbul-reports": "npm:^3.0.0" + "@types/node": "npm:*" + "@types/yargs": "npm:^17.0.8" + chalk: "npm:^4.0.0" + checksum: 10/f74bf512fd09bbe2433a2ad460b04668b7075235eea9a0c77d6a42222c10a79b9747dc2b2a623f140ed40d6865a2ed8f538f3cbb75169120ea863f29a7ed76cd + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.13 resolution: "@jridgewell/gen-mapping@npm:0.3.13" @@ -3065,7 +3521,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -3824,7 +4280,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.9.7, @noble/curves@npm:^1.4.2, @noble/curves@npm:^1.7.0, @noble/curves@npm:~1.9.0": +"@noble/curves@npm:1.9.7, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.4.2, @noble/curves@npm:^1.6.0, @noble/curves@npm:^1.7.0, @noble/curves@npm:~1.9.0": version: 1.9.7 resolution: "@noble/curves@npm:1.9.7" dependencies: @@ -3842,6 +4298,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:~1.7.0": + version: 1.7.0 + resolution: "@noble/curves@npm:1.7.0" + dependencies: + "@noble/hashes": "npm:1.6.0" + checksum: 10/2a11ef4895907d0b241bd3b72f9e6ebe56f0e705949bfd5efe003f25233549f620d287550df2d24ad56a1f953b82ec5f7cf4bd7cb78b1b2e76eb6dd516d44cf8 + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -3856,13 +4321,27 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.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": +"@noble/hashes@npm:1.6.0": + version: 1.6.0 + resolution: "@noble/hashes@npm:1.6.0" + checksum: 10/b44b043b02adbecd33596adeed97d9f9864c24a2410f7ac3b847986c2ecf1f6f0df76024b3f1b14d6ea954932960d88898fe551fb9d39844a8b870e9f9044ea1 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.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: 10/474b7f56bc6fb2d5b3a42132561e221b0ea4f91e590f4655312ca13667840896b34195e2b53b7f097ec080a1fdd3b58d902c2a8d0fbdf51d2e238b53808a177e languageName: node linkType: hard +"@noble/hashes@npm:~1.6.0": + version: 1.6.1 + resolution: "@noble/hashes@npm:1.6.1" + checksum: 10/74d9ad7b1437a22ba3b877584add3367587fbf818113152f293025d20d425aa74c191d18d434797312f2270458bc9ab3241c34d14ec6115fb16438b3248f631f + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -4291,6 +4770,40 @@ __metadata: languageName: node linkType: hard +"@pioneer-platform/loggerdog@npm:^8.0.1, @pioneer-platform/loggerdog@npm:^8.1.31": + version: 8.11.0 + resolution: "@pioneer-platform/loggerdog@npm:8.11.0" + dependencies: + os: "npm:^0.1.1" + ts-node: "npm:^10.9.1" + typescript: "npm:^5.0.4" + checksum: 10/fa70096c25276b6decc49d2ed953cf120e05636e023fbcf282302421de70dc6d1cfa90f14cd934e880ee3ed020c4fe2e1f4a6bda6ea205a3d842f18dc4a6094d + languageName: node + linkType: hard + +"@pioneer-platform/pioneer-coins@npm:^8.1.19": + version: 8.1.90 + resolution: "@pioneer-platform/pioneer-coins@npm:8.1.90" + dependencies: + "@pioneer-platform/loggerdog": "npm:^8.1.31" + bech32: "npm:^1.1.4" + bignumber.js: "npm:^9.0.1" + bitcoin-regex: "npm:^2.0.0" + bitcoincash-regex: "npm:^1.1.8" + bs58check: "npm:^3.0.1" + crypto-js: "npm:^4.0.0" + dash-regex: "npm:^1.0.10" + dogecoin-regex: "npm:^1.0.9" + ethereum-regex: "npm:^1.1.12" + ethereum-tx-decoder: "npm:^3.0.0" + litecoin-regex: "npm:^1.0.8" + monero-regex: "npm:^1.0.8" + neo-regex: "npm:^1.0.7" + ripple-regex: "npm:^1.1.8" + checksum: 10/3e0bd436c2fd0fad2278865985b5e955e4ad4d347331c8d7263ebe7b0e4538c95ce24c9c8f6a4df8cac1d92acfd2de601dda258ad22e595825a6c7ce209b0b11 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -4710,7 +5223,7 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.3, @scure/base@npm:^1.2.6, @scure/base@npm:~1.2.5": +"@scure/base@npm:^1.1.3, @scure/base@npm:^1.2.6, @scure/base@npm:~1.2.1, @scure/base@npm:~1.2.5": version: 1.2.6 resolution: "@scure/base@npm:1.2.6" checksum: 10/c1a7bd5e0b0c8f94c36fbc220f4a67cc832b00e2d2065c7d8a404ed81ab1c94c5443def6d361a70fc382db3496e9487fb9941728f0584782b274c18a4bed4187 @@ -4735,7 +5248,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.5.0, @scure/bip32@npm:^1.7.0": version: 1.7.0 resolution: "@scure/bip32@npm:1.7.0" dependencies: @@ -4756,7 +5269,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.4.0, @scure/bip39@npm:^1.6.0": version: 1.6.0 resolution: "@scure/bip39@npm:1.6.0" dependencies: @@ -4766,6 +5279,26 @@ __metadata: languageName: node linkType: hard +"@scure/starknet@npm:1.1.0": + version: 1.1.0 + resolution: "@scure/starknet@npm:1.1.0" + dependencies: + "@noble/curves": "npm:~1.7.0" + "@noble/hashes": "npm:~1.6.0" + checksum: 10/88d846584ccf7ab213472382461d8db5a5b50e1b638205122935e3aff214cbba0703f75ed4d63ed4b1f820fe3c5b08b86013c9c0cc855fd197e36bda4d86f274 + languageName: node + linkType: hard + +"@scure/starknet@npm:^1.0.0": + version: 1.1.2 + resolution: "@scure/starknet@npm:1.1.2" + dependencies: + "@noble/curves": "npm:~1.9.0" + "@noble/hashes": "npm:~1.8.0" + checksum: 10/886c1c6a2db9fd2ac6d375b2360873c32bf6145c8de929bb9be7cc5552a6bbc8570f6aaeb4fd21393092f38a7c1d696dd06e40af0ddce24d2a637da97095e240 + languageName: node + linkType: hard + "@shapeshift/notifications-service@workspace:apps/notifications-service": version: 0.0.0-use.local resolution: "@shapeshift/notifications-service@workspace:apps/notifications-service" @@ -4778,13 +5311,31 @@ __metadata: languageName: unknown linkType: soft -"@shapeshift/shared-types@workspace:*, @shapeshift/shared-types@workspace:packages/shared-types": - version: 0.0.0-use.local - resolution: "@shapeshift/shared-types@workspace:packages/shared-types" - languageName: unknown - linkType: soft - -"@shapeshift/shared-utils@workspace:*, @shapeshift/shared-utils@workspace:packages/shared-utils": +"@shapeshift/send-swap-service@workspace:apps/send-swap-service": + version: 0.0.0-use.local + resolution: "@shapeshift/send-swap-service@workspace:apps/send-swap-service" + dependencies: + "@prisma/client": "npm:6.13.0" + "@shapeshift/shared-types": "workspace:*" + "@shapeshift/shared-utils": "workspace:*" + "@shapeshiftoss/hdwallet-core": "npm:^1.62.21" + "@shapeshiftoss/hdwallet-native": "npm:^1.62.21" + "@types/jest": "npm:^29.5.12" + axios: "npm:^1.6.2" + jest: "npm:^29.7.0" + prisma: "npm:6.13.0" + rxjs: "npm:^7.8.1" + ts-jest: "npm:^29.1.2" + languageName: unknown + linkType: soft + +"@shapeshift/shared-types@workspace:*, @shapeshift/shared-types@workspace:packages/shared-types": + version: 0.0.0-use.local + resolution: "@shapeshift/shared-types@workspace:packages/shared-types" + languageName: unknown + linkType: soft + +"@shapeshift/shared-utils@workspace:*, @shapeshift/shared-utils@workspace:packages/shared-utils": version: 0.0.0-use.local resolution: "@shapeshift/shared-utils@workspace:packages/shared-utils" dependencies: @@ -4830,6 +5381,21 @@ __metadata: languageName: node linkType: hard +"@shapeshiftoss/bitcoinjs-lib@npm:7.0.0-shapeshift.2": + version: 7.0.0-shapeshift.2 + resolution: "@shapeshiftoss/bitcoinjs-lib@npm:7.0.0-shapeshift.2" + dependencies: + "@noble/hashes": "npm:^1.2.0" + bech32: "npm:^2.0.0" + bip174: "npm:^3.0.0" + bs58check: "npm:^4.0.0" + uint8array-tools: "npm:^0.0.9" + valibot: "npm:^0.38.0" + varuint-bitcoin: "npm:^2.0.0" + checksum: 10/50fd1eceb8b257bea8dbce6fb4df9339dd4cfc2ac8df79430328d84030b617a836bdc77af3c911ace7d76fe9d4c2732043f2d696ac49115eb9be080f343e93e3 + languageName: node + linkType: hard + "@shapeshiftoss/blockbook@npm:^9.3.0": version: 9.3.0 resolution: "@shapeshiftoss/blockbook@npm:9.3.0" @@ -4934,6 +5500,24 @@ __metadata: languageName: node linkType: hard +"@shapeshiftoss/hdwallet-core@npm:^1.62.21, @shapeshiftoss/hdwallet-core@npm:^1.62.41, @shapeshiftoss/hdwallet-core@npm:latest": + version: 1.62.41 + resolution: "@shapeshiftoss/hdwallet-core@npm:1.62.41" + dependencies: + "@shapeshiftoss/bitcoinjs-lib": "npm:7.0.0-shapeshift.2" + "@shapeshiftoss/proto-tx-builder": "npm:0.10.0" + "@solana/web3.js": "npm:1.95.8" + bs58check: "npm:^4.0.0" + eip-712: "npm:^1.0.0" + ethers: "npm:5.7.2" + eventemitter2: "npm:^5.0.1" + lodash: "npm:^4.17.21" + rxjs: "npm:^6.4.0" + type-assertions: "npm:^1.1.0" + checksum: 10/5428159ad5834b6efc042da8484fe5db75fb7daba161f764deb1c5bbd76e4ba7426427cd66d8a93501db1f42ced79b9cdafce5ef4b75b69120b0ae20baf53e69 + languageName: node + linkType: hard + "@shapeshiftoss/hdwallet-ledger@npm:1.62.21": version: 1.62.21 resolution: "@shapeshiftoss/hdwallet-ledger@npm:1.62.21" @@ -4964,6 +5548,44 @@ __metadata: languageName: node linkType: hard +"@shapeshiftoss/hdwallet-native@npm:^1.62.21": + version: 1.62.41 + resolution: "@shapeshiftoss/hdwallet-native@npm:1.62.41" + dependencies: + "@bitcoinerlab/secp256k1": "npm:^1.1.1" + "@noble/curves": "npm:^1.4.0" + "@scure/starknet": "npm:^1.0.0" + "@shapeshiftoss/bitcoinjs-lib": "npm:7.0.0-shapeshift.2" + "@shapeshiftoss/hdwallet-core": "npm:^1.62.41" + "@shapeshiftoss/proto-tx-builder": "npm:0.10.0" + "@ton/core": "npm:^0.62.1" + "@ton/crypto": "npm:^3.3.0" + "@ton/ton": "npm:^16.1.0" + "@zxing/text-encoding": "npm:^0.9.0" + bchaddrjs: "npm:^0.4.9" + bech32: "npm:^1.1.4" + bignumber.js: "npm:^9.0.1" + bip32: "npm:^2.0.5" + bip39: "npm:^3.0.2" + bnb-javascript-sdk-nobroadcast: "npm:2.16.15" + bs58check: "npm:^4.0.0" + crypto-js: "npm:^4.0.0" + ecpair: "npm:^3.0.0-rc.0" + eip-712: "npm:^1.0.0" + ethers: "npm:5.7.2" + eventemitter2: "npm:^5.0.1" + funtypes: "npm:^3.0.1" + hash-wasm: "npm:^4.11.0" + lodash: "npm:^4.17.21" + node-fetch: "npm:^2.6.1" + p-lazy: "npm:^3.1.0" + scrypt-js: "npm:^3.0.1" + starknet: "npm:^9.3.0" + tendermint-tx-builder: "npm:1.0.16" + checksum: 10/9287a01a9fb51fc187c45a45a8323091ea079c249b47d67d8927185b46f7928f10f660168861b0846f157024f82c846c07adcdf7a7e87840875f2e685de8b59c + languageName: node + linkType: hard + "@shapeshiftoss/proto-tx-builder@npm:0.10.0": version: 0.10.0 resolution: "@shapeshiftoss/proto-tx-builder@npm:0.10.0" @@ -5132,6 +5754,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 10/297f95ff77c82c54de8c9907f186076e715ff2621c5222ba50b8d40a170661c0c5242c763cba2a4791f0f91cb1d8ffa53ea1d7294570cf8cd4694c0e383e484d + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.34.0": version: 0.34.41 resolution: "@sinclair/typebox@npm:0.34.41" @@ -5146,7 +5775,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/commons@npm:^3.0.1": +"@sinonjs/commons@npm:^3.0.0, @sinonjs/commons@npm:^3.0.1": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" dependencies: @@ -5155,6 +5784,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.0" + checksum: 10/78155c7bd866a85df85e22028e046b8d46cf3e840f72260954f5e3ed5bd97d66c595524305a6841ffb3f681a08f6e5cef572a2cce5442a8a232dc29fb409b83e + languageName: node + linkType: hard + "@sinonjs/fake-timers@npm:^13.0.0": version: 13.0.5 resolution: "@sinonjs/fake-timers@npm:13.0.5" @@ -5920,6 +6558,39 @@ __metadata: languageName: node linkType: hard +"@starknet-io/get-starknet-wallet-standard@npm:^5.0.0": + version: 5.0.0 + resolution: "@starknet-io/get-starknet-wallet-standard@npm:5.0.0" + dependencies: + "@starknet-io/types-js": "npm:^0.7.10" + "@wallet-standard/base": "npm:^1.1.0" + "@wallet-standard/features": "npm:^1.1.0" + ox: "npm:^0.4.4" + checksum: 10/3cba448993cd041875577b96611e0adaed5409898d77dbe5d6fe3580283dd59b48aff4a13c5efaf483ac5e7263caecdb56b102dba1bbbc7886e6a405c40b0c77 + languageName: node + linkType: hard + +"@starknet-io/starknet-types-010@npm:@starknet-io/types-js@0.10.0": + version: 0.10.0 + resolution: "@starknet-io/types-js@npm:0.10.0" + checksum: 10/04d033173ac53876154eb67d74602029199618537b3665ef67600dfeae1c2a3ec0cd9a486ff3d537403ba55acc63d6ae61760286364ad8d09ddbba4d1917ddd1 + languageName: node + linkType: hard + +"@starknet-io/starknet-types-09@npm:@starknet-io/types-js@~0.9.1": + version: 0.9.2 + resolution: "@starknet-io/types-js@npm:0.9.2" + checksum: 10/e71cc4f69c52e763273013801801fba33831c6bff9cc99fa065648647afba895944fc3b6ee438b004dac81367ccbf572396fd4c8b8f38ea16ecd838da87bd192 + languageName: node + linkType: hard + +"@starknet-io/types-js@npm:^0.7.10": + version: 0.7.10 + resolution: "@starknet-io/types-js@npm:0.7.10" + checksum: 10/e7e10878d6d576dcd30c6910a819e8e9cbd72102c71a93be7e0282f229d75c5765d2b5d152f7ae0693987cdb00e3d04f02bad371d91f420ddbbed435aadfbe3e + languageName: node + linkType: hard + "@swc/helpers@npm:^0.5.11": version: 0.5.17 resolution: "@swc/helpers@npm:0.5.17" @@ -5956,6 +6627,51 @@ __metadata: languageName: node linkType: hard +"@ton/core@npm:^0.62.1": + version: 0.62.1 + resolution: "@ton/core@npm:0.62.1" + peerDependencies: + "@ton/crypto": ">=3.2.0" + checksum: 10/f8c477a73f8b4f00451da7ab56c0aae328c6c49b94f01ebac3297da4977a2b315b5dd67da4ce78fc32e3f300e1e2e49e3b981d2de7d160244118856b5a90becf + languageName: node + linkType: hard + +"@ton/crypto-primitives@npm:2.1.0": + version: 2.1.0 + resolution: "@ton/crypto-primitives@npm:2.1.0" + dependencies: + jssha: "npm:3.2.0" + checksum: 10/71119f74461ae17bf2cfe7e0a6fcea8d4e359665ea6878b0c935cfd83ca0d84f9c299df3467adb1b1b7ba50f7d446732f2c13b5ea5e26dc1703a6dc24063be3a + languageName: node + linkType: hard + +"@ton/crypto@npm:^3.3.0": + version: 3.3.0 + resolution: "@ton/crypto@npm:3.3.0" + dependencies: + "@ton/crypto-primitives": "npm:2.1.0" + jssha: "npm:3.2.0" + tweetnacl: "npm:1.0.3" + checksum: 10/0561f2c95df7a53c47262393590bae8c5254ea5802cbecd2fea589073d926f60466292d594171aa3f3375ccaa2b763368fa1023bd5b6ebfe4da16297731cb379 + languageName: node + linkType: hard + +"@ton/ton@npm:^16.1.0": + version: 16.1.0 + resolution: "@ton/ton@npm:16.1.0" + dependencies: + axios: "npm:^1.6.7" + dataloader: "npm:^2.0.0" + symbol.inspect: "npm:1.0.1" + teslabot: "npm:^1.3.0" + zod: "npm:^3.21.4" + peerDependencies: + "@ton/core": ">=0.62.0 <1.0.0" + "@ton/crypto": ">=3.2.0" + checksum: 10/2e77fdfcb248c112f6b25c668dcad67fbd9f3ce9c2a72c4dd156a55c8e54f6187b4db9940c6548d93ec93732f871d3c0715e97afdc9211899f3fa54742f64bc0 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.11 resolution: "@tsconfig/node10@npm:1.0.11" @@ -6049,7 +6765,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.20.5": +"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -6081,7 +6797,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__traverse@npm:*": +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": version: 7.28.0 resolution: "@types/babel__traverse@npm:7.28.0" dependencies: @@ -6205,6 +6921,13 @@ __metadata: languageName: node linkType: hard +"@types/fast-text-encoding@npm:^1.0.1": + version: 1.0.3 + resolution: "@types/fast-text-encoding@npm:1.0.3" + checksum: 10/34ec2bbaf3e3ee36b7b74375293becc735378f77e9cd93b810ad988b42991ee80d30fb942e6ba03adfc1f0cb0e2024a0aeee84475847563ed6782e21c4c0f5f0 + languageName: node + linkType: hard + "@types/google-protobuf@npm:^3.15.5": version: 3.15.12 resolution: "@types/google-protobuf@npm:3.15.12" @@ -6212,6 +6935,15 @@ __metadata: languageName: node linkType: hard +"@types/graceful-fs@npm:^4.1.3": + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" + dependencies: + "@types/node": "npm:*" + checksum: 10/79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 + languageName: node + linkType: hard + "@types/http-cache-semantics@npm:*": version: 4.0.4 resolution: "@types/http-cache-semantics@npm:4.0.4" @@ -6226,7 +6958,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" checksum: 10/3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 @@ -6242,7 +6974,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.4": +"@types/istanbul-reports@npm:^3.0.0, @types/istanbul-reports@npm:^3.0.4": version: 3.0.4 resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: @@ -6251,6 +6983,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.12": + version: 29.5.14 + resolution: "@types/jest@npm:29.5.14" + dependencies: + expect: "npm:^29.0.0" + pretty-format: "npm:^29.0.0" + checksum: 10/59ec7a9c4688aae8ee529316c43853468b6034f453d08a2e1064b281af9c81234cec986be796288f1bbb29efe943bc950e70c8fa8faae1e460d50e3cf9760f9b + languageName: node + linkType: hard + "@types/jest@npm:^30.0.0": version: 30.0.0 resolution: "@types/jest@npm:30.0.0" @@ -6351,6 +7093,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^15.0.2": + version: 15.14.9 + resolution: "@types/node@npm:15.14.9" + checksum: 10/5c9cc5346ca0c565e02f11ae605911aa0892134099a5c730f49b4662635e5fbbbbeb24b2b56752ee14f092d1ca90837a35de428b9e243d7ab63c5907bfb07d89 + languageName: node + linkType: hard + "@types/node@npm:^22.10.7": version: 22.19.1 resolution: "@types/node@npm:22.19.1" @@ -6452,7 +7201,7 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.3": +"@types/stack-utils@npm:^2.0.0, @types/stack-utils@npm:^2.0.3": version: 2.0.3 resolution: "@types/stack-utils@npm:2.0.3" checksum: 10/72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 @@ -6507,6 +7256,15 @@ __metadata: languageName: node linkType: hard +"@types/yargs@npm:^17.0.8": + version: 17.0.35 + resolution: "@types/yargs@npm:17.0.35" + dependencies: + "@types/yargs-parser": "npm:*" + checksum: 10/47bcd4476a4194ea11617ea71cba8a1eddf5505fc39c44336c1a08d452a0de4486aedbc13f47a017c8efbcb5a8aa358d976880663732ebcbc6dbcbbecadb0581 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:8.46.4": version: 8.46.4 resolution: "@typescript-eslint/eslint-plugin@npm:8.46.4" @@ -6923,6 +7681,22 @@ __metadata: languageName: node linkType: hard +"@wallet-standard/base@npm:^1.1.0": + version: 1.1.0 + resolution: "@wallet-standard/base@npm:1.1.0" + checksum: 10/11dbb8ed80566265916ab193ad5eab1585d55996781a88039d2bc4480428b1e778901b2dcff3e688dcac7de45e8a9272026f37f07f1e75168caff581906c5079 + languageName: node + linkType: hard + +"@wallet-standard/features@npm:^1.1.0": + version: 1.1.0 + resolution: "@wallet-standard/features@npm:1.1.0" + dependencies: + "@wallet-standard/base": "npm:^1.1.0" + checksum: 10/e046f813ec4bfea172aeb6c11358a962afe8f9a6961453e621d624f89d8b5fc8a44404dacfe18d33be815df6e9117bbf914009f5a9f9ea91ff90a136043fcac8 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": version: 1.14.1 resolution: "@webassemblyjs/ast@npm:1.14.1" @@ -7095,6 +7869,13 @@ __metadata: languageName: node linkType: hard +"@zxing/text-encoding@npm:^0.9.0": + version: 0.9.0 + resolution: "@zxing/text-encoding@npm:0.9.0" + checksum: 10/268e4ef64b8eaa32b990240bdfd1f7b3e2b501a6ed866a565f7c9747f04ac884fbe0537fe12bb05d9241b98fb111270c0fd0023ef0a02d23a6619b4589e98f6b + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -7109,6 +7890,20 @@ __metadata: languageName: node linkType: hard +"abi-wan-kanabi@npm:2.2.4": + version: 2.2.4 + resolution: "abi-wan-kanabi@npm:2.2.4" + dependencies: + ansicolors: "npm:^0.3.2" + cardinal: "npm:^2.1.1" + fs-extra: "npm:^10.0.0" + yargs: "npm:^17.7.2" + bin: + generate: dist/generate.js + checksum: 10/988c9c1cb926307c45ffc568d1b53c226bc986be8017e4f138a520c82ef5b72b3e762084bea2fa2730b3838d6956349a20c9fdcc6f494d5474e80214cc4cd00b + languageName: node + linkType: hard + "abitype@npm:1.1.0": version: 1.1.0 resolution: "abitype@npm:1.1.0" @@ -7124,6 +7919,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:^1.0.6": + version: 1.2.3 + resolution: "abitype@npm:1.2.3" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 10/94e744c2fc301b1cff59163a21b499aae0ddecdf4d3bef1579ff16b705e6f5738fd314125d791ed142487db2473d4fadcdbabb1e05e4b5d35715bc4ef35e400a + languageName: node + linkType: hard + "abitype@npm:^1.0.9": version: 1.2.0 resolution: "abitype@npm:1.2.0" @@ -7313,7 +8123,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.10.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -7332,7 +8142,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.2": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -7364,7 +8174,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.2.0": +"ansi-styles@npm:^5.0.0, ansi-styles@npm:^5.2.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 10/d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 @@ -7378,6 +8188,13 @@ __metadata: languageName: node linkType: hard +"ansicolors@npm:^0.3.2, ansicolors@npm:~0.3.2": + version: 0.3.2 + resolution: "ansicolors@npm:0.3.2" + checksum: 10/0704d1485d84d65a47aacd3d2d26f501f21aeeb509922c8f2496d0ec5d346dc948efa64f3151aef0571d73e5c44eb10fd02f27f59762e9292fe123bb1ea9ff7d + languageName: node + linkType: hard + "ansis@npm:4.1.0": version: 4.1.0 resolution: "ansis@npm:4.1.0" @@ -7385,7 +8202,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.0.3, anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -7479,6 +8296,33 @@ __metadata: languageName: node linkType: hard +"asn1.js@npm:^4.10.1": + version: 4.10.1 + resolution: "asn1.js@npm:4.10.1" + dependencies: + bn.js: "npm:^4.0.0" + inherits: "npm:^2.0.1" + minimalistic-assert: "npm:^1.0.0" + checksum: 10/5a02104b9ba167917c786a3fdac9840a057d29e6b609250e6af924d0529ead1a32417da13eec809cadea8f991eb67782196f3df427c5b4f30eaf22044fc64fda + languageName: node + linkType: hard + +"asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: "npm:~2.1.0" + checksum: 10/cf629291fee6c1a6f530549939433ebf32200d7849f38b810ff26ee74235e845c0c12b2ed0f1607ac17383d19b219b69cefa009b920dab57924c5c544e495078 + languageName: node + linkType: hard + +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 10/f4f991ae2df849cc678b1afba52d512a7cbf0d09613ba111e72255409ff9158550c775162a47b12d015d1b82b3c273e8e25df0e4783d3ddb008a293486d00a07 + languageName: node + linkType: hard + "async-function@npm:^1.0.0": version: 1.0.0 resolution: "async-function@npm:1.0.0" @@ -7493,6 +8337,13 @@ __metadata: languageName: node linkType: hard +"async-limiter@npm:~1.0.0": + version: 1.0.1 + resolution: "async-limiter@npm:1.0.1" + checksum: 10/2b849695b465d93ad44c116220dee29a5aeb63adac16c1088983c339b0de57d76e82533e8e364a93a9f997f28bbfc6a92948cefc120652bd07f3b59f8d75cf2b + languageName: node + linkType: hard + "async-mutex@npm:^0.4.0": version: 0.4.1 resolution: "async-mutex@npm:0.4.1" @@ -7502,6 +8353,13 @@ __metadata: languageName: node linkType: hard +"async@npm:~1.5.2": + version: 1.5.2 + resolution: "async@npm:1.5.2" + checksum: 10/8afcdcee05168250926a3e7bd4dfaa74b681a74f634bae2af424fb716042461cbd20a375d9bc2534daa50a2d45286c9b174952fb239cee4ab8d6351a40c65327 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -7518,6 +8376,20 @@ __metadata: languageName: node linkType: hard +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: 10/2ac497d739f71be3264cf096a33ab256a1fea7fe80b87dc51ec29374505bd5a661279ef1c22989d68528ea61ed634021ca63b31cf1d3c2a3682ffc106f7d0e96 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.13.2 + resolution: "aws4@npm:1.13.2" + checksum: 10/290b9f84facbad013747725bfd8b4c42d0b3b04b5620d8418f0219832ef95a7dc597a4af7b1589ae7fce18bacde96f40911c3cda36199dd04d9f8e01f72fa50a + languageName: node + linkType: hard + "axios-cache-interceptor@npm:^1.5.3": version: 1.8.3 resolution: "axios-cache-interceptor@npm:1.8.3" @@ -7531,6 +8403,15 @@ __metadata: languageName: node linkType: hard +"axios@npm:0.21.1": + version: 0.21.1 + resolution: "axios@npm:0.21.1" + dependencies: + follow-redirects: "npm:^1.10.0" + checksum: 10/271afc61381d285c688f0579eec170b66dc4177219a3f31e67d1fe485be7a2dae4af2c366fdee703a8ec887c52778fdc7f5fec30650a97f61f68c1a7696648b0 + languageName: node + linkType: hard + "axios@npm:0.27.2": version: 0.27.2 resolution: "axios@npm:0.27.2" @@ -7572,7 +8453,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.13.0, axios@npm:^1.6.2, axios@npm:^1.6.8, axios@npm:^1.7.4": +"axios@npm:^1.13.0, axios@npm:^1.6.2, axios@npm:^1.6.7, axios@npm:^1.6.8, axios@npm:^1.7.4": version: 1.13.2 resolution: "axios@npm:1.13.2" dependencies: @@ -7583,6 +8464,18 @@ __metadata: languageName: node linkType: hard +"b4a@npm:^1.6.0": + version: 1.7.3 + resolution: "b4a@npm:1.7.3" + peerDependencies: + react-native-b4a: "*" + peerDependenciesMeta: + react-native-b4a: + optional: true + checksum: 10/048ddd0eeec6a75e6f8dee07d52354e759032f0ef678b556e05bf5a137d7a4102002cadb953b3fb37a635995a1013875d715d115dbafaf12bcad6528d2166054 + languageName: node + linkType: hard + "babel-jest@npm:30.2.0": version: 30.2.0 resolution: "babel-jest@npm:30.2.0" @@ -7600,6 +8493,36 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" + dependencies: + "@jest/transform": "npm:^29.7.0" + "@types/babel__core": "npm:^7.1.14" + babel-plugin-istanbul: "npm:^6.1.1" + babel-preset-jest: "npm:^29.6.3" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + slash: "npm:^3.0.0" + peerDependencies: + "@babel/core": ^7.8.0 + checksum: 10/8a0953bd813b3a8926008f7351611055548869e9a53dd36d6e7e96679001f71e65fd7dbfe253265c3ba6a4e630dc7c845cf3e78b17d758ef1880313ce8fba258 + languageName: node + linkType: hard + +"babel-plugin-istanbul@npm:^6.1.1": + version: 6.1.1 + resolution: "babel-plugin-istanbul@npm:6.1.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.0.0" + "@istanbuljs/load-nyc-config": "npm:^1.0.0" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-instrument: "npm:^5.0.4" + test-exclude: "npm:^6.0.0" + checksum: 10/ffd436bb2a77bbe1942a33245d770506ab2262d9c1b3c1f1da7f0592f78ee7445a95bc2efafe619dd9c1b6ee52c10033d6c7d29ddefe6f5383568e60f31dfe8d + languageName: node + linkType: hard + "babel-plugin-istanbul@npm:^7.0.1": version: 7.0.1 resolution: "babel-plugin-istanbul@npm:7.0.1" @@ -7622,7 +8545,19 @@ __metadata: languageName: node linkType: hard -"babel-preset-current-node-syntax@npm:^1.2.0": +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" + dependencies: + "@babel/template": "npm:^7.3.3" + "@babel/types": "npm:^7.3.3" + "@types/babel__core": "npm:^7.1.14" + "@types/babel__traverse": "npm:^7.0.6" + checksum: 10/9bfa86ec4170bd805ab8ca5001ae50d8afcb30554d236ba4a7ffc156c1a92452e220e4acbd98daefc12bf0216fccd092d0a2efed49e7e384ec59e0597a926d65 + languageName: node + linkType: hard + +"babel-preset-current-node-syntax@npm:^1.0.0, babel-preset-current-node-syntax@npm:^1.2.0": version: 1.2.0 resolution: "babel-preset-current-node-syntax@npm:1.2.0" dependencies: @@ -7659,6 +8594,28 @@ __metadata: languageName: node linkType: hard +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" + dependencies: + babel-plugin-jest-hoist: "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10/aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb + languageName: node + linkType: hard + +"babel-runtime@npm:6.26.0": + version: 6.26.0 + resolution: "babel-runtime@npm:6.26.0" + dependencies: + core-js: "npm:^2.4.0" + regenerator-runtime: "npm:^0.11.0" + checksum: 10/2cdf0f083b9598a43cdb11cbf1e7060584079a9a2230f06aec997ba81e887ef17fdcb5ad813a484ee099e06d2de0cea832bdd3011c06325acb284284c754ee8f + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -7726,7 +8683,7 @@ __metadata: languageName: node linkType: hard -"bchaddrjs@npm:^0.4.4": +"bchaddrjs@npm:^0.4.4, bchaddrjs@npm:^0.4.9": version: 0.4.9 resolution: "bchaddrjs@npm:0.4.9" dependencies: @@ -7736,6 +8693,15 @@ __metadata: languageName: node linkType: hard +"bcrypt-pbkdf@npm:^1.0.0": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: "npm:^0.14.3" + checksum: 10/13a4cde058250dbf1fa77a4f1b9a07d32ae2e3b9e28e88a0c7a1827835bc3482f3e478c4a0cfd4da6ff0c46dae07da1061123a995372b32cc563d9975f975404 + languageName: node + linkType: hard + "bech32@npm:1.1.4, bech32@npm:^1.1.2, bech32@npm:^1.1.3, bech32@npm:^1.1.4": version: 1.1.4 resolution: "bech32@npm:1.1.4" @@ -7757,6 +8723,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.44": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10/4bc6ae152a96edc9f95020f5fc66b13d26a9ad9a021225a9f0213f7e3dc44269f423aa8c42e19d6ac4a63bb2b22140b95d10be8f9ca7a6d9aa1b22b330d1f514 + languageName: node + linkType: hard + "big.js@npm:^5.2.2": version: 5.2.2 resolution: "big.js@npm:5.2.2" @@ -7764,6 +8737,13 @@ __metadata: languageName: node linkType: hard +"bigi@npm:^1.1.0, bigi@npm:^1.4.2": + version: 1.4.2 + resolution: "bigi@npm:1.4.2" + checksum: 10/e8165beb2ad113add286f81a066653295737a3edc6287effae7b72c2d7695be19d36774069e80a977df7187857c9e2c4a9517f6ec966bb887e097ea8d36cc2e0 + languageName: node + linkType: hard + "bigint-buffer@npm:^1.1.5": version: 1.1.5 resolution: "bigint-buffer@npm:1.1.5" @@ -7823,7 +8803,7 @@ __metadata: languageName: node linkType: hard -"bip174@npm:^3.0.0-rc.0": +"bip174@npm:^3.0.0, bip174@npm:^3.0.0-rc.0": version: 3.0.0 resolution: "bip174@npm:3.0.0" dependencies: @@ -7840,7 +8820,7 @@ __metadata: languageName: node linkType: hard -"bip32@npm:^2.0.4": +"bip32@npm:^2.0.4, bip32@npm:^2.0.5": version: 2.0.6 resolution: "bip32@npm:2.0.6" dependencies: @@ -7855,6 +8835,15 @@ __metadata: languageName: node linkType: hard +"bip39@npm:^3.0.2, bip39@npm:^3.0.4": + version: 3.1.0 + resolution: "bip39@npm:3.1.0" + dependencies: + "@noble/hashes": "npm:^1.2.0" + checksum: 10/406c0b5bdab0d43df2ff8916c5b57bb7f65cd36a115cbd7620a75b972638f8511840bfc27bfc68946027086fd33ed3050d8cd304e85fd527503b23d857a8486e + languageName: node + linkType: hard + "bip66@npm:^1.1.0, bip66@npm:^1.1.5": version: 1.1.5 resolution: "bip66@npm:1.1.5" @@ -7871,6 +8860,20 @@ __metadata: languageName: node linkType: hard +"bitcoin-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "bitcoin-regex@npm:2.0.0" + checksum: 10/d53287d62f06217feed6a54e838a0021de1aeed628a3f9da8b78e6580c3d966b04e7f863cb3d1404867321825c95942c4f635961032636c66540d342f2635083 + languageName: node + linkType: hard + +"bitcoincash-regex@npm:^1.1.8": + version: 1.1.9 + resolution: "bitcoincash-regex@npm:1.1.9" + checksum: 10/b4f33db377b7e30fbfa042487ea4c635e5fa30966e67aad68a466968164b2da828081f57b40e48d54109a9fd524da403660a6d29a2165132001d44a63951270b + languageName: node + linkType: hard + "bitcoinjs-lib@npm:^5.2.0": version: 5.2.0 resolution: "bitcoinjs-lib@npm:5.2.0" @@ -7937,27 +8940,58 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^4.11.0, bn.js@npm:^4.11.8, bn.js@npm:^4.11.9": +"bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.11.0, bn.js@npm:^4.11.8, bn.js@npm:^4.11.9": version: 4.12.2 resolution: "bn.js@npm:4.12.2" checksum: 10/5803983405c087443e0e6c9bb5d0bc863d9f987d77e710f81b14c55616494f5a274e1650ee892531acb3529d52c0e0ea48aa12d2873dd80a75dde9d73a2ec518 languageName: node linkType: hard -"bn.js@npm:^5.1.2, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": +"bn.js@npm:^5.1.2, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1, bn.js@npm:^5.2.2": version: 5.2.2 resolution: "bn.js@npm:5.2.2" checksum: 10/51ebb2df83b33e5d8581165206e260d5e9c873752954616e5bf3758952b84d7399a9c6d00852815a0aeefb1150a7f34451b62d4287342d457fa432eee869e83e languageName: node linkType: hard -"body-parser@npm:^2.2.0": - version: 2.2.1 - resolution: "body-parser@npm:2.2.1" +"bnb-javascript-sdk-nobroadcast@npm:2.16.15": + version: 2.16.15 + resolution: "bnb-javascript-sdk-nobroadcast@npm:2.16.15" dependencies: - bytes: "npm:^3.1.2" - content-type: "npm:^1.0.5" - debug: "npm:^4.4.3" + axios: "npm:0.21.1" + bech32: "npm:^1.1.3" + big.js: "npm:^5.2.2" + bip32: "npm:^2.0.5" + bip39: "npm:^3.0.2" + bn.js: "npm:^4.11.8" + camelcase: "npm:^5.3.1" + crypto-browserify: "npm:^3.12.0" + crypto-js: "npm:^3.1.9-1" + elliptic: "npm:^6.0.0" + eslint-utils: "npm:^1.4.2" + events: "npm:^3.0.0" + is_js: "npm:^0.9.0" + lodash: "npm:^4.17.19" + minimist: "npm:^1.2.5" + ndjson: "npm:^1.5.0" + protocol-buffers-encodings: "npm:^1.1.0" + pumpify: "npm:^2.0.1" + secure-random: "npm:^1.1.2" + tiny-secp256k1: "npm:^1.1.3" + url: "npm:^0.11.0" + uuid: "npm:^3.3.2" + websocket-stream: "npm:^5.5.0" + checksum: 10/a6033ad1f6f088ad116350f6802dec57c2599ddebd24240936a5a1ae6ce52f7ae97f8e52256863f7b4eaaeeee6dcf7ff188c314a4cd2f198417ce490a212d11e + languageName: node + linkType: hard + +"body-parser@npm:^2.2.0": + version: 2.2.1 + resolution: "body-parser@npm:2.2.1" + dependencies: + bytes: "npm:^3.1.2" + content-type: "npm:^1.0.5" + debug: "npm:^4.4.3" http-errors: "npm:^2.0.0" iconv-lite: "npm:^0.7.0" on-finished: "npm:^2.4.1" @@ -8014,14 +9048,14 @@ __metadata: languageName: node linkType: hard -"brorand@npm:^1.1.0": +"brorand@npm:^1.0.1, brorand@npm:^1.1.0": version: 1.1.0 resolution: "brorand@npm:1.1.0" checksum: 10/8a05c9f3c4b46572dec6ef71012b1946db6cae8c7bb60ccd4b7dd5a84655db49fe043ecc6272e7ef1f69dc53d6730b9e2a3a03a8310509a3d797a618cbee52be languageName: node linkType: hard -"browserify-aes@npm:^1.0.6, browserify-aes@npm:^1.2.0": +"browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.0.6, browserify-aes@npm:^1.2.0": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" dependencies: @@ -8042,6 +9076,57 @@ __metadata: languageName: node linkType: hard +"browserify-cipher@npm:^1.0.1": + version: 1.0.1 + resolution: "browserify-cipher@npm:1.0.1" + dependencies: + browserify-aes: "npm:^1.0.4" + browserify-des: "npm:^1.0.0" + evp_bytestokey: "npm:^1.0.0" + checksum: 10/2d8500acf1ee535e6bebe808f7a20e4c3a9e2ed1a6885fff1facbfd201ac013ef030422bec65ca9ece8ffe82b03ca580421463f9c45af6c8415fd629f4118c13 + languageName: node + linkType: hard + +"browserify-des@npm:^1.0.0": + version: 1.0.2 + resolution: "browserify-des@npm:1.0.2" + dependencies: + cipher-base: "npm:^1.0.1" + des.js: "npm:^1.0.0" + inherits: "npm:^2.0.1" + safe-buffer: "npm:^5.1.2" + checksum: 10/2fd9018e598b1b25e002abaf656d46d8e0f2ee2666ff18852d37e5c3d0e47701d6824256b060fac395420d56a0c49c2b0d40a194e6fbd837bfdd893e7eb5ade4 + languageName: node + linkType: hard + +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.1.1": + version: 4.1.1 + resolution: "browserify-rsa@npm:4.1.1" + dependencies: + bn.js: "npm:^5.2.1" + randombytes: "npm:^2.1.0" + safe-buffer: "npm:^5.2.1" + checksum: 10/62ae0da60e49e8d5dd3b0922119b6edee94ebfa3a184211c804024b3a75f9dab31a1d124cc0545ed050e273f0325c2fd7aba6a51e44ba6f726fceae3210ddade + languageName: node + linkType: hard + +"browserify-sign@npm:^4.2.3": + version: 4.2.5 + resolution: "browserify-sign@npm:4.2.5" + dependencies: + bn.js: "npm:^5.2.2" + browserify-rsa: "npm:^4.1.1" + create-hash: "npm:^1.2.0" + create-hmac: "npm:^1.1.7" + elliptic: "npm:^6.6.1" + inherits: "npm:^2.0.4" + parse-asn1: "npm:^5.1.9" + readable-stream: "npm:^2.3.8" + safe-buffer: "npm:^5.2.1" + checksum: 10/ccfe54ab61b8e01e84c507b60912f9ae8701f4e53accc3d85c3773db13f14c51f17b684167735d28c59aaf5523ee59c66cc831ddc178bc7f598257e590ca1a35 + languageName: node + linkType: hard + "browserslist@npm:^4.24.0": version: 4.28.0 resolution: "browserslist@npm:4.28.0" @@ -8075,6 +9160,15 @@ __metadata: languageName: node linkType: hard +"bs58@npm:^5.0.0": + version: 5.0.0 + resolution: "bs58@npm:5.0.0" + dependencies: + base-x: "npm:^4.0.0" + checksum: 10/2475cb0684e07077521aac718e604a13e0f891d58cff923d437a2f7e9e28703ab39fce9f84c7c703ab369815a675f11e3bd394d38643bfe8969fbe42e6833d45 + languageName: node + linkType: hard + "bs58@npm:^6.0.0": version: 6.0.0 resolution: "bs58@npm:6.0.0" @@ -8095,6 +9189,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: 10/dbbecc7a09f3836e821149266c864c4bbd545539cea43c35f23f4c3c46b54c86c52b65d224b9ea2e916fa6d93bd2ce9fac5b6c6bfcf19621a9c209a5602f71c8 + languageName: node + linkType: hard + "bs58check@npm:^4.0.0": version: 4.0.0 resolution: "bs58check@npm:4.0.0" @@ -8345,7 +9449,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.3.0": +"camelcase@npm:^6.2.0, camelcase@npm:^6.3.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 10/8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -8359,6 +9463,25 @@ __metadata: languageName: node linkType: hard +"cardinal@npm:^2.1.1": + version: 2.1.1 + resolution: "cardinal@npm:2.1.1" + dependencies: + ansicolors: "npm:~0.3.2" + redeyed: "npm:~2.1.0" + bin: + cdl: ./bin/cdl.js + checksum: 10/caf0d34739ef7b1d80e1753311f889997b62c4490906819eb5da5bd46e7f5e5caba7a8a96ca401190c7d9c18443a7749e5338630f7f9a1ae98d60cac49b9008e + languageName: node + linkType: hard + +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: 10/ea1efdf430975fdbac3505cdd21007f7ac5aa29b6d4d1c091f965853cd1bf87e4b08ea07b31a6d688b038872b7cdf0589d9262d59c699d199585daad052aeb20 + languageName: node + linkType: hard + "cashaddrjs@npm:^0.3.12": version: 0.3.12 resolution: "cashaddrjs@npm:0.3.12" @@ -8455,6 +9578,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^3.2.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 10/75bc67902b4d1c7b435497adeb91598f6d52a3389398e44294f6601b20cfef32cf2176f7be0eb961d9e085bb333a8a5cae121cb22f81cf238ae7f58eb80e9397 + languageName: node + linkType: hard + "ci-info@npm:^4.2.0": version: 4.3.1 resolution: "ci-info@npm:4.3.1" @@ -8494,7 +9624,7 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.2.2": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.2": version: 1.4.3 resolution: "cjs-module-lexer@npm:1.4.3" checksum: 10/d2b92f919a2dedbfd61d016964fce8da0035f827182ed6839c97cac56e8a8077cfa6a59388adfe2bc588a19cef9bbe830d683a76a6e93c51f65852062cfe2591 @@ -8603,6 +9733,20 @@ __metadata: languageName: node linkType: hard +"codeclimate-test-reporter@npm:^0.5.1": + version: 0.5.1 + resolution: "codeclimate-test-reporter@npm:0.5.1" + dependencies: + async: "npm:~1.5.2" + commander: "npm:2.9.0" + lcov-parse: "npm:0.0.10" + request: "npm:~2.88.0" + bin: + codeclimate-test-reporter: ./bin/codeclimate.js + checksum: 10/96d54217ca6b113c46a8cd373168015ace9e30ac336d1fcd0e9c31d1477cddb6e263b55556c927d4b05be6485c779a662be3208771b3b4e51728f8dc5f0c579a + languageName: node + linkType: hard + "coinselect@npm:^3.1.13": version: 3.1.13 resolution: "coinselect@npm:3.1.13" @@ -8610,7 +9754,7 @@ __metadata: languageName: node linkType: hard -"collect-v8-coverage@npm:^1.0.2": +"collect-v8-coverage@npm:^1.0.0, collect-v8-coverage@npm:^1.0.2": version: 1.0.3 resolution: "collect-v8-coverage@npm:1.0.3" checksum: 10/656443261fb7b79cf79e89cba4b55622b07c1d4976c630829d7c5c585c73cda1c2ff101f316bfb19bb9e2c58d724c7db1f70a21e213dcd14099227c5e6019860 @@ -8633,7 +9777,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.8": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -8642,6 +9786,15 @@ __metadata: languageName: node linkType: hard +"commander@npm:2.9.0": + version: 2.9.0 + resolution: "commander@npm:2.9.0" + dependencies: + graceful-readlink: "npm:>= 1.0.0" + checksum: 10/65d08cbbf0ce36d3326e4904b8b8be1571547e96ae33834a7296fc84ab2d703c4b9f4ac2836ab8a7d33b145b545d20ac820f67bfef52cca021dbc8fbdf960686 + languageName: node + linkType: hard + "commander@npm:4.1.1": version: 4.1.1 resolution: "commander@npm:4.1.1" @@ -8688,6 +9841,13 @@ __metadata: languageName: node linkType: hard +"component-type@npm:1.2.1": + version: 1.2.1 + resolution: "component-type@npm:1.2.1" + checksum: 10/48e36d09852d8196a72a1353d4407bc35484ff052fc043ecf2e27786d4e8d5b93021c408cfe63237b585fd66bb0d1dbeabed96051356416a3b2a521aabf16d12 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -8756,6 +9916,20 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^2.4.0": + version: 2.6.12 + resolution: "core-js@npm:2.6.12" + checksum: 10/7c624eb00a59c74c769d5d80f751f3bf1fc6201205b6562f27286ad5e00bbca1483f2f7eb0c2854b86f526ef5c7dc958b45f2ff536f8a31b8e9cb1a13a96efca + languageName: node + linkType: hard + +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 10/d0f7587346b44a1fe6c269267e037dd34b4787191e473c3e685f507229d88561c40eb18872fabfff02977301815d474300b7bfbd15396c13c5377393f7e87ec3 + languageName: node + linkType: hard + "core-util-is@npm:^1.0.3, core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -8821,6 +9995,16 @@ __metadata: languageName: node linkType: hard +"create-ecdh@npm:^4.0.4": + version: 4.0.4 + resolution: "create-ecdh@npm:4.0.4" + dependencies: + bn.js: "npm:^4.1.0" + elliptic: "npm:^6.5.3" + checksum: 10/0dd7fca9711d09e152375b79acf1e3f306d1a25ba87b8ff14c2fd8e68b83aafe0a7dd6c4e540c9ffbdd227a5fa1ad9b81eca1f233c38bb47770597ba247e614b + languageName: node + linkType: hard + "create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": version: 1.2.0 resolution: "create-hash@npm:1.2.0" @@ -8848,6 +10032,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + prompts: "npm:^2.0.1" + bin: + create-jest: bin/create-jest.js + checksum: 10/847b4764451672b4174be4d5c6d7d63442ec3aa5f3de52af924e4d996d87d7801c18e125504f25232fc75840f6625b3ac85860fac6ce799b5efae7bdcaf4a2b7 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -8885,6 +10086,26 @@ __metadata: languageName: node linkType: hard +"crypto-browserify@npm:^3.12.0": + version: 3.12.1 + resolution: "crypto-browserify@npm:3.12.1" + dependencies: + browserify-cipher: "npm:^1.0.1" + browserify-sign: "npm:^4.2.3" + create-ecdh: "npm:^4.0.4" + create-hash: "npm:^1.2.0" + create-hmac: "npm:^1.1.7" + diffie-hellman: "npm:^5.0.3" + hash-base: "npm:~3.0.4" + inherits: "npm:^2.0.4" + pbkdf2: "npm:^3.1.2" + public-encrypt: "npm:^4.0.3" + randombytes: "npm:^2.1.0" + randomfill: "npm:^1.0.4" + checksum: 10/13da0b5f61b3e8e68fcbebf0394f2b2b4d35a0d0ba6ab762720c13391d3697ea42735260a26328a6a3d872be7d4cb5abe98a7a8f88bc93da7ba59b993331b409 + languageName: node + linkType: hard + "crypto-hash@npm:^1.3.0": version: 1.3.0 resolution: "crypto-hash@npm:1.3.0" @@ -8892,13 +10113,20 @@ __metadata: languageName: node linkType: hard -"crypto-js@npm:4.2.0": +"crypto-js@npm:4.2.0, crypto-js@npm:^4.0.0": version: 4.2.0 resolution: "crypto-js@npm:4.2.0" checksum: 10/c7bcc56a6e01c3c397e95aa4a74e4241321f04677f9a618a8f48a63b5781617248afb9adb0629824792e7ec20ca0d4241a49b6b2938ae6f973ec4efc5c53c924 languageName: node linkType: hard +"crypto-js@npm:^3.1.9-1": + version: 3.3.0 + resolution: "crypto-js@npm:3.3.0" + checksum: 10/d7e11f3a387fb143be834e1a25ecf57ead6f5765e90fbf3aed9cead680cc38b1d241718768b7bfec448a843f569374ea5b5870ac7a8165e4bfa1915f0b00c89c + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -8908,6 +10136,22 @@ __metadata: languageName: node linkType: hard +"dash-regex@npm:^1.0.10": + version: 1.0.11 + resolution: "dash-regex@npm:1.0.11" + checksum: 10/1a5955b762e7ac32547b4e5365c5eb163eeaea375d5f63e0fabe7ee4a9700d3d29d356771a79f793031b9e81f9df04ea5e4a6d65f4207d9d26308c8d43fcb3b9 + languageName: node + linkType: hard + +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: "npm:^1.0.0" + checksum: 10/137b287fa021201ce100cef772c8eeeaaafdd2aa7282864022acf3b873021e54cb809e9c060fa164840bf54ff72d00d6e2d8da1ee5a86d7200eeefa1123a8f7f + languageName: node + linkType: hard + "data-view-buffer@npm:^1.0.2": version: 1.0.2 resolution: "data-view-buffer@npm:1.0.2" @@ -8941,6 +10185,13 @@ __metadata: languageName: node linkType: hard +"dataloader@npm:^2.0.0": + version: 2.2.3 + resolution: "dataloader@npm:2.2.3" + checksum: 10/83fe6259abe00ae64c5f48252ef59d8e5fcabda9fd4d26685f14a76eeca596bf6f9500d9f22a0094c50c3ea782a0977728f9367e232dfa0fdb5c9d646de279b2 + languageName: node + linkType: hard + "dayjs@npm:^1.11.3": version: 1.11.19 resolution: "dayjs@npm:1.11.19" @@ -9017,6 +10268,18 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^1.0.0": + version: 1.7.1 + resolution: "dedent@npm:1.7.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10/78785ef592e37e0b1ca7a7a5964c8f3dee1abdff46c5bb49864168579c122328f6bb55c769bc7e005046a7381c3372d3859f0f78ab083950fa146e1c24873f4f + languageName: node + linkType: hard + "dedent@npm:^1.6.0": version: 1.7.0 resolution: "dedent@npm:1.7.0" @@ -9116,6 +10379,16 @@ __metadata: languageName: node linkType: hard +"des.js@npm:^1.0.0": + version: 1.1.0 + resolution: "des.js@npm:1.1.0" + dependencies: + inherits: "npm:^2.0.1" + minimalistic-assert: "npm:^1.0.0" + checksum: 10/d35fc82b5a0b2127b12699212e90b54ddd8134e0cf8d27a8c30507ed3572aa574ab71800cbb473769128a52dcf21acc3271c5c359508a5aa772e990df3b1a698 + languageName: node + linkType: hard + "destr@npm:^2.0.3": version: 2.0.5 resolution: "destr@npm:2.0.5" @@ -9123,13 +10396,20 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.1.0": +"detect-newline@npm:^3.0.0, detect-newline@npm:^3.1.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" checksum: 10/ae6cd429c41ad01b164c59ea36f264a2c479598e61cba7c99da24175a7ab80ddf066420f2bec9a1c57a6bead411b4655ff15ad7d281c000a89791f48cbe939e7 languageName: node linkType: hard +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 10/179daf9d2f9af5c57ad66d97cb902a538bcf8ed64963fa7aa0c329b3de3665ce2eb6ffdc2f69f29d445fa4af2517e5e55e5b6e00c00a9ae4f43645f97f7078cb + languageName: node + linkType: hard + "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -9137,6 +10417,17 @@ __metadata: languageName: node linkType: hard +"diffie-hellman@npm:^5.0.3": + version: 5.0.3 + resolution: "diffie-hellman@npm:5.0.3" + dependencies: + bn.js: "npm:^4.1.0" + miller-rabin: "npm:^4.0.0" + randombytes: "npm:^2.0.0" + checksum: 10/2ff28231f93b27a4903461432d2de831df02e3568ea7633d5d7b6167eb73077f823b2bca26de6ba4f5c7ecd10a3df5aa94d376d136ab6209948c03cc4e4ac1fe + languageName: node + linkType: hard + "docker-classic@npm:@pulumi/docker@3.6.1": version: 3.6.1 resolution: "@pulumi/docker@npm:3.6.1" @@ -9147,6 +10438,13 @@ __metadata: languageName: node linkType: hard +"dogecoin-regex@npm:^1.0.9": + version: 1.0.10 + resolution: "dogecoin-regex@npm:1.0.10" + checksum: 10/14cbefcdf1c364566f8d1bf3ba41640635b57c09e10b9545ff914bbc0adbfccdf212e0415a52603cd00f0681ce8d040a4c5768082ee340b0ed7a27c90434e840 + languageName: node + linkType: hard + "dot-case@npm:^3.0.4": version: 3.0.4 resolution: "dot-case@npm:3.0.4" @@ -9216,6 +10514,30 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^3.5.1": + version: 3.7.1 + resolution: "duplexify@npm:3.7.1" + dependencies: + end-of-stream: "npm:^1.0.0" + inherits: "npm:^2.0.1" + readable-stream: "npm:^2.0.0" + stream-shift: "npm:^1.0.0" + checksum: 10/7799984d178fb57e11c43f5f172a10f795322ec85ff664c2a98d2c2de6deeb9d7a30b810f83923dcd7ebe0f1786724b8aee2b62ca4577522141f93d6d48fb31c + languageName: node + linkType: hard + +"duplexify@npm:^4.1.1": + version: 4.1.3 + resolution: "duplexify@npm:4.1.3" + dependencies: + end-of-stream: "npm:^1.4.1" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + stream-shift: "npm:^1.0.2" + checksum: 10/b44b98ba0ffac3a658b4b1bf877219e996db288c5ae6f3dc55ca9b2cbef7df60c10eabfdd947f3d73a623eb9975a74a66d6d61e6f26bff90155315adb362aa77 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -9223,6 +10545,37 @@ __metadata: languageName: node linkType: hard +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: "npm:~0.1.0" + safer-buffer: "npm:^2.1.0" + checksum: 10/d43591f2396196266e186e6d6928038cc11c76c3699a912cb9c13757060f7bbc7f17f47c4cb16168cdeacffc7965aef021142577e646fb3cb88810c15173eb57 + languageName: node + linkType: hard + +"ecpair@npm:^3.0.0-rc.0": + version: 3.0.0 + resolution: "ecpair@npm:3.0.0" + dependencies: + uint8array-tools: "npm:^0.0.8" + valibot: "npm:^0.37.0" + wif: "npm:^5.0.0" + checksum: 10/4deca80b35f96f974e0983d5aab169fe0ef4abe51ce0f6efbfe9e83afdb3278c87855d39134443eeec8847dc1b117e91552a57879779ec7d0e20c811146f86f1 + languageName: node + linkType: hard + +"ecurve@npm:^1.0.6": + version: 1.0.6 + resolution: "ecurve@npm:1.0.6" + dependencies: + bigi: "npm:^1.1.0" + safe-buffer: "npm:^5.0.1" + checksum: 10/5f738e564ad956acbcd743239b0b9e144d1fca999b6018370d86084545bf03ac5b63229918ab0b2ff87f676e85c23dedabc0f1ad6ea2eef160da70cb3119924b + languageName: node + linkType: hard + "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -9282,7 +10635,7 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:6.6.1, elliptic@npm:^6.4.0, elliptic@npm:^6.5.2, elliptic@npm:^6.5.4, elliptic@npm:^6.5.7": +"elliptic@npm:6.6.1, elliptic@npm:^6.0.0, elliptic@npm:^6.4.0, elliptic@npm:^6.5.2, elliptic@npm:^6.5.3, elliptic@npm:^6.5.4, elliptic@npm:^6.5.7, elliptic@npm:^6.6.1": version: 6.6.1 resolution: "elliptic@npm:6.6.1" dependencies: @@ -9334,7 +10687,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0": +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.5 resolution: "end-of-stream@npm:1.4.5" dependencies: @@ -9664,6 +11017,22 @@ __metadata: languageName: node linkType: hard +"eslint-utils@npm:^1.4.2": + version: 1.4.3 + resolution: "eslint-utils@npm:1.4.3" + dependencies: + eslint-visitor-keys: "npm:^1.1.0" + checksum: 10/77079d7123385011fc63c438ab95b01482eac04f2b9cd6e942d1f645ea430d610d721fe150bed8ec4ac17e7c4a11f2d6253a8182551ece09444c62c6a9973ad7 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^1.1.0": + version: 1.3.0 + resolution: "eslint-visitor-keys@npm:1.3.0" + checksum: 10/595ab230e0fcb52f86ba0986a9a473b9fcae120f3729b43f1157f88f27f8addb1e545c4e3d444185f2980e281ca15be5ada6f65b4599eec227cf30e41233b762 + languageName: node + linkType: hard + "eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" @@ -9738,7 +11107,7 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.0, esprima@npm:^4.0.1": +"esprima@npm:^4.0.0, esprima@npm:^4.0.1, esprima@npm:~4.0.0": version: 4.0.1 resolution: "esprima@npm:4.0.1" bin: @@ -9836,6 +11205,22 @@ __metadata: languageName: node linkType: hard +"ethereum-regex@npm:^1.1.12": + version: 1.1.13 + resolution: "ethereum-regex@npm:1.1.13" + checksum: 10/25f89a66cb0a54b5e40ab004c51a55d530c4ac0883d3bd13a03c7fba4b8313eabe41f97d40fe5d820622492a512d7bddf8f46961d36e8ce79425a6e058fbc7e4 + languageName: node + linkType: hard + +"ethereum-tx-decoder@npm:^3.0.0": + version: 3.0.0 + resolution: "ethereum-tx-decoder@npm:3.0.0" + dependencies: + ethers: "npm:^4.0.37" + checksum: 10/19c760749d09c232042ef8f59b59e7c877121a62fbea4ac82d45b38eaf5c3e2bfdf7fa089478af9d0749eb139957e6ebee371daf6eb6588ce5b75845d53b96d2 + languageName: node + linkType: hard + "ethereumjs-tx@npm:1.3.7": version: 1.3.7 resolution: "ethereumjs-tx@npm:1.3.7" @@ -9944,6 +11329,23 @@ __metadata: languageName: node linkType: hard +"ethers@npm:^4.0.37": + version: 4.0.49 + resolution: "ethers@npm:4.0.49" + dependencies: + aes-js: "npm:3.0.0" + bn.js: "npm:^4.11.9" + elliptic: "npm:6.5.4" + hash.js: "npm:1.1.3" + js-sha3: "npm:0.5.7" + scrypt-js: "npm:2.0.4" + setimmediate: "npm:1.0.4" + uuid: "npm:2.0.1" + xmlhttprequest: "npm:1.8.0" + checksum: 10/a4cec0254f940a0fb118317d23676faa46eb5540fc0a3b9177b8aef71318f509ed19b8264f102b1a2a32d0256274ecc526fd926bd22a4a4ac25cd8e0e6560f12 + languageName: node + linkType: hard + "ethers@npm:^5.1.0": version: 5.8.0 resolution: "ethers@npm:5.8.0" @@ -10013,14 +11415,14 @@ __metadata: languageName: node linkType: hard -"events@npm:^3.2.0, events@npm:^3.3.0": +"events@npm:^3.0.0, events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: 10/a3d47e285e28d324d7180f1e493961a2bbb4cad6412090e4dec114f4db1f5b560c7696ee8e758f55e23913ede856e3689cd3aa9ae13c56b5d8314cd3b3ddd1be languageName: node linkType: hard -"evp_bytestokey@npm:^1.0.3": +"evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3": version: 1.0.3 resolution: "evp_bytestokey@npm:1.0.3" dependencies: @@ -10031,7 +11433,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.1.0, execa@npm:^5.1.1": +"execa@npm:^5.0.0, execa@npm:^5.1.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -10055,6 +11457,13 @@ __metadata: languageName: node linkType: hard +"exit@npm:^0.1.2": + version: 0.1.2 + resolution: "exit@npm:0.1.2" + checksum: 10/387555050c5b3c10e7a9e8df5f43194e95d7737c74532c409910e585d5554eaff34960c166643f5e23d042196529daad059c292dcf1fb61b8ca878d3677f4b87 + languageName: node + linkType: hard + "expect@npm:30.2.0, expect@npm:^30.0.0": version: 30.2.0 resolution: "expect@npm:30.2.0" @@ -10069,6 +11478,19 @@ __metadata: languageName: node linkType: hard +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" + dependencies: + "@jest/expect-utils": "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/63f97bc51f56a491950fb525f9ad94f1916e8a014947f8d8445d3847a665b5471b768522d659f5e865db20b6c2033d2ac10f35fcbd881a4d26407a4f6f18451a + languageName: node + linkType: hard + "expo-server-sdk@npm:^3.15.0": version: 3.15.0 resolution: "expo-server-sdk@npm:3.15.0" @@ -10129,6 +11551,27 @@ __metadata: languageName: node linkType: hard +"extend@npm:~3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10/59e89e2dc798ec0f54b36d82f32a27d5f6472c53974f61ca098db5d4648430b725387b53449a34df38fd0392045434426b012f302b3cc049a6500ccf82877e4e + languageName: node + linkType: hard + +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: 10/26967d6c7ecbfb5bc5b7a6c43503dc5fafd9454802037e9fa1665e41f615da4ff5918bd6cb871a3beabed01a31eca1ccd0bdfb41231f50ad50d405a430f78377 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: 10/bfd6d55f3c0c04d826fe0213264b383c03f32825af6b1ff777f3f2dc49467e599361993568d75b7b19a8ea1bb08c8e7cd8c3d87d179ced91bb0dcf81ca6938e0 + languageName: node + linkType: hard + "eyes@npm:^0.1.8": version: 0.1.8 resolution: "eyes@npm:0.1.8" @@ -10214,6 +11657,13 @@ __metadata: languageName: node linkType: hard +"fast-text-encoding@npm:^1.0.3": + version: 1.0.6 + resolution: "fast-text-encoding@npm:1.0.6" + checksum: 10/f7b9e2e7a21e4ae5f4b8d3729850be83fb45052b28c9c38c09b8366463a291d6dc5448359238bdaf87f6a9e907d5895a94319a2c5e0e9f0786859ad6312d1d06 + languageName: node + linkType: hard + "fast-uri@npm:^3.0.1": version: 3.1.0 resolution: "fast-uri@npm:3.1.0" @@ -10241,7 +11691,7 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.2": +"fb-watchman@npm:^2.0.0, fb-watchman@npm:^2.0.2": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" dependencies: @@ -10357,6 +11807,21 @@ __metadata: languageName: node linkType: hard +"fiosdk-offline@npm:^1.2.21": + version: 1.2.21 + resolution: "fiosdk-offline@npm:1.2.21" + dependencies: + "@fioprotocol/fiojs": "npm:1.0.1" + "@types/fast-text-encoding": "npm:^1.0.1" + bip39: "npm:^3.0.2" + fast-text-encoding: "npm:^1.0.3" + hdkey: "npm:^1.1.1" + validate: "npm:^5.1.0" + wif: "npm:^2.0.6" + checksum: 10/c548b185ec21d8924309dfddbc6762c616550608f9d8c17099b60cd66a80b7757cae6f0c03d024f752d003a4dd32f51d12127ae6563bb6773c68ceda7e57b0b3 + languageName: node + linkType: hard + "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -10386,7 +11851,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.10.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": version: 1.15.11 resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: @@ -10415,6 +11880,13 @@ __metadata: languageName: node linkType: hard +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 10/c1e1644d5e074ac063ecbc3fb8582013ef91fff0e3fa41e76db23d2f62bc6d9677aac86db950917deed4fe1fdd772df780cfaa352075f23deec9c015313afb97 + languageName: node + linkType: hard + "fork-ts-checker-webpack-plugin@npm:9.1.0": version: 9.1.0 resolution: "fork-ts-checker-webpack-plugin@npm:9.1.0" @@ -10464,6 +11936,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.6" + mime-types: "npm:^2.1.12" + checksum: 10/1b6f3ccbf4540e535887b42218a2431a3f6cfdea320119c2affa2a7a374ad8fdd1e60166fc865181f45d49b1684c3e90e7b2190d3fe016692957afb9cf0d0d02 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -10532,7 +12015,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.3, fsevents@npm:~2.3.2": +"fsevents@npm:^2.3.2, fsevents@npm:^2.3.3, fsevents@npm:~2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -10542,7 +12025,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -10579,6 +12062,13 @@ __metadata: languageName: node linkType: hard +"funtypes@npm:^3.0.1": + version: 3.0.2 + resolution: "funtypes@npm:3.0.2" + checksum: 10/e36cf6b01aefbb2c5692e6f99fe4d1a7591b22c9bc0a2b88bd8187fffc004a71a08d90865ce68dc54e86965dd8a1875e3b53e2af769d310428a166c5f45db204 + languageName: node + linkType: hard + "generator-function@npm:^2.0.0": version: 2.0.1 resolution: "generator-function@npm:2.0.1" @@ -10665,6 +12155,15 @@ __metadata: languageName: node linkType: hard +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: "npm:^1.0.0" + checksum: 10/ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 + languageName: node + linkType: hard + "giget@npm:^2.0.0": version: 2.0.0 resolution: "giget@npm:2.0.0" @@ -10754,7 +12253,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.5, glob@npm:^7.1.4, glob@npm:^7.1.6": +"glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -10842,14 +12341,21 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 languageName: node linkType: hard -"graphemer@npm:^1.4.0": +"graceful-readlink@npm:>= 1.0.0": + version: 1.0.1 + resolution: "graceful-readlink@npm:1.0.1" + checksum: 10/9ecd6cbbcac5a0070c89f3e4279a9a812f21270aa0eacd3d7150c05ec27e0a0773064813226cbb18fa28162f44a7175a9a4911ca9e539d6c03ee9d3f21b78381 + languageName: node + linkType: hard + +"graphemer@npm:^1.4.0": version: 1.4.0 resolution: "graphemer@npm:1.4.0" checksum: 10/6dd60dba97007b21e3a829fab3f771803cc1292977fe610e240ea72afd67e5690ac9eeaafc4a99710e78962e5936ab5a460787c2a1180f1cb0ccfac37d29f897 @@ -10891,6 +12397,23 @@ __metadata: languageName: node linkType: hard +"har-schema@npm:^2.0.0": + version: 2.0.0 + resolution: "har-schema@npm:2.0.0" + checksum: 10/d8946348f333fb09e2bf24cc4c67eabb47c8e1d1aa1c14184c7ffec1140a49ec8aa78aa93677ae452d71d5fc0fdeec20f0c8c1237291fc2bcb3f502a5d204f9b + languageName: node + linkType: hard + +"har-validator@npm:~5.1.3": + version: 5.1.5 + resolution: "har-validator@npm:5.1.5" + dependencies: + ajv: "npm:^6.12.3" + har-schema: "npm:^2.0.0" + checksum: 10/b998a7269ca560d7f219eedc53e2c664cd87d487e428ae854a6af4573fc94f182fe9d2e3b92ab968249baec7ebaf9ead69cf975c931dc2ab282ec182ee988280 + languageName: node + linkType: hard + "hard-rejection@npm:^2.1.0": version: 2.1.0 resolution: "hard-rejection@npm:2.1.0" @@ -10969,6 +12492,33 @@ __metadata: languageName: node linkType: hard +"hash-base@npm:~3.0.4": + version: 3.0.5 + resolution: "hash-base@npm:3.0.5" + dependencies: + inherits: "npm:^2.0.4" + safe-buffer: "npm:^5.2.1" + checksum: 10/6a82675a5de2ea9347501bbe655a2334950c7ec972fd9810ae9529e06aeab8f7e8ef68fc2112e5e6f0745561a7e05326efca42ad59bb5fd116537f5f8b0a216d + languageName: node + linkType: hard + +"hash-wasm@npm:^4.11.0": + version: 4.12.0 + resolution: "hash-wasm@npm:4.12.0" + checksum: 10/529549f9565389b09310147a03cb6a5dd993f34f6742ed7fc84816e8881ce30036137aa995353bc2e8575883a6681299c3b2d181df56fcbe246f9cda82912084 + languageName: node + linkType: hard + +"hash.js@npm:1.1.3": + version: 1.1.3 + resolution: "hash.js@npm:1.1.3" + dependencies: + inherits: "npm:^2.0.3" + minimalistic-assert: "npm:^1.0.0" + checksum: 10/0dc4cb8164a906b06cc2ca2f333581a3fb91c36b64acd1e2f57da1b51ac5ed6b2135141f0513b734bf80e2c955b8d88fe0eade2a54c92d73d2eb26f49252d209 + languageName: node + linkType: hard + "hash.js@npm:1.1.7, hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": version: 1.1.7 resolution: "hash.js@npm:1.1.7" @@ -10988,6 +12538,17 @@ __metadata: languageName: node linkType: hard +"hdkey@npm:^1.1.1": + version: 1.1.2 + resolution: "hdkey@npm:1.1.2" + dependencies: + bs58check: "npm:^2.1.2" + safe-buffer: "npm:^5.1.1" + secp256k1: "npm:^3.0.1" + checksum: 10/aaebe82e1ac01a70463d0e1b2109d89efb20b7227c0e504d61c7a18c39362ecc6a2652b445677b9959c1b3d7b9ef393b4709bc406be2990a24eccbd9049a4dd0 + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -11061,6 +12622,17 @@ __metadata: languageName: node linkType: hard +"http-signature@npm:~1.2.0": + version: 1.2.0 + resolution: "http-signature@npm:1.2.0" + dependencies: + assert-plus: "npm:^1.0.0" + jsprim: "npm:^1.2.2" + sshpk: "npm:^1.7.0" + checksum: 10/2ff7112e6b0d8f08b382dfe705078c655501f2ddd76cf589d108445a9dd388a0a9be928c37108261519a7f53e6bbd1651048d74057b804807cce1ec49e87a95b + languageName: node + linkType: hard + "http2-wrapper@npm:^1.0.0-beta.5.2": version: 1.0.3 resolution: "http2-wrapper@npm:1.0.3" @@ -11177,7 +12749,7 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.2.0": +"import-local@npm:^3.0.2, import-local@npm:^3.2.0": version: 3.2.0 resolution: "import-local@npm:3.2.0" dependencies: @@ -11474,7 +13046,7 @@ __metadata: languageName: node linkType: hard -"is-generator-fn@npm:^2.1.0": +"is-generator-fn@npm:^2.0.0, is-generator-fn@npm:^2.1.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" checksum: 10/a6ad5492cf9d1746f73b6744e0c43c0020510b59d56ddcb78a91cbc173f09b5e6beff53d75c9c5a29feb618bfef2bf458e025ecf3a57ad2268e2fb2569f56215 @@ -11641,6 +13213,13 @@ __metadata: languageName: node linkType: hard +"is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 10/4b433bfb0f9026f079f4eb3fbaa4ed2de17c9995c3a0b5c800bec40799b4b2a8b4e051b1ada77749deb9ded4ae52fe2096973f3a93ff83df1a5a7184a669478c + languageName: node + linkType: hard + "is-unicode-supported@npm:^0.1.0": version: 0.1.0 resolution: "is-unicode-supported@npm:0.1.0" @@ -11674,6 +13253,13 @@ __metadata: languageName: node linkType: hard +"is_js@npm:^0.9.0": + version: 0.9.0 + resolution: "is_js@npm:0.9.0" + checksum: 10/c401de405046c79ad66c1cfccbd45ccd5dbd86bbbffb09cc081217e30b3a22d9332a5ce5f9d8a296301d6cb83e9932a02aa1e02acc642a4674639295d1920ed0 + languageName: node + linkType: hard + "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -11720,6 +13306,13 @@ __metadata: languageName: node linkType: hard +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 10/22d9c181015226d4534a227539256897bbbcb7edd1066ca4fc4d3a06dbd976325dfdd16b3983c7d236a89f256805c1a685a772e0364e98873d3819b064ad35a1 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" @@ -11727,6 +13320,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^5.0.4": + version: 5.2.1 + resolution: "istanbul-lib-instrument@npm:5.2.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^6.3.0" + checksum: 10/bbc4496c2f304d799f8ec22202ab38c010ac265c441947f075c0f7d46bd440b45c00e46017cf9053453d42182d768b1d6ed0e70a142c95ab00df9843aa5ab80e + languageName: node + linkType: hard + "istanbul-lib-instrument@npm:^6.0.0, istanbul-lib-instrument@npm:^6.0.2": version: 6.0.3 resolution: "istanbul-lib-instrument@npm:6.0.3" @@ -11751,6 +13357,17 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + source-map: "npm:^0.6.1" + checksum: 10/5526983462799aced011d776af166e350191b816821ea7bcf71cab3e5272657b062c47dc30697a22a43656e3ced78893a42de677f9ccf276a28c913190953b82 + languageName: node + linkType: hard + "istanbul-lib-source-maps@npm:^5.0.0": version: 5.0.6 resolution: "istanbul-lib-source-maps@npm:5.0.6" @@ -11881,6 +13498,17 @@ __metadata: languageName: node linkType: hard +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" + dependencies: + execa: "npm:^5.0.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + checksum: 10/3d93742e56b1a73a145d55b66e96711fbf87ef89b96c2fab7cfdfba8ec06612591a982111ca2b712bb853dbc16831ec8b43585a2a96b83862d6767de59cbf83d + languageName: node + linkType: hard + "jest-circus@npm:30.2.0": version: 30.2.0 resolution: "jest-circus@npm:30.2.0" @@ -11909,6 +13537,34 @@ __metadata: languageName: node linkType: hard +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + co: "npm:^4.6.0" + dedent: "npm:^1.0.0" + is-generator-fn: "npm:^2.0.0" + jest-each: "npm:^29.7.0" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + pure-rand: "npm:^6.0.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 10/716a8e3f40572fd0213bcfc1da90274bf30d856e5133af58089a6ce45089b63f4d679bd44e6be9d320e8390483ebc3ae9921981993986d21639d9019b523123d + languageName: node + linkType: hard + "jest-cli@npm:30.2.0": version: 30.2.0 resolution: "jest-cli@npm:30.2.0" @@ -11934,6 +13590,32 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + create-jest: "npm:^29.7.0" + exit: "npm:^0.1.2" + import-local: "npm:^3.0.2" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + yargs: "npm:^17.3.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 10/6cc62b34d002c034203065a31e5e9a19e7c76d9e8ef447a6f70f759c0714cb212c6245f75e270ba458620f9c7b26063cd8cf6cd1f7e3afd659a7cc08add17307 + languageName: node + linkType: hard + "jest-config@npm:30.2.0": version: 30.2.0 resolution: "jest-config@npm:30.2.0" @@ -11977,6 +13659,44 @@ __metadata: languageName: node linkType: hard +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/test-sequencer": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-jest: "npm:^29.7.0" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + deepmerge: "npm:^4.2.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-circus: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + parse-json: "npm:^5.2.0" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-json-comments: "npm:^3.1.1" + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: 10/6bdf570e9592e7d7dd5124fc0e21f5fe92bd15033513632431b211797e3ab57eaa312f83cc6481b3094b72324e369e876f163579d60016677c117ec4853cf02b + languageName: node + linkType: hard + "jest-diff@npm:30.2.0": version: 30.2.0 resolution: "jest-diff@npm:30.2.0" @@ -11989,6 +13709,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + diff-sequences: "npm:^29.6.3" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/6f3a7eb9cd9de5ea9e5aa94aed535631fa6f80221832952839b3cb59dd419b91c20b73887deb0b62230d06d02d6b6cf34ebb810b88d904bb4fe1e2e4f0905c98 + languageName: node + linkType: hard + "jest-docblock@npm:30.2.0": version: 30.2.0 resolution: "jest-docblock@npm:30.2.0" @@ -11998,6 +13730,15 @@ __metadata: languageName: node linkType: hard +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" + dependencies: + detect-newline: "npm:^3.0.0" + checksum: 10/8d48818055bc96c9e4ec2e217a5a375623c0d0bfae8d22c26e011074940c202aa2534a3362294c81d981046885c05d304376afba9f2874143025981148f3e96d + languageName: node + linkType: hard + "jest-each@npm:30.2.0": version: 30.2.0 resolution: "jest-each@npm:30.2.0" @@ -12011,6 +13752,19 @@ __metadata: languageName: node linkType: hard +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + pretty-format: "npm:^29.7.0" + checksum: 10/bd1a077654bdaa013b590deb5f7e7ade68f2e3289180a8c8f53bc8a49f3b40740c0ec2d3a3c1aee906f682775be2bebbac37491d80b634d15276b0aa0f2e3fda + languageName: node + linkType: hard + "jest-environment-node@npm:30.2.0": version: 30.2.0 resolution: "jest-environment-node@npm:30.2.0" @@ -12026,6 +13780,27 @@ __metadata: languageName: node linkType: hard +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/9cf7045adf2307cc93aed2f8488942e39388bff47ec1df149a997c6f714bfc66b2056768973770d3f8b1bf47396c19aa564877eb10ec978b952c6018ed1bd637 + languageName: node + linkType: hard + +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 10/88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 + languageName: node + linkType: hard + "jest-haste-map@npm:30.2.0": version: 30.2.0 resolution: "jest-haste-map@npm:30.2.0" @@ -12048,6 +13823,29 @@ __metadata: languageName: node linkType: hard +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/graceful-fs": "npm:^4.1.3" + "@types/node": "npm:*" + anymatch: "npm:^3.0.3" + fb-watchman: "npm:^2.0.0" + fsevents: "npm:^2.3.2" + graceful-fs: "npm:^4.2.9" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + walker: "npm:^1.0.8" + dependenciesMeta: + fsevents: + optional: true + checksum: 10/8531b42003581cb18a69a2774e68c456fb5a5c3280b1b9b77475af9e346b6a457250f9d756bfeeae2fe6cbc9ef28434c205edab9390ee970a919baddfa08bb85 + languageName: node + linkType: hard + "jest-leak-detector@npm:30.2.0": version: 30.2.0 resolution: "jest-leak-detector@npm:30.2.0" @@ -12058,6 +13856,16 @@ __metadata: languageName: node linkType: hard +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 + languageName: node + linkType: hard + "jest-matcher-utils@npm:30.2.0": version: 30.2.0 resolution: "jest-matcher-utils@npm:30.2.0" @@ -12070,6 +13878,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/981904a494299cf1e3baed352f8a3bd8b50a8c13a662c509b6a53c31461f94ea3bfeffa9d5efcfeb248e384e318c87de7e3baa6af0f79674e987482aa189af40 + languageName: node + linkType: hard + "jest-message-util@npm:30.2.0": version: 30.2.0 resolution: "jest-message-util@npm:30.2.0" @@ -12087,6 +13907,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" + dependencies: + "@babel/code-frame": "npm:^7.12.13" + "@jest/types": "npm:^29.6.3" + "@types/stack-utils": "npm:^2.0.0" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 10/31d53c6ed22095d86bab9d14c0fa70c4a92c749ea6ceece82cf30c22c9c0e26407acdfbdb0231435dc85a98d6d65ca0d9cbcd25cd1abb377fe945e843fb770b9 + languageName: node + linkType: hard + "jest-mock@npm:30.2.0": version: 30.2.0 resolution: "jest-mock@npm:30.2.0" @@ -12098,7 +13935,18 @@ __metadata: languageName: node linkType: hard -"jest-pnp-resolver@npm:^1.2.3": +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + checksum: 10/ae51d1b4f898724be5e0e52b2268a68fcd876d9b20633c864a6dd6b1994cbc48d62402b0f40f3a1b669b30ebd648821f086c26c08ffde192ced951ff4670d51c + languageName: node + linkType: hard + +"jest-pnp-resolver@npm:^1.2.2, jest-pnp-resolver@npm:^1.2.3": version: 1.2.3 resolution: "jest-pnp-resolver@npm:1.2.3" peerDependencies: @@ -12117,6 +13965,13 @@ __metadata: languageName: node linkType: hard +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 10/0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a + languageName: node + linkType: hard + "jest-resolve-dependencies@npm:30.2.0": version: 30.2.0 resolution: "jest-resolve-dependencies@npm:30.2.0" @@ -12127,6 +13982,16 @@ __metadata: languageName: node linkType: hard +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" + dependencies: + jest-regex-util: "npm:^29.6.3" + jest-snapshot: "npm:^29.7.0" + checksum: 10/1e206f94a660d81e977bcfb1baae6450cb4a81c92e06fad376cc5ea16b8e8c6ea78c383f39e95591a9eb7f925b6a1021086c38941aa7c1b8a6a813c2f6e93675 + languageName: node + linkType: hard + "jest-resolve@npm:30.2.0": version: 30.2.0 resolution: "jest-resolve@npm:30.2.0" @@ -12143,6 +14008,23 @@ __metadata: languageName: node linkType: hard +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-pnp-resolver: "npm:^1.2.2" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + resolve: "npm:^1.20.0" + resolve.exports: "npm:^2.0.0" + slash: "npm:^3.0.0" + checksum: 10/faa466fd9bc69ea6c37a545a7c6e808e073c66f46ab7d3d8a6ef084f8708f201b85d5fe1799789578b8b47fa1de47b9ee47b414d1863bc117a49e032ba77b7c7 + languageName: node + linkType: hard + "jest-runner@npm:30.2.0": version: 30.2.0 resolution: "jest-runner@npm:30.2.0" @@ -12173,6 +14055,35 @@ __metadata: languageName: node linkType: hard +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/environment": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + graceful-fs: "npm:^4.2.9" + jest-docblock: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-leak-detector: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-resolve: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + source-map-support: "npm:0.5.13" + checksum: 10/9d8748a494bd90f5c82acea99be9e99f21358263ce6feae44d3f1b0cd90991b5df5d18d607e73c07be95861ee86d1cbab2a3fc6ca4b21805f07ac29d47c1da1e + languageName: node + linkType: hard + "jest-runtime@npm:30.2.0": version: 30.2.0 resolution: "jest-runtime@npm:30.2.0" @@ -12203,6 +14114,36 @@ __metadata: languageName: node linkType: hard +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/globals": "npm:^29.7.0" + "@jest/source-map": "npm:^29.6.3" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + cjs-module-lexer: "npm:^1.0.0" + collect-v8-coverage: "npm:^1.0.0" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-bom: "npm:^4.0.0" + checksum: 10/59eb58eb7e150e0834a2d0c0d94f2a0b963ae7182cfa6c63f2b49b9c6ef794e5193ef1634e01db41420c36a94cefc512cdd67a055cd3e6fa2f41eaf0f82f5a20 + languageName: node + linkType: hard + "jest-snapshot@npm:30.2.0": version: 30.2.0 resolution: "jest-snapshot@npm:30.2.0" @@ -12232,6 +14173,34 @@ __metadata: languageName: node linkType: hard +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@babel/generator": "npm:^7.7.2" + "@babel/plugin-syntax-jsx": "npm:^7.7.2" + "@babel/plugin-syntax-typescript": "npm:^7.7.2" + "@babel/types": "npm:^7.3.3" + "@jest/expect-utils": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + chalk: "npm:^4.0.0" + expect: "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + natural-compare: "npm:^1.4.0" + pretty-format: "npm:^29.7.0" + semver: "npm:^7.5.3" + checksum: 10/cb19a3948256de5f922d52f251821f99657339969bf86843bd26cf3332eae94883e8260e3d2fba46129a27c3971c1aa522490e460e16c7fad516e82d10bbf9f8 + languageName: node + linkType: hard + "jest-util@npm:30.2.0": version: 30.2.0 resolution: "jest-util@npm:30.2.0" @@ -12246,6 +14215,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + graceful-fs: "npm:^4.2.9" + picomatch: "npm:^2.2.3" + checksum: 10/30d58af6967e7d42bd903ccc098f3b4d3859ed46238fbc88d4add6a3f10bea00c226b93660285f058bc7a65f6f9529cf4eb80f8d4707f79f9e3a23686b4ab8f3 + languageName: node + linkType: hard + "jest-validate@npm:30.2.0": version: 30.2.0 resolution: "jest-validate@npm:30.2.0" @@ -12260,6 +14243,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + leven: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + checksum: 10/8ee1163666d8eaa16d90a989edba2b4a3c8ab0ffaa95ad91b08ca42b015bfb70e164b247a5b17f9de32d096987cada63ed8491ab82761bfb9a28bc34b27ae161 + languageName: node + linkType: hard + "jest-watcher@npm:30.2.0": version: 30.2.0 resolution: "jest-watcher@npm:30.2.0" @@ -12276,6 +14273,22 @@ __metadata: languageName: node linkType: hard +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + jest-util: "npm:^29.7.0" + string-length: "npm:^4.0.1" + checksum: 10/4f616e0345676631a7034b1d94971aaa719f0cd4a6041be2aa299be437ea047afd4fe05c48873b7963f5687a2f6c7cbf51244be8b14e313b97bfe32b1e127e55 + languageName: node + linkType: hard + "jest-worker@npm:30.2.0": version: 30.2.0 resolution: "jest-worker@npm:30.2.0" @@ -12300,6 +14313,37 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" + dependencies: + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 10/364cbaef00d8a2729fc760227ad34b5e60829e0869bd84976bdfbd8c0d0f9c2f22677b3e6dd8afa76ed174765351cd12bae3d4530c62eefb3791055127ca9745 + languageName: node + linkType: hard + +"jest@npm:^29.7.0": + version: 29.7.0 + resolution: "jest@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + import-local: "npm:^3.0.2" + jest-cli: "npm:^29.7.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 10/97023d78446098c586faaa467fbf2c6b07ff06e2c85a19e3926adb5b0effe9ac60c4913ae03e2719f9c01ae8ffd8d92f6b262cedb9555ceeb5d19263d8c6362a + languageName: node + linkType: hard + "jest@npm:^30.0.0": version: 30.2.0 resolution: "jest@npm:30.2.0" @@ -12328,6 +14372,13 @@ __metadata: languageName: node linkType: hard +"js-sha3@npm:0.5.7": + version: 0.5.7 + resolution: "js-sha3@npm:0.5.7" + checksum: 10/32885c7edb50fca04017bacada8e5315c072d21d3d35e071e9640fc5577e200076a4718e0b2f33d86ab704accb68d2ade44f1e2ca424cc73a5929b9129dab948 + languageName: node + linkType: hard + "js-sha3@npm:0.8.0, js-sha3@npm:^0.8.0": version: 0.8.0 resolution: "js-sha3@npm:0.8.0" @@ -12391,6 +14442,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: 10/5450133242845100e694f0ef9175f44c012691a9b770b2571e677314e6f70600abb10777cdfc9a0c6a9f2ac6d134577403633de73e2fcd0f97875a67744e2d14 + languageName: node + linkType: hard + "jsesc@npm:^3.0.2": version: 3.1.0 resolution: "jsesc@npm:3.1.0" @@ -12435,6 +14493,13 @@ __metadata: languageName: node linkType: hard +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 10/8b3b64eff4a807dc2a3045b104ed1b9335cd8d57aa74c58718f07f0f48b8baa3293b00af4dcfbdc9144c3aafea1e97982cc27cc8e150fc5d93c540649507a458 + languageName: node + linkType: hard + "json-stable-stringify-without-jsonify@npm:^1.0.1": version: 1.0.1 resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" @@ -12456,7 +14521,7 @@ __metadata: languageName: node linkType: hard -"json-stringify-safe@npm:^5.0.1": +"json-stringify-safe@npm:^5.0.1, json-stringify-safe@npm:~5.0.1": version: 5.0.1 resolution: "json-stringify-safe@npm:5.0.1" checksum: 10/59169a081e4eeb6f9559ae1f938f656191c000e0512aa6df9f3c8b2437a4ab1823819c6b9fd1818a4e39593ccfd72e9a051fdd3e2d1e340ed913679e888ded8c @@ -12511,6 +14576,25 @@ __metadata: languageName: node linkType: hard +"jsprim@npm:^1.2.2": + version: 1.4.2 + resolution: "jsprim@npm:1.4.2" + dependencies: + assert-plus: "npm:1.0.0" + extsprintf: "npm:1.3.0" + json-schema: "npm:0.4.0" + verror: "npm:1.10.0" + checksum: 10/df2bf234eab1b5078d01bcbff3553d50a243f7b5c10a169745efeda6344d62798bd1d85bcca6a8446f3b5d0495e989db45f9de8dae219f0f9796e70e0c776089 + languageName: node + linkType: hard + +"jssha@npm:3.2.0": + version: 3.2.0 + resolution: "jssha@npm:3.2.0" + checksum: 10/6d01e8fa96a05534f19f81f7311f3f925d456007fd772085acada24f6013573c2854650d6b23c193afe18ba06efb40b93ce3ea6c45797ae4f79c0afcfaf6def0 + languageName: node + linkType: hard + "jssha@npm:^3.3.1": version: 3.3.1 resolution: "jssha@npm:3.3.1" @@ -12560,6 +14644,20 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^3.0.3": + version: 3.0.3 + resolution: "kleur@npm:3.0.3" + checksum: 10/0c0ecaf00a5c6173d25059c7db2113850b5457016dfa1d0e3ef26da4704fbb186b4938d7611246d86f0ddf1bccf26828daa5877b1f232a65e7373d0122a83e7f + languageName: node + linkType: hard + +"lcov-parse@npm:0.0.10": + version: 0.0.10 + resolution: "lcov-parse@npm:0.0.10" + checksum: 10/4aa7adbece1de730e29071b5f861ae1a34618cef5df4b5df913f35222316947588b56291fdb5e7452d9a80f44bddd3b8380d57531a3b0bc5b4454975c68045de + languageName: node + linkType: hard + "leven@npm:^3.1.0": version: 3.1.0 resolution: "leven@npm:3.1.0" @@ -12600,6 +14698,13 @@ __metadata: languageName: node linkType: hard +"litecoin-regex@npm:^1.0.8": + version: 1.0.9 + resolution: "litecoin-regex@npm:1.0.9" + checksum: 10/85601240a622ff74b8978837a7422879511b290fa4c04e605f6fa4915448f298e51a5fcb5ddab444a0ec3e6a6abf5fedbae8b02260d56b214785c9769275313b + languageName: node + linkType: hard + "load-esm@npm:1.0.3": version: 1.0.3 resolution: "load-esm@npm:1.0.3" @@ -12669,7 +14774,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.17.21": +"lodash@npm:4.17.21, lodash@npm:^4.17.19, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 @@ -12711,6 +14816,13 @@ __metadata: languageName: node linkType: hard +"lossless-json@npm:^4.2.0": + version: 4.3.0 + resolution: "lossless-json@npm:4.3.0" + checksum: 10/a984a882c79b6e62a917d0202518472c17587bdee002f1427d7ec61115f9fbdeb5f0baa9f0e9fff8d9dbacd17da6b6c910012b2dcab69dd33d151cb7c13d5a37 + languageName: node + linkType: hard + "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -12972,7 +15084,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.0, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.0, micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -12982,6 +15094,18 @@ __metadata: languageName: node linkType: hard +"miller-rabin@npm:^4.0.0": + version: 4.0.1 + resolution: "miller-rabin@npm:4.0.1" + dependencies: + bn.js: "npm:^4.0.0" + brorand: "npm:^1.0.1" + bin: + miller-rabin: bin/miller-rabin + checksum: 10/2a38ba9d1e878d94ee8a8ab3505b40e8d44fb9700a7716570fe4c8ca7e20d49b69aea579106580618c877cc6ff969eff71705042fafb47573736bf89404417bc + languageName: node + linkType: hard + "mime-db@npm:1.52.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" @@ -12996,7 +15120,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -13112,7 +15236,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.5, minimist@npm:^1.2.6": +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f @@ -13271,6 +15395,13 @@ __metadata: languageName: node linkType: hard +"monero-regex@npm:^1.0.8": + version: 1.0.9 + resolution: "monero-regex@npm:1.0.9" + checksum: 10/b444ed32bbe621269fe249749588fbc2bec12ba75994695fc1763ff5b244d69a75dc03f2733680beb42700a272313a039ce6bb2cf362b338459a591e87f98640 + languageName: node + linkType: hard + "ms@npm:^2.0.0, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -13409,6 +15540,20 @@ __metadata: languageName: node linkType: hard +"ndjson@npm:^1.5.0": + version: 1.5.0 + resolution: "ndjson@npm:1.5.0" + dependencies: + json-stringify-safe: "npm:^5.0.1" + minimist: "npm:^1.2.0" + split2: "npm:^2.1.0" + through2: "npm:^2.0.3" + bin: + ndjson: cli.js + checksum: 10/eccc11dac1c74c22d11207257f3d863b7b70c0977a5486bf945e9784c51a49b97ed40b884b033e50dba2a78b169299fa43b381134b0495d17846b7ffcf6323ba + languageName: node + linkType: hard + "negotiator@npm:0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" @@ -13437,6 +15582,13 @@ __metadata: languageName: node linkType: hard +"neo-regex@npm:^1.0.7": + version: 1.0.8 + resolution: "neo-regex@npm:1.0.8" + checksum: 10/c68455418e8bb2c2803b7275ca43d82ec0db9218126e8dea53e5b9a3d447d11340c277319b0e6dfbf3585fa23935832cbf9d3297f3222728e593740306f91f50 + languageName: node + linkType: hard + "netmask@npm:^2.0.2": version: 2.0.2 resolution: "netmask@npm:2.0.2" @@ -13754,6 +15906,13 @@ __metadata: languageName: node linkType: hard +"oauth-sign@npm:~0.9.0": + version: 0.9.0 + resolution: "oauth-sign@npm:0.9.0" + checksum: 10/1809a366d258f41fdf4ab5310cff3d1e15f96b187503bc7333cef4351de7bd0f52cb269bc95800f1fae5fb04dd886287df1471985fd67e8484729fdbcf857119 + languageName: node + linkType: hard + "object-assign@npm:^4, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -13880,6 +16039,13 @@ __metadata: languageName: node linkType: hard +"os@npm:^0.1.1": + version: 0.1.2 + resolution: "os@npm:0.1.2" + checksum: 10/dc2d99759eef13f5dc47ddb12c67b9760a7196fd83a35a7aec2d75b82f91163ca1d4e8872238f8c2a35f4cddd5adf5ce6638a234c0563c748d3cd1d69a9f7153 + languageName: node + linkType: hard + "osmojs@npm:^0.37.0": version: 0.37.0 resolution: "osmojs@npm:0.37.0" @@ -13928,6 +16094,26 @@ __metadata: languageName: node linkType: hard +"ox@npm:^0.4.4": + version: 0.4.4 + resolution: "ox@npm:0.4.4" + dependencies: + "@adraffy/ens-normalize": "npm:^1.10.1" + "@noble/curves": "npm:^1.6.0" + "@noble/hashes": "npm:^1.5.0" + "@scure/bip32": "npm:^1.5.0" + "@scure/bip39": "npm:^1.4.0" + abitype: "npm:^1.0.6" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/7e6624a91b2064c160af1bde0febb8044a35a4353885afa1cf2993f4755cad5f485857469393455a4fce3f4bc0e95f18b3dd34d5f59fd07c25fcc7ddb3047bf4 + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -13935,7 +16121,7 @@ __metadata: languageName: node linkType: hard -"p-lazy@npm:3.1.0": +"p-lazy@npm:3.1.0, p-lazy@npm:^3.1.0": version: 3.1.0 resolution: "p-lazy@npm:3.1.0" checksum: 10/289dbfd9de7d241498fd2d1ac9ec4e36de78672a92175f4b3c5fa9527b62f7549b06f549570b47f218d90b946f3a75611f9b8407934fe01512bc4fb56fdbf47d @@ -14070,7 +16256,7 @@ __metadata: languageName: node linkType: hard -"pako@npm:^2.0.3": +"pako@npm:^2.0.3, pako@npm:^2.0.4": version: 2.1.0 resolution: "pako@npm:2.1.0" checksum: 10/38a04991d0ec4f4b92794a68b8c92bf7340692c5d980255c92148da96eb3e550df7a86a7128b5ac0c65ecddfe5ef3bbe9c6dab13e1bc315086e759b18f7c1401 @@ -14086,6 +16272,19 @@ __metadata: languageName: node linkType: hard +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.9": + version: 5.1.9 + resolution: "parse-asn1@npm:5.1.9" + dependencies: + asn1.js: "npm:^4.10.1" + browserify-aes: "npm:^1.2.0" + evp_bytestokey: "npm:^1.0.3" + pbkdf2: "npm:^3.1.5" + safe-buffer: "npm:^5.2.1" + checksum: 10/bc3d616a8076fa8a9a34cab8af6905859a1bafd0c49c98132acc7d29b779c2b81d4a8fc610f5bedc9770cc4bfc323f7c939ad7413e9df6ba60cb931010c42f52 + languageName: node + linkType: hard + "parse-conflict-json@npm:^3.0.0": version: 3.0.1 resolution: "parse-conflict-json@npm:3.0.1" @@ -14210,7 +16409,7 @@ __metadata: languageName: node linkType: hard -"pbkdf2@npm:^3.0.17": +"pbkdf2@npm:^3.0.17, pbkdf2@npm:^3.1.2, pbkdf2@npm:^3.1.5": version: 3.1.5 resolution: "pbkdf2@npm:3.1.5" dependencies: @@ -14231,6 +16430,13 @@ __metadata: languageName: node linkType: hard +"performance-now@npm:^2.1.0": + version: 2.1.0 + resolution: "performance-now@npm:2.1.0" + checksum: 10/534e641aa8f7cba160f0afec0599b6cecefbb516a2e837b512be0adbe6c1da5550e89c78059c7fabc5c9ffdf6627edabe23eb7c518c4500067a898fa65c2b550 + languageName: node + linkType: hard + "picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -14245,7 +16451,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc @@ -14266,7 +16472,7 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.7": +"pirates@npm:^4.0.4, pirates@npm:^4.0.7": version: 4.0.7 resolution: "pirates@npm:4.0.7" checksum: 10/2427f371366081ae42feb58214f04805d6b41d6b84d74480ebcc9e0ddbd7105a139f7c653daeaf83ad8a1a77214cf07f64178e76de048128fec501eab3305a96 @@ -14387,6 +16593,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: 10/dea96bc83c83cd91b2bfc55757b6b2747edcaac45b568e46de29deee80742f17bc76fe8898135a70d904f4928eafd8bb693cd1da4896e8bdd3c5e82cadf1d2bb + languageName: node + linkType: hard + "pretty-ms@npm:7.0.1": version: 7.0.1 resolution: "pretty-ms@npm:7.0.1" @@ -14501,6 +16718,16 @@ __metadata: languageName: node linkType: hard +"prompts@npm:^2.0.1": + version: 2.4.2 + resolution: "prompts@npm:2.4.2" + dependencies: + kleur: "npm:^3.0.3" + sisteransi: "npm:^1.0.5" + checksum: 10/c52536521a4d21eff4f2f2aa4572446cad227464066365a7167e52ccf8d9839c099f9afec1aba0eed3d5a2514b3e79e0b3e7a1dc326b9acde6b75d27ed74b1a9 + languageName: node + linkType: hard + "protobufjs@npm:^6.10.2, protobufjs@npm:^6.11.3, protobufjs@npm:^6.8.8, protobufjs@npm:~6.11.2, protobufjs@npm:~6.11.3": version: 6.11.4 resolution: "protobufjs@npm:6.11.4" @@ -14545,6 +16772,17 @@ __metadata: languageName: node linkType: hard +"protocol-buffers-encodings@npm:^1.1.0": + version: 1.2.0 + resolution: "protocol-buffers-encodings@npm:1.2.0" + dependencies: + b4a: "npm:^1.6.0" + signed-varint: "npm:^2.0.1" + varint: "npm:5.0.0" + checksum: 10/eaef96f1018c5d3af1bf422568cad0b42c9642c450d2bc83c7a8624118fd5288f513227699bba87177e7f916cc0e7a57773f3fa03eb2e84531c55107f545f20b + languageName: node + linkType: hard + "proxy-addr@npm:^2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -14562,6 +16800,29 @@ __metadata: languageName: node linkType: hard +"psl@npm:^1.1.28": + version: 1.15.0 + resolution: "psl@npm:1.15.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10/5e7467eb5196eb7900d156783d12907d445c0122f76c73203ce96b148a6ccf8c5450cc805887ffada38ff92d634afcf33720c24053cb01d5b6598d1c913c5caf + languageName: node + linkType: hard + +"public-encrypt@npm:^4.0.3": + version: 4.0.3 + resolution: "public-encrypt@npm:4.0.3" + dependencies: + bn.js: "npm:^4.1.0" + browserify-rsa: "npm:^4.0.0" + create-hash: "npm:^1.1.0" + parse-asn1: "npm:^5.0.0" + randombytes: "npm:^2.0.1" + safe-buffer: "npm:^5.1.2" + checksum: 10/059d64da8ba9ea0733377d23b57b6cbe5be663c8eb187b9c051eec85f799ff95c4e194eb3a69db07cc1f73a2a63519e67716ae9b8630e13e7149840d0abe044d + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.3 resolution: "pump@npm:3.0.3" @@ -14572,14 +16833,32 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0": +"pumpify@npm:^2.0.1": + version: 2.0.1 + resolution: "pumpify@npm:2.0.1" + dependencies: + duplexify: "npm:^4.1.1" + inherits: "npm:^2.0.3" + pump: "npm:^3.0.0" + checksum: 10/54bfdd04a30f459de5f1d1d022dc729e7257748900adf567a3b009f5aefe4a862ca91f3fb272f86c621eae631c4cc41f0efe5ee270752e2f9a90e7e63a9f8570 + languageName: node + linkType: hard + +"punycode@npm:^1.4.1": + version: 1.4.1 + resolution: "punycode@npm:1.4.1" + checksum: 10/af2700dde1a116791ff8301348ff344c47d6c224e875057237d1b5112035655fb07a6175cfdb8bf0e3a8cdfd2dc82b3a622e0aefd605566c0e949a6d0d1256a4 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10/febdc4362bead22f9e2608ff0171713230b57aff9dddc1c273aa2a651fbd366f94b7d6a71d78342a7c0819906750351ca7f2edd26ea41b626d87d6a13d1bd059 languageName: node linkType: hard -"pure-rand@npm:^6.1.0": +"pure-rand@npm:^6.0.0, pure-rand@npm:^6.1.0": version: 6.1.0 resolution: "pure-rand@npm:6.1.0" checksum: 10/256aa4bcaf9297256f552914e03cbdb0039c8fe1db11fa1e6d3f80790e16e563eb0a859a1e61082a95e224fc0c608661839439f8ecc6a3db4e48d46d99216ee4 @@ -14611,6 +16890,22 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.12.3": + version: 6.14.1 + resolution: "qs@npm:6.14.1" + dependencies: + side-channel: "npm:^1.1.0" + checksum: 10/34b5ab00a910df432d55180ef39c1d1375e550f098b5ec153b41787f1a6a6d7e5f9495593c3b112b77dbc6709d0ae18e55b82847a4c2bbbb0de1e8ccbb1794c5 + languageName: node + linkType: hard + +"qs@npm:~6.5.2": + version: 6.5.3 + resolution: "qs@npm:6.5.3" + checksum: 10/485c990fba7ad17671e16c92715fb064c1600337738f5d140024eb33a49fbc1ed31890d3db850117c760caeb9c9cc9f4ba22a15c20dd119968e41e3d3fe60b28 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -14648,7 +16943,7 @@ __metadata: languageName: node linkType: hard -"randombytes@npm:^2.0.1, randombytes@npm:^2.1.0": +"randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" dependencies: @@ -14657,6 +16952,16 @@ __metadata: languageName: node linkType: hard +"randomfill@npm:^1.0.4": + version: 1.0.4 + resolution: "randomfill@npm:1.0.4" + dependencies: + randombytes: "npm:^2.0.5" + safe-buffer: "npm:^5.1.0" + checksum: 10/33734bb578a868d29ee1b8555e21a36711db084065d94e019a6d03caa67debef8d6a1bfd06a2b597e32901ddc761ab483a85393f0d9a75838f1912461d4dbfc7 + languageName: node + linkType: hard + "range-parser@npm:^1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" @@ -14698,7 +17003,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.3.1": +"react-is@npm:^18.0.0, react-is@npm:^18.3.1": version: 18.3.1 resolution: "react-is@npm:18.3.1" checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22 @@ -14778,7 +17083,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.3.8": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.8, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -14793,7 +17098,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.2, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -14837,6 +17142,15 @@ __metadata: languageName: node linkType: hard +"redeyed@npm:~2.1.0": + version: 2.1.1 + resolution: "redeyed@npm:2.1.1" + dependencies: + esprima: "npm:~4.0.0" + checksum: 10/86880f97d54bb55bbf1c338e27fe28f18f52afc2f5afa808354a09a3777aa79b4f04e04844350d7fec80aa2d299196bde256b21f586e7e5d9b63494bd4a9db27 + languageName: node + linkType: hard + "reflect-metadata@npm:^0.1.13": version: 0.1.14 resolution: "reflect-metadata@npm:0.1.14" @@ -14860,6 +17174,13 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.11.0": + version: 0.11.1 + resolution: "regenerator-runtime@npm:0.11.1" + checksum: 10/64e62d78594c227e7d5269811bca9e4aa6451332adaae8c79a30cab0fa98733b1ad90bdb9d038095c340c6fad3b414a49a8d9e0b6b424ab7ff8f94f35704f8a2 + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.14.0": version: 0.14.1 resolution: "regenerator-runtime@npm:0.14.1" @@ -14881,6 +17202,34 @@ __metadata: languageName: node linkType: hard +"request@npm:~2.88.0": + version: 2.88.2 + resolution: "request@npm:2.88.2" + dependencies: + aws-sign2: "npm:~0.7.0" + aws4: "npm:^1.8.0" + caseless: "npm:~0.12.0" + combined-stream: "npm:~1.0.6" + extend: "npm:~3.0.2" + forever-agent: "npm:~0.6.1" + form-data: "npm:~2.3.2" + har-validator: "npm:~5.1.3" + http-signature: "npm:~1.2.0" + is-typedarray: "npm:~1.0.0" + isstream: "npm:~0.1.2" + json-stringify-safe: "npm:~5.0.1" + mime-types: "npm:~2.1.19" + oauth-sign: "npm:~0.9.0" + performance-now: "npm:^2.1.0" + qs: "npm:~6.5.2" + safe-buffer: "npm:^5.1.2" + tough-cookie: "npm:~2.5.0" + tunnel-agent: "npm:^0.6.0" + uuid: "npm:^3.3.2" + checksum: 10/005b8b237b56f1571cfd4ecc09772adaa2e82dcb884fc14ea2bb25e23dbf7c2009f9929e0b6d3fd5802e33ed8ee705a3b594c8f9467c1458cd973872bf89db8e + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -14943,7 +17292,14 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.22.8": +"resolve.exports@npm:^2.0.0": + version: 2.0.3 + resolution: "resolve.exports@npm:2.0.3" + checksum: 10/536efee0f30a10fac8604e6cdc7844dbc3f4313568d09f06db4f7ed8a5b8aeb8585966fe975083d1f2dfbc87cf5f8bc7ab65a5c23385c14acbb535ca79f8398a + languageName: node + linkType: hard + +"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.8": version: 1.22.11 resolution: "resolve@npm:1.22.11" dependencies: @@ -14956,7 +17312,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": +"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": version: 1.22.11 resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" dependencies: @@ -15021,6 +17377,13 @@ __metadata: languageName: node linkType: hard +"ripple-regex@npm:^1.1.8": + version: 1.1.8 + resolution: "ripple-regex@npm:1.1.8" + checksum: 10/b3d197de92ffeacd7d436451213caffa542e4ac896cce37bce8ceee833dfa5394d721c8004dbbd2ffbaa358bbea4937b4ee7d5df7446fa625f299110d5e1e24f + languageName: node + linkType: hard + "rlp@npm:^2.0.0, rlp@npm:^2.2.3": version: 2.2.7 resolution: "rlp@npm:2.2.7" @@ -15151,7 +17514,7 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 @@ -15190,7 +17553,14 @@ __metadata: languageName: node linkType: hard -"scrypt-js@npm:3.0.1, scrypt-js@npm:^3.0.0": +"scrypt-js@npm:2.0.4": + version: 2.0.4 + resolution: "scrypt-js@npm:2.0.4" + checksum: 10/584c42ca17f8da7d9eec483b56743e868d1e795634f9581169f0b40c7abc5d4266dfb9d59d8f0a65479885c74fd44f3a99aca5a5048d3c4f7d33d88389aa2014 + languageName: node + linkType: hard + +"scrypt-js@npm:3.0.1, scrypt-js@npm:^3.0.0, scrypt-js@npm:^3.0.1": version: 3.0.1 resolution: "scrypt-js@npm:3.0.1" checksum: 10/2f8aa72b7f76a6f9c446bbec5670f80d47497bccce98474203d89b5667717223eeb04a50492ae685ed7adc5a060fc2d8f9fd988f8f7ebdaf3341967f3aeff116 @@ -15226,6 +17596,13 @@ __metadata: languageName: node linkType: hard +"secure-random@npm:^1.1.2": + version: 1.1.2 + resolution: "secure-random@npm:1.1.2" + checksum: 10/8b5d32df870346a9400a6a31c0c4da4862df0416289fef7ec8bfda1187cf2ae08a281f57d174287f9a43b5e757478d22631c7dc2582fddcd2cfafc1d92042d72 + languageName: node + linkType: hard + "semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.0": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -15244,7 +17621,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.3.1": +"semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -15346,6 +17723,13 @@ __metadata: languageName: node linkType: hard +"setimmediate@npm:1.0.4": + version: 1.0.4 + resolution: "setimmediate@npm:1.0.4" + checksum: 10/eb11c0c817a9373d07a0501c298ebcac72755a1d6444b44d5b7827bc1f81848801fae14067dd14b1cc0529fbc7a794d1a661b99dfbc83784dbbccdf0914a7e63 + languageName: node + linkType: hard + "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -15503,7 +17887,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 10/a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -15517,6 +17901,15 @@ __metadata: languageName: node linkType: hard +"signed-varint@npm:^2.0.1": + version: 2.0.1 + resolution: "signed-varint@npm:2.0.1" + dependencies: + varint: "npm:~5.0.0" + checksum: 10/4043d9c993257d856f7f5210c7cd53ac017f7c830796aa974f5f9936ce8f44fe2d8571865f736b59ee6d875a4d85879136e8d8952e5eff7b087d0398c037a156 + languageName: node + linkType: hard + "sigstore@npm:^2.2.0": version: 2.3.1 resolution: "sigstore@npm:2.3.1" @@ -15531,6 +17924,13 @@ __metadata: languageName: node linkType: hard +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 10/aba6438f46d2bfcef94cf112c835ab395172c75f67453fe05c340c770d3c402363018ae1ab4172a1026a90c47eaccf3af7b6ff6fa749a680c2929bd7fa2b37a4 + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -15700,6 +18100,15 @@ __metadata: languageName: node linkType: hard +"split2@npm:^2.1.0": + version: 2.2.0 + resolution: "split2@npm:2.2.0" + dependencies: + through2: "npm:^2.0.2" + checksum: 10/5799dcd2ae5d2f905c4f3e14942d8efa98c4c5ea13e5e5e978f4693e9596393174cdab59ad2d046ec1799c081d82b8e75c627d2911c034a8b5827623e719ccc8 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -15707,6 +18116,27 @@ __metadata: languageName: node linkType: hard +"sshpk@npm:^1.7.0": + version: 1.18.0 + resolution: "sshpk@npm:1.18.0" + dependencies: + asn1: "npm:~0.2.3" + assert-plus: "npm:^1.0.0" + bcrypt-pbkdf: "npm:^1.0.0" + dashdash: "npm:^1.12.0" + ecc-jsbn: "npm:~0.1.1" + getpass: "npm:^0.1.1" + jsbn: "npm:~0.1.0" + safer-buffer: "npm:^2.0.2" + tweetnacl: "npm:~0.14.0" + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: 10/858339d43e3c6b6a848772a66f69442ce74f1a37655d9f35ba9d1f85329499ff0000af9f8ab83dbb39ad24c0c370edabe0be1e39863f70c6cded9924b8458c34 + languageName: node + linkType: hard + "ssri@npm:^10.0.0, ssri@npm:^10.0.6": version: 10.0.6 resolution: "ssri@npm:10.0.6" @@ -15732,7 +18162,7 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.6": +"stack-utils@npm:^2.0.3, stack-utils@npm:^2.0.6": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" dependencies: @@ -15741,6 +18171,25 @@ __metadata: languageName: node linkType: hard +"starknet@npm:^9.3.0": + version: 9.3.0 + resolution: "starknet@npm:9.3.0" + dependencies: + "@noble/curves": "npm:~1.7.0" + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + "@scure/starknet": "npm:1.1.0" + "@starknet-io/get-starknet-wallet-standard": "npm:^5.0.0" + "@starknet-io/starknet-types-010": "npm:@starknet-io/types-js@0.10.0" + "@starknet-io/starknet-types-09": "npm:@starknet-io/types-js@~0.9.1" + abi-wan-kanabi: "npm:2.2.4" + lossless-json: "npm:^4.2.0" + pako: "npm:^2.0.4" + ts-mixer: "npm:^6.0.3" + checksum: 10/7c2c78cd149d919089edbd3e8a823a235fd84ff432eb1abe8fa351b95d3e8b98607b24790dc331fd043e27467cda93bf6f97d859f3a572ac6adb141d9cd283cb + languageName: node + linkType: hard + "statuses@npm:^2.0.1, statuses@npm:~2.0.2": version: 2.0.2 resolution: "statuses@npm:2.0.2" @@ -15774,6 +18223,13 @@ __metadata: languageName: node linkType: hard +"stream-shift@npm:^1.0.0, stream-shift@npm:^1.0.2": + version: 1.0.3 + resolution: "stream-shift@npm:1.0.3" + checksum: 10/a24c0a3f66a8f9024bd1d579a533a53be283b4475d4e6b4b3211b964031447bdf6532dd1f3c2b0ad66752554391b7c62bd7ca4559193381f766534e723d50242 + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -15781,7 +18237,7 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.2": +"string-length@npm:^4.0.1, string-length@npm:^4.0.2": version: 4.0.2 resolution: "string-length@npm:4.0.2" dependencies: @@ -16002,6 +18458,13 @@ __metadata: languageName: node linkType: hard +"symbol.inspect@npm:1.0.1": + version: 1.0.1 + resolution: "symbol.inspect@npm:1.0.1" + checksum: 10/47fa8d38d0bc5d04c06df2f71bba1a723ee0e015ca042c47b29c11f107877dd1a2e2d2154c9ef5eec11e92e4165d126c844f06d05da80e477581c8f284f05fdf + languageName: node + linkType: hard + "synckit@npm:^0.11.7, synckit@npm:^0.11.8": version: 0.11.11 resolution: "synckit@npm:0.11.11" @@ -16045,6 +18508,23 @@ __metadata: languageName: node linkType: hard +"tendermint-tx-builder@npm:1.0.16": + version: 1.0.16 + resolution: "tendermint-tx-builder@npm:1.0.16" + dependencies: + "@bithighlander/bitcoin-cash-js-lib": "npm:^5.2.1" + "@pioneer-platform/loggerdog": "npm:^8.0.1" + "@pioneer-platform/pioneer-coins": "npm:^8.1.19" + "@shapeshiftoss/hdwallet-core": "npm:latest" + "@types/node": "npm:^15.0.2" + bip39: "npm:^3.0.4" + codeclimate-test-reporter: "npm:^0.5.1" + fiosdk-offline: "npm:^1.2.21" + google-protobuf: "npm:^3.17.0" + checksum: 10/867800d3f4d365eab33598a0465620e9127420335fb5bcd9a27a1788ff4659605178e105f20930cd4f9e193a7fdd39a204174f28929851a866922299d7957639 + languageName: node + linkType: hard + "terser-webpack-plugin@npm:^5.3.11": version: 5.3.14 resolution: "terser-webpack-plugin@npm:5.3.14" @@ -16081,6 +18561,13 @@ __metadata: languageName: node linkType: hard +"teslabot@npm:^1.3.0": + version: 1.5.0 + resolution: "teslabot@npm:1.5.0" + checksum: 10/714ab9a3eadf17efdbcc78c140246c6cf02b0fceff2fd3b9e37de7c86ad05af816037145a258b4b115327bc88219d782f64047812483169037d6956f46bb1450 + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -16099,6 +18586,23 @@ __metadata: languageName: node linkType: hard +"text-encoding@npm:0.7.0": + version: 0.7.0 + resolution: "text-encoding@npm:0.7.0" + checksum: 10/c61b7a59a54c58f0714da0ef4c1f65732821bb1f761e6f21c3e681e51c6dd56fdefa4fcfccd3254bf47132d87420fbc9898da21a804c89e87861f4e1478d5f18 + languageName: node + linkType: hard + +"through2@npm:^2.0.2, through2@npm:^2.0.3": + version: 2.0.5 + resolution: "through2@npm:2.0.5" + dependencies: + readable-stream: "npm:~2.3.6" + xtend: "npm:~4.0.1" + checksum: 10/cd71f7dcdc7a8204fea003a14a433ef99384b7d4e31f5497e1f9f622b3cf3be3691f908455f98723bdc80922a53af7fa10c3b7abbe51c6fd3d536dbc7850e2c4 + languageName: node + linkType: hard + "tiny-invariant@npm:^1.1.0": version: 1.3.3 resolution: "tiny-invariant@npm:1.3.3" @@ -16210,6 +18714,16 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:~2.5.0": + version: 2.5.0 + resolution: "tough-cookie@npm:2.5.0" + dependencies: + psl: "npm:^1.1.28" + punycode: "npm:^2.1.1" + checksum: 10/024cb13a4d1fe9af57f4323dff765dd9b217cc2a69be77e3b8a1ca45600aa33a097b6ad949f225d885e904f4bd3ceccef104741ef202d8378e6ca78e850ff82f + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -16266,6 +18780,46 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.2": + version: 29.4.6 + resolution: "ts-jest@npm:29.4.6" + dependencies: + bs-logger: "npm:^0.2.6" + fast-json-stable-stringify: "npm:^2.1.0" + handlebars: "npm:^4.7.8" + json5: "npm:^2.2.3" + lodash.memoize: "npm:^4.1.2" + make-error: "npm:^1.3.6" + semver: "npm:^7.7.3" + type-fest: "npm:^4.41.0" + yargs-parser: "npm:^21.1.1" + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/transform": ^29.0.0 || ^30.0.0 + "@jest/types": ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/transform": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + bin: + ts-jest: cli.js + checksum: 10/e0ff9e13f684166d5331808b288043b8054f49a1c2970480a92ba3caec8d0ff20edd092f2a4e7a3ad8fcb9ba4d674bee10ec7ee75046d8066bbe43a7d16cf72e + languageName: node + linkType: hard + "ts-jest@npm:^29.2.5": version: 29.4.5 resolution: "ts-jest@npm:29.4.5" @@ -16322,7 +18876,14 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.9.2": +"ts-mixer@npm:^6.0.3": + version: 6.0.4 + resolution: "ts-mixer@npm:6.0.4" + checksum: 10/f20571a4a4ff7b5e1a2ff659208c1ea9d4180dda932b71d289edc99e25a2948c9048e2e676b930302ac0f8e88279e0da6022823183e67de3906a3f3a8b72ea80 + languageName: node + linkType: hard + +"ts-node@npm:^10.9.1, ts-node@npm:^10.9.2": version: 10.9.2 resolution: "ts-node@npm:10.9.2" dependencies: @@ -16434,6 +18995,15 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10/7f0d9ed5c22404072b2ae8edc45c071772affd2ed14a74f03b4e71b4dd1a14c3714d85aed64abcaaee5fec2efc79002ba81155c708f4df65821b444abb0cfade + languageName: node + linkType: hard + "turbo-darwin-64@npm:2.6.1": version: 2.6.1 resolution: "turbo-darwin-64@npm:2.6.1" @@ -16505,13 +19075,20 @@ __metadata: languageName: node linkType: hard -"tweetnacl@npm:^1.0.3": +"tweetnacl@npm:1.0.3, tweetnacl@npm:^1.0.3": version: 1.0.3 resolution: "tweetnacl@npm:1.0.3" checksum: 10/ca122c2f86631f3c0f6d28efb44af2a301d4a557a62a3e2460286b08e97567b258c2212e4ad1cfa22bd6a57edcdc54ba76ebe946847450ab0999e6d48ccae332 languageName: node linkType: hard +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 10/04ee27901cde46c1c0a64b9584e04c96c5fe45b38c0d74930710751ea991408b405747d01dfae72f80fc158137018aea94f9c38c651cb9c318f0861a310c3679 + languageName: node + linkType: hard + "type-assertions@npm:^1.1.0": version: 1.1.0 resolution: "type-assertions@npm:1.1.0" @@ -16591,6 +19168,13 @@ __metadata: languageName: node linkType: hard +"typecast@npm:0.0.1": + version: 0.0.1 + resolution: "typecast@npm:0.0.1" + checksum: 10/83e30b5b9c4085977f8699b7b1a40d8cfbb2b107b4da04f64efb65d88d964cffe7a677f5247c5ef06b9cddd94381a940e3295b30bf59c517661683f39fd4989e + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-buffer@npm:1.0.3" @@ -16693,7 +19277,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.7.3": +"typescript@npm:^5.0.4, typescript@npm:^5.7.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -16723,7 +19307,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.7.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.0.4#optional!builtin, typescript@patch:typescript@npm%3A^5.7.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -16790,6 +19374,13 @@ __metadata: languageName: node linkType: hard +"ultron@npm:~1.1.0": + version: 1.1.1 + resolution: "ultron@npm:1.1.1" + checksum: 10/7cc6e8e98a2c62c87ab25a79a274f90492f13f5cf7c622dbda1ec85913e207aed392c26e76ed6250c4f05f842571b05dcce1f8ad0f5ecded64a99002b1fdf6e5 + languageName: node + linkType: hard + "unbox-primitive@npm:^1.1.0": version: 1.1.0 resolution: "unbox-primitive@npm:1.1.0" @@ -16984,6 +19575,16 @@ __metadata: languageName: node linkType: hard +"url@npm:^0.11.0": + version: 0.11.4 + resolution: "url@npm:0.11.4" + dependencies: + punycode: "npm:^1.4.1" + qs: "npm:^6.12.3" + checksum: 10/e787d070f0756518b982a4653ef6cdf4d9030d8691eee2d483344faf2b530b71d302287fa63b292299455fea5075c502a5ad5f920cb790e95605847f957a65e4 + languageName: node + linkType: hard + "utf-8-validate@npm:^5.0.2": version: 5.0.10 resolution: "utf-8-validate@npm:5.0.10" @@ -17008,6 +19609,22 @@ __metadata: languageName: node linkType: hard +"uuid@npm:2.0.1": + version: 2.0.1 + resolution: "uuid@npm:2.0.1" + checksum: 10/a5772e9231dd1e4fb111915f8ffe59f499bae7c20dfde09ac457a7a62b12abd6112d082496bdd209277cba1ac4e7a2bc83b8748ae0ca8fc74401b1df31f126e0 + languageName: node + linkType: hard + +"uuid@npm:^3.3.2": + version: 3.4.0 + resolution: "uuid@npm:3.4.0" + bin: + uuid: ./bin/uuid + checksum: 10/4f2b86432b04cc7c73a0dd1bcf11f1fc18349d65d2e4e32dd0fc658909329a1e0cc9244aa93f34c0cccfdd5ae1af60a149251a5f420ec3ac4223a3dab198fb2e + languageName: node + linkType: hard + "uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -17051,6 +19668,18 @@ __metadata: languageName: node linkType: hard +"valibot@npm:^0.37.0": + version: 0.37.0 + resolution: "valibot@npm:0.37.0" + peerDependencies: + typescript: ">=5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/c55132b60fa0e23b36327b263777065bfb5355dfb454393c39745659984950943ccbf5ff44010a28bcf4696200da29225af4515ac0e584e86554a8540628a579 + languageName: node + linkType: hard + "valibot@npm:^0.38.0": version: 0.38.0 resolution: "valibot@npm:0.38.0" @@ -17080,6 +19709,17 @@ __metadata: languageName: node linkType: hard +"validate@npm:^5.1.0": + version: 5.2.0 + resolution: "validate@npm:5.2.0" + dependencies: + "@eivifj/dot": "npm:^1.0.1" + component-type: "npm:1.2.1" + typecast: "npm:0.0.1" + checksum: 10/416998167ceb814a6f952febd662a635541aa3d8bff44d341240bc9d718172836a72730edeab3c512b5c6720b70ecdbb3a7fedf48f46287e294fd5fca772498d + languageName: node + linkType: hard + "validator@npm:13.15.20": version: 13.15.20 resolution: "validator@npm:13.15.20" @@ -17094,7 +19734,14 @@ __metadata: languageName: node linkType: hard -"varint@npm:^5.0.2": +"varint@npm:5.0.0": + version: 5.0.0 + resolution: "varint@npm:5.0.0" + checksum: 10/527c65ad87f1d140c03cf734d5c193430ef75fc21b7ec9d2b72f06ee19dbf686be70e0bee27674db3807cedb73ba13ce36a589427ebe52ac620de11686a74c1c + languageName: node + linkType: hard + +"varint@npm:^5.0.2, varint@npm:~5.0.0": version: 5.0.2 resolution: "varint@npm:5.0.2" checksum: 10/e1a66bf9a6cea96d1f13259170d4d41b845833acf3a9df990ea1e760d279bd70d5b1f4c002a50197efd2168a2fd43eb0b808444600fd4d23651e8d42fe90eb05 @@ -17133,6 +19780,17 @@ __metadata: languageName: node linkType: hard +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: "npm:^1.0.0" + core-util-is: "npm:1.0.2" + extsprintf: "npm:^1.2.0" + checksum: 10/da548149dd9c130a8a2587c9ee71ea30128d1526925707e2d01ed9c5c45c9e9f86733c66a328247cdd5f7c1516fb25b0f959ba754bfbe15072aa99ff96468a29 + languageName: node + linkType: hard + "viem@npm:^2.40.3": version: 2.40.3 resolution: "viem@npm:2.40.3" @@ -17257,6 +19915,20 @@ __metadata: languageName: node linkType: hard +"websocket-stream@npm:^5.5.0": + version: 5.5.2 + resolution: "websocket-stream@npm:5.5.2" + dependencies: + duplexify: "npm:^3.5.1" + inherits: "npm:^2.0.1" + readable-stream: "npm:^2.3.3" + safe-buffer: "npm:^5.1.2" + ws: "npm:^3.2.0" + xtend: "npm:^4.0.0" + checksum: 10/df207dac6c25d84fa7b4923e096b71499f03895f0c56c9ddbd2086376da17a36a563e493acde68fe043f8628fb1fe4e068e9f155cd46338bb7ac6a3cc2574f7b + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -17388,6 +20060,15 @@ __metadata: languageName: node linkType: hard +"wif@npm:^5.0.0": + version: 5.0.0 + resolution: "wif@npm:5.0.0" + dependencies: + bs58check: "npm:^4.0.0" + checksum: 10/3af0d4e9f1d1b35672b860470b6545f34f77b4a804e06ccae2f06f43c96a31fba859cf64889344eded621433eb609fc4c95aa28d62c02057372232b85231e414 + languageName: node + linkType: hard + "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" @@ -17442,6 +20123,16 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^4.0.2": + version: 4.0.2 + resolution: "write-file-atomic@npm:4.0.2" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^3.0.7" + checksum: 10/3be1f5508a46c190619d5386b1ac8f3af3dbe951ed0f7b0b4a0961eed6fc626bd84b50cf4be768dabc0a05b672f5d0c5ee7f42daa557b14415d18c3a13c7d246 + languageName: node + linkType: hard + "write-file-atomic@npm:^5.0.0, write-file-atomic@npm:^5.0.1": version: 5.0.1 resolution: "write-file-atomic@npm:5.0.1" @@ -17527,6 +20218,17 @@ __metadata: languageName: node linkType: hard +"ws@npm:^3.2.0": + version: 3.3.3 + resolution: "ws@npm:3.3.3" + dependencies: + async-limiter: "npm:~1.0.0" + safe-buffer: "npm:~5.1.0" + ultron: "npm:~1.1.0" + checksum: 10/4b4a7e5d11025e399d82a7471bfb4818d563c892f5d953c2de937d262bd8e8acc8b340220001c01f8392574fccbc2df153d6031e285b8b38441187ea0c2cfd72 + languageName: node + linkType: hard + "ws@npm:^7, ws@npm:^7.5.10": version: 7.5.10 resolution: "ws@npm:7.5.10" @@ -17542,6 +20244,13 @@ __metadata: languageName: node linkType: hard +"xmlhttprequest@npm:1.8.0": + version: 1.8.0 + resolution: "xmlhttprequest@npm:1.8.0" + checksum: 10/4f2cc2029f863d425ba8d6ef717de7ee44cd44ceae97df45c122343ecbcd4418559fbb8bdc3fa3678ea8cb24fb31a143ed0e8f7bb302c13185bc4486d81d8399 + languageName: node + linkType: hard + "xstream@npm:^11.14.0": version: 11.14.0 resolution: "xstream@npm:11.14.0" @@ -17552,7 +20261,7 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.2": +"xtend@npm:^4.0.0, xtend@npm:^4.0.2, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: 10/ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a @@ -17650,7 +20359,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.7.2": +"yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -17692,3 +20401,10 @@ __metadata: checksum: 10/b2144b38807673a4254dae06fe1a212729550609e606289c305e45c585b36fab1dbba44fe6cde90db9b28be465ec63f4c2a50867aeec6672f6bc36b6c9a361a0 languageName: node linkType: hard + +"zod@npm:^3.21.4": + version: 3.25.76 + resolution: "zod@npm:3.25.76" + checksum: 10/f0c963ec40cd96858451d1690404d603d36507c1fc9682f2dae59ab38b578687d542708a7fdbf645f77926f78c9ed558f57c3d3aa226c285f798df0c4da16995 + languageName: node + linkType: hard