Skip to content

Commit

Permalink
🗃️ Also index data related to tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
KONFeature committed Aug 27, 2024
1 parent 2819002 commit 25ef61e
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 46 deletions.
8 changes: 4 additions & 4 deletions iac/builder/Ponder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const ponderInstanceTypeConfig = {
hardware: {
cpu: "0.5 vCPU",
memory: "1 GB",
storage: "20 GB"
} as const
storage: "20 GB",
} as const,
},
reading: {
suffix: "IndexerReader",
Expand All @@ -65,8 +65,8 @@ export const ponderInstanceTypeConfig = {
hardware: {
cpu: "0.25 vCPU",
memory: "0.5 GB",
storage: "10 GB"
} as const
storage: "10 GB",
} as const,
},
};

Expand Down
92 changes: 56 additions & 36 deletions packages/ponder/ponder.schema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createSchema } from "@ponder/core";

export default createSchema((p) => ({
// Content related stuff
/* -------------------------------------------------------------------------- */
/* Content related stuff */
/* -------------------------------------------------------------------------- */

Content: p.createTable({
id: p.bigint(),

Expand Down Expand Up @@ -31,7 +34,10 @@ export default createSchema((p) => ({
createdTimestamp: p.bigint(),
}),

// Interaction related
/* -------------------------------------------------------------------------- */
/* Interaction related stuff */
/* -------------------------------------------------------------------------- */

ContentInteractionContract: p.createTable({
id: p.hex(), // address

Expand All @@ -45,7 +51,40 @@ export default createSchema((p) => ({
removedTimestamp: p.bigint().optional(),
}),

// Campaign related
InteractionEvent: p.createTable(
{
id: p.string(),

interactionId: p.hex().references("ContentInteractionContract.id"),
interaction: p.one("interactionId"),

user: p.hex(),
type: p.enum("InteractionEventType"),
data: p.json().optional(),

timestamp: p.bigint(),
},
{
interactionIndex: p.index("interactionId"),
userIndex: p.index("user"),

userInteractionIndex: p.index(["user", "interactionId"]),
}
),

InteractionEventType: p.createEnum([
// Referral type
"REFERRED",
"CREATE_REFERRAL_LINK",
// Press type
"OPEN_ARTICLE",
"READ_ARTICLE",
]),

/* -------------------------------------------------------------------------- */
/* Campaign related stuff */
/* -------------------------------------------------------------------------- */

Campaign: p.createTable(
{
id: p.hex(),
Expand Down Expand Up @@ -103,45 +142,26 @@ export default createSchema((p) => ({
}
),

// Interaction events
InteractionEvent: p.createTable(
{
id: p.string(),

interactionId: p.hex().references("ContentInteractionContract.id"),
interaction: p.one("interactionId"),

user: p.hex(),
type: p.enum("InteractionEventType"),
data: p.json().optional(),

timestamp: p.bigint(),
},
{
interactionIndex: p.index("interactionId"),
userIndex: p.index("user"),

userInteractionIndex: p.index(["user", "interactionId"]),
}
),

InteractionEventType: p.createEnum([
// Referral type
"REFERRED",
"CREATE_REFERRAL_LINK",
// Press type
"OPEN_ARTICLE",
"READ_ARTICLE",
]),
/* -------------------------------------------------------------------------- */
/* Rewards related stuff */
/* -------------------------------------------------------------------------- */
Token: p.createTable({
// Address of the token contract
id: p.hex(),

// Rewards related stuff
// Token information
decimals: p.int(),
name: p.string(),
symbol: p.string(),
}),
RewardingContract: p.createTable(
{
// Address of the rewarding contract
id: p.hex(),

// Address of the token that will be distributed
token: p.hex(),
tokenId: p.hex().references("Token.id"),
token: p.one("tokenId"),

// The total amount distributed and claimed
totalDistributed: p.bigint(),
Expand All @@ -151,7 +171,7 @@ export default createSchema((p) => ({
rewards: p.many("Reward.contractId"),
},
{
tokenIndex: p.index("token"),
tokenIndex: p.index("tokenId"),
}
),
Reward: p.createTable(
Expand Down
41 changes: 38 additions & 3 deletions packages/ponder/src/api/rewards.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ponder } from "@/generated";
import { and, desc, eq, like, not } from "@ponder/core";
import { type Address, isAddress } from "viem";
import { getTokens } from "./tokens";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unreachable code error
Expand All @@ -19,20 +20,34 @@ ponder.get("/rewards/:wallet", async (ctx) => {
}

// Get the tables we will query
const { Reward } = ctx.tables;
const { Reward, RewardingContract } = ctx.tables;

// Perform the sql query
const rewards = await ctx.db
.select({
amount: Reward.pendingAmount,
address: Reward.contractId,
token: RewardingContract.tokenId,
})
.from(Reward)
.innerJoin(
RewardingContract,
eq(RewardingContract.id, Reward.contractId)
)
.where(and(eq(Reward.user, wallet), not(eq(Reward.pendingAmount, 0n))))
.orderBy(desc(Reward.pendingAmount));

// Get all the tokens for the rewards
const tokens = await getTokens({
addresses: rewards.map((r) => r.token),
ctx,
});

// Return the result as json
return ctx.json(rewards);
return ctx.json({
rewards,
tokens,
});
});

/**
Expand All @@ -48,16 +63,23 @@ ponder.get("/rewards/:wallet/history", async (ctx) => {
const walletfilter = `%${wallet}`;

// Get the tables we will query
const { RewardAddedEvent, RewardClaimedEvent } = ctx.tables;
const { RewardAddedEvent, RewardClaimedEvent, RewardingContract, Reward } =
ctx.tables;

// Perform the sql query
const rewardAddedPromise = ctx.db
.select({
amount: RewardAddedEvent.amount,
txHash: RewardAddedEvent.txHash,
timestamp: RewardAddedEvent.timestamp,
token: RewardingContract.tokenId,
})
.from(RewardAddedEvent)
.innerJoin(Reward, eq(Reward.id, RewardAddedEvent.rewardId))
.innerJoin(
RewardingContract,
eq(RewardingContract.id, Reward.contractId)
)
.where(like(RewardAddedEvent.rewardId, walletfilter))
.limit(100)
.orderBy(desc(RewardAddedEvent.timestamp));
Expand All @@ -68,8 +90,14 @@ ponder.get("/rewards/:wallet/history", async (ctx) => {
amount: RewardClaimedEvent.amount,
txHash: RewardClaimedEvent.txHash,
timestamp: RewardClaimedEvent.timestamp,
token: RewardingContract.tokenId,
})
.from(RewardClaimedEvent)
.innerJoin(Reward, eq(Reward.id, RewardClaimedEvent.rewardId))
.innerJoin(
RewardingContract,
eq(RewardingContract.id, Reward.contractId)
)
.where(like(RewardClaimedEvent.rewardId, walletfilter))
.limit(100)
.orderBy(desc(RewardClaimedEvent.timestamp));
Expand All @@ -79,9 +107,16 @@ ponder.get("/rewards/:wallet/history", async (ctx) => {
rewardClaimedPromise,
]);

// Get all the tokens for the rewards events
const tokens = await getTokens({
addresses: [...rewardAdded, ...rewardClaimed].map((r) => r.token),
ctx,
});

// Return the result as json
return ctx.json({
added: rewardAdded,
claimed: rewardClaimed,
tokens,
});
});
60 changes: 60 additions & 0 deletions packages/ponder/src/api/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { type ApiContext, ponder } from "@/generated";
import { eq, inArray } from "@ponder/core";
import { type Address, isAddress } from "viem";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unreachable code error
BigInt.prototype.toJSON = function (): string {
return this.toString();
};

/**
* Get a tokens information by its address
*/
ponder.get("/tokens/:address", async (ctx) => {
// Extract token address
const address = ctx.req.param("address") as Address;
if (!isAddress(address)) {
return ctx.text("Invalid token address address", 400);
}

// Get the tables we will query
const { Token } = ctx.tables;

// Perform the sql query
const rewards = await ctx.db
.select({
address: Token.id,
name: Token.name,
symbol: Token.symbol,
decimals: Token.decimals,
})
.from(Token)
.where(eq(Token.id, address));

// Return the result as json
return ctx.json(rewards);
});

/**
* Get all the tokens information
*/
export async function getTokens({
addresses,
ctx,
}: { addresses: readonly Address[]; ctx: ApiContext }) {
// Convert the addresses to a set
const addressSet = new Set(addresses);

// Find all the tokens
const { Token } = ctx.tables;
return await ctx.db
.select({
address: Token.id,
name: Token.name,
symbol: Token.symbol,
decimals: Token.decimals,
})
.from(Token)
.where(inArray(Token.id, [...addressSet]));
}
46 changes: 43 additions & 3 deletions packages/ponder/src/campaign/campaignReward.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Context, ponder } from "@/generated";
import type { Address } from "viem";
import { type Address, erc20Abi } from "viem";
import { referralCampaignAbi } from "../../abis/frak-campaign-abis";

ponder.on("Campaigns:RewardAdded", async ({ event, context }) => {
Expand Down Expand Up @@ -129,7 +129,7 @@ async function getRewardingContract({
contract: Address;
context: Context;
}) {
const { RewardingContract } = context.db;
const { RewardingContract, Token } = context.db;
// Try to find a rewarding contract for the given event emitter
let rewardingContract = await RewardingContract.findUnique({
id: contract,
Expand All @@ -145,12 +145,52 @@ async function getRewardingContract({
rewardingContract = await RewardingContract.create({
id: contract,
data: {
token,
tokenId: token,
totalDistributed: 0n,
totalClaimed: 0n,
},
});
}

// Create the token if needed
const token = await Token.findUnique({ id: rewardingContract.tokenId });
if (!token) {
try {
// Fetch a few onchain data
const [name, symbol, decimals] = await context.client.multicall({
contracts: [
{
abi: erc20Abi,
functionName: "name",
address: rewardingContract.tokenId,
},
{
abi: erc20Abi,
functionName: "symbol",
address: rewardingContract.tokenId,
},
{
abi: erc20Abi,
functionName: "decimals",
address: rewardingContract.tokenId,
},
] as const,
allowFailure: false,
});

// Create the token
await Token.create({
id: rewardingContract.tokenId,
data: {
name,
symbol,
decimals,
},
});
} catch (e) {
console.error(e, "Unable to fetch token data");
}
}

return rewardingContract;
}

0 comments on commit 25ef61e

Please sign in to comment.