Skip to content

Commit

Permalink
solana gas dropoff rent min to fresh wallets (#1082)
Browse files Browse the repository at this point in the history
Co-authored-by: Evan Gray <battledingo@gmail.com>
  • Loading branch information
kev1n-peters and evan-gray authored Oct 5, 2023
1 parent 5e49317 commit d4d0388
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 27 deletions.
13 changes: 12 additions & 1 deletion sdk/src/contexts/solana/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,18 @@ export class SolanaContext<
BigNumber.from(amount).toBigInt(),
decimals,
);
return BigNumber.from(nativeTokenAmount);
// an non-existent account cannot be sent less than the rent exempt amount
// in order to create the wallet, it must be sent at least the rent exemption minimum
const acctExists =
(await this.connection!.getAccountInfo(new PublicKey(walletAddress))) !==
null;
if (acctExists) return BigNumber.from(nativeTokenAmount);
const minBalance = await this.connection!.getMinimumBalanceForRentExemption(
0,
);
return nativeTokenAmount > minBalance
? BigNumber.from(nativeTokenAmount)
: BigNumber.from(0);
}

async calculateMaxSwapAmount(
Expand Down
33 changes: 32 additions & 1 deletion wormhole-connect/src/utils/routes/operator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PublicKey } from '@solana/web3.js';
import {
ChainId,
ChainName,
Expand All @@ -12,7 +13,14 @@ import { RelayRoute } from './relay';
// import { HashflowRoute } from './hashflow';
import { CCTPRelayRoute } from './cctpRelay';
import { CosmosGatewayRoute } from './cosmosGateway';
import { ParsedMessage, PayloadType, getMessage, isEvmChain, wh } from '../sdk';
import {
ParsedMessage,
PayloadType,
getMessage,
isEvmChain,
solanaContext,
wh,
} from '../sdk';
import { isCosmWasmChain } from '../cosmos';
import RouteAbstract from './routeAbstract';
import {
Expand Down Expand Up @@ -484,6 +492,29 @@ export class Operator {
return r.maxSwapAmount(destChain, token, walletAddress);
}

async minSwapAmountNative(
route: Route,
destChain: ChainName | ChainId,
token: TokenId,
walletAddress: string,
): Promise<BigNumber> {
const chainName = wh.toChainName(destChain);
if (chainName === 'solana') {
const context = solanaContext();
// an non-existent account cannot be sent less than the rent exempt amount
// in order to create the wallet, it must be sent at least the rent exemption minimum
const acctExists =
(await context.connection!.getAccountInfo(
new PublicKey(walletAddress),
)) !== null;
if (acctExists) return BigNumber.from(0);
const minBalance =
await context.connection!.getMinimumBalanceForRentExemption(0);
return BigNumber.from(minBalance);
}
return BigNumber.from(0);
}

tryFetchRedeemTx(
route: Route,
txData: UnsignedMessage,
Expand Down
79 changes: 54 additions & 25 deletions wormhole-connect/src/views/Bridge/NativeGasSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Slider, { SliderThumb } from '@mui/material/Slider';
import { styled } from '@mui/material/styles';
import { BigNumber, utils } from 'ethers';
import { utils } from 'ethers';
import React, { useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { makeStyles } from 'tss-react/mui';
Expand Down Expand Up @@ -88,6 +88,7 @@ function formatAmount(amount?: number): number {

const INITIAL_STATE = {
disabled: false,
min: 0,
max: 0,
nativeGas: 0,
token: formatAmount(),
Expand Down Expand Up @@ -137,7 +138,7 @@ function GasSlider(props: { disabled: boolean }) {
if (actualMaxSwap) {
// address the bug that the swapAmount='maxSwapAmount' results in a 'minimumSendAmount'
// that could be higher than 'amount' (due to the buffer packed into minimumSendAmount)
// not a perfect fix for all posible 'getMinSendAmount' functions - but valid for linear ones
// not a perfect fix for all possible 'getMinSendAmount' functions - but valid for linear ones
//
// For example, if 'amount' is 1.1, and relayerFee is 1, then 'amountNum' (the receive amount) is
// 0.1, and 'actualMaxSwap' is 0.1.
Expand Down Expand Up @@ -178,30 +179,35 @@ function GasSlider(props: { disabled: boolean }) {
!RouteOperator.getRoute(route).NATIVE_GAS_DROPOFF_SUPPORTED ||
!receivingWallet.address ||
!receivingToken
)
) {
return;
}

const tokenId = receivingToken.tokenId!;
RouteOperator.maxSwapAmount(
route,
toChain,
tokenId,
receivingWallet.address,
)
.then((res: BigNumber) => {
if (!res) {
let cancelled = false;
(async () => {
const tokenId = receivingToken.tokenId!;

try {
const maxSwapAmount = await RouteOperator.maxSwapAmount(
route,
toChain,
tokenId,
receivingWallet.address,
);
if (cancelled) return;
if (!maxSwapAmount) {
dispatch(setMaxSwapAmt(undefined));
return;
}
const toChainDecimals = getTokenDecimals(
wh.toChainId(toChain),
tokenId,
);
const amt = toDecimals(res, toChainDecimals, 6);
const amt = toDecimals(maxSwapAmount, toChainDecimals, 6);
dispatch(setMaxSwapAmt(Number.parseFloat(amt)));
})
.catch((e) => {
if (e.message.includes('swap rate not set')) {
} catch (e: any) {
if (cancelled) return;
if (e.message?.includes('swap rate not set')) {
if (route === Route.CCTPRelay) {
dispatch(setTransferRoute(Route.CCTPManual));
} else {
Expand All @@ -210,13 +216,32 @@ function GasSlider(props: { disabled: boolean }) {
} else {
throw e;
}
});
}

// get conversion rate of token
const { gasToken } = CHAINS[toChain]!;
getConversion(token, gasToken).then((res: number) => {
setState((prevState) => ({ ...prevState, conversionRate: res }));
});
// get conversion rate of token
const { gasToken } = CHAINS[toChain]!;
const conversionRate = await getConversion(token, gasToken);
if (cancelled) return;
const minNative = await RouteOperator.minSwapAmountNative(
route,
toChain,
tokenId,
receivingWallet.address,
);
const minNativeAdjusted = Number.parseFloat(
toDecimals(
minNative,
getTokenDecimals(wh.toChainId(toChain), 'native'),
),
);
if (cancelled) return;
const min = conversionRate ? minNativeAdjusted / conversionRate : 0;
setState((prevState) => ({ ...prevState, conversionRate, min }));
})();

return () => {
cancelled = true;
};
}, [
sendingToken,
receivingToken,
Expand Down Expand Up @@ -255,9 +280,10 @@ function GasSlider(props: { disabled: boolean }) {
// compute amounts on change
const handleChange = (e: any) => {
if (!amountNum || !state.conversionRate) return;
const newGasAmount = e.target.value * state.conversionRate;
const newTokenAmount = amountNum - e.target.value;
const swapAmount = e.target.value;
const value = e.target.value < state.min ? 0 : e.target.value;
const newGasAmount = value * state.conversionRate;
const newTokenAmount = amountNum - value;
const swapAmount = value;
const conversion = {
nativeGas: formatAmount(newGasAmount),
token: formatAmount(newTokenAmount),
Expand Down Expand Up @@ -368,6 +394,9 @@ function GasSlider(props: { disabled: boolean }) {
}
valueLabelDisplay="auto"
onChange={handleChange}
marks={
state.min ? [{ value: state.min, label: 'Min' }] : undefined
}
/>
<div className={classes.amounts}>
<div className={classes.amountDisplay}>
Expand Down

0 comments on commit d4d0388

Please sign in to comment.