Skip to content

Commit

Permalink
prevent max amount from overshooting when selecting ETH. Prevent page…
Browse files Browse the repository at this point in the history
… from crashing on network switch
  • Loading branch information
jankjr committed Oct 18, 2023
1 parent 3db75ce commit 7df9ec3
Show file tree
Hide file tree
Showing 12 changed files with 1,178 additions and 308 deletions.
1,308 changes: 1,054 additions & 254 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/rtoken-selector/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const availableTokensAtom = atom((get) => {
}

for (const token of owned) {
if (!tokenList[token.address]) {
if (tokenList[token.address] == null) {
tokenList[token.address] = {
address: token.address,
symbol: token.symbol,
Expand Down
1 change: 0 additions & 1 deletion src/state/chain/atoms/chainAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { blockDuration } from 'utils/constants'
import { formatEther } from 'viem'
import { Address, PublicClient, WalletClient } from 'wagmi'
import rtokens from '@lc-labs/rtokens'

/**
* #########################
* Chain state related atoms
Expand Down
44 changes: 34 additions & 10 deletions src/state/updater.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
import { Component } from 'react'
import RpayFeed from './rpay/RpayFeed'
import RTokenUpdater from './rtoken/updater'
import CollateralYieldUpdater from './updaters/CollateralYieldUpdater'
import PricesUpdater from './updaters/PriceUpdater'
import AccountUpdater from './wallet/updaters/AccountUpdater'
import { TokenBalancesUpdater } from './wallet/updaters/TokenBalancesUpdater'
import { useChainId } from 'wagmi'

class CatchErrors extends Component<{ children: any }> {
state = {
hasError: false,
}
constructor(props: any) {
super(props)
}
componentDidCatch() {
this.setState({ hasError: true })
}

render() {
if (this.state.hasError) {
return null
}
return <>{this.props.children}</>
}
}
/**
* Updater
*/
const Updater = () => (
<>
<PricesUpdater />
<AccountUpdater />
<RpayFeed />
<RTokenUpdater />
<CollateralYieldUpdater />
<TokenBalancesUpdater />
</>
)
const Updater = () => {
const id = useChainId()

return (
<CatchErrors key={id}>
<PricesUpdater />
<AccountUpdater />
<RpayFeed />
<RTokenUpdater />
<CollateralYieldUpdater />
<TokenBalancesUpdater />
</CatchErrors>
)
}

export default Updater
8 changes: 4 additions & 4 deletions src/state/wallet/updaters/TokenBalancesUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,23 @@ const balancesCallAtom = atom((get) => {
return undefined
}


const tokens: [Address, number][] = [
[rToken.address, rToken.decimals],
[RSR_ADDRESS[chainId], 18],
...rToken.collaterals.map((token): [Address, number] => [
token.address,
token.decimals,
]),
...zapTokens.map(i => ([i.address.address as Address, i.decimals] as [Address, number]))
...(zapTokens?.map(i => ([i.address.address as Address, i.decimals] as [Address, number]))??[])
];

if (rToken.stToken) {
tokens.push([rToken.stToken.address, rToken.stToken.decimals])
}

return {
tokens,
calls: tokens.map(([address]) => ({
tokens: tokens??[],
calls: (tokens??[]).map(([address]) => ({
address,
abi: ERC20,
functionName: 'balanceOf',
Expand All @@ -51,6 +50,7 @@ const balancesCallAtom = atom((get) => {
}
})


export const TokenBalancesUpdater = () => {
const { tokens, calls } = useAtomValue(balancesCallAtom) ?? {}
const setBalances = useSetAtom(balancesAtom)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Divider } from 'theme-ui'
import {
approvalPending,
selectedZapTokenAtom,
zapInputString,
zapTxHash,
} from '../state/atoms'
import { approvalTxFeeAtom, ui, zapTxFeeAtom } from '../state/ui-atoms'
Expand Down
1 change: 0 additions & 1 deletion src/views/issuance/components/zap/components/ZapButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const ZapButton = (props: Partial<LoadingButtonProps>) => {
useEffect(() => {

if (ttx != null) {
console.log("Setting previous")
setPrevious(ttx)
}

Expand Down
1 change: 0 additions & 1 deletion src/views/issuance/components/zap/components/ZapInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const ZapDust = () => {
if (total.amount < 10000n) {
str = '*'
}
console.log(total.amount)

return (
<span
Expand Down
25 changes: 22 additions & 3 deletions src/views/issuance/components/zap/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import useRToken from 'hooks/useRToken'
import { useAtomValue } from 'jotai'
import mixpanel from 'mixpanel-browser'
import { Suspense, useEffect, useState } from 'react'
import { Component, Suspense, useEffect, useState } from 'react'
import { blockAtom, gasFeeAtom } from 'state/atoms'
import { Card } from 'theme-ui'
import ConfirmZap from './components/ConfirmZap'
Expand All @@ -23,6 +23,25 @@ const UpdateBlockAndGas = () => {
return null
}

class CatchErrors extends Component<{children: any}> {
state = {
hasError: false
}
constructor(props: any) {
super(props)
}
componentDidCatch() {
this.setState({ hasError: true })
}

render() {
if (this.state.hasError) {
return null
}
return <>{this.props.children}</>
}
}

/**
* Zap widget
*/
Expand All @@ -40,7 +59,7 @@ const Zap = () => {


return (
<>
<CatchErrors>
<Suspense fallback={<></>}>
<UpdateBlockAndGas />
</Suspense>
Expand All @@ -49,7 +68,7 @@ const Zap = () => {
<ZapButton onClick={handleClick} />
</Card>
{isZapping && <ConfirmZap onClose={() => setZapping(false)} />}
</>
</CatchErrors>
)
}

Expand Down
41 changes: 33 additions & 8 deletions src/views/issuance/components/zap/state/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import { resolvedZapState, zappableTokens } from './zapper'
import { type SearcherResult } from '@reserve-protocol/token-zapper/types/searcher/SearcherResult'
import { type ZapTransaction } from '@reserve-protocol/token-zapper/types/searcher/ZapTransaction'
import { parseEther } from 'viem'

/**
* I've tried to keep react effects to a minimum so most async code is triggered via some signal
Expand Down Expand Up @@ -179,19 +180,43 @@ export const zapQuotePromise = loadable(
a.catch((e) => console.log(e.message))

const out = await a
console.log(out)
return out
})
)

export const zapQuote = simplifyLoadable(zapQuotePromise)

export const selectedZapTokenBalance = onlyNonNullAtom((get) => {

const approximateGasUsage: Record<string, bigint> = {
'0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f': 3_000_000n,
'0xe72b141df173b999ae7c1adcbf60cc9833ce56a8': 3_000_000n,
'0xacdf0dba4b9839b96221a8487e9ca660a48212be': 6_000_000n,
'0xf2098092a5b9d25a3cc7ddc76a0553c9922eea9e': 3_000_000n,
'0x9b451beb49a03586e6995e5a93b9c745d068581e': 3_000_000n,
'0xfc0b1eef20e4c68b3dcf36c4537cfa7ce46ca70b': 3_000_000n,
'0x50249c768a6d3cb4b6565c0a2bfbdb62be94915c': 3_000_000n,
'0xcc7ff230365bd730ee4b352cc2492cedac49383e': 6_000_000n
}
export const selectedZapTokenBalance = atom((get) => {
const token = get(selectedZapTokenAtom)
if (token == null) {
return null
}
const zapState = get(resolvedZapState)
if (zapState == null) {
return null
}
const rtoken = get(rTokenAtom)
const zapTransaction = get(resolvedZapTransaction)
const quantities = get(balancesAtom) ?? {}
const bal = quantities[token.address.address as any]?.balance ?? 0n

return token.from(bal)
const fr = quantities[token.address.address as any]?.balance ?? "0"
let bal = token.from(fr)
if (token.address.address === '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') {
const a = zapState.gasPrice * (zapTransaction?.transaction.gasEstimate ?? approximateGasUsage[rtoken?.address.toLowerCase() ?? ""] ?? 2_500_000n);
bal = bal.sub(token.from(a))
bal = bal.amount < 0n ? token.zero : bal
}
return bal
})

export const approvalNeededAtom = loadable(
Expand Down Expand Up @@ -307,9 +332,9 @@ const zapTxAtom = atom(async (get) => {
permit2 =
signature != null && permit != null
? {
permit: permit.permit,
signature,
}
permit: permit.permit,
signature,
}
: undefined
}
const tx = await result.toTransaction({
Expand Down
21 changes: 14 additions & 7 deletions src/views/issuance/components/zap/state/ui-atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ export const zapOutputValue = onlyNonNullAtom((get) => {
const state = atom((get) => {
const quotePromise = get(zapQuotePromise)
const approvePromise = get(approvalNeededAtom)
const prev = get(previousZapTransaction)
const tx = get(zapTransaction)
const units = get(zapTransactionGasEstimateUnits)
const balances = get(hasSufficientGasTokenAndERC20TokenBalance)
Expand Down Expand Up @@ -243,7 +242,13 @@ const loadingStates = new Set<UIState>([
// 'approval_sent_loading',
'signature_loading',
])
const buttonEnabled = atom((get) => buttonEnabledStates.has(get(state)) || get(previousZapTransaction) != null)
const buttonEnabled = atom((get) => {
const s = get(state);
if (s === 'insufficient_gas_balance' || s === 'insufficient_token_balance') {
return false
}
return buttonEnabledStates.has(s) || get(previousZapTransaction) != null
})
const buttonIsLoading = atom((get) => loadingStates.has(get(state)))
const buttonLabel = atom((get) => {
if (get(zapSender) == null) {
Expand All @@ -253,16 +258,18 @@ const buttonLabel = atom((get) => {
if (loadedState == null) {
return '+ Zap'
}
const s = get(state)

if (get(previousZapTransaction) != null) {
return `+ Mint ${loadedState.rToken.symbol}`
}

switch (get(state)) {
switch (s) {
case 'insufficient_gas_balance':
return 'Insufficient ETH balance'
case 'insufficient_token_balance':
return `Insufficient ${loadedState.tokenToZap.symbol} balance`
}
if (get(previousZapTransaction) != null) {
return `+ Mint ${loadedState.rToken.symbol}`
}
switch (s) {
case 'quote_error':
return 'Failed to find zap'
case 'approval_error':
Expand Down
33 changes: 16 additions & 17 deletions src/views/issuance/components/zap/state/zapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { atom } from 'jotai'
import { loadable } from 'jotai/utils'
import { onlyNonNullAtom, simplifyLoadable } from 'utils/atoms/utils'
import { createProxiedOneInchAggregator } from './createProxiedOneInchAggregator'
import { clientAtom } from 'state/atoms'
import { Web3Provider } from "@ethersproject/providers"
import { chainIdAtom, clientAtom } from 'state/atoms'
import { Provider, Web3Provider } from "@ethersproject/providers"
import { PublicClient } from 'viem'
import { ChainId } from 'utils/chains'

export function publicClientToProvider(publicClient: PublicClient) {
const { chain } = publicClient
Expand All @@ -22,12 +23,12 @@ export function publicClientToProvider(publicClient: PublicClient) {
}, network)
}

const providerAtom = atom<any>(get => {
const providerAtom = atom(get => {
const cli = get(clientAtom)
if (cli == null) {
return null
}
return publicClientToProvider(cli as any)
return publicClientToProvider(cli as any) as Web3Provider
})


Expand All @@ -51,6 +52,7 @@ const ONE_INCH_PROXIES = [

export const zapperState = loadable(
atom(async (get) => {
get(chainIdAtom)
const provider = get(providerAtom)

// To inject register data into the zapper initialize code, it's probably best to load it all here.
Expand All @@ -59,10 +61,11 @@ export const zapperState = loadable(
if (provider == null) {
return null
}
provider.on('error', () => {})

try {

const chainIdToConfig: Record<number, {config: any, setup: (uni: Universe<any>) => Promise<any>}> = {
const chainIdToConfig: Record<number, { config: any, setup: (uni: Universe<any>) => Promise<any> }> = {
1: {
config: ethereumConfig,
setup: setupEthereumZapper
Expand All @@ -72,26 +75,22 @@ export const zapperState = loadable(
setup: setupBaseZapper
}
}

const universe = await Universe.createWithConfig(
provider,
chainIdToConfig[provider.network.chainId].config,
chainIdToConfig[provider.network.chainId].setup
)
try {
if (ONE_INCH_PROXIES.length !== 0) {
universe.dexAggregators.push(
createProxiedOneInchAggregator(universe, ONE_INCH_PROXIES)
)
}
} catch (e) {
console.log(e)
if (ONE_INCH_PROXIES.length !== 0) {
universe.dexAggregators.push(
createProxiedOneInchAggregator(universe, ONE_INCH_PROXIES)
)
}

return universe

} catch (e) {
console.log(e)
throw e
return null
}
})
)
Expand All @@ -106,7 +105,7 @@ export const zapperLoaded = atom(async (get) => {
return true
})

export const zappableTokens = atom(async(get) => {
export const zappableTokens = atom(async (get) => {
const uni = get(resolvedZapState)
if (uni == null) {
return []
Expand Down

0 comments on commit 7df9ec3

Please sign in to comment.