@manifoldxyz/dropkit 0.1.0 → 0.2.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
@@ -110,6 +110,8 @@ The adapter reads allowance and creates an approval leg when required. Manifold
110
110
  | `onQuantityChange` | Receives the clamped requested quantity. The parent must update `quantity`. |
111
111
  | `connect` | Optional `{ walletConnectProjectId }` forwarded to the shared connect widget. |
112
112
  | `onEvent` | Optional event callback. No telemetry is sent by the package. |
113
+ | `crossChain` | Manifold claims only. Default on. `false` turns cross-chain minting off; `{ chains: [8453] }` narrows the pay chains. |
114
+ | `payChainId` | Forces the pay chain. The drop chain means a direct mint. Must be a pay chain. |
113
115
 
114
116
  `MintButton` additionally accepts `className`, `labels`, and `children`.
115
117
  A function child receives the full `MintState` and replaces the default button.
@@ -148,6 +150,8 @@ export function CustomMint({ config }: { config: MintConfig }) {
148
150
  | `refresh` | Stable callback returning `Promise<void>`. Re-reads drop data. Queues refreshes during an attempt. |
149
151
  | `result` | `{ txHash, explorerUrl, tokens }` after success, otherwise null. Token IDs and amounts are bigint. |
150
152
  | `canAct` | Whether a click can act. False during an attempt, including its pre-prompt planning phase. |
153
+ | `route` | `{ kind: 'direct', chainId }` or `{ kind: 'relay', originChainId, amountIn, relayFee, originGas, quotedAt }`. Null before the wallet read. |
154
+ | `crossChain` | Inputs for `PayWithPicker`. Null when Relay cannot mint this drop for this wallet. |
151
155
 
152
156
  `Money` is `{ value: bigint, decimals: number, symbol: string, formatted: string }`.
153
157
  The component ships no CSS. The default button exposes `data-state`, `data-reason`, `disabled`, and `aria-busy` during asynchronous states.
@@ -174,7 +178,7 @@ A reset click never sends another transaction. Fresh drop and wallet reads must
174
178
  | `sold-out` | Sold out | None |
175
179
  | `disconnected` | Connect wallet | Open connect modal |
176
180
  | `connecting` | Connecting… | None |
177
- | `live` | Mint · {total} | Switch chain if needed, then approve and mint |
181
+ | `live` | Mint · {total}, or Mint · {amount} on {chain} over Relay | Switch chain if needed, then approve and mint |
178
182
  | `ineligible` | Not eligible | None |
179
183
  | `insufficient-funds` | Insufficient {symbol} | None |
180
184
  | `switching-chain` | Switching network… | None |
@@ -207,10 +211,15 @@ ERC20 totals include the token price and any native fee. Gas is checked separate
207
211
  | `RPC_ERROR` | `error` | Read or transaction transport failed. Supply a working RPC URL. |
208
212
  | `CHAIN_SWITCH_FAILED` | `error` | Wallet could not switch to, or did not remain on, the configured chain. |
209
213
  | `ALLOWLIST_UNAVAILABLE` | `error` | Required allowlist data could not be loaded. |
214
+ | `BRIDGE_UNAVAILABLE` | `error` | Relay could not quote or returned a route the button cannot run. Nothing was sent. |
215
+ | `BRIDGE_TIMEOUT` | `error` | Paid, but Relay had no final status after 10 minutes. The next click resumes the same request. |
216
+ | `BRIDGE_REFUNDED` | `error` | Relay could not mint and is refunding the payer. |
217
+ | `BRIDGE_FAILED` | `error` | Relay failed. `message` names the Relay request and says whether a refund was sent. |
210
218
 
211
219
  ## Events and lifecycle
212
220
 
213
- `onEvent` receives `{ type, state, reason, chainId, quantity, config: { type, chainId, address }, txHash? }`.
221
+ `onEvent` receives `{ type, state, reason, chainId, quantity, config: { type, chainId, address }, route, originChainId, txHash? }`.
222
+ `route` is `direct` or `relay`. `originChainId` is the chain the collector pays on.
214
223
  Types are `state`, `attempt`, `approve-submitted`, `tx-submitted`, `success`, and `error`.
215
224
  The config address is the mint target for contract configs and the creator contract for Manifold claims.
216
225
  Transaction events use the frozen attempt, not later props. Payloads omit payer and recipient addresses.
@@ -229,18 +238,57 @@ Receipt parsing filters mint transfers by token contract and recipient. Cancella
229
238
  No direct analytics requests or `/public/instance/tx` posts are made.
230
239
  Manifold allowlist calls are GET requests. JSON-RPC reads and wallet transactions still use their normal transports.
231
240
 
241
+ ## Cross-chain mint (Relay)
242
+
243
+ A Manifold claim can be paid from another chain. Relay takes the payment there and calls `mintProxy` on the drop chain, minting to the recipient.
244
+
245
+ - **When:** native-priced claim on a V6/V7 extension (721 or 1155), drop on Ethereum, Base, Optimism or Shape (pay chains: Ethereum, Base, Optimism, Shape, ApeChain), or on Sepolia (pay chain: Base Sepolia). ERC20-priced drops, V8 claims and contract configs mint directly only.
246
+ - **Which chain pays:** the chain the wallet is on. A wallet on another pay chain with enough funds mints over Relay, even when the drop chain also has funds. A wallet on the drop chain mints directly.
247
+ - **Providers:** Dropkit only reads balances through the connected wallet, for the chain it is on. It never queries another chain's RPC. Relay quotes come from `api.relay.link` (no API key).
248
+ - **Cost:** the label shows the amount paid on the pay chain, excluding gas, e.g. `Mint · 0.00326 ETH on Base` for a 0.003 ETH mint (Relay fee ≈ 0.00026 ETH). The funds check includes gas.
249
+ - **Short on the wallet chain:** `Not enough ETH on Base. Pick another network to pay from.` Render `PayWithPicker` so collectors can switch.
250
+ - **One signature:** the collector signs one deposit on the pay chain. The button polls Relay until the mint lands on the drop chain, then reads token IDs. `result.relay` holds `{ requestId, originTxHash, originChainId }`.
251
+ - **Never pays twice on this page:** after a timeout, the next click resumes the same Relay request instead of paying again. Only one button per drop and wallet can prompt a payment at a time. A reload forgets it, same as claim-widgets.
252
+
253
+ `PayWithPicker` lists every pay chain with Relay's fee (`None` on the drop chain) and the balance of the wallet's current chain. Chains Relay cannot quote are hidden. Picking a row switches the wallet. It quotes every 20 seconds while mounted and renders nothing when Relay does not apply.
254
+
255
+ ```tsx
256
+ import { MintButton, PayWithPicker, type MintConfig } from '@manifoldxyz/dropkit';
257
+
258
+ export function CrossChainMint({ config }: { config: MintConfig }) {
259
+ return (
260
+ <MintButton config={config}>
261
+ {(m) => (
262
+ <>
263
+ <button type="button" onClick={m.mint} disabled={!m.canAct} data-state={m.state}>
264
+ {m.route?.kind === 'relay' ? `Mint · ${m.route.amountIn.formatted} ${m.route.amountIn.symbol}` : 'Mint'}
265
+ </button>
266
+ {m.message && <p role="status">{m.message}</p>}
267
+ <PayWithPicker mint={m} className="pay-with" />
268
+ </>
269
+ )}
270
+ </MintButton>
271
+ );
272
+ }
273
+ ```
274
+
275
+ Rows expose `data-chain`, `data-selected`, `data-loading` and `aria-pressed`. `renderRow` replaces the row content.
276
+
232
277
  ## Local verification
233
278
 
234
- From the repository root, use Node 24 and put Foundry binaries in `PATH`:
279
+ From the repository root, use Node 24 and pnpm 10.34.5:
235
280
 
236
281
  ```sh
237
282
  pnpm install --frozen-lockfile
238
283
  cd packages/react/dropkit
239
- pnpm exec vitest run
284
+ pnpm test # fast unit and component tests, no network
285
+ pnpm test:chain # adapters against a local Sepolia fork
240
286
  pnpm run typecheck
241
287
  pnpm run lint
242
288
  pnpm run build
289
+ pnpm verify:package # packed tarball in clean React 18 and 19 consumers
243
290
  ```
244
291
 
245
- The full tests require `SEPOLIA_RPC_URL` and `anvil`. Fork tests use block 11838500 and fail rather than skip when prerequisites are missing.
292
+ `test:chain` requires `anvil` on `PATH` and `SEPOLIA_RPC_URL`. It forks block 11838500 and fails rather than skips when either is missing.
293
+ `verify:package` needs npm registry access. It type-checks every TypeScript example in this README.
246
294
  The repository's pnpm policy enforces a seven-day release age, except for `@manifoldxyz/*` packages.