From fe0b4473a5e9f947176a19147444227ee146520f Mon Sep 17 00:00:00 2001 From: jtgi Date: Sat, 13 Jul 2024 07:07:24 +0900 Subject: [PATCH] rate limit --- app/lib/bullish.server.ts | 7 +++++++ app/lib/validations.server.ts | 11 ++++++----- app/lib/viem.server.ts | 11 ++++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/lib/bullish.server.ts b/app/lib/bullish.server.ts index c9a88ef..adbff16 100644 --- a/app/lib/bullish.server.ts +++ b/app/lib/bullish.server.ts @@ -37,6 +37,13 @@ export const openRankLimiter = new Bottleneck({ connection: bottleneckConnection, }); +export const nodeRpcLimiter = new Bottleneck({ + maxConcurrent: 10, + datastore: "ioredis", + clearDatastore: false, + connection: bottleneckConnection, +}); + export const webhookQueue = new Queue("webhookQueue", { connection, }); diff --git a/app/lib/validations.server.ts b/app/lib/validations.server.ts index 6a5eeed..1dae58b 100644 --- a/app/lib/validations.server.ts +++ b/app/lib/validations.server.ts @@ -37,7 +37,7 @@ import { db } from "./db.server"; import { Cast, CastId } from "@neynar/nodejs-sdk/build/neynar-api/v2"; import axios from "axios"; import { UnrecoverableError } from "bullmq"; -import { openRankLimiter } from "./bullish.server"; +import { nodeRpcLimiter, openRankLimiter } from "./bullish.server"; export type RuleDefinition = { name: RuleName; @@ -1890,7 +1890,7 @@ export async function holdsErc721(args: CheckFunctionArgs) { isOwner = await getSetCache({ key: `erc721-owner:${contractAddress}:${tokenId}`, get: async () => { - const owner = await contract.read.ownerOf([BigInt(tokenId)]); + const owner = await nodeRpcLimiter.schedule(() => contract.read.ownerOf([BigInt(tokenId)])); return [cast.author.custody_address, ...cast.author.verifications].some( (address) => address.toLowerCase() === owner.toLowerCase() ); @@ -1901,7 +1901,7 @@ export async function holdsErc721(args: CheckFunctionArgs) { for (const address of [cast.author.custody_address, ...cast.author.verifications]) { const balance = await getSetCache({ key: `erc721-balance:${contractAddress}:${address}`, - get: () => contract.read.balanceOf([getAddress(address)]), + get: () => nodeRpcLimiter.schedule(() => contract.read.balanceOf([getAddress(address)])), ttlSeconds: 60 * 60 * 2, }); @@ -1997,7 +1997,8 @@ export async function holdsErc1155(args: CheckFunctionArgs) { for (const address of [cast.author.custody_address, ...cast.author.verifications]) { const balance = await getSetCache({ key: `erc1155-${contractAddress}-${address}-${tokenId}`, - get: () => contract.read.balanceOf([getAddress(address), BigInt(tokenId)]), + get: () => + nodeRpcLimiter.schedule(() => contract.read.balanceOf([getAddress(address), BigInt(tokenId)])), ttlSeconds: 60 * 60 * 2, }); @@ -2068,7 +2069,7 @@ export async function verifyErc20Balance({ }); const balances = (await Promise.all( - wallets.map((add) => contract.read.balanceOf([getAddress(add)])) + wallets.map((add) => nodeRpcLimiter.schedule(() => contract.read.balanceOf([getAddress(add)]))) )) as bigint[]; const decimals = await contract.read.decimals(); const minBalanceBigInt = parseUnits(minBalanceRequired ?? "0", decimals); diff --git a/app/lib/viem.server.ts b/app/lib/viem.server.ts index cb2bea2..dca4ee0 100644 --- a/app/lib/viem.server.ts +++ b/app/lib/viem.server.ts @@ -3,7 +3,16 @@ import { arbitrum, base, mainnet, zora, optimism } from "viem/chains"; const mainnetClient = createPublicClient({ chain: mainnet, - transport: http(`https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`), + transport: fallback( + [ + http(`https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`), + http(`https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`), + ], + { + retryCount: 3, + retryDelay: 2000, + } + ), }); const optimismClient = createPublicClient({