Skip to content

Commit

Permalink
test(ECO-2862): Add missing test cases for arena processing
Browse files Browse the repository at this point in the history
  • Loading branch information
CRBl69 committed Feb 24, 2025
1 parent fad7696 commit e04400c
Showing 1 changed file with 268 additions and 36 deletions.
304 changes: 268 additions & 36 deletions src/typescript/sdk/tests/e2e/arena/general/arena.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { type Account, type UserTransactionResponse } from "@aptos-labs/ts-sdk";
import type { Ed25519Account, Account, UserTransactionResponse } from "@aptos-labs/ts-sdk";
import {
type AnyNumberString,
ARENA_MODULE_ADDRESS,
EmojicoinArena,
getAptosClient,
ONE_APT_BIGINT,
sleep,
type SymbolEmoji,
} from "../../../../src";
import { EmojicoinClient } from "../../../../src/client/emojicoin-client";
Expand All @@ -15,6 +16,7 @@ import {
type ArenaPositionModel,
type ArenaSwapModel,
fetchArenaInfo,
fetchSwapEvents,
postgrest,
stringifyJSONWithBigInts,
TableName,
Expand All @@ -33,12 +35,34 @@ import {
import { getFundedAccount } from "../../../utils/test-accounts";
import {
ONE_SECOND_MICROSECONDS,
registerAndUnlockInitialMarketsForArenaTest,
setNextMeleeDurationAndEnsureCrank,
waitUntilCurrentMeleeEnds,
} from "../utils";
import { getPublisher } from "../../../utils/helpers";

const getEmojicoinLockedDiffFromSwapRes = (
swapRes: Awaited<ReturnType<typeof EmojicoinClient.prototype.arena.swap>>,
melee: MeleeEmojiData
) => {
let emojicoin0Locked = 0n;
let emojicoin1Locked = 0n;
const swaps = swapRes.events.swapEvents;
const swap0 = swaps.find((s) => s.marketID === melee.market1.marketID)!;
const swap1 = swaps.find((s) => s.marketID === melee.market2.marketID)!;
const swap = swapRes.events.arenaSwapEvents[0];
if (swap1.isSell) {
emojicoin0Locked += swap0.netProceeds;
emojicoin1Locked -= swap1.inputAmount;
expect(swap0.netProceeds).toEqual(swap.emojicoin0Proceeds);
} else {
emojicoin0Locked -= swap0.inputAmount;
emojicoin1Locked += swap1.netProceeds;
expect(swap1.netProceeds).toEqual(swap.emojicoin1Proceeds);
}

return { emojicoin0Locked, emojicoin1Locked };
};

const PROCESSING_WAIT_TIME = 2 * 1000;
const waitForProcessor = <
T extends { version: AnyNumberString } | { response: UserTransactionResponse },
Expand Down Expand Up @@ -81,34 +105,28 @@ describe("ensures an arena correctly unfolds and the processor data is accurate"

const MELEE_DURATION = ONE_SECOND_MICROSECONDS * 10n;

const emojis: SymbolEmoji[][] = [
["♑"],
["♒"],
["♈"],
["♎"],
["♍"],
["♊"],
["♌"],
["⛎"],
["♓"],
["♐"],
["♏"],
["♉"],
];

beforeAll(
async () => {
await registerAndUnlockInitialMarketsForArenaTest();
const emojis: SymbolEmoji[][] = [
["♑"],
["♒"],
["♈"],
["♎"],
["♍"],
["♊"],
["♌"],
["⛎"],
["♓"],
["♐"],
["♏"],
["♉"],
];
for (const emoji of emojis) {
await emojicoin.register(getFundedAccount("667"), emoji);
await emojicoin.buy(getFundedAccount("667"), emoji, 100000000n);
}
await waitUntilCurrentMeleeEnds();
await setNextMeleeDurationAndEnsureCrank(MELEE_DURATION).then((res) => {
melee = res.melee;
return waitForProcessor(res);
});

return true;
melee = await fetchArenaMeleeView(1n).then(fetchMeleeEmojiData);
},
5 * 60 * 1000
);
Expand All @@ -121,17 +139,6 @@ describe("ensures an arena correctly unfolds and the processor data is accurate"
expect(res.bytecode).toBeTruthy();
});

it("verifies an arena has already started with a duration of 15 seconds", async () => {
expect(melee.view.duration).toEqual(MELEE_DURATION);
});

it("verifies an arena has started in the last 5 seconds", async () => {
const fiveSeconds = 5 * 1000;
const now = new Date().getTime();
const fiveSecondsAgo = now - fiveSeconds;
expect(melee.view.startTime.getTime()).toBeGreaterThan(fiveSecondsAgo);
});

it("verifies arena data is correctly inserted into the processor", async () => {
const arenaInfo = await fetchArenaInfo({});
expect(arenaInfo).toBeTruthy();
Expand All @@ -152,6 +159,55 @@ describe("ensures an arena correctly unfolds and the processor data is accurate"
expect(arenaInfo?.emojicoin1MarketAddress).toEqual(melee.market2.marketAddress);
});

it("verifies an arena ends with no activity and nothing bad happens", async () => {
const publisher = getPublisher();

await emojicoin.arena.setNextMeleeDuration(publisher, MELEE_DURATION);

await waitUntilCurrentMeleeEnds();

const swaps0 = await fetchSwapEvents({ marketID: melee.market1.marketID });
const swaps1 = await fetchSwapEvents({ marketID: melee.market2.marketID });

expect(swaps0).toHaveLength(0);
expect(swaps1).toHaveLength(0);

const enterResponse = await emojicoin.arena.enter(
getFundedAccount("007"),
1n * 10n ** 8n,
false,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis,
"symbol1"
);
await waitForProcessor(enterResponse);

await emojicoin.buy(publisher, melee.market1.symbolEmojis, 1n);
await emojicoin.buy(publisher, melee.market2.symbolEmojis, 1n);

const leaderboard: ArenaLeaderboardHistoryModel[] | null = await postgrest
.from(TableName.ArenaLeaderboardHistory)
.select("*")
.eq("melee_id", melee.view.meleeID.toString())
.then((r) => r.data)
.then((r) => (r === null ? null : r.map(toArenaLeaderboardHistoryModel)));

expect(leaderboard).not.toBeNull();
expect(leaderboard).toHaveLength(0);
}, 20000);

it("verifies an arena has already started with a duration of 15 seconds", async () => {
melee = await fetchArenaMeleeView(2n).then(fetchMeleeEmojiData);
expect(melee.view.duration).toEqual(MELEE_DURATION);
});

it("verifies an arena has started in the last 5 seconds", async () => {
const fiveSeconds = 5 * 1000;
const now = new Date().getTime();
const fiveSecondsAgo = now - fiveSeconds;
expect(melee.view.startTime.getTime()).toBeGreaterThan(fiveSecondsAgo);
});

it("verifies enter+swap+exit procedure", async () => {
const account = getFundedAccount("007");
const enterResponse = await emojicoin.arena.enter(
Expand Down Expand Up @@ -310,7 +366,7 @@ describe("ensures an arena correctly unfolds and the processor data is accurate"
expect(position.withdrawals).toBeLessThanOrEqual((withdrawalsApt * 10001n) / 10000n);
expect(position.emojicoin0Balance).toEqual(0n);
expect(position.emojicoin1Balance).toEqual(0n);
});
}, 30000);
});

describe("ensures leaderboard history is working", () => {
Expand Down Expand Up @@ -497,3 +553,179 @@ describe("ensures leaderboard history is working", () => {
expect(leaderboard3.lastExit0).toBeNull();
}, 15000);
});

describe("ensures arena info is working", () => {
const emojicoin = new EmojicoinClient();

let melee: MeleeEmojiData;

const MELEE_DURATION = ONE_SECOND_MICROSECONDS * 15n;

const publisher = getPublisher();

// Utility function to avoid repetitive code. Only the `account` and `escrowCoin` differs.
const enterHelper = (account: Account, escrowCoin: "symbol1" | "symbol2") =>
emojicoin.arena.enter(
account,
ONE_APT_BIGINT,
false,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis,
escrowCoin
);

beforeAll(async () => {
await waitUntilCurrentMeleeEnds();
await setNextMeleeDurationAndEnsureCrank(MELEE_DURATION).then((res) => {
melee = res.melee;
return waitForEmojicoinIndexer(res.version, PROCESSING_WAIT_TIME);
});
}, 30000);

beforeEach(async () => {
await waitUntilCurrentMeleeEnds();
// Crank the melee to end it and start a new one.
const res = await emojicoin.arena.enter(
publisher,
1n,
false,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis,
"symbol1"
);
melee = await fetchArenaMeleeView(res.arena.event.meleeID).then(fetchMeleeEmojiData);
await waitForProcessor(res);

return true;
}, 30000);

it("verifies that arena info data is correct 1", async () => {
const account1 = getFundedAccount("420");
const account2 = getFundedAccount("421");
const account3 = getFundedAccount("422");

let volume = 0n;

const account1EnterRes = await enterHelper(account1, "symbol1");
const account2EnterRes = await enterHelper(account2, "symbol1");
const account3EnterRes = await enterHelper(account3, "symbol1");

volume += account1EnterRes.events.arenaEnterEvents[0].quoteVolume;
volume += account2EnterRes.events.arenaEnterEvents[0].quoteVolume;
volume += account3EnterRes.events.arenaEnterEvents[0].quoteVolume;

await emojicoin.arena.exit(account1, melee.market1.symbolEmojis, melee.market2.symbolEmojis);
const swapRes = await emojicoin.arena.swap(
account2,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis
);
volume += swapRes.events.arenaSwapEvents[0].quoteVolume;
const res = await emojicoin.arena.exit(
account2,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis
);
await waitForProcessor(res);

const arenaInfo = await fetchArenaInfo({});

expect(arenaInfo).toBeTruthy();
expect(arenaInfo!.meleeID).toEqual(melee.view.meleeID);
expect(arenaInfo!.duration).toEqual(melee.view.duration);
expect(arenaInfo!.startTime).toEqual(melee.view.startTime);
expect(arenaInfo!.maxMatchAmount).toEqual(melee.view.maxMatchAmount);
expect(arenaInfo!.maxMatchPercentage).toEqual(melee.view.maxMatchPercentage);
expect(arenaInfo!.volume).toEqual(volume);
expect(arenaInfo!.emojicoin0Locked).toEqual(
account3EnterRes.events.arenaEnterEvents[0].emojicoin0Proceeds
);
expect(arenaInfo!.emojicoin1Locked).toEqual(0n);
}, 30000);

it("verifies that arena info data is correct 2", async () => {
let volume = 0n;
let emojicoin0Locked = 0n;
let emojicoin1Locked = 0n;

const accounts = [
getFundedAccount("420"),
getFundedAccount("421"),
getFundedAccount("422"),
getFundedAccount("423"),
getFundedAccount("424"),
getFundedAccount("425"),
getFundedAccount("426"),
getFundedAccount("427"),
];

for (const account of accounts.map((a, i) => [a, i] as [Ed25519Account, number])) {
const enterRes = await enterHelper(
account[0],
`symbol${(account[1] % 2) + 1}` as "symbol1" | "symbol2"
);
volume += enterRes.events.arenaEnterEvents[0].quoteVolume;
emojicoin0Locked += enterRes.events.arenaEnterEvents[0].emojicoin0Proceeds;
emojicoin1Locked += enterRes.events.arenaEnterEvents[0].emojicoin1Proceeds;
}

for (const account of accounts) {
const swapRes = await emojicoin.arena.swap(
account,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis
);
volume += swapRes.events.arenaSwapEvents[0].quoteVolume;
const diff = getEmojicoinLockedDiffFromSwapRes(swapRes, melee);
emojicoin0Locked += diff.emojicoin0Locked;
emojicoin1Locked += diff.emojicoin1Locked;
}

for (const account of accounts.slice(0, Math.floor(accounts.length / 2))) {
const swapRes = await emojicoin.arena.swap(
account,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis
);
volume += swapRes.events.arenaSwapEvents[0].quoteVolume;
const diff = getEmojicoinLockedDiffFromSwapRes(swapRes, melee);
emojicoin0Locked += diff.emojicoin0Locked;
emojicoin1Locked += diff.emojicoin1Locked;
}

for (const account of accounts.slice(Math.floor(accounts.length / 2))) {
const exitRes = await emojicoin.arena.exit(
account,
melee.market1.symbolEmojis,
melee.market2.symbolEmojis
);
emojicoin0Locked -= exitRes.events.arenaExitEvents[0].emojicoin0Proceeds;
emojicoin1Locked -= exitRes.events.arenaExitEvents[0].emojicoin1Proceeds;
}

await sleep(1000);

const arenaPositions: ArenaPositionModel[] | null = await postgrest
.from(TableName.ArenaPosition)
.select("*")
.eq("melee_id", melee.view.meleeID)
.then((r) => r.data)
.then((r) => (r === null ? null : r.map(toArenaPositionModel)));

expect(arenaPositions).not.toBeNull();
expect(arenaPositions!.reduce((p, c) => p + c.emojicoin0Balance, 0n)).toEqual(emojicoin0Locked);
expect(arenaPositions!.reduce((p, c) => p + c.emojicoin1Balance, 0n)).toEqual(emojicoin1Locked);

const arenaInfo = await fetchArenaInfo({});

expect(arenaInfo).toBeTruthy();
expect(arenaInfo!.meleeID).toEqual(melee.view.meleeID);
expect(arenaInfo!.duration).toEqual(melee.view.duration);
expect(arenaInfo!.startTime).toEqual(melee.view.startTime);
expect(arenaInfo!.maxMatchAmount).toEqual(melee.view.maxMatchAmount);
expect(arenaInfo!.maxMatchPercentage).toEqual(melee.view.maxMatchPercentage);
expect(arenaInfo!.volume).toEqual(volume);
expect(arenaInfo!.emojicoin0Locked).toEqual(emojicoin0Locked);
expect(arenaInfo!.emojicoin1Locked).toEqual(emojicoin1Locked);
}, 30000);
});

0 comments on commit e04400c

Please sign in to comment.