Skip to content

Commit 21a1c5d

Browse files
committed
feat: sets token for sale
1 parent f14b65a commit 21a1c5d

File tree

8 files changed

+123
-34
lines changed

8 files changed

+123
-34
lines changed

app/src/context/evm/larskristo-hellheads/LarskristoHellheadsContext.types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type LarskristoHellheadsContextActions = {
1010
fetchContractValues: { isLoading: boolean };
1111
getTokenPrice: { isLoading: boolean };
1212
buyToken: { isPending: boolean; isConfirmed: boolean; transactionHash?: string };
13+
setTokenForSale: { isPending: boolean; isConfirmed: boolean; transactionHash?: string };
1314
};
1415

1516
export type LarskristoHellheadsContractValues = {
@@ -42,5 +43,6 @@ export type LarskristoHellheadsContextType = {
4243
getTokenPrice: (tokenId: number, options?: { excludeExchangeRate?: boolean }) => Promise<TokenPrice | undefined>;
4344
royaltyInfo: (tokenId: number) => Promise<void>;
4445
buyToken: (tokenId: number) => Promise<void>;
46+
setTokenForSale: (tokenId: number, price: string) => Promise<void>;
4547
connectedAccountIsOwner: () => boolean;
4648
};

app/src/context/evm/larskristo-hellheads/LarskristoHellheadsContextController.tsx

+41
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export const LarskristoHellheadsContextController = ({ children }: LarskristoHel
3535
isPending: false,
3636
isConfirmed: false,
3737
},
38+
setTokenForSale: {
39+
isPending: false,
40+
isConfirmed: false,
41+
},
3842
getTokenPrice: {
3943
isLoading: false,
4044
},
@@ -52,6 +56,42 @@ export const LarskristoHellheadsContextController = ({ children }: LarskristoHel
5256

5357
const connectedAccountIsOwner = () => owner === connectedAccountAddress;
5458

59+
const setTokenForSale = async (tokenId: number, price: string) => {
60+
try {
61+
setActions((prev) => ({
62+
...prev,
63+
setTokenForSale: {
64+
isPending: true,
65+
isConfirmed: false,
66+
},
67+
}));
68+
69+
const contract = getContractInstance();
70+
71+
const hash = await contract.write.setTokenForSale([BigInt(tokenId), ethers.parseEther(price)], {
72+
account: connectedAccountAddress as ZeroXAddress,
73+
chain: undefined,
74+
});
75+
76+
console.log({ hash });
77+
78+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
79+
80+
console.log({ receipt });
81+
82+
setActions((prev) => ({
83+
...prev,
84+
setTokenForSale: {
85+
isPending: false,
86+
isConfirmed: true,
87+
transactionHash: receipt.transactionHash,
88+
},
89+
}));
90+
} catch (error) {
91+
console.error(error);
92+
}
93+
};
94+
5595
const buyToken = async (tokenId: number) => {
5696
try {
5797
setActions((prev) => ({
@@ -230,6 +270,7 @@ export const LarskristoHellheadsContextController = ({ children }: LarskristoHel
230270
royaltyInfo,
231271
royalty,
232272
connectedAccountIsOwner,
273+
setTokenForSale,
233274
};
234275

235276
return <LarskristoHellheadsContext.Provider value={props}>{children}</LarskristoHellheadsContext.Provider>;

app/src/ui/button/Button.module.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ $HALF_LOADER_SIZE: 15px;
353353
&[disabled] {
354354
border-color: var(--color-dark-1);
355355
color: var(--color-dark-1);
356-
background-color: var(--color-white);
356+
background-color: transparent;
357357

358358
&:active,
359359
&:hover,

app/src/ui/modal/Modal.tsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,7 @@ export const Modal = ({
3838

3939
// Save page scrollY before modal is opened and restore it after close
4040
// It fixes auto scroll to input fields on focus in android
41-
const [pageScrollY, setPageScrollY] = useState(0);
42-
43-
useEffect(() => {
44-
if (isOpened && fullscreenVariant === "default") {
45-
setTimeout(() => {
46-
setPageScrollY(window.scrollY);
47-
window.scrollTo(0, 0);
48-
}, MODAL_ANIMATION_TIME);
49-
}
50-
}, [isOpened, fullscreenVariant]);
41+
const [pageScrollY] = useState(0);
5142

5243
useEffect(() => {
5344
if (!isOpened && pageScrollY) {

app/src/ui/svpervnder/collections/larskristo_hellheads/details-modal/DetailsModal.module.scss

+4
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@
145145
text-align: center;
146146
color: var(--color-background);
147147
background-color: var(--color-background);
148+
149+
&-setTokenForSale {
150+
margin-top: $space-default;
151+
}
148152
}
149153

150154
&--connect-wallet {

app/src/ui/svpervnder/collections/larskristo_hellheads/details-modal/DetailsModal.module.scss.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type Styles = {
1818
"details-modal__price-block--owner-pill": string;
1919
"details-modal__price-block--price": string;
2020
"details-modal__price-block--success": string;
21+
"details-modal__price-block--success-setTokenForSale": string;
2122
"details-modal__price-card": string;
2223
"details-modal__set-for-sale": string;
2324
"details-modal__set-for-sale--form": string;

app/src/ui/svpervnder/collections/larskristo_hellheads/details-modal/DetailsModal.tsx

+71-21
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ import evm from "providers/evm";
1616
import { DetailsModalProps } from "./DetailsModal.types";
1717
import styles from "./DetailsModal.module.scss";
1818

19-
const handleOnSetTokenPriceSubmit = (values: Record<string, string>) => {
20-
console.log({ values });
21-
};
22-
2319
export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className, item }) => {
2420
const [isSetForSale, setIsSetForSale] = useState(false);
2521
const tokenPriceInputRef = useRef<HTMLInputElement | undefined>();
@@ -37,6 +33,11 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
3733
}
3834
};
3935

36+
const handleOnSetTokenPriceSubmit = async (values: Record<string, string>) => {
37+
await ERC721.setTokenForSale(item.id!, values.tokenPrice);
38+
setIsSetForSale(false);
39+
};
40+
4041
const handleOnBuyNowClick = () => {
4142
ERC721.buyToken(item.id!);
4243
};
@@ -53,7 +54,7 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
5354
await ERC721.ownerOf(item.id!);
5455
await ERC721.getTokenPrice(item.id!);
5556
})();
56-
}, [item.id, ERC721.actions.buyToken.isConfirmed]);
57+
}, [item.id, ERC721.actions.buyToken.isConfirmed, ERC721.actions.setTokenForSale.isConfirmed]);
5758

5859
useEffect(() => {
5960
if (!ERC721.tokenPrice?.rawValue) return;
@@ -117,10 +118,17 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
117118
</div>
118119
<div>
119120
<Typography.Text flat truncate className={styles["details-modal__price-block--owner-pill"]}>
120-
<span>Owned by:</span> <Typography.Anchor>{ensName || ERC721.owner}</Typography.Anchor>
121+
<span>Owned by:</span>{" "}
122+
<Typography.Anchor
123+
href={`${evm.getBlockExplorerUrl()}/address/${ensName || ERC721.owner}`}
124+
target="_blank"
125+
>
126+
{ensName || ERC721.owner}
127+
</Typography.Anchor>
121128
</Typography.Text>
122129
</div>
123130
</div>
131+
124132
{!isConnected && (
125133
<div className={styles["details-modal__price-block--connect-wallet"]}>
126134
<Button fullWidth variant="outlined" onClick={handleOnDisplayWidgetClick}>
@@ -158,22 +166,44 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
158166
<Typography.Text flat>
159167
Purchase confirmed! Check your transaction{" "}
160168
<Typography.Anchor
169+
target="_blank"
161170
href={`${evm.getBlockExplorerUrl()}/tx/${ERC721.actions.buyToken.transactionHash}`}
162171
>
163172
here <Icon name="icon-exit-up2" />
164173
</Typography.Anchor>
165174
</Typography.Text>
166175
</div>
167176
)}
177+
178+
{ERC721.actions.setTokenForSale.isConfirmed && (
179+
<div
180+
className={clsx(
181+
styles["details-modal__price-block--success-setTokenForSale"],
182+
styles["details-modal__price-block--success"],
183+
)}
184+
>
185+
<Typography.Text flat>
186+
New price set! <br />
187+
This token is now for sale.
188+
<br /> Check your transaction{" "}
189+
<Typography.Anchor
190+
target="_blank"
191+
href={`${evm.getBlockExplorerUrl()}/tx/${ERC721.actions.setTokenForSale.transactionHash}`}
192+
>
193+
here <Icon name="icon-exit-up2" />
194+
</Typography.Anchor>
195+
</Typography.Text>
196+
</div>
197+
)}
168198
</Card.Content>
169199

170200
{ERC721.tokenPrice && !ERC721.connectedAccountIsOwner() && (
171201
<Card.Actions>
172202
<Button
173203
fullWidth
174204
disabled={!isConnected || ERC721.actions.buyToken.isPending}
175-
onClick={handleOnBuyNowClick}
176205
isLoading={ERC721.actions.buyToken.isPending}
206+
onClick={handleOnBuyNowClick}
177207
>
178208
Buy for: {ERC721.tokenPrice?.formattedValue} ETH
179209
</Button>
@@ -188,7 +218,7 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
188218
onClick={handleSetForSaleToggle}
189219
isLoading={ERC721.actions.buyToken.isPending}
190220
>
191-
Set For Sale
221+
{ERC721.tokenPrice ? "Set A New Price" : "Set For Sale"}
192222
</Button>
193223
</Card.Actions>
194224
)}
@@ -198,20 +228,20 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
198228
<Button
199229
variant="outlined"
200230
color="secondary"
201-
disabled={!isConnected}
231+
disabled={!isConnected || ERC721.actions.setTokenForSale.isPending}
202232
onClick={handleSetForSaleToggle}
203233
isLoading={ERC721.actions.buyToken.isPending}
204234
>
205235
Cancel
206236
</Button>
207237
<Button
208238
color="primary"
209-
disabled={!isConnected}
210-
isLoading={ERC721.actions.buyToken.isPending}
211239
type="submit"
212240
onClick={handleSubmit}
241+
disabled={!isConnected || ERC721.actions.setTokenForSale.isPending}
242+
isLoading={ERC721.actions.setTokenForSale.isPending}
213243
>
214-
Set For Sale
244+
{ERC721.tokenPrice ? "Confirm New Price" : "Set For Sale"}
215245
</Button>
216246
</Card.Actions>
217247
)}
@@ -220,7 +250,10 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
220250
/>
221251
<Card className={styles["details-modal__forge-card"]}>
222252
<Card.Content>
223-
<Typography.Headline5>Forge This Item</Typography.Headline5>
253+
<Typography.Headline5 flat={!ERC721.connectedAccountIsOwner()}>Forge This Item</Typography.Headline5>
254+
{!ERC721.connectedAccountIsOwner() && (
255+
<Typography.TextLead>(Only the current owner can forge this item)</Typography.TextLead>
256+
)}
224257
<Typography.Text flat>Extend this item's story, exclusively for you by Lars Kristo.</Typography.Text>
225258
<Typography.Description>
226259
* Each forge is also registered in the Ethereum blockchain tied to the original NFT.
@@ -239,7 +272,7 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
239272
</div>
240273
</Grid.Col>
241274
<Grid.Col className={styles["details-modal__forge-card--row-button"]} lg={4}>
242-
<Button variant="outlined" color="secondary">
275+
<Button variant="outlined" color="secondary" disabled={!ERC721.connectedAccountIsOwner()}>
243276
Forge
244277
</Button>
245278
</Grid.Col>
@@ -258,7 +291,7 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
258291
</div>
259292
</Grid.Col>
260293
<Grid.Col className={styles["details-modal__forge-card--row-button"]} lg={4}>
261-
<Button variant="outlined" color="secondary">
294+
<Button variant="outlined" color="secondary" disabled={!ERC721.connectedAccountIsOwner()}>
262295
Forge
263296
</Button>
264297
</Grid.Col>
@@ -277,7 +310,7 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
277310
</div>
278311
</Grid.Col>
279312
<Grid.Col className={styles["details-modal__forge-card--row-button"]} lg={4}>
280-
<Button variant="outlined" color="secondary">
313+
<Button variant="outlined" color="secondary" disabled={!ERC721.connectedAccountIsOwner()}>
281314
Forge
282315
</Button>
283316
</Grid.Col>
@@ -318,27 +351,44 @@ export const DetailsModal: React.FC<DetailsModalProps> = ({ onClose, className,
318351
<Typography.Headline5>Details</Typography.Headline5>
319352
<div className={styles["details-modal__details-card--row"]}>
320353
<Typography.Text flat>Contract Address</Typography.Text>
321-
<Typography.Anchor truncate>{ERC721.contractAddress}</Typography.Anchor>
354+
<Typography.Anchor
355+
truncate
356+
href={`${evm.getBlockExplorerUrl()}/token/${ERC721.contractAddress}`}
357+
target="_blank"
358+
>
359+
{ERC721.contractAddress}
360+
</Typography.Anchor>
322361
</div>
323362
<div className={styles["details-modal__details-card--row"]}>
324363
<Typography.Text flat>Token ID</Typography.Text>
325-
<Typography.Anchor>#{item.id!}</Typography.Anchor>
364+
<Typography.Anchor
365+
href={`${evm.getBlockExplorerUrl()}/nft/${ERC721.contractAddress}/${item.id!}`}
366+
target="_blank"
367+
>
368+
#{item.id!}
369+
</Typography.Anchor>
326370
</div>
327371
<div className={styles["details-modal__details-card--row"]}>
328372
<Typography.Text flat>Token Standard</Typography.Text>
329373
<Typography.Anchor>ERC721</Typography.Anchor>
330374
</div>
331375
<div className={styles["details-modal__details-card--row"]}>
332376
<Typography.Text flat>Owner</Typography.Text>
333-
<Typography.Anchor truncate>{ensName || ERC721.owner}</Typography.Anchor>
377+
<Typography.Anchor
378+
truncate
379+
href={`${evm.getBlockExplorerUrl()}/address/${ensName || ERC721.owner}`}
380+
target="_blank"
381+
>
382+
{ensName || ERC721.owner}
383+
</Typography.Anchor>
334384
</div>
335385
<div className={styles["details-modal__details-card--row"]}>
336386
<Typography.Text flat>Royalty</Typography.Text>
337-
<Typography.Anchor>{ERC721.royalty?.percentageFormatted}</Typography.Anchor>
387+
<Typography.Text flat>{ERC721.royalty?.percentageFormatted}</Typography.Text>
338388
</div>
339389
<div className={styles["details-modal__details-card--row"]}>
340390
<Typography.Text flat>Chain</Typography.Text>
341-
<Typography.Anchor>Ethereum</Typography.Anchor>
391+
<Typography.Text flat>Ethereum</Typography.Text>
342392
</div>
343393
</Card.Content>
344394
</Card>

app/src/ui/svpervnder/collections/larskristo_hellheads/grid-item/GridItem.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export const GridItem: React.FC<GridItemProps> = ({ item, index, handleExpand, c
3030
setTokenPrice(result);
3131

3232
setIsFetchingTokenPrice(false);
33-
}, 100 * (index + 1));
34-
}, [tokenPrice?.rawValue]);
33+
}, 500 + 100 * (index + 1));
34+
}, [index]);
3535

3636
const renderTokenPrice = () => {
3737
if (tokenPrice?.rawValue === BigInt(0)) {

0 commit comments

Comments
 (0)