Skip to content

[SDK] Refactor: Update Pay Functions to use UB #7064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
efc4687
[SDK] Feature: Switch buyWithCrypto to Universal Bridge service
gregfromstl May 15, 2025
6744d0a
[SDK] Feature: Add Bridge.Transfer module
gregfromstl May 15, 2025
77ad035
changeset
gregfromstl May 15, 2025
2c33488
Merge branch 'main' into greg/tool-4362-switch-payembed-to-ub-service
gregfromstl May 15, 2025
02b1c4a
[SDK] Update Routes endpoint for transaction pay modal
gregfromstl May 15, 2025
8112650
changeset
gregfromstl May 15, 2025
dfe29f0
lint
gregfromstl May 15, 2025
421d959
chore: use configured bridge domain
gregfromstl May 15, 2025
cdaf130
max limit, domains
joaquim-verges May 16, 2025
91de2cc
add icon, duration
joaquim-verges May 16, 2025
727d0e9
fix approval, minimal status adapter
joaquim-verges May 16, 2025
abd77ec
refactor: update import for Transfer module in getTransfer.ts
gregfromstl May 16, 2025
1b00833
Merge branch 'greg/tool-4506-update-routes-endpoint' of https://githu…
gregfromstl May 16, 2025
6eff0e0
[Docs] Stylus dev workflow (#7060)
kumaryash90 May 15, 2025
ea05f7d
[SDK] Export prepareUserOp and add support for new wallets (#7061)
joaquim-verges May 15, 2025
57ecf61
[Vault] default to engine-cloud vault proxy (#7058)
d4mr May 15, 2025
05516bd
[Docs] Engine (#7057)
saminacodes May 16, 2025
267feda
[DOCS] fix link in contributing.md (#7063)
gap-editor May 16, 2025
c4a9fac
[Engine] Add FAILED status to execution result and update error handl…
joaquim-verges May 16, 2025
a7c1257
[Engine] Add all testnets to test transaction options (#7066)
joaquim-verges May 16, 2025
378993b
[Docs] Engine Update (#7052)
saminacodes May 16, 2025
e701530
[SDK] Fix buyWithCrypto false not respected when returning from quote…
joaquim-verges May 16, 2025
4bc4caf
[Dashboard] Add pagination to server wallets page (#7070)
joaquim-verges May 16, 2025
b953056
Version Packages (#7042)
joaquim-verges May 16, 2025
72bc53b
[SDK] Update package READMEs with correct naming and documentation li…
joaquim-verges May 16, 2025
c6af295
Prioritize JWT over service API keys in authentication (#7020)
jnsdls May 16, 2025
58ca5f7
chore: Update Accelerate features list (#7053)
arcoraven May 17, 2025
b128358
Version Packages (#7073)
joaquim-verges May 17, 2025
6d8d185
[Dashboard] Remove taint for THIRDWEB_ENGINE_URL (#7074)
joaquim-verges May 17, 2025
197d85b
update status endpoint
joaquim-verges May 17, 2025
8aa198b
fix 0 balance issue
joaquim-verges May 17, 2025
638dddc
lint
joaquim-verges May 17, 2025
2e6ad54
Merge remote-tracking branch 'origin' into greg/tool-4506-update-rout…
joaquim-verges May 17, 2025
4eeca27
fix
joaquim-verges May 17, 2025
8bcecf5
fix test
joaquim-verges May 17, 2025
f989121
remove tx history, show 0 fees for now
joaquim-verges May 18, 2025
535ee7e
lint
joaquim-verges May 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/open-readers-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Faster useSendTransaction execution
154 changes: 154 additions & 0 deletions .changeset/stale-yaks-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
"thirdweb": minor
---

Adds Bridge.Transfer module for direct token transfers:

```typescript
import { Bridge, NATIVE_TOKEN_ADDRESS } from "thirdweb";

const quote = await Bridge.Transfer.prepare({
chainId: 1,
tokenAddress: NATIVE_TOKEN_ADDRESS,
amount: toWei("0.01"),
sender: "0x...",
receiver: "0x...",
client: thirdwebClient,
});
```

This will return a quote that might look like:
```typescript
{
originAmount: 10000026098875381n,
destinationAmount: 10000000000000000n,
blockNumber: 22026509n,
timestamp: 1741730936680,
estimatedExecutionTimeMs: 1000
steps: [
{
originToken: {
chainId: 1,
address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
symbol: "ETH",
name: "Ethereum",
decimals: 18,
priceUsd: 2000,
iconUri: "https://..."
},
destinationToken: {
chainId: 1,
address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
symbol: "ETH",
name: "Ethereum",
decimals: 18,
priceUsd: 2000,
iconUri: "https://..."
},
originAmount: 10000026098875381n,
destinationAmount: 10000000000000000n,
estimatedExecutionTimeMs: 1000
transactions: [
{
action: "approval",
id: "0x",
to: "0x...",
data: "0x...",
chainId: 1,
type: "eip1559"
},
{
action: "transfer",
to: "0x...",
value: 10000026098875381n,
data: "0x...",
chainId: 1,
type: "eip1559"
}
]
}
],
expiration: 1741730936680,
intent: {
chainId: 1,
tokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
amount: 10000000000000000n,
sender: "0x...",
receiver: "0x..."
}
}
```

## Sending the transactions
The `transactions` array is a series of [ox](https://oxlib.sh) EIP-1559 transactions that must be executed one after the other in order to fulfill the complete route. There are a few things to keep in mind when executing these transactions:
- Approvals will have the `approval` action specified. You can perform approvals with `sendAndConfirmTransaction`, then proceed to the next transaction.
- All transactions are assumed to be executed by the `sender` address, regardless of which chain they are on. The final transaction will use the `receiver` as the recipient address.
- If an `expiration` timestamp is provided, all transactions must be executed before that time to guarantee successful execution at the specified price.

NOTE: To get the status of each non-approval transaction, use `Bridge.status` rather than checking for transaction inclusion. This function will ensure full completion of the transfer.

You can include arbitrary data to be included on any webhooks and status responses with the `purchaseData` option:

```ts
const quote = await Bridge.Transfer.prepare({
chainId: 1,
tokenAddress: NATIVE_TOKEN_ADDRESS,
amount: toWei("0.01"),
sender: "0x...",
receiver: "0x...",
purchaseData: {
reference: "payment-123",
metadata: {
note: "Transfer to Alice"
}
},
client: thirdwebClient,
});
```

## Fees
There may be fees associated with the transfer. These fees are paid by the `feePayer` address, which defaults to the `sender` address. You can specify a different address with the `feePayer` option. If you do not specify an option or explicitly specify `sender`, the fees will be added to the input amount. If you specify the `receiver` as the fee payer the fees will be subtracted from the destination amount.

For example, if you were to request a transfer with `feePayer` set to `receiver`:
```typescript
const quote = await Bridge.Transfer.prepare({
chainId: 1,
tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
amount: 100_000_000n, // 100 USDC
sender: "0x...",
receiver: "0x...",
feePayer: "receiver",
client: thirdwebClient,
});
```

The returned quote might look like:
```typescript
{
originAmount: 100_000_000n, // 100 USDC
destinationAmount: 99_970_000n, // 99.97 USDC
...
}
```

If you were to request a transfer with `feePayer` set to `sender`:
```typescript
const quote = await Bridge.Transfer.prepare({
chainId: 1,
tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
amount: 100_000_000n, // 100 USDC
sender: "0x...",
receiver: "0x...",
feePayer: "sender",
client: thirdwebClient,
});
```

The returned quote might look like:
```typescript
{
originAmount: 100_030_000n, // 100.03 USDC
destinationAmount: 100_000_000n, // 100 USDC
...
}
```
2 changes: 2 additions & 0 deletions apps/dashboard/src/@/constants/thirdweb.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
NEXT_PUBLIC_IPFS_GATEWAY_URL,
} from "@/constants/public-envs";
import {
THIRDWEB_BRIDGE_URL,
THIRDWEB_BUNDLER_DOMAIN,
THIRDWEB_INAPP_WALLET_DOMAIN,
THIRDWEB_INSIGHT_API_DOMAIN,
Expand Down Expand Up @@ -35,6 +36,7 @@ export function getConfiguredThirdwebClient(options: {
social: THIRDWEB_SOCIAL_API_DOMAIN,
bundler: THIRDWEB_BUNDLER_DOMAIN,
insight: THIRDWEB_INSIGHT_API_DOMAIN,
bridge: THIRDWEB_BRIDGE_URL,
});
}

Expand Down
3 changes: 3 additions & 0 deletions apps/dashboard/src/constants/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ export const THIRDWEB_INSIGHT_API_DOMAIN =

export const THIRDWEB_ANALYTICS_DOMAIN =
process.env.NEXT_PUBLIC_ANALYTICS_URL || "c.thirdweb-dev.com";

export const THIRDWEB_BRIDGE_URL =
process.env.NEXT_PUBLIC_BRIDGE_URL || "bridge.thirdweb-dev.com";
2 changes: 2 additions & 0 deletions apps/login/src/lib/dev-mode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setThirdwebDomains } from "thirdweb/utils";
import {
THIRDWEB_BRIDGE_DOMAIN,
THIRDWEB_BUNDLER_DOMAIN,
THIRDWEB_INAPP_WALLET_DOMAIN,
THIRDWEB_PAY_DOMAIN,
Expand All @@ -19,6 +20,7 @@ export function initDevMode() {
storage: THIRDWEB_STORAGE_DOMAIN,
social: THIRDWEB_SOCIAL_API_DOMAIN,
bundler: THIRDWEB_BUNDLER_DOMAIN,
bridge: THIRDWEB_BRIDGE_DOMAIN,
});
}
}
3 changes: 3 additions & 0 deletions apps/login/src/lib/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ export const THIRDWEB_SOCIAL_API_DOMAIN =

export const THIRDWEB_BUNDLER_DOMAIN =
process.env.NEXT_PUBLIC_BUNDLER_URL || "bundler.thirdweb-dev.com";

export const THIRDWEB_BRIDGE_DOMAIN =
process.env.NEXT_PUBLIC_BRIDGE_URL || "bridge.thirdweb-dev.com";
1 change: 1 addition & 0 deletions apps/playground-web/src/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ setThirdwebDomains({
pay: process.env.NEXT_PUBLIC_PAY_URL,
analytics: process.env.NEXT_PUBLIC_ANALYTICS_URL,
insight: process.env.NEXT_PUBLIC_INSIGHT_URL,
bridge: process.env.NEXT_PUBLIC_BRIDGE_URL,
});

const isDev =
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/.size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "thirdweb (cjs)",
"path": "./dist/cjs/exports/thirdweb.js",
"limit": "200 kB"
"limit": "350 kB"
},
{
"name": "thirdweb (minimal + tree-shaking)",
Expand Down
30 changes: 18 additions & 12 deletions packages/thirdweb/src/bridge/Buy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Address as ox__Address } from "ox";
import { defineChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
import { getThirdwebBaseUrl } from "../utils/domains.js";
import { getClientFetch } from "../utils/fetch.js";
import { stringify } from "../utils/json.js";
import { UNIVERSAL_BRIDGE_URL } from "./constants.js";
import type { PreparedQuote, Quote } from "./types/Quote.js";

/**
Expand Down Expand Up @@ -113,12 +113,13 @@ export async function quote(options: quote.Options): Promise<quote.Result> {
"buyAmountWei" in options ? options.buyAmountWei : options.amount;

const clientFetch = getClientFetch(client);
const url = new URL(`${UNIVERSAL_BRIDGE_URL}/buy/quote`);
const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/buy/quote`);
url.searchParams.set("originChainId", originChainId.toString());
url.searchParams.set("originTokenAddress", originTokenAddress);
url.searchParams.set("destinationChainId", destinationChainId.toString());
url.searchParams.set("destinationTokenAddress", destinationTokenAddress);
url.searchParams.set("buyAmountWei", amount.toString());
url.searchParams.set("amount", amount.toString());
if (maxSteps) {
url.searchParams.set("maxSteps", maxSteps.toString());
}
Expand Down Expand Up @@ -199,7 +200,7 @@ export declare namespace quote {
* This will return a quote that might look like:
* ```typescript
* {
* originAmount: 10000026098875381n,
* originAmount: 2000030000n,
* destinationAmount: 1000000000000000000n,
* blockNumber: 22026509n,
* timestamp: 1741730936680,
Expand All @@ -208,11 +209,11 @@ export declare namespace quote {
* {
* originToken: {
* chainId: 1,
* address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* symbol: "ETH",
* name: "Ethereum",
* decimals: 18,
* priceUsd: 2000,
* address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
* symbol: "USDC",
* name: "USDC",
* decimals: 6,
* priceUsd: 1,
* iconUri: "https://..."
* },
* destinationToken: {
Expand All @@ -224,7 +225,7 @@ export declare namespace quote {
* priceUsd: 2000,
* iconUri: "https://..."
* },
* originAmount: 10000026098875381n,
* originAmount: 2000030000n,
* destinationAmount: 1000000000000000000n,
* estimatedExecutionTimeMs: 1000
* transactions: [
Expand All @@ -250,7 +251,7 @@ export declare namespace quote {
* expiration: 1741730936680,
* intent: {
* originChainId: 1,
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
* destinationChainId: 10,
* destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* amount: 1000000000000000000n
Expand Down Expand Up @@ -334,15 +335,16 @@ export async function prepare(
} = options;

const clientFetch = getClientFetch(client);
const url = new URL(`${UNIVERSAL_BRIDGE_URL}/buy/prepare`);
const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/buy/prepare`);

const response = await clientFetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: stringify({
buyAmountWei: amount.toString(),
buyAmountWei: amount.toString(), // legacy
amount: amount.toString(),
originChainId: originChainId.toString(),
originTokenAddress,
destinationChainId: destinationChainId.toString(),
Expand Down Expand Up @@ -382,6 +384,8 @@ export async function prepare(
destinationChainId,
destinationTokenAddress,
amount,
sender,
receiver,
},
};
}
Expand All @@ -407,6 +411,8 @@ export declare namespace prepare {
destinationChainId: number;
destinationTokenAddress: ox__Address.Address;
amount: bigint;
sender: ox__Address.Address;
receiver: ox__Address.Address;
purchaseData?: unknown;
};
};
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/bridge/Chains.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ThirdwebClient } from "../client/client.js";
import { getThirdwebBaseUrl } from "../utils/domains.js";
import { getClientFetch } from "../utils/fetch.js";
import { UNIVERSAL_BRIDGE_URL } from "./constants.js";
import type { Chain } from "./types/Chain.js";

/**
Expand Down Expand Up @@ -54,7 +54,7 @@ export async function chains(options: chains.Options): Promise<chains.Result> {
const { client } = options;

const clientFetch = getClientFetch(client);
const url = new URL(`${UNIVERSAL_BRIDGE_URL}/chains`);
const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/chains`);

const response = await clientFetch(url.toString());
if (!response.ok) {
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/bridge/Routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Address as ox__Address, Hex as ox__Hex } from "ox";
import type { ThirdwebClient } from "../client/client.js";
import { getThirdwebBaseUrl } from "../utils/domains.js";
import { getClientFetch } from "../utils/fetch.js";
import { UNIVERSAL_BRIDGE_URL } from "./constants.js";
import type { Route } from "./types/Route.js";

/**
Expand Down Expand Up @@ -133,7 +133,7 @@ export async function routes(options: routes.Options): Promise<routes.Result> {
} = options;

const clientFetch = getClientFetch(client);
const url = new URL(`${UNIVERSAL_BRIDGE_URL}/routes`);
const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/routes`);
if (originChainId) {
url.searchParams.set("originChainId", originChainId.toString());
}
Expand Down
Loading
Loading