@b3dotfun/sdk 0.1.65-alpha.4 → 0.1.65-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.js +62 -50
- package/dist/cjs/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/dist/cjs/anyspend/react/components/ccShopAbi.js +63 -0
- package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +60 -48
- package/dist/esm/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/dist/esm/anyspend/react/components/ccShopAbi.js +60 -0
- package/dist/types/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +76 -51
- package/src/anyspend/react/components/ccShopAbi.ts +64 -0
|
@@ -35,46 +35,11 @@ const react_1 = require("react");
|
|
|
35
35
|
const viem_1 = require("viem");
|
|
36
36
|
const chains_1 = require("viem/chains");
|
|
37
37
|
const AnySpendCustom_1 = require("./AnySpendCustom");
|
|
38
|
+
const ccShopAbi_1 = require("./ccShopAbi");
|
|
38
39
|
// Collector Club Shop contract addresses on Base
|
|
39
40
|
const CC_SHOP_ADDRESS = "0x47366E64E4917dd4DdC04Fb9DC507c1dD2b87294";
|
|
40
41
|
const CC_SHOP_ADDRESS_STAGING = "0x8b751143342ac41eB965E55430e3F7Adf6BE01fA";
|
|
41
42
|
const BASE_CHAIN_ID = 8453;
|
|
42
|
-
// ABI for buyPacksFor function only
|
|
43
|
-
const BUY_PACKS_FOR_ABI = {
|
|
44
|
-
inputs: [
|
|
45
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
46
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
47
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
48
|
-
],
|
|
49
|
-
name: "buyPacksFor",
|
|
50
|
-
outputs: [],
|
|
51
|
-
stateMutability: "nonpayable",
|
|
52
|
-
type: "function",
|
|
53
|
-
};
|
|
54
|
-
// ABI for buyPacksForWithDiscount function (with discount code)
|
|
55
|
-
const BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
56
|
-
inputs: [
|
|
57
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
58
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
59
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
60
|
-
{ internalType: "string", name: "discountCode", type: "string" },
|
|
61
|
-
],
|
|
62
|
-
name: "buyPacksForWithDiscount",
|
|
63
|
-
outputs: [],
|
|
64
|
-
stateMutability: "nonpayable",
|
|
65
|
-
type: "function",
|
|
66
|
-
};
|
|
67
|
-
// ABI for isDiscountCodeValid view function
|
|
68
|
-
const IS_DISCOUNT_CODE_VALID_ABI = {
|
|
69
|
-
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
70
|
-
name: "isDiscountCodeValid",
|
|
71
|
-
outputs: [
|
|
72
|
-
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
73
|
-
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
74
|
-
],
|
|
75
|
-
stateMutability: "view",
|
|
76
|
-
type: "function",
|
|
77
|
-
};
|
|
78
43
|
const basePublicClient = (0, viem_1.createPublicClient)({
|
|
79
44
|
chain: chains_1.base,
|
|
80
45
|
transport: (0, viem_1.http)(constants_2.PUBLIC_BASE_RPC_URL),
|
|
@@ -95,38 +60,78 @@ function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activeTab =
|
|
|
95
60
|
const [discountInfo, setDiscountInfo] = (0, react_1.useState)({
|
|
96
61
|
isValid: false,
|
|
97
62
|
discountAmount: BigInt(0),
|
|
63
|
+
minPurchaseAmount: BigInt(0),
|
|
98
64
|
isLoading: false,
|
|
99
65
|
error: null,
|
|
100
66
|
});
|
|
101
67
|
// Validate discount code on-chain when provided
|
|
102
68
|
(0, react_1.useEffect)(() => {
|
|
103
69
|
if (!discountCode) {
|
|
104
|
-
setDiscountInfo({
|
|
70
|
+
setDiscountInfo({
|
|
71
|
+
isValid: false,
|
|
72
|
+
discountAmount: BigInt(0),
|
|
73
|
+
minPurchaseAmount: BigInt(0),
|
|
74
|
+
isLoading: false,
|
|
75
|
+
error: null,
|
|
76
|
+
});
|
|
105
77
|
return;
|
|
106
78
|
}
|
|
107
79
|
let cancelled = false;
|
|
108
80
|
const validateDiscount = async () => {
|
|
109
81
|
setDiscountInfo(prev => ({ ...prev, isLoading: true, error: null }));
|
|
110
82
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
83
|
+
// Validate against specific pack and fetch full details in parallel
|
|
84
|
+
const [validForPack, codeDetails] = await Promise.all([
|
|
85
|
+
basePublicClient.readContract({
|
|
86
|
+
address: ccShopAddress,
|
|
87
|
+
abi: [ccShopAbi_1.IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI],
|
|
88
|
+
functionName: "isDiscountCodeValidForPack",
|
|
89
|
+
args: [discountCode, BigInt(packId)],
|
|
90
|
+
}),
|
|
91
|
+
basePublicClient.readContract({
|
|
92
|
+
address: ccShopAddress,
|
|
93
|
+
abi: [ccShopAbi_1.GET_DISCOUNT_CODE_ABI],
|
|
94
|
+
functionName: "getDiscountCode",
|
|
95
|
+
args: [discountCode],
|
|
96
|
+
}),
|
|
97
|
+
]);
|
|
117
98
|
if (cancelled)
|
|
118
99
|
return;
|
|
119
|
-
const [isValid, discountAmount] =
|
|
120
|
-
|
|
100
|
+
const [isValid, discountAmount] = validForPack;
|
|
101
|
+
const { minPurchaseAmount, packId: restrictedPackId, exists } = codeDetails;
|
|
102
|
+
if (!exists) {
|
|
121
103
|
setDiscountInfo({
|
|
122
104
|
isValid: false,
|
|
123
105
|
discountAmount: BigInt(0),
|
|
106
|
+
minPurchaseAmount: BigInt(0),
|
|
124
107
|
isLoading: false,
|
|
125
|
-
error: "
|
|
108
|
+
error: "Discount code does not exist",
|
|
126
109
|
});
|
|
127
110
|
return;
|
|
128
111
|
}
|
|
129
|
-
|
|
112
|
+
if (!isValid) {
|
|
113
|
+
// Provide specific error based on code details
|
|
114
|
+
if (restrictedPackId !== BigInt(0) && restrictedPackId !== BigInt(packId)) {
|
|
115
|
+
setDiscountInfo({
|
|
116
|
+
isValid: false,
|
|
117
|
+
discountAmount: BigInt(0),
|
|
118
|
+
minPurchaseAmount: BigInt(0),
|
|
119
|
+
isLoading: false,
|
|
120
|
+
error: "Discount code is not valid for this pack",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
setDiscountInfo({
|
|
125
|
+
isValid: false,
|
|
126
|
+
discountAmount: BigInt(0),
|
|
127
|
+
minPurchaseAmount: BigInt(0),
|
|
128
|
+
isLoading: false,
|
|
129
|
+
error: "Invalid or expired discount code",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
setDiscountInfo({ isValid: true, discountAmount, minPurchaseAmount, isLoading: false, error: null });
|
|
130
135
|
}
|
|
131
136
|
catch (error) {
|
|
132
137
|
if (cancelled)
|
|
@@ -135,6 +140,7 @@ function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activeTab =
|
|
|
135
140
|
setDiscountInfo({
|
|
136
141
|
isValid: false,
|
|
137
142
|
discountAmount: BigInt(0),
|
|
143
|
+
minPurchaseAmount: BigInt(0),
|
|
138
144
|
isLoading: false,
|
|
139
145
|
error: "Failed to validate discount code",
|
|
140
146
|
});
|
|
@@ -144,7 +150,7 @@ function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activeTab =
|
|
|
144
150
|
return () => {
|
|
145
151
|
cancelled = true;
|
|
146
152
|
};
|
|
147
|
-
}, [discountCode, ccShopAddress]);
|
|
153
|
+
}, [discountCode, ccShopAddress, packId]);
|
|
148
154
|
// Calculate effective dstAmount after discount
|
|
149
155
|
const effectiveDstAmount = (0, react_1.useMemo)(() => {
|
|
150
156
|
if (!discountCode || !discountInfo.isValid || discountInfo.discountAmount === BigInt(0)) {
|
|
@@ -169,13 +175,13 @@ function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activeTab =
|
|
|
169
175
|
try {
|
|
170
176
|
if (discountCode && discountInfo.isValid) {
|
|
171
177
|
return (0, viem_1.encodeFunctionData)({
|
|
172
|
-
abi: [BUY_PACKS_FOR_WITH_DISCOUNT_ABI],
|
|
178
|
+
abi: [ccShopAbi_1.BUY_PACKS_FOR_WITH_DISCOUNT_ABI],
|
|
173
179
|
functionName: "buyPacksForWithDiscount",
|
|
174
180
|
args: [recipientAddress, BigInt(packId), BigInt(packAmount), discountCode],
|
|
175
181
|
});
|
|
176
182
|
}
|
|
177
183
|
return (0, viem_1.encodeFunctionData)({
|
|
178
|
-
abi: [BUY_PACKS_FOR_ABI],
|
|
184
|
+
abi: [ccShopAbi_1.BUY_PACKS_FOR_ABI],
|
|
179
185
|
functionName: "buyPacksFor",
|
|
180
186
|
args: [recipientAddress, BigInt(packId), BigInt(packAmount)],
|
|
181
187
|
});
|
|
@@ -194,6 +200,12 @@ function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activeTab =
|
|
|
194
200
|
if (discountCode && discountInfo.error) {
|
|
195
201
|
return ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: discountInfo.error }) }));
|
|
196
202
|
}
|
|
203
|
+
if (discountCode &&
|
|
204
|
+
discountInfo.isValid &&
|
|
205
|
+
discountInfo.minPurchaseAmount > BigInt(0) &&
|
|
206
|
+
BigInt(packAmount) < discountInfo.minPurchaseAmount) {
|
|
207
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-red-500", children: ["Minimum purchase of ", discountInfo.minPurchaseAmount.toString(), " pack", discountInfo.minPurchaseAmount > BigInt(1) ? "s" : "", " required for this discount code"] }) }));
|
|
208
|
+
}
|
|
197
209
|
if (discountCode && discountInfo.isValid && effectiveDstAmount === "0") {
|
|
198
210
|
return ((0, jsx_runtime_1.jsx)("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: "Discount exceeds total price" }) }));
|
|
199
211
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare const BUY_PACKS_FOR_ABI: {
|
|
2
|
+
readonly inputs: readonly [{
|
|
3
|
+
readonly internalType: "address";
|
|
4
|
+
readonly name: "user";
|
|
5
|
+
readonly type: "address";
|
|
6
|
+
}, {
|
|
7
|
+
readonly internalType: "uint256";
|
|
8
|
+
readonly name: "packId";
|
|
9
|
+
readonly type: "uint256";
|
|
10
|
+
}, {
|
|
11
|
+
readonly internalType: "uint256";
|
|
12
|
+
readonly name: "amount";
|
|
13
|
+
readonly type: "uint256";
|
|
14
|
+
}];
|
|
15
|
+
readonly name: "buyPacksFor";
|
|
16
|
+
readonly outputs: readonly [];
|
|
17
|
+
readonly stateMutability: "nonpayable";
|
|
18
|
+
readonly type: "function";
|
|
19
|
+
};
|
|
20
|
+
export declare const BUY_PACKS_FOR_WITH_DISCOUNT_ABI: {
|
|
21
|
+
readonly inputs: readonly [{
|
|
22
|
+
readonly internalType: "address";
|
|
23
|
+
readonly name: "user";
|
|
24
|
+
readonly type: "address";
|
|
25
|
+
}, {
|
|
26
|
+
readonly internalType: "uint256";
|
|
27
|
+
readonly name: "packId";
|
|
28
|
+
readonly type: "uint256";
|
|
29
|
+
}, {
|
|
30
|
+
readonly internalType: "uint256";
|
|
31
|
+
readonly name: "amount";
|
|
32
|
+
readonly type: "uint256";
|
|
33
|
+
}, {
|
|
34
|
+
readonly internalType: "string";
|
|
35
|
+
readonly name: "discountCode";
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
}];
|
|
38
|
+
readonly name: "buyPacksForWithDiscount";
|
|
39
|
+
readonly outputs: readonly [];
|
|
40
|
+
readonly stateMutability: "nonpayable";
|
|
41
|
+
readonly type: "function";
|
|
42
|
+
};
|
|
43
|
+
export declare const IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI: {
|
|
44
|
+
readonly inputs: readonly [{
|
|
45
|
+
readonly internalType: "string";
|
|
46
|
+
readonly name: "code";
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
}, {
|
|
49
|
+
readonly internalType: "uint256";
|
|
50
|
+
readonly name: "packId";
|
|
51
|
+
readonly type: "uint256";
|
|
52
|
+
}];
|
|
53
|
+
readonly name: "isDiscountCodeValidForPack";
|
|
54
|
+
readonly outputs: readonly [{
|
|
55
|
+
readonly internalType: "bool";
|
|
56
|
+
readonly name: "isValid";
|
|
57
|
+
readonly type: "bool";
|
|
58
|
+
}, {
|
|
59
|
+
readonly internalType: "uint256";
|
|
60
|
+
readonly name: "discountAmount";
|
|
61
|
+
readonly type: "uint256";
|
|
62
|
+
}];
|
|
63
|
+
readonly stateMutability: "view";
|
|
64
|
+
readonly type: "function";
|
|
65
|
+
};
|
|
66
|
+
export declare const GET_DISCOUNT_CODE_ABI: {
|
|
67
|
+
readonly inputs: readonly [{
|
|
68
|
+
readonly internalType: "string";
|
|
69
|
+
readonly name: "code";
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
}];
|
|
72
|
+
readonly name: "getDiscountCode";
|
|
73
|
+
readonly outputs: readonly [{
|
|
74
|
+
readonly components: readonly [{
|
|
75
|
+
readonly internalType: "uint256";
|
|
76
|
+
readonly name: "discountAmount";
|
|
77
|
+
readonly type: "uint256";
|
|
78
|
+
}, {
|
|
79
|
+
readonly internalType: "uint256";
|
|
80
|
+
readonly name: "expiresAt";
|
|
81
|
+
readonly type: "uint256";
|
|
82
|
+
}, {
|
|
83
|
+
readonly internalType: "bool";
|
|
84
|
+
readonly name: "used";
|
|
85
|
+
readonly type: "bool";
|
|
86
|
+
}, {
|
|
87
|
+
readonly internalType: "bool";
|
|
88
|
+
readonly name: "exists";
|
|
89
|
+
readonly type: "bool";
|
|
90
|
+
}, {
|
|
91
|
+
readonly internalType: "uint256";
|
|
92
|
+
readonly name: "maxUses";
|
|
93
|
+
readonly type: "uint256";
|
|
94
|
+
}, {
|
|
95
|
+
readonly internalType: "uint256";
|
|
96
|
+
readonly name: "usedCount";
|
|
97
|
+
readonly type: "uint256";
|
|
98
|
+
}, {
|
|
99
|
+
readonly internalType: "uint256";
|
|
100
|
+
readonly name: "packId";
|
|
101
|
+
readonly type: "uint256";
|
|
102
|
+
}, {
|
|
103
|
+
readonly internalType: "uint256";
|
|
104
|
+
readonly name: "minPurchaseAmount";
|
|
105
|
+
readonly type: "uint256";
|
|
106
|
+
}];
|
|
107
|
+
readonly internalType: "struct CCShop.DiscountCode";
|
|
108
|
+
readonly name: "";
|
|
109
|
+
readonly type: "tuple";
|
|
110
|
+
}];
|
|
111
|
+
readonly stateMutability: "view";
|
|
112
|
+
readonly type: "function";
|
|
113
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// CCShop contract ABI fragments used by AnySpendCollectorClubPurchase
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.GET_DISCOUNT_CODE_ABI = exports.IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI = exports.BUY_PACKS_FOR_WITH_DISCOUNT_ABI = exports.BUY_PACKS_FOR_ABI = void 0;
|
|
5
|
+
exports.BUY_PACKS_FOR_ABI = {
|
|
6
|
+
inputs: [
|
|
7
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
8
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
9
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
10
|
+
],
|
|
11
|
+
name: "buyPacksFor",
|
|
12
|
+
outputs: [],
|
|
13
|
+
stateMutability: "nonpayable",
|
|
14
|
+
type: "function",
|
|
15
|
+
};
|
|
16
|
+
exports.BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
17
|
+
inputs: [
|
|
18
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
19
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
20
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
21
|
+
{ internalType: "string", name: "discountCode", type: "string" },
|
|
22
|
+
],
|
|
23
|
+
name: "buyPacksForWithDiscount",
|
|
24
|
+
outputs: [],
|
|
25
|
+
stateMutability: "nonpayable",
|
|
26
|
+
type: "function",
|
|
27
|
+
};
|
|
28
|
+
exports.IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI = {
|
|
29
|
+
inputs: [
|
|
30
|
+
{ internalType: "string", name: "code", type: "string" },
|
|
31
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
32
|
+
],
|
|
33
|
+
name: "isDiscountCodeValidForPack",
|
|
34
|
+
outputs: [
|
|
35
|
+
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
36
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
37
|
+
],
|
|
38
|
+
stateMutability: "view",
|
|
39
|
+
type: "function",
|
|
40
|
+
};
|
|
41
|
+
exports.GET_DISCOUNT_CODE_ABI = {
|
|
42
|
+
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
43
|
+
name: "getDiscountCode",
|
|
44
|
+
outputs: [
|
|
45
|
+
{
|
|
46
|
+
components: [
|
|
47
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
48
|
+
{ internalType: "uint256", name: "expiresAt", type: "uint256" },
|
|
49
|
+
{ internalType: "bool", name: "used", type: "bool" },
|
|
50
|
+
{ internalType: "bool", name: "exists", type: "bool" },
|
|
51
|
+
{ internalType: "uint256", name: "maxUses", type: "uint256" },
|
|
52
|
+
{ internalType: "uint256", name: "usedCount", type: "uint256" },
|
|
53
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
54
|
+
{ internalType: "uint256", name: "minPurchaseAmount", type: "uint256" },
|
|
55
|
+
],
|
|
56
|
+
internalType: "struct CCShop.DiscountCode",
|
|
57
|
+
name: "",
|
|
58
|
+
type: "tuple",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
stateMutability: "view",
|
|
62
|
+
type: "function",
|
|
63
|
+
};
|
|
@@ -32,46 +32,11 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
32
32
|
import { createPublicClient, encodeFunctionData, http } from "viem";
|
|
33
33
|
import { base } from "viem/chains";
|
|
34
34
|
import { AnySpendCustom } from "./AnySpendCustom.js";
|
|
35
|
+
import { BUY_PACKS_FOR_ABI, BUY_PACKS_FOR_WITH_DISCOUNT_ABI, GET_DISCOUNT_CODE_ABI, IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI, } from "./ccShopAbi.js";
|
|
35
36
|
// Collector Club Shop contract addresses on Base
|
|
36
37
|
const CC_SHOP_ADDRESS = "0x47366E64E4917dd4DdC04Fb9DC507c1dD2b87294";
|
|
37
38
|
const CC_SHOP_ADDRESS_STAGING = "0x8b751143342ac41eB965E55430e3F7Adf6BE01fA";
|
|
38
39
|
const BASE_CHAIN_ID = 8453;
|
|
39
|
-
// ABI for buyPacksFor function only
|
|
40
|
-
const BUY_PACKS_FOR_ABI = {
|
|
41
|
-
inputs: [
|
|
42
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
43
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
44
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
45
|
-
],
|
|
46
|
-
name: "buyPacksFor",
|
|
47
|
-
outputs: [],
|
|
48
|
-
stateMutability: "nonpayable",
|
|
49
|
-
type: "function",
|
|
50
|
-
};
|
|
51
|
-
// ABI for buyPacksForWithDiscount function (with discount code)
|
|
52
|
-
const BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
53
|
-
inputs: [
|
|
54
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
55
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
56
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
57
|
-
{ internalType: "string", name: "discountCode", type: "string" },
|
|
58
|
-
],
|
|
59
|
-
name: "buyPacksForWithDiscount",
|
|
60
|
-
outputs: [],
|
|
61
|
-
stateMutability: "nonpayable",
|
|
62
|
-
type: "function",
|
|
63
|
-
};
|
|
64
|
-
// ABI for isDiscountCodeValid view function
|
|
65
|
-
const IS_DISCOUNT_CODE_VALID_ABI = {
|
|
66
|
-
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
67
|
-
name: "isDiscountCodeValid",
|
|
68
|
-
outputs: [
|
|
69
|
-
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
70
|
-
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
71
|
-
],
|
|
72
|
-
stateMutability: "view",
|
|
73
|
-
type: "function",
|
|
74
|
-
};
|
|
75
40
|
const basePublicClient = createPublicClient({
|
|
76
41
|
chain: base,
|
|
77
42
|
transport: http(PUBLIC_BASE_RPC_URL),
|
|
@@ -92,38 +57,78 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
92
57
|
const [discountInfo, setDiscountInfo] = useState({
|
|
93
58
|
isValid: false,
|
|
94
59
|
discountAmount: BigInt(0),
|
|
60
|
+
minPurchaseAmount: BigInt(0),
|
|
95
61
|
isLoading: false,
|
|
96
62
|
error: null,
|
|
97
63
|
});
|
|
98
64
|
// Validate discount code on-chain when provided
|
|
99
65
|
useEffect(() => {
|
|
100
66
|
if (!discountCode) {
|
|
101
|
-
setDiscountInfo({
|
|
67
|
+
setDiscountInfo({
|
|
68
|
+
isValid: false,
|
|
69
|
+
discountAmount: BigInt(0),
|
|
70
|
+
minPurchaseAmount: BigInt(0),
|
|
71
|
+
isLoading: false,
|
|
72
|
+
error: null,
|
|
73
|
+
});
|
|
102
74
|
return;
|
|
103
75
|
}
|
|
104
76
|
let cancelled = false;
|
|
105
77
|
const validateDiscount = async () => {
|
|
106
78
|
setDiscountInfo(prev => ({ ...prev, isLoading: true, error: null }));
|
|
107
79
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
80
|
+
// Validate against specific pack and fetch full details in parallel
|
|
81
|
+
const [validForPack, codeDetails] = await Promise.all([
|
|
82
|
+
basePublicClient.readContract({
|
|
83
|
+
address: ccShopAddress,
|
|
84
|
+
abi: [IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI],
|
|
85
|
+
functionName: "isDiscountCodeValidForPack",
|
|
86
|
+
args: [discountCode, BigInt(packId)],
|
|
87
|
+
}),
|
|
88
|
+
basePublicClient.readContract({
|
|
89
|
+
address: ccShopAddress,
|
|
90
|
+
abi: [GET_DISCOUNT_CODE_ABI],
|
|
91
|
+
functionName: "getDiscountCode",
|
|
92
|
+
args: [discountCode],
|
|
93
|
+
}),
|
|
94
|
+
]);
|
|
114
95
|
if (cancelled)
|
|
115
96
|
return;
|
|
116
|
-
const [isValid, discountAmount] =
|
|
117
|
-
|
|
97
|
+
const [isValid, discountAmount] = validForPack;
|
|
98
|
+
const { minPurchaseAmount, packId: restrictedPackId, exists } = codeDetails;
|
|
99
|
+
if (!exists) {
|
|
118
100
|
setDiscountInfo({
|
|
119
101
|
isValid: false,
|
|
120
102
|
discountAmount: BigInt(0),
|
|
103
|
+
minPurchaseAmount: BigInt(0),
|
|
121
104
|
isLoading: false,
|
|
122
|
-
error: "
|
|
105
|
+
error: "Discount code does not exist",
|
|
123
106
|
});
|
|
124
107
|
return;
|
|
125
108
|
}
|
|
126
|
-
|
|
109
|
+
if (!isValid) {
|
|
110
|
+
// Provide specific error based on code details
|
|
111
|
+
if (restrictedPackId !== BigInt(0) && restrictedPackId !== BigInt(packId)) {
|
|
112
|
+
setDiscountInfo({
|
|
113
|
+
isValid: false,
|
|
114
|
+
discountAmount: BigInt(0),
|
|
115
|
+
minPurchaseAmount: BigInt(0),
|
|
116
|
+
isLoading: false,
|
|
117
|
+
error: "Discount code is not valid for this pack",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
setDiscountInfo({
|
|
122
|
+
isValid: false,
|
|
123
|
+
discountAmount: BigInt(0),
|
|
124
|
+
minPurchaseAmount: BigInt(0),
|
|
125
|
+
isLoading: false,
|
|
126
|
+
error: "Invalid or expired discount code",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
setDiscountInfo({ isValid: true, discountAmount, minPurchaseAmount, isLoading: false, error: null });
|
|
127
132
|
}
|
|
128
133
|
catch (error) {
|
|
129
134
|
if (cancelled)
|
|
@@ -132,6 +137,7 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
132
137
|
setDiscountInfo({
|
|
133
138
|
isValid: false,
|
|
134
139
|
discountAmount: BigInt(0),
|
|
140
|
+
minPurchaseAmount: BigInt(0),
|
|
135
141
|
isLoading: false,
|
|
136
142
|
error: "Failed to validate discount code",
|
|
137
143
|
});
|
|
@@ -141,7 +147,7 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
141
147
|
return () => {
|
|
142
148
|
cancelled = true;
|
|
143
149
|
};
|
|
144
|
-
}, [discountCode, ccShopAddress]);
|
|
150
|
+
}, [discountCode, ccShopAddress, packId]);
|
|
145
151
|
// Calculate effective dstAmount after discount
|
|
146
152
|
const effectiveDstAmount = useMemo(() => {
|
|
147
153
|
if (!discountCode || !discountInfo.isValid || discountInfo.discountAmount === BigInt(0)) {
|
|
@@ -191,6 +197,12 @@ export function AnySpendCollectorClubPurchase({ loadOrder, mode = "modal", activ
|
|
|
191
197
|
if (discountCode && discountInfo.error) {
|
|
192
198
|
return (_jsx("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: _jsx("p", { className: "text-sm text-red-500", children: discountInfo.error }) }));
|
|
193
199
|
}
|
|
200
|
+
if (discountCode &&
|
|
201
|
+
discountInfo.isValid &&
|
|
202
|
+
discountInfo.minPurchaseAmount > BigInt(0) &&
|
|
203
|
+
BigInt(packAmount) < discountInfo.minPurchaseAmount) {
|
|
204
|
+
return (_jsx("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: _jsxs("p", { className: "text-sm text-red-500", children: ["Minimum purchase of ", discountInfo.minPurchaseAmount.toString(), " pack", discountInfo.minPurchaseAmount > BigInt(1) ? "s" : "", " required for this discount code"] }) }));
|
|
205
|
+
}
|
|
194
206
|
if (discountCode && discountInfo.isValid && effectiveDstAmount === "0") {
|
|
195
207
|
return (_jsx("div", { className: "mb-4 flex flex-col items-center gap-3 text-center", children: _jsx("p", { className: "text-sm text-red-500", children: "Discount exceeds total price" }) }));
|
|
196
208
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare const BUY_PACKS_FOR_ABI: {
|
|
2
|
+
readonly inputs: readonly [{
|
|
3
|
+
readonly internalType: "address";
|
|
4
|
+
readonly name: "user";
|
|
5
|
+
readonly type: "address";
|
|
6
|
+
}, {
|
|
7
|
+
readonly internalType: "uint256";
|
|
8
|
+
readonly name: "packId";
|
|
9
|
+
readonly type: "uint256";
|
|
10
|
+
}, {
|
|
11
|
+
readonly internalType: "uint256";
|
|
12
|
+
readonly name: "amount";
|
|
13
|
+
readonly type: "uint256";
|
|
14
|
+
}];
|
|
15
|
+
readonly name: "buyPacksFor";
|
|
16
|
+
readonly outputs: readonly [];
|
|
17
|
+
readonly stateMutability: "nonpayable";
|
|
18
|
+
readonly type: "function";
|
|
19
|
+
};
|
|
20
|
+
export declare const BUY_PACKS_FOR_WITH_DISCOUNT_ABI: {
|
|
21
|
+
readonly inputs: readonly [{
|
|
22
|
+
readonly internalType: "address";
|
|
23
|
+
readonly name: "user";
|
|
24
|
+
readonly type: "address";
|
|
25
|
+
}, {
|
|
26
|
+
readonly internalType: "uint256";
|
|
27
|
+
readonly name: "packId";
|
|
28
|
+
readonly type: "uint256";
|
|
29
|
+
}, {
|
|
30
|
+
readonly internalType: "uint256";
|
|
31
|
+
readonly name: "amount";
|
|
32
|
+
readonly type: "uint256";
|
|
33
|
+
}, {
|
|
34
|
+
readonly internalType: "string";
|
|
35
|
+
readonly name: "discountCode";
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
}];
|
|
38
|
+
readonly name: "buyPacksForWithDiscount";
|
|
39
|
+
readonly outputs: readonly [];
|
|
40
|
+
readonly stateMutability: "nonpayable";
|
|
41
|
+
readonly type: "function";
|
|
42
|
+
};
|
|
43
|
+
export declare const IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI: {
|
|
44
|
+
readonly inputs: readonly [{
|
|
45
|
+
readonly internalType: "string";
|
|
46
|
+
readonly name: "code";
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
}, {
|
|
49
|
+
readonly internalType: "uint256";
|
|
50
|
+
readonly name: "packId";
|
|
51
|
+
readonly type: "uint256";
|
|
52
|
+
}];
|
|
53
|
+
readonly name: "isDiscountCodeValidForPack";
|
|
54
|
+
readonly outputs: readonly [{
|
|
55
|
+
readonly internalType: "bool";
|
|
56
|
+
readonly name: "isValid";
|
|
57
|
+
readonly type: "bool";
|
|
58
|
+
}, {
|
|
59
|
+
readonly internalType: "uint256";
|
|
60
|
+
readonly name: "discountAmount";
|
|
61
|
+
readonly type: "uint256";
|
|
62
|
+
}];
|
|
63
|
+
readonly stateMutability: "view";
|
|
64
|
+
readonly type: "function";
|
|
65
|
+
};
|
|
66
|
+
export declare const GET_DISCOUNT_CODE_ABI: {
|
|
67
|
+
readonly inputs: readonly [{
|
|
68
|
+
readonly internalType: "string";
|
|
69
|
+
readonly name: "code";
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
}];
|
|
72
|
+
readonly name: "getDiscountCode";
|
|
73
|
+
readonly outputs: readonly [{
|
|
74
|
+
readonly components: readonly [{
|
|
75
|
+
readonly internalType: "uint256";
|
|
76
|
+
readonly name: "discountAmount";
|
|
77
|
+
readonly type: "uint256";
|
|
78
|
+
}, {
|
|
79
|
+
readonly internalType: "uint256";
|
|
80
|
+
readonly name: "expiresAt";
|
|
81
|
+
readonly type: "uint256";
|
|
82
|
+
}, {
|
|
83
|
+
readonly internalType: "bool";
|
|
84
|
+
readonly name: "used";
|
|
85
|
+
readonly type: "bool";
|
|
86
|
+
}, {
|
|
87
|
+
readonly internalType: "bool";
|
|
88
|
+
readonly name: "exists";
|
|
89
|
+
readonly type: "bool";
|
|
90
|
+
}, {
|
|
91
|
+
readonly internalType: "uint256";
|
|
92
|
+
readonly name: "maxUses";
|
|
93
|
+
readonly type: "uint256";
|
|
94
|
+
}, {
|
|
95
|
+
readonly internalType: "uint256";
|
|
96
|
+
readonly name: "usedCount";
|
|
97
|
+
readonly type: "uint256";
|
|
98
|
+
}, {
|
|
99
|
+
readonly internalType: "uint256";
|
|
100
|
+
readonly name: "packId";
|
|
101
|
+
readonly type: "uint256";
|
|
102
|
+
}, {
|
|
103
|
+
readonly internalType: "uint256";
|
|
104
|
+
readonly name: "minPurchaseAmount";
|
|
105
|
+
readonly type: "uint256";
|
|
106
|
+
}];
|
|
107
|
+
readonly internalType: "struct CCShop.DiscountCode";
|
|
108
|
+
readonly name: "";
|
|
109
|
+
readonly type: "tuple";
|
|
110
|
+
}];
|
|
111
|
+
readonly stateMutability: "view";
|
|
112
|
+
readonly type: "function";
|
|
113
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// CCShop contract ABI fragments used by AnySpendCollectorClubPurchase
|
|
2
|
+
export const BUY_PACKS_FOR_ABI = {
|
|
3
|
+
inputs: [
|
|
4
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
5
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
6
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
7
|
+
],
|
|
8
|
+
name: "buyPacksFor",
|
|
9
|
+
outputs: [],
|
|
10
|
+
stateMutability: "nonpayable",
|
|
11
|
+
type: "function",
|
|
12
|
+
};
|
|
13
|
+
export const BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
14
|
+
inputs: [
|
|
15
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
16
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
17
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
18
|
+
{ internalType: "string", name: "discountCode", type: "string" },
|
|
19
|
+
],
|
|
20
|
+
name: "buyPacksForWithDiscount",
|
|
21
|
+
outputs: [],
|
|
22
|
+
stateMutability: "nonpayable",
|
|
23
|
+
type: "function",
|
|
24
|
+
};
|
|
25
|
+
export const IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI = {
|
|
26
|
+
inputs: [
|
|
27
|
+
{ internalType: "string", name: "code", type: "string" },
|
|
28
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
29
|
+
],
|
|
30
|
+
name: "isDiscountCodeValidForPack",
|
|
31
|
+
outputs: [
|
|
32
|
+
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
33
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
34
|
+
],
|
|
35
|
+
stateMutability: "view",
|
|
36
|
+
type: "function",
|
|
37
|
+
};
|
|
38
|
+
export const GET_DISCOUNT_CODE_ABI = {
|
|
39
|
+
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
40
|
+
name: "getDiscountCode",
|
|
41
|
+
outputs: [
|
|
42
|
+
{
|
|
43
|
+
components: [
|
|
44
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
45
|
+
{ internalType: "uint256", name: "expiresAt", type: "uint256" },
|
|
46
|
+
{ internalType: "bool", name: "used", type: "bool" },
|
|
47
|
+
{ internalType: "bool", name: "exists", type: "bool" },
|
|
48
|
+
{ internalType: "uint256", name: "maxUses", type: "uint256" },
|
|
49
|
+
{ internalType: "uint256", name: "usedCount", type: "uint256" },
|
|
50
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
51
|
+
{ internalType: "uint256", name: "minPurchaseAmount", type: "uint256" },
|
|
52
|
+
],
|
|
53
|
+
internalType: "struct CCShop.DiscountCode",
|
|
54
|
+
name: "",
|
|
55
|
+
type: "tuple",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
stateMutability: "view",
|
|
59
|
+
type: "function",
|
|
60
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare const BUY_PACKS_FOR_ABI: {
|
|
2
|
+
readonly inputs: readonly [{
|
|
3
|
+
readonly internalType: "address";
|
|
4
|
+
readonly name: "user";
|
|
5
|
+
readonly type: "address";
|
|
6
|
+
}, {
|
|
7
|
+
readonly internalType: "uint256";
|
|
8
|
+
readonly name: "packId";
|
|
9
|
+
readonly type: "uint256";
|
|
10
|
+
}, {
|
|
11
|
+
readonly internalType: "uint256";
|
|
12
|
+
readonly name: "amount";
|
|
13
|
+
readonly type: "uint256";
|
|
14
|
+
}];
|
|
15
|
+
readonly name: "buyPacksFor";
|
|
16
|
+
readonly outputs: readonly [];
|
|
17
|
+
readonly stateMutability: "nonpayable";
|
|
18
|
+
readonly type: "function";
|
|
19
|
+
};
|
|
20
|
+
export declare const BUY_PACKS_FOR_WITH_DISCOUNT_ABI: {
|
|
21
|
+
readonly inputs: readonly [{
|
|
22
|
+
readonly internalType: "address";
|
|
23
|
+
readonly name: "user";
|
|
24
|
+
readonly type: "address";
|
|
25
|
+
}, {
|
|
26
|
+
readonly internalType: "uint256";
|
|
27
|
+
readonly name: "packId";
|
|
28
|
+
readonly type: "uint256";
|
|
29
|
+
}, {
|
|
30
|
+
readonly internalType: "uint256";
|
|
31
|
+
readonly name: "amount";
|
|
32
|
+
readonly type: "uint256";
|
|
33
|
+
}, {
|
|
34
|
+
readonly internalType: "string";
|
|
35
|
+
readonly name: "discountCode";
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
}];
|
|
38
|
+
readonly name: "buyPacksForWithDiscount";
|
|
39
|
+
readonly outputs: readonly [];
|
|
40
|
+
readonly stateMutability: "nonpayable";
|
|
41
|
+
readonly type: "function";
|
|
42
|
+
};
|
|
43
|
+
export declare const IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI: {
|
|
44
|
+
readonly inputs: readonly [{
|
|
45
|
+
readonly internalType: "string";
|
|
46
|
+
readonly name: "code";
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
}, {
|
|
49
|
+
readonly internalType: "uint256";
|
|
50
|
+
readonly name: "packId";
|
|
51
|
+
readonly type: "uint256";
|
|
52
|
+
}];
|
|
53
|
+
readonly name: "isDiscountCodeValidForPack";
|
|
54
|
+
readonly outputs: readonly [{
|
|
55
|
+
readonly internalType: "bool";
|
|
56
|
+
readonly name: "isValid";
|
|
57
|
+
readonly type: "bool";
|
|
58
|
+
}, {
|
|
59
|
+
readonly internalType: "uint256";
|
|
60
|
+
readonly name: "discountAmount";
|
|
61
|
+
readonly type: "uint256";
|
|
62
|
+
}];
|
|
63
|
+
readonly stateMutability: "view";
|
|
64
|
+
readonly type: "function";
|
|
65
|
+
};
|
|
66
|
+
export declare const GET_DISCOUNT_CODE_ABI: {
|
|
67
|
+
readonly inputs: readonly [{
|
|
68
|
+
readonly internalType: "string";
|
|
69
|
+
readonly name: "code";
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
}];
|
|
72
|
+
readonly name: "getDiscountCode";
|
|
73
|
+
readonly outputs: readonly [{
|
|
74
|
+
readonly components: readonly [{
|
|
75
|
+
readonly internalType: "uint256";
|
|
76
|
+
readonly name: "discountAmount";
|
|
77
|
+
readonly type: "uint256";
|
|
78
|
+
}, {
|
|
79
|
+
readonly internalType: "uint256";
|
|
80
|
+
readonly name: "expiresAt";
|
|
81
|
+
readonly type: "uint256";
|
|
82
|
+
}, {
|
|
83
|
+
readonly internalType: "bool";
|
|
84
|
+
readonly name: "used";
|
|
85
|
+
readonly type: "bool";
|
|
86
|
+
}, {
|
|
87
|
+
readonly internalType: "bool";
|
|
88
|
+
readonly name: "exists";
|
|
89
|
+
readonly type: "bool";
|
|
90
|
+
}, {
|
|
91
|
+
readonly internalType: "uint256";
|
|
92
|
+
readonly name: "maxUses";
|
|
93
|
+
readonly type: "uint256";
|
|
94
|
+
}, {
|
|
95
|
+
readonly internalType: "uint256";
|
|
96
|
+
readonly name: "usedCount";
|
|
97
|
+
readonly type: "uint256";
|
|
98
|
+
}, {
|
|
99
|
+
readonly internalType: "uint256";
|
|
100
|
+
readonly name: "packId";
|
|
101
|
+
readonly type: "uint256";
|
|
102
|
+
}, {
|
|
103
|
+
readonly internalType: "uint256";
|
|
104
|
+
readonly name: "minPurchaseAmount";
|
|
105
|
+
readonly type: "uint256";
|
|
106
|
+
}];
|
|
107
|
+
readonly internalType: "struct CCShop.DiscountCode";
|
|
108
|
+
readonly name: "";
|
|
109
|
+
readonly type: "tuple";
|
|
110
|
+
}];
|
|
111
|
+
readonly stateMutability: "view";
|
|
112
|
+
readonly type: "function";
|
|
113
|
+
};
|
package/package.json
CHANGED
|
@@ -33,51 +33,18 @@ import React, { useEffect, useMemo, useState } from "react";
|
|
|
33
33
|
import { createPublicClient, encodeFunctionData, http } from "viem";
|
|
34
34
|
import { base } from "viem/chains";
|
|
35
35
|
import { AnySpendCustom } from "./AnySpendCustom";
|
|
36
|
+
import {
|
|
37
|
+
BUY_PACKS_FOR_ABI,
|
|
38
|
+
BUY_PACKS_FOR_WITH_DISCOUNT_ABI,
|
|
39
|
+
GET_DISCOUNT_CODE_ABI,
|
|
40
|
+
IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI,
|
|
41
|
+
} from "./ccShopAbi";
|
|
36
42
|
|
|
37
43
|
// Collector Club Shop contract addresses on Base
|
|
38
44
|
const CC_SHOP_ADDRESS = "0x47366E64E4917dd4DdC04Fb9DC507c1dD2b87294";
|
|
39
45
|
const CC_SHOP_ADDRESS_STAGING = "0x8b751143342ac41eB965E55430e3F7Adf6BE01fA";
|
|
40
46
|
const BASE_CHAIN_ID = 8453;
|
|
41
47
|
|
|
42
|
-
// ABI for buyPacksFor function only
|
|
43
|
-
const BUY_PACKS_FOR_ABI = {
|
|
44
|
-
inputs: [
|
|
45
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
46
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
47
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
48
|
-
],
|
|
49
|
-
name: "buyPacksFor",
|
|
50
|
-
outputs: [],
|
|
51
|
-
stateMutability: "nonpayable",
|
|
52
|
-
type: "function",
|
|
53
|
-
} as const;
|
|
54
|
-
|
|
55
|
-
// ABI for buyPacksForWithDiscount function (with discount code)
|
|
56
|
-
const BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
57
|
-
inputs: [
|
|
58
|
-
{ internalType: "address", name: "user", type: "address" },
|
|
59
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
60
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
61
|
-
{ internalType: "string", name: "discountCode", type: "string" },
|
|
62
|
-
],
|
|
63
|
-
name: "buyPacksForWithDiscount",
|
|
64
|
-
outputs: [],
|
|
65
|
-
stateMutability: "nonpayable",
|
|
66
|
-
type: "function",
|
|
67
|
-
} as const;
|
|
68
|
-
|
|
69
|
-
// ABI for isDiscountCodeValid view function
|
|
70
|
-
const IS_DISCOUNT_CODE_VALID_ABI = {
|
|
71
|
-
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
72
|
-
name: "isDiscountCodeValid",
|
|
73
|
-
outputs: [
|
|
74
|
-
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
75
|
-
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
76
|
-
],
|
|
77
|
-
stateMutability: "view",
|
|
78
|
-
type: "function",
|
|
79
|
-
} as const;
|
|
80
|
-
|
|
81
48
|
const basePublicClient = createPublicClient({
|
|
82
49
|
chain: base,
|
|
83
50
|
transport: http(PUBLIC_BASE_RPC_URL),
|
|
@@ -193,11 +160,13 @@ export function AnySpendCollectorClubPurchase({
|
|
|
193
160
|
const [discountInfo, setDiscountInfo] = useState<{
|
|
194
161
|
isValid: boolean;
|
|
195
162
|
discountAmount: bigint;
|
|
163
|
+
minPurchaseAmount: bigint;
|
|
196
164
|
isLoading: boolean;
|
|
197
165
|
error: string | null;
|
|
198
166
|
}>({
|
|
199
167
|
isValid: false,
|
|
200
168
|
discountAmount: BigInt(0),
|
|
169
|
+
minPurchaseAmount: BigInt(0),
|
|
201
170
|
isLoading: false,
|
|
202
171
|
error: null,
|
|
203
172
|
});
|
|
@@ -205,7 +174,13 @@ export function AnySpendCollectorClubPurchase({
|
|
|
205
174
|
// Validate discount code on-chain when provided
|
|
206
175
|
useEffect(() => {
|
|
207
176
|
if (!discountCode) {
|
|
208
|
-
setDiscountInfo({
|
|
177
|
+
setDiscountInfo({
|
|
178
|
+
isValid: false,
|
|
179
|
+
discountAmount: BigInt(0),
|
|
180
|
+
minPurchaseAmount: BigInt(0),
|
|
181
|
+
isLoading: false,
|
|
182
|
+
error: null,
|
|
183
|
+
});
|
|
209
184
|
return;
|
|
210
185
|
}
|
|
211
186
|
|
|
@@ -215,34 +190,68 @@ export function AnySpendCollectorClubPurchase({
|
|
|
215
190
|
setDiscountInfo(prev => ({ ...prev, isLoading: true, error: null }));
|
|
216
191
|
|
|
217
192
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
193
|
+
// Validate against specific pack and fetch full details in parallel
|
|
194
|
+
const [validForPack, codeDetails] = await Promise.all([
|
|
195
|
+
basePublicClient.readContract({
|
|
196
|
+
address: ccShopAddress as `0x${string}`,
|
|
197
|
+
abi: [IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI],
|
|
198
|
+
functionName: "isDiscountCodeValidForPack",
|
|
199
|
+
args: [discountCode, BigInt(packId)],
|
|
200
|
+
}),
|
|
201
|
+
basePublicClient.readContract({
|
|
202
|
+
address: ccShopAddress as `0x${string}`,
|
|
203
|
+
abi: [GET_DISCOUNT_CODE_ABI],
|
|
204
|
+
functionName: "getDiscountCode",
|
|
205
|
+
args: [discountCode],
|
|
206
|
+
}),
|
|
207
|
+
]);
|
|
224
208
|
|
|
225
209
|
if (cancelled) return;
|
|
226
210
|
|
|
227
|
-
const [isValid, discountAmount] =
|
|
211
|
+
const [isValid, discountAmount] = validForPack;
|
|
212
|
+
const { minPurchaseAmount, packId: restrictedPackId, exists } = codeDetails;
|
|
228
213
|
|
|
229
|
-
if (!
|
|
214
|
+
if (!exists) {
|
|
230
215
|
setDiscountInfo({
|
|
231
216
|
isValid: false,
|
|
232
217
|
discountAmount: BigInt(0),
|
|
218
|
+
minPurchaseAmount: BigInt(0),
|
|
233
219
|
isLoading: false,
|
|
234
|
-
error: "
|
|
220
|
+
error: "Discount code does not exist",
|
|
235
221
|
});
|
|
236
222
|
return;
|
|
237
223
|
}
|
|
238
224
|
|
|
239
|
-
|
|
225
|
+
if (!isValid) {
|
|
226
|
+
// Provide specific error based on code details
|
|
227
|
+
if (restrictedPackId !== BigInt(0) && restrictedPackId !== BigInt(packId)) {
|
|
228
|
+
setDiscountInfo({
|
|
229
|
+
isValid: false,
|
|
230
|
+
discountAmount: BigInt(0),
|
|
231
|
+
minPurchaseAmount: BigInt(0),
|
|
232
|
+
isLoading: false,
|
|
233
|
+
error: "Discount code is not valid for this pack",
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
setDiscountInfo({
|
|
237
|
+
isValid: false,
|
|
238
|
+
discountAmount: BigInt(0),
|
|
239
|
+
minPurchaseAmount: BigInt(0),
|
|
240
|
+
isLoading: false,
|
|
241
|
+
error: "Invalid or expired discount code",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setDiscountInfo({ isValid: true, discountAmount, minPurchaseAmount, isLoading: false, error: null });
|
|
240
248
|
} catch (error) {
|
|
241
249
|
if (cancelled) return;
|
|
242
250
|
console.error("Failed to validate discount code", { discountCode, error });
|
|
243
251
|
setDiscountInfo({
|
|
244
252
|
isValid: false,
|
|
245
253
|
discountAmount: BigInt(0),
|
|
254
|
+
minPurchaseAmount: BigInt(0),
|
|
246
255
|
isLoading: false,
|
|
247
256
|
error: "Failed to validate discount code",
|
|
248
257
|
});
|
|
@@ -254,7 +263,7 @@ export function AnySpendCollectorClubPurchase({
|
|
|
254
263
|
return () => {
|
|
255
264
|
cancelled = true;
|
|
256
265
|
};
|
|
257
|
-
}, [discountCode, ccShopAddress]);
|
|
266
|
+
}, [discountCode, ccShopAddress, packId]);
|
|
258
267
|
|
|
259
268
|
// Calculate effective dstAmount after discount
|
|
260
269
|
const effectiveDstAmount = useMemo(() => {
|
|
@@ -330,6 +339,22 @@ export function AnySpendCollectorClubPurchase({
|
|
|
330
339
|
);
|
|
331
340
|
}
|
|
332
341
|
|
|
342
|
+
if (
|
|
343
|
+
discountCode &&
|
|
344
|
+
discountInfo.isValid &&
|
|
345
|
+
discountInfo.minPurchaseAmount > BigInt(0) &&
|
|
346
|
+
BigInt(packAmount) < discountInfo.minPurchaseAmount
|
|
347
|
+
) {
|
|
348
|
+
return (
|
|
349
|
+
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
350
|
+
<p className="text-sm text-red-500">
|
|
351
|
+
Minimum purchase of {discountInfo.minPurchaseAmount.toString()} pack
|
|
352
|
+
{discountInfo.minPurchaseAmount > BigInt(1) ? "s" : ""} required for this discount code
|
|
353
|
+
</p>
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
333
358
|
if (discountCode && discountInfo.isValid && effectiveDstAmount === "0") {
|
|
334
359
|
return (
|
|
335
360
|
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// CCShop contract ABI fragments used by AnySpendCollectorClubPurchase
|
|
2
|
+
|
|
3
|
+
export const BUY_PACKS_FOR_ABI = {
|
|
4
|
+
inputs: [
|
|
5
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
6
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
7
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
8
|
+
],
|
|
9
|
+
name: "buyPacksFor",
|
|
10
|
+
outputs: [],
|
|
11
|
+
stateMutability: "nonpayable",
|
|
12
|
+
type: "function",
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export const BUY_PACKS_FOR_WITH_DISCOUNT_ABI = {
|
|
16
|
+
inputs: [
|
|
17
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
18
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
19
|
+
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
20
|
+
{ internalType: "string", name: "discountCode", type: "string" },
|
|
21
|
+
],
|
|
22
|
+
name: "buyPacksForWithDiscount",
|
|
23
|
+
outputs: [],
|
|
24
|
+
stateMutability: "nonpayable",
|
|
25
|
+
type: "function",
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
export const IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI = {
|
|
29
|
+
inputs: [
|
|
30
|
+
{ internalType: "string", name: "code", type: "string" },
|
|
31
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
32
|
+
],
|
|
33
|
+
name: "isDiscountCodeValidForPack",
|
|
34
|
+
outputs: [
|
|
35
|
+
{ internalType: "bool", name: "isValid", type: "bool" },
|
|
36
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
37
|
+
],
|
|
38
|
+
stateMutability: "view",
|
|
39
|
+
type: "function",
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
export const GET_DISCOUNT_CODE_ABI = {
|
|
43
|
+
inputs: [{ internalType: "string", name: "code", type: "string" }],
|
|
44
|
+
name: "getDiscountCode",
|
|
45
|
+
outputs: [
|
|
46
|
+
{
|
|
47
|
+
components: [
|
|
48
|
+
{ internalType: "uint256", name: "discountAmount", type: "uint256" },
|
|
49
|
+
{ internalType: "uint256", name: "expiresAt", type: "uint256" },
|
|
50
|
+
{ internalType: "bool", name: "used", type: "bool" },
|
|
51
|
+
{ internalType: "bool", name: "exists", type: "bool" },
|
|
52
|
+
{ internalType: "uint256", name: "maxUses", type: "uint256" },
|
|
53
|
+
{ internalType: "uint256", name: "usedCount", type: "uint256" },
|
|
54
|
+
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
55
|
+
{ internalType: "uint256", name: "minPurchaseAmount", type: "uint256" },
|
|
56
|
+
],
|
|
57
|
+
internalType: "struct CCShop.DiscountCode",
|
|
58
|
+
name: "",
|
|
59
|
+
type: "tuple",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
stateMutability: "view",
|
|
63
|
+
type: "function",
|
|
64
|
+
} as const;
|