Skip to content

Commit

Permalink
[ECO-2709] Add aggregate market state script (#527)
Browse files Browse the repository at this point in the history
  • Loading branch information
xbtmatt authored and alnoki committed Feb 4, 2025
1 parent 7b49464 commit b82426a
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 7 deletions.
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

0 comments on commit b82426a

Please sign in to comment.