Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ECO-2709] Add aggregate market state script #527

Merged
merged 3 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"test:sdk": "pnpm --prefix src/typescript run test:sdk",
"test:sdk:e2e": "pnpm --prefix src/typescript run test:sdk:e2e",
"test:sdk:unit": "pnpm --prefix src/typescript run test:sdk:unit",
"test:verbose": "pnpm --prefix src/typescript run test:verbose"
"test:verbose": "pnpm --prefix src/typescript run test:verbose",
"verify-processor-data": "pnpm --prefix src/typescript/sdk run verify-processor-data"
}
}
3 changes: 2 additions & 1 deletion src/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"test:sdk:sequential": "pnpm run load-env:test -- turbo run test:sdk:sequential --filter @econia-labs/emojicoin-sdk --force --log-prefix none",
"test:sdk:unit": "NO_TEST_SETUP=true pnpm run load-env:test -- turbo run test:unit --filter @econia-labs/emojicoin-sdk --force --log-prefix none",
"test:unit": "NO_TEST_SETUP=true pnpm run load-env:test -- turbo run test:unit --force --log-prefix none --ui tui",
"test:verbose": "FETCH_DEBUG=true VERBOSE_TEST_LOGS=true pnpm run load-env:test -- turbo run test --force"
"test:verbose": "FETCH_DEBUG=true VERBOSE_TEST_LOGS=true pnpm run load-env:test -- turbo run test --force",
"verify-processor-data": "pnpm --prefix sdk run verify-processor-data"
},
"version": "0.0.0",
"workspaces": [
Expand Down
3 changes: 2 additions & 1 deletion src/typescript/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
"test:e2e": "pnpm run test:sdk:parallel && pnpm run test:sdk:sequential",
"test:sdk:parallel": "pnpm jest --testPathIgnorePatterns=tests/e2e/broker",
"test:sdk:sequential": "pnpm jest --runInBand tests/e2e/broker",
"test:unit": "pnpm jest tests/unit"
"test:unit": "pnpm jest tests/unit",
"verify-processor-data": "pnpm dotenv -e ../.env -- pnpm tsx --conditions=react-server src/scripts/verify-processor-data.ts"
},
"typings": "dist/index.d.ts",
"version": "0.3.0"
Expand Down
22 changes: 21 additions & 1 deletion src/typescript/sdk/src/client/emojicoin-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type InputGenerateTransactionOptions,
type WaitForTransactionOptions,
AptosConfig,
type LedgerVersionArg,
} from "@aptos-labs/ts-sdk";
import { type ChatEmoji, type SymbolEmoji } from "../emoji_data/types";
import { EmojicoinDotFun, getEvents } from "../emojicoin_dot_fun";
Expand All @@ -29,7 +30,7 @@ import { DEFAULT_REGISTER_MARKET_GAS_OPTIONS, INTEGRATOR_ADDRESS } from "../cons
import { waitFor } from "../utils";
import { postgrest } from "../indexer-v2/queries";
import { TableName } from "../indexer-v2/types/json-types";
import { toSwapEvent, type AnyNumberString } from "../types";
import { toMarketView, toRegistryView, toSwapEvent, type AnyNumberString } from "../types";

const { expect, Expect } = customExpect;

Expand Down Expand Up @@ -135,10 +136,14 @@ export class EmojicoinClient {
marketExists: typeof EmojicoinClient.prototype.isMarketRegisteredView;
simulateBuy: typeof EmojicoinClient.prototype.simulateBuy;
simulateSell: typeof EmojicoinClient.prototype.simulateSell;
registry: typeof EmojicoinClient.prototype.registryView;
market: typeof EmojicoinClient.prototype.marketView;
} = {
marketExists: this.isMarketRegisteredView.bind(this),
simulateBuy: this.simulateBuy.bind(this),
simulateSell: this.simulateSell.bind(this),
registry: this.registryView.bind(this),
market: this.marketView.bind(this),
};

private integrator: AccountAddress;
Expand Down Expand Up @@ -338,6 +343,21 @@ export class EmojicoinClient {
return typeof res.vec.pop() !== "undefined";
}

private async registryView(options?: LedgerVersionArg) {
return await EmojicoinDotFun.RegistryView.view({
aptos: this.aptos,
options,
}).then(toRegistryView);
}

private async marketView(marketAddress: AccountAddressInput, options?: LedgerVersionArg) {
return await EmojicoinDotFun.MarketView.view({
marketAddress,
aptos: this.aptos,
options,
}).then(toMarketView);
}

