Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0085579
Minting claim actions
prxt6529 Mar 13, 2026
0cf1ed2
WIP
prxt6529 Mar 13, 2026
a7e67ce
WIP
prxt6529 Mar 13, 2026
adf20ba
WIP
prxt6529 Mar 13, 2026
8e872ab
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 13, 2026
73eb8fb
WIP
prxt6529 Mar 13, 2026
bb5c4d7
WIP
prxt6529 Mar 13, 2026
4ed4ef7
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 13, 2026
0bd2114
WIP - auto select phase
prxt6529 Mar 16, 2026
2ff8757
WIP
prxt6529 Mar 16, 2026
ef718f8
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 16, 2026
cb3d24f
WIP
prxt6529 Mar 16, 2026
9a4dddc
WIP
prxt6529 Mar 16, 2026
a6c7617
WIP
prxt6529 Mar 16, 2026
ebbe1ce
WIP
prxt6529 Mar 16, 2026
8da72cf
WIP
prxt6529 Mar 16, 2026
c7a10eb
WIP
prxt6529 Mar 16, 2026
5cf5f21
WIP
prxt6529 Mar 16, 2026
dde5e47
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 16, 2026
465d9cf
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 17, 2026
1bc006d
WIP
prxt6529 Mar 17, 2026
8f06e72
WIP
prxt6529 Mar 17, 2026
73d794a
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 17, 2026
29bd6c0
WIP
prxt6529 Mar 17, 2026
5e6c4d7
WIP
prxt6529 Mar 17, 2026
55a10e3
WIP
prxt6529 Mar 17, 2026
08230d0
WIP
prxt6529 Mar 17, 2026
74a5260
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 17, 2026
3deb8e0
WIP
prxt6529 Mar 17, 2026
669d810
WIP
prxt6529 Mar 17, 2026
ce401dc
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 17, 2026
b39de5c
Merge branch 'main' into minting-claims-actions
prxt6529 Mar 17, 2026
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { getAutoSelectedLaunchPhase } from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers";

describe("getAutoSelectedLaunchPhase", () => {
const phases = [
{
key: "phase0" as const,
schedule: {
startMs: 1_000,
endMs: 2_000,
},
},
{
key: "phase1" as const,
schedule: {
startMs: 3_000,
endMs: 4_000,
},
},
{
key: "phase2" as const,
schedule: {
startMs: 5_000,
endMs: 6_000,
},
},
{
key: "publicphase" as const,
schedule: {
startMs: 7_000,
endMs: 8_000,
},
},
];

it("returns blank when metadata is not published", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: false,
isInitialized: true,
nowMs: 1_500,
phases,
})
).toBe("");
});

it("keeps phase0 selected until the claim is initialized", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: false,
nowMs: 7_500,
phases,
})
).toBe("phase0");
});

it("selects phase0 before it starts and while it is active", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 500,
phases,
})
).toBe("phase0");

expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 1_500,
phases,
})
).toBe("phase0");
});

it("moves to the next upcoming phase after a phase ends", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 2_500,
phases,
})
).toBe("phase1");

expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 4_500,
phases,
})
).toBe("phase2");
});

it("selects public phase until it ends, then research", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 7_500,
phases,
})
).toBe("publicphase");

expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 8_001,
phases,
})
).toBe("research");
});

it("does not fall back to phase0 when phase0's schedule is null but later phases still exist", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 8_001,
phases: phases.map((phase, index) =>
index === 0 ? { ...phase, schedule: null } : phase
),
})
).toBe("research");
});

it("ignores a null schedule on a later phase when an earlier valid phase still matches", () => {
expect(
getAutoSelectedLaunchPhase({
hasPublishedMetadata: true,
isInitialized: true,
nowMs: 3_500,
phases: phases.map((phase, index) =>
index === 2 ? { ...phase, schedule: null } : phase
),
})
).toBe("phase1");
});
});
139 changes: 139 additions & 0 deletions __tests__/services/api/memes-minting-claims-api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { MEMES_CONTRACT } from "@/constants/constants";
import {
getDistributionAirdropsArtist,
getDistributionAirdropsTeam,
getMemesMintingClaimActions,
getMemesMintingClaimActionTypes,
upsertMemesMintingClaimAction,
} from "@/services/api/memes-minting-claims-api";
import { getAuthJwt, getStagingAuth } from "@/services/auth/auth.utils";

jest.mock("@/services/auth/auth.utils", () => ({
getStagingAuth: jest.fn(),
getAuthJwt: jest.fn(),
}));

const fetchMock = jest.fn() as jest.MockedFunction<typeof fetch>;
globalThis.fetch = fetchMock;

function mockFetchOk<T>(body: T): Response {
return {
ok: true,
json: async () => body,
} as Response;
}

describe("memes-minting-claims-api", () => {
const encodedContract = encodeURIComponent(MEMES_CONTRACT);

beforeEach(() => {
jest.clearAllMocks();
fetchMock.mockReset();
(getStagingAuth as jest.Mock).mockReturnValue(null);
(getAuthJwt as jest.Mock).mockReturnValue(null);
});

it("fetches supported MEMES claim action types", async () => {
fetchMock.mockResolvedValue(
mockFetchOk({
contract: MEMES_CONTRACT,
action_types: ["ARTIST_AIRDROP", "TEAM_AIRDROP"],
})
);

const response = await getMemesMintingClaimActionTypes();

expect(fetchMock).toHaveBeenCalledWith(
`https://api.test.6529.io/api/minting-claims/actions/${encodedContract}/types`,
expect.objectContaining({
method: "GET",
})
);
expect(response.action_types).toEqual(["ARTIST_AIRDROP", "TEAM_AIRDROP"]);
});

it("fetches MEMES claim actions by claim id", async () => {
fetchMock.mockResolvedValue(
mockFetchOk({
contract: MEMES_CONTRACT,
claim_id: 123,
actions: [{ action: "ARTIST_AIRDROP", completed: false }],
})
);

const response = await getMemesMintingClaimActions(123);
const [firstAction] = response.actions;

expect(fetchMock).toHaveBeenCalledWith(
`https://api.test.6529.io/api/minting-claims/actions/${encodedContract}/123`,
expect.objectContaining({
method: "GET",
})
);
expect(response.actions).toHaveLength(1);
expect(firstAction?.action).toBe("ARTIST_AIRDROP");
});

it("upserts a MEMES claim action", async () => {
(getAuthJwt as jest.Mock).mockReturnValue("jwt");
fetchMock.mockResolvedValue(
mockFetchOk({
contract: MEMES_CONTRACT,
claim_id: 123,
actions: [{ action: "ARTIST_AIRDROP", completed: true }],
})
);

const response = await upsertMemesMintingClaimAction(123, {
action: "ARTIST_AIRDROP",
completed: true,
});
const [firstAction] = response.actions;

expect(fetchMock).toHaveBeenCalledWith(
`https://api.test.6529.io/api/minting-claims/actions/${encodedContract}/123`,
expect.objectContaining({
method: "POST",
headers: expect.objectContaining({
Authorization: "Bearer jwt",
"Content-Type": "application/json",
}),
body: JSON.stringify({
action: "ARTIST_AIRDROP",
completed: true,
}),
})
);
expect(firstAction?.completed).toBe(true);
});

it("fetches artist airdrops from the split json endpoint", async () => {
fetchMock.mockResolvedValue(mockFetchOk([{ wallet: "0xabc", amount: 2 }]));

const response = await getDistributionAirdropsArtist(MEMES_CONTRACT, 123);

expect(fetchMock).toHaveBeenCalledWith(
`https://api.test.6529.io/api/distributions/${MEMES_CONTRACT}/123/artist-airdrops`,
expect.objectContaining({
method: "GET",
headers: {},
})
);
expect(response).toEqual([{ wallet: "0xabc", amount: 2 }]);
});

it("fetches team airdrops from the split json endpoint", async () => {
fetchMock.mockResolvedValue(mockFetchOk([{ wallet: "0xdef", amount: 1 }]));

const response = await getDistributionAirdropsTeam(MEMES_CONTRACT, 123);

expect(fetchMock).toHaveBeenCalledWith(
`https://api.test.6529.io/api/distributions/${MEMES_CONTRACT}/123/team-airdrops`,
expect.objectContaining({
method: "GET",
headers: {},
})
);
expect(response).toEqual([{ wallet: "0xdef", amount: 1 }]);
});
});
Loading
Loading