diff --git a/apps/scan/public/avalanche.png b/apps/scan/public/avalanche.png new file mode 100644 index 000000000..caedb14e0 Binary files /dev/null and b/apps/scan/public/avalanche.png differ diff --git a/apps/scan/src/types/chain.ts b/apps/scan/src/types/chain.ts index 17704477e..d6152d73d 100644 --- a/apps/scan/src/types/chain.ts +++ b/apps/scan/src/types/chain.ts @@ -1,19 +1,25 @@ -import { base, optimism, polygon } from 'wagmi/chains'; +import { avalanche, base, optimism, polygon } from 'wagmi/chains'; export enum Chain { BASE = 'base', SOLANA = 'solana', POLYGON = 'polygon', OPTIMISM = 'optimism', + AVALANCHE = 'avalanche', } -export const SUPPORTED_CHAINS = Object.values([Chain.BASE, Chain.SOLANA]); +export const SUPPORTED_CHAINS = Object.values([ + Chain.BASE, + Chain.SOLANA, + Chain.AVALANCHE, +]); export const CHAIN_LABELS: Record = { [Chain.BASE]: 'Base', [Chain.SOLANA]: 'Solana', [Chain.POLYGON]: 'Polygon', [Chain.OPTIMISM]: 'Optimism', + [Chain.AVALANCHE]: 'Avalanche', }; export const CHAIN_ICONS: Record = { @@ -21,6 +27,7 @@ export const CHAIN_ICONS: Record = { [Chain.SOLANA]: '/solana.png', [Chain.POLYGON]: '/polygon.png', [Chain.OPTIMISM]: '/optimism.png', + [Chain.AVALANCHE]: '/avalanche.png', }; export const CHAIN_ID: Record = { @@ -28,4 +35,5 @@ export const CHAIN_ID: Record = { [Chain.POLYGON]: polygon.id, [Chain.OPTIMISM]: optimism.id, [Chain.SOLANA]: 0, + [Chain.AVALANCHE]: avalanche.id, }; diff --git a/sync/transfers/trigger/chains/evm/avalanche/bitquery/config.ts b/sync/transfers/trigger/chains/evm/avalanche/bitquery/config.ts new file mode 100644 index 000000000..dbdd01c34 --- /dev/null +++ b/sync/transfers/trigger/chains/evm/avalanche/bitquery/config.ts @@ -0,0 +1,23 @@ +import { ONE_MINUTE_IN_SECONDS } from '@/trigger/lib/constants'; +import { FACILITATORS_BY_CHAIN } from '@/trigger/lib/facilitators'; +import { + Network, + PaginationStrategy, + QueryProvider, + SyncConfig, +} from '@/trigger/types'; +import { buildQuery, transformResponse } from './query'; + +export const avalancheChainConfig: SyncConfig = { + cron: '*/30 * * * *', + maxDurationInSeconds: ONE_MINUTE_IN_SECONDS * 10, + chain: 'base', + provider: QueryProvider.BITQUERY, + paginationStrategy: PaginationStrategy.OFFSET, + limit: 100_000, + facilitators: FACILITATORS_BY_CHAIN(Network.AVALANCHE), + buildQuery, + transformResponse, + enabled: true, + machine: 'medium-1x', +}; diff --git a/sync/transfers/trigger/chains/evm/avalanche/bitquery/query.ts b/sync/transfers/trigger/chains/evm/avalanche/bitquery/query.ts new file mode 100644 index 000000000..9e23a8dc3 --- /dev/null +++ b/sync/transfers/trigger/chains/evm/avalanche/bitquery/query.ts @@ -0,0 +1,77 @@ +import { USDC_MULTIPLIER } from '@/trigger/lib/constants'; +import { + Facilitator, + FacilitatorConfig, + SyncConfig, + TransferEventData, +} from '@/trigger/types'; + +export function buildQuery( + config: SyncConfig, + facilitatorConfig: FacilitatorConfig, + since: Date, + now: Date, + offset?: number +): string { + return ` + { + ethereum(network: ${config.chain}) { + transfers( + currency: {is: "${facilitatorConfig.token.address}"} + options: {limit: ${config.limit}, offset: ${offset || 0}} + date: {since: "${since.toISOString()}", till: "${now.toISOString()}"} + txFrom: {is: "${facilitatorConfig.address}"} + ) { + sender { + address + } + receiver { + address + } + currency { + name + symbol + address + } + amount + block { + timestamp { + time + } + height + } + transaction { + hash + txFrom { + address + } + } + } + } + } + `; +} + +export function transformResponse( + data: unknown, + config: SyncConfig, + facilitator: Facilitator, + facilitatorConfig: FacilitatorConfig +): TransferEventData[] { + const response = data as { ethereum: { transfers: any[] } }; + + return response.ethereum.transfers.map((transfer: any) => ({ + address: transfer.currency?.address, + transaction_from: + transfer.transaction?.txFrom?.address || transfer.sender.address, + sender: transfer.sender.address, + recipient: transfer.receiver.address, + amount: Math.round(parseFloat(transfer.amount) * USDC_MULTIPLIER), + block_timestamp: new Date(transfer.block.timestamp.time), + tx_hash: transfer.transaction.hash, + chain: config.chain, + provider: config.provider, + decimals: facilitatorConfig.token.decimals, + facilitator_id: facilitator.id, + })); +} diff --git a/sync/transfers/trigger/chains/evm/avalanche/bitquery/sync.ts b/sync/transfers/trigger/chains/evm/avalanche/bitquery/sync.ts new file mode 100644 index 000000000..17e3b7307 --- /dev/null +++ b/sync/transfers/trigger/chains/evm/avalanche/bitquery/sync.ts @@ -0,0 +1,5 @@ +import { createChainSyncTask } from '../../../../sync'; +import { avalancheChainConfig } from './config'; + +export const avalancheBitquerySyncTransfers = + createChainSyncTask(avalancheChainConfig);