From 7958c505b44e024c8aeade4370f6fad94fd3141e Mon Sep 17 00:00:00 2001 From: KONFeature Date: Fri, 5 Jul 2024 11:16:24 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Load=20balanced=20RPC=20+?= =?UTF-8?q?=20index=20+=20better=20reward=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ponder.config.ts | 32 +++++--- ponder.schema.ts | 171 ++++++++++++++++++++++++++---------------- src/campaignReward.ts | 34 +++++++-- sst.config.ts | 2 +- 4 files changed, 155 insertions(+), 84 deletions(-) diff --git a/ponder.config.ts b/ponder.config.ts index 182490a..71533f8 100644 --- a/ponder.config.ts +++ b/ponder.config.ts @@ -1,5 +1,5 @@ -import { createConfig, mergeAbis } from "@ponder/core"; -import { http, fallback, parseAbiItem } from "viem"; +import { createConfig, loadBalance, mergeAbis, rateLimit } from "@ponder/core"; +import { http, parseAbiItem } from "viem"; import { interactionCampaignAbi, referralCampaignAbi, @@ -28,18 +28,21 @@ export default createConfig({ // Testnets arbitrumSepolia: { chainId: 421614, - transport: fallback([ - http( - `https://arbitrum-sepolia.blockpi.network/v1/rpc/${process.env.BLOCKPI_API_KEY_ARB_SEPOLIA}` + transport: loadBalance([ + rateLimit( + http( + `https://arbitrum-sepolia.blockpi.network/v1/rpc/${process.env.BLOCKPI_API_KEY_ARB_SEPOLIA}` + ), + { requestsPerSecond: 20, browser: false } ), - http( - `https://arb-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}` + rateLimit( + http( + `https://arb-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}` + ), + { requestsPerSecond: 20, browser: false } ), - http(), - ], { - // Huge retry delay to avoid spamming - retryDelay: 600 - }), + rateLimit(http(), { requestsPerSecond: 5, browser: false }), + ]), ...pollingConfig, }, }, @@ -51,6 +54,7 @@ export default createConfig({ network: { arbitrumSepolia: { startBlock: 35765963, + maxBlockRange: 5000, }, /*arbitrum: { startBlock: 203956680, @@ -73,6 +77,7 @@ export default createConfig({ network: { arbitrumSepolia: { startBlock: 54321880, + maxBlockRange: 5000, }, }, }, @@ -83,6 +88,7 @@ export default createConfig({ network: { arbitrumSepolia: { startBlock: 60118981, + maxBlockRange: 5000, }, }, }, @@ -103,6 +109,7 @@ export default createConfig({ network: { arbitrumSepolia: { startBlock: 60118981, + maxBlockRange: 5000, }, }, }, @@ -117,6 +124,7 @@ export default createConfig({ network: { arbitrumSepolia: { startBlock: 60118981, + maxBlockRange: 5000, }, }, }, diff --git a/ponder.schema.ts b/ponder.schema.ts index 8998dbe..4a29cf3 100644 --- a/ponder.schema.ts +++ b/ponder.schema.ts @@ -117,78 +117,119 @@ export default createSchema((p) => ({ capResets: p.many("CampaignCapReset.campaignId"), }), - CampaignCapReset: p.createTable({ - id: p.string(), // campaign address + timestamp - - campaignId: p.hex().references("Campaign.id"), - campaign: p.one("campaignId"), - - timestamp: p.bigint(), - previousTimestamp: p.bigint(), - distributedAmount: p.bigint(), - }), + CampaignCapReset: p.createTable( + { + id: p.string(), // campaign address + timestamp + + campaignId: p.hex().references("Campaign.id"), + campaign: p.one("campaignId"), + + timestamp: p.bigint(), + previousTimestamp: p.bigint(), + distributedAmount: p.bigint(), + }, + { + campaignIndex: p.index("campaignId"), + } + ), // Press events - PressEvent: p.createTable({ - id: p.string(), + PressEvent: p.createTable( + { + id: p.string(), - interactionId: p.hex().references("ContentInteractionContract.id"), - interaction: p.one("interactionId"), + interactionId: p.hex().references("ContentInteractionContract.id"), + interaction: p.one("interactionId"), - user: p.hex(), - type: p.enum("PressEventType"), - data: p.json(), + user: p.hex(), + type: p.enum("PressEventType"), + data: p.json(), - timestamp: p.bigint(), - }), + timestamp: p.bigint(), + }, + { + interactionIndex: p.index("interactionId"), + userIndex: p.index("user"), + + userInteractionIndex: p.index(["user", "interactionId"]), + } + ), PressEventType: p.createEnum(["OPEN_ARTICLE", "READ_ARTICLE", "REFERRED"]), // Rewards related stuff - RewardingContract: p.createTable({ - // Address of the rewarding contract - id: p.hex(), - - // Address of the token that will be distributed - token: p.hex(), - - // All the rewards - rewards: p.many("Reward.contractId"), - }), - Reward: p.createTable({ - id: p.string(), // reward contract + user - - contractId: p.hex().references("RewardingContract.id"), - contract: p.one("contractId"), - - user: p.hex(), - - pendingAmount: p.bigint(), - totalAmount: p.bigint(), - - rewardAddedEvents: p.many("RewardAddedEvent.rewardId"), - rewardClaimedEvents: p.many("RewardClaimedEvent.rewardId"), - }), - RewardAddedEvent: p.createTable({ - id: p.string(), - - rewardId: p.string().references("Reward.id"), - reward: p.one("rewardId"), - - amount: p.bigint(), - - txHash: p.hex(), - timestamp: p.bigint(), - }), - RewardClaimedEvent: p.createTable({ - id: p.string(), - - rewardId: p.string().references("Reward.id"), - reward: p.one("rewardId"), - - amount: p.bigint(), - - txHash: p.hex(), - timestamp: p.bigint(), - }), + RewardingContract: p.createTable( + { + // Address of the rewarding contract + id: p.hex(), + + // Address of the token that will be distributed + token: p.hex(), + + // The total amount distributed and claimed + totalDistributed: p.bigint(), + totalClaimed: p.bigint(), + + // All the rewards + rewards: p.many("Reward.contractId"), + }, + { + tokenIndex: p.index("token"), + } + ), + Reward: p.createTable( + { + id: p.string(), // reward contract + user + + contractId: p.hex().references("RewardingContract.id"), + contract: p.one("contractId"), + + user: p.hex(), + + pendingAmount: p.bigint(), + totalReceived: p.bigint(), + totalClaimed: p.bigint(), + + rewardAddedEvents: p.many("RewardAddedEvent.rewardId"), + rewardClaimedEvents: p.many("RewardClaimedEvent.rewardId"), + }, + { + userIndex: p.index("user"), + contractIndex: p.index("contractId"), + + userContractIndex: p.index(["user", "contractId"]), + } + ), + RewardAddedEvent: p.createTable( + { + id: p.string(), + + rewardId: p.string().references("Reward.id"), + reward: p.one("rewardId"), + + amount: p.bigint(), + + txHash: p.hex(), + timestamp: p.bigint(), + }, + { + rewardIndex: p.index("rewardId"), + } + ), + RewardClaimedEvent: p.createTable( + { + id: p.string(), + + rewardId: p.string().references("Reward.id"), + reward: p.one("rewardId"), + + amount: p.bigint(), + + txHash: p.hex(), + timestamp: p.bigint(), + }, + { + rewardIndex: p.index("rewardId"), + } + ), })); diff --git a/src/campaignReward.ts b/src/campaignReward.ts index c204c80..ed4af8b 100644 --- a/src/campaignReward.ts +++ b/src/campaignReward.ts @@ -3,7 +3,7 @@ import type { Address } from "viem"; import { referralCampaignAbi } from "../abis/frak-campaign-abis"; ponder.on("Campaigns:RewardAdded", async ({ event, context }) => { - const { Reward, RewardAddedEvent } = context.db; + const { RewardingContract, Reward, RewardAddedEvent } = context.db; // Try to find a rewarding contract for the given event emitter const rewardingContract = await getRewardingContract({ @@ -11,6 +11,15 @@ ponder.on("Campaigns:RewardAdded", async ({ event, context }) => { context, }); + // Update rewarding contract + await RewardingContract.update({ + id: rewardingContract.id, + data: { + totalDistributed: + rewardingContract.totalDistributed + event.args.amount, + }, + }); + // Update the current user reward (insert it if not found) const rewardId = `${event.log.address}-${event.args.user}`; await Reward.upsert({ @@ -19,11 +28,12 @@ ponder.on("Campaigns:RewardAdded", async ({ event, context }) => { contractId: rewardingContract.id, user: event.args.user, pendingAmount: event.args.amount, - totalAmount: event.args.amount, + totalReceived: event.args.amount, + totalClaimed: 0n, }, update: ({ current }) => ({ pendingAmount: current.pendingAmount + event.args.amount, - totalAmount: current.totalAmount + event.args.amount, + totalReceived: current.totalReceived + event.args.amount, }), }); @@ -40,7 +50,7 @@ ponder.on("Campaigns:RewardAdded", async ({ event, context }) => { }); ponder.on("Campaigns:RewardClaimed", async ({ event, context }) => { - const { Reward, RewardClaimedEvent } = context.db; + const { RewardingContract, Reward, RewardClaimedEvent } = context.db; // Try to find a rewarding contract for the given event emitter const rewardingContract = await getRewardingContract({ @@ -48,6 +58,14 @@ ponder.on("Campaigns:RewardClaimed", async ({ event, context }) => { context, }); + // Update rewarding contract + await RewardingContract.update({ + id: rewardingContract.id, + data: { + totalClaimed: rewardingContract.totalClaimed + event.args.amount, + }, + }); + // Update the current user reward (insert it if not found) const rewardId = `${event.log.address}-${event.args.user}`; await Reward.upsert({ @@ -55,11 +73,13 @@ ponder.on("Campaigns:RewardClaimed", async ({ event, context }) => { create: { contractId: rewardingContract.id, user: event.args.user, - pendingAmount: -event.args.amount, - totalAmount: 0n, + totalClaimed: event.args.amount, + totalReceived: 0n, + pendingAmount: 0n, }, update: ({ current }) => ({ pendingAmount: current.pendingAmount - event.args.amount, + totalClaimed: current.totalClaimed + event.args.amount, }), }); @@ -104,6 +124,8 @@ async function getRewardingContract({ id: contract, data: { token, + totalDistributed: 0n, + totalClaimed: 0n, }, }); } diff --git a/sst.config.ts b/sst.config.ts index faa317d..340d92c 100644 --- a/sst.config.ts +++ b/sst.config.ts @@ -46,7 +46,7 @@ function IndexerStack({ app, stack }: StackContext) { // BlockPi rpcs new Config.Secret(stack, "BLOCKPI_API_KEY_ARB_SEPOLIA"), // Alchemy RPC - new Config.Secret(stack, "ALCHEMY_API_KEY") + new Config.Secret(stack, "ALCHEMY_API_KEY"), ]; // Get our CDK secrets map