diff --git a/cms/src/components/wallet/BitcoinWallet.vue b/cms/src/components/wallet/BitcoinWallet.vue index c1a9dd5..40cb123 100644 --- a/cms/src/components/wallet/BitcoinWallet.vue +++ b/cms/src/components/wallet/BitcoinWallet.vue @@ -7,7 +7,7 @@ - + + + + + + + + + diff --git a/cms/src/stores/bitcoinWallet.ts b/cms/src/stores/bitcoinWallet.ts index 011c7f8..a83a4fd 100644 --- a/cms/src/stores/bitcoinWallet.ts +++ b/cms/src/stores/bitcoinWallet.ts @@ -1,7 +1,11 @@ import { defineStore } from 'pinia'; import { ref } from 'vue'; import { api } from '@/plugins/axios'; +import type { BitcoinWalletRevealSeedPayload } from '@/types/bitcoinWallet/BitcoinWalletRevealSeedPayload'; +import type { BitcoinWalletRevealSeedResult } from '@/types/bitcoinWallet/BitcoinWalletRevealSeedResult'; import type { BitcoinWalletStatus } from '@/types/bitcoinWallet/BitcoinWalletStatus'; +import type { BitcoinWalletWithdrawPayload } from '@/types/bitcoinWallet/BitcoinWalletWithdrawPayload'; +import type { BitcoinWalletWithdrawResult } from '@/types/bitcoinWallet/BitcoinWalletWithdrawResult'; export const useBitcoinWalletStore = defineStore('bitcoinWallet', () => { const status = ref(null); @@ -14,8 +18,22 @@ export const useBitcoinWalletStore = defineStore('bitcoinWallet', () => { return data; }; + const withdrawAll = async (payload: BitcoinWalletWithdrawPayload): Promise => { + const { data } = await api.post('/bitcoin-wallet/withdraw', payload); + + return data; + }; + + const revealSeed = async (payload: BitcoinWalletRevealSeedPayload): Promise => { + const { data } = await api.post('/bitcoin-wallet/reveal-seed', payload); + + return data; + }; + return { status, - fetchStatus + fetchStatus, + withdrawAll, + revealSeed }; }); diff --git a/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedPayload.ts b/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedPayload.ts new file mode 100644 index 0000000..670cfef --- /dev/null +++ b/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedPayload.ts @@ -0,0 +1,3 @@ +export interface BitcoinWalletRevealSeedPayload { + password: string; +} diff --git a/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedResult.ts b/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedResult.ts new file mode 100644 index 0000000..6ba90ab --- /dev/null +++ b/cms/src/types/bitcoinWallet/BitcoinWalletRevealSeedResult.ts @@ -0,0 +1,3 @@ +export interface BitcoinWalletRevealSeedResult { + mnemonic: string; +} diff --git a/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawPayload.ts b/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawPayload.ts new file mode 100644 index 0000000..c45aa79 --- /dev/null +++ b/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawPayload.ts @@ -0,0 +1,5 @@ +export interface BitcoinWalletWithdrawPayload { + destinationAddress: string; + feeRateSatVbyte: number; + password: string; +} diff --git a/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawResult.ts b/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawResult.ts new file mode 100644 index 0000000..63d4b29 --- /dev/null +++ b/cms/src/types/bitcoinWallet/BitcoinWalletWithdrawResult.ts @@ -0,0 +1,4 @@ +export interface BitcoinWalletWithdrawResult { + txHash: string; + amountBtc: string; +} diff --git a/cms/src/utils/bitcoin/isBitcoinAddress.ts b/cms/src/utils/bitcoin/isBitcoinAddress.ts new file mode 100644 index 0000000..a1845c4 --- /dev/null +++ b/cms/src/utils/bitcoin/isBitcoinAddress.ts @@ -0,0 +1,28 @@ +import type { BitcoinNetwork } from '@/types/bitcoinWallet/BitcoinNetwork'; + +// Soft validation only: prefix/length checks to reject obvious garbage and wrong-network +// addresses early. Checksums and spendability are validated by Electrum on payto. +const BASE58 = '[1-9A-HJ-NP-Za-km-z]'; + +const NETWORK_ADDRESS_PATTERNS: Record = { + mainnet: [ + new RegExp(`^1${BASE58}{25,34}$`), + new RegExp(`^3${BASE58}{25,34}$`), + /^bc1[a-z0-9]{25,87}$/ + ], + testnet4: [ + new RegExp(`^[mn]${BASE58}{25,34}$`), + new RegExp(`^2${BASE58}{25,34}$`), + /^(?:tb1|bcrt1)[a-z0-9]{25,87}$/ + ] +}; + +export const isBitcoinAddress = (value: unknown, network: BitcoinNetwork): boolean => { + if (typeof value !== 'string') { + return false; + } + + const trimmed = value.trim(); + + return NETWORK_ADDRESS_PATTERNS[network].some(pattern => pattern.test(trimmed)); +};