add evm send with native and erc-20 token support

This commit is contained in:
2026-05-29 00:33:56 +08:00
parent 9e579f957d
commit 44a91428fc
7 changed files with 285 additions and 9 deletions
+2 -1
View File
@@ -16,7 +16,8 @@
"build:native": "swiftc -O -o src/bun/native/biometric-helper src/bun/native/biometric-helper.swift" "build:native": "swiftc -O -o src/bun/native/biometric-helper src/bun/native/biometric-helper.swift"
}, },
"dependencies": { "dependencies": {
"@alchemy/wallet-apis": "^5.0.0-beta.28", "@alchemy/common": "^5.0.0",
"@alchemy/wallet-apis": "^5.0.0",
"@koins/utils": "workspace:*", "@koins/utils": "workspace:*",
"drizzle-orm": "^1.0.0-beta.22", "drizzle-orm": "^1.0.0-beta.22",
"electrobun": "^1.18.1", "electrobun": "^1.18.1",
+64 -1
View File
@@ -10,7 +10,11 @@ import { syncTransactionHistory } from '../../lib/sync'
import { eq, and } from 'drizzle-orm' import { eq, and } from 'drizzle-orm'
import { db } from '../../lib/db' import { db } from '../../lib/db'
import { getClient } from '../../lib/viem' import { getClient } from '../../lib/viem'
import { formatGwei } from 'viem' import { formatGwei, parseEther, parseUnits, encodeFunctionData, createWalletClient } from 'viem'
import { mnemonicToAccount } from 'viem/accounts'
import { getChain } from '../../lib/viem/getChain'
import { getChainID } from '../../lib/alchemy/network'
import { alchemyTransport } from '@alchemy/common'
import { sqlite } from '../../lib/sqlite' import { sqlite } from '../../lib/sqlite'
import { txHistory, evmWallets } from '../../lib/db/schema' import { txHistory, evmWallets } from '../../lib/db/schema'
import type { import type {
@@ -566,5 +570,64 @@ export function createEvmHandlers(
db.delete(evmWallets).where(eq(evmWallets.id, id)).run() db.delete(evmWallets).where(eq(evmWallets.id, id)).run()
console.log('[rpc] evmDeleteWallet complete') console.log('[rpc] evmDeleteWallet complete')
}, },
evmSendTransfer: async ({
seed,
to,
amount,
chainid,
contractAddress,
tokenDecimals,
}: {
seed: string
to: string
amount: string
chainid: string
contractAddress?: string
tokenDecimals?: number
}) => {
const key = await Bun.secrets.get({
service: 'koins',
name: 'alchemy_key',
})
if (!key) throw new Error('API key not set')
const account = mnemonicToAccount(seed)
const id = getChainID(chainid)
if (!id) throw new Error('Unsupported chain')
const chain = getChain(id)
const walletClient = createWalletClient({
account,
chain,
transport: alchemyTransport({ apiKey: key }),
})
if (contractAddress && tokenDecimals !== undefined) {
const erc20Abi = [
{
name: 'transfer',
type: 'function',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ name: '', type: 'bool' }],
},
] as const
const data = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [to as `0x${string}`, parseUnits(amount, tokenDecimals)],
})
const hash = await walletClient.sendTransaction({
to: contractAddress as `0x${string}`,
data,
value: 0n,
})
return hash
}
const hash = await walletClient.sendTransaction({
to: to as `0x${string}`,
value: parseEther(amount),
})
return hash
},
} }
} }
@@ -487,6 +487,11 @@
{/if} {/if}
<div class="flex gap-2 flex-wrap"> <div class="flex gap-2 flex-wrap">
<Button
variant="outline"
onclick={() => navigate('/multicoin/send')}>
Send
</Button>
<Button <Button
variant="outline" variant="outline"
onclick={() => navigate('/settings')}> onclick={() => navigate('/settings')}>
+2
View File
@@ -117,6 +117,7 @@ export type TransactionDetails = {
export type TokenBalanceResult = { export type TokenBalanceResult = {
symbol: string symbol: string
decimals: number
balance: string balance: string
contractAddress: string contractAddress: string
logo?: string logo?: string
@@ -163,6 +164,7 @@ export type EvmRpcRequests = {
evmListWallets: { params: {}; response: EvmWalletInfo[] } evmListWallets: { params: {}; response: EvmWalletInfo[] }
evmGetSeed: { params: { vaultKey: string }; response: string } evmGetSeed: { params: { vaultKey: string }; response: string }
evmDeleteWallet: { params: { id: string }; response: void } evmDeleteWallet: { params: { id: string }; response: void }
evmSendTransfer: { params: { seed: string; to: string; amount: string; chainid: string; contractAddress?: string; tokenDecimals?: number }; response: string }
} }
export type MoneroRpcRequests = { export type MoneroRpcRequests = {
@@ -39,6 +39,7 @@ export type NetworkId = (typeof networks)[number]['id']
export type TokenBalance = { export type TokenBalance = {
symbol: string symbol: string
decimals: number
balance: string balance: string
contractAddress: string contractAddress: string
logo?: string logo?: string
@@ -406,6 +407,25 @@ export const EvmWallet = () => {
transactions = txs ?? [] transactions = txs ?? []
} }
const send = async (
to: string,
amount: string,
contractAddress?: string,
tokenDecimals?: number,
) => {
if (!electrobun.rpc || !seed) throw new Error('Wallet not unlocked')
const hash = await electrobun.rpc.request.evmSendTransfer({
seed,
to,
amount,
chainid: net().chainid,
contractAddress,
tokenDecimals,
})
await refresh()
return hash
}
const lock = () => { const lock = () => {
clearWallet() clearWallet()
} }
@@ -531,6 +551,7 @@ export const EvmWallet = () => {
unlockWallet, unlockWallet,
deleteWallet, deleteWallet,
clearSelection, clearSelection,
send,
syncTxHistory, syncTxHistory,
setAutoSync, setAutoSync,
flushTxCache, flushTxCache,
@@ -0,0 +1,183 @@
<script lang="ts">
import { evmWallet as wallet } from '$lib/states/evm-wallet.svelte.js'
import { Button } from '$lib/components/ui/button/index.js'
import { Input } from '$lib/components/ui/input/index.js'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from '$lib/components/ui/select/index.js'
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription,
} from '$lib/components/ui/card/index.js'
import { navigate } from 'sv-router/generated'
const w = wallet
type TokenOption = {
symbol: string
balance: string
contractAddress: string
decimals: number
logo?: string
}
const tokens = $derived<TokenOption[]>([
{
symbol: w.symbol,
balance: w.balance,
contractAddress: '',
decimals: 18,
},
...w.tokenBalances.map((t) => ({
symbol: t.symbol,
balance: t.balance,
contractAddress: t.contractAddress,
decimals: t.decimals,
logo: t.logo,
})),
])
let selectedSymbol = $state(w.symbol)
let recipient = $state('')
let amount = $state('')
let sending = $state(false)
let sentResult = $state<{ txHash: string } | null>(null)
let sendError = $state('')
const selectedToken = $derived(
tokens.find((t) => t.symbol === selectedSymbol) ?? tokens[0],
)
const exceedsBalance = $derived(
amount !== '' &&
selectedToken.balance !== '0' &&
parseFloat(amount) > parseFloat(selectedToken.balance),
)
const canSend = $derived(
recipient.length === 42 &&
recipient.startsWith('0x') &&
amount !== '' &&
parseFloat(amount) > 0 &&
!exceedsBalance &&
!sending,
)
const handleSend = async () => {
sending = true
sendError = ''
sentResult = null
try {
const txHash = await w.send(
recipient,
amount,
selectedToken.contractAddress || undefined,
selectedToken.decimals,
)
sentResult = { txHash }
recipient = ''
amount = ''
} catch (e) {
sendError = e instanceof Error ? e.message : 'Send failed'
} finally {
sending = false
}
}
</script>
<div class="mx-auto mt-16 max-w-md">
<Button
variant="outline"
size="sm"
onclick={() => navigate(w.network === 'eth' ? '/multicoin' : `/multicoin/${w.network}`)}
class="mb-4">
&larr; Back
</Button>
<Card>
<CardHeader>
<CardTitle>Send</CardTitle>
<CardDescription>
Balance: {selectedToken.balance} {selectedToken.symbol}
</CardDescription>
</CardHeader>
<CardContent>
<form
onsubmit={(e) => {
e.preventDefault()
handleSend()
}}
class="space-y-4">
<div class="space-y-1.5">
<label class="text-xs font-medium">Asset</label>
<Select type="single" bind:value={selectedSymbol}>
<SelectTrigger>
{selectedToken.symbol}
</SelectTrigger>
<SelectContent>
{#each tokens as t}
<SelectItem value={t.symbol}>
{t.symbol} ({t.balance})
</SelectItem>
{/each}
</SelectContent>
</Select>
</div>
<div class="space-y-1.5">
<label class="text-xs font-medium">Recipient Address</label>
<Input
placeholder="0x..."
bind:value={recipient} />
</div>
<div class="space-y-1.5">
<label class="text-xs font-medium">Amount ({selectedToken.symbol})</label>
<Input
type="number"
step="0.000000000000000001"
min="0"
placeholder="0.0"
bind:value={amount} />
{#if exceedsBalance}
<p class="text-xs text-red-500">
Amount exceeds balance
</p>
{/if}
</div>
<Button type="submit" disabled={!canSend} class="w-full">
{sending ? 'Sending...' : 'Send'}
</Button>
</form>
{#if sendError}
<p class="mt-3 text-xs text-red-500">{sendError}</p>
{/if}
{#if sentResult}
<div
class="mt-4 space-y-2 rounded-md border border-green-500/30 bg-green-500/5 p-3">
<p class="text-xs font-medium text-green-600">
Transaction Sent
</p>
<p class="font-mono text-xs break-all">
<a
href="{w.explorerUrl}{sentResult.txHash}"
target="_blank"
rel="noopener noreferrer"
class="underline hover:text-green-500">
{sentResult.txHash}
</a>
</p>
</div>
{/if}
</CardContent>
</Card>
</div>
+8 -7
View File
@@ -13,7 +13,8 @@
"apps/desktop": { "apps/desktop": {
"name": "koins", "name": "koins",
"dependencies": { "dependencies": {
"@alchemy/wallet-apis": "^5.0.0-beta.28", "@alchemy/common": "^5.0.0",
"@alchemy/wallet-apis": "^5.0.0",
"@koins/utils": "workspace:*", "@koins/utils": "workspace:*",
"drizzle-orm": "^1.0.0-beta.22", "drizzle-orm": "^1.0.0-beta.22",
"electrobun": "^1.18.1", "electrobun": "^1.18.1",
@@ -72,11 +73,11 @@
"packages": { "packages": {
"@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], "@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="],
"@alchemy/common": ["@alchemy/common@5.0.0-beta.28", "", { "dependencies": { "zod": "^3.23.0" }, "peerDependencies": { "viem": "^2.45.0" } }, "sha512-VyqYyfYPuRTXpu8X+fFIL2JU9iS4ZnOl9wXmDS0s/3H3Y5wYJzz/dZ1Vo7u33B9L+aWPkSMGHVJun1AZdNEacA=="], "@alchemy/common": ["@alchemy/common@5.0.2", "", { "dependencies": { "zod": "^3.23.0" }, "peerDependencies": { "viem": "^2.45.0" } }, "sha512-ORFjRhAJ07BUjhNqjgx2b4khWu0sNNEnnTXj3DT7UKFxZvK79cpyoE/6E0D17OWiz40y+uk6eRtkWog5bNWVpg=="],
"@alchemy/wallet-api-types": ["@alchemy/wallet-api-types@0.1.0-alpha.27", "", { "dependencies": { "@alchemy/common": "5.0.0-beta.4", "deep-equal": "^2.2.3", "ox": "^0.6.12", "typebox": "^1.0.81", "viem": "^2.45.0" }, "peerDependencies": { "typescript": "^5.8.2" } }, "sha512-d6DaAASwa3k7vpXEbG96hUiV+41CBGfTRNiXsjG5UgX4tox3Z0Dlf9QC+SBkyzc1/PiJx9fCuQxYNfrELqPxkw=="], "@alchemy/wallet-api-types": ["@alchemy/wallet-api-types@0.2.0-alpha.3", "", { "dependencies": { "@alchemy/common": "5.0.0-beta.32", "deep-equal": "^2.2.3", "ox": "^0.6.12", "viem": "^2.45.0", "zod": "^4.4.1" }, "peerDependencies": { "typescript": "^5.8.2" } }, "sha512-VaNj8gzGfPveo8i6l3aX4YovQezu4oDuAqyAy2k1LqsLf18pn2+iw3cV/WiFTwMrdQXLtnDYzS9D6NdkgEokzw=="],
"@alchemy/wallet-apis": ["@alchemy/wallet-apis@5.0.0-beta.28", "", { "dependencies": { "@alchemy/common": "5.0.0-beta.28", "@alchemy/wallet-api-types": "0.1.0-alpha.27", "deep-equal": "^2.2.3", "ox": "^0.11.1", "typebox": "1.0.81" }, "peerDependencies": { "viem": "^2.45.0" } }, "sha512-ySr+ASorGI7bxPZ1fPH/CpszyXEVbUBLEfqWPzLmQiC5gvC186HppBFYPUrVUfVDU7izSNrkX7r64e99ZcppgA=="], "@alchemy/wallet-apis": ["@alchemy/wallet-apis@5.0.2", "", { "dependencies": { "@alchemy/common": "5.0.2", "@alchemy/wallet-api-types": "0.2.0-alpha.3", "deep-equal": "^2.2.3", "ox": "^0.11.1", "zod": "^4.4.1" }, "peerDependencies": { "@solana/kit": ">=6.0.0", "@solana/web3.js": "^1.95.0", "viem": "^2.45.0" }, "optionalPeers": ["@solana/kit", "@solana/web3.js"] }, "sha512-HmwOAvpe248LOQaJ3Der2owT82l0OwxBfVbwiXd7WXlJ+k04L1ooFKo5AjiWXMaoJQzpIkh37n66SUmyVVd4PA=="],
"@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="], "@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="],
@@ -1050,12 +1051,10 @@
"@alchemy/common/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@alchemy/common/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@alchemy/wallet-api-types/@alchemy/common": ["@alchemy/common@5.0.0-beta.4", "", { "peerDependencies": { "viem": "^2.45.0" } }, "sha512-A9IGGG6QA+UEtf+mFiuNfuruS0wTlSsXx2jIWXeeJ18VYMEFmPFFpPP2tWfGLKpxyuA1QQqkHSQqs6dBpG1EZw=="], "@alchemy/wallet-api-types/@alchemy/common": ["@alchemy/common@5.0.0-beta.32", "", { "dependencies": { "zod": "^3.23.0" }, "peerDependencies": { "viem": "^2.45.0" } }, "sha512-gVUrMhnhdD8UUipd70XUbmxm3PX3OhHK1TSi3ODBGOH3Jd9SoH0+mFagSn62pwi5XFPCOg3UGGXj9bmLKFlGhA=="],
"@alchemy/wallet-api-types/ox": ["ox@0.6.12", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-78hziRGLj0qTDa0UW4+ynv9tW2Cp1vmCfGokL8D7kiSDh6Y0LAfHL+HaDN4l2a9jcrOG3fexTDtLNtDNkEwLtg=="], "@alchemy/wallet-api-types/ox": ["ox@0.6.12", "", { "dependencies": { "@adraffy/ens-normalize": "^1.10.1", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0", "@scure/bip39": "^1.4.0", "abitype": "^1.0.6", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-78hziRGLj0qTDa0UW4+ynv9tW2Cp1vmCfGokL8D7kiSDh6Y0LAfHL+HaDN4l2a9jcrOG3fexTDtLNtDNkEwLtg=="],
"@alchemy/wallet-apis/typebox": ["typebox@1.0.81", "", {}, "sha512-bCslZUmZESHhBn4kHDghzH2oo3qu8m2W89xDLxQHv/aPvY4i81Nd1jvijlBp9wSpsVytDSfSoosbiBAjgsNb2Q=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
@@ -1118,6 +1117,8 @@
"vite/rolldown": ["rolldown@1.0.0-rc.18", "", { "dependencies": { "@oxc-project/types": "=0.128.0", "@rolldown/pluginutils": "1.0.0-rc.18" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-x64": "1.0.0-rc.18", "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg=="], "vite/rolldown": ["rolldown@1.0.0-rc.18", "", { "dependencies": { "@oxc-project/types": "=0.128.0", "@rolldown/pluginutils": "1.0.0-rc.18" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-x64": "1.0.0-rc.18", "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg=="],
"@alchemy/wallet-api-types/@alchemy/common/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@types/bun/bun-types/@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="], "@types/bun/bun-types/@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="],
"axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], "axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],