Skip to content

Commit

Permalink
Merge pull request #1210 from ponder-sh/accounts
Browse files Browse the repository at this point in the history
accounts + native transfers
  • Loading branch information
kyscott18 authored Nov 26, 2024
2 parents 1d4b2da + 1e249e2 commit 7a83bb2
Show file tree
Hide file tree
Showing 57 changed files with 8,142 additions and 3,766 deletions.
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"fixed": [["@ponder/core", "create-ponder", "eslint-config-ponder"]],
"ignore": [
"@ponder/common",
"ponder-examples-accounts",
"ponder-examples-feature-blocks",
"ponder-examples-feature-factory",
"ponder-examples-feature-filter",
Expand Down
5 changes: 5 additions & 0 deletions examples/feature-accounts/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Mainnet RPC URL used for fetching blockchain data. Alchemy is recommended.
PONDER_RPC_URL_1=https://eth-mainnet.g.alchemy.com/v2/...

# (Optional) Postgres database URL. If not provided, SQLite will be used.
DATABASE_URL=
3 changes: 3 additions & 0 deletions examples/feature-accounts/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "ponder"
}
18 changes: 18 additions & 0 deletions examples/feature-accounts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Dependencies
/node_modules

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# Misc
.DS_Store

# Env files
.env*.local

# Ponder
/generated/
/.ponder/
27 changes: 27 additions & 0 deletions examples/feature-accounts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "ponder-examples-feature-accounts",
"private": true,
"type": "module",
"scripts": {
"dev": "ponder dev",
"start": "ponder start",
"codegen": "ponder codegen",
"serve": "ponder serve",
"lint": "eslint .",
"typecheck": "tsc"
},
"dependencies": {
"@ponder/core": "workspace:*",
"hono": "^4.5.0",
"viem": "^2.21.3"
},
"devDependencies": {
"@types/node": "^20.10.0",
"eslint": "^8.54.0",
"eslint-config-ponder": "workspace:*",
"typescript": "^5.3.2"
},
"engines": {
"node": ">=18.14"
}
}
27 changes: 27 additions & 0 deletions examples/feature-accounts/ponder-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This file enables type checking and editor autocomplete for this Ponder project.
// After upgrading, you may find that changes have been made to this file.
// If this happens, please commit the changes. Do not manually edit this file.
// See https://ponder.sh/docs/getting-started/installation#typescript for more information.

declare module "@/generated" {
import type { Virtual } from "@ponder/core";

type config = typeof import("./ponder.config.ts").default;
type schema = typeof import("./ponder.schema.ts");

export const ponder: Virtual.Registry<config, schema>;

export type EventNames = Virtual.EventNames<config>;
export type Event<name extends EventNames = EventNames> = Virtual.Event<
config,
name
>;
export type Context<name extends EventNames = EventNames> = Virtual.Context<
config,
schema,
name
>;
export type ApiContext = Virtual.ApiContext<schema>;
export type IndexingFunctionArgs<name extends EventNames = EventNames> =
Virtual.IndexingFunctionArgs<config, schema, name>;
}
22 changes: 22 additions & 0 deletions examples/feature-accounts/ponder.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createConfig } from "@ponder/core";
import { http, createPublicClient } from "viem";

const latestBlockMainnet = await createPublicClient({
transport: http(process.env.PONDER_RPC_URL_1),
}).getBlock();

export default createConfig({
networks: {
mainnet: {
chainId: 1,
transport: http(process.env.PONDER_RPC_URL_1),
},
},
accounts: {
BeaverBuilder: {
network: "mainnet",
startBlock: Number(latestBlockMainnet.number) - 100,
address: "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5",
},
},
});
7 changes: 7 additions & 0 deletions examples/feature-accounts/ponder.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { onchainTable } from "@ponder/core";

export const transactionEvents = onchainTable("transaction_events", (t) => ({
to: t.hex().primaryKey(),
value: t.bigint().notNull(),
data: t.hex().notNull(),
}));
22 changes: 22 additions & 0 deletions examples/feature-accounts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ponder } from "@/generated";
import * as schema from "../ponder.schema";

