-
Notifications
You must be signed in to change notification settings - Fork 71
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: Add view for get/set ERC20 balances (#39) #50
Conversation
I think we can merge w/o this, document as a known limitation, and we can add an issue for future usage. Running with the revm in WASM & capturing the storage slots accessed during |
src/hooks/useErcBalance.ts
Outdated
contract.read.name(), | ||
contract.read.symbol(), | ||
contract.read.decimals(), | ||
contract.read.balanceOf([address]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe for next pr: it's probably better to just leave this as only the balanceOf call, and then add the name/symbol/decimals metadata to the tokens store?
It should attempt to validate that the address is an erc20 (not sure how really), and maybe include if it's 721, which can save the tokenUri if it exists? q is whether we want multiple stores for 20/721/1155 or if it's possible to keep in 1 and just filter it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd consider splitting this up into useErc20Balance
and useErc20Metadata
Hooks, that both use React Query. Two reasons:
- Metadata is independent of address, and we can cache that separately (instead of having it as separate cache entries for erc/address pairs).
- We can still just use React Query still for
useErc20Metadata
as syncing it into Zustand probably isn't worth it (syncing RQ with external stores like Zustand is generally not recommended). - Invalidating metadata is useless since it's immutable (https://github.com/paradigmxyz/rivet/pull/50/files#diff-83dac5097fa3ff04758ab51b989502a815d5ef851830cd4f9aee80856305c6f9R59-R61)
Might be worth doing in this PR as it's pretty trivial.
src/hooks/useErcBalance.ts
Outdated
contract.read.name(), | ||
contract.read.symbol(), | ||
contract.read.decimals(), | ||
contract.read.balanceOf([address]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd consider splitting this up into useErc20Balance
and useErc20Metadata
Hooks, that both use React Query. Two reasons:
- Metadata is independent of address, and we can cache that separately (instead of having it as separate cache entries for erc/address pairs).
- We can still just use React Query still for
useErc20Metadata
as syncing it into Zustand probably isn't worth it (syncing RQ with external stores like Zustand is generally not recommended). - Invalidating metadata is useless since it's immutable (https://github.com/paradigmxyz/rivet/pull/50/files#diff-83dac5097fa3ff04758ab51b989502a815d5ef851830cd4f9aee80856305c6f9R59-R61)
Might be worth doing in this PR as it's pretty trivial.
src/hooks/useErcBalance.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to useErc20Balance
?
src/hooks/useErcBalance.ts
Outdated
|
||
type UseErcBalanceParameters = { | ||
address: Address | ||
erc: Address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
erc: Address | |
contractAddress: Address |
src/hooks/useErcBalance.ts
Outdated
erc, | ||
address, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
erc, | |
address, | |
address, | |
tokenAddress, |
src/hooks/useErcBalance.ts
Outdated
queryKey: getErcBalanceQueryKey([client.key, { erc, address }]), | ||
async queryFn() { | ||
const contract = getContract({ | ||
address: erc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
address: erc, | |
address: tokenAddress, |
src/hooks/useSetErcBalance.ts
Outdated
if (slotGuess >= 10n) { | ||
throw "couldn't find balancesOf mapping past slot 10" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (slotGuess >= 10n) { | |
throw "couldn't find balancesOf mapping past slot 10" | |
} | |
if (slotGuess >= 10n) | |
throw new BaseError('could not find storage for: `balanceOf`') |
src/hooks/useSetErcBalance.ts
Outdated
} | ||
} | ||
} catch (e) { | ||
console.error(e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this try/catch? Can we remove and let React Query catch the error?
src/screens/index.tsx
Outdated
@@ -251,6 +254,34 @@ function RemoveButton({ onClick }: { onClick: (e: any) => void }) { | |||
) | |||
} | |||
|
|||
export function DetailButton({ onClick }: { onClick: (e: any) => void }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI - we have a Button.Symbol
now!
src/hooks/useSetErcBalance.ts
Outdated
type SetErcBalanceParameters = { | ||
address: Address | ||
erc: Address | ||
value: bigint | ||
} | ||
|
||
import { type Address, encodeAbiParameters, keccak256, pad, toHex } from 'viem' | ||
import { queryClient } from '~/react-query' | ||
import { useClient } from './useClient' | ||
import { getErcBalanceQueryKey } from './useErcBalance' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type SetErcBalanceParameters = { | |
address: Address | |
erc: Address | |
value: bigint | |
} | |
import { type Address, encodeAbiParameters, keccak256, pad, toHex } from 'viem' | |
import { queryClient } from '~/react-query' | |
import { useClient } from './useClient' | |
import { getErcBalanceQueryKey } from './useErcBalance' | |
import { type Address, encodeAbiParameters, keccak256, pad, toHex } from 'viem' | |
import { queryClient } from '~/react-query' | |
import { useClient } from './useClient' | |
import { getErcBalanceQueryKey } from './useErcBalance' | |
type SetErcBalanceParameters = { | |
address: Address | |
erc: Address | |
value: bigint | |
} |
src/zustand/tokens.ts
Outdated
export type TokensState = { | ||
tokens: Address[] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will need to make sure this works across accounts too.
export type TokensState = { | |
tokens: Address[] | |
} | |
export type TokensState = { | |
tokens: Record<Address, Address[]> | |
} |
5877f76
to
3df6bad
Compare
vid: https://twitter.com/dittoproj/status/1699904944898240966?s=20
balanceOf
like rebasing token (stETH)formatUnits/parseUnits
totalSupply
, but can do it using the same approach as this@jxom said we can add a new issue to embed
revm
to do this better but my workaround might work for now for most tokens.This setERCBalance is a bit like fuzzing! By assuming the
balanceOf
mapping is usually near slot 0, we can iterate through each and randomly check values to set we are setting the right storage slot.Note: one issue is ERC20 tokens that use special slots (assembly, diamond storage, etc) - this stops checking at slot 10. Those would have to be hardcoded or we could read the source code to find the storage constant, not sure how easy that is?
q: