diff --git a/app/wallet/overview-tab.tsx b/app/wallet/overview-tab.tsx index be2d399..f7a45f0 100644 --- a/app/wallet/overview-tab.tsx +++ b/app/wallet/overview-tab.tsx @@ -22,6 +22,7 @@ import { fetchAddressDetails, fetchTransaction, getAddress } from '@/lib/ledger' import { delay } from '@/lib/util'; import styles from './overview-tab.module.css'; +import { sompiToKas } from '@/lib/kaspa-util'; export default function OverviewTab(props) { const groupRef = useRef(null); @@ -127,7 +128,7 @@ export default function OverviewTab(props) { selectedAddress.derivationPath, ); - selectedAddress.balance = addressDetails.balance / 100000000; + selectedAddress.balance = sompiToKas(addressDetails.balance); selectedAddress.utxos = addressDetails.utxos; selectedAddress.newTransactions++; // selectedAddress.txCount = addressDetails.txCount; diff --git a/app/wallet/page.tsx b/app/wallet/page.tsx index 0c32aa3..ce242e5 100644 --- a/app/wallet/page.tsx +++ b/app/wallet/page.tsx @@ -18,6 +18,7 @@ import { delay } from '@/lib/util'; import { useElementSize } from '@mantine/hooks'; import { notifications } from '@mantine/notifications'; import SettingsStore from '@/lib/settings-store'; +import { kasToSompi, sompiToKas, NETWORK_UTXO_LIMIT } from '@/lib/kaspa-util'; let loadingAddressBatch = false; let addressInitialized = false; @@ -36,7 +37,7 @@ function loadAddressDetails(rawAddress) { const fetchAddressPromise = fetchAddressDetails(rawAddress.address, rawAddress.derivationPath); return fetchAddressPromise.then((addressDetails) => { - rawAddress.balance = addressDetails.balance / 100000000; + rawAddress.balance = sompiToKas(addressDetails.balance); rawAddress.utxos = addressDetails.utxos; // rawAddress.txCount = addressDetails.txCount; rawAddress.loading = false; @@ -111,15 +112,30 @@ async function demoLoadAddress(bip32, setAddresses, setRawAddresses, lastReceive const demoAddresses = []; for (let i = 0; i <= lastReceiveIndex; i++) { + const balance = Math.round(Math.random() * 10000); const currAddress = { key: i, address: bip32.getAddress(0, i), - balance: Math.round(Math.random() * 10000), + balance, derivationPath: `44'/111111'/0'/0/${i}`, utxos: [], loading: true, }; + currAddress.utxos.push({ + prevTxId: 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + outpointIndex: 0, + amount: kasToSompi(balance - (NETWORK_UTXO_LIMIT - 1)), + }); + + for (let j = 0; j < NETWORK_UTXO_LIMIT - 1; j++) { + currAddress.utxos.push({ + prevTxId: 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + outpointIndex: 0, + amount: kasToSompi(1), + }); + } + demoAddresses.push(currAddress); setRawAddresses([...demoAddresses]); diff --git a/components/send-form.tsx b/components/send-form.tsx index 4fcef81..5ddb919 100644 --- a/components/send-form.tsx +++ b/components/send-form.tsx @@ -20,7 +20,7 @@ import { useState, useEffect } from 'react'; import { createTransaction, sendAmount, selectUtxos } from '@/lib/ledger'; import AddressText from '@/components/address-text'; import { useForm } from '@mantine/form'; -import { kasToSompi, sompiToKas } from '@/lib/kaspa-util'; +import { kasToSompi, sompiToKas, NETWORK_UTXO_LIMIT } from '@/lib/kaspa-util'; export default function SendForm(props) { const [confirming, setConfirming] = useState(false); @@ -146,44 +146,50 @@ export default function SendForm(props) { if (amount && sendTo) { let calculatedFee: string | number = '-'; - if (deviceType === 'demo') { - calculatedFee = - fee === '-' ? sompiToKas(Math.round(Math.random() * 10000)) : Number(fee); - setCanSendAmount(Number(amount) <= props.addressContext.balance - calculatedFee); - if (includeFeeInAmount) { - const afterFeeDisplay = sompiToKas(kasToSompi(amount) - calculatedFee); - setAmountDescription(`Amount after fee: ${afterFeeDisplay}`); + + const { + hasEnough, + utxos, + fee: feeCalcResult, + total: utxoTotalAmount, + } = selectUtxos(kasToSompi(amount), props.addressContext.utxos, includeFeeInAmount); + + if (utxos.length > NETWORK_UTXO_LIMIT) { + const maxCompoundableAmount = sompiToKas( + utxos.slice(0, NETWORK_UTXO_LIMIT).reduce((acc, utxo) => { + return acc + utxo.amount; + }, 0), + ); + notifications.show({ + title: 'Error', + color: 'red', + message: `You have too many UTXOs to send this amount. Please compound first by sending KAS to your address. Maximum sendable without compounding (including fee): ${maxCompoundableAmount}`, + autoClose: false, + loading: false, + }); + setCanSendAmount(false); + } else if (hasEnough) { + let changeAmount = utxoTotalAmount - kasToSompi(amount); + if (!includeFeeInAmount) { + changeAmount -= feeCalcResult; } - } else if (deviceType === 'usb') { - const { - hasEnough, - fee: feeCalcResult, - total: utxoTotalAmount, - } = selectUtxos(kasToSompi(amount), props.addressContext.utxos, includeFeeInAmount); - - if (hasEnough) { - let changeAmount = utxoTotalAmount - kasToSompi(amount); - if (!includeFeeInAmount) { - changeAmount -= feeCalcResult; - } - let expectedFee = feeCalcResult; - // The change is added to the fee if it's less than 0.0001 KAS - console.info('changeAmount', changeAmount); - if (changeAmount < 10000) { - console.info(`Adding dust change ${changeAmount} sompi to fee`); - expectedFee += changeAmount; - } + let expectedFee = feeCalcResult; + // The change is added to the fee if it's less than 0.0001 KAS + console.info('changeAmount', changeAmount); + if (changeAmount < 10000) { + console.info(`Adding dust change ${changeAmount} sompi to fee`); + expectedFee += changeAmount; + } - calculatedFee = sompiToKas(expectedFee); - const afterFeeDisplay = sompiToKas(kasToSompi(amount) - expectedFee); - setCanSendAmount(true); - if (includeFeeInAmount) { - setAmountDescription(`Amount after fee: ${afterFeeDisplay}`); - } - } else { - setCanSendAmount(false); + calculatedFee = sompiToKas(expectedFee); + const afterFeeDisplay = sompiToKas(kasToSompi(amount) - expectedFee); + setCanSendAmount(true); + if (includeFeeInAmount) { + setAmountDescription(`Amount after fee: ${afterFeeDisplay}`); } + } else { + setCanSendAmount(false); } if (fee === '-' || fee !== calculatedFee) { diff --git a/lib/kaspa-util.ts b/lib/kaspa-util.ts index 8ac734d..30b2636 100644 --- a/lib/kaspa-util.ts +++ b/lib/kaspa-util.ts @@ -181,3 +181,5 @@ export function kasToSompi(amount: number) { throw new Error('Invalid amount'); } } + +export const NETWORK_UTXO_LIMIT = 84;