ponder.on("BeaverBuilder:transaction:from", async ({ event, context }) => {
if (event.transaction.to === null) return;

await context.db
.insert(schema.transactionEvents)
.values({
to: event.transaction.to,
value: event.transaction.value,
data: event.transaction.input,
})
.onConflictDoUpdate((row) => ({
value: row.value + event.transaction.value,
data: event.transaction.input,
}));
});

ponder.on("BeaverBuilder:transfer:to", async ({ event }) => {
console.log("sent", event.transfer);
});
26 changes: 26 additions & 0 deletions examples/feature-accounts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
// Type checking
"strict": true,
"noUncheckedIndexedAccess": true,

// Interop constraints
"verbatimModuleSyntax": false,
"esModuleInterop": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,

// Language and environment
"moduleResolution": "bundler",
"module": "ESNext",
"noEmit": true,
"lib": ["ES2022"],
"target": "ES2022",

// Skip type checking for node modules
"skipLibCheck": true
},
"include": ["./**/*.ts"],
"exclude": ["node_modules"]
}
6 changes: 0 additions & 6 deletions packages/core/src/_test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,3 @@ export const ACCOUNTS = [

// Named accounts
export const [ALICE, BOB] = ACCOUNTS;

// Deployed contract addresses.
export const CONTRACTS = {
erc20Address: "0x5fbdb2315678afecb367f032d93f642f64180aa3",
factoryAddress: "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
} as const;
53 changes: 25 additions & 28 deletions packages/core/src/_test/e2e/erc20/erc20.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import path from "node:path";
import { ALICE, BOB } from "@/_test/constants.js";
import { ALICE } from "@/_test/constants.js";
import {
setupAnvil,
setupCommon,
setupIsolatedDatabase,
} from "@/_test/setup.js";
import { simulate } from "@/_test/simulate.js";
import { deployErc20, mintErc20 } from "@/_test/simulate.js";
import {
getFreePort,
postGraphql,
waitForIndexedBlock,
} from "@/_test/utils.js";
import { serve } from "@/bin/commands/serve.js";
import { start } from "@/bin/commands/start.js";
import { range } from "@/utils/range.js";
import { rimrafSync } from "rimraf";
import { zeroAddress } from "viem";
import { parseEther, zeroAddress } from "viem";
import { beforeEach, describe, expect, test } from "vitest";

const rootDir = path.join(".", "src", "_test", "e2e", "erc20");
Expand All @@ -35,7 +34,7 @@ const cliOptions = {
logFormat: "pretty",
};

test("erc20", async (context) => {
test("erc20", async () => {
const port = await getFreePort();

const cleanup = await start({
Expand All @@ -46,12 +45,16 @@ test("erc20", async (context) => {
},
});

await simulate({
erc20Address: context.erc20.address,
factoryAddress: context.factory.address,
const { address } = await deployErc20({ sender: ALICE });

await mintErc20({
erc20: address,
to: ALICE,
amount: parseEther("1"),
sender: ALICE,
});

await waitForIndexedBlock(port, "mainnet", 8);
await waitForIndexedBlock(port, "mainnet", 2);

const response = await postGraphql(
port,
Expand All @@ -67,19 +70,16 @@ test("erc20", async (context) => {

expect(response.status).toBe(200);
const body = (await response.json()) as any;

expect(body.errors).toBe(undefined);
const accounts = body.data.accounts.items;
expect(accounts[0]).toMatchObject({
address: zeroAddress,
balance: (-2 * 10 ** 18).toString(),
balance: (-1 * 10 ** 18).toString(),
});
expect(accounts[1]).toMatchObject({
address: BOB.toLowerCase(),
balance: (2 * 10 ** 18).toString(),
});
expect(accounts[2]).toMatchObject({
address: ALICE.toLowerCase(),
balance: "0",
balance: (10 ** 18).toString(),
});

await cleanup();
Expand All @@ -89,7 +89,7 @@ const isPglite = !!process.env.DATABASE_URL;

// Fix this once it's easier to have per-command kill functions in Ponder.ts.
describe.skipIf(isPglite)("postgres database", () => {
test.todo("ponder serve", async (context) => {
test.todo("ponder serve", async () => {
const startPort = await getFreePort();

const cleanupStart = await start({
Expand All @@ -100,13 +100,14 @@ describe.skipIf(isPglite)("postgres database", () => {
},
});

for (const _ in range(0, 3)) {
await simulate({
erc20Address: context.erc20.address,
factoryAddress: context.factory.address,
});
}
const { address } = await deployErc20({ sender: ALICE });

await mintErc20({
erc20: address,
to: ALICE,
amount: parseEther("1"),
sender: ALICE,
});
const servePort = await getFreePort();

const cleanupServe = await serve({
Expand Down Expand Up @@ -137,15 +138,11 @@ describe.skipIf(isPglite)("postgres database", () => {
expect(accounts).toHaveLength(3);
expect(accounts[0]).toMatchObject({
address: zeroAddress,
balance: (-4 * 10 ** 18).toString(),
balance: (-1 * 10 ** 18).toString(),
});
expect(accounts[1]).toMatchObject({
address: BOB.toLowerCase(),
balance: (4 * 10 ** 18).toString(),
});
expect(accounts[2]).toMatchObject({
address: ALICE.toLowerCase(),
balance: "0",
balance: (10 ** 18).toString(),
});

await cleanupServe();
Expand Down
8 changes: 1 addition & 7 deletions packages/core/src/_test/e2e/erc20/ponder.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { http } from "viem";

import { createConfig } from "../../../config/config.js";
import { CONTRACTS } from "../../constants.js";
import { erc20ABI } from "../../generated.js";

const poolId = Number(process.env.VITEST_POOL_ID ?? 1);
Expand Down Expand Up @@ -29,11 +27,7 @@ export default createConfig({
Erc20: {
network: "mainnet",
abi: erc20ABI,
address: CONTRACTS.erc20Address,
filter: {
event:
"Transfer(address indexed from, address indexed to, uint256 amount)",
},
address: "0x5fbdb2315678afecb367f032d93f642f64180aa3",
},
},
});
33 changes: 27 additions & 6 deletions packages/core/src/_test/e2e/factory/factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
setupCommon,
setupIsolatedDatabase,
} from "@/_test/setup.js";
import { simulatePairSwap } from "@/_test/simulate.js";
import { deployFactory } from "@/_test/simulate.js";
import { createPair } from "@/_test/simulate.js";
import { swapPair } from "@/_test/simulate.js";
import {
getFreePort,
postGraphql,
Expand All @@ -32,7 +34,7 @@ const cliOptions = {
logFormat: "pretty",
};

test("factory", async (context) => {
test("factory", async () => {
const port = await getFreePort();

const cleanup = await start({
Expand All @@ -43,7 +45,20 @@ test("factory", async (context) => {
},
});

await waitForIndexedBlock(port, "mainnet", 5);
const { address } = await deployFactory({ sender: ALICE });
const { result: pair } = await createPair({
factory: address,
sender: ALICE,
});
await swapPair({
pair,
amount0Out: 1n,
amount1Out: 1n,
to: ALICE,
sender: ALICE,
});

await waitForIndexedBlock(port, "mainnet", 3);

let response = await postGraphql(
port,
Expand All @@ -69,12 +84,18 @@ test("factory", async (context) => {
id: expect.any(String),
from: ALICE.toLowerCase(),
to: ALICE.toLowerCase(),
pair: context.factory.pair.toLowerCase(),
pair,
});

await simulatePairSwap(context.factory.pair);
await swapPair({
pair,
amount0Out: 1n,
amount1Out: 1n,
to: ALICE,
sender: ALICE,
});

await waitForIndexedBlock(port, "mainnet", 6);
await waitForIndexedBlock(port, "mainnet", 4);

response = await postGraphql(
port,
Expand Down
Loading

0 comments on commit 7a83bb2

Please sign in to comment.