Skip to content

Commit 7d08528

Browse files
WIP brige embed
1 parent 38627d3 commit 7d08528

39 files changed

+6001
-131
lines changed

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Welcome, AI copilots! This guide captures the coding standards, architectural de
2020
- Biome governs formatting and linting; its rules live in biome.json.
2121
- Run pnpm biome check --apply before committing.
2222
- Avoid editor‑specific configs; rely on the shared settings.
23+
- make sure everything builds after each file change by running `pnpm build`
2324

2425
2526

@@ -39,7 +40,8 @@ Welcome, AI copilots! This guide captures the coding standards, architectural de
3940
- Co‑locate tests: foo.ts ↔ foo.test.ts.
4041
- Use real function invocations with stub data; avoid brittle mocks.
4142
- For network interactions, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce.
42-
- Keep tests deterministic and side‑effect free; Jest is pre‑configured.
43+
- Keep tests deterministic and side‑effect free; Vitest is pre‑configured.
44+
- to run the tests: `cd packages thirdweb & pnpm test:dev <filename>`
4345

4446
4547

packages/thirdweb/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
"@thirdweb-dev/insight": "workspace:*",
241241
"@walletconnect/ethereum-provider": "2.20.1",
242242
"@walletconnect/sign-client": "2.20.1",
243+
"@xstate/fsm": "^2.1.0",
243244
"abitype": "1.0.8",
244245
"cross-spawn": "7.0.6",
245246
"fuse.js": "7.1.0",

