@manifoldxyz/dropkit 0.2.1 → 0.3.0

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/README.md CHANGED
@@ -274,6 +274,58 @@ export function CrossChainMint({ config }: { config: MintConfig }) {
274
274
 
275
275
  Rows expose `data-chain`, `data-selected`, `data-loading` and `aria-pressed`. `renderRow` replaces the row content.
276
276
 
277
+ ## Cart checkout
278
+
279
+ `@manifoldxyz/dropkit/cart` collects several Manifold claims in one cart and sends one mint transaction per item. Importing only `@manifoldxyz/dropkit` never loads the cart. The cart takes Manifold claims only, all on one chain.
280
+
281
+ ```tsx
282
+ import { AddToCartButton, CartProvider, useCart, useCheckout } from '@manifoldxyz/dropkit/cart';
283
+ import type { ManifoldClaimConfig } from '@manifoldxyz/dropkit';
284
+
285
+ const claim: ManifoldClaimConfig = {
286
+ type: 'manifold-claim',
287
+ chainId: 1,
288
+ creatorContract: '0x0000000000000000000000000000000000000001',
289
+ instanceId: '1',
290
+ extension: '0x0000000000000000000000000000000000000002', // required in the cart
291
+ };
292
+
293
+ function Checkout() {
294
+ const cart = useCart();
295
+ const checkout = useCheckout();
296
+ return (
297
+ <div>
298
+ <p>{cart.count} in cart</p>
299
+ {checkout.state === 'idle' && <button onClick={checkout.checkout}>Check out</button>}
300
+ {checkout.state === 'ready' && <button onClick={checkout.confirm}>Confirm in wallet</button>}
301
+ {checkout.state === 'checking' && <button onClick={checkout.check}>Check again</button>}
302
+ {checkout.message && <p>{checkout.message}</p>}
303
+ </div>
304
+ );
305
+ }
306
+
307
+ export function Shop() {
308
+ return (
309
+ <CartProvider chainId={1} rpcUrls={{ 1: 'https://eth.example/rpc' }}>
310
+ <AddToCartButton config={claim} />
311
+ <Checkout />
312
+ </CartProvider>
313
+ );
314
+ }
315
+ ```
316
+
317
+ Things to know:
318
+
319
+ - **`extension` is required** on every cart config. Without it `add()` throws `INVALID_CONFIG`.
320
+ - **Pass `rpcUrls`.** Without an RPC URL for the cart's chain, the provider logs a console warning once: receipts, holdings and balance checks then use a public RPC that can be rate-limited during a sale.
321
+ - **One `CartProvider` per `storageKey` per tab.** The default key is `dropkit-cart:<chainId>`. A second provider with the same key throws `INVALID_CONFIG`.
322
+ - **What the cart keeps in `sessionStorage`:** one JSON value under the storage key holding the items (config and quantity), the mints that were sent and are not yet settled (payer, recipient, quantity, transaction hash and the holdings count taken before sending), and the results shown to the collector. It holds no keys and no signatures. It lives for the tab session. If `sessionStorage` is unavailable or full, checkout stops with `PERSISTENCE_UNAVAILABLE` before anything is sent.
323
+ - **Each click makes one wallet request:** switch network, add network, or one mint. `confirm()` must run from a click handler.
324
+ - **`useHoldings(configs, { wallet? })`** reads what a wallet holds of each drop, for "You own N" on a drop page: `get(config)` returns `{ kind, count }`: `owned` for an ERC-1155 drop (the token balance, transfers included), `minted` for an ERC-721 drop with a wallet limit (mints from that drop), and `owned-in-contract` for other ERC-721 drops (the balance on the whole contract, which equals `owned` when the contract holds only this drop). It reads the connected wallet by default, on mount, on a wallet change, after each collected mint and on `refresh()`, with no timer. Use it inside `CartProvider`.
325
+ - **Links for collected items.** Each `cart.collected` entry has `tokens` (from the receipt) and `links`: `tx` (block explorer), `tokens` (one OpenSea item page each) and `profile`. Pass `openseaCollections={{ '0xYourContract': 'your-opensea-slug' }}` to `CartProvider` and `profile` opens the collector's OpenSea holdings filtered to your collection. Without a slug, `profile` is the plain OpenSea profile when no token id is known (a lost receipt on a 721), else null. OpenSea has no testnets, so Sepolia gets the explorer link only.
326
+ - **A mint that cannot be confirmed** is never retried for you. It is listed in `cart.unconfirmed` until the collector dismisses it or adds the item back. Each entry has `wallet` (the wallet that sent it) and `message`, which names that wallet: "We couldn't confirm this. Check wallet 0x1234…abcd. If it didn't go through, add it again."
327
+ - **Any wallet works**, smart accounts included. A wallet that returns a request id instead of a transaction hash (a Safe through WalletConnect, for example) shows "Still confirming", then settles from the collector's holdings: Collected if the mint landed, otherwise Unconfirmed. The cart never resends it.
328
+
277
329
  ## Local verification
278
330
 
279
331
  From the repository root, use Node 24 and pnpm 10.34.5: