Skip to content

Commit 63671ee

Browse files
committed
feat: add storm assasin paid mint drop
1 parent e184bd9 commit 63671ee

1 file changed

Lines changed: 125 additions & 1 deletion

File tree

app/ClientPage.tsx

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ const INFERNO_PULSE = {
6464
kind: 101,
6565
} as const;
6666

67+
const STORM_ASSASIN = {
68+
metadataCid: "bafkreiavecfbxphf3qejwyktvh5eosa7eveqghcft4sjcrfugrxqat6wbi",
69+
imageCid: "bafybeie7ot5lpvbjkwpxnky4hzovbotim5266hsnyag7qff6ekxpeiqelm",
70+
localImagePath: "/nfts/storm-assasin.png",
71+
name: "StackUp: Storm Assasin",
72+
kind: 102,
73+
} as const;
74+
6775
type NftOverrideEntry = {
6876
name?: string;
6977
image?: string; // /public path or ipfs://...
@@ -136,6 +144,8 @@ export default function ClientPage() {
136144
>("idle");
137145
const [infernoFeeUstx, setInfernoFeeUstx] = useState<number | null>(null);
138146
const [infernoUri, setInfernoUri] = useState<string | null>(null);
147+
const [stormFeeUstx, setStormFeeUstx] = useState<number | null>(null);
148+
const [stormUri, setStormUri] = useState<string | null>(null);
139149
const [nftOverrides, setNftOverrides] = useState<NftOverrides>({
140150
byTokenId: {},
141151
byKind: {},
@@ -697,7 +707,13 @@ export default function ClientPage() {
697707
}
698708

699709
try {
700-
const [milestonesCV, infernoFeeCV, infernoUriCV] = await Promise.all([
710+
const [
711+
milestonesCV,
712+
infernoFeeCV,
713+
infernoUriCV,
714+
stormFeeCV,
715+
stormUriCV,
716+
] = await Promise.all([
701717
fetchCallReadOnlyFunction({
702718
contractAddress: CONTRACT_ADDRESS,
703719
contractName: CONTRACT_NAME,
@@ -722,6 +738,22 @@ export default function ClientPage() {
722738
network: STACKS_NETWORK_OBJ,
723739
senderAddress: caller,
724740
}),
741+
fetchCallReadOnlyFunction({
742+
contractAddress: CONTRACT_ADDRESS,
743+
contractName: CONTRACT_NAME,
744+
functionName: "get-mint-fee-kind",
745+
functionArgs: [uintCV(STORM_ASSASIN.kind)],
746+
network: STACKS_NETWORK_OBJ,
747+
senderAddress: caller,
748+
}),
749+
fetchCallReadOnlyFunction({
750+
contractAddress: CONTRACT_ADDRESS,
751+
contractName: CONTRACT_NAME,
752+
functionName: "get-badge-uri",
753+
functionArgs: [uintCV(STORM_ASSASIN.kind)],
754+
network: STACKS_NETWORK_OBJ,
755+
senderAddress: caller,
756+
}),
725757
]);
726758

727759
const ms = cvToValue(milestonesCV) as unknown;
@@ -750,6 +782,20 @@ export default function ClientPage() {
750782
const uriUnwrapped = unwrapCvToValue(cvToValue(infernoUriCV) as unknown);
751783
setInfernoUri(typeof uriUnwrapped === "string" ? uriUnwrapped : null);
752784

785+
const stormFeeUnwrapped = unwrapCvToValue(cvToValue(stormFeeCV) as unknown);
786+
const stormFee =
787+
stormFeeUnwrapped === null
788+
? null
789+
: typeof stormFeeUnwrapped === "bigint"
790+
? Number(stormFeeUnwrapped)
791+
: typeof stormFeeUnwrapped === "number"
792+
? stormFeeUnwrapped
793+
: null;
794+
setStormFeeUstx(stormFee);
795+
796+
const stormUriUnwrapped = unwrapCvToValue(cvToValue(stormUriCV) as unknown);
797+
setStormUri(typeof stormUriUnwrapped === "string" ? stormUriUnwrapped : null);
798+
753799
try {
754800
const kindsToLoad = BADGE_MILESTONES.map((m) => m.kind);
755801
const uriPairs = await Promise.all(
@@ -1138,6 +1184,45 @@ export default function ClientPage() {
11381184
}
11391185
};
11401186

1187+
const mintStormAssasin = async () => {
1188+
if (!address) {
1189+
setError("Connect wallet first.");
1190+
return;
1191+
}
1192+
if (stormFeeUstx === null) {
1193+
setError("Price not loaded yet. Click “Refresh On-Chain” and try again.");
1194+
return;
1195+
}
1196+
1197+
setError("");
1198+
setStatus("Minting Storm Assasin...");
1199+
1200+
try {
1201+
openContractCall({
1202+
contractAddress: CONTRACT_ADDRESS,
1203+
contractName: CONTRACT_NAME,
1204+
functionName: "mint-paid-kind",
1205+
functionArgs: [uintCV(STORM_ASSASIN.kind)],
1206+
// This call transfers STX as a mint fee. In Allow mode, the wallet
1207+
// doesn't require us to enumerate the exact STX movement as a post-condition.
1208+
postConditionMode: PostConditionMode.Allow,
1209+
network: STACKS_NETWORK_OBJ,
1210+
appDetails: {
1211+
name: APP_NAME,
1212+
icon: new URL(APP_ICON_PATH, window.location.origin).toString(),
1213+
},
1214+
onFinish: () => {
1215+
setStatus("Mint submitted");
1216+
scheduleRefresh(address);
1217+
},
1218+
onCancel: () => setStatus("Mint cancelled"),
1219+
});
1220+
} catch {
1221+
setError("Failed to open mint transaction.");
1222+
setStatus("Mint failed");
1223+
}
1224+
};
1225+
11411226
return (
11421227
<div className={styles.page}>
11431228
<div className={styles.shell}>
@@ -1413,6 +1498,45 @@ export default function ClientPage() {
14131498
Mint
14141499
</button>
14151500
</div>
1501+
1502+
<div className={styles.dropTile}>
1503+
<div className={styles.dropTileThumb}>
1504+
<Image
1505+
src={STORM_ASSASIN.localImagePath}
1506+
alt={STORM_ASSASIN.name}
1507+
width={520}
1508+
height={520}
1509+
unoptimized
1510+
style={{
1511+
width: "100%",
1512+
height: "100%",
1513+
objectFit: "contain",
1514+
}}
1515+
/>
1516+
</div>
1517+
<div className={styles.dropTileBody}>
1518+
<div className={styles.dropTileTitle}>{STORM_ASSASIN.name}</div>
1519+
<div className={styles.dropTileLine}>
1520+
Kind <code>u{STORM_ASSASIN.kind}</code>
1521+
{" · "}
1522+
{stormFeeUstx === null
1523+
? "Price: —"
1524+
: `Price: ${(stormFeeUstx / 1_000_000).toFixed(2)} STX`}
1525+
</div>
1526+
<div className={styles.dropTileLine}>
1527+
Metadata:{" "}
1528+
<span>{stormUri ? "Configured" : "Not configured"}</span>
1529+
</div>
1530+
</div>
1531+
<button
1532+
className={`${styles.button} ${styles.dropTileButton}`}
1533+
onClick={mintStormAssasin}
1534+
disabled={!address || !stormUri}
1535+
type="button"
1536+
>
1537+
Mint
1538+
</button>
1539+
</div>
14161540
</div>
14171541

14181542
{!address ? (

0 commit comments

Comments
 (0)