packages/thirdweb/scripts/wallets/generate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,11 @@ export type MinimalWalletInfo = {
238238
/**
239239
* @internal
240240
*/
241-
const ALL_MINIMAL_WALLET_INFOS = <const>${JSON.stringify(
241+
const ALL_MINIMAL_WALLET_INFOS = ${JSON.stringify(
242242
[...walletInfos, ...customWalletInfos],
243243
null,
244244
2,
245-
)} satisfies MinimalWalletInfo[];
245+
)} as const satisfies MinimalWalletInfo[];
246246
247247
export default ALL_MINIMAL_WALLET_INFOS;
248248
`,

packages/thirdweb/src/pay/convert/cryptoToFiat.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { Address } from "abitype";
22
import type { Chain } from "../../chains/types.js";
33
import type { ThirdwebClient } from "../../client/client.js";
4-
import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
5-
import { getBytecode } from "../../contract/actions/get-bytecode.js";
6-
import { getContract } from "../../contract/contract.js";
74
import { isAddress } from "../../utils/address.js";
85
import { getTokenPrice } from "./get-token.js";
96
import type { SupportedFiatCurrency } from "./type.js";
@@ -76,21 +73,6 @@ export async function convertCryptoToFiat(
7673
"Invalid fromTokenAddress. Expected a valid EVM contract address",
7774
);
7875
}
79-
// Make sure it's either a valid contract or a native token address
80-
if (fromTokenAddress.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
81-
const bytecode = await getBytecode(
82-
getContract({
83-
address: fromTokenAddress,
84-
chain,
85-
client,
86-
}),
87-
).catch(() => undefined);
88-
if (!bytecode || bytecode === "0x") {
89-
throw new Error(
90-
`Error: ${fromTokenAddress} on chainId: ${chain.id} is not a valid contract address.`,
91-
);
92-
}
93-
}
9476

9577
const price = await getTokenPrice(client, fromTokenAddress, chain.id);
9678
if (!price) {

packages/thirdweb/src/pay/convert/fiatToCrypto.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { Address } from "abitype";
22
import type { Chain } from "../../chains/types.js";
33
import type { ThirdwebClient } from "../../client/client.js";
4-
import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
5-
import { getBytecode } from "../../contract/actions/get-bytecode.js";
6-
import { getContract } from "../../contract/contract.js";
74
import { isAddress } from "../../utils/address.js";
85
import { getTokenPrice } from "./get-token.js";
96
import type { SupportedFiatCurrency } from "./type.js";
@@ -75,21 +72,6 @@ export async function convertFiatToCrypto(
7572
if (!isAddress(to)) {
7673
throw new Error("Invalid `to`. Expected a valid EVM contract address");
7774
}
78-
// Make sure it's either a valid contract or a native token
79-
if (to.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
80-
const bytecode = await getBytecode(
81-
getContract({
82-
address: to,
83-
chain,
84-
client,
85-
}),
86-
).catch(() => undefined);
87-
if (!bytecode || bytecode === "0x") {
88-
throw new Error(
89-
`Error: ${to} on chainId: ${chain.id} is not a valid contract address.`,
90-
);
91-
}
92-
}
9375
const price = await getTokenPrice(client, to, chain.id);
9476
if (!price || price === 0) {
9577
throw new Error(
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# BridgeEmbed 2.0 — **Product Specification (Revised)**
2+
3+
**Version:** 1.0
4+
**Updated:** 30 May 2025
5+
**Author:** Product Team, thirdweb
6+
7+
---
8+
9+
## 1 · Purpose
10+
11+
BridgeEmbed is a drop-in replacement for PayEmbed that unlocks multi-hop cross-chain payments, token swaps, and fiat on-ramp flows by building on the new Bridge.\* API layer.
12+
Developers should adopt the widget with zero code changes to existing PayEmbed integrations (same props & callbacks) while gaining:
13+
14+
- Swap, bridge, or transfer any crypto asset to any asset on the desired chain.
15+
- Accept fiat (card/Apple Pay/Google Pay) via on-ramp partners and settle in the target token.
16+
- Support three payment contexts—funding a wallet, paying a seller, or funding a transaction.
17+
- Automatic route discovery, optimisation, and step-by-step execution via Bridge.routes, quote, prepare, and status.
18+
19+
### Goal
20+
21+
| Success Criteria | Description |
22+
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
23+
| Drop-in upgrade | Swap `<PayEmbed … />` for `<BridgeEmbed … />` and see identical behaviour for same-chain / same-token payments. |
24+
| Multi-hop routing | Fund USDC on Base using an NZD credit card, or swap MATIC→ETH→USDC across chains in one flow. |
25+
| Unified UX | All three modes share one cohesive flow (quote → route preview → step runner → success). |
26+
| Fast integration | ≤ 5-minute copy-paste setup, props-only—no back-end work. |
27+
28+
### 3 modes to cover different use cases
29+
30+
| Mode | Typical Use-case | Destination of Funds |
31+
| --------------------- | ------------------------------------------------------------------- | -------------------------------------------- |
32+
| fund_wallet (default) | User acquires Token X for their own wallet. | Connected wallet |
33+
| direct_payment | User buys a product; seller requires Token Y on Chain C. | Seller address |
34+
| transaction | dApp needs to cover value/erc20Value of a prepared on-chain action. | Connected wallet, then transaction broadcast |
35+
36+
BridgeEmbed 2.0 is the successor to **PayEmbed** and delivers a **modular, cross-platform hook library and UI component** for fiat / crypto on-ramping, token swaps, bridging, and direct payments.
37+
Developers can import:
38+
39+
| Layer | What it contains | Platform variants |
40+
| -------------------------- | ------------------------------------------------------------------------ | ------------------------------- |
41+
| **Core hooks & utilities** | Logic, data-fetching, state machines, type helpers | **Shared** (single TS codebase) |
42+
| **Core UI components** | Payment-method picker, route preview, step runner, error & success views | **Web / React Native** |
43+
| **Higher-level flows** | `<FundWallet />`, `<DirectPayment />`, `<TransactionPayment />` | **Web / React Native** |
44+
| **Turn-key widget** | `<BridgeEmbed />` (switches flow by `mode` prop) | **Web / React Native** |
45+
46+
This structure keeps one business-logic layer while letting each platform ship native UX.
47+
48+
---
49+
50+
## 2 · High-Level Goals
51+
52+
| Goal | Success Criteria |
53+
| ------------------------- | --------------------------------------------------------------------------------------------- |
54+
| **Drop-in replacement** | Existing PayEmbed users swap imports; same props still work. |
55+
| **Modularity** | Apps may import only `useBridgeQuote` or `<PaymentMethodSelector />` without the full widget. |
56+
| **Cross-platform parity** | Web and React Native share ≥ 90 % of code via core hooks; UI feels native on each platform. |
57+
| **Robust error UX** | Every failure surfaces the underlying Bridge API message and offers a **Retry** action. |
58+
59+
---
60+
61+
## 3 · Package & File Structure
62+
63+
```
64+
packages/thirdweb/src/react/
65+
├─ core/ # shared TS logic & hooks
66+
│ └─ src/
67+
│ ├─ hooks/
68+
│ ├─ machines/ # XState or equivalent
69+
│ └─ utils/
70+
├─ web/ # React (DOM) components
71+
│ └─ components/
72+
└─ native/ # React Native components
73+
└─ components/
74+
```
75+
76+
---
77+
78+
## 4 · Key Exports
79+
80+
### 4.1 Hooks (core)
81+
82+
| Hook | Responsibility |
83+
| -------------------------- | -------------------------------------------------------------------------- |
84+
| `usePaymentMethods()` | Detect connected wallet balances, other-wallet option, available on-ramps. |
85+
| `useBridgeRoutes(params)` | Thin wrapper over `Bridge.routes/quote`; caches and re-tries. |
86+
| `useBridgePrepare(params)` | Call `Bridge.prepare`, returns signed tx set & metadata. |
87+
| `useStepExecutor(steps)` | Drive sequential execution + status polling, emits progress/error events. |
88+
| `useBridgeError()` | Provide typed error object with `.code`, `.message`, `.retry()` helper. |
89+
90+
### 4.2 Core UI Components
91+
92+
| Component | Props | Web / RN notes |
93+
| ----------------------- | -------------------------------- | --------------------------------------------- |
94+
| `PaymentMethodSelector` | `methods`, `onSelect` | Web: dropdown / wallet list; RN: ActionSheet. |
95+
| `RoutePreview` | `route`, `onConfirm` | Shows hops, fees, ETA. |
96+
| `StepRunner` | `steps`, `onComplete`, `onError` | Progress bar + per-step status. |
97+
| `ErrorBanner` | `error` | Always shows retry CTA. |
98+
| `SuccessScreen` | `receipt` | Shows final tx hash, share buttons. |
99+
100+
### 4.3 Higher-Level Components
101+
102+
| Name | Mode encapsulated |
103+
| ------------------------ | ------------------ |
104+
| `<FundWallet />` | `"fund_wallet"` |
105+
| `<DirectPayment />` | `"direct_payment"` |
106+
| `<TransactionPayment />` | `"transaction"` |
107+
108+
### 4.4 Turn-key Widget
109+
110+
```tsx
111+
import { BridgeEmbed } from "thirdweb/react";
112+
```
113+
114+
EXACT Same prop surface as `<PayEmbed />` for this one, should be a drop replacement with no code changes.
115+
116+
---
117+
118+
## 5 · User Flows
119+
120+
_All flows share the same state machine; UI differs by platform._
121+
122+
1. **Requirement Resolution** – derive target token/chain/amount.
123+
2. **Method Selection**`PaymentMethodSelector`.
124+
3. **Quote & Route**`useBridgeRoutes` → show `RoutePreview`.
125+
4. **Confirm** – user approves (wallet popup or on-ramp).
126+
5. **Execute Steps**`StepRunner` driven by `useStepExecutor`.
127+
6. **Success**`SuccessScreen` with receipts.
128+
7. **Error & Retry** – Any failure shows `ErrorBanner`; calling `.retry()` re-enters machine at the failed state (idempotent by design).
129+
130+
## 6. UX & UI Requirements
131+
132+
- Responsive (mobile-first; desktop ≤ 480 px width).
133+
- Single modal with internal stepper—no new windows.
134+
- Progress feedback: percent bar + “Step 2 / 4: Swapping MATIC → USDC”.
135+
- Retry / resume: if closed mid-flow, reopening fetches Bridge.status and resumes.
136+
- Theming: inherits PayEmbed theme prop (light/dark & accent).
137+
- Localization: reuse existing i18n keys; add new strings.
138+
139+
---
140+
141+
## 7 · Error Handling Guidelines
142+
143+
- **Surface origin:** Display `error.message` from Bridge/on-ramp APIs; prepend user-friendly context (“Swap failed – ”).
144+
- **Retry always available:** `StepRunner` pauses; user can press **Retry** (calls hook’s `.retry()`) or **Cancel**.
145+
- **Automatic back-off:** Core hooks implement exponential back-off for transient network errors.
146+
- **Developer visibility:** All hooks throw typed errors so host apps can catch & log if using components piecemeal.
147+
148+
---
149+
150+
## 8 · Cross-Platform Parity Requirements
151+
152+
| Feature | Web | React Native |
153+
| ----------------- | ---------------------------------------- | ------------------------------------------------------ |
154+
| Wallet connectors | MetaMask, Coinbase Wallet, WalletConnect | WalletConnect, MetaMask Mobile Deeplink, in-app wallet |
155+
| Fiat on-ramp UI | window popup (Stripe, Ramp) | Safari/Chrome Custom Tab / In-App Browser |
156+
| Step progress | Horizontal stepper with overall progress | Vertical list with checkmarks |
157+
158+
The **state machine & hooks are identical**; only presentation components differ.

0 commit comments

Comments
 (0)