private async swap(
swapper: Account,
symbolEmojis: SymbolEmoji[],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { toAggregateMarketState, type DatabaseJsonType } from "../types";
import { postgrest } from "./client";

export const callAggregateMarketState = () =>
// Since this is a read-only function call, prefer to call this as a `GET` request. It makes API
// gateway authorization simpler and cleaner.
postgrest
.rpc("aggregate_market_state", {}, { get: true })
.select("*")
.single()
.then((res) => {
if (res.data) {
return res.data as DatabaseJsonType["aggregate_market_state"];
}
if (res.error) {
console.error(res.error);
}
throw new Error("RPC call to `aggregate_market_state` failed, `null` was returned.");
})
.then(toAggregateMarketState);
25 changes: 25 additions & 0 deletions src/typescript/sdk/src/indexer-v2/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ export type ArenaLeaderboardModel = ReturnType<typeof toArenaLeaderboardModel>;
export type ArenaLeaderboardHistoryModel = ReturnType<typeof toArenaLeaderboardHistoryModel>;
export type ArenaInfoModel = ReturnType<typeof toArenaInfoModel>;
export type UserPoolsRPCModel = ReturnType<typeof toUserPoolsRPCResponse>;
export type AggregateMarketStateModel = ReturnType<typeof toAggregateMarketState>;

/**
* Converts a function that converts a type to another type into a function that converts the type
Expand Down Expand Up @@ -771,6 +772,28 @@ export const toPriceFeed = (data: DatabaseJsonType["price_feed"]) => {
};
};

export const toAggregateMarketState = (data: DatabaseJsonType["aggregate_market_state"]) => ({
lastEmojicoinTransactionVersion: BigInt(data.last_emojicoin_transaction_version),
cumulativeChatMessages: BigInt(data.cumulative_chat_messages),
cumulativeIntegratorFees: BigInt(data.cumulative_integrator_fees),
cumulativeQuoteVolume: BigInt(data.cumulative_quote_volume),
cumulativeSwaps: BigInt(data.cumulative_swaps),
fullyDilutedValue: BigInt(data.fully_diluted_value),
lastBumpTime: postgresTimestampToMicroseconds(data.last_bump_time),
marketCap: BigInt(data.market_cap),
numMarkets: BigInt(data.n_markets),
nonce: BigInt(data.nonce),
totalQuoteLocked: BigInt(data.total_quote_locked),
totalValueLocked: BigInt(data.total_value_locked),
numMarketsInBondingCurve: BigInt(data.n_markets_in_bonding_curve),
numMarketsPostBondingCurve: BigInt(data.n_markets_post_bonding_curve),
numGlobalStateEvents: BigInt(data.n_global_state_events),
numMarketRegistrationEvents: BigInt(data.n_market_registration_events),
numSwapEvents: BigInt(data.n_swap_events),
numChatEvents: BigInt(data.n_chat_events),
numLiquidityEvents: BigInt(data.n_liquidity_events),
});

export const DatabaseTypeConverter = {
[TableName.GlobalStateEvents]: toGlobalStateEventModel,
[TableName.PeriodicStateEvents]: toPeriodicStateEventModel,
Expand All @@ -795,6 +818,7 @@ export const DatabaseTypeConverter = {
[TableName.ArenaLeaderboard]: toArenaLeaderboardModel,
[TableName.ArenaLeaderboardHistory]: toArenaLeaderboardHistoryModel,
[DatabaseRpc.UserPools]: toUserPoolsRPCResponse,
[DatabaseRpc.AggregateMarketState]: toAggregateMarketState,
};

export type DatabaseModels = {
Expand All @@ -821,6 +845,7 @@ export type DatabaseModels = {
[TableName.ArenaLeaderboard]: ArenaLeaderboardModel;
[TableName.ArenaLeaderboardHistory]: ArenaLeaderboardHistoryModel;
[DatabaseRpc.UserPools]: UserPoolsRPCModel;
[DatabaseRpc.AggregateMarketState]: AggregateMarketStateModel;
};

export type AnyEventTable =
Expand Down
26 changes: 24 additions & 2 deletions src/typescript/sdk/src/indexer-v2/types/json-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ export enum TableName {

export enum DatabaseRpc {
UserPools = "user_pools",
PriceFeed = "price_feed",
AggregateMarketState = "aggregate_market_state",
}

// Fields that only exist after being processed by a processor.
Expand Down Expand Up @@ -514,6 +514,27 @@ export type DatabaseJsonType = {
ProcessedFields &
UserLPCoinBalance & { daily_volume: Uint128String }
>;
[DatabaseRpc.AggregateMarketState]: Flatten<{
last_emojicoin_transaction_version: Uint64String;
cumulative_chat_messages: Uint64String;
cumulative_integrator_fees: Uint128String;
cumulative_quote_volume: Uint128String;
cumulative_swaps: Uint64String;
fully_diluted_value: Uint128String;
last_bump_time: PostgresTimestamp;
market_cap: Uint128String;
n_markets: Uint64String;
nonce: Uint64String;
total_quote_locked: Uint128String;
total_value_locked: Uint128String;
n_markets_in_bonding_curve: Uint64String;
n_markets_post_bonding_curve: Uint64String;
n_global_state_events: Uint64String;
n_market_registration_events: Uint64String;
n_swap_events: Uint64String;
n_chat_events: Uint64String;
n_liquidity_events: Uint64String;
}>;
};

type Columns = DatabaseJsonType[TableName.GlobalStateEvents] &
Expand All @@ -538,6 +559,7 @@ type Columns = DatabaseJsonType[TableName.GlobalStateEvents] &
DatabaseJsonType[TableName.ArenaInfo] &
DatabaseJsonType[TableName.ArenaLeaderboard] &
DatabaseJsonType[TableName.ArenaLeaderboardHistory] &
DatabaseJsonType[DatabaseRpc.UserPools];
DatabaseJsonType[DatabaseRpc.UserPools] &
DatabaseJsonType[DatabaseRpc.AggregateMarketState];

export type AnyColumnName = keyof Columns;
68 changes: 68 additions & 0 deletions src/typescript/sdk/src/scripts/verify-processor-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable import/no-unused-modules */
import { EmojicoinClient } from "../client/emojicoin-client";
import { callAggregateMarketState } from "../indexer-v2/queries/fetch-aggregate-market-state";

const main = async () => {
const aggregateMarketState = await callAggregateMarketState();
const emojicoin = new EmojicoinClient();
const registryState = await emojicoin.view.registry({
ledgerVersion: aggregateMarketState.lastEmojicoinTransactionVersion,
});

const totalNumDatabaseEvents = BigInt(
aggregateMarketState.numGlobalStateEvents +
aggregateMarketState.numSwapEvents +
aggregateMarketState.numLiquidityEvents +
aggregateMarketState.numChatEvents +
aggregateMarketState.numMarketRegistrationEvents
);

if (!(registryState.nonce !== totalNumDatabaseEvents)) {
console.error(`Registry state nonce doesn't match total number of database events.`);
console.error(
`Registry nonce: ${registryState.nonce}, numDatabaseEvents: ${totalNumDatabaseEvents})`
);
}

const equalValues = Object.entries(aggregateMarketState)
.map(([key, aggValue]) => {
// Skip these keys as they are checked above or not checked at all and only for viewing.
if (
[
"lastEmojicoinTransactionVersion",
// The lastBumpTime in the registry view is NOT the same as the last event bump time.
// It's the last bump time of the global registry (once per day).
// The lastBumpTime in the aggregated market state *is* the last event bump time.
// So they'll never be equal- thus, ignore.
"lastBumpTime",
"numMarketsInBondingCurve", // Only for debugging- not checked.
"numMarketsPostBondingCurve", // Only for debugging- not checked.
"numGlobalStateEvents",
"numMarketRegistrationEvents",
"numSwapEvents",
"numChatEvents",
"numLiquidityEvents",
].includes(key)
) {
return true;
}
const viewValue = registryState[key as keyof typeof registryState];
const equal = aggValue === viewValue;
if (!equal) {
console.error(`Key ${key} not equal. Database: ${aggValue} View: ${viewValue}`);
}
return equal;
})
.every((v) => v);
if (!equalValues) {
console.error(aggregateMarketState);
console.error(registryState);
throw new Error("Aggregate market state does not match the registry view on-chain.");
}
/* eslint-disable no-console */
console.info("Aggregate market state matches the registry view on-chain!");
console.dir(aggregateMarketState);
/* eslint-enable no-console */
};

main();
2 changes: 1 addition & 1 deletion src/typescript/sdk/src/types/json-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type JsonTypes = {
fully_diluted_value: AggregatorSnapshot<Uint128String>;
cumulative_integrator_fees: AggregatorSnapshot<Uint128String>;
cumulative_swaps: AggregatorSnapshot<Uint128String>;
cumulative_chat_messages: AggregatorSnapshot<Uint128String>;
cumulative_chat_messages: AggregatorSnapshot<Uint64String>;
};

// The result of the contract's `market_view` view function. NOT the database view.
Expand Down
1 change: 1 addition & 0 deletions src/typescript/sdk/tests/e2e/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "../../src/indexer-v2/types/postgres-numeric-types";

// This is not the full response type; it's just what we use in this test.
// NOTE: This does *not* cover the RPC function calls/schemas. Only tables and views.
interface DatabaseSchema {
definitions: {
[Table in TableName]: {
Expand Down
Loading