Skip to content

Commit

Permalink
Allow users to deposit raw ETH instead of just WETH (#848)
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenshively authored Apr 15, 2024
1 parent 53205f7 commit 3a43ed0
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 90 deletions.
51 changes: 38 additions & 13 deletions earn/src/components/markets/modal/SupplyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Modal from 'shared/lib/components/common/Modal';
import TokenAmountInput from 'shared/lib/components/common/TokenAmountInput';
import Tooltip from 'shared/lib/components/common/Tooltip';
import { Text } from 'shared/lib/components/common/Typography';
import { ALOE_II_ROUTER_ADDRESS } from 'shared/lib/data/constants/ChainSpecific';
import { ALOE_II_ROUTER_ADDRESS, ETH_RESERVED_FOR_GAS } from 'shared/lib/data/constants/ChainSpecific';
import { ROUTER_TRANSMITTANCE, TERMS_OF_SERVICE_URL } from 'shared/lib/data/constants/Values';
import { GN, GNFormat } from 'shared/lib/data/GoodNumber';
import { Permit2State, usePermit2 } from 'shared/lib/data/hooks/UsePermit2';
Expand Down Expand Up @@ -75,8 +75,9 @@ function getConfirmButton(state: ConfirmButtonState, token: Token): { text: stri
}

type DepositButtonProps = {
depositAmount: GN;
depositBalance: GN;
supplyAmount: GN;
userBalanceTotal: GN;
userBalanceToken: GN;
token: Token;
kitty: Kitty;
accountAddress: Address;
Expand All @@ -85,15 +86,19 @@ type DepositButtonProps = {
};

function DepositButton(props: DepositButtonProps) {
const { depositAmount, depositBalance, token, kitty, accountAddress, setIsOpen, setPendingTxn } = props;
const { supplyAmount, userBalanceTotal, userBalanceToken, token, kitty, accountAddress, setIsOpen, setPendingTxn } =
props;
const { activeChain } = useContext(ChainContext);
const [isPending, setIsPending] = useState(false);

const supplyAmountToken = GN.min(supplyAmount, userBalanceToken);
const supplyAmountEth = supplyAmount.lte(userBalanceTotal) ? supplyAmount.sub(supplyAmountToken) : undefined;

const {
state: permit2State,
action: permit2Action,
result: permit2Result,
} = usePermit2(activeChain, token, accountAddress, ALOE_II_ROUTER_ADDRESS[activeChain.id], depositAmount);
} = usePermit2(activeChain, token, accountAddress, ALOE_II_ROUTER_ADDRESS[activeChain.id], supplyAmountToken);

const { config: depsitWithPermit2Config, refetch: refetchDepositWithPermit2 } = usePrepareContractWrite({
address: ALOE_II_ROUTER_ADDRESS[activeChain.id],
Expand All @@ -107,6 +112,9 @@ function DepositButton(props: DepositButtonProps) {
BigNumber.from(permit2Result.deadline),
permit2Result.signature ?? '0x',
],
overrides: {
value: supplyAmountEth?.toBigNumber(),
},
chainId: activeChain.id,
enabled: permit2State === Permit2State.DONE,
});
Expand Down Expand Up @@ -142,9 +150,9 @@ function DepositButton(props: DepositButtonProps) {
let confirmButtonState: ConfirmButtonState;
if (isPending) {
confirmButtonState = ConfirmButtonState.WAITING_FOR_TRANSACTION;
} else if (depositAmount.isZero()) {
} else if (supplyAmount.isZero()) {
confirmButtonState = ConfirmButtonState.LOADING;
} else if (depositAmount.gt(depositBalance)) {
} else if (supplyAmount.gt(userBalanceTotal)) {
confirmButtonState = ConfirmButtonState.INSUFFICIENT_ASSET;
} else {
confirmButtonState = permit2StateToButtonStateMap[permit2State] ?? ConfirmButtonState.READY;
Expand Down Expand Up @@ -193,25 +201,41 @@ export default function SupplyModal(props: SupplyModalProps) {
const { activeChain } = useContext(ChainContext);
const { address: userAddress } = useAccount();

const { refetch: refetchBalance, data: userBalanceResult } = useBalance({
const { refetch: refetchBalanceToken, data: tokenBalanceResult } = useBalance({
address: userAddress,
token: selectedRow.asset.address,
chainId: activeChain.id,
watch: false,
enabled: isOpen,
});

const isWeth = selectedRow.asset.name === 'Wrapped Ether';
const { refetch: refetchBalanceEth, data: ethBalanceResult } = useBalance({
address: userAddress,
chainId: activeChain.id,
watch: false,
enabled: isOpen && isWeth,
});

useEffect(() => {
let interval: NodeJS.Timer | null = null;
interval = setInterval(() => refetchBalance(), 13_000);
interval = setInterval(() => {
refetchBalanceToken();
refetchBalanceEth();
}, 13_000);
return () => {
if (interval != null) {
clearInterval(interval);
}
};
}, [refetchBalance]);
}, [refetchBalanceToken, refetchBalanceEth]);

const tokenBalance = GN.fromBigNumber(tokenBalanceResult?.value ?? BigNumber.from(0), selectedRow.asset.decimals);
const ethBalance = GN.fromBigNumber(ethBalanceResult?.value ?? BigNumber.from(0), 18);
const userBalance = isWeth
? tokenBalance.add(GN.max(ethBalance.sub(ETH_RESERVED_FOR_GAS[activeChain.id]), GN.zero(18)))
: tokenBalance;

const userBalance = GN.fromDecimalString(userBalanceResult?.formatted ?? '0', selectedRow.asset.decimals);
const supplyAmount = GN.fromDecimalString(amount || '0', selectedRow.asset.decimals);
const apyPercentage = roundPercentage(selectedRow.apy, 2).toFixed(2);

Expand Down Expand Up @@ -273,8 +297,9 @@ export default function SupplyModal(props: SupplyModalProps) {
</div>
<DepositButton
accountAddress={userAddress ?? '0x'}
depositAmount={supplyAmount}
depositBalance={userBalance}
supplyAmount={supplyAmount}
userBalanceTotal={userBalance}
userBalanceToken={tokenBalance}
kitty={selectedRow.kitty}
token={selectedRow.asset}
setIsOpen={setIsOpen}
Expand Down
4 changes: 2 additions & 2 deletions earn/src/components/portfolio/PortfolioGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ export default function PortfolioGrid(props: PortfolioGridProps) {
const { balances, activeAsset, tokenColors, tokenQuotes, tokenPriceData, errorLoadingPrices } = props;
const activeAssetAddress = activeAsset != null ? activeAsset.address : null;
const currentTokenQuote =
activeAssetAddress != null ? tokenQuotes.find((quote) => quote.token.address === activeAssetAddress) : undefined;
activeAssetAddress != null ? tokenQuotes.find((quote) => quote.token?.address === activeAssetAddress) : undefined;
const currentTokenPriceData =
activeAsset != null ? tokenPriceData.find((data) => data.token.address === activeAsset.address) : undefined;
activeAsset != null ? tokenPriceData.find((data) => data.token?.address === activeAsset.address) : undefined;
const activeColor = activeAsset ? tokenColors.get(activeAsset.address) : undefined;
return (
<Grid>
Expand Down
57 changes: 40 additions & 17 deletions earn/src/components/portfolio/modal/EarnInterestModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { BaseMaxButton } from 'shared/lib/components/common/Input';
import Modal from 'shared/lib/components/common/Modal';
import Tooltip from 'shared/lib/components/common/Tooltip';
import { Text } from 'shared/lib/components/common/Typography';
import { ALOE_II_ROUTER_ADDRESS } from 'shared/lib/data/constants/ChainSpecific';
import { ALOE_II_ROUTER_ADDRESS, ETH_RESERVED_FOR_GAS } from 'shared/lib/data/constants/ChainSpecific';
import { ROUTER_TRANSMITTANCE, TERMS_OF_SERVICE_URL } from 'shared/lib/data/constants/Values';
import { GN } from 'shared/lib/data/GoodNumber';
import { GN, GNFormat } from 'shared/lib/data/GoodNumber';
import { usePermit2, Permit2State } from 'shared/lib/data/hooks/UsePermit2';
import { Kitty } from 'shared/lib/data/Kitty';
import { Token } from 'shared/lib/data/Token';
Expand Down Expand Up @@ -76,8 +76,9 @@ function getConfirmButton(state: ConfirmButtonState, token: Token): { text: stri
}

type DepositButtonProps = {
depositAmount: GN;
depositBalance: GN;
supplyAmount: GN;
userBalanceTotal: GN;
userBalanceToken: GN;
token: Token;
kitty: Kitty;
accountAddress: Address;
Expand All @@ -86,15 +87,19 @@ type DepositButtonProps = {
};

function DepositButton(props: DepositButtonProps) {
const { depositAmount, depositBalance, token, kitty, accountAddress, setIsOpen, setPendingTxn } = props;
const { supplyAmount, userBalanceTotal, userBalanceToken, token, kitty, accountAddress, setIsOpen, setPendingTxn } =
props;
const { activeChain } = useContext(ChainContext);
const [isPending, setIsPending] = useState(false);

const supplyAmountToken = GN.min(supplyAmount, userBalanceToken);
const supplyAmountEth = supplyAmount.lte(userBalanceTotal) ? supplyAmount.sub(supplyAmountToken) : undefined;

const {
state: permit2State,
action: permit2Action,
result: permit2Result,
} = usePermit2(activeChain, token, accountAddress, ALOE_II_ROUTER_ADDRESS[activeChain.id], depositAmount);
} = usePermit2(activeChain, token, accountAddress, ALOE_II_ROUTER_ADDRESS[activeChain.id], supplyAmountToken);

const { config: depsitWithPermit2Config, refetch: refetchDepositWithPermit2 } = usePrepareContractWrite({
address: ALOE_II_ROUTER_ADDRESS[activeChain.id],
Expand All @@ -108,6 +113,9 @@ function DepositButton(props: DepositButtonProps) {
BigNumber.from(permit2Result.deadline),
permit2Result.signature ?? '0x',
],
overrides: {
value: supplyAmountEth?.toBigNumber(),
},
chainId: activeChain.id,
enabled: permit2State === Permit2State.DONE,
});
Expand Down Expand Up @@ -143,9 +151,9 @@ function DepositButton(props: DepositButtonProps) {
let confirmButtonState: ConfirmButtonState;
if (isPending) {
confirmButtonState = ConfirmButtonState.WAITING_FOR_TRANSACTION;
} else if (depositAmount.isZero()) {
} else if (supplyAmount.isZero()) {
confirmButtonState = ConfirmButtonState.LOADING;
} else if (depositAmount.gt(depositBalance)) {
} else if (supplyAmount.gt(userBalanceTotal)) {
confirmButtonState = ConfirmButtonState.INSUFFICIENT_ASSET;
} else {
confirmButtonState = permit2StateToButtonStateMap[permit2State] ?? ConfirmButtonState.READY;
Expand Down Expand Up @@ -215,18 +223,27 @@ export default function EarnInterestModal(props: EarnInterestModalProps) {
}, [defaultOption]);

// Get the user's balance of the selected token
const { refetch: refetchDepositBalance, data: depositBalance } = useBalance({
const { refetch: refetchBalanceToken, data: tokenBalanceResult } = useBalance({
address: account?.address ?? '0x',
token: selectedOption.address,
chainId: activeChain.id,
enabled: isOpen,
});

const isWeth = selectedOption.name === 'Wrapped Ether';
const { refetch: refetchBalanceEth, data: ethBalanceResult } = useBalance({
address: account?.address ?? '0x',
chainId: activeChain.id,
watch: false,
enabled: isOpen && isWeth,
});

useEffect(() => {
let interval: NodeJS.Timer | null = null;
if (isOpen) {
interval = setInterval(() => {
refetchDepositBalance();
refetchBalanceToken();
refetchBalanceEth();
}, 13_000);
}
if (!isOpen && interval != null) {
Expand All @@ -237,7 +254,7 @@ export default function EarnInterestModal(props: EarnInterestModalProps) {
clearInterval(interval);
}
};
}, [refetchDepositBalance, isOpen]);
}, [refetchBalanceToken, refetchBalanceEth, isOpen]);

// Get the active kitty that corresponds to the selected token and is in
// the selected token / collateral token lending pair
Expand All @@ -261,8 +278,13 @@ export default function EarnInterestModal(props: EarnInterestModalProps) {
? selectedPairOption.token1
: selectedPairOption.token0;

const gnDepositAmount = GN.fromDecimalString(inputValue || '0', selectedOption.decimals);
const gnDepositBalance = GN.fromDecimalString(depositBalance?.formatted ?? '0', selectedOption.decimals);
const tokenBalance = GN.fromBigNumber(tokenBalanceResult?.value ?? BigNumber.from(0), selectedOption.decimals);
const ethBalance = GN.fromBigNumber(ethBalanceResult?.value ?? BigNumber.from(0), 18);
const userBalance = isWeth
? tokenBalance.add(GN.max(ethBalance.sub(ETH_RESERVED_FOR_GAS[activeChain.id]), GN.zero(18)))
: tokenBalance;

const supplyAmount = GN.fromDecimalString(inputValue || '0', selectedOption.decimals);

return (
<Modal
Expand All @@ -285,8 +307,8 @@ export default function EarnInterestModal(props: EarnInterestModalProps) {
<BaseMaxButton
size='L'
onClick={() => {
if (depositBalance != null) {
setInputValue(depositBalance?.formatted);
if (tokenBalanceResult != null) {
setInputValue(userBalance.toString(GNFormat.DECIMAL));
}
}}
>
Expand Down Expand Up @@ -369,8 +391,9 @@ export default function EarnInterestModal(props: EarnInterestModalProps) {
</div>
<div className='w-full'>
<DepositButton
depositAmount={gnDepositAmount}
depositBalance={gnDepositBalance}
supplyAmount={supplyAmount}
userBalanceTotal={userBalance}
userBalanceToken={tokenBalance}
token={selectedOption}
kitty={activeKitty}
accountAddress={account.address ?? '0x'}
Expand Down
20 changes: 17 additions & 3 deletions earn/src/data/LendingPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Address } from 'wagmi';

import { ContractCallReturnContextEntries, convertBigNumbersForReturnContexts } from '../util/Multicall';
import { computeLTV } from './BalanceSheet';
import { UNISWAP_POOL_DENYLIST } from './constants/Addresses';
import { UNISWAP_POOL_DENYLIST, ZERO_ADDRESS } from './constants/Addresses';
import { TOPIC0_CREATE_MARKET_EVENT } from './constants/Signatures';
import { asFactoryData, FactoryData } from './FactoryData';
import { asOracleData, OracleData } from './OracleData';
Expand Down Expand Up @@ -311,6 +311,8 @@ export async function getLendingPairBalances(
tokenSet.add(pair.token1);
});

const ethBalance = await provider.getBalance(userAddress);

const multicall = new Multicall({
ethersProvider: provider,
multicallCustomContractAddress: MULTICALL_ADDRESS[chainId],
Expand Down Expand Up @@ -360,6 +362,12 @@ export async function getLendingPairBalances(
const deprecatedLendingPairBalancesArray: LendingPairBalances[] = [];
const balancesMap: LendingPairBalancesMap = new Map();

balancesMap.set(ZERO_ADDRESS, {
value: GN.fromBigNumber(ethBalance, 18).toNumber(),
gn: GN.fromBigNumber(ethBalance, 18),
form: 'raw',
});

lendingPairs.forEach((lendingPair) => {
const hexes = [
`${lendingPair.token0.address}.balanceOf`,
Expand All @@ -382,8 +390,14 @@ export async function getLendingPairBalances(
balancesMap.set(lendingPair.kitty1.address, { value: gns[3].toNumber(), gn: gns[3], form: 'underlying' });

deprecatedLendingPairBalancesArray.push({
token0Balance: toImpreciseNumber(BigNumber.from(hexes[0]), lendingPair.token0.decimals),
token1Balance: toImpreciseNumber(BigNumber.from(hexes[1]), lendingPair.token1.decimals),
token0Balance: toImpreciseNumber(
BigNumber.from(hexes[0]).add(lendingPair.token0.name === 'Wrapped Ether' ? ethBalance : 0),
lendingPair.token0.decimals
),
token1Balance: toImpreciseNumber(
BigNumber.from(hexes[1]).add(lendingPair.token1.name === 'Wrapped Ether' ? ethBalance : 0),
lendingPair.token1.decimals
),
kitty0Balance: toImpreciseNumber(BigNumber.from(hexes[2]), lendingPair.token0.decimals),
kitty1Balance: toImpreciseNumber(BigNumber.from(hexes[3]), lendingPair.token1.decimals),
});
Expand Down
11 changes: 9 additions & 2 deletions earn/src/pages/MarketsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { getLendingPairBalances, LendingPairBalancesMap } from '../data/LendingP
import { fetchBorrowerDatas, UniswapPoolInfo } from '../data/MarginAccount';
import { PriceRelayLatestResponse } from '../data/PriceRelayResponse';
import { getProminentColor } from '../util/Colors';
import { ZERO_ADDRESS } from '../data/constants/Addresses';

const SECONDARY_COLOR = 'rgba(130, 160, 182, 1)';
const SELECTED_TAB_KEY = 'selectedTab';
Expand Down Expand Up @@ -252,11 +253,17 @@ export default function MarketsPage() {

const supplyRows = useMemo(() => {
const rows: SupplyTableRow[] = [];
const ethBalance = balancesMap.get(ZERO_ADDRESS);
lendingPairs.forEach((pair) => {
const isToken0Weth = pair.token0.name === 'Wrapped Ether';
const isToken1Weth = pair.token1.name === 'Wrapped Ether';

const token0Price = tokenQuotes.get(pair.token0.symbol) || 0;
const token1Price = tokenQuotes.get(pair.token1.symbol) || 0;
const token0Balance = balancesMap.get(pair.token0.address)?.value || 0;
const token1Balance = balancesMap.get(pair.token1.address)?.value || 0;
const token0Balance =
(balancesMap.get(pair.token0.address)?.value || 0) + ((isToken0Weth && ethBalance?.value) || 0);
const token1Balance =
(balancesMap.get(pair.token1.address)?.value || 0) + ((isToken1Weth && ethBalance?.value) || 0);
const kitty0Balance = balancesMap.get(pair.kitty0.address)?.value || 0;
const kitty1Balance = balancesMap.get(pair.kitty1.address)?.value || 0;

Expand Down
12 changes: 6 additions & 6 deletions earn/src/pages/PortfolioPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ export type PriceEntry = {
};

export type TokenQuote = {
token: Token;
token?: Token;
price: number;
};

export type TokenPriceData = {
token: Token;
token?: Token;
priceEntries: PriceEntry[];
};

Expand Down Expand Up @@ -175,13 +175,13 @@ export default function PortfolioPage() {
}
const tokenQuoteData: TokenQuote[] = Object.entries(latestPriceResponse).map(([symbol, data]) => {
return {
token: getTokenBySymbol(activeChain.id, symbol)!,
token: getTokenBySymbol(activeChain.id, symbol),
price: data.price,
};
});
const tokenPriceData: TokenPriceData[] = Object.entries(historicalPriceResponse).map(([symbol, data]) => {
return {
token: getTokenBySymbol(activeChain.id, symbol)!,
token: getTokenBySymbol(activeChain.id, symbol),
priceEntries: data.prices,
};
});
Expand Down Expand Up @@ -240,8 +240,8 @@ export default function PortfolioPage() {

const combinedBalances: TokenBalance[] = useMemo(() => {
const combined = lendingPairs.flatMap((pair, i) => {
const token0Quote = tokenQuotes.find((quote) => quote.token.address === pair.token0.address);
const token1Quote = tokenQuotes.find((quote) => quote.token.address === pair.token1.address);
const token0Quote = tokenQuotes.find((quote) => quote.token?.address === pair.token0.address);
const token1Quote = tokenQuotes.find((quote) => quote.token?.address === pair.token1.address);
const token0Price = token0Quote?.price || 0;
const token1Price = token1Quote?.price || 0;
const pairName: string = `${pair.token0.symbol}-${pair.token1.symbol}`;
Expand Down
Loading

0 comments on commit 3a43ed0

Please sign in to comment.