-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fetch EVM balances, make everything generic over EVM chain, v2 schema…
… fixes (#3962) - **feat(app2): add balances evm** - **refactor(app2): better schema, better balances** - **feat(app2): fetch a balance** - **feat(app2): fetch all sepolia balances** - **feat(app2): fetch all evm balances** - **feat(app2): show token errors** - **fix(app2): evm balance fetching** - **feat(app2): use v2_chains** - **feat(app2): add chain.toViemChain()** - **feat(app2): fetch balances for all evm chains, also significant cleanup** - **feat(app2): generic over evm chain** - **chore(app2): fmt**
- Loading branch information
Showing
25 changed files
with
1,006 additions
and
1,396 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { | ||
arbitrumSepolia, | ||
berachainTestnetbArtio, | ||
holesky, | ||
scrollSepolia, | ||
sepolia | ||
} from "viem/chains" | ||
|
||
export const VIEM_CHAINS = [ | ||
sepolia, | ||
holesky, | ||
berachainTestnetbArtio, | ||
arbitrumSepolia, | ||
scrollSepolia | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import { Schema } from "effect" | ||
|
||
export const Hex = Schema.String.pipe(Schema.pattern(/^0x[0-9a-f]+$/)) | ||
export const Hex = Schema.TemplateLiteral("0x", Schema.String).pipe(Schema.pattern(/^0x[0-9a-f]+$/)) | ||
|
||
// TODO: validate ERC55 checksum | ||
export const HexChecksum = Schema.String.pipe(Schema.pattern(/^0x[0-9a-fA-F]+$/)) | ||
export const HexChecksum = Schema.TemplateLiteral("0x", Schema.String).pipe( | ||
Schema.pattern(/^0x[0-9a-fA-F]+$/) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Data, Effect, Option, Schema, Schedule } from "effect" | ||
import { erc20Abi, type PublicClient } from "viem" | ||
import type { TimeoutException } from "effect/Cause" | ||
import type { DurationInput } from "effect/Duration" | ||
import type { ReadContractErrorType } from "viem" | ||
import { getPublicClient } from "$lib/services/evm/clients" | ||
import { RawTokenBalance, TokenRawAmount, type TokenRawDenom } from "$lib/schema/token" | ||
import type { NoViemChainError } from "$lib/services/evm/clients" | ||
import type { AddressEvmCanonical } from "$lib/schema/address" | ||
import type { Chain } from "$lib/schema/chain" | ||
import type { CreatePublicClientError } from "$lib/services/transfer" | ||
|
||
export type FetchBalanceError = | ||
| NoViemChainError | ||
| TimeoutException | ||
| ReadContractError | ||
| CreatePublicClientError | ||
export class ReadContractError extends Data.TaggedError("ReadContractError")<{ | ||
cause: ReadContractErrorType | ||
}> {} | ||
|
||
// Schema for the balance response | ||
export const BalanceSchema = Schema.Struct({ | ||
balance: Schema.String, | ||
token: Schema.String, | ||
address: Schema.String | ||
}) | ||
|
||
const fetchTokenBalance = ({ | ||
client, | ||
tokenAddress, | ||
walletAddress | ||
}: { | ||
client: PublicClient | ||
tokenAddress: TokenRawDenom | ||
walletAddress: AddressEvmCanonical | ||
}) => | ||
Effect.tryPromise({ | ||
try: () => | ||
client.readContract({ | ||
address: tokenAddress, | ||
abi: erc20Abi, | ||
functionName: "balanceOf", | ||
args: [walletAddress] | ||
}), | ||
catch: err => new ReadContractError({ cause: err as ReadContractErrorType }) | ||
}) | ||
|
||
export const createBalanceQuery = ({ | ||
chain, | ||
tokenAddress, | ||
walletAddress, | ||
refetchInterval, | ||
writeData, | ||
writeError | ||
}: { | ||
chain: Chain | ||
tokenAddress: TokenRawDenom | ||
walletAddress: AddressEvmCanonical | ||
refetchInterval: DurationInput | ||
writeData: (data: RawTokenBalance) => void | ||
writeError: (error: Option.Option<FetchBalanceError>) => void | ||
}) => { | ||
const fetcherPipeline = Effect.gen(function* (_) { | ||
yield* Effect.log(`starting balances fetcher for ${walletAddress}:${tokenAddress}`) | ||
const client = yield* getPublicClient(chain) | ||
|
||
const balance = yield* Effect.retry( | ||
fetchTokenBalance({ client, tokenAddress, walletAddress }).pipe(Effect.timeout("10 seconds")), | ||
{ times: 4 } | ||
) | ||
|
||
yield* Effect.sync(() => { | ||
writeData(RawTokenBalance.make(Option.some(TokenRawAmount.make(balance)))) | ||
writeError(Option.none()) | ||
}) | ||
}).pipe( | ||
Effect.tapError(error => | ||
Effect.sync(() => { | ||
writeError(Option.some(error)) | ||
}) | ||
), | ||
Effect.catchAll(_ => Effect.succeed(null)) | ||
) | ||
|
||
return Effect.repeat( | ||
fetcherPipeline, | ||
Schedule.addDelay(Schedule.repeatForever, () => refetchInterval) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Data, Effect, Option } from "effect" | ||
import { | ||
createPublicClient, | ||
createWalletClient, | ||
http, | ||
custom, | ||
type CreatePublicClientErrorType, | ||
type CreateWalletClientErrorType | ||
} from "viem" | ||
import { getConnectorClient, type GetConnectorClientErrorType } from "@wagmi/core" | ||
import { wagmiConfig } from "$lib/wallet/evm/wagmi-config" | ||
import { | ||
CreatePublicClientError, | ||
CreateWalletClientError, | ||
ConnectorClientError | ||
} from "../transfer/errors.ts" | ||
import type { Chain } from "$lib/schema/chain.ts" | ||
|
||
export class NoViemChainError extends Data.TaggedError("NoViemChain")<{ | ||
chain: Chain | ||
}> {} | ||
|
||
export const getPublicClient = (chain: Chain) => | ||
Effect.gen(function* () { | ||
const viemChain = chain.toViemChain() | ||
|
||
if (Option.isNone(viemChain)) { | ||
return yield* new NoViemChainError({ chain }) | ||
} | ||
|
||
const client = yield* Effect.try({ | ||
try: () => | ||
createPublicClient({ | ||
chain: viemChain.value, | ||
transport: http() | ||
}), | ||
catch: err => new CreatePublicClientError({ cause: err as CreatePublicClientErrorType }) | ||
}) | ||
return client | ||
}) | ||
|
||
export const getWalletClient = (chain: Chain) => | ||
Effect.gen(function* () { | ||
const viemChain = chain.toViemChain() | ||
|
||
if (Option.isNone(viemChain)) { | ||
return yield* new NoViemChainError({ chain }) | ||
} | ||
|
||
const connectorClient = yield* Effect.tryPromise({ | ||
try: () => getConnectorClient(wagmiConfig), | ||
catch: err => new ConnectorClientError({ cause: err as GetConnectorClientErrorType }) | ||
}) | ||
|
||
return yield* Effect.try({ | ||
try: () => | ||
createWalletClient({ | ||
chain: viemChain.value, | ||
transport: custom(connectorClient.transport) | ||
}), | ||
catch: err => new CreateWalletClientError({ cause: err as CreateWalletClientErrorType }) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.