Compare commits

...
Author SHA1 Message Date
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
881c7a96f7 Version Packages (#8372)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-07 06:04:42 +07:00
FirekeeperandGitHub ad68969559 Refactor Solana sign transaction API for flexible inputs (#8371) 2025-11-07 05:56:11 +07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
915d955da1 Version Packages (#8370)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-06 13:58:49 -08:00
Joaquim VergesandGitHub 7da16cff2f [SDK] Make maxAmount optional in wrapFetchWithPayment (#8369) 2025-11-06 13:54:29 -08:00
Yash094 f0edc0f253 Update vault documentation text and faq (#8367)
```
[Portal] Fix: Update Vault open-source status and add FAQ

## Notes for the reviewer

This PR updates the Vault portal documentation to accurately reflect its open-source status.

*   Removed "open-source" from the main description and metadata on the Vault overview page.
*   Added a new FAQ entry under `/vault/faqs` clarifying that Vault is not yet open source.

## How to test

1.  Navigate to <https://portal.thirdweb.com/vault>
    *   Verify the main description no longer includes "open-source".
    *   (Optional) Inspect page metadata to confirm "open-source" is removed from the description.
2.  Navigate to <https://portal.thirdweb.com/vault/faqs>
    *   Verify the new FAQ "Is Vault open source?" is present with the answer "Vault is not yet open source, but we plan to release it soon."
```

---
[Slack Thread](https://thirdwebdev.slack.com/archives/C09DS2CKGP2/p1762411784588169?thread_ts=1762411784.588169&cid=C09DS2CKGP2)

<a href="https://cursor.com/background-agent?bcId=bc-ab8c2b24-6a74-4e0c-b302-d84d69ac5953"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-cursor-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-cursor-light.svg"><img alt="Open in Cursor" src="https://cursor.com/open-in-cursor.svg"></picture></a>&nbsp;<a href="https://cursor.com/agents?id=bc-ab8c2b24-6a74-4e0c-b302-d84d69ac5953"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-web-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-web-light.svg"><img alt="Open in Web" src="https://cursor.com/open-in-web.svg"></picture></a>

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the description of the `Vault` service to remove references to it being open source and refining the language for clarity.

### Detailed summary
- Added a new `Details` section in `apps/portal/src/app/vault/faqs/page.mdx` about Vault's open-source status.
- Updated the `description` in `apps/portal/src/app/vault/layout.tsx` to remove "open-source."
- Revised the `description` in `apps/portal/src/app/vault/page.mdx` to remove "open-source."
- Modified the main description of `Vault` in `apps/portal/src/app/vault/page.mdx` for clarity.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

## Documentation
* Added new FAQ entry addressing open-source availability with information on future release plans
* Updated Vault service descriptions emphasizing non-custodial key management design and TEE security architecture
* Refined documentation metadata to provide clearer communication of Vault's core features, security model, and service capabilities

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-06 17:58:37 +00:00
Yash094 0a2d269782 Dashboard: Update faucet amount for injective testnet (#8368)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds a new entry for the `Injective testnet` in the `faucet.ts` file, indicating a change in the configuration for test networks.

### Detailed summary
- Added a new entry for `Injective testnet` with a value of `1` in the configuration.
- The existing entry for `Arc testnet` remains unchanged.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

## New Features
* Added support for Injective testnet, enabling faucet claims on this network with customized claim amount configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-06 16:07:33 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
3d996d1d23 Version Packages (#8364)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-05 16:06:25 -08:00
Joaquim VergesandGitHub 40781b8170 [SDK] Allow custom chain for SIWE authentication (#8365) 2025-11-05 15:47:54 -08:00
MananTank d907017217 [MNY-295] Fix BuyWidget autoconnect not working when receiverAddress prop is set (#8363)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on fixing the `BuyWidget` component's autoconnect functionality when the `receiverAddress` prop is set, and it introduces a new story variant to demonstrate this behavior.

### Detailed summary
- Updated `BuyWidget` to conditionally render the `AutoConnect` component based on `connectOptions.autoConnect`.
- Introduced a new story `NoAutoConnect` in `BuyWidget.stories.tsx` to showcase the widget with `autoConnect` set to false.
- Modified `FundWallet.tsx` to ensure proper handling of the `autoConnect` prop.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
  * Fixed auto-connect functionality that was not working correctly when the receiver address property is set on the buy widget.

* **New Features**
  * Added configurable auto-connect option to the buy widget, enabling users to control whether auto-connection is enabled or disabled based on their needs.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-05 21:10:50 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
cf53c2d7f1 Version Packages (#8362)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-05 12:10:38 -08:00
Joaquim VergesandGitHub 207cb9dbeb [SDK] Enhance isMobile() detection with additional mobile signals (#8361) 2025-11-05 11:48:57 -08:00
Jonas DanielsandGitHub b4d365bdc0 Move x402 route from /payments/x402 to /x402 (#8360) 2025-11-04 16:30:08 -08:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
ea4e2fe376 Version Packages (#8357)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-04 14:20:21 -08:00
Joaquim VergesandGitHub 1d54cff7b4 [SDK] Add support for extra metadata in x402 payment requests (#8358) 2025-11-04 14:13:48 -08:00
YashandGitHub fd04eefea9 Update implementations, fix deployment (#8356) 2025-11-05 00:10:12 +07:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
4cc68310dc Version Packages (#8355)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-04 00:47:18 -08:00
Joaquim VergesandGitHub ccdce496e0 [SDK] Fix hiddenWallets prop not applying to all wallets screen (#8354) 2025-11-04 00:39:11 -08:00
Joaquim VergesandGitHub 2725f337f8 [Dashboard] Fix active users aggregation in project highlights card (#8346) 2025-11-02 18:23:41 -08:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2c6ea3e6dd Version Packages (#8340)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-31 18:48:20 -07:00
Joaquim VergesandGitHub 219fc69d72 [SDK] Update x402 flows to use remote /accepts endpoint (#8345) 2025-10-31 18:40:39 -07:00
Joaquim VergesandGitHub 8d17685683 [SDK] Refactor payment processing to use facilitator.accepts (#8344) 2025-10-31 18:38:51 -07:00
Jonas DanielsandGitHub 8ef418c543 Move success URL handling from PayPage to StyledBuyWidget component (#8342) 2025-10-30 14:35:32 -07:00
jnsdls b88f75b494 Add successUrl support to payment page (#8341)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces various configuration files, updates the `PayPage` component with URL validation, and enhances the fetch functionality with OAuth support. It also includes the addition of testing HTML and size limits for the package.

### Detailed summary
- Added `wrap` export in `nexus-fetch.ts`.
- Introduced `biome.json`, `.size-limit.json`, `tsconfig.json`, `tsconfig.build.json`, and `knip.json` for configuration.
- Enhanced `PayPage` with `onlyUrl` validation and success URL handling.
- Added testing HTML file for `nexus-fetch`.
- Updated `package.json` with author and dependencies.
- Improved fetch functionality with OAuth and error handling.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Payment page now supports redirecting to a provided success URL after payment completes.
  - New nexus-fetch library adds automatic OAuth token management, PKCE-backed sign-in flow, automatic token refresh, and integrated handling of proxy payment flows for web requests.

- **Chores**
  - Added build/configuration and size-limit files for the new nexus-fetch package.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-30 20:40:41 +00:00
MananTank c83f1c6f7a [MNY-298] SDK: Remove Bridge.routes, Update Docs (#8335)
<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview

This PR focuses on removing the `Bridge.routes` functionality and the `useBridgeRoutes` hook from the codebase, along with related references and documentation updates in various files.

### Detailed summary

- Deleted `Routes.ts`, `Routes.test.ts`, and `useBridgeRoutes.ts` files.
- Removed references to `Bridge.routes` in multiple `.mdx` files and sidebar components.
- Updated `index.ts` to stop exporting `routes`.
- Adjusted `useSendTransactionCore` to use `Bridge.tokens` instead of `Bridge.routes`.
- Removed the `PriceComparator` class and associated methods for route comparison.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Breaking Changes**
  * Removed the Bridge.routes API, which previously supported route discovery with filtering by chains and tokens, pagination, and sorting
  * Removed the useBridgeRoutes React hook

* **Documentation**
  * Removed documentation pages covering bridge route discovery and filtering functionality
  * Removed related sidebar and guide navigation links

* **Tests**
  * Removed test suites for removed APIs

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-30 14:46:34 +00:00
Joaquim VergesGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
bc0295487b Version Packages (#8339)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-30 07:07:28 +07:00
FirekeeperandGitHub 90b779107d Add Solana wallet balance endpoint and UI integration (#8338) 2025-10-30 06:56:00 +07:00
83 changed files with 1822 additions and 1574 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ module.exports = [
"/wallets",
"/account-abstraction",
"/payments",
"/payments/x402",
"/x402",
"/nexus",
"/auth",
"/in-app-wallets",
+5
View File
@@ -479,6 +479,11 @@ async function redirects() {
destination: "/tokens",
permanent: false,
},
{
source: "/payments/x402",
destination: "/x402",
permanent: false,
},
];
}
+2
View File
@@ -7,6 +7,8 @@ const customClaimAmounts: Record<number, number> = {
531050104: 5,
// Arc testnet
5042002: 1,
// Injective testnet
1439: 1,
};
const defaultClaimAmount = 0.01;
@@ -14,6 +14,7 @@ type AggregatedMetrics = {
export function ProjectHighlightsCard(props: {
selectedChart: string | undefined;
aggregatedUserStats: InAppWalletStats[];
userStats: InAppWalletStats[];
volumeStats: UniversalBridgeStats[];
teamSlug: string;
@@ -22,6 +23,7 @@ export function ProjectHighlightsCard(props: {
}) {
const {
selectedChart,
aggregatedUserStats,
userStats,
volumeStats,
teamSlug,
@@ -70,6 +72,12 @@ export function ProjectHighlightsCard(props: {
: "activeUsers"
}
aggregateFn={(_data, key) => {
if (key === "activeUsers") {
return aggregatedUserStats.reduce(
(acc, curr) => acc + curr.uniqueWalletsConnected,
0,
);
}
return timeSeriesData.reduce((acc, curr) => acc + curr[key], 0);
}}
chartConfig={chartConfig}
@@ -378,8 +378,18 @@ async function AsyncAppHighlightsCard(props: {
params: PageParams;
authToken: string;
}) {
const [walletUserStatsTimeSeries, universalBridgeUsage] =
const [aggregatedUserStats, walletUserStatsTimeSeries, universalBridgeUsage] =
await Promise.allSettled([
getInAppWalletUsage(
{
from: props.range.from,
period: "all",
projectId: props.project.id,
teamId: props.project.teamId,
to: props.range.to,
},
props.authToken,
),
getInAppWalletUsage(
{
from: props.range.from,
@@ -408,6 +418,11 @@ async function AsyncAppHighlightsCard(props: {
) {
return (
<ProjectHighlightsCard
aggregatedUserStats={
aggregatedUserStats.status === "fulfilled"
? aggregatedUserStats.value
: []
}
selectedChart={props.selectedChart}
selectedChartQueryParam={props.selectedChartQueryParam}
teamSlug={props.params.team_slug}
@@ -40,6 +40,13 @@ import {
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
import {
@@ -57,6 +64,7 @@ import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";
import { WalletProductIcon } from "@/icons/WalletProductIcon";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { cn } from "@/lib/utils";
import { fetchSolanaBalance } from "../lib/getSolanaBalance";
import { updateDefaultProjectWallet } from "../lib/vault.client";
import { CreateServerWallet } from "../server-wallets/components/create-server-wallet.client";
import type { Wallet as EVMWallet } from "../server-wallets/wallet-table/types";
@@ -79,6 +87,7 @@ interface ServerWalletsTableProps {
teamSlug: string;
client: ThirdwebClient;
solanaPermissionError?: boolean;
authToken: string;
}
export function ServerWalletsTable(props: ServerWalletsTableProps) {
@@ -95,10 +104,14 @@ export function ServerWalletsTable(props: ServerWalletsTableProps) {
solanaTotalPages,
client,
solanaPermissionError,
authToken,
} = props;
const [activeChain, setActiveChain] = useState<WalletChain>("evm");
const [selectedChainId, setSelectedChainId] = useState<number>(1);
const [selectedSolanaChain, setSelectedSolanaChain] = useState<
"solana:mainnet" | "solana:devnet"
>("solana:mainnet");
const [showSmartAccount, setShowSmartAccount] = useState(false);
const queryClient = useQueryClient();
@@ -145,11 +158,33 @@ export function ServerWalletsTable(props: ServerWalletsTableProps) {
</>
)}
{activeChain === "solana" && (
<CreateSolanaWallet
project={project}
teamSlug={teamSlug}
disabled={solanaPermissionError}
/>
<>
<CreateSolanaWallet
project={project}
teamSlug={teamSlug}
disabled={solanaPermissionError}
/>
<Select
value={selectedSolanaChain}
onValueChange={(value) =>
setSelectedSolanaChain(
value as "solana:mainnet" | "solana:devnet",
)
}
>
<SelectTrigger className="w-fit min-w-[180px] rounded-full bg-background hover:bg-accent/50">
<SelectValue placeholder="Select network" />
</SelectTrigger>
<SelectContent className="rounded-xl">
<SelectItem value="solana:mainnet" className="rounded-lg">
Solana Mainnet
</SelectItem>
<SelectItem value="solana:devnet" className="rounded-lg">
Solana Devnet
</SelectItem>
</SelectContent>
</Select>
</>
)}
</div>
@@ -278,6 +313,8 @@ export function ServerWalletsTable(props: ServerWalletsTableProps) {
project={project}
teamSlug={teamSlug}
client={client}
authToken={authToken}
chainId={selectedSolanaChain}
/>
))}
</TableBody>
@@ -507,11 +544,15 @@ function SolanaWalletRow({
project,
teamSlug,
client,
authToken,
chainId,
}: {
wallet: SolanaWallet;
project: Project;
teamSlug: string;
client: ThirdwebClient;
authToken: string;
chainId: "solana:mainnet" | "solana:devnet";
}) {
const engineService = project.services.find(
(s) => s.name === "engineCloud",
@@ -547,7 +588,12 @@ function SolanaWalletRow({
</TableCell>
<TableCell>
<SolanaWalletBalance publicKey={wallet.publicKey} />
<SolanaWalletBalance
publicKey={wallet.publicKey}
authToken={authToken}
clientId={project.publishableKey}
chainId={chainId}
/>
</TableCell>
<TableCell>
@@ -739,16 +785,27 @@ function WalletBalance({
);
}
function SolanaWalletBalance({ publicKey }: { publicKey: string }) {
function SolanaWalletBalance({
publicKey,
authToken,
clientId,
chainId,
}: {
publicKey: string;
authToken: string;
clientId: string;
chainId: "solana:mainnet" | "solana:devnet";
}) {
const balance = useQuery({
queryFn: async () => {
// TODO: Implement actual Solana balance fetching
return {
displayValue: "0",
symbol: "SOL",
};
return await fetchSolanaBalance({
publicKey,
authToken,
clientId,
chainId,
});
},
queryKey: ["solanaWalletBalance", publicKey],
queryKey: ["solanaWalletBalance", publicKey, chainId],
});
if (balance.isFetching) {
@@ -0,0 +1,60 @@
import { configure, getSolanaWalletBalance } from "@thirdweb-dev/api";
import { THIRDWEB_API_HOST } from "@/constants/urls";
// Configure the API client to use the correct base URL
configure({
override: {
baseUrl: THIRDWEB_API_HOST,
},
});
export async function fetchSolanaBalance({
publicKey,
authToken,
clientId,
chainId = "solana:mainnet",
}: {
publicKey: string;
authToken: string;
clientId: string;
chainId?: "solana:mainnet" | "solana:devnet";
}): Promise<{
displayValue: string;
symbol: string;
value: string;
decimals: number;
} | null> {
try {
const response = await getSolanaWalletBalance({
path: {
address: publicKey,
},
query: {
chainId,
},
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
"x-client-id": clientId,
},
});
if (response.error || !response.data) {
console.error(
"Error fetching Solana balance:",
response.error || "No data returned",
);
return null;
}
return {
displayValue: response.data.result.displayValue,
symbol: "SOL",
value: response.data.result.value,
decimals: response.data.result.decimals,
};
} catch (error) {
console.error("Error fetching Solana balance:", error);
return null;
}
}
@@ -215,6 +215,7 @@ export default async function TransactionsAnalyticsPage(props: {
solanaWallets={solanaAccounts.data.items}
teamSlug={params.team_slug}
solanaPermissionError={isSolanaPermissionError}
authToken={authToken}
/>
)}
</div>
@@ -70,8 +70,8 @@ export function CreateSolanaWallet(props: {
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button
variant="default"
className="gap-2 rounded-full bg-foreground hover:bg-foreground/90"
variant="outline"
className="gap-1.5 rounded-full bg-background text-foreground"
disabled={props.disabled}
>
<PlusIcon className="size-4" />
@@ -202,6 +202,7 @@ export default async function Page(props: {
solanaWallets={solanaAccounts.data.items}
teamSlug={params.team_slug}
solanaPermissionError={isSolanaPermissionError || false}
authToken={authToken}
/>
)}
</div>
@@ -9,7 +9,12 @@ import { payLandingWallets } from "./wallets";
const client = getClientThirdwebClient();
export function StyledBuyWidget(
props: Omit<BuyWidgetProps, "client" | "theme">,
props: Omit<
BuyWidgetProps,
"client" | "theme" | "onSuccess" | "onError" | "onCancel"
> & {
successUrl?: string;
},
) {
const { theme } = useTheme();
@@ -22,6 +27,22 @@ export function StyledBuyWidget(
connectOptions={{
wallets: payLandingWallets,
}}
onSuccess={() => {
if (props.successUrl) {
try {
const url = new URL(props.successUrl);
url.searchParams.set("success", "true");
window.location.href = url.toString();
} catch (error) {
// Log URL construction error for debugging
console.error(
"Failed to construct redirect URL:",
props.successUrl,
error,
);
}
}
}}
/>
);
}
+30 -4
View File
@@ -21,19 +21,44 @@ type SearchParams = {
[key: string]: string | string[] | undefined;
};
const onlyAddress = (v: string) => (isAddress(v) ? v : undefined);
const onlyNumber = (v: string) =>
Number.isNaN(Number(v)) ? undefined : Number(v);
/**
* Validates and normalizes a URL string.
* Only allows http: and https: protocols with a valid hostname.
* @returns normalized URL string on success, undefined on failure
*/
const onlyUrl = (v: string): string | undefined => {
try {
const url = new URL(v);
// Only allow http or https protocols
if (url.protocol !== "http:" && url.protocol !== "https:") {
return undefined;
}
// Ensure hostname is non-empty
if (!url.hostname) {
return undefined;
}
// Return normalized URL
return url.toString();
} catch {
// Invalid URL format
return undefined;
}
};
export default async function PayPage(props: {
searchParams: Promise<SearchParams>;
}) {
const searchParams = await props.searchParams;
const onlyAddress = (v: string) => (isAddress(v) ? v : undefined);
const onlyNumber = (v: string) =>
Number.isNaN(Number(v)) ? undefined : Number(v);
const receiver = parse(searchParams.receiver, onlyAddress);
const token = parse(searchParams.token, onlyAddress);
const chain = parse(searchParams.chain, onlyNumber);
const amount = parse(searchParams.amount, onlyNumber);
const successUrl = parse(searchParams.successUrl, onlyUrl);
return (
<ThemeProvider
@@ -51,6 +76,7 @@ export default async function PayPage(props: {
tokenAddress={token}
receiverAddress={receiver}
amount={amount ? amount.toString() : undefined}
successUrl={successUrl}
/>
</div>
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"dependencies": {
"@abstract-foundation/agw-react": "^1.6.4",
"@abstract-foundation/agw-react": "^1.10.0",
"@ai-sdk/react": "^2.0.25",
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-avatar": "^1.1.10",
-172
View File
@@ -1,172 +0,0 @@
import {
Callout,
OpenSourceCard,
createMetadata,
InstallTabs,
SDKCard,
Grid,
ArticleIconCard,
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@doc";
import {
ReactIcon,
TypeScriptIcon,
UnityIcon,
DotNetIcon,
UnrealEngineIcon,
} from "@/icons";
import { ExternalLink } from "lucide-react";
export const metadata = createMetadata({
image: {
title: "Find Routes",
icon: "payments",
},
title: "Find Routes",
description: "Find available routes for bridging, swapping, and paying.",
});
# Find Routes
Use thirdweb's `Bridge.routes` utility to find all routes between tokens.
### Get All Routes
Retrieve all supported routes:
```typescript
import { Bridge } from "thirdweb";
const routes = await Bridge.routes({
client,
});
```
This returns an array of route objects with information about supported token pairs:
```typescript
[
{
destinationToken: {
address: "0x12c88a3C30A7AaBC1dd7f2c08a97145F5DCcD830",
chainId: 1,
decimals: 18,
iconUri: "https://assets.coingecko.com/coins/images/37207/standard/G.jpg",
name: "G7",
symbol: "G7",
},
originToken: {
address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
chainId: 480,
decimals: 18,
iconUri: "https://assets.relay.link/icons/1/light.png",
name: "Ether",
symbol: "ETH",
}
},
// ... more routes
]
```
### Filter Routes by Origin
Get all routes starting from a specific chain and token:
```typescript
import { Bridge } from "thirdweb";
// Get all routes starting from mainnet ETH
const routes = await Bridge.routes({
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
client,
});
```
### Filter Routes by Destination
Find routes that end at a specific destination:
```typescript
import { Bridge } from "thirdweb";
// Get all routes ending at USDC on Optimism
const routes = await Bridge.routes({
destinationChainId: 10,
destinationTokenAddress: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
client,
});
```
### Filter Routes by Both Origin and Destination
Find direct routes between specific token pairs:
```typescript
import { Bridge } from "thirdweb";
// Get routes from mainnet ETH to USDC on Optimism
const routes = await Bridge.routes({
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
destinationChainId: 10,
destinationTokenAddress: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
client,
});
```
### Pagination
For large result sets, use pagination to retrieve routes in chunks:
```typescript
import { Bridge } from "thirdweb";
// Get the first 10 routes starting from mainnet ETH
const routes = await Bridge.routes({
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
limit: 10,
offset: 0,
client,
});
// Get the next 10 routes
const nextRoutes = await Bridge.routes({
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
limit: 10,
offset: 10,
client,
});
```
### Limit Route Complexity
Control the maximum number of steps in the returned routes:
```typescript
import { Bridge } from "thirdweb";
// Get only direct routes (1 step)
const directRoutes = await Bridge.routes({
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
maxSteps: 1,
client,
});
```
## Going further
To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides:
- [Send a Payment](/payments/send)
- [Get Token Prices](/payments/prices)
## Explore Full API References
- [Bridge.routes](/references/typescript/v5/routes)
-1
View File
@@ -60,7 +60,6 @@ You can execute this quote exactly like a buy. See the [Send a Payment](/payment
To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides:
- [Send a Payment](/payments/send)
- [Get Routes](/payments/routes)
## Explore Full API References
-4
View File
@@ -28,10 +28,6 @@ export const sidebar: SideBar = {
href: `${bridgeSlug}/tokens`,
name: "Get Token Prices",
},
{
href: `${bridgeSlug}/routes`,
name: "Get Routes",
},
{
href: `${bridgeSlug}/bridge-widget-script`,
name: "BridgeWidget Script",
+1 -108
View File
@@ -255,23 +255,6 @@ export class SwapManager {
throw new Error("Transaction timeout - please check status manually");
}
async getAvailableRoutes(params: {
originChainId?: number;
originTokenAddress?: string;
destinationChainId?: number;
destinationTokenAddress?: string;
limit?: number;
}) {
try {
return await Bridge.routes({
...params,
client: this.client,
});
} catch (error) {
throw new Error(`Failed to get routes: ${error.message}`);
}
}
}
```
@@ -319,92 +302,6 @@ async function swapEthToMatic(amount: string, userAccount: Account) {
}
```
### Advanced Price Comparison
Compare prices across multiple routes to find the best deal:
```typescript
interface RouteComparison {
route: any;
quote: SwapQuote;
priceImpact: number;
gasEstimate: bigint;
}
export class PriceComparator {
private swapManager: SwapManager;
constructor(swapManager: SwapManager) {
this.swapManager = swapManager;
}
async compareRoutes(
fromToken: { chainId: number; address: string },
amount: bigint,
userAddress: string
): Promise<RouteComparison[]> {
// Get available routes
const routes = await this.swapManager.getAvailableRoutes({
originChainId: fromToken.chainId,
originTokenAddress: fromToken.address,
limit: 10,
});
// Get quotes for each route
const comparisons: RouteComparison[] = [];
for (const route of routes) {
try {
const quote = await this.swapManager.getQuote({
fromChainId: route.originToken.chainId,
fromTokenAddress: route.originToken.address,
toChainId: route.destinationToken.chainId,
toTokenAddress: route.destinationToken.address,
amount,
userAddress,
client: this.swapManager.client,
});
const priceImpact = this.calculatePriceImpact(quote, amount);
const gasEstimate = this.estimateGasCosts(quote);
comparisons.push({
route,
quote,
priceImpact,
gasEstimate,
});
} catch (error) {
console.warn(`Failed to get quote for route ${route.originToken.symbol} -> ${route.destinationToken.symbol}:`, error);
}
}
// Sort by best output amount
return comparisons.sort((a, b) =>
Number(b.quote.destinationAmount) - Number(a.quote.destinationAmount)
);
}
private calculatePriceImpact(quote: SwapQuote, inputAmount: bigint): number {
// Simplified price impact calculation
const ratio = Number(quote.destinationAmount) / Number(quote.originAmount);
return Math.abs(1 - ratio) * 100; // Percentage
}
private estimateGasCosts(quote: SwapQuote): bigint {
// Sum up gas estimates from all transactions
return quote.steps.reduce((total, step) => {
return total + step.transactions.reduce((stepTotal: bigint, tx: any) => {
return stepTotal + (tx.gasLimit || 200000n); // Default estimate
}, 0n);
}, 0n);
}
}
```
<Callout variant="info">
**Gas Optimization Tip:** For frequently traded pairs, consider caching route data to reduce API calls and improve UI responsiveness.
</Callout>
</TabsContent>
</Tabs>
@@ -415,20 +312,16 @@ export class PriceComparator {
- **Cross-chain swaps** - Exchange tokens across 50+ supported chains
- **Real-time quotes** - Get up-to-date pricing and execution estimates
- **Route optimization** - Find the best path for any token pair
- **Price comparison** - Compare multiple routes to maximize output
- **Status tracking** - Monitor cross-chain transaction progress
## Going Further
- [Send a Payment](/payments/send) - Learn about peer-to-peer transfers
- [Get Routes](/payments/routes) - Explore route discovery APIs
- [Token Prices](/payments/tokens) - Access real-time token pricing
- [Token Prices](/bridge/tokens) - Access real-time token pricing
- [Webhooks](/payments/webhooks) - Get notified when swaps complete
## API Reference
- [Bridge.Buy.prepare](/references/typescript/v5/buy/prepare)
- [Bridge.routes](/references/typescript/v5/routes)
- [Bridge.status](/references/typescript/v5/status)
@@ -119,7 +119,6 @@ const nextTokens = await Bridge.tokens({
To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides:
- [Send a Payment](/payments/send)
- [Get Routes](/payments/routes)
## Explore Full API References
@@ -264,7 +264,6 @@ Once this code completes, your payment has been fully executed. Payments normall
- [Sell a Product](/payments/products)
- [Token Prices](/payments/tokens)
- [Routes](/payments/routes)
## Explore API References
-4
View File
@@ -49,10 +49,6 @@ export const sidebar: SideBar = {
href: `/bridge/tokens`,
name: "Get Token Prices",
},
{
href: `/bridge/routes`,
name: "Get Routes",
},
{
href: `${paymentsSlug}/webhooks`,
name: "Webhooks",
@@ -222,7 +222,6 @@ async function verifyWebhook(
To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides:
- [Send a Payment](/payments/send)
- [Find Routes](/payments/routes)
- [Get Token Prices](/payments/tokens)
## Explore Full API References
+4
View File
@@ -44,3 +44,7 @@ The admin key is temporarily stored in dashboard when performing actions that re
Please note, you should never share your admin key with third parties.
</Details>
<Details summary="Is Vault open source?">
Vault is not yet open source, but we plan to release it soon.
</Details>
+1 -1
View File
@@ -12,7 +12,7 @@ export default async function Layout(props: { children: React.ReactNode }) {
export const metadata = createMetadata({
description:
"Vault is an open-source non-custodial key management service, secured with TEE architecture (AWS Nitro Enclaves) and designed for blockchain applications.",
"Vault is a non-custodial key management service secured with TEE architecture (AWS Nitro Enclaves), designed for blockchain applications.",
title: "Vault",
image: {
icon: "vault",
+2 -2
View File
@@ -5,7 +5,7 @@ import { ArrowLeftRightIcon, UserLockIcon, UsersIcon, WalletIcon } from "lucide-
export const metadata = createMetadata({
title: "thirdweb Vault",
description: "An open-source, secure key management service for storing, managing, and protecting cryptographic keys and secrets.",
description: "A secure key management service for storing, managing, and protecting cryptographic keys and secrets.",
image: {
icon: "vault",
title: "thirdweb Vault",
@@ -14,7 +14,7 @@ export const metadata = createMetadata({
# Vault
Vault is an open-source non-custodial key management service, secured with TEE architecture (AWS Nitro Enclaves) and designed for blockchain applications.
Vault is a non-custodial key management service secured with TEE architecture (AWS Nitro Enclaves), designed for blockchain applications.
## Features
+56
View File
@@ -1,5 +1,61 @@
# wagmi-inapp
## 0.0.26
### Patch Changes
- Updated dependencies []:
- thirdweb@5.111.6
- @thirdweb-dev/wagmi-adapter@0.2.181
## 0.0.25
### Patch Changes
- Updated dependencies [[`7da16cf`](https://github.com/thirdweb-dev/js/commit/7da16cff2ff3aaa80c7fe5614639d950d88b5bf2)]:
- thirdweb@5.111.5
- @thirdweb-dev/wagmi-adapter@0.2.180
## 0.0.24
### Patch Changes
- Updated dependencies [[`d907017`](https://github.com/thirdweb-dev/js/commit/d907017217a0927e7f69eab0470b1fe120b4d1a1), [`40781b8`](https://github.com/thirdweb-dev/js/commit/40781b8170a1014ef76e438ba3c8244137dba7ea)]:
- thirdweb@5.111.4
- @thirdweb-dev/wagmi-adapter@0.2.179
## 0.0.23
### Patch Changes
- Updated dependencies [[`207cb9d`](https://github.com/thirdweb-dev/js/commit/207cb9dbeb82411809bafa2a8e833b1f10414b92)]:
- thirdweb@5.111.3
- @thirdweb-dev/wagmi-adapter@0.2.178
## 0.0.22
### Patch Changes
- Updated dependencies [[`1d54cff`](https://github.com/thirdweb-dev/js/commit/1d54cff7b442b8667ddd54bd783502c367ffc83e), [`fd04eef`](https://github.com/thirdweb-dev/js/commit/fd04eefea9a2a99eee3740d6746040c9f81cd43f)]:
- thirdweb@5.111.2
- @thirdweb-dev/wagmi-adapter@0.2.177
## 0.0.21
### Patch Changes
- Updated dependencies [[`ccdce49`](https://github.com/thirdweb-dev/js/commit/ccdce496e09bc9420b0e15854328651c01f36279)]:
- thirdweb@5.111.1
- @thirdweb-dev/wagmi-adapter@0.2.176
## 0.0.20
### Patch Changes
- Updated dependencies [[`c83f1c6`](https://github.com/thirdweb-dev/js/commit/c83f1c6f7a38c8a9f424798e6e726e1d037ed042), [`219fc69`](https://github.com/thirdweb-dev/js/commit/219fc69d72b1ca861bf0228589039788008ea0c0)]:
- thirdweb@5.111.0
- @thirdweb-dev/wagmi-adapter@0.2.175
## 0.0.19
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "wagmi-inapp",
"private": true,
"version": "0.0.19",
"version": "0.0.26",
"type": "module",
"scripts": {
"dev": "vite",
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/api
## 0.1.2
### Patch Changes
- [#8338](https://github.com/thirdweb-dev/js/pull/8338) [`90b7791`](https://github.com/thirdweb-dev/js/commit/90b779107d3cb726adeca6a3f885129c12599c6a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - added solana token balances endpoint
## 0.1.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/api",
"version": "0.1.1",
"version": "0.1.2",
"repository": {
"type": "git",
"url": "git+https://github.com/thirdweb-dev/js.git#main"
+28
View File
@@ -66,6 +66,9 @@ import type {
GetSolanaTransactionData,
GetSolanaTransactionErrors,
GetSolanaTransactionResponses,
GetSolanaWalletBalanceData,
GetSolanaWalletBalanceErrors,
GetSolanaWalletBalanceResponses,
GetTokenOwnersData,
GetTokenOwnersErrors,
GetTokenOwnersResponses,
@@ -1590,6 +1593,31 @@ export const createSolanaWallet = <ThrowOnError extends boolean = false>(
});
};
/**
* Get Solana Wallet Balance
* Get the SOL or SPL token balance for a Solana wallet on a specific Solana network.
*
* **Authentication**: Pass `x-client-id` for frontend usage from allowlisted origins or `x-secret-key` for backend usage.
*/
export const getSolanaWalletBalance = <ThrowOnError extends boolean = false>(
options: Options<GetSolanaWalletBalanceData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).get<
GetSolanaWalletBalanceResponses,
GetSolanaWalletBalanceErrors,
ThrowOnError
>({
security: [
{
name: "x-client-id",
type: "apiKey",
},
],
url: "/v1/solana/wallets/{address}/balance",
...options,
});
};
/**
* Sign Solana Message
* Sign an arbitrary message with a Solana wallet. Supports both text and hexadecimal message formats with automatic format detection.
+92 -83
View File
@@ -2135,6 +2135,10 @@ export type GetWalletTransactionsData = {
address: string;
};
query: {
/**
* Chain ID(s) to request transaction data for. You can specify multiple chain IDs by repeating the parameter, up to a maximum of 50. Example: ?chainId=1&chainId=137
*/
chainId: Array<number>;
/**
* Filter by block timestamp (Unix timestamp) greater than or equal to this value
*/
@@ -2171,10 +2175,6 @@ export type GetWalletTransactionsData = {
* Sort order: 'asc' for ascending, 'desc' for descending
*/
sortOrder?: "asc" | "desc";
/**
* Chain ID(s) to request transaction data for. You can specify multiple chain IDs by repeating the parameter, up to a maximum of 50. Example: ?chainId=1&chainId=137
*/
chainId: Array<number>;
};
url: "/v1/wallets/{address}/transactions";
};
@@ -3234,11 +3234,11 @@ export type WriteContractErrors = {
/**
* Payment ID
*/
id: string;
id?: string;
/**
* Bridge quote for completing the payment
*/
quote: {
quote?: {
/**
* Block number when quote was generated
*/
@@ -4314,11 +4314,11 @@ export type SendTransactionsErrors = {
/**
* Payment ID
*/
id: string;
id?: string;
/**
* Bridge quote for completing the payment
*/
quote: {
quote?: {
/**
* Block number when quote was generated
*/
@@ -4725,11 +4725,11 @@ export type PaymentsPurchaseErrors = {
/**
* Payment ID
*/
id: string;
id?: string;
/**
* Bridge quote for completing the payment
*/
quote: {
quote?: {
/**
* Block number when quote was generated
*/
@@ -4914,17 +4914,7 @@ export type VerifyX402PaymentData = {
paymentPayload: {
x402Version: number;
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
payload:
| {
signature: string;
@@ -4943,17 +4933,7 @@ export type VerifyX402PaymentData = {
};
paymentRequirements: {
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
maxAmountRequired: string;
resource: string;
description: string;
@@ -5015,7 +4995,6 @@ export type VerifyX402PaymentResponses = {
| "invalid_exact_svm_payload_transaction_instruction_not_spl_token_transfer_checked"
| "invalid_exact_svm_payload_transaction_instruction_not_token_2022_transfer_checked"
| "invalid_exact_svm_payload_transaction_not_a_transfer_instruction"
| "invalid_exact_svm_payload_transaction_cannot_derive_receiver_ata"
| "invalid_exact_svm_payload_transaction_receiver_ata_not_found"
| "invalid_exact_svm_payload_transaction_sender_ata_not_found"
| "invalid_exact_svm_payload_transaction_simulation_failed"
@@ -5049,17 +5028,7 @@ export type SettleX402PaymentData = {
paymentPayload: {
x402Version: number;
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
payload:
| {
signature: string;
@@ -5078,17 +5047,7 @@ export type SettleX402PaymentData = {
};
paymentRequirements: {
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
maxAmountRequired: string;
resource: string;
description: string;
@@ -5154,7 +5113,6 @@ export type SettleX402PaymentResponses = {
| "invalid_exact_svm_payload_transaction_instruction_not_spl_token_transfer_checked"
| "invalid_exact_svm_payload_transaction_instruction_not_token_2022_transfer_checked"
| "invalid_exact_svm_payload_transaction_not_a_transfer_instruction"
| "invalid_exact_svm_payload_transaction_cannot_derive_receiver_ata"
| "invalid_exact_svm_payload_transaction_receiver_ata_not_found"
| "invalid_exact_svm_payload_transaction_sender_ata_not_found"
| "invalid_exact_svm_payload_transaction_simulation_failed"
@@ -5183,7 +5141,10 @@ export type SettleX402PaymentResponses = {
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet";
| "sei-testnet"
| "polygon"
| "polygon-amoy"
| "peaq";
errorMessage?: string;
};
};
@@ -5226,17 +5187,7 @@ export type SupportedX402PaymentsResponses = {
kinds: Array<{
x402Version: 1;
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
extra?: {
defaultAsset?: {
/**
@@ -5340,11 +5291,11 @@ export type FetchWithPaymentErrors = {
/**
* Payment ID
*/
id: string;
id?: string;
/**
* Bridge quote for completing the payment
*/
quote: {
quote?: {
/**
* Block number when quote was generated
*/
@@ -5552,17 +5503,7 @@ export type ListPayableServicesResponses = {
x402Version: number;
accepts: Array<{
scheme: "exact";
network:
| "base-sepolia"
| "base"
| "avalanche-fuji"
| "avalanche"
| "iotex"
| "solana-devnet"
| "solana"
| "sei"
| "sei-testnet"
| string;
network: string;
maxAmountRequired: string;
resource: string;
description: string;
@@ -6298,11 +6239,11 @@ export type BridgeSwapErrors = {
/**
* Payment ID
*/
id: string;
id?: string;
/**
* Bridge quote for completing the payment
*/
quote: {
quote?: {
/**
* Block number when quote was generated
*/
@@ -6640,6 +6581,74 @@ export type CreateSolanaWalletResponses = {
export type CreateSolanaWalletResponse =
CreateSolanaWalletResponses[keyof CreateSolanaWalletResponses];
export type GetSolanaWalletBalanceData = {
body?: never;
path: {
/**
* Public key of the Solana wallet.
*/
address: string;
};
query: {
/**
* Solana network to query. Choose either solana:mainnet or solana:devnet.
*/
chainId: "solana:mainnet" | "solana:devnet";
/**
* SPL token mint address. Omit to retrieve native SOL balance.
*/
tokenAddress?: string;
};
url: "/v1/solana/wallets/{address}/balance";
};
export type GetSolanaWalletBalanceErrors = {
/**
* Invalid request parameters. This occurs when the wallet address or token mint is invalid, or the request mixes a token mint with multiple networks.
*/
400: unknown;
/**
* Authentication required. Include `x-client-id` or `x-secret-key` headers.
*/
401: unknown;
/**
* Internal server error. This may occur due to RPC connectivity issues or unexpected failures.
*/
500: unknown;
};
export type GetSolanaWalletBalanceResponses = {
/**
* Wallet balance retrieved successfully for the requested Solana network.
*/
200: {
/**
* Balance data for the requested Solana network.
*/
result: {
/**
* Requested Solana network.
*/
chainId: "solana:mainnet" | "solana:devnet";
/**
* Number of decimals used by the token.
*/
decimals: number;
/**
* Human-readable balance formatted using token decimals.
*/
displayValue: string;
/**
* Raw balance value expressed in base units (lamports).
*/
value: string;
};
};
};
export type GetSolanaWalletBalanceResponse =
GetSolanaWalletBalanceResponses[keyof GetSolanaWalletBalanceResponses];
export type SignSolanaMessageData = {
/**
* Request payload for signing an arbitrary Solana message.
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/engine
## 3.4.0
### Minor Changes
- [#8371](https://github.com/thirdweb-dev/js/pull/8371) [`ad68969`](https://github.com/thirdweb-dev/js/commit/ad68969559a80a5f188a647ef0e1c04f0003a0ad) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Reworked Solana Sign Transaction API that accepts more flexible inputs
## 3.3.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/engine",
"version": "3.3.0",
"version": "3.4.0",
"repository": {
"type": "git",
"url": "git+https://github.com/thirdweb-dev/js.git#main"
+27 -27
View File
@@ -274,6 +274,33 @@ export const sendSolanaTransaction = <ThrowOnError extends boolean = false>(
});
};
/**
* Sign Solana Transaction
* Sign a Solana transaction without broadcasting it
*/
export const signSolanaTransaction = <ThrowOnError extends boolean = false>(
options: Options<SignSolanaTransactionData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).post<
SignSolanaTransactionResponses,
unknown,
ThrowOnError
>({
security: [
{
name: "x-secret-key",
type: "apiKey",
},
],
url: "/v1/solana/sign/transaction",
...options,
headers: {
"Content-Type": "application/json",
...options.headers,
},
});
};
/**
* Cancel Transaction
* Attempt to cancel a queued transaction. Transactions that have been sent and are waiting for mine cannot be cancelled.
@@ -424,33 +451,6 @@ export const signSolanaMessage = <ThrowOnError extends boolean = false>(
});
};
/**
* Sign Solana Transaction
* Sign a serialized Solana transaction.
*/
export const signSolanaTransaction = <ThrowOnError extends boolean = false>(
options?: Options<SignSolanaTransactionData, ThrowOnError>,
) => {
return (options?.client ?? _heyApiClient).post<
SignSolanaTransactionResponses,
unknown,
ThrowOnError
>({
security: [
{
name: "x-secret-key",
type: "apiKey",
},
],
url: "/v1/solana/sign-transaction",
...options,
headers: {
"Content-Type": "application/json",
...options?.headers,
},
});
};
/**
* Get Transactions
* Search transactions with various filters and pagination
+90 -48
View File
@@ -556,6 +556,10 @@ export type Eip7702OwnerExecution = {
* The delegated EOA address
*/
from: AddressDef;
/**
* Optional fleet ID to route the transaction to a specific bundler fleet
*/
fleetId?: string | null;
};
/**
@@ -570,6 +574,10 @@ export type Eip7702SessionKeyExecution = {
* The account address is the address of a delegated account you want to execute the transaction on. This account has granted a session key to the `session_key_address`
*/
accountAddress: AddressDef;
/**
* Optional fleet ID to route the transaction to a specific bundler fleet
*/
fleetId?: string | null;
};
export type EmptyIdempotencySetResponse = {
@@ -1000,15 +1008,11 @@ export type RpcErrorResponse = {
/**
* Request to send a Solana transaction
*/
export type SendSolanaTransactionRequest = {
export type SendSolanaTransactionRequest = SolanaTransactionInput & {
/**
* Idempotency key for this transaction (defaults to random UUID)
*/
idempotencyKey?: string;
/**
* List of Solana instructions to execute
*/
instructions: Array<SolanaInstructionData>;
/**
* Solana execution options
*/
@@ -1188,6 +1192,30 @@ export type SignResultData = {
signedData: string;
};
/**
* Request to sign a Solana transaction
*/
export type SignSolanaTransactionRequest = SolanaTransactionInput & {
/**
* Solana execution options
*/
executionOptions: SolanaExecutionOptions;
};
/**
* Data returned from successful signing
*/
export type SignSolanaTransactionResponse = {
/**
* The signature (base58-encoded)
*/
signature: string;
/**
* The signed serialized transaction (base64-encoded)
*/
signedTransaction: string;
};
/**
* Request to sign typed data
*/
@@ -1452,6 +1480,21 @@ export type SolanaRpcResponseErrorData =
type: "NODE_UNHEALTHY";
};
/**
* Input for Solana transaction - either build from instructions or use pre-built
*/
export type SolanaTransactionInput =
| SolanaTransactionInputInstructions
| SolanaTransactionInputSerialized;
export type SolanaTransactionInputInstructions = {
instructions: Array<SolanaInstructionData>;
};
export type SolanaTransactionInputSerialized = {
transaction: string;
};
/**
* Execution Option Variants
* All supported specific execution options are contained here
@@ -1503,6 +1546,22 @@ export type SuccessResponseQueuedTransactionsResponse = {
};
};
export type SuccessResponseSignSolanaTransactionResponse = {
/**
* Data returned from successful signing
*/
result: {
/**
* The signature (base58-encoded)
*/
signature: string;
/**
* The signed serialized transaction (base64-encoded)
*/
signedTransaction: string;
};
};
export type ThirdwebError =
| {
error: ThirdwebSerializationError;
@@ -1847,6 +1906,32 @@ export type SendSolanaTransactionResponses = {
export type SendSolanaTransactionResponse =
SendSolanaTransactionResponses[keyof SendSolanaTransactionResponses];
export type SignSolanaTransactionData = {
/**
* Sign Solana transaction request
*/
body: SignSolanaTransactionRequest;
headers?: {
/**
* Vault access token
*/
"x-vault-access-token"?: string | null;
};
path?: never;
query?: never;
url: "/v1/solana/sign/transaction";
};
export type SignSolanaTransactionResponses = {
/**
* Successfully signed Solana transaction
*/
200: SuccessResponseSignSolanaTransactionResponse;
};
export type SignSolanaTransactionResponse2 =
SignSolanaTransactionResponses[keyof SignSolanaTransactionResponses];
export type CancelTransactionData = {
body?: never;
path: {
@@ -2076,49 +2161,6 @@ export type SignSolanaMessageResponses = {
export type SignSolanaMessageResponse =
SignSolanaMessageResponses[keyof SignSolanaMessageResponses];
export type SignSolanaTransactionData = {
body?: {
/**
* Base58 encoded Solana public key
*/
from: string;
/**
* Base64 encoded Solana transaction
*/
transaction: string;
};
headers?: {
/**
* Vault Access Token used to access your EOA
*/
"x-vault-access-token"?: string;
};
path?: never;
query?: never;
url: "/v1/solana/sign-transaction";
};
export type SignSolanaTransactionResponses = {
/**
* Transaction signed
*/
200: {
result: {
/**
* Base58 encoded signature
*/
signature: string;
/**
* Base58 encoded Solana public key
*/
signerPublicKey: string;
};
};
};
export type SignSolanaTransactionResponse =
SignSolanaTransactionResponses[keyof SignSolanaTransactionResponses];
export type GetTransactionsData = {
body?: never;
path?: never;
+49
View File
@@ -1,5 +1,54 @@
# @thirdweb-dev/nebula
## 0.2.84
### Patch Changes
- Updated dependencies []:
- thirdweb@5.111.6
## 0.2.83
### Patch Changes
- Updated dependencies [[`7da16cf`](https://github.com/thirdweb-dev/js/commit/7da16cff2ff3aaa80c7fe5614639d950d88b5bf2)]:
- thirdweb@5.111.5
## 0.2.82
### Patch Changes
- Updated dependencies [[`d907017`](https://github.com/thirdweb-dev/js/commit/d907017217a0927e7f69eab0470b1fe120b4d1a1), [`40781b8`](https://github.com/thirdweb-dev/js/commit/40781b8170a1014ef76e438ba3c8244137dba7ea)]:
- thirdweb@5.111.4
## 0.2.81
### Patch Changes
- Updated dependencies [[`207cb9d`](https://github.com/thirdweb-dev/js/commit/207cb9dbeb82411809bafa2a8e833b1f10414b92)]:
- thirdweb@5.111.3
## 0.2.80
### Patch Changes
- Updated dependencies [[`1d54cff`](https://github.com/thirdweb-dev/js/commit/1d54cff7b442b8667ddd54bd783502c367ffc83e), [`fd04eef`](https://github.com/thirdweb-dev/js/commit/fd04eefea9a2a99eee3740d6746040c9f81cd43f)]:
- thirdweb@5.111.2
## 0.2.79
### Patch Changes
- Updated dependencies [[`ccdce49`](https://github.com/thirdweb-dev/js/commit/ccdce496e09bc9420b0e15854328651c01f36279)]:
- thirdweb@5.111.1
## 0.2.78
### Patch Changes
- Updated dependencies [[`c83f1c6`](https://github.com/thirdweb-dev/js/commit/c83f1c6f7a38c8a9f424798e6e726e1d037ed042), [`219fc69`](https://github.com/thirdweb-dev/js/commit/219fc69d72b1ca861bf0228589039788008ea0c0)]:
- thirdweb@5.111.0
## 0.2.77
### Patch Changes
+1 -1
View File
@@ -57,5 +57,5 @@
"type": "module",
"types": "./dist/types/exports/thirdweb.d.ts",
"typings": "./dist/types/exports/thirdweb.d.ts",
"version": "0.2.77"
"version": "0.2.84"
}
+13
View File
@@ -0,0 +1,13 @@
[
{
"import": "*",
"limit": "1 kB",
"name": "@thirdweb-dev/nexus (esm)",
"path": "./dist/esm/exports/nexus.js"
},
{
"limit": "1 kB",
"name": "@thirdweb-dev/nexus (cjs)",
"path": "./dist/cjs/exports/nexus.js"
}
]
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
"extends": "//",
"overrides": [
{
"assist": {
"actions": {
"source": {
"useSortedKeys": "off"
}
}
},
"includes": ["package.json"]
}
]
}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/exports/**"],
"ignore": ["src/**/__generated__/**", "**/*.bench.ts"],
"ignoreBinaries": ["printf"],
"ignoreDependencies": ["tslib"],
"project": ["src/**/*.{ts,tsx}"],
"rules": {
"enumMembers": "off",
"optionalPeerDependencies": "off"
}
}
+76
View File
@@ -0,0 +1,76 @@
{
"author": "thirdweb eng <[email protected]>",
"browser": {
"crypto": false
},
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"devDependencies": {
"@biomejs/biome": "2.0.6",
"@size-limit/preset-small-lib": "11.2.0",
"knip": "5.60.2",
"rimraf": "6.0.1",
"size-limit": "11.2.0",
"typescript": "5.8.3"
},
"engines": {
"node": ">=22"
},
"exports": {
".": {
"types": "./dist/types/exports/nexus-fetch.d.ts",
"import": "./dist/esm/exports/nexus-fetch.js",
"default": "./dist/cjs/exports/nexus-fetch.js"
}
},
"files": [
"dist/*",
"src/*",
"!**/*.tsbuildinfo",
"!**/*.test.ts",
"!**/*.test.tsx",
"!**/*.test.ts.snap",
"!**/*.test-d.ts",
"!**/*.bench.ts",
"!tsconfig.build.json"
],
"license": "Apache-2.0",
"main": "./dist/cjs/exports/nexus-fetch.js",
"module": "./dist/esm/exports/nexus-fetch.js",
"name": "@thirdweb-dev/fetch",
"peerDependencies": {
"typescript": ">=5.0.4"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/thirdweb-dev/js.git#main"
},
"scripts": {
"build": "pnpm clean && pnpm build:types && pnpm build:cjs && pnpm build:esm",
"build:cjs": "tsc --noCheck --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json",
"build:esm": "tsc --noCheck --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json",
"build:types": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"clean": "rimraf dist",
"dev": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir ./dist/esm --watch",
"dev:cjs": "printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json && tsc --noCheck --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false --watch",
"dev:esm": "printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json && tsc --noCheck --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm --watch",
"fix": "biome check ./src --fix",
"format": "biome format ./src --write",
"knip": "knip",
"lint": "knip && biome check ./src && tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --noEmit",
"size": "size-limit",
"typecheck": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --noEmit",
"test:ui": "bun ./testing/test.html --watch"
},
"sideEffects": false,
"type": "module",
"types": "./dist/types/exports/nexus-fetch.d.ts",
"typings": "./dist/types/exports/nexus-fetch.d.ts",
"version": "0.0.0"
}
@@ -0,0 +1 @@
export { wrap } from "../fetch.js";
+459
View File
@@ -0,0 +1,459 @@
//---
// Constants
//---
const TOKEN_DATA_KEY = "tw:nexus:td";
const STATE_KEY = "tw:nexus:state";
const VERIFIER_KEY = "tw:nexus:verifier";
const NEXUS_API_BASE_URL = "https://nexus-api.thirdweb-dev.com";
const OAUTH_CLIENT_ID = "9ad56e84-5abe-4ea5-a6c2-9834b9d31245";
// Token expiry buffer: consider token expired 5 minutes before actual expiry
const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
//---
// Types
//---
type TokenData = {
accessToken: string;
refreshToken: string;
expiresAt: number;
};
type OAuthTokenResponse = {
access_token: string;
expires_in: number;
refresh_token: string;
token_type: "Bearer";
};
//---
// Token Encoding/Decoding
//---
function encodeTokenData(data: TokenData): string {
return btoa(JSON.stringify(data));
}
function decodeTokenData(encoded: string): TokenData | null {
try {
const decoded = atob(encoded);
return JSON.parse(decoded) as TokenData;
} catch {
return null;
}
}
//---
// Initialization
// Automatically wrap global fetch and complete OAuth flow if returning from authorization
//---
(() => {
if (hasWindow()) {
completeOauthFlow();
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = wrap(originalFetch);
} catch (error) {
console.error("[thirdweb nexus] Error wrapping fetch", error);
globalThis.fetch = originalFetch;
}
}
})();
//---
// Main Fetch Wrapper
//---
/**
* Wraps a fetch function to automatically handle HTTP 402 (Payment Required) responses.
* When a 402 is encountered, it uses the Nexus API to process the payment and retry the request.
*
* @param fetchFn - The fetch function to wrap (typically globalThis.fetch)
* @param options - Optional configuration
* @param options.maxValue - Maximum payment value allowed in wei
* @returns A wrapped fetch function that handles 402 responses
*/
export function wrap(
fetchFn: typeof globalThis.fetch,
options?: {
maxValue?: bigint;
},
) {
return async (input: RequestInfo | URL, init?: RequestInit) => {
const response = await fetchFn(input, init);
if (response.status !== 402) {
return response;
}
const AT = await getAuthToken();
if (!AT) {
// open login page in a new tab or throw an error if opening fails
startOauthFlow();
return new Response("Payment required", { status: 402 });
}
// create the proxy request
const proxyUrl = new URL("/fetch", NEXUS_API_BASE_URL);
proxyUrl.searchParams.set(
"data",
btoa(
JSON.stringify({
// original request related information
request: {
url: new URL(
typeof input === "string"
? input
: input instanceof Request
? input.url
: input,
).toString(),
method:
(typeof input === "string"
? init?.method
: input instanceof Request
? input.method
: "GET") ?? "GET",
headers:
typeof input === "string"
? init?.headers
: input instanceof Request
? input.headers
: {},
},
// x402 options
x402Options: {
...(options?.maxValue
? { maxValue: options.maxValue.toString() }
: {}),
},
}),
),
);
let proxyRes = await fetchFn(proxyUrl, {
method: "POST",
body:
(typeof input === "string"
? init?.body
: input instanceof Request
? input.body
: init?.body) ?? undefined,
headers: {
Authorization: `Bearer ${AT}`,
},
});
// Handle 401 (unauthorized) - try to refresh token and retry
if (proxyRes.status === 401) {
const refreshedToken = await refreshAccessToken();
if (refreshedToken) {
// Retry with refreshed token
proxyRes = await fetchFn(proxyUrl, {
method: "POST",
body:
(typeof input === "string"
? init?.body
: input instanceof Request
? input.body
: init?.body) ?? undefined,
headers: {
Authorization: `Bearer ${refreshedToken}`,
},
});
} else {
// Refresh failed, start OAuth flow
startOauthFlow();
return new Response("Unauthorized", { status: 401 });
}
}
if (proxyRes.status === 402) {
// means the wallet doesn't have funds and needs to onramp, open the funding link
const data = (await proxyRes.json()) as {
result: { link: string; message: string };
};
// TODO: we can await this and then retry the call after by setting up
// window.postMessage communication and opening this in a new tab
const paymentUrl = new URL(data.result.link);
paymentUrl.searchParams.set("successUrl", window.location.href);
window.location.href = paymentUrl.toString();
return new Response("Payment required", { status: 402 });
}
return proxyRes;
};
}
//---
// Browser/Environment Utilities
//---
function hasWindow(): boolean {
return typeof window !== "undefined";
}
function getLocalStorage(): Storage | null {
try {
return hasWindow() ? window.localStorage : null;
} catch {
return null;
}
}
//---
// Token Data Management
//---
let inMemoryTokenData: TokenData | null = null;
function getTokenData(): TokenData | null {
if (inMemoryTokenData !== null) {
return inMemoryTokenData;
}
const encoded = getLocalStorage()?.getItem(TOKEN_DATA_KEY);
if (encoded) {
const data = decodeTokenData(encoded);
if (data) {
inMemoryTokenData = data;
return data;
}
}
return null;
}
function setTokenData(
accessToken: string,
refreshToken: string,
expiresIn: number,
): void {
if (!accessToken || accessToken === "undefined") {
return;
}
if (!refreshToken || refreshToken === "undefined") {
return;
}
// Calculate expiry timestamp (current time + expires_in seconds)
const expiresAt = Date.now() + expiresIn * 1000;
const tokenData: TokenData = { accessToken, refreshToken, expiresAt };
const encoded = encodeTokenData(tokenData);
getLocalStorage()?.setItem(TOKEN_DATA_KEY, encoded);
inMemoryTokenData = tokenData;
}
function deleteTokenData(): void {
getLocalStorage()?.removeItem(TOKEN_DATA_KEY);
inMemoryTokenData = null;
}
function isTokenExpired(tokenData: TokenData | null): boolean {
if (!tokenData) {
return true;
}
return Date.now() >= tokenData.expiresAt - TOKEN_EXPIRY_BUFFER_MS;
}
//---
// Token Validation & Refresh
//---
/**
* Gets a valid access token, refreshing it if necessary.
* @returns A valid access token or null if unavailable
*/
async function getAuthToken(): Promise<string | null> {
const tokenData = getTokenData();
// If we have a token but it's expired, try to refresh
if (tokenData && isTokenExpired(tokenData)) {
const refreshedToken = await refreshAccessToken();
return refreshedToken;
}
return tokenData?.accessToken ?? null;
}
/**
* Refreshes an expired access token using the refresh token.
* @returns A new access token or null if refresh fails
*/
async function refreshAccessToken(): Promise<string | null> {
const tokenData = getTokenData();
if (!tokenData) {
return null;
}
try {
const response = await fetch(`${NEXUS_API_BASE_URL}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: tokenData.refreshToken,
client_id: OAUTH_CLIENT_ID,
}),
});
if (!response.ok) {
// Refresh failed, clear all tokens
deleteTokenData();
return null;
}
const data = (await response.json()) as OAuthTokenResponse;
// Store new tokens
setTokenData(data.access_token, data.refresh_token, data.expires_in);
return data.access_token;
} catch (error) {
console.error("[thirdweb nexus] Error refreshing access token", error);
return null;
}
}
//---
// OAuth PKCE (Proof Key for Code Exchange) Utilities
//---
function storeState(code: string): void {
getLocalStorage()?.setItem(STATE_KEY, code);
}
function getState(): string | null | undefined {
return getLocalStorage()?.getItem(STATE_KEY);
}
function deleteState(): void {
getLocalStorage()?.removeItem(STATE_KEY);
}
function storeVerifier(verifier: string): void {
getLocalStorage()?.setItem(VERIFIER_KEY, verifier);
}
function getVerifier(): string | null | undefined {
return getLocalStorage()?.getItem(VERIFIER_KEY);
}
function deleteVerifier(): void {
getLocalStorage()?.removeItem(VERIFIER_KEY);
}
/**
* Generates a cryptographically random code verifier for PKCE.
*/
function generateCodeVerifier(): string {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
/**
* Generates a code challenge from a verifier for PKCE using SHA-256.
*/
async function generateCodeChallenge(verifier: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest("SHA-256", data);
return base64UrlEncode(new Uint8Array(hash));
}
/**
* Base64 URL-safe encoding (without padding).
*/
function base64UrlEncode(array: Uint8Array): string {
const base64 = btoa(String.fromCharCode(...array));
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
//---
// OAuth Flow
//---
/**
* Initiates the OAuth authorization flow by redirecting to the Nexus authorization endpoint.
*/
async function startOauthFlow(): Promise<void> {
const state = crypto.randomUUID();
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
const redirectUri = new URL(window.location.href);
const url = new URL("/oauth/authorize", NEXUS_API_BASE_URL);
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", OAUTH_CLIENT_ID);
url.searchParams.set("code_challenge", challenge);
url.searchParams.set("code_challenge_method", "S256");
url.searchParams.set("state", state);
url.searchParams.set(
"redirect_uri",
`${redirectUri.origin}${redirectUri.pathname}`,
);
storeState(state);
storeVerifier(verifier);
// just open the page directly
window.location.href = url.toString();
}
/**
* Completes the OAuth flow by exchanging the authorization code for tokens.
* Called automatically on page load if code parameter is present in URL.
*/
async function completeOauthFlow(): Promise<void> {
try {
const searchParams = new URL(window.location.href).searchParams;
const code = searchParams.get("code");
const state = searchParams.get("state");
const error = searchParams.get("error");
if (error) {
console.error("[thirdweb nexus] OAuth error:", error);
return;
}
if (!code || !state) {
// No code or state found, nothing to do
return;
}
if (state !== getState()) {
// State mismatch - possible CSRF attack
console.warn("[thirdweb nexus] OAuth state mismatch");
return;
}
const verifier = getVerifier();
if (!verifier) {
// No verifier found, nothing to do
return;
}
// Delete the state and verifier so they can't be reused
deleteState();
deleteVerifier();
const redirectUri = new URL(window.location.href);
const response = await fetch(`${NEXUS_API_BASE_URL}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
code,
code_verifier: verifier,
grant_type: "authorization_code",
redirect_uri: `${redirectUri.origin}${redirectUri.pathname}`,
client_id: OAUTH_CLIENT_ID,
}),
});
if (!response.ok) {
console.error(
"[thirdweb nexus] Failed to exchange authorization code for tokens",
);
return;
}
const data = (await response.json()) as OAuthTokenResponse;
// Store all token data
setTokenData(data.access_token, data.refresh_token, data.expires_in);
} catch (error) {
console.error("[thirdweb nexus] Error completing OAuth flow:", error);
}
}
+21
View File
@@ -0,0 +1,21 @@
<html>
<head>
<title>Nexus Fetch Testing</title>
<script src="../src/exports/nexus-fetch.ts"></script>
</head>
<body>
<button id="test-button">Test</button>
<script>
const testButton = document.getElementById("test-button");
testButton.addEventListener("click", () => {
fetch("https://q1qkkomp.nx.link/kindle", {
method: "POST",
body: JSON.stringify({
recipient: "0x0890C23024089675D072E984f28A93bb391a35Ab",
})
});
});
testButton.click();
</script>
</body>
</html>
+48
View File
@@ -0,0 +1,48 @@
{
// This tsconfig file contains the shared config for the build (tsconfig.build.json) and type checking (tsconfig.json) config.
"compilerOptions": {
// Incremental builds
// NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes.
"allowJs": false,
"allowSyntheticDefaultImports": true,
"checkJs": false,
// Interop constraints
"esModuleInterop": false,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
// Incremental builds
// NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes.
"incremental": false,
// jsx for "/react" portion
"jsx": "react-jsx",
"lib": [
"ES2022", // By using ES2022 we get access to the `.cause` property on `Error` instances.
"DOM" // We are adding `DOM` here to get the `fetch`, etc. types. This should be removed once these types are available via DefinitelyTyped.
],
"module": "NodeNext",
// Language and environment
"moduleResolution": "NodeNext",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
// Skip type checking for node modules
"skipLibCheck": true,
// Type checking
"strict": true,
"target": "ES2021",
"useDefineForClassFields": true,
"useUnknownInCatchVariables": true,
"verbatimModuleSyntax": true
},
// This tsconfig file contains the shared config for the build (tsconfig.build.json) and type checking (tsconfig.json) config.
"include": []
}
+16
View File
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"moduleResolution": "node",
"rootDir": "./src",
"sourceMap": true
},
"exclude": [
"src/**/*.test.ts",
"src/**/*.test.tsx",
"src/**/*.test-d.ts",
"src/**/*.bench.ts",
"src/**/*.macro.ts"
],
"extends": "./tsconfig.base.json",
"include": ["src"]
}
+13
View File
@@ -0,0 +1,13 @@
{
// This configuration is used for local development and type checking.
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist",
"paths": {
"~test/*": ["./test/src/*"]
}
},
"exclude": [],
"extends": "./tsconfig.base.json",
"include": ["src", "test"]
}
+51
View File
@@ -1,5 +1,56 @@
# thirdweb
## 5.111.6
### Patch Changes
- Updated dependencies [[`ad68969`](https://github.com/thirdweb-dev/js/commit/ad68969559a80a5f188a647ef0e1c04f0003a0ad)]:
- @thirdweb-dev/engine@3.4.0
## 5.111.5
### Patch Changes
- [#8369](https://github.com/thirdweb-dev/js/pull/8369) [`7da16cf`](https://github.com/thirdweb-dev/js/commit/7da16cff2ff3aaa80c7fe5614639d950d88b5bf2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Make maxAmount optional in wrapFetchWithPayment and loosen schema validation for payment payloads
## 5.111.4
### Patch Changes
- [#8363](https://github.com/thirdweb-dev/js/pull/8363) [`d907017`](https://github.com/thirdweb-dev/js/commit/d907017217a0927e7f69eab0470b1fe120b4d1a1) Thanks [@MananTank](https://github.com/MananTank)! - Fix BuyWidget autoconnect not working when receiverAddress prop is set
- [#8365](https://github.com/thirdweb-dev/js/pull/8365) [`40781b8`](https://github.com/thirdweb-dev/js/commit/40781b8170a1014ef76e438ba3c8244137dba7ea) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Respect passed chain when doing SIWE for in-app/ecosystem wallet
## 5.111.3
### Patch Changes
- [#8361](https://github.com/thirdweb-dev/js/pull/8361) [`207cb9d`](https://github.com/thirdweb-dev/js/commit/207cb9dbeb82411809bafa2a8e833b1f10414b92) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add extra mobile detection for isMobile() function
## 5.111.2
### Patch Changes
- [#8358](https://github.com/thirdweb-dev/js/pull/8358) [`1d54cff`](https://github.com/thirdweb-dev/js/commit/1d54cff7b442b8667ddd54bd783502c367ffc83e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add support for extraMetadata in x402 payment requirements
- [#8356](https://github.com/thirdweb-dev/js/pull/8356) [`fd04eef`](https://github.com/thirdweb-dev/js/commit/fd04eefea9a2a99eee3740d6746040c9f81cd43f) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Update implementations, fix deployment
## 5.111.1
### Patch Changes
- [#8354](https://github.com/thirdweb-dev/js/pull/8354) [`ccdce49`](https://github.com/thirdweb-dev/js/commit/ccdce496e09bc9420b0e15854328651c01f36279) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix hiddenWallets prop not applying to all wallets screen
## 5.111.0
### Minor Changes
- [#8335](https://github.com/thirdweb-dev/js/pull/8335) [`c83f1c6`](https://github.com/thirdweb-dev/js/commit/c83f1c6f7a38c8a9f424798e6e726e1d037ed042) Thanks [@MananTank](https://github.com/MananTank)! - Remove `Bridge.routes` and `useBridgeRoutes`
### Patch Changes
- [#8345](https://github.com/thirdweb-dev/js/pull/8345) [`219fc69`](https://github.com/thirdweb-dev/js/commit/219fc69d72b1ca861bf0228589039788008ea0c0) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update x402 flows to use remote /accepts endpoint
## 5.110.6
### Patch Changes
+1 -1
View File
@@ -430,5 +430,5 @@
}
},
"typings": "./dist/types/exports/thirdweb.d.ts",
"version": "5.110.6"
"version": "5.111.6"
}
-161
View File
@@ -1,161 +0,0 @@
import { http, passthrough } from "msw";
import { setupServer } from "msw/node";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { TEST_CLIENT } from "~test/test-clients.js";
import { routes } from "./Routes.js";
const server = setupServer(
http.get("https://bridge.thirdweb.com/v1/routes", () => {
passthrough();
}),
);
describe.runIf(process.env.TW_SECRET_KEY)("Bridge.routes", () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it("should get a valid list of routes", async () => {
const allRoutes = await routes({
client: TEST_CLIENT,
});
expect(allRoutes).toBeDefined();
expect(Array.isArray(allRoutes)).toBe(true);
});
it("should filter routes by origin chain", async () => {
const filteredRoutes = await routes({
client: TEST_CLIENT,
originChainId: 1,
});
expect(filteredRoutes).toBeDefined();
expect(Array.isArray(filteredRoutes)).toBe(true);
expect(
filteredRoutes.every((route) => route.originToken.chainId === 1),
).toBe(true);
});
it("should filter routes by destination chain", async () => {
const filteredRoutes = await routes({
client: TEST_CLIENT,
destinationChainId: 1,
});
expect(filteredRoutes).toBeDefined();
expect(Array.isArray(filteredRoutes)).toBe(true);
expect(
filteredRoutes.every((route) => route.destinationToken.chainId === 1),
).toBe(true);
});
it("should filter routes by origin token", async () => {
const filteredRoutes = await routes({
client: TEST_CLIENT,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
});
expect(filteredRoutes).toBeDefined();
expect(Array.isArray(filteredRoutes)).toBe(true);
expect(
filteredRoutes.every(
(route) =>
route.originToken.address ===
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
),
).toBe(true);
});
it("should filter routes by destination token", async () => {
const filteredRoutes = await routes({
client: TEST_CLIENT,
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
});
expect(filteredRoutes).toBeDefined();
expect(Array.isArray(filteredRoutes)).toBe(true);
expect(
filteredRoutes.every(
(route) =>
route.destinationToken.address ===
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
),
).toBe(true);
});
it("should combine filters", async () => {
const filteredRoutes = await routes({
client: TEST_CLIENT,
destinationChainId: 10,
destinationTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
});
expect(filteredRoutes).toBeDefined();
expect(Array.isArray(filteredRoutes)).toBe(true);
expect(filteredRoutes.length).toBeGreaterThan(0);
expect(
filteredRoutes.every(
(route) =>
route.originToken.chainId === 1 &&
route.destinationToken.chainId === 10 &&
route.originToken.address ===
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" &&
route.destinationToken.address ===
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
),
).toBe(true);
});
it("should respect limit and offset", async () => {
const page1Routes = await routes({
client: TEST_CLIENT,
limit: 1,
offset: 1,
});
expect(page1Routes).toBeDefined();
expect(Array.isArray(page1Routes)).toBe(true);
expect(page1Routes.length).toBe(1);
const page2Routes = await routes({
client: TEST_CLIENT,
limit: 1,
offset: 2,
});
expect(page2Routes).toBeDefined();
expect(Array.isArray(page2Routes)).toBe(true);
expect(page2Routes.length).toBe(1);
expect(JSON.stringify(page1Routes)).not.toEqual(
JSON.stringify(page2Routes),
);
});
it("should surface any errors", async () => {
server.use(
http.get("https://bridge.thirdweb.com/v1/routes", () => {
return Response.json(
{
code: "InvalidRoutesRequest",
message: "The provided request is invalid.",
},
{ status: 400 },
);
}),
);
await expect(
routes({
client: TEST_CLIENT,
limit: 1000,
offset: 1000,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
"[Error: The provided request is invalid.]",
);
});
});
-228
View File
@@ -1,228 +0,0 @@
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 { ApiError } from "./types/Errors.js";
import type { Route } from "./types/Route.js";
/**
* Retrieves supported Bridge routes based on the provided filters.
*
* When multiple filters are specified, a route must satisfy all filters to be included (it acts as an AND operator).
*
* @example
* ```typescript
* import { Bridge } from "thirdweb";
*
* const routes = await Bridge.routes({
* client: thirdwebClient,
* });
* ```
*
* Returned routes might look something like:
* ```typescript
* [
* {
* destinationToken: {
* address: "0x12c88a3C30A7AaBC1dd7f2c08a97145F5DCcD830",
* chainId: 1,
* decimals: 18,
* iconUri: "https://assets.coingecko.com/coins/images/37207/standard/G.jpg",
* name: "G7",
* symbol: "G7",
* },
* originToken: {
* address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* chainId: 480,
* decimals: 18,
* iconUri: "https://assets.relay.link/icons/1/light.png",
* name: "Ether",
* symbol: "ETH",
* }
* },
* {
* destinationToken: {
* address: "0x4d224452801ACEd8B2F0aebE155379bb5D594381",
* chainId: 1,
* decimals: 18,
* iconUri: "https://coin-images.coingecko.com/coins/images/24383/large/apecoin.jpg?1696523566",
* name: "ApeCoin",
* symbol: "APE",
* },
* originToken: {
* address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* chainId: 480,
* decimals: 18,
* iconUri: "https://assets.relay.link/icons/1/light.png",
* name: "Ether",
* symbol: "ETH",
* }
* }
* ]
* ```
*
* You can filter for specific chains or tokens:
* ```typescript
* import { Bridge } from "thirdweb";
*
* // Get all routes starting from mainnet ETH
* const routes = await Bridge.routes({
* originChainId: 1,
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* client: thirdwebClient,
* });
* ```
*
* The returned routes will be limited based on the API. You can paginate through the results using the `limit` and `offset` parameters:
* ```typescript
* import { Bridge } from "thirdweb";
*
* // Get the first 10 routes starting from mainnet ETH
* const routes = await Bridge.routes({
* originChainId: 1,
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* limit: 10,
* offset: 0,
* client: thirdwebClient,
* });
* ```
*
* You can sort the returned routes by `popularity`:
* ```ts
* import { Bridge } from "thirdweb";
*
* // Get the 10 most popular routes starting from mainnet ETH
* const routes = await Bridge.routes({
* originChainId: 1,
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
* limit: 10,
* offset: 0,
* sortBy: "popularity",
* client: thirdwebClient,
* });
* ```
*
* @param options - The options for the quote.
* @param options.client - Your thirdweb client.
* @param options.originChainId - Filter by a specific origin chain ID.
* @param options.originTokenAddress - Filter by a specific origin token address.
* @param options.destinationChainId - Filter by a specific destination chain ID.
* @param options.destinationTokenAddress - Filter by a specific destination token address.
* @param options.transactionHash - Filter by a specific transaction hash.
* @param options.maxSteps - Limit the number of steps returned.
* @param options.sortBy - Sort the routes by various categories.
* @param options.limit - Limit the number of routes returned.
* @param options.offset - Offset the number of routes returned.
*
* @returns A promise that resolves to an array of routes.
*
* @throws Will throw an error if there is an issue fetching the routes.
* @bridge
* @beta
*/
export async function routes(options: routes.Options): Promise<routes.Result> {
const {
client,
originChainId,
originTokenAddress,
destinationChainId,
destinationTokenAddress,
maxSteps,
sortBy,
limit,
offset,
includePrices,
} = options;
const clientFetch = getClientFetch(client);
const url = new URL(`${getThirdwebBaseUrl("bridge")}/v1/routes`);
if (originChainId) {
url.searchParams.set("originChainId", originChainId.toString());
}
if (originTokenAddress) {
url.searchParams.set("originTokenAddress", originTokenAddress);
}
if (destinationChainId) {
url.searchParams.set("destinationChainId", destinationChainId.toString());
}
if (destinationTokenAddress) {
url.searchParams.set("destinationTokenAddress", destinationTokenAddress);
}
if (maxSteps) {
url.searchParams.set("maxSteps", maxSteps.toString());
}
if (limit) {
url.searchParams.set("limit", limit.toString());
}
if (offset) {
url.searchParams.set("offset", offset.toString());
}
if (sortBy) {
url.searchParams.set("sortBy", sortBy);
}
if (includePrices) {
url.searchParams.set("includePrices", includePrices.toString());
}
const response = await clientFetch(url.toString());
if (!response.ok) {
const errorJson = await response.json();
throw new ApiError({
code: errorJson.code || "UNKNOWN_ERROR",
correlationId: errorJson.correlationId || undefined,
message: errorJson.message || response.statusText,
statusCode: response.status,
});
}
const { data }: { data: Route[] } = await response.json();
return data;
}
/**
* Namespace containing types for the routes function.
* @namespace routes
* @bridge
*/
export declare namespace routes {
/**
* Options for fetching available bridge routes.
* @interface Options
* @bridge
*/
type Options = {
/** Your thirdweb client */
client: ThirdwebClient;
/** The origin chain ID to filter routes by */
originChainId?: number;
/** The origin token address to filter routes by */
originTokenAddress?: ox__Address.Address;
/** The destination chain ID to filter routes by */
destinationChainId?: number;
/** The destination token address to filter routes by */
destinationTokenAddress?: ox__Address.Address;
/** Transaction hash to filter routes by */
transactionHash?: ox__Hex.Hex;
/**
* Sort routes by popularity
* @deprecated
*/
sortBy?: "popularity";
/** Maximum number of steps in the route */
maxSteps?: number;
/** Whether to include price information in the response */
includePrices?: boolean;
/** Number of results to return (pagination) */
limit?: number;
/** Number of results to skip (pagination) */
offset?: number;
};
/**
* Result returned from fetching bridge routes.
* Contains an array of available routes.
* @interface Result
* @bridge
*/
type Result = Route[];
}
-1
View File
@@ -1,7 +1,6 @@
export * as Buy from "./Buy.js";
export { chains } from "./Chains.js";
export * as Onramp from "./Onramp.js";
export { routes } from "./Routes.js";
export * as Sell from "./Sell.js";
export { status } from "./Status.js";
export { tokens } from "./Token.js";
@@ -9,44 +9,48 @@ import { getContract } from "../contract.js";
import { deployCloneFactory } from "./utils/bootstrap.js";
describe.runIf(process.env.TW_SECRET_KEY)("deploy dynamic", () => {
it.sequential("should deploy dynamic contract with extensions", async () => {
await deployCloneFactory({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
// TODO: Fix this test
it.skip.sequential(
"should deploy dynamic contract with extensions",
async () => {
await deployCloneFactory({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const deployed = await deployPublishedContract({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
contractId: "EvolvingNFT",
contractParams: {
contractURI: "",
defaultAdmin: TEST_ACCOUNT_A.address,
name: "Evolving nft",
royaltyBps: 0n,
royaltyRecipient: TEST_ACCOUNT_A.address,
saleRecipient: TEST_ACCOUNT_A.address,
symbol: "ENFT",
trustedForwarders: [],
},
});
const deployed = await deployPublishedContract({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
contractId: "EvolvingNFT",
contractParams: {
contractURI: "",
defaultAdmin: TEST_ACCOUNT_A.address,
name: "Evolving nft",
royaltyBps: 0n,
royaltyRecipient: TEST_ACCOUNT_A.address,
saleRecipient: TEST_ACCOUNT_A.address,
symbol: "ENFT",
trustedForwarders: [],
},
});
expect(deployed).toBeDefined();
expect(deployed).toBeDefined();
const contract = getContract({
address: deployed,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const contract = getContract({
address: deployed,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const extensions = await readContract({
contract,
method: resolveMethod("getAllExtensions"),
params: [],
});
const extensions = await readContract({
contract,
method: resolveMethod("getAllExtensions"),
params: [],
});
expect(extensions.length).toEqual(3);
});
expect(extensions.length).toEqual(3);
},
);
});
@@ -1,6 +1,6 @@
export const ZKSYNC_IMPLEMENTATIONS: Record<number, Record<string, string>> = {
232: {
MarketplaceV3: "0xB7387cF425eC8Da1b04b8E34De86245cb32bCA6D",
MarketplaceV3: "0x4e0C3577335961Ff800FFDA24981EB2F38D94483",
},
300: {
MarketplaceV3: "0x58e0F289C7dD2025eBd0696d913ECC0fdc1CC8bc",
@@ -18,7 +18,7 @@ export const ZKSYNC_IMPLEMENTATIONS: Record<number, Record<string, string>> = {
MarketplaceV3: "0x2dA4Dd326A6482679547071be21f74685d730504",
},
37111: {
MarketplaceV3: "0xF8fd1016420c2B7832714241d36Efe55D41df126",
MarketplaceV3: "0x12A305cc7168fa3b7B172fE53c57b9a22716F667",
},
50104: {
MarketplaceV3: "0x292c324920d4120F928dA6f04548442fa10B8B0f",
@@ -171,7 +171,8 @@ describe.runIf(process.env.TW_SECRET_KEY)("getEvents", () => {
expect(events.length).toBe(14);
});
it("should get individual events with extension no filter using indexer", async () => {
// TODO: re-enable this test once indexer is fixed
it.skip("should get individual events with extension no filter using indexer", async () => {
const events = await getContractEvents({
contract: DOODLES_CONTRACT,
events: [transferEvent()],
@@ -182,7 +183,8 @@ describe.runIf(process.env.TW_SECRET_KEY)("getEvents", () => {
expect(events.length).toBe(38);
});
it("should get events for signature using indexer", async () => {
// TODO: re-enable this test once indexer is fixed
it.skip("should get events for signature using indexer", async () => {
const events = await getContractEvents({
contract: DOODLES_CONTRACT,
events: [
@@ -198,7 +200,8 @@ describe.runIf(process.env.TW_SECRET_KEY)("getEvents", () => {
expect(events.length).toBe(2);
});
it("should get specified events using indexer", async () => {
// TODO: re-enable this test once indexer is fixed
it.skip("should get specified events using indexer", async () => {
const events = await getContractEvents({
contract: USDT_CONTRACT,
events: [
-4
View File
@@ -74,10 +74,6 @@ export type {
} from "../react/core/hooks/transaction/useSendTransaction.js";
export { useSimulateTransaction } from "../react/core/hooks/transaction/useSimulateTransaction.js";
export type { BridgePrepareResult } from "../react/core/hooks/useBridgePrepare.js";
export {
type UseBridgeRoutesParams,
useBridgeRoutes,
} from "../react/core/hooks/useBridgeRoutes.js";
export type { CompletedStatusResult } from "../react/core/hooks/useStepExecutor.js";
export { useActiveAccount } from "../react/core/hooks/wallets/useActiveAccount.js";
// wallet hooks
@@ -11,50 +11,54 @@ import { sendTransaction } from "../../../transaction/actions/send-transaction.j
import { installPublishedExtension } from "./installPublishedExtension.js";
describe.runIf(process.env.TW_SECRET_KEY)("install extension", () => {
it.sequential("should install extension to a dynamic contract", async () => {
await deployCloneFactory({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
// TODO: Fix this test
it.skip.sequential(
"should install extension to a dynamic contract",
async () => {
await deployCloneFactory({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const deployed = await deployPublishedContract({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
contractId: "EvolvingNFT",
contractParams: {
contractURI: "",
defaultAdmin: TEST_ACCOUNT_A.address,
name: "Evolving nft",
royaltyBps: 0n,
royaltyRecipient: TEST_ACCOUNT_A.address,
saleRecipient: TEST_ACCOUNT_A.address,
symbol: "ENFT",
trustedForwarders: [],
},
});
const deployed = await deployPublishedContract({
account: TEST_ACCOUNT_A,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
contractId: "EvolvingNFT",
contractParams: {
contractURI: "",
defaultAdmin: TEST_ACCOUNT_A.address,
name: "Evolving nft",
royaltyBps: 0n,
royaltyRecipient: TEST_ACCOUNT_A.address,
saleRecipient: TEST_ACCOUNT_A.address,
symbol: "ENFT",
trustedForwarders: [],
},
});
const contract = getContract({
address: deployed,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const contract = getContract({
address: deployed,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
});
const transaction = installPublishedExtension({
account: TEST_ACCOUNT_A,
contract,
extensionName: "DirectListingsLogic",
});
const transaction = installPublishedExtension({
account: TEST_ACCOUNT_A,
contract,
extensionName: "DirectListingsLogic",
});
await sendTransaction({ account: TEST_ACCOUNT_A, transaction });
await sendTransaction({ account: TEST_ACCOUNT_A, transaction });
const extensions = await readContract({
contract,
method: resolveMethod("getAllExtensions"),
params: [],
});
const extensions = await readContract({
contract,
method: resolveMethod("getAllExtensions"),
params: [],
});
expect(extensions.length).toEqual(4);
});
expect(extensions.length).toEqual(4);
},
);
});
@@ -11,7 +11,8 @@ import { deployPublishedContract } from "../../prebuilts/deploy-published.js";
import { uninstallExtension } from "./uninstallExtension.js";
describe.runIf(process.env.TW_SECRET_KEY)("uninstall extension", () => {
it.sequential(
// TODO: Fix this test
it.skip.sequential(
"should uninstall extension from a dynamic contract",
async () => {
await deployCloneFactory({
@@ -16,6 +16,7 @@ import {
fetchBytecodeFromCompilerMetadata,
} from "../../utils/any-evm/deploy-metadata.js";
import { encodeExtraDataWithUri } from "../../utils/any-evm/encode-extra-data-with-uri.js";
import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js";
import type { Hex } from "../../utils/encoding/hex.js";
import type { Account } from "../../wallets/interfaces/wallet.js";
import { getAllDefaultConstructorParamsForImplementation } from "./get-required-transactions.js";
@@ -213,7 +214,8 @@ export async function deployContractfromDeployMetadata(
if (
deployMetadata.routerType === "dynamic" &&
deployMetadata.defaultExtensions
deployMetadata.defaultExtensions &&
!isZkSyncChain(chain)
) {
for (const e of deployMetadata.defaultExtensions) {
await getOrDeployInfraForPublishedContract({
@@ -253,10 +253,10 @@ export function useSendTransactionCore(args: {
(nativeCost > 0n && nativeBalance.value < nativeCost);
if (shouldShowModal) {
const supportedDestinations = await Bridge.routes({
const tokens = await Bridge.tokens({
client: tx.client,
destinationChainId: tx.chain.id,
destinationTokenAddress: _erc20Value?.tokenAddress,
chainId: tx.chain.id,
tokenAddress: _erc20Value?.tokenAddress,
}).catch((err) => {
trackPayEvent({
client: tx.client,
@@ -269,11 +269,8 @@ export function useSendTransactionCore(args: {
return null;
});
if (
!supportedDestinations ||
supportedDestinations.length === 0
) {
// not a supported destination -> show deposit screen
if (!tokens || tokens.length === 0) {
// not a supported token -> show deposit screen
trackPayEvent({
client: tx.client,
error: JSON.stringify({
@@ -1,137 +0,0 @@
import {
beforeEach,
describe,
expect,
it,
type MockedFunction,
vi,
} from "vitest";
import { routes } from "../../../bridge/Routes.js";
import { ApiError } from "../../../bridge/types/Errors.js";
import type { Route } from "../../../bridge/types/Route.js";
import type { ThirdwebClient } from "../../../client/client.js";
import type { UseBridgeRoutesParams } from "./useBridgeRoutes.js";
// Mock the Bridge routes function
vi.mock("../../../bridge/Routes.js", () => ({
routes: vi.fn(),
}));
const mockRoutes = routes as MockedFunction<typeof routes>;
// Mock client
const mockClient = { clientId: "test" } as ThirdwebClient;
// Mock route data
const mockRouteData: Route[] = [
{
destinationToken: {
address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
chainId: 137,
decimals: 18,
name: "Wrapped Ethereum",
priceUsd: 2000.0,
symbol: "WETH",
},
originToken: {
address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
chainId: 1,
decimals: 18,
name: "Ethereum",
priceUsd: 2000.0,
symbol: "ETH",
},
},
];
describe("useBridgeRoutes", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should export correct hook parameters type", () => {
// Type-only test to verify UseBridgeRoutesParams interface
const params: UseBridgeRoutesParams = {
client: mockClient,
destinationChainId: 137,
enabled: true,
originChainId: 1,
};
expect(params).toBeDefined();
expect(params.client).toBe(mockClient);
expect(params.originChainId).toBe(1);
expect(params.destinationChainId).toBe(137);
expect(params.enabled).toBe(true);
});
it("should handle different parameter combinations", () => {
const fullParams: UseBridgeRoutesParams = {
client: mockClient,
destinationChainId: 137,
destinationTokenAddress: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
enabled: false,
limit: 10,
maxSteps: 3,
offset: 0,
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
sortBy: "popularity",
};
expect(fullParams).toBeDefined();
expect(fullParams.sortBy).toBe("popularity");
expect(fullParams.maxSteps).toBe(3);
expect(fullParams.limit).toBe(10);
expect(fullParams.offset).toBe(0);
});
it("should have optional enabled parameter defaulting to true", () => {
const paramsWithoutEnabled: UseBridgeRoutesParams = {
client: mockClient,
destinationChainId: 137,
originChainId: 1,
};
expect(paramsWithoutEnabled.enabled).toBeUndefined(); // Should be optional
});
it("should validate that Bridge.routes would be called with correct parameters", async () => {
const testParams = {
client: mockClient,
destinationChainId: 137,
originChainId: 1,
originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as const,
};
// Mock the routes function to return our test data
mockRoutes.mockResolvedValue(mockRouteData);
// Directly call the routes function to verify it works with our parameters
const result = await routes(testParams);
expect(mockRoutes).toHaveBeenCalledWith(testParams);
expect(result).toEqual(mockRouteData);
});
it("should handle API errors properly", async () => {
const apiError = new ApiError({
code: "INVALID_INPUT",
message: "Invalid parameters",
statusCode: 400,
});
mockRoutes.mockRejectedValue(apiError);
try {
await routes({
client: mockClient,
destinationChainId: 137,
originChainId: 1,
});
} catch (error) {
expect(error).toBe(apiError);
expect(error).toBeInstanceOf(ApiError);
}
});
});
@@ -1,75 +0,0 @@
import { useQuery } from "@tanstack/react-query";
import type { routes as RoutesTypes } from "../../../bridge/Routes.js";
import { routes } from "../../../bridge/Routes.js";
import { ApiError } from "../../../bridge/types/Errors.js";
import { mapBridgeError } from "../errors/mapBridgeError.js";
/**
* Parameters for the useBridgeRoutes hook
*/
export type UseBridgeRoutesParams = RoutesTypes.Options & {
/**
* Whether to enable the query. Useful for conditional fetching.
* @default true
*/
enabled?: boolean;
};
/**
* Hook that fetches available bridge routes with caching and retry logic
*
* @param params - Parameters for fetching routes including client and filter options
* @returns React Query result with routes data, loading state, and error handling
*
* @example
* ```tsx
* const { data: routes, isLoading, error } = useBridgeRoutes({
* client: thirdwebClient,
* originChainId: 1,
* destinationChainId: 137,
* originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
* });
* ```
*/
export function useBridgeRoutes(params: UseBridgeRoutesParams) {
const { enabled = true, ...routeParams } = params;
return useQuery({
enabled: enabled && !!routeParams.client,
gcTime: 10 * 60 * 1000,
queryFn: () => routes(routeParams),
queryKey: [
"bridge-routes",
{
destinationChainId: routeParams.destinationChainId,
destinationTokenAddress: routeParams.destinationTokenAddress,
limit: routeParams.limit,
maxSteps: routeParams.maxSteps,
offset: routeParams.offset,
originChainId: routeParams.originChainId,
originTokenAddress: routeParams.originTokenAddress,
sortBy: routeParams.sortBy,
},
], // 5 minutes - routes are relatively stable
retry: (failureCount, error) => {
// Handle both ApiError and generic Error instances
if (error instanceof ApiError) {
const bridgeError = mapBridgeError(error);
// Don't retry on client-side errors (4xx)
if (
bridgeError.statusCode &&
bridgeError.statusCode >= 400 &&
bridgeError.statusCode < 500
) {
return false;
}
}
// Retry up to 3 times for server errors or network issues
return failureCount < 3;
}, // 10 minutes garbage collection
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
staleTime: 5 * 60 * 1000, // Exponential backoff, max 30s
});
}
@@ -142,7 +142,9 @@ export function ConnectModal(
const inAppWallet = wallets.find((wallet) => wallet.id === "inApp") as
| Wallet<"inApp">
| undefined;
const externalWallets = wallets.filter((wallet) => wallet.id !== "inApp");
const externalWallets = wallets
.filter((wallet) => wallet.id !== "inApp")
.filter((wallet) => !props.hiddenWallets?.includes(wallet.id));
const showBranding = props.connectModal?.showThirdwebBranding !== false;
const connectionManager = useConnectionManager();
@@ -25,6 +25,7 @@ import type {
import type { CompletedStatusResult } from "../../../core/hooks/useStepExecutor.js";
import type { SupportedTokens } from "../../../core/utils/defaultTokens.js";
import { webWindowAdapter } from "../../adapters/WindowAdapter.js";
import { AutoConnect } from "../AutoConnect/AutoConnect.js";
import connectLocaleEn from "../ConnectWallet/locale/en.js";
import { EmbedContainer } from "../ConnectWallet/Modal/ConnectEmbed.js";
import { DynamicHeight } from "../components/DynamicHeight.js";
@@ -359,6 +360,21 @@ export function BuyWidget(props: BuyWidgetProps) {
className={props.className}
style={props.style}
>
{props.connectOptions?.autoConnect !== false && (
<AutoConnect
client={props.client}
wallets={props.connectOptions?.wallets}
timeout={
typeof props.connectOptions?.autoConnect === "object"
? props.connectOptions?.autoConnect?.timeout
: undefined
}
appMetadata={props.connectOptions?.appMetadata}
accountAbstraction={props.connectOptions?.accountAbstraction}
chain={props.connectOptions?.chain}
/>
)}
<BridgeWidgetContent
{...props}
theme={props.theme || "dark"}
@@ -298,6 +298,7 @@ export function FundWallet(props: FundWalletProps) {
}}
theme={theme}
{...props.connectOptions}
autoConnect={false}
/>
) : (
<Button
@@ -8,6 +8,7 @@ import walletInfos from "../../../../../wallets/__generated__/wallet-infos.js";
import { NON_SEARCHABLE_WALLETS } from "../../../../../wallets/constants.js";
import { createWallet } from "../../../../../wallets/create-wallet.js";
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
import type { WalletId } from "../../../../../wallets/wallet-types.js";
import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
import { iconSize, spacing } from "../../../../core/design-system/index.js";
import { useSetSelectionData } from "../../../providers/wallet-ui-states-provider.js";
@@ -34,15 +35,16 @@ function AllWalletsUI(props: {
recommendedWallets: Wallet[] | undefined;
connectLocale: ConnectLocale;
disableSelectionDataReset?: boolean;
walletIdsToHide?: WalletId[];
}) {
const { itemsToShow, lastItemRef } = useShowMore<HTMLLIElement>(10, 10);
const setSelectionData = useSetSelectionData();
const walletList = useMemo(() => {
return walletInfos.filter(
(info) => !NON_SEARCHABLE_WALLETS.includes(info.id),
);
}, []);
return walletInfos
.filter((info) => !NON_SEARCHABLE_WALLETS.includes(info.id))
.filter((info) => !props.walletIdsToHide?.includes(info.id));
}, [props.walletIdsToHide]);
const fuseInstance = useMemo(() => {
return new Fuse(walletList, {
@@ -172,6 +172,7 @@ export const ConnectModalContent = (props: {
onBack={handleBack}
onSelect={setScreen}
recommendedWallets={props.recommendedWallets}
walletIdsToHide={props.walletIdsToHide}
size={props.size}
/>
</Suspense>
@@ -138,6 +138,20 @@ export function LargeAmount() {
);
}
export function NoAutoConnect() {
return (
<Variant
client={storyClient}
chain={ethereum}
tokenAddress="0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
amount="150000"
connectOptions={{
autoConnect: false,
}}
/>
);
}
function Variant(props: BuyWidgetProps) {
return (
<div
+41 -2
View File
@@ -30,6 +30,45 @@ function isIOS(): boolean {
/**
* @internal
*/
export function isMobile(): boolean {
return isAndroid() || isIOS();
function hasTouchScreen(): boolean {
if (typeof window === "undefined" || typeof navigator === "undefined") {
return false;
}
return (
"ontouchstart" in window ||
navigator.maxTouchPoints > 0 ||
// @ts-expect-error - msMaxTouchPoints is IE specific
navigator.msMaxTouchPoints > 0
);
}
/**
* @internal
*/
function hasMobileAPIs(): boolean {
if (typeof window === "undefined") {
return false;
}
return "orientation" in window || "onorientationchange" in window;
}
/**
* @internal
*/
export function isMobile(): boolean {
// Primary signal: OS detection via user agent
const isMobileOS = isAndroid() || isIOS();
if (isMobileOS) {
return true;
}
// Secondary signal: catch edge cases like webviews with modified user agents
// Both touch capability AND mobile-specific APIs must be present to avoid
// false positives on touch-enabled desktops
if (hasTouchScreen() && hasMobileAPIs()) {
return true;
}
return false;
}
@@ -1,5 +1,6 @@
import { signLoginPayload } from "../../../../auth/core/sign-login-payload.js";
import type { LoginPayload } from "../../../../auth/core/types.js";
import type { Chain } from "../../../../chains/types.js";
import { getCachedChain } from "../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { getClientFetch } from "../../../../utils/fetch.js";
@@ -15,10 +16,11 @@ import type { AuthStoredTokenWithCookieReturnType } from "./types.js";
export async function siweAuthenticate(args: {
wallet: Wallet;
client: ThirdwebClient;
chain?: Chain;
ecosystem?: Ecosystem;
}): Promise<AuthStoredTokenWithCookieReturnType> {
const { wallet, client, ecosystem } = args;
const siweChain = getCachedChain(1); // always use mainnet for SIWE for wide wallet compatibility
const { wallet, client, ecosystem, chain } = args;
const siweChain = chain || getCachedChain(1); // fallback to mainnet for SIWE for wide wallet compatibility
// only connect if the wallet doesn't already have an account
const account =
wallet.getAccount() || (await wallet.connect({ chain: siweChain, client }));
@@ -69,7 +69,7 @@ export type SingleStepAuthArgsType =
| {
strategy: "wallet";
wallet: Wallet;
chain: Chain;
chain?: Chain;
}
| {
strategy: "guest";
@@ -186,6 +186,7 @@ export class InAppNativeConnector implements InAppConnector {
client: this.client,
ecosystem: params.ecosystem,
wallet: params.wallet,
chain: params.chain,
});
}
case "github":
@@ -107,6 +107,7 @@ describe("InAppWebConnector.connect", () => {
client: TEST_CLIENT,
ecosystem: undefined,
wallet: mockWallet,
chain: ethereum,
});
});
@@ -368,6 +368,7 @@ export class InAppWebConnector implements InAppConnector {
client: this.client,
ecosystem: this.ecosystem,
wallet: args.wallet,
chain: args.chain,
});
}
}
+16 -228
View File
@@ -1,23 +1,18 @@
import type { Abi } from "abitype";
import { toFunctionSelector } from "viem/utils";
import { ChainIdToNetwork, type Money, moneySchema } from "x402/types";
import { getCachedChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
import { resolveContractAbi } from "../contract/actions/resolve-abi.js";
import { getContract } from "../contract/contract.js";
import { isPermitSupported } from "../extensions/erc20/__generated__/IERC20Permit/write/permit.js";
import { isTransferWithAuthorizationSupported } from "../extensions/erc20/__generated__/USDC/write/transferWithAuthorization.js";
import { getAddress } from "../utils/address.js";
import { toUnits } from "../utils/units.js";
import { decodePayment } from "./encode.js";
import type { ThirdwebX402Facilitator } from "./facilitator.js";
import {
networkToChainId,
networkToCaip2ChainId,
type RequestedPaymentPayload,
type RequestedPaymentRequirements,
} from "./schemas.js";
import {
type DefaultAsset,
type ERC20TokenAmount,
type PaymentArgs,
type PaymentRequiredResult,
@@ -49,96 +44,27 @@ export async function decodePaymentRequest(
routeConfig = {},
method,
paymentData,
extraMetadata,
} = args;
const {
description,
mimeType,
maxTimeoutSeconds,
inputSchema,
outputSchema,
errorMessages,
discoverable,
} = routeConfig;
const { errorMessages } = routeConfig;
let chainId: number;
try {
chainId = networkToChainId(network);
} catch (error) {
return {
status: 402,
responseHeaders: { "Content-Type": "application/json" },
responseBody: {
x402Version,
error:
error instanceof Error
? error.message
: `Invalid network: ${network}`,
accepts: [],
},
};
}
const atomicAmountForAsset = await processPriceToAtomicAmount(
const paymentRequirementsResult = await facilitator.accepts({
resourceUrl,
method,
network,
price,
chainId,
facilitator,
);
if ("error" in atomicAmountForAsset) {
return {
status: 402,
responseHeaders: { "Content-Type": "application/json" },
responseBody: {
x402Version,
error: atomicAmountForAsset.error,
accepts: [],
},
};
}
const { maxAmountRequired, asset } = atomicAmountForAsset;
const paymentRequirements: RequestedPaymentRequirements[] = [];
const mappedNetwork = ChainIdToNetwork[chainId];
paymentRequirements.push({
scheme: "exact",
network: mappedNetwork ? mappedNetwork : `eip155:${chainId}`,
maxAmountRequired,
resource: resourceUrl,
description: description ?? "",
mimeType: mimeType ?? "application/json",
payTo: getAddress(facilitator.address), // always pay to the facilitator address first
maxTimeoutSeconds: maxTimeoutSeconds ?? 86400,
asset: getAddress(asset.address),
outputSchema: {
input: {
type: "http",
method,
discoverable: discoverable ?? true,
...inputSchema,
},
output: outputSchema,
},
extra: {
recipientAddress: payTo, // input payTo is the final recipient address
...((asset as ERC20TokenAmount["asset"]).eip712 ?? {}),
},
routeConfig,
payTo,
extraMetadata,
});
// Check for payment header
// Check for payment header, if none, return the payment requirements
if (!paymentData) {
return {
status: 402,
responseHeaders: {
"Content-Type": "application/json",
},
responseBody: {
x402Version,
error: errorMessages?.paymentRequired || "X-PAYMENT header is required",
accepts: paymentRequirements,
},
};
return paymentRequirementsResult;
}
const paymentRequirements = paymentRequirementsResult.responseBody.accepts;
// decode b64 payment
let decodedPayment: RequestedPaymentPayload;
try {
@@ -163,8 +89,8 @@ export async function decodePaymentRequest(
const selectedPaymentRequirements = paymentRequirements.find(
(value) =>
value.scheme === decodedPayment.scheme &&
networkToChainId(value.network) ===
networkToChainId(decodedPayment.network),
networkToCaip2ChainId(value.network) ===
networkToCaip2ChainId(decodedPayment.network),
);
if (!selectedPaymentRequirements) {
return {
@@ -190,86 +116,6 @@ export async function decodePaymentRequest(
};
}
/**
* Parses the amount from the given price
*
* @param price - The price to parse
* @param network - The network to get the default asset for
* @returns The parsed amount or an error message
*/
async function processPriceToAtomicAmount(
price: Money | ERC20TokenAmount,
chainId: number,
facilitator: ThirdwebX402Facilitator,
): Promise<
{ maxAmountRequired: string; asset: DefaultAsset } | { error: string }
> {
// Handle USDC amount (string) or token amount (ERC20TokenAmount)
let maxAmountRequired: string;
let asset: DefaultAsset;
if (typeof price === "string" || typeof price === "number") {
// USDC amount in dollars
const parsedAmount = moneySchema.safeParse(price);
if (!parsedAmount.success) {
return {
error: `Invalid price (price: ${price}). Must be in the form "$3.10", 0.10, "0.001", ${parsedAmount.error}`,
};
}
const parsedUsdAmount = parsedAmount.data;
const defaultAsset = await getDefaultAsset(chainId, facilitator);
if (!defaultAsset) {
return {
error: `Unable to get default asset on chain ${chainId}. Please specify an asset in the payment requirements.`,
};
}
asset = defaultAsset;
maxAmountRequired = toUnits(
parsedUsdAmount.toString(),
defaultAsset.decimals,
).toString();
} else {
// Token amount in atomic units
maxAmountRequired = price.amount;
const tokenExtras = await getOrDetectTokenExtras({
facilitator,
partialAsset: price.asset,
chainId,
});
if (!tokenExtras) {
return {
error: `Unable to find token information for ${price.asset.address} on chain ${chainId}. Please specify the asset decimals and eip712 information in the asset options.`,
};
}
asset = {
address: price.asset.address,
decimals: tokenExtras.decimals,
eip712: {
name: tokenExtras.name,
version: tokenExtras.version,
primaryType: tokenExtras.primaryType,
},
};
}
return {
maxAmountRequired,
asset,
};
}
async function getDefaultAsset(
chainId: number,
facilitator: ThirdwebX402Facilitator,
): Promise<DefaultAsset | undefined> {
const supportedAssets = await facilitator.supported();
const matchingAsset = supportedAssets.kinds.find(
(supported) => networkToChainId(supported.network) === chainId,
);
const assetConfig = matchingAsset?.extra?.defaultAsset as DefaultAsset;
return assetConfig;
}
export async function getSupportedSignatureType(args: {
client: ThirdwebClient;
asset: string;
@@ -309,61 +155,3 @@ export async function getSupportedSignatureType(args: {
}
return undefined;
}
async function getOrDetectTokenExtras(args: {
facilitator: ThirdwebX402Facilitator;
partialAsset: ERC20TokenAmount["asset"];
chainId: number;
}): Promise<
| {
name: string;
version: string;
decimals: number;
primaryType: SupportedSignatureType;
}
| undefined
> {
const { facilitator, partialAsset, chainId } = args;
if (
partialAsset.eip712?.name &&
partialAsset.eip712?.version &&
partialAsset.decimals !== undefined
) {
return {
name: partialAsset.eip712.name,
version: partialAsset.eip712.version,
decimals: partialAsset.decimals,
primaryType: partialAsset.eip712.primaryType,
};
}
// read from facilitator
const response = await facilitator
.supported({
chainId,
tokenAddress: partialAsset.address,
})
.catch(() => {
return {
kinds: [],
};
});
const exactScheme = response.kinds?.find((kind) => kind.scheme === "exact");
if (!exactScheme) {
return undefined;
}
const supportedAsset = exactScheme.extra?.supportedAssets?.find(
(asset) =>
asset.address.toLowerCase() === partialAsset.address.toLowerCase(),
);
if (!supportedAsset) {
return undefined;
}
return {
name: supportedAsset.eip712.name,
version: supportedAsset.eip712.version,
decimals: supportedAsset.decimals,
primaryType: supportedAsset.eip712.primaryType as SupportedSignatureType,
};
}
+2 -6
View File
@@ -1,8 +1,5 @@
import type { ExactEvmPayload } from "x402/types";
import {
type RequestedPaymentPayload,
RequestedPaymentPayloadSchema,
} from "./schemas.js";
import type { RequestedPaymentPayload } from "./schemas.js";
/**
* Encodes a payment payload into a base64 string, ensuring bigint values are properly stringified
@@ -44,8 +41,7 @@ export function decodePayment(payment: string): RequestedPaymentPayload {
...parsed,
payload: parsed.payload as ExactEvmPayload,
};
const validated = RequestedPaymentPayloadSchema.parse(obj);
return validated;
return obj;
}
/**
+48 -6
View File
@@ -2,13 +2,15 @@ import type { VerifyResponse } from "x402/types";
import type { ThirdwebClient } from "../client/client.js";
import { stringify } from "../utils/json.js";
import { withCache } from "../utils/promise/withCache.js";
import type {
FacilitatorSettleResponse,
FacilitatorSupportedResponse,
FacilitatorVerifyResponse,
RequestedPaymentPayload,
RequestedPaymentRequirements,
import {
type FacilitatorSettleResponse,
type FacilitatorSupportedResponse,
type FacilitatorVerifyResponse,
networkToCaip2ChainId,
type RequestedPaymentPayload,
type RequestedPaymentRequirements,
} from "./schemas.js";
import type { PaymentArgs, PaymentRequiredResult } from "./types.js";
export type WaitUntil = "simulated" | "submitted" | "confirmed";
@@ -46,6 +48,9 @@ export type ThirdwebX402Facilitator = {
chainId: number;
tokenAddress?: string;
}) => Promise<FacilitatorSupportedResponse>;
accepts: (
args: Omit<PaymentArgs, "facilitator">,
) => Promise<PaymentRequiredResult>;
};
const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
@@ -256,6 +261,43 @@ export function facilitator(
},
);
},
async accepts(
args: Omit<PaymentArgs, "facilitator">,
): Promise<PaymentRequiredResult> {
const url = config.baseUrl ?? DEFAULT_BASE_URL;
let headers = { "Content-Type": "application/json" };
const authHeaders = await facilitator.createAuthHeaders();
headers = { ...headers, ...authHeaders.verify }; // same as verify
const caip2ChainId = networkToCaip2ChainId(args.network);
const res = await fetch(`${url}/accepts`, {
method: "POST",
headers,
body: stringify({
resourceUrl: args.resourceUrl,
method: args.method,
network: caip2ChainId,
price: args.price,
routeConfig: args.routeConfig,
serverWalletAddress: facilitator.address,
recipientAddress: args.payTo,
extraMetadata: args.extraMetadata,
}),
});
if (res.status !== 402) {
throw new Error(
`Failed to construct payment requirements: ${res.statusText} - ${await res.text()}`,
);
}
return {
status: res.status as 402,
responseBody:
(await res.json()) as PaymentRequiredResult["responseBody"],
responseHeaders: {
"Content-Type": "application/json",
},
};
},
};
return facilitator;
+18 -5
View File
@@ -2,7 +2,8 @@ import { getCachedChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
import type { Wallet } from "../wallets/interfaces/wallet.js";
import {
networkToChainId,
extractEvmChainId,
networkToCaip2ChainId,
type RequestedPaymentRequirements,
RequestedPaymentRequirementsSchema,
} from "./schemas.js";
@@ -51,7 +52,7 @@ export function wrapFetchWithPayment(
fetch: typeof globalThis.fetch,
client: ThirdwebClient,
wallet: Wallet,
maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
maxValue?: bigint,
) {
return async (input: RequestInfo, init?: RequestInit) => {
const response = await fetch(input, init);
@@ -90,15 +91,25 @@ export function wrapFetchWithPayment(
);
}
if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
if (
maxValue &&
BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue
) {
throw new Error(
`Payment amount exceeds maximum allowed (currently set to ${maxValue} in base units)`,
);
}
const paymentChainId = networkToChainId(
const caip2ChainId = networkToCaip2ChainId(
selectedPaymentRequirements.network,
);
const paymentChainId = extractEvmChainId(caip2ChainId);
// TODO (402): support solana
if (paymentChainId === null) {
throw new Error(
`Unsupported chain ID: ${selectedPaymentRequirements.network}`,
);
}
// switch to the payment chain if it's not the current chain
if (paymentChainId !== chain.id) {
@@ -150,7 +161,9 @@ function defaultPaymentRequirementsSelector(
}
// find the payment requirements matching the connected wallet chain
const matchingPaymentRequirements = paymentRequirements.find(
(x) => networkToChainId(x.network) === chainId && x.scheme === scheme,
(x) =>
extractEvmChainId(networkToCaip2ChainId(x.network)) === chainId &&
x.scheme === scheme,
);
if (matchingPaymentRequirements) {
+93 -23
View File
@@ -15,7 +15,7 @@ const FacilitatorNetworkSchema = z.string();
export type FacilitatorNetwork = z.infer<typeof FacilitatorNetworkSchema>;
export const RequestedPaymentPayloadSchema = PaymentPayloadSchema.extend({
const RequestedPaymentPayloadSchema = PaymentPayloadSchema.extend({
network: FacilitatorNetworkSchema,
});
@@ -59,7 +59,7 @@ export const SupportedSignatureTypeSchema = z.enum([
"Permit",
]);
export const FacilitatorSupportedAssetSchema = z.object({
const FacilitatorSupportedAssetSchema = z.object({
address: z.string(),
decimals: z.number(),
eip712: z.object({
@@ -92,25 +92,95 @@ export type FacilitatorSupportedResponse = z.infer<
typeof FacilitatorSupportedResponseSchema
>;
export function networkToChainId(network: string | Chain): number {
if (typeof network === "object") {
return network.id;
}
if (network.startsWith("eip155:")) {
const chainId = parseInt(network.split(":")[1] ?? "0");
if (!Number.isNaN(chainId) && chainId > 0) {
return chainId;
} else {
throw new Error(`Invalid network: ${network}`);
}
}
const mappedChainId = EvmNetworkToChainId.get(network as Network);
if (!mappedChainId) {
throw new Error(`Invalid network: ${network}`);
}
// TODO (402): support solana networks
if (mappedChainId === 101 || mappedChainId === 103) {
throw new Error("Solana networks not supported yet.");
}
return mappedChainId;
function isEvmChain(caip2ChainId: Caip2ChainId): boolean {
return caip2ChainId.startsWith("eip155:");
}
/**
* Extract numeric chain ID from CAIP-2 EVM chain (e.g., "eip155:1" -> 1)
*/
export function extractEvmChainId(caip2ChainId: Caip2ChainId): number | null {
if (!isEvmChain(caip2ChainId)) {
return null;
}
const parts = caip2ChainId.split(":");
const chainId = Number(parts[1]);
return Number.isNaN(chainId) ? null : chainId;
}
/**
* CAIP-2 compliant blockchain identifier
* @see https://chainagnostic.org/CAIPs/caip-2
*/
const Caip2ChainIdSchema = z
.union([z.string(), z.number().int().positive()])
.transform((value, ctx) => {
// Handle proper CAIP-2 format (already valid)
if (typeof value === "string" && value.includes(":")) {
const [namespace, reference] = value.split(":");
// Solana mainnet/devnet aliases
if (namespace === "solana" && reference === "mainnet") {
return "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ" as const;
}
if (namespace === "solana" && reference === "devnet") {
return "solana:8E9rvCKLFQia2Y35HXjjpWzj8weVo44K" as const;
}
// Validate CAIP-2 format
const namespaceRegex = /^[-a-z0-9]{3,8}$/;
const referenceRegex = /^[-_a-zA-Z0-9]{1,32}$/;
if (!namespaceRegex.test(namespace ?? "")) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid CAIP-2 namespace: ${namespace}. Must match [-a-z0-9]{3,8}`,
});
return z.NEVER;
}
if (!referenceRegex.test(reference ?? "")) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid CAIP-2 reference: ${reference}. Must match [-_a-zA-Z0-9]{1,32}`,
});
return z.NEVER;
}
return value as `${string}:${string}`;
}
// Handle number (EVM chain ID fallback)
if (typeof value === "number") {
return `eip155:${value}` as const;
}
// Handle string number (EVM chain ID fallback)
const numValue = Number(value);
if (!Number.isNaN(numValue) && Number.isInteger(numValue) && numValue > 0) {
return `eip155:${numValue}` as const;
}
const mappedChainId = EvmNetworkToChainId.get(value as Network);
if (mappedChainId) {
return `eip155:${mappedChainId}` as const;
}
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid chain ID: ${value}. Must be a CAIP-2 identifier (e.g., "eip155:1", "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ"), a numeric chain ID for EVM, or "solana:mainnet"/"solana:devnet"`,
});
return z.NEVER;
})
.describe(
"CAIP-2 blockchain identifier (e.g., 'eip155:1' for Ethereum, 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ' for Solana mainnet). Also accepts numeric EVM chain IDs (e.g., 1, 137) or aliases ('solana:mainnet', 'solana:devnet') for backward compatibility.",
);
type Caip2ChainId = z.output<typeof Caip2ChainIdSchema>;
export function networkToCaip2ChainId(network: string | Chain): Caip2ChainId {
if (typeof network === "object") {
return `eip155:${network.id}` as const;
}
return Caip2ChainIdSchema.parse(network);
}
+18 -4
View File
@@ -10,7 +10,8 @@ import type { Account } from "../wallets/interfaces/wallet.js";
import { getSupportedSignatureType } from "./common.js";
import { encodePayment } from "./encode.js";
import {
networkToChainId,
extractEvmChainId,
networkToCaip2ChainId,
type RequestedPaymentPayload,
type RequestedPaymentRequirements,
type UnsignedPaymentPayload,
@@ -71,7 +72,14 @@ async function signPaymentHeader(
x402Version: number,
): Promise<RequestedPaymentPayload> {
const from = getAddress(account.address);
const chainId = networkToChainId(paymentRequirements.network);
const caip2ChainId = networkToCaip2ChainId(paymentRequirements.network);
const chainId = extractEvmChainId(caip2ChainId);
// TODO (402): support solana
if (chainId === null) {
throw new Error(`Unsupported chain ID: ${paymentRequirements.network}`);
}
const supportedSignatureType = await getSupportedSignatureType({
client,
asset: paymentRequirements.asset,
@@ -191,7 +199,10 @@ async function signERC3009Authorization(
}: ExactEvmPayloadAuthorization,
{ asset, network, extra }: RequestedPaymentRequirements,
): Promise<{ signature: Hex }> {
const chainId = networkToChainId(network);
const chainId = extractEvmChainId(networkToCaip2ChainId(network));
if (chainId === null) {
throw new Error(`Unsupported chain ID: ${network}`);
}
const name = extra?.name;
const version = extra?.version;
@@ -233,7 +244,10 @@ async function signERC2612Permit(
{ from, to, value, validBefore, nonce }: ExactEvmPayloadAuthorization,
{ asset, network, extra }: RequestedPaymentRequirements,
): Promise<{ signature: Hex }> {
const chainId = networkToChainId(network);
const chainId = extractEvmChainId(networkToCaip2ChainId(network));
if (chainId === null) {
throw new Error(`Unsupported chain ID: ${network}`);
}
const name = extra?.name;
const version = extra?.version;
+2 -3
View File
@@ -6,7 +6,6 @@ import type { ThirdwebX402Facilitator, WaitUntil } from "./facilitator.js";
import type {
FacilitatorNetwork,
FacilitatorSettleResponse,
FacilitatorSupportedAssetSchema,
RequestedPaymentPayload,
RequestedPaymentRequirements,
SupportedSignatureTypeSchema,
@@ -36,6 +35,8 @@ export type PaymentArgs = {
routeConfig?: PaymentMiddlewareConfig;
/** Optional recipient address to receive the payment if different from your facilitator address */
payTo?: string;
/** Optional extra data to be included in the payment request */
extraMetadata?: Record<string, unknown>;
};
export type SettlePaymentArgs = PaymentArgs & {
@@ -110,5 +111,3 @@ export type ERC20TokenAmount = {
};
};
};
export type DefaultAsset = z.infer<typeof FacilitatorSupportedAssetSchema>;
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/wagmi-adapter
## 0.2.181
## 0.2.180
## 0.2.179
## 0.2.178
## 0.2.177
## 0.2.176
## 0.2.175
## 0.2.174
## 0.2.173
+1 -1
View File
@@ -56,5 +56,5 @@
"type": "module",
"types": "./dist/types/exports/thirdweb.d.ts",
"typings": "./dist/types/exports/thirdweb.d.ts",
"version": "0.2.174"
"version": "0.2.181"
}
+107 -111
View File
@@ -574,8 +574,8 @@ importers:
apps/playground-web:
dependencies:
'@abstract-foundation/agw-react':
specifier: ^1.6.4
version: 1.6.4(cp5zk5cv43kemeeoylvueylari)
specifier: ^1.10.0
version: 1.10.0(e4yhxgmiqbrp6q7xc7oorwzzpy)
'@ai-sdk/react':
specifier: ^2.0.25
version: 2.0.25([email protected])([email protected])
@@ -997,7 +997,7 @@ importers:
version: 2.37.9([email protected])([email protected])([email protected])([email protected])
wagmi:
specifier: 2.17.5
version: 2.17.5(@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])
version: 2.17.5(@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])
devDependencies:
'@biomejs/biome':
specifier: 2.0.6
@@ -1277,6 +1277,27 @@ importers:
specifier: 5.8.3
version: 5.8.3
packages/nexus-fetch:
devDependencies:
'@biomejs/biome':
specifier: 2.0.6
version: 2.0.6
'@size-limit/preset-small-lib':
specifier: 11.2.0
version: 11.2.0([email protected])
knip:
specifier: 5.60.2
version: 5.60.2(@types/[email protected])([email protected])
rimraf:
specifier: 6.0.1
version: 6.0.1
size-limit:
specifier: 11.2.0
version: 11.2.0
typescript:
specifier: 5.8.3
version: 5.8.3
packages/react-native-adapter:
dependencies:
'@aws-sdk/client-kms':
@@ -1806,42 +1827,29 @@ packages:
graphql:
optional: true
'@abstract-foundation/agw-client@1.6.2':
resolution: {integrity: sha512-NaXMfRFdujZNWeFn89p1gzxt17bWW9QTW3kIcmiaDSjPxifT9sKa+Qa1v33Rz3kpBM/PJVQkaFUgA7Jff3bidw==}
'@abstract-foundation/agw-client@1.10.0':
resolution: {integrity: sha512-OGI8V8/MWbcJSw3st+pSxBUHEWkfS1ikBAr4ylSAsuyn+VpvV+G/lto0LZR7uAZnH3uK/cTv5e6YKYGodb1KIw==}
peerDependencies:
abitype: ^1.0.0
typescript: '>=5.0.4'
viem: ^2.22.23
viem: ^2.33.3
peerDependenciesMeta:
typescript:
optional: true
'@abstract-foundation/agw-[email protected]':
resolution: {integrity: sha512-DhGsLT6xei/asqnJeql2bSVdsWv9pgegWHbETSlK22JZwOhKivC5mxdR97XEg9jK680ymBFKIrFwxEMmcjog5Q==}
'@abstract-foundation/agw-[email protected]':
resolution: {integrity: sha512-QWsCSZFCTkuOuqt+lL2Eguez7BK2tNyie6OtVzxwvwEZa7uk1mPicAQWjy1WUBQIPucVP2gJxnozGFLo9liTtA==}
peerDependencies:
abitype: ^1.0.0
typescript: '>=5.0.4'
viem: ^2.22.23
peerDependenciesMeta:
typescript:
optional: true
'@abstract-foundation/[email protected]':
resolution: {integrity: sha512-8ZOuQOACWuYf9T13AfejlJeS/Ai2zfem3etvrIzrDUurYmy3smA/3bXzRA0ydjgYsu2yvMwE8nBMggiit+OGew==}
peerDependencies:
'@privy-io/cross-app-connect': ^0.2.0
'@privy-io/react-auth': ^2.8.2
'@rainbow-me/rainbowkit': '*'
'@privy-io/cross-app-connect': ^0.2.2
'@privy-io/react-auth': ^2.21.2
'@tanstack/react-query': ^5
react: '>=18'
secp256k1: '>=5.0.1'
thirdweb: ^5.68.0
typescript: '>=5.0.4'
viem: ^2.22.23
viem: ^2.33.3
wagmi: ^2.14.11
peerDependenciesMeta:
'@rainbow-me/rainbowkit':
optional: true
thirdweb:
optional: true
typescript:
@@ -8233,7 +8241,6 @@ packages:
'@walletconnect/[email protected]':
resolution: {integrity: sha512-a/K5PRIFPCjfHq5xx3WYKHAAF8Ft2I1LtxloyibqiQOoUtNLfKgFB1r8sdMvXM7/PADNPe4iAw4uSE6PrARrfg==}
deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases'
'@walletconnect/[email protected]':
resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==}
@@ -8245,7 +8252,6 @@ packages:
'@walletconnect/[email protected]':
resolution: {integrity: sha512-IAs/IqmE1HVL9EsvqkNRU4NeAYe//h9NwqKi7ToKYZv4jhcC3BBemUD1r8iQJSTHMhO41EKn1G9/DiBln3ZiwQ==}
deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases'
'@walletconnect/[email protected]':
resolution: {integrity: sha512-lTcUbMjQ0YUZ5wzCLhpBeS9OkWYgLLly6BddEp2+pm4QxiwCCU2Nao0nBJXgzKbZYQOgrEGqtdm/7ze67gjzRA==}
@@ -8271,7 +8277,6 @@ packages:
'@walletconnect/[email protected]':
resolution: {integrity: sha512-LkKg+EjcSUpPUhhvRANgkjPL38wJPIWumAYD8OK/g4OFuJ4W3lS/XTCKthABQfFqmiNbNbVllmywiyE44KdpQg==}
deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases'
'@walletconnect/[email protected]':
resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==}
@@ -8283,7 +8288,6 @@ packages:
'@walletconnect/[email protected]':
resolution: {integrity: sha512-SMXGGXyj78c8Ru2f665ZFZU24phn0yZyCP5Ej7goxVQxABwqWKM/odj3j/IxZv+hxA8yU13yxaubgVefnereqw==}
deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases'
'@walletconnect/[email protected]':
resolution: {integrity: sha512-Nc1Z6VXnya152yucNDPnlTkrhG289tCUfcjiWqDmwNYzFSfdT5oJQHlnffQXlvoks90kn5Ru8Rnwag2CH1YOVQ==}
@@ -16643,26 +16647,18 @@ snapshots:
optionalDependencies:
graphql: 16.11.0
'@abstract-foundation/agw-client@1.6.2([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))':
'@abstract-foundation/agw-client@1.10.0([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))':
dependencies:
abitype: 1.1.1([email protected])([email protected])
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
optionalDependencies:
typescript: 5.8.3
'@abstract-foundation/agw-[email protected]([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))':
'@abstract-foundation/agw-[email protected](e4yhxgmiqbrp6q7xc7oorwzzpy)':
dependencies:
abitype: 1.1.1([email protected])([email protected])
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
optionalDependencies:
typescript: 5.8.3
optional: true
'@abstract-foundation/[email protected](cp5zk5cv43kemeeoylvueylari)':
dependencies:
'@abstract-foundation/agw-client': 1.6.2([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))
'@abstract-foundation/agw-client': 1.10.0([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))
'@privy-io/cross-app-connect': 0.2.2(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected]([email protected])([email protected])([email protected])([email protected]))
'@privy-io/react-auth': 2.17.3(@abstract-foundation/agw-client@1.8.5([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected])))(@solana/[email protected](@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected]))(@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
'@privy-io/react-auth': 2.17.3(@abstract-foundation/agw-client@1.10.0([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected])))(@solana/[email protected](@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected]))(@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
'@tanstack/react-query': 5.81.5([email protected])
react: 19.1.0
secp256k1: 5.0.1
@@ -21459,7 +21455,7 @@ snapshots:
- typescript
- utf-8-validate
'@privy-io/[email protected](@abstract-foundation/agw-client@1.8.5([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected])))(@solana/[email protected](@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected]))(@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])':
'@privy-io/[email protected](@abstract-foundation/agw-client@1.10.0([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected])))(@solana/[email protected](@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected]))(@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])':
dependencies:
'@coinbase/wallet-sdk': 4.3.2
'@floating-ui/react': 0.26.28([email protected]([email protected]))([email protected])
@@ -21505,7 +21501,7 @@ snapshots:
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
zustand: 5.0.7(@types/[email protected])([email protected])([email protected]([email protected]))
optionalDependencies:
'@abstract-foundation/agw-client': 1.8.5([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))
'@abstract-foundation/agw-client': 1.10.0([email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))
'@solana/spl-token': 0.4.14(@solana/[email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])
'@solana/web3.js': 1.98.2([email protected])([email protected])([email protected])([email protected])
transitivePeerDependencies:
@@ -22562,11 +22558,11 @@ snapshots:
- utf-8-validate
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-wallet': 1.7.8([email protected])([email protected])([email protected])
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
valtio: 1.13.2(@types/[email protected])([email protected])
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
transitivePeerDependencies:
@@ -22735,12 +22731,12 @@ snapshots:
- utf-8-validate
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
lit: 3.3.0
valtio: 1.13.2(@types/[email protected])([email protected])
transitivePeerDependencies:
@@ -22921,12 +22917,12 @@ snapshots:
- valtio
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-wallet': 1.7.8([email protected])([email protected])([email protected])
lit: 3.3.0
transitivePeerDependencies:
@@ -23136,10 +23132,10 @@ snapshots:
- utf-8-validate
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-wallet': 1.7.8([email protected])([email protected])([email protected])
lit: 3.3.0
qrcode: 1.5.3
@@ -23316,14 +23312,14 @@ snapshots:
- utf-8-validate
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-polyfills': 1.7.8
'@reown/appkit-wallet': 1.7.8([email protected])([email protected])([email protected])
'@walletconnect/logger': 2.1.2
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
valtio: 1.13.2(@types/[email protected])([email protected])
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
transitivePeerDependencies:
@@ -23553,18 +23549,18 @@ snapshots:
- utf-8-validate
- zod
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
'@reown/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@reown/appkit-common': 1.7.8([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-pay': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-controllers': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-pay': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-polyfills': 1.7.8
'@reown/appkit-scaffold-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-scaffold-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-ui': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit-utils': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])
'@reown/appkit-wallet': 1.7.8([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/universal-provider': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
bs58: 6.0.0
valtio: 1.13.2(@types/[email protected])([email protected])
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
@@ -28048,7 +28044,7 @@ snapshots:
- bufferutil
- utf-8-validate
'@wagmi/[email protected](@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))([email protected])':
'@wagmi/[email protected](@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))([email protected])':
dependencies:
'@base-org/account': 1.1.1(@types/[email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
'@coinbase/wallet-sdk': 4.3.6(@types/[email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
@@ -28057,9 +28053,9 @@ snapshots:
'@safe-global/safe-apps-provider': 0.18.6([email protected])([email protected])([email protected])([email protected])
'@safe-global/safe-apps-sdk': 9.1.0([email protected])([email protected])([email protected])([email protected])
'@wagmi/core': 2.21.2(@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))
'@walletconnect/ethereum-provider': 2.21.1(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/ethereum-provider': 2.21.1(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
cbw-sdk: '@coinbase/[email protected]'
porto: 0.2.19(@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))
porto: 0.2.19(@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))
viem: 2.37.9([email protected])([email protected])([email protected])([email protected])
optionalDependencies:
typescript: 5.8.3
@@ -28449,21 +28445,21 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-provider': 1.0.14
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/jsonrpc-ws-connection': 1.0.16([email protected])([email protected])
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/logger': 2.1.2
'@walletconnect/relay-api': 1.0.11
'@walletconnect/relay-auth': 1.1.0
'@walletconnect/safe-json': 1.0.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.0
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/window-getters': 1.0.1
es-toolkit: 1.33.0
events: 3.3.0
@@ -28621,21 +28617,21 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-provider': 1.0.14
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/jsonrpc-ws-connection': 1.0.16([email protected])([email protected])
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/logger': 2.1.2
'@walletconnect/relay-api': 1.0.11
'@walletconnect/relay-auth': 1.1.0
'@walletconnect/safe-json': 1.0.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.1
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/window-getters': 1.0.1
es-toolkit: 1.33.0
events: 3.3.0
@@ -28917,18 +28913,18 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@reown/appkit': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@reown/appkit': 1.7.8(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/jsonrpc-http-connection': 1.0.8([email protected])
'@walletconnect/jsonrpc-provider': 1.0.14
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/sign-client': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1
'@walletconnect/universal-provider': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/sign-client': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/universal-provider': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -29267,16 +29263,16 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/core': 2.21.0([email protected])([email protected])([email protected])([email protected])
'@walletconnect/core': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/events': 1.0.1
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/logger': 2.1.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.0
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -29407,16 +29403,16 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/core': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/core': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/events': 1.0.1
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/logger': 2.1.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.1
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -29864,18 +29860,18 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/events': 1.0.1
'@walletconnect/jsonrpc-http-connection': 1.0.8([email protected])
'@walletconnect/jsonrpc-provider': 1.0.14
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/logger': 2.1.2
'@walletconnect/sign-client': 2.21.0([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])
'@walletconnect/sign-client': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.0(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.0([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
es-toolkit: 1.33.0
events: 3.3.0
transitivePeerDependencies:
@@ -30020,18 +30016,18 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@walletconnect/events': 1.0.1
'@walletconnect/jsonrpc-http-connection': 1.0.8([email protected])
'@walletconnect/jsonrpc-provider': 1.0.14
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/logger': 2.1.2
'@walletconnect/sign-client': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])
'@walletconnect/sign-client': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
'@walletconnect/types': 2.21.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/utils': 2.21.1([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
es-toolkit: 1.33.0
events: 3.3.0
transitivePeerDependencies:
@@ -30262,18 +30258,18 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@noble/ciphers': 1.2.1
'@noble/curves': 1.8.1
'@noble/hashes': 1.7.1
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/relay-api': 1.0.11
'@walletconnect/relay-auth': 1.1.0
'@walletconnect/safe-json': 1.0.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.0
'@walletconnect/types': 2.21.0(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/window-getters': 1.0.1
'@walletconnect/window-metadata': 1.0.1
bs58: 6.0.0
@@ -30391,18 +30387,18 @@ snapshots:
- utf-8-validate
- zod
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])':
'@walletconnect/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])':
dependencies:
'@noble/ciphers': 1.2.1
'@noble/curves': 1.8.1
'@noble/hashes': 1.7.1
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1
'@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/relay-api': 1.0.11
'@walletconnect/relay-auth': 1.1.0
'@walletconnect/safe-json': 1.0.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.1
'@walletconnect/types': 2.21.1(@react-native-async-storage/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])
'@walletconnect/window-getters': 1.0.1
'@walletconnect/window-metadata': 1.0.1
bs58: 6.0.0
@@ -37243,7 +37239,7 @@ snapshots:
[email protected]: {}
[email protected](@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])):
[email protected](@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])):
dependencies:
'@wagmi/core': 2.21.2(@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))
hono: 4.9.9
@@ -37257,7 +37253,7 @@ snapshots:
'@tanstack/react-query': 5.81.5([email protected])
react: 19.1.0
typescript: 5.8.3
wagmi: 2.17.5(@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])
wagmi: 2.17.5(@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected])
transitivePeerDependencies:
- '@types/react'
- immer
@@ -40765,10 +40761,10 @@ snapshots:
- utf-8-validate
- zod
[email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]):
[email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]):
dependencies:
'@tanstack/react-query': 5.81.5([email protected])
'@wagmi/connectors': 5.11.2(@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))([email protected])
'@wagmi/connectors': 5.11.2(@tanstack/[email protected]([email protected]))(@types/[email protected])(@wagmi/[email protected](@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected](@tanstack/[email protected])(@tanstack/[email protected]([email protected]))(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])([email protected]))([email protected]))([email protected])
'@wagmi/core': 2.21.2(@tanstack/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected])([email protected])([email protected])([email protected]))
react: 19.1.0
use-sync-external-store: 1.4.0([email protected])