Skip to content

Commit

Permalink
File-based caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Destiner committed Apr 24, 2024
1 parent b26a722 commit f8d5f6d
Show file tree
Hide file tree
Showing 7 changed files with 1,310 additions and 36 deletions.
2 changes: 1 addition & 1 deletion api/og.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ImageResponse } from '@vercel/og';
import React from 'react';
import { isAddress } from 'viem';

import addresses from '../addresses.json';
import addresses from '../data/addresses.json';

export const config = {
runtime: 'edge',
Expand Down
File renamed without changes.
1,219 changes: 1,219 additions & 0 deletions data/cache.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"cache": "bun scripts/cache.ts",
"lint": "bun run lint:prettier && bun run lint:eslint && bun run lint:stylelint",
"lint:prettier": "prettier -c \"src/**/*.{json,js,ts,vue}\"",
"lint:eslint": "eslint \"src/**/*.{js,ts,vue}\"",
Expand Down
62 changes: 28 additions & 34 deletions pages/contract/[address].vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,27 @@
</template>

<script setup lang="ts">
import { type Address, type Hex, createPublicClient, http } from 'viem';
import { keccak256, type Address, type Hex } from 'viem';
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import addresses from '@/addresses.json';
import BlockInfo from '@/components/contract/BlockInfo.vue';
import type { Status } from '@/components/contract/BlockStatus.vue';
import ButtonCopy from '@/components/contract/ButtonCopy.vue';
import ChainList from '@/components/contract/ChainList.vue';
import {
type Chain,
CHAINS,
getChainData,
getChainEndpointUrl,
} from '@/utils/chains';
import addresses from '@/data/addresses.json';
import cache from '@/data/cache.json';
import { type Chain, CHAINS, getCode as getChainCode } from '@/utils/chains';
const route = useRoute();
const address = computed(() => route.params.address as Address);
const address = computed(
() => (route.params.address as Address).toLowerCase() as Address,
);
const label = computed(
() =>
(addresses as Record<Address, string | undefined>)[
address.value.toLowerCase() as Address
] || 'Contract',
(addresses as Record<Address, string | undefined>)[address.value] ||
'Contract',
);
// eslint-disable-next-line no-undef
Expand Down Expand Up @@ -78,27 +75,24 @@ watch(
},
);
async function getChainCode(chain: Chain): Promise<Hex | null | undefined> {
const endpointUrl = getChainEndpointUrl(chain);
if (!endpointUrl) {
return undefined;
async function getCodeHash(chain: Chain): Promise<Hex | null> {
const cachedCodeHash =
(cache as Record<Address, Partial<Record<Chain, Hex>>>)[address.value]?.[
chain
] || null;
if (cachedCodeHash) {
return cachedCodeHash;
}
const chainClient = createPublicClient({
chain: getChainData(chain),
transport: http(endpointUrl),
});
try {
const code = await chainClient.getBytecode({
address: address.value,
});
return code || null;
} catch {
return undefined;
const code = await getChainCode(chain, address.value);
if (code) {
console.log(chain, code.slice(0, 16), keccak256(code));
return keccak256(code);
}
return null;
}
async function fetchCode(): Promise<void> {
let referenceBytecode: Hex | null = null;
let referenceCodeHash: Hex | null = null;
// Split chains into batches to query contract code in parallel
const batchSize = 10;
const batchedChains: Chain[][] = [];
Expand All @@ -111,15 +105,15 @@ async function fetchCode(): Promise<void> {
for (const batch of batchedChains) {
await Promise.all(
batch.map(async (chain) => {
const code = await getChainCode(chain);
if (!referenceBytecode && code) {
referenceBytecode = code;
const codeHash = await getCodeHash(chain);
if (!referenceCodeHash && codeHash) {
referenceCodeHash = codeHash;
}
const status = code
? referenceBytecode === code
const status = codeHash
? referenceCodeHash === codeHash
? 'success'
: 'warning'
: code === null
: codeHash === null
? 'empty'
: 'error';
const chainIndex = chains.value.findIndex(
Expand Down
31 changes: 31 additions & 0 deletions scripts/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { keccak256, type Address, type Hex } from 'viem';

import addresses from '@/data/addresses.json';
import cache from '@/data/cache.json';
import { type Chain, CHAINS, getCode as getChainCode } from '@/utils/chains';

async function run(): Promise<void> {
const newCache: Record<Address, Partial<Record<Chain, string>>> = {};
for (const addressString in addresses) {
const address = addressString as Address;
console.log(address);
newCache[address] = {};
for (const chain of CHAINS) {
const addressCache = (
cache as Record<Address, Partial<Record<Chain, Hex>>>
)[address];
if (addressCache && addressCache[chain]) {
newCache[address as Address][chain] = addressCache[chain];
continue;
}
const code = await getChainCode(chain, address);
if (!code) {
continue;
}
newCache[address as Address][chain] = keccak256(code);
}
}
console.log(JSON.stringify(cache, null, 2));
}

run();
31 changes: 30 additions & 1 deletion utils/chains.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Address, Chain as ChainData } from 'viem';
import {
createPublicClient,
http,
type Address,
type Chain as ChainData,
type Hex,
} from 'viem';
import {
mainnet,
sepolia,
Expand Down Expand Up @@ -648,11 +654,34 @@ function getAddressExplorerUrl(chain: Chain, address: Address): string | null {
return `${defaultBlockExplorer.url}/address/${address}`;
}

async function getCode(
chain: Chain,
address: Address,
): Promise<Hex | null | undefined> {
const endpointUrl = getChainEndpointUrl(chain);
if (!endpointUrl) {
return undefined;
}
const chainClient = createPublicClient({
chain: getChainData(chain),
transport: http(endpointUrl),
});
try {
const code = await chainClient.getBytecode({
address,
});
return code || null;
} catch {
return undefined;
}
}

export {
CHAINS,
getChainEndpointUrl,
getChainName,
getChainData,
getAddressExplorerUrl,
getCode,
};
export type { Chain };

0 comments on commit f8d5f6d

Please sign in to comment.