Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: amend fee not showing units on 0 #42

Merged
merged 2 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions src/pages/auth/CreateWallet/CreateWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,33 +97,39 @@ export const CreateWallet = () => {
/* ******************************************************************************************* */
/* Chris current TODO */
// TODO: show errors to user (user displayable values)
// TODO: provide updates to user as staking, claim, and unstaking information changes (successes, errors, and changing gas fees)
// TODO: provide updates to user as send information updates (errors and changing gas fees)
// TODO: provide updates to user as staking, claim, and unstaking information changes (errors)
// TODO: provide updates to user as send information updates (errors)
/* ******************************************************************************************* */

/* ******************************************************************************************* */
/* David Current TODOs */
// TODO: put Loader on loading screen, not "loading"
// TODO: amend fee showing as 0 rather than 0 MLD (send page)
// TODO: add fee display and updates for stake, unstake, and claim
// TODO: refresh on complete of query (call validator and wallet refresh hooks or make other hook that fully updates all information)
/* ******************************************************************************************* */

/* Current TODOs */
// TODO: ensure logout after blur + timeout (blur is click outside application to close). to remove sensitive data after time period
// TODO: enable transaction fees for wallet transactions (revenue)
// TODO: abstract wallet prefix and mnemonic decryption
// TODO: modify accounts/wallets management for scalability (password per account, multiple wallets per account, multiple accounts in storage, search by password+decryption)

/* Nice to have TODOs */
// TODO: fix copytextfield issue of enlarged border on click
// TODO: clean up helper functions and hooks
// TODO: make "clear" and "max" button send screen inputs. make placement and appearance for these uniform (send and unstake sections)
// TODO: add search icon to search field, add onclick
// TODO: keep track of current page for case of re-open before timeout
// TODO: prevent re-building auth every time wallet updates
// TODO: ensure new encrypted mnemonic overwrites old in case of same password and name (but let user know first)
// TODO: test path and create error for no wallet exists and user attempts login
// TODO: ensure new encrypted mnemonic overwrites old in case of same password and name (but let user know first)
// TODO: handle error printout for create/import wallet (in place of subtitle on verify screen?)
// TODO: modify auth to accounts & wallets structure to make this scalable for later upgrades

/* Less Critical TODOs */
// TODO: amend fee showing as 0 rather than 0 MLD (send page)
// TODO: make "clear" and "max" button send screen inputs. make placement and appearance for these uniform (send and unstake sections)
// TODO: keep track of current page for case of re-open after timeout
// TODO: clean up helper functions and hooks
// TODO: prevent re-building auth every time wallet updates
// TODO: add search icon to search field (component), add onclick

/* Interchain-compatibility TODOs (mobile version before this) */
// TODO: add button to "add chain" at bottom of Holdings list
// TODO: abstract wallet prefix and mnemonic decryption
// TODO: add link to github repo for registry
// TODO: abstract IBC needs
// TODO: ensure pipeline to other registries is functional. change here auto-PRs there
Expand Down
10 changes: 7 additions & 3 deletions src/pages/main/Send/AddressInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Input } from '@/ui-kit';
import { useAtom, useSetAtom } from 'jotai';
import { addressVerifiedAtom, recipientAddressAtom } from '@/atoms';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { addressVerifiedAtom, recipientAddressAtom, walletStateAtom } from '@/atoms';
import { useEffect, useState } from 'react';
import { WALLET_PREFIX } from '@/constants';
import { cn } from '@/helpers';
Expand All @@ -12,12 +12,15 @@ interface AddressInputProps {
updateSendAsset: (asset: Asset, propagateChanges: boolean) => void;
}

// TODO: set placeholder as user's address.
// TODO: return validity
export const AddressInput: React.FC<AddressInputProps> = ({
addBottomMargin = true,
updateSendAsset,
}) => {
const [address, setAddress] = useAtom(recipientAddressAtom);
const setAddressVerified = useSetAtom(addressVerifiedAtom);
const walletState = useAtomValue(walletStateAtom);

const [addressStatus, setAddressStatus] = useState<'error' | 'success' | null>(null);
const [allowValidateAddress, setAllowValidatePassword] = useState(false);
Expand All @@ -37,6 +40,7 @@ export const AddressInput: React.FC<AddressInputProps> = ({

const isAddressValid = hasPrefix && isValidLength && isAlphanumeric;
setAddressStatus(isAddressValid ? 'success' : 'error');
setAddressVerified(isAddressValid); // Set the verified state for address validity check
};

const handleAddressChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -95,7 +99,7 @@ export const AddressInput: React.FC<AddressInputProps> = ({
status={addressStatus}
showMessageText={true}
messageText={addressStatus === 'error' ? 'Address not in supported format' : ''}
placeholder="Wallet Address or ICNS"
placeholder={walletState.address || 'Wallet Address or ICNS'}
icon={<QRCodeScannerDialog updateSendAsset={updateSendAsset} />}
value={address}
onChange={handleAddressChange}
Expand Down
96 changes: 73 additions & 23 deletions src/pages/main/Send/Send.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import {
sendStateAtom,
walletStateAtom,
selectedAssetAtom,
addressVerifiedAtom,
} from '@/atoms';
import { Asset, TransactionResult, TransactionSuccess } from '@/types';
import { AssetInput, WalletSuccessScreen } from '@/components';
import {
cn,
// cn,
formatBalanceDisplay,
removeTrailingZeroes,
sendTransaction,
Expand All @@ -36,6 +37,7 @@ export const Send = () => {
const [callbackChangeMap, setCallbackChangeMap] = useAtom(callbackChangeMapAtom);
const [isLoading, setLoading] = useAtom(loadingAtom);
const [recipientAddress, setRecipientAddress] = useAtom(recipientAddressAtom);
const addressVerified = useAtomValue(addressVerifiedAtom);
const [selectedAsset, setSelectedAsset] = useAtom(selectedAssetAtom);

// TODO: only query for exchange rate on transaction type swap
Expand All @@ -48,63 +50,101 @@ export const Send = () => {
});
const [simulatedFee, setSimulatedFee] = useState<{
fee: string;
textClass: 'text-error' | 'text-warn' | '';
} | null>({ fee: '0', textClass: '' });
textClass: 'text-error' | 'text-warn' | 'text-blue';
} | null>({ fee: '0 MLD', textClass: 'text-blue' });
const [sendPlaceholder, setSendPlaceholder] = useState<string>('');
const [receivePlaceholder, setReceivePlaceholder] = useState<string>('');
const [transactionMessage, setTransactionMessage] = useState<string>('');
// const [transactionMessage, setTransactionMessage] = useState<string>('');
const [isSuccess, setIsSuccess] = useState<TransactionSuccess>({ success: false });

const handleTransaction = async ({ simulateTransaction = false } = {}) => {
console.log('Starting handleTransaction function');
console.log('transactionType.isValid:', transactionType.isValid);

if (!transactionType.isValid) return;
// TODO: simulate against user's own address (build one if none exists)
if (!recipientAddress) return;

let currentRecipientAddress = '';
if (!addressVerified || !recipientAddress) {
currentRecipientAddress = walletState.address;
} else {
currentRecipientAddress = recipientAddress;
}

console.log('Recipient address:', currentRecipientAddress);
if (!currentRecipientAddress) return;

const sendAsset = sendState.asset;
const sendAmount = sendState.amount;
const receiveAsset = receiveState.asset;

console.log('sendAsset:', sendAsset);
console.log('receiveAsset:', receiveAsset);

if (!sendAsset || !receiveAsset) return;

const assetToSend = walletAssets.find(a => a.denom === sendAsset.denom);
console.log('assetToSend:', assetToSend);
if (!assetToSend) return;

const adjustedAmount = (
sendAmount * Math.pow(10, assetToSend.exponent || GREATER_EXPONENT_DEFAULT)
).toFixed(0); // No decimals, minor unit

console.log('Adjusted amount:', adjustedAmount);

const sendObject = {
recipientAddress,
recipientAddress: currentRecipientAddress,
amount: adjustedAmount,
denom: sendAsset.denom,
};

if (!simulateTransaction) setLoading(true);
console.log('sendObject:', sendObject);

if (!simulateTransaction) {
console.log('Setting loading state');
setLoading(true);
}

try {
let result: TransactionResult;
// Routing logic based on transactionType
console.log('transaction type', transactionType.isSwap);

if (!transactionType.isSwap) {
console.log('Executing sendTransaction');
result = await sendTransaction(walletState.address, sendObject, simulateTransaction);
console.log('sendTransaction result:', result);
} else if (transactionType.isSwap) {
const swapObject = { sendObject, resultDenom: receiveAsset.denom };
console.log('Executing swapTransaction with swapObject:', swapObject);
result = await swapTransaction(walletState.address, swapObject, simulateTransaction);
console.log('swapTransaction result:', result);
} else {
throw new Error('Invalid transaction type');
}

console.log('Result data:', simulateTransaction, result?.data?.code);

// Process result for simulation or actual transaction
if (simulateTransaction && result?.data?.code === 0) {
console.log('Simulation successful');
return result;
} else if (result.success && result.data?.code === 0) {
console.log('Transaction successful:', result.data.txHash);
setIsSuccess({ success: true, txHash: result.data.txHash });
} else {
console.error('Transaction failed', result.data);
console.error('Transaction failed:', result.data);
}
} catch (error) {
console.error('Error in transaction handling', error);
console.error('Error in transaction handling:', error);
} finally {
if (!simulateTransaction) setLoading(false);
if (!simulateTransaction) {
console.log('Resetting loading state');
setLoading(false);
}
}

console.log('Ending handleTransaction function');
// TODO: also put refetch after the stake and unstake functions
// TODO: re-apply refetch if helpers are changed into hooks
// refetch();
Expand All @@ -116,6 +156,11 @@ export const Send = () => {
if (!walletAsset) return 0;

const maxAmount = parseFloat(walletAsset.amount || '0');
console.log('simulated fee', simulatedFee ? parseFloat(simulatedFee.fee) : 0);
console.log(
'alternative representation',
simulatedFee ? parseFloat(simulatedFee.fee.split(' ')[0]) : 0,
);
const feeAmount = simulatedFee ? parseFloat(simulatedFee.fee) : 0;

const maxAvailable = Math.max(0, maxAmount - feeAmount);
Expand Down Expand Up @@ -224,10 +269,13 @@ export const Send = () => {

const updateFee = async () => {
const simulationResponse = await handleTransaction({ simulateTransaction: true });
console.log('simulationResponse', simulationResponse, simulationResponse?.data);

if (simulationResponse && simulationResponse.data) {
const gasWanted = parseInt(simulationResponse.data.gasWanted || '0', 10);
console.log('gas wanted', simulationResponse.data.gasWanted, gasWanted);

// TODO; get default gas price from chain registry
const defaultGasPrice = 0.025;
const exponent = sendState.asset?.exponent || GREATER_EXPONENT_DEFAULT;
const symbol = sendState.asset.symbol || DEFAULT_ASSET.symbol || 'MLD';
Expand All @@ -238,7 +286,8 @@ export const Send = () => {

setSimulatedFee({
fee: formatBalanceDisplay(feeInGreaterUnit.toFixed(exponent), symbol),
textClass: feePercentage > 1 ? 'text-error' : feePercentage > 0.75 ? 'text-warn' : '',
textClass:
feePercentage > 1 ? 'text-error' : feePercentage > 0.75 ? 'text-warn' : 'text-blue',
});
} else {
// TODO: handle error on fee return
Expand All @@ -259,17 +308,17 @@ export const Send = () => {

if (sendAsset.denom === receiveAsset.denom) {
newTransactionType.isSwap = false;
setTransactionMessage(`Sending ${sendAsset.symbol} through Symphony.`);
// setTransactionMessage(`Sending ${sendAsset.symbol} through Symphony.`);
} else if (!sendAsset.isIbc && !receiveAsset.isIbc && sendAsset.denom !== receiveAsset.denom) {
newTransactionType.isSwap = true;
setTransactionMessage(
newTransactionType.isValid
? `Sending ${sendAsset.symbol} into ${receiveAsset.symbol} on Symphony.`
: `No exchange on current pair`,
);
// setTransactionMessage(
// newTransactionType.isValid
// ? `Sending ${sendAsset.symbol} into ${receiveAsset.symbol} on Symphony.`
// : `No exchange on current pair`,
// );
} else {
newTransactionType.isValid = false;
setTransactionMessage('Not yet supported');
// setTransactionMessage('Not yet supported');
}

setTransactionType(newTransactionType);
Expand Down Expand Up @@ -442,9 +491,10 @@ export const Send = () => {
</NavLink>
<div>
<h1 className="text-h5 text-white font-bold">Send</h1>
<div className={cn(`${transactionType.isValid ? 'text-neutral-1' : 'text-error'}`)}>
{/* TODO: remove if remains unused. potential spot for info and error messages */}
{/* <div className={cn(`${transactionType.isValid ? 'text-neutral-1' : 'text-error'}`)}>
{transactionMessage}
</div>
</div> */}
</div>
<div className="max-w-5 w-full max-h-5" />
</div>
Expand Down Expand Up @@ -489,10 +539,10 @@ export const Send = () => {
<div className="flex flex-grow" />

{/* Fee Section */}
<div className="flex justify-between items-center text-blue text-sm font-bold">
<div className={`flex justify-between items-center text-sm text-blue font-bold`}>
<p>Fee</p>
<p className={simulatedFee?.textClass}>
{simulatedFee ? simulatedFee.fee : 'Unknown...'}
{simulatedFee && sendState.amount !== 0 ? simulatedFee?.fee : '-'}
</p>
</div>

Expand Down