Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c37af4f191 | ||
|
|
f964b8aaff | ||
|
|
424e6b8f9b | ||
|
|
763d3de5a8 | ||
|
|
f6f138ccbc | ||
|
|
f5d5e73e57 | ||
|
|
673e51b11c | ||
|
|
282edf7ccb | ||
|
|
a87e91b99c | ||
|
|
0e630c4550 | ||
|
|
11c7bf0955 | ||
|
|
c9e430d092 | ||
|
|
1f440d0c97 | ||
|
|
3a0aac4393 | ||
|
|
7b13015053 | ||
|
|
7f081618e1 |
@@ -21,7 +21,8 @@ export type AuthOption =
|
||||
| "steam"
|
||||
| "apple"
|
||||
| "coinbase"
|
||||
| "line";
|
||||
| "line"
|
||||
| "epic";
|
||||
|
||||
export type Ecosystem = {
|
||||
name: string;
|
||||
|
||||
+16
-9
@@ -225,6 +225,10 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
|
||||
moduleInfo: {
|
||||
bytecodeUri: selectedModule.metadata.bytecodeUri,
|
||||
},
|
||||
isStylus:
|
||||
(selectedModule.metadata.compilers?.stylus &&
|
||||
selectedModule.metadata.compilers?.stylus?.length > 0) ||
|
||||
false,
|
||||
});
|
||||
},
|
||||
queryKey: [
|
||||
@@ -432,6 +436,7 @@ async function isModuleCompatible(options: {
|
||||
moduleInfo: {
|
||||
bytecodeUri: string;
|
||||
};
|
||||
isStylus: boolean;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
// 1. get module's bytecode
|
||||
@@ -444,15 +449,17 @@ async function isModuleCompatible(options: {
|
||||
|
||||
// 2. check compatibility with core and installed modules
|
||||
try {
|
||||
const isCompatible = await checkModulesCompatibility({
|
||||
chain: options.contractInfo.chain,
|
||||
client: options.client,
|
||||
coreBytecode: options.contractInfo.bytecode,
|
||||
moduleBytecodes: [
|
||||
moduleBytecode,
|
||||
...options.contractInfo.installedModuleBytecodes,
|
||||
],
|
||||
});
|
||||
const isCompatible =
|
||||
options.isStylus ||
|
||||
(await checkModulesCompatibility({
|
||||
chain: options.contractInfo.chain,
|
||||
client: options.client,
|
||||
coreBytecode: options.contractInfo.bytecode,
|
||||
moduleBytecodes: [
|
||||
moduleBytecode,
|
||||
...options.contractInfo.installedModuleBytecodes,
|
||||
],
|
||||
}));
|
||||
|
||||
return isCompatible;
|
||||
} catch (e) {
|
||||
|
||||
+10
-4
@@ -10,7 +10,11 @@ import {
|
||||
CircleXIcon,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { NATIVE_TOKEN_ADDRESS, type ThirdwebClient } from "thirdweb";
|
||||
import {
|
||||
getAddress,
|
||||
NATIVE_TOKEN_ADDRESS,
|
||||
type ThirdwebClient,
|
||||
} from "thirdweb";
|
||||
import type { Status, Token } from "thirdweb/bridge";
|
||||
import { status } from "thirdweb/bridge";
|
||||
import { toTokens } from "thirdweb/utils";
|
||||
@@ -123,6 +127,8 @@ function TokenInfo(props: {
|
||||
const isNativeToken =
|
||||
props.token.address.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();
|
||||
|
||||
const tokenAddress = getAddress(props.token.address);
|
||||
|
||||
return (
|
||||
<div className="flex-1 pt-10 pb-9 px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center">
|
||||
@@ -206,7 +212,7 @@ function TokenInfo(props: {
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-foreground ">{props.addressLabel}</p>
|
||||
<WalletAddress
|
||||
address={props.walletAddress}
|
||||
address={getAddress(props.walletAddress)}
|
||||
client={props.client}
|
||||
className="py-0.5 h-auto text-sm [&>*]:!font-sans tabular-nums [&>*]:font-normal text-muted-foreground hover:text-foreground"
|
||||
iconClassName="size-3"
|
||||
@@ -222,8 +228,8 @@ function TokenInfo(props: {
|
||||
? `/${chainQuery.data?.slug || props.token.chainId}`
|
||||
: `/${chainQuery.data?.slug || props.token.chainId}/${props.token.address}`
|
||||
}
|
||||
textToShow={props.token.address}
|
||||
textToCopy={props.token.address}
|
||||
textToShow={tokenAddress}
|
||||
textToCopy={tokenAddress}
|
||||
copyTooltip="Copy Token Address"
|
||||
className="-translate-x-1"
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Clock4Icon,
|
||||
InfoIcon,
|
||||
} from "lucide-react";
|
||||
import { toTokens } from "thirdweb";
|
||||
import { getAddress, toTokens } from "thirdweb";
|
||||
import { status } from "thirdweb/bridge";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import {
|
||||
@@ -153,6 +153,9 @@ function GeneericTxDetails(props: {
|
||||
|
||||
const timestamp = getDatefromTimestamp(block.timestamp);
|
||||
|
||||
const fromAddress = getAddress(transaction.from);
|
||||
const toAddress = transaction.to ? getAddress(transaction.to) : undefined;
|
||||
|
||||
return (
|
||||
<div className="border rounded-xl p-4 lg:p-6 bg-card">
|
||||
{/* section 1 */}
|
||||
@@ -216,8 +219,8 @@ function GeneericTxDetails(props: {
|
||||
<section className="border-b py-6 space-y-5 lg:space-y-3">
|
||||
<GridItem label="From" tooltip="The sending party of the transaction.">
|
||||
<CopyTextButton
|
||||
textToCopy={transaction.from}
|
||||
textToShow={transaction.from}
|
||||
textToCopy={fromAddress}
|
||||
textToShow={fromAddress}
|
||||
tooltip="Copy from address"
|
||||
variant="ghost"
|
||||
copyIconPosition="right"
|
||||
@@ -225,14 +228,14 @@ function GeneericTxDetails(props: {
|
||||
/>
|
||||
</GridItem>
|
||||
|
||||
{transaction.to && (
|
||||
{toAddress && (
|
||||
<GridItem
|
||||
label="Interacted with (To)"
|
||||
tooltip="The receiving party of the transaction (could be a contract address)."
|
||||
>
|
||||
<CopyTextButton
|
||||
textToCopy={transaction.to}
|
||||
textToShow={transaction.to}
|
||||
textToCopy={toAddress}
|
||||
textToShow={toAddress}
|
||||
tooltip="Copy to address"
|
||||
variant="ghost"
|
||||
copyIconPosition="right"
|
||||
|
||||
@@ -183,13 +183,64 @@ const STYLUS = {
|
||||
],
|
||||
description:
|
||||
"Airdrop your NFTs or tokens to a large number of recipients. Built with Arbitrum Stylus.",
|
||||
displayName: "Arbitrum Stylus Contracts",
|
||||
displayName: "Airdrop - Arbitrum Stylus",
|
||||
id: "stylus",
|
||||
name: "Stylus",
|
||||
showInExplore: true,
|
||||
isBeta: true,
|
||||
} satisfies ExploreCategory;
|
||||
|
||||
const MODULAR_CONTRACTS_STYLUS = {
|
||||
contracts: [
|
||||
// erc721 token
|
||||
[
|
||||
"thirdweb.eth/ERC721CoreInitializable",
|
||||
[
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC721",
|
||||
"deployer.thirdweb.eth/BatchMetadataERC721",
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC721",
|
||||
],
|
||||
{
|
||||
description: "ERC721 NFTs that only owners can mint.",
|
||||
title: "Modular NFT Collection",
|
||||
},
|
||||
],
|
||||
// erc1155 token
|
||||
[
|
||||
"thirdweb.eth/ERC1155CoreInitializable",
|
||||
[
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC1155",
|
||||
"deployer.thirdweb.eth/BatchMetadataERC1155",
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC1155",
|
||||
"deployer.thirdweb.eth/SequentialTokenIdERC1155",
|
||||
],
|
||||
{
|
||||
description: "ERC1155 NFTs that only owners can mint.",
|
||||
title: "Modular Edition",
|
||||
},
|
||||
],
|
||||
// erc20 token
|
||||
[
|
||||
"thirdweb.eth/ERC20CoreInitializable",
|
||||
[
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC20",
|
||||
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC20",
|
||||
],
|
||||
{
|
||||
description: "ERC20 Tokens that only owners can mint.",
|
||||
title: "Modular Token",
|
||||
},
|
||||
],
|
||||
],
|
||||
description:
|
||||
"Collection of highly customizable and upgradeable smart contracts built with the modular contracts framework.",
|
||||
displayName: "Modular Contracts - Arbitrum Stylus",
|
||||
id: "modular-contracts",
|
||||
name: "modular",
|
||||
showInExplore: true,
|
||||
isBeta: true,
|
||||
} satisfies ExploreCategory;
|
||||
|
||||
const CATEGORIES: Record<string, ExploreCategory> = {
|
||||
[POPULAR.id]: POPULAR,
|
||||
[NFTS.id]: NFTS,
|
||||
@@ -203,6 +254,7 @@ const CATEGORIES: Record<string, ExploreCategory> = {
|
||||
[COMMERCE.id]: COMMERCE,
|
||||
[STAKING.id]: STAKING,
|
||||
[GOVERNANCE.id]: GOVERNANCE,
|
||||
[MODULAR_CONTRACTS_STYLUS.id]: MODULAR_CONTRACTS_STYLUS,
|
||||
};
|
||||
|
||||
export function getCategory(id: string) {
|
||||
|
||||
+1
@@ -58,6 +58,7 @@ const authOptions = [
|
||||
"apple",
|
||||
"coinbase",
|
||||
"line",
|
||||
"epic",
|
||||
] as const satisfies AuthOption[];
|
||||
|
||||
type AuthOptionsFormData = {
|
||||
|
||||
+81
-31
@@ -1,45 +1,95 @@
|
||||
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
|
||||
"use client";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Spinner } from "@workspace/ui/components/spinner";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import type { Project } from "@/api/project/projects";
|
||||
import { UnifiedTransactionsTable } from "../components/transactions-table.client";
|
||||
import { getTransactionAnalyticsSummary } from "../lib/analytics-summary.client";
|
||||
import type { Wallet } from "../server-wallets/wallet-table/types";
|
||||
import { TransactionAnalyticsFilter } from "./filter";
|
||||
import { TransactionsChartCard } from "./tx-chart/tx-chart";
|
||||
import type { SolanaWallet } from "../solana-wallets/wallet-table/types";
|
||||
import { EngineChecklist } from "./ftux.client";
|
||||
import { TransactionAnalyticsSummary } from "./summary";
|
||||
import { TransactionsAnalytics } from "./tx-chart/tx-chart";
|
||||
|
||||
export function TransactionsAnalyticsPageContent(props: {
|
||||
searchParams: {
|
||||
from?: string | undefined | string[];
|
||||
to?: string | undefined | string[];
|
||||
interval?: string | undefined | string[];
|
||||
};
|
||||
project: Project;
|
||||
showAnalytics: boolean;
|
||||
wallets?: Wallet[];
|
||||
wallets: Wallet[];
|
||||
teamSlug: string;
|
||||
client: ThirdwebClient;
|
||||
authToken: string;
|
||||
teamId: string;
|
||||
isManagedVault: boolean;
|
||||
testTxWithWallet: string | undefined;
|
||||
testSolanaTxWithWallet: string | undefined;
|
||||
solanaWallets: SolanaWallet[];
|
||||
}) {
|
||||
return (
|
||||
<ResponsiveSearchParamsProvider value={props.searchParams}>
|
||||
<div className="flex grow flex-col gap-6">
|
||||
{props.showAnalytics && (
|
||||
<>
|
||||
<div className="flex justify-end">
|
||||
<TransactionAnalyticsFilter />
|
||||
</div>
|
||||
<TransactionsChartCard
|
||||
project={props.project}
|
||||
searchParams={props.searchParams}
|
||||
teamSlug={props.teamSlug}
|
||||
wallets={props.wallets ?? []}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<UnifiedTransactionsTable
|
||||
client={props.client}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
/>
|
||||
const engineTxSummaryQuery = useQuery({
|
||||
queryKey: [
|
||||
"engine-tx-analytics-summary",
|
||||
props.teamId,
|
||||
props.project.publishableKey,
|
||||
props.authToken,
|
||||
],
|
||||
queryFn: async () => {
|
||||
const data = await getTransactionAnalyticsSummary({
|
||||
clientId: props.project.publishableKey,
|
||||
teamId: props.teamId,
|
||||
authToken: props.authToken,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
});
|
||||
|
||||
if (engineTxSummaryQuery.isPending) {
|
||||
return (
|
||||
<div className="flex h-[642px] grow items-center justify-center bg-card rounded-xl border">
|
||||
<Spinner className="size-10" />
|
||||
</div>
|
||||
</ResponsiveSearchParamsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const hasTransactions = engineTxSummaryQuery.data
|
||||
? engineTxSummaryQuery.data.totalCount > 0
|
||||
: false;
|
||||
|
||||
return (
|
||||
<div className="flex grow flex-col gap-10">
|
||||
<EngineChecklist
|
||||
isManagedVault={props.isManagedVault}
|
||||
client={props.client}
|
||||
hasTransactions={hasTransactions}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
testTxWithWallet={props.testTxWithWallet}
|
||||
testSolanaTxWithWallet={props.testSolanaTxWithWallet}
|
||||
wallets={props.wallets}
|
||||
solanaWallets={props.solanaWallets}
|
||||
/>
|
||||
|
||||
{props.showAnalytics && hasTransactions && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<TransactionAnalyticsSummary
|
||||
clientId={props.project.publishableKey}
|
||||
teamId={props.project.teamId}
|
||||
initialData={engineTxSummaryQuery.data}
|
||||
/>
|
||||
<TransactionsAnalytics
|
||||
project={props.project}
|
||||
authToken={props.authToken}
|
||||
teamSlug={props.teamSlug}
|
||||
wallets={props.wallets ?? []}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<UnifiedTransactionsTable
|
||||
client={props.client}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
useResponsiveSearchParams,
|
||||
useSetResponsiveSearchParams,
|
||||
} from "responsive-rsc";
|
||||
import { DateRangeSelector } from "@/components/analytics/date-range-selector";
|
||||
import { IntervalSelector } from "@/components/analytics/interval-selector";
|
||||
import { normalizeTimeISOString } from "@/lib/time";
|
||||
import { getTxAnalyticsFiltersFromSearchParams } from "../lib/utils";
|
||||
|
||||
export function TransactionAnalyticsFilter() {
|
||||
const responsiveSearchParams = useResponsiveSearchParams();
|
||||
const setResponsiveSearchParams = useSetResponsiveSearchParams();
|
||||
|
||||
const { range, interval } = getTxAnalyticsFiltersFromSearchParams({
|
||||
from: responsiveSearchParams.from,
|
||||
interval: responsiveSearchParams.interval,
|
||||
to: responsiveSearchParams.to,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="no-scrollbar flex items-center gap-3 max-sm:overflow-auto">
|
||||
<DateRangeSelector
|
||||
popoverAlign="end"
|
||||
range={range}
|
||||
setRange={(newRange) => {
|
||||
setResponsiveSearchParams((v) => {
|
||||
return {
|
||||
...v,
|
||||
from: normalizeTimeISOString(newRange.from),
|
||||
to: normalizeTimeISOString(newRange.to),
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<IntervalSelector
|
||||
intervalType={interval}
|
||||
setIntervalType={(newInterval) => {
|
||||
setResponsiveSearchParams((v) => {
|
||||
return {
|
||||
...v,
|
||||
interval: newInterval,
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { ActivityIcon, CoinsIcon } from "lucide-react";
|
||||
import { toEther } from "thirdweb/utils";
|
||||
import { StatCard } from "@/components/analytics/stat"; // Assuming correct path
|
||||
import type { TransactionSummaryData } from "../lib/analytics";
|
||||
import type { TransactionSummaryData } from "../lib/analytics-summary.client";
|
||||
|
||||
// Renders the UI based on fetched data or pending state
|
||||
function TransactionAnalyticsSummaryUI(props: {
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
import { NEXT_PUBLIC_ENGINE_CLOUD_URL } from "@/constants/public-envs";
|
||||
import type { TransactionStats } from "@/types/analytics";
|
||||
|
||||
export async function getTransactionsChartData({
|
||||
teamId,
|
||||
clientId,
|
||||
from,
|
||||
to,
|
||||
interval,
|
||||
authToken,
|
||||
}: {
|
||||
teamId: string;
|
||||
clientId: string;
|
||||
from: string;
|
||||
to: string;
|
||||
interval: "day" | "week";
|
||||
authToken: string;
|
||||
}): Promise<TransactionStats[]> {
|
||||
const filters = {
|
||||
endDate: to,
|
||||
resolution: interval,
|
||||
startDate: from,
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
`${NEXT_PUBLIC_ENGINE_CLOUD_URL}/v1/transactions/analytics`,
|
||||
{
|
||||
body: JSON.stringify(filters),
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": clientId,
|
||||
"x-team-id": teamId,
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 || response.status === 400) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// TODO - need to handle this error state, like we do with the connect charts
|
||||
throw new Error(
|
||||
`Error fetching transactions chart data: ${response.status} ${
|
||||
response.statusText
|
||||
} - ${await response.text().catch(() => "Unknown error")}`,
|
||||
);
|
||||
}
|
||||
|
||||
type TransactionsChartResponse = {
|
||||
result: {
|
||||
analytics: Array<{
|
||||
timeBucket: string;
|
||||
chainId: string;
|
||||
count: number;
|
||||
}>;
|
||||
metadata: {
|
||||
resolution: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const data = (await response.json()) as TransactionsChartResponse;
|
||||
|
||||
return data.result.analytics.map((stat) => ({
|
||||
chainId: Number(stat.chainId),
|
||||
count: stat.count,
|
||||
date: stat.timeBucket,
|
||||
}));
|
||||
}
|
||||
+3
-9
@@ -108,10 +108,10 @@ export function TransactionsChartCardUI(props: {
|
||||
customHeader={
|
||||
<div className="relative px-6 pt-6">
|
||||
<h3 className="mb-0.5 font-semibold text-xl tracking-tight">
|
||||
Daily Transactions
|
||||
Transactions
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Amount of daily transactions by chain.
|
||||
transactions broken down by chain
|
||||
</p>
|
||||
|
||||
<div className="top-6 right-6 mb-8 grid grid-cols-2 items-center gap-2 md:absolute md:mb-0 md:flex">
|
||||
@@ -194,13 +194,7 @@ function EmptyChartContent(props: {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="flex items-center gap-2 rounded-full border bg-background px-3.5 py-1.5 text-sm">
|
||||
<span className="!pointer-events-auto relative flex size-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75" />
|
||||
<span className="relative inline-flex size-2 rounded-full bg-primary" />
|
||||
</span>
|
||||
Waiting for transactions...
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">No transactions found</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
+48
-53
@@ -1,73 +1,68 @@
|
||||
import { ResponsiveSuspense } from "responsive-rsc";
|
||||
"use client";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import type { Project } from "@/api/project/projects";
|
||||
import { getTransactionsChart } from "../../lib/analytics";
|
||||
import { getTxAnalyticsFiltersFromSearchParams } from "../../lib/utils";
|
||||
import {
|
||||
DateRangeSelector,
|
||||
getLastNDaysRange,
|
||||
} from "@/components/analytics/date-range-selector";
|
||||
import { IntervalSelector } from "@/components/analytics/interval-selector";
|
||||
import { normalizeTimeISOString } from "@/lib/time";
|
||||
import type { Wallet } from "../../server-wallets/wallet-table/types";
|
||||
import { getTransactionsChartData } from "./data";
|
||||
import { TransactionsChartCardUI } from "./tx-chart-ui";
|
||||
|
||||
async function AsyncTransactionsChartCard(props: {
|
||||
from: string;
|
||||
to: string;
|
||||
interval: "day" | "week";
|
||||
export function TransactionsAnalytics(props: {
|
||||
project: Project;
|
||||
wallets: Wallet[];
|
||||
authToken: string;
|
||||
teamSlug: string;
|
||||
}) {
|
||||
const data = await getTransactionsChart({
|
||||
const [range, setRange] = useState(() => getLastNDaysRange("last-30"));
|
||||
const [interval, setInterval] = useState<"day" | "week">("day");
|
||||
|
||||
const params = {
|
||||
clientId: props.project.publishableKey,
|
||||
from: props.from,
|
||||
interval: props.interval,
|
||||
from: normalizeTimeISOString(range.from),
|
||||
interval: interval,
|
||||
teamId: props.project.teamId,
|
||||
to: props.to,
|
||||
to: normalizeTimeISOString(range.to),
|
||||
authToken: props.authToken,
|
||||
};
|
||||
|
||||
const engineTxAnalytics = useQuery({
|
||||
queryKey: ["engine-tx-analytics", params],
|
||||
queryFn: async () => {
|
||||
const data = await getTransactionsChartData(params);
|
||||
return data;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<TransactionsChartCardUI
|
||||
isPending={false}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
userOpStats={data}
|
||||
wallets={props.wallets}
|
||||
/>
|
||||
);
|
||||
}
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-end">
|
||||
<div className="no-scrollbar flex items-center gap-3 max-sm:overflow-auto">
|
||||
<DateRangeSelector
|
||||
popoverAlign="end"
|
||||
range={range}
|
||||
setRange={setRange}
|
||||
/>
|
||||
|
||||
export function TransactionsChartCard(props: {
|
||||
searchParams: {
|
||||
from?: string | undefined | string[];
|
||||
to?: string | undefined | string[];
|
||||
interval?: string | undefined | string[];
|
||||
};
|
||||
project: Project;
|
||||
wallets: Wallet[];
|
||||
teamSlug: string;
|
||||
}) {
|
||||
const { range, interval } = getTxAnalyticsFiltersFromSearchParams(
|
||||
props.searchParams,
|
||||
);
|
||||
|
||||
return (
|
||||
<ResponsiveSuspense
|
||||
// TODO - change this if this component does not end up using these params
|
||||
fallback={
|
||||
<TransactionsChartCardUI
|
||||
isPending={true}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
userOpStats={[]}
|
||||
wallets={[]}
|
||||
/>
|
||||
}
|
||||
searchParamsUsed={["from", "to", "interval"]}
|
||||
>
|
||||
<AsyncTransactionsChartCard
|
||||
from={range.from.toISOString()}
|
||||
interval={interval}
|
||||
<IntervalSelector
|
||||
intervalType={interval}
|
||||
setIntervalType={setInterval}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TransactionsChartCardUI
|
||||
isPending={engineTxAnalytics.isPending}
|
||||
project={props.project}
|
||||
teamSlug={props.teamSlug}
|
||||
to={range.to.toISOString()}
|
||||
userOpStats={engineTxAnalytics.data ?? []}
|
||||
wallets={props.wallets}
|
||||
/>
|
||||
</ResponsiveSuspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import { NEXT_PUBLIC_ENGINE_CLOUD_URL } from "@/constants/public-envs";
|
||||
|
||||
export type TransactionSummaryData = {
|
||||
totalCount: number;
|
||||
totalGasCostWei: string;
|
||||
totalGasUnitsUsed: string; // Keep fetched data structure
|
||||
};
|
||||
|
||||
type AnalyticsSummaryApiResponse = {
|
||||
result: {
|
||||
summary: {
|
||||
totalCount: number;
|
||||
totalGasCostWei: string;
|
||||
totalGasUnitsUsed: string;
|
||||
};
|
||||
metadata: {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Fetches data from the /analytics-summary endpoint
|
||||
export async function getTransactionAnalyticsSummary(props: {
|
||||
teamId: string;
|
||||
clientId: string;
|
||||
authToken: string;
|
||||
}): Promise<TransactionSummaryData> {
|
||||
const body = {};
|
||||
const defaultData: TransactionSummaryData = {
|
||||
totalCount: 0,
|
||||
totalGasCostWei: "0",
|
||||
totalGasUnitsUsed: "0",
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${NEXT_PUBLIC_ENGINE_CLOUD_URL}/v1/transactions/analytics-summary`,
|
||||
{
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
Authorization: `Bearer ${props.authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 || response.status === 400) {
|
||||
console.error("Unauthorized fetching transaction summary");
|
||||
return defaultData;
|
||||
}
|
||||
const errorText = await response.text();
|
||||
throw new Error(
|
||||
`Error fetching transaction summary: ${response.status} ${response.statusText} - ${errorText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = (await response.json()) as AnalyticsSummaryApiResponse;
|
||||
|
||||
return {
|
||||
totalCount: data.result.summary.totalCount ?? 0,
|
||||
totalGasCostWei: data.result.summary.totalGasCostWei ?? "0",
|
||||
totalGasUnitsUsed: data.result.summary.totalGasUnitsUsed ?? "0",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch transaction summary:", error);
|
||||
return defaultData;
|
||||
}
|
||||
}
|
||||
-147
@@ -1,157 +1,10 @@
|
||||
import { getAuthToken } from "@/api/auth-token";
|
||||
import { NEXT_PUBLIC_ENGINE_CLOUD_URL } from "@/constants/public-envs";
|
||||
import type { TransactionStats } from "@/types/analytics";
|
||||
import type {
|
||||
Transaction,
|
||||
TransactionsResponse,
|
||||
} from "../analytics/tx-table/types";
|
||||
|
||||
// Define the structure of the data we expect back from our fetch function
|
||||
export type TransactionSummaryData = {
|
||||
totalCount: number;
|
||||
totalGasCostWei: string;
|
||||
totalGasUnitsUsed: string; // Keep fetched data structure
|
||||
};
|
||||
|
||||
// Define the structure of the API response
|
||||
type AnalyticsSummaryApiResponse = {
|
||||
result: {
|
||||
summary: {
|
||||
totalCount: number;
|
||||
totalGasCostWei: string;
|
||||
totalGasUnitsUsed: string;
|
||||
};
|
||||
metadata: {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Fetches data from the /analytics-summary endpoint
|
||||
export async function getTransactionAnalyticsSummary(props: {
|
||||
teamId: string;
|
||||
clientId: string;
|
||||
}): Promise<TransactionSummaryData> {
|
||||
const authToken = await getAuthToken();
|
||||
const body = {};
|
||||
const defaultData: TransactionSummaryData = {
|
||||
totalCount: 0,
|
||||
totalGasCostWei: "0",
|
||||
totalGasUnitsUsed: "0",
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${NEXT_PUBLIC_ENGINE_CLOUD_URL}/v1/transactions/analytics-summary`,
|
||||
{
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 || response.status === 400) {
|
||||
console.error("Unauthorized fetching transaction summary");
|
||||
return defaultData;
|
||||
}
|
||||
const errorText = await response.text();
|
||||
throw new Error(
|
||||
`Error fetching transaction summary: ${response.status} ${response.statusText} - ${errorText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = (await response.json()) as AnalyticsSummaryApiResponse;
|
||||
|
||||
return {
|
||||
totalCount: data.result.summary.totalCount ?? 0,
|
||||
totalGasCostWei: data.result.summary.totalGasCostWei ?? "0",
|
||||
totalGasUnitsUsed: data.result.summary.totalGasUnitsUsed ?? "0",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch transaction summary:", error);
|
||||
return defaultData;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTransactionsChart({
|
||||
teamId,
|
||||
clientId,
|
||||
from,
|
||||
to,
|
||||
interval,
|
||||
}: {
|
||||
teamId: string;
|
||||
clientId: string;
|
||||
from: string;
|
||||
to: string;
|
||||
interval: "day" | "week";
|
||||
}): Promise<TransactionStats[]> {
|
||||
const authToken = await getAuthToken();
|
||||
|
||||
const filters = {
|
||||
endDate: to,
|
||||
resolution: interval,
|
||||
startDate: from,
|
||||
};
|
||||
|
||||
const response = await fetch(
|
||||
`${NEXT_PUBLIC_ENGINE_CLOUD_URL}/v1/transactions/analytics`,
|
||||
{
|
||||
body: JSON.stringify(filters),
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": clientId,
|
||||
"x-team-id": teamId,
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 || response.status === 400) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// TODO - need to handle this error state, like we do with the connect charts
|
||||
throw new Error(
|
||||
`Error fetching transactions chart data: ${response.status} ${
|
||||
response.statusText
|
||||
} - ${await response.text().catch(() => "Unknown error")}`,
|
||||
);
|
||||
}
|
||||
|
||||
type TransactionsChartResponse = {
|
||||
result: {
|
||||
analytics: Array<{
|
||||
timeBucket: string;
|
||||
chainId: string;
|
||||
count: number;
|
||||
}>;
|
||||
metadata: {
|
||||
resolution: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const data = (await response.json()) as TransactionsChartResponse;
|
||||
|
||||
return data.result.analytics.map((stat) => ({
|
||||
chainId: Number(stat.chainId),
|
||||
count: stat.count,
|
||||
date: stat.timeBucket,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getSingleTransaction({
|
||||
teamId,
|
||||
clientId,
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
import { getFiltersFromSearchParams } from "@/lib/time";
|
||||
|
||||
export function getTxAnalyticsFiltersFromSearchParams(params: {
|
||||
from?: string | undefined | string[];
|
||||
to?: string | undefined | string[];
|
||||
interval?: string | undefined | string[];
|
||||
}) {
|
||||
return getFiltersFromSearchParams({
|
||||
defaultRange: "last-30",
|
||||
from: params.from,
|
||||
interval: params.interval,
|
||||
to: params.to,
|
||||
});
|
||||
}
|
||||
+15
-35
@@ -7,10 +7,7 @@ import { ProjectPage } from "@/components/blocks/project-page/project-page";
|
||||
import { NEXT_PUBLIC_THIRDWEB_VAULT_URL } from "@/constants/public-envs";
|
||||
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
|
||||
import { TransactionsAnalyticsPageContent } from "./analytics/analytics-page";
|
||||
import { EngineChecklist } from "./analytics/ftux.client";
|
||||
import { TransactionAnalyticsSummary } from "./analytics/summary";
|
||||
import { ServerWalletsTable } from "./components/server-wallets-table.client";
|
||||
import { getTransactionAnalyticsSummary } from "./lib/analytics";
|
||||
import type { Wallet } from "./server-wallets/wallet-table/types";
|
||||
import { listSolanaAccounts } from "./solana-wallets/lib/vault.client";
|
||||
import type { SolanaWallet } from "./solana-wallets/wallet-table/types";
|
||||
@@ -110,12 +107,6 @@ export default async function TransactionsAnalyticsPage(props: {
|
||||
const isSolanaPermissionError =
|
||||
solanaAccounts.error?.message.includes("AUTH_INSUFFICIENT_SCOPE") ?? false;
|
||||
|
||||
const initialData = await getTransactionAnalyticsSummary({
|
||||
clientId: project.publishableKey,
|
||||
teamId: project.teamId,
|
||||
}).catch(() => undefined);
|
||||
const hasTransactions = initialData ? initialData.totalCount > 0 : false;
|
||||
|
||||
const client = getClientThirdwebClient({
|
||||
jwt: authToken,
|
||||
teamId: project.teamId,
|
||||
@@ -152,41 +143,30 @@ export default async function TransactionsAnalyticsPage(props: {
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-10">
|
||||
<EngineChecklist
|
||||
isManagedVault={isManagedVault}
|
||||
client={client}
|
||||
hasTransactions={hasTransactions}
|
||||
project={project}
|
||||
teamSlug={params.team_slug}
|
||||
testTxWithWallet={searchParams.testTxWithWallet as string | undefined}
|
||||
testSolanaTxWithWallet={
|
||||
searchParams.testSolanaTxWithWallet as string | undefined
|
||||
}
|
||||
wallets={wallets ?? []}
|
||||
solanaWallets={solanaAccounts.data.items}
|
||||
/>
|
||||
{hasTransactions &&
|
||||
!searchParams.testTxWithWallet &&
|
||||
!searchParams.testSolanaTxWithWallet && (
|
||||
<TransactionAnalyticsSummary
|
||||
clientId={project.publishableKey}
|
||||
initialData={initialData}
|
||||
teamId={project.teamId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* transactions */}
|
||||
<TransactionsAnalyticsPageContent
|
||||
client={client}
|
||||
authToken={authToken}
|
||||
project={project}
|
||||
searchParams={searchParams}
|
||||
showAnalytics={
|
||||
hasTransactions &&
|
||||
!searchParams.testTxWithWallet &&
|
||||
!searchParams.testSolanaTxWithWallet
|
||||
}
|
||||
teamSlug={params.team_slug}
|
||||
wallets={wallets}
|
||||
wallets={wallets ?? []}
|
||||
solanaWallets={solanaAccounts.data.items}
|
||||
teamId={project.teamId}
|
||||
isManagedVault={isManagedVault}
|
||||
testTxWithWallet={
|
||||
typeof searchParams.testTxWithWallet === "string"
|
||||
? searchParams.testTxWithWallet
|
||||
: undefined
|
||||
}
|
||||
testSolanaTxWithWallet={
|
||||
typeof searchParams.testSolanaTxWithWallet === "string"
|
||||
? searchParams.testSolanaTxWithWallet
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Server Wallets (EVM + Solana) */}
|
||||
|
||||
+1
@@ -87,6 +87,7 @@ const authMethodsToPickFrom: InAppWalletAuth[] = [
|
||||
"line",
|
||||
"x",
|
||||
"tiktok",
|
||||
"epic",
|
||||
"coinbase",
|
||||
"farcaster",
|
||||
"telegram",
|
||||
|
||||
@@ -43,7 +43,6 @@ export function X402RightSection(props: { options: X402PlaygroundOptions }) {
|
||||
fetch,
|
||||
THIRDWEB_CLIENT,
|
||||
activeWallet,
|
||||
BigInt(1 * 10 ** 18),
|
||||
);
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("chainId", props.options.chain.id.toString());
|
||||
|
||||
@@ -20,6 +20,7 @@ const allInAppWalletLoginMethods: InAppWalletAuth[] = [
|
||||
"line",
|
||||
"x",
|
||||
"tiktok",
|
||||
"epic",
|
||||
"facebook",
|
||||
"apple",
|
||||
"coinbase",
|
||||
|
||||
@@ -742,6 +742,14 @@ const paymentRedirects = {
|
||||
"/payments/swap": "/bridge/swap",
|
||||
"/payments/tokens": "/bridge/tokens",
|
||||
"/payments/routes": "/bridge/routes",
|
||||
"/payments/x402": "/x402",
|
||||
"/payments/x402/:path*": "/x402/:path*",
|
||||
"/payments/fund": "/bridge/fund",
|
||||
"/payments/products": "/bridge/products",
|
||||
"/payments/transactions": "/bridge/transactions",
|
||||
"/payments/send": "/bridge/send",
|
||||
"/payments/webhooks": "/bridge/webhooks",
|
||||
"/payments/custom-data": "/bridge/custom-data",
|
||||
};
|
||||
|
||||
const contractRedirects = {
|
||||
|
||||
@@ -40,8 +40,8 @@ const links = [
|
||||
name: "Contracts",
|
||||
},
|
||||
{
|
||||
href: "/payments",
|
||||
name: "Payments",
|
||||
href: "/x402",
|
||||
name: "x402",
|
||||
},
|
||||
{
|
||||
href: "/bridge",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 312 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 310 KiB |
+2
-2
@@ -188,8 +188,8 @@ Any purchase data you provide to a payment will be included on all webhooks, sta
|
||||
|
||||
To connect with other auth strategies, use external wallets, or sponsor gas for users, check out the following guides:
|
||||
|
||||
- [Send a Payment](/payments/send)
|
||||
- [Webhooks](/payments/webhooks)
|
||||
- [Send a Payment](/bridge/send)
|
||||
- [Webhooks](/bridge/webhooks)
|
||||
|
||||
## Explore Full API References
|
||||
|
||||
+2
-2
@@ -74,8 +74,8 @@ The BuyWidget handles the complete payment flow, supporting both crypto and fiat
|
||||
|
||||
## Going Further
|
||||
|
||||
- [Custom Payment Data](/payments/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/payments/webhooks) - Get notified when payments complete
|
||||
- [Custom Payment Data](/bridge/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/bridge/webhooks) - Get notified when payments complete
|
||||
|
||||
## API Reference
|
||||
|
||||
@@ -97,7 +97,7 @@ const preparedQuote = await Bridge.Buy.prepare({
|
||||
|
||||
The prepared quote will contain details about the payment, including the transactions needed to execute it.
|
||||
|
||||
You can execute the included transactions using the wallet of your choice. To learn how to send it using thirdweb Wallets, checkout the [Send a Payment](/payments/send) guide.
|
||||
You can execute the included transactions using the wallet of your choice. To learn how to send it using thirdweb Wallets, checkout the [Send a Payment](/bridge/send) guide.
|
||||
|
||||
</TabsContent>
|
||||
|
||||
|
||||
+2
-2
@@ -77,8 +77,8 @@ The CheckoutWidget handles the complete payment flow, supporting both crypto and
|
||||
|
||||
## Going Further
|
||||
|
||||
- [Custom Payment Data](/payments/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/payments/webhooks) - Get notified when payments complete
|
||||
- [Custom Payment Data](/bridge/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/bridge/webhooks) - Get notified when payments complete
|
||||
|
||||
## API Reference
|
||||
|
||||
@@ -52,14 +52,14 @@ client,
|
||||
});
|
||||
```
|
||||
|
||||
You can execute this quote exactly like a buy. See the [Send a Payment](/payments/send) guide for more details on this process.
|
||||
You can execute this quote exactly like a buy. See the [Send a Payment](/bridge/send) guide for more details on this process.
|
||||
|
||||
|
||||
## 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)
|
||||
- [Send a Payment](/bridge/send)
|
||||
|
||||
## Explore Full API References
|
||||
|
||||
|
||||
+2
-2
@@ -262,8 +262,8 @@ Once this code completes, your payment has been fully executed. Payments normall
|
||||
|
||||
## Going further
|
||||
|
||||
- [Sell a Product](/payments/products)
|
||||
- [Token Prices](/payments/tokens)
|
||||
- [Sell a Product](/bridge/products)
|
||||
- [Token Prices](/bridge/tokens)
|
||||
|
||||
## Explore API References
|
||||
|
||||
@@ -16,21 +16,45 @@ export const sidebar: SideBar = {
|
||||
{
|
||||
isCollapsible: false,
|
||||
links: [
|
||||
{
|
||||
href: `${bridgeSlug}/sell`,
|
||||
name: "Sell Tokens",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/swap`,
|
||||
name: "Swap Tokens",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/bridge-widget-script`,
|
||||
name: "BridgeWidget Script",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/fund`,
|
||||
name: "Fund Wallets",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/products`,
|
||||
name: "Sell a Product",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/transactions`,
|
||||
name: "Pay for Transactions",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/send`,
|
||||
name: "Send a Payment",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/sell`,
|
||||
name: "Sell Tokens",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/tokens`,
|
||||
name: "Get Token Prices",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/bridge-widget-script`,
|
||||
name: "BridgeWidget Script",
|
||||
href: `${bridgeSlug}/webhooks`,
|
||||
name: "Webhooks",
|
||||
},
|
||||
{
|
||||
href: `${bridgeSlug}/custom-data`,
|
||||
name: "Custom Data",
|
||||
},
|
||||
],
|
||||
name: "Guides",
|
||||
|
||||
@@ -317,9 +317,9 @@ async function swapEthToMatic(amount: string, userAccount: Account) {
|
||||
|
||||
## Going Further
|
||||
|
||||
- [Send a Payment](/payments/send) - Learn about peer-to-peer transfers
|
||||
- [Send a Payment](/bridge/send) - Learn about peer-to-peer transfers
|
||||
- [Token Prices](/bridge/tokens) - Access real-time token pricing
|
||||
- [Webhooks](/payments/webhooks) - Get notified when swaps complete
|
||||
- [Webhooks](/bridge/webhooks) - Get notified when swaps complete
|
||||
|
||||
## API Reference
|
||||
|
||||
|
||||
@@ -59,7 +59,17 @@ This returns token information with current price:
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
iconUri: "https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
|
||||
priceUsd: 1.00
|
||||
prices: {
|
||||
USD: 1.00,
|
||||
EUR: 0.86,
|
||||
GBP: 0.74,
|
||||
JPY: 148.85,
|
||||
KRW: 1393.14,
|
||||
CNY: 7.18,
|
||||
INR: 86.20,
|
||||
NOK: 10.18,
|
||||
SEK: 9.68,
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -118,7 +128,7 @@ 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)
|
||||
- [Send a Payment](/bridge/send)
|
||||
|
||||
## Explore Full API References
|
||||
|
||||
|
||||
+2
-2
@@ -86,8 +86,8 @@ The TransactionWidget handles the complete payment flow, supporting both crypto
|
||||
|
||||
## Going Further
|
||||
|
||||
- [Custom Payment Data](/payments/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/payments/webhooks) - Get notified when payments complete
|
||||
- [Custom Payment Data](/bridge/custom-data) - Attach metadata to payments
|
||||
- [Webhooks](/bridge/webhooks) - Get notified when payments complete
|
||||
|
||||
## API Reference
|
||||
|
||||
+2
-2
@@ -221,8 +221,8 @@ 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)
|
||||
- [Get Token Prices](/payments/tokens)
|
||||
- [Send a Payment](/bridge/send)
|
||||
- [Get Token Prices](/bridge/tokens)
|
||||
|
||||
## Explore Full API References
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import { Step, Steps, createMetadata } from "@doc";
|
||||
|
||||
export const metadata = createMetadata({
|
||||
title: "Stylus Minting Modules | thirdweb Documentation",
|
||||
description:
|
||||
"Customizable rust modules written for thirdweb's modular contracts",
|
||||
image: {
|
||||
title: "Stylus Minting Modules",
|
||||
icon: "contracts",
|
||||
},
|
||||
});
|
||||
|
||||
# Stylus Minting Modules
|
||||
|
||||
You can now install rust based modules into a solidity based [modular contract](https://github.com/thirdweb-dev/modular-contracts/blob/dev/design-document.md).
|
||||
|
||||
This plug-n-play model also allows users to write their own custom minting logic in rust, taking advantage of Stylus' interoperability and gas savings. These modules work with existing router contracts and modules written in solidity.
|
||||
|
||||
## Deploy through dashboard
|
||||
|
||||
Deploying a Modular Contract and installing modules is easy through the thirdweb dashboard and ideal when you don't want to modify any code on the contract.
|
||||
|
||||
<Steps>
|
||||
<Step title="Select Modular Contract for Stylus">
|
||||
|
||||
Navigate to the Modular Contracts section on Explore and select any token contract for your project.
|
||||
</Step>
|
||||
|
||||
<Step title="Deploy Contract">
|
||||
Select Arbitrum Sepolia or any other Stylus-supported network, then select Deploy. Once you have deployed one of these contracts, the modules come pre-installed, both solidity and rust ones.
|
||||
</Step>
|
||||
|
||||
<Step title="Edit Modules">
|
||||
If you wish to install a custom module, navigate to modules tab on your contract page. Enter the publisher address and select module from the dropdown, and click install.
|
||||
|
||||
You can uninstall a module from the same page.
|
||||
|
||||
Here's a list of prebuilt Rust / Stylus specific modules provided by thirdweb:
|
||||
- [Mintable ERC20](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusMintableERC20)
|
||||
- [Transferable ERC20](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusTransferableERC20)
|
||||
- [Mintable ERC721](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusMintableERC721)
|
||||
- [Transferable ERC721](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusTransferableERC721)
|
||||
- [Mintable ERC1155](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusMintableERC1155)
|
||||
- [Transferable ERC1155](https://thirdweb.com/0x6453a486d52e0EB6E79Ec4491038E2522a926936/StylusTransferableERC1155)
|
||||
|
||||
You can customize these modules via thirdweb CLI as described below.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Build with CLI
|
||||
|
||||
If you want to modify the contract code or publish a custom module, you can use the thirdweb CLI.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a new Stylus module">
|
||||
|
||||
In your CLI, run the following command to create a new directory with an template module. Select the module template from the list shown.
|
||||
|
||||
```bash
|
||||
npx thirdweb create-stylus
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Modify logic">
|
||||
In the `src/lib.rs` file you can modify the contract logic such as adding fees, gating logic, analytics events, and more.
|
||||
</Step>
|
||||
|
||||
<Step title="Build & Test">
|
||||
|
||||
To build your project, run the following command:
|
||||
|
||||
```bash
|
||||
cargo stylus build
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Deploy or Publish Your Module">
|
||||
You can publish your module and install it to your modular contract. Publishing stores your module metadata in thirdweb’s on-chain registry so anyone (including you) can use that exact version later with a few clicks.
|
||||
|
||||
To publish your module, ensure you have your thirdweb secret key from your created project, then run the following command:
|
||||
|
||||
```bash
|
||||
npx thirdweb publish-stylus -k YOUR_TW_SECRET_KEY
|
||||
```
|
||||
|
||||
Once published, the module is now available to install via thirdweb dashboard from the modules tab of your contract.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Interacting with the Contract
|
||||
|
||||
Using the thirdweb SDKs, you can interact with your Stylus Modular contracts / modules to mint tokens, transfer ownership, and more.
|
||||
|
||||
The following describes how to interact with a ERC721 modular contract using thirdweb SDK. (Swap for ERC20 or ERC1155 variants as needed.)
|
||||
|
||||
### Upload metadata
|
||||
|
||||
```ts
|
||||
import { BatchMetadataERC721 } from "thirdweb/modules";
|
||||
|
||||
const transaction = BatchMetadataERC721.uploadMetadata({
|
||||
contract,
|
||||
metadatas: [
|
||||
{ name: "My NFT", description: "This is my NFT" },
|
||||
],
|
||||
});
|
||||
|
||||
await sendTransaction({
|
||||
transaction,
|
||||
account,
|
||||
});
|
||||
```
|
||||
|
||||
### Mint with role
|
||||
|
||||
```typescript
|
||||
import { MintableERC721 } from "thirdweb/modules";
|
||||
|
||||
const transaction = MintableERC721.mintWithRole({
|
||||
contract,
|
||||
to: "0x...", // Address to mint tokens to
|
||||
nfts: [
|
||||
{
|
||||
name: "My NFT",
|
||||
description: "This is my NFT",
|
||||
image: "ipfs://...",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Send the transaction
|
||||
await sendTransaction({ transaction, account });
|
||||
```
|
||||
|
||||
### Set transfer rules
|
||||
|
||||
```ts
|
||||
import { sendTransaction } from "thirdweb";
|
||||
import { TransferableERC20 } from "thirdweb/modules";
|
||||
|
||||
const transaction = TransferableERC20.setTransferable({
|
||||
contract,
|
||||
enableTransfer: ...,
|
||||
overrides: {
|
||||
...
|
||||
}
|
||||
});
|
||||
|
||||
// Send the transaction
|
||||
await sendTransaction({ transaction, account });
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- [Modular Contracts Design Doc](https://github.com/thirdweb-dev/modular-contracts/blob/dev/design-document.md)
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
import { Step, Steps, createMetadata } from "@doc";
|
||||
|
||||
export const metadata = createMetadata({
|
||||
title: "Stylus Zk Mint Contract | thirdweb Documentation",
|
||||
description:
|
||||
"Stylus Zk Minting contract and app template",
|
||||
image: {
|
||||
title: "Stylus Zk Mint",
|
||||
icon: "contracts",
|
||||
},
|
||||
});
|
||||
|
||||
# ZK Minting with Stylus
|
||||
|
||||
You can now create and deploy ZK Proof based token contracts with Stylus. The templates provide you with Stylus contracts, ZK circuit files, and a ready-to-deploy Next.js app + API for proof generation and minting.
|
||||
|
||||
Follow the steps below to learn how to build a privacy-preserving ERC721 token system using Zero-Knowledge proofs on Arbitrum Stylus. Users can mint tokens by proving they own a minimum amount of ETH without revealing their exact balance. However, you can customize the circuits and the contracts to add your own checks for minting.
|
||||
|
||||
## What you'll build
|
||||
|
||||
- ZK Circuit: Proves token ownership without revealing exact balances
|
||||
- Stylus Contract: Rust-based ERC721 contract that verifies ZK proofs
|
||||
- Frontend: Next.js app for generating proofs and minting tokens
|
||||
- Oracle System: Secure balance verification mechanism
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Create a project on your thirdweb account](https://thirdweb.com/dashboard)
|
||||
- Install thirdweb CLI by running `npm install -g thirdweb`
|
||||
- Install Rust tool chain by running `curl https://sh.rustup.rs -sSf | sh` or visit rust-lang.org
|
||||
- Install solc by running `npm install -g solc` or visit soliditylang.org
|
||||
- Install Node version 18 or higher
|
||||
|
||||
<Steps>
|
||||
<Step title="Project Setup">
|
||||
|
||||
In your CLI, run the following command to create a new directory with an template module. Select the module template from the list shown.
|
||||
|
||||
```bash
|
||||
npx thirdweb create-stylus
|
||||
```
|
||||
|
||||
Select **"Stylus ZK ERC721"** from the dropdown menu. This will:
|
||||
- Clone the repository to your machine
|
||||
- Set up the project structure
|
||||
- Install basic dependencies
|
||||
</Step>
|
||||
|
||||
<Step title="Install Dependencies">
|
||||
Install dependencies for all components:
|
||||
|
||||
```bash
|
||||
# Install root dependencies
|
||||
pnpm install
|
||||
|
||||
# Install circuit dependencies
|
||||
cd circuits
|
||||
pnpm install
|
||||
cd ..
|
||||
|
||||
# Install frontend dependencies
|
||||
cd app
|
||||
pnpm install
|
||||
cd ..
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Generate Cryptographic Keys">
|
||||
Run the setup script to generate oracle keys and build the ZK circuit:
|
||||
|
||||
```bash
|
||||
chmod +x setup.sh
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
This script will:
|
||||
- Generate a random oracle secret key
|
||||
- Inject the secret into the ZK circuit
|
||||
- Compile the circuit with circom
|
||||
- Generate proving and verification keys
|
||||
- Create the trusted setup for Groth16
|
||||
|
||||
**⚠️ Important**: The oracle secret is critical for security. Keep it private!
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Deploy the Contract">
|
||||
### Using thirdweb CLI
|
||||
|
||||
|
||||
```bash
|
||||
cd contracts
|
||||
npx thirdweb@latest deploy-stylus -k <THIRDWEB_SECRET_KEY>
|
||||
```
|
||||
|
||||
### Using Stylus CLI (Alternative)
|
||||
|
||||
```bash
|
||||
cd contracts
|
||||
cargo stylus deploy --endpoint arbitrum-sepolia
|
||||
```
|
||||
|
||||
Copy the deployed contract address - you'll need it for the frontend.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure the Frontend">
|
||||
Update the contract address in your frontend:
|
||||
|
||||
```bash
|
||||
cd app
|
||||
# Edit pages/index.tsx or lib/config.ts
|
||||
# Update ZK_MINT_CONTRACT_ADDRESS with your deployed address
|
||||
```
|
||||
|
||||
Create environment file:
|
||||
|
||||
```bash
|
||||
# app/.env.local
|
||||
RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
|
||||
ORACLE_SECRET_KEY=<your_oracle_secret_from_setup>
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Run the Application">
|
||||
```bash
|
||||
cd app
|
||||
pnpm dev
|
||||
# Visit http://localhost:3000
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Test the System">
|
||||
1. **Connect Wallet**: Connect to Arbitrum Sepolia testnet
|
||||
2. **Generate Proof**: Click "Generate ZK Proof" - this proves you have sufficient balance
|
||||
3. **Mint Tokens**: Use the proof to mint ERC721 tokens
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Customizing the ZK Logic
|
||||
|
||||
The above described logic for ZK verification of user balance is provided as a template. You can modify the logic and write your own custom ZK verification code and circuits as described below.
|
||||
|
||||
### Understanding the Circuit
|
||||
|
||||
The core circuit (`circuits/token_ownership.circom`) has these components:
|
||||
|
||||
```txt
|
||||
template TokenOwnership(oracle_secret) {
|
||||
// Private inputs (hidden from public)
|
||||
signal input actual_balance; // Real balance from oracle
|
||||
signal input salt; // Randomness for uniqueness
|
||||
|
||||
// Public inputs (visible on-chain)
|
||||
signal input min_required_balance; // Minimum threshold
|
||||
signal input token_contract_hash; // Which token to check
|
||||
signal input user_address_hash; // User's address hash
|
||||
signal input timestamp; // When oracle signed data
|
||||
signal input oracle_commitment; // Oracle's commitment
|
||||
|
||||
// Output
|
||||
signal output nullifier; // Prevents replay attacks
|
||||
}
|
||||
```
|
||||
|
||||
### Customization Examples
|
||||
|
||||
#### 1. Change Balance Threshold Logic
|
||||
|
||||
Replace the balance check with custom logic:
|
||||
|
||||
```txt
|
||||
// Original: Simple greater-than check
|
||||
component gte = GreaterEqThan(64);
|
||||
gte.in[0] <== actual_balance;
|
||||
gte.in[1] <== min_required_balance;
|
||||
gte.out === 1;
|
||||
|
||||
// Custom: Logarithmic scaling
|
||||
component log_check = LogarithmicCheck();
|
||||
log_check.balance <== actual_balance;
|
||||
log_check.threshold <== min_required_balance;
|
||||
log_check.out === 1;
|
||||
```
|
||||
|
||||
#### 2. Add Multiple Token Support
|
||||
|
||||
Extend the circuit to verify multiple token balances:
|
||||
|
||||
```txt
|
||||
template MultiTokenOwnership(oracle_secret, num_tokens) {
|
||||
signal input actual_balances[num_tokens];
|
||||
signal input min_required_balances[num_tokens];
|
||||
signal input token_addresses[num_tokens];
|
||||
|
||||
// Verify each token separately
|
||||
component checks[num_tokens];
|
||||
for (var i = 0; i < num_tokens; i++) {
|
||||
checks[i] = GreaterEqThan(64);
|
||||
checks[i].in[0] <== actual_balances[i];
|
||||
checks[i].in[1] <== min_required_balances[i];
|
||||
checks[i].out === 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Add Time-Based Constraints
|
||||
|
||||
Add expiration logic to proofs:
|
||||
|
||||
```txt
|
||||
// Add time validation
|
||||
component time_check = LessThan(64);
|
||||
time_check.in[0] <== timestamp;
|
||||
time_check.in[1] <== max_timestamp; // New public input
|
||||
time_check.out === 1;
|
||||
```
|
||||
|
||||
### Rebuilding After Changes
|
||||
|
||||
After modifying the circuit:
|
||||
|
||||
```bash
|
||||
cd circuits
|
||||
pnpm run build
|
||||
|
||||
# Re-inject verification keys
|
||||
cd ..
|
||||
node scripts/inject-vk.js
|
||||
|
||||
# Recompile contract
|
||||
cd contracts
|
||||
cargo build
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- [thirdweb Arbitrum Documentation](https://portal.thirdweb.com/contracts/arbitrum-stylus/stylus-contract)
|
||||
- [Arbitrum Stylus Documentation](https://docs.arbitrum.io/stylus/concepts/how-it-works)
|
||||
|
||||
@@ -56,6 +56,14 @@ export const sidebar: SideBar = {
|
||||
href: `${slug}/arbitrum-stylus/airdrop-contract`,
|
||||
name: "Stylus Airdrop Contract",
|
||||
},
|
||||
{
|
||||
href: `${slug}/arbitrum-stylus/zk-mint`,
|
||||
name: "Stylus Zk Mint",
|
||||
},
|
||||
{
|
||||
href: `${slug}/arbitrum-stylus/minting-modules`,
|
||||
name: "Stylus Minting Modules",
|
||||
},
|
||||
],
|
||||
name: "Arbitrum Stylus",
|
||||
},
|
||||
|
||||
@@ -41,14 +41,14 @@ export default function Page() {
|
||||
|
||||
function Hero() {
|
||||
return (
|
||||
<section className="grid gap-4 py-14 lg:grid-cols-[1fr_420px] lg:py-0">
|
||||
<section className="grid gap-4 pt-14 pb-6 lg:grid-cols-[1fr_420px] lg:py-0">
|
||||
{/* Left */}
|
||||
<div className="flex flex-col justify-center">
|
||||
<div>
|
||||
<h1 className="mb-3 font-bold text-4xl tracking-tighter lg:text-6xl">
|
||||
thirdweb Documentation
|
||||
</h1>
|
||||
<p className="mb-8 max-w-lg text-lg text-muted-foreground leading-normal text-pretty">
|
||||
<p className="mb-6 max-w-lg text-base lg:text-lg text-muted-foreground leading-normal text-pretty">
|
||||
Platform for building the next generation of internet products
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
@@ -178,10 +178,10 @@ function LearningResourcesSection() {
|
||||
title="Wallets"
|
||||
/>
|
||||
<ArticleCardIndex
|
||||
description="Create onchain payments and sell products"
|
||||
href="/payments"
|
||||
icon={PaymentsIcon}
|
||||
title="Payments"
|
||||
description="Instant payments for APIs, websites, and agents"
|
||||
href="/x402"
|
||||
icon={ZapIcon}
|
||||
title="x402"
|
||||
/>
|
||||
<ArticleCardIndex
|
||||
description="Bridge, swap, and onramp tokens on any chain"
|
||||
|
||||
@@ -61,33 +61,6 @@ export const sidebar: SideBar = {
|
||||
name: "Guides",
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
isCollapsible: false,
|
||||
links: [
|
||||
{
|
||||
href: `${paymentsSlug}/x402`,
|
||||
name: "Get Started",
|
||||
},
|
||||
{
|
||||
href: `${paymentsSlug}/x402/client`,
|
||||
name: "Client Side",
|
||||
},
|
||||
{
|
||||
href: `${paymentsSlug}/x402/server`,
|
||||
name: "Server Side",
|
||||
},
|
||||
{
|
||||
href: `${paymentsSlug}/x402/agents`,
|
||||
name: "Agents",
|
||||
},
|
||||
{
|
||||
href: `${paymentsSlug}/x402/facilitator`,
|
||||
name: "Facilitator",
|
||||
},
|
||||
],
|
||||
name: "x402",
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
isCollapsible: false,
|
||||
links: [
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ The client library wraps the native `fetch` API and handles:
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="http">
|
||||
## Prepare Payment
|
||||
## Fetch with Payment
|
||||
|
||||
Fetch any x402-compatible API and automatically handle payment flows when APIs return a `402 Payment Required` response using the authenticated wallet.
|
||||
|
||||
+3
-1
@@ -27,7 +27,9 @@ You can view all transactions processed by your facilitator in your project dash
|
||||
|
||||
## Chain and token support
|
||||
|
||||
Our facilitator supports payments on any EVM chain, as long as the payment token supports either:
|
||||
The thirdweb x402 client/server stack supports payments on [170+ EVM chains](https://thirdweb.com/chainlist?service=account-abstraction).
|
||||
|
||||
The payment token must support either:
|
||||
|
||||
- ERC-2612 permit (most ERC20 tokens)
|
||||
- ERC-3009 sign with authorization (USDC on all chains)
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createMetadata } from "@doc";
|
||||
import { DocLayout } from "@/components/Layouts/DocLayout";
|
||||
import { sidebar } from "./sidebar";
|
||||
|
||||
export default async function Layout(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<DocLayout editPageButton={true} sideBar={sidebar}>
|
||||
{props.children}
|
||||
</DocLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export const metadata = createMetadata({
|
||||
description:
|
||||
"Instant payments for your APIs, websites, and autonomous agents. x402 is an open-source protocol that turns the HTTP 402 status code into a fully-featured, on-chain payment layer.",
|
||||
image: {
|
||||
icon: "payments",
|
||||
title: "x402 Payments Docs",
|
||||
},
|
||||
title: "x402 Payments Documentation",
|
||||
});
|
||||
@@ -12,9 +12,8 @@ export const metadata = createMetadata({
|
||||
|
||||
# x402 Payments
|
||||
|
||||
x402 is an open-source protocol that turns the dormant HTTP 402 Payment Required status code into a fully-featured, on-chain payment layer for APIs, websites, and autonomous agents.
|
||||
[x402](https://x402.org) is an open-source protocol that turns the dormant HTTP 402 Payment Required status code into a fully-featured, on-chain payment layer for APIs, websites, and autonomous agents.
|
||||
|
||||
Learn more about the protocol at [x402.org](https://x402.org).
|
||||
|
||||
<ArticleIconCard
|
||||
title="x402 Playground"
|
||||
@@ -23,6 +22,15 @@ Learn more about the protocol at [x402.org](https://x402.org).
|
||||
href="https://playground.thirdweb.com/payments/x402"
|
||||
/>
|
||||
|
||||
## Chain and token support
|
||||
|
||||
The thirdweb x402 client/server stack supports payments on [170+ EVM chains](https://thirdweb.com/chainlist?service=account-abstraction).
|
||||
|
||||
The payment token must support either:
|
||||
|
||||
- ERC-2612 permit (most ERC20 tokens)
|
||||
- ERC-3009 sign with authorization (USDC on all chains)
|
||||
|
||||
## Client Side
|
||||
|
||||
`wrapFetchWithPayment` wraps the native fetch API to automatically handle `402 Payment Required` responses from any x402-compatible API:
|
||||
@@ -42,7 +50,7 @@ const fetchWithPay = wrapFetchWithPayment(fetch, client, wallet);
|
||||
const response = await fetchWithPay('https://api.example.com/paid-endpoint');
|
||||
```
|
||||
|
||||
You can also use the thirdweb API to fetch any x402 compatible endpoint and pay for it with the authenticated wallet. See the [client side docs](/payments/x402/client) for more details.
|
||||
You can also use the thirdweb API to fetch any x402 compatible endpoint and pay for it with the authenticated wallet. See the [client side docs](/x402/client) for more details.
|
||||
|
||||
## Server Side
|
||||
|
||||
@@ -88,7 +96,7 @@ export async function GET(request: Request) {
|
||||
}
|
||||
```
|
||||
|
||||
You can also create middlewares to handle payment for multiple endpoints, see the [server side docs](/payments/x402/server) for more details. The [facilitator](/payments/x402/facilitator) handles settling the payment onchain using your own server wallet.
|
||||
You can also create middlewares to handle payment for multiple endpoints, see the [server side docs](/x402/server) for more details. The [facilitator](/x402/facilitator) handles settling the payment onchain using your own server wallet.
|
||||
|
||||
## Going Further
|
||||
|
||||
@@ -97,18 +105,18 @@ You can also create middlewares to handle payment for multiple endpoints, see th
|
||||
title="Client Side"
|
||||
description="Learn how to interact with x402-compatible APIs from your client applications"
|
||||
icon={TypeScriptIcon}
|
||||
href="/payments/x402/client"
|
||||
href="/x402/client"
|
||||
/>
|
||||
<ArticleIconCard
|
||||
title="Server Side"
|
||||
description="Accept x402 payments in your APIs with middleware or dedicated endpoints"
|
||||
icon={EngineIcon}
|
||||
href="/payments/x402/server"
|
||||
href="/x402/server"
|
||||
/>
|
||||
<ArticleIconCard
|
||||
title="Facilitator API"
|
||||
description="Configure the facilitator service to verify and settle payments"
|
||||
icon={EngineIcon}
|
||||
href="/payments/x402/facilitator"
|
||||
href="/x402/facilitator"
|
||||
/>
|
||||
</div>
|
||||
+12
-4
@@ -421,23 +421,31 @@ Protect multiple endpoints with a shared middleware:
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
## Price Configuration
|
||||
## Price and Token Configuration
|
||||
|
||||
You can specify prices in multiple ways:
|
||||
|
||||
### USD String
|
||||
|
||||
This will default to using USDC on the specified network.
|
||||
|
||||
```typescript
|
||||
price: "$0.10" // 10 cents in USD
|
||||
network: polygon, // or any other EVM chain
|
||||
price: "$0.10" // 10 cents in USDC
|
||||
```
|
||||
|
||||
### ERC20 Token
|
||||
|
||||
You can use any ERC20 token that supports the ERC-2612 permit or ERC-3009 sign with authorization.
|
||||
|
||||
Simply pass the amount in base units and the token address.
|
||||
|
||||
```typescript
|
||||
network: arbitrum,
|
||||
price: {
|
||||
amount: "100000", // Amount in base units (100000 = 0.1 USDC with 6 decimals)
|
||||
amount: "1000000000000000", // Amount in base units (0.001 tokens with 18 decimals)
|
||||
asset: {
|
||||
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Token address
|
||||
address: "0xf01E52B0BAC3E147f6CAf956a64586865A0aA928", // Token address
|
||||
}
|
||||
}
|
||||
```
|
||||
|
Before Width: | Height: | Size: 376 KiB After Width: | Height: | Size: 376 KiB |
@@ -0,0 +1,77 @@
|
||||
import { ExternalLinkIcon, ZapIcon } from "lucide-react";
|
||||
import type { SideBar } from "@/components/Layouts/DocLayout";
|
||||
import { EngineIcon, ReactIcon, TypeScriptIcon, UnityIcon } from "@/icons";
|
||||
import { UnrealEngineIcon } from "../../icons/sdks/UnrealEngineIcon";
|
||||
|
||||
const x402Slug = "/x402";
|
||||
|
||||
export const sidebar: SideBar = {
|
||||
links: [
|
||||
{
|
||||
href: x402Slug,
|
||||
name: "Get Started",
|
||||
icon: <ZapIcon />,
|
||||
},
|
||||
{
|
||||
href: "https://playground.thirdweb.com/payments/x402",
|
||||
icon: <ExternalLinkIcon />,
|
||||
name: "Playground",
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
isCollapsible: false,
|
||||
links: [
|
||||
{
|
||||
href: `${x402Slug}/client`,
|
||||
name: "Client Side",
|
||||
},
|
||||
{
|
||||
href: `${x402Slug}/server`,
|
||||
name: "Server Side",
|
||||
},
|
||||
{
|
||||
href: `${x402Slug}/agents`,
|
||||
name: "Agents",
|
||||
},
|
||||
{
|
||||
href: `${x402Slug}/facilitator`,
|
||||
name: "Facilitator",
|
||||
},
|
||||
],
|
||||
name: "Guides",
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
isCollapsible: false,
|
||||
links: [
|
||||
{
|
||||
href: `https://payments.thirdweb.com/reference`,
|
||||
icon: <EngineIcon />,
|
||||
name: "REST API",
|
||||
},
|
||||
{
|
||||
href: "/references/typescript/v5",
|
||||
icon: <TypeScriptIcon />,
|
||||
name: "TypeScript",
|
||||
},
|
||||
{
|
||||
href: "/references/typescript/v5",
|
||||
icon: <ReactIcon />,
|
||||
name: "React",
|
||||
},
|
||||
{
|
||||
href: "/unity",
|
||||
icon: <UnityIcon />,
|
||||
name: "Unity",
|
||||
},
|
||||
{
|
||||
href: "/unreal-engine",
|
||||
icon: <UnrealEngineIcon />,
|
||||
name: "Unreal Engine",
|
||||
},
|
||||
],
|
||||
name: "API References",
|
||||
},
|
||||
],
|
||||
name: "x402",
|
||||
};
|
||||
@@ -15,6 +15,7 @@ const authOptions: InAppWalletAuth[] = [
|
||||
"facebook",
|
||||
"x",
|
||||
"tiktok",
|
||||
"epic",
|
||||
"discord",
|
||||
"telegram",
|
||||
"twitch",
|
||||
|
||||
@@ -29,6 +29,9 @@ type AuthMethod =
|
||||
| "telegram"
|
||||
| "farcaster"
|
||||
| "github"
|
||||
| "line"
|
||||
| "tiktok"
|
||||
| "epic"
|
||||
| "passkey"
|
||||
| "wallet"
|
||||
| "guest"
|
||||
@@ -55,6 +58,9 @@ const authMethods: { id: AuthMethod; label: string; description: string }[] = [
|
||||
{ id: "telegram", label: "Telegram", description: "Telegram OAuth" },
|
||||
{ id: "farcaster", label: "Farcaster", description: "Farcaster OAuth" },
|
||||
{ id: "github", label: "GitHub", description: "GitHub OAuth" },
|
||||
{ id: "line", label: "Line", description: "Line OAuth" },
|
||||
{ id: "tiktok", label: "TikTok", description: "TikTok OAuth" },
|
||||
{ id: "epic", label: "Epic Games", description: "Epic Games OAuth" },
|
||||
{ id: "passkey", label: "Passkey", description: "WebAuthn passkey" },
|
||||
{
|
||||
id: "wallet",
|
||||
@@ -244,6 +250,9 @@ const account = await wallet.connect({
|
||||
case "telegram":
|
||||
case "farcaster":
|
||||
case "github":
|
||||
case "line":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
return (
|
||||
baseSetup +
|
||||
`// ${authMethod.charAt(0).toUpperCase() + authMethod.slice(1)} OAuth
|
||||
@@ -561,6 +570,9 @@ function PrebuiltUIExample() {
|
||||
case "telegram":
|
||||
case "farcaster":
|
||||
case "github":
|
||||
case "line":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
return (
|
||||
baseSetup +
|
||||
` await wallet.connect({\n client,\n strategy: "${authMethod}",\n });\n` +
|
||||
@@ -639,7 +651,10 @@ var address = await wallet.LoginWithOtp("123456");`
|
||||
case "x":
|
||||
case "telegram":
|
||||
case "farcaster":
|
||||
case "github": {
|
||||
case "github":
|
||||
case "line":
|
||||
case "tiktok":
|
||||
case "epic": {
|
||||
const providerMap: Record<string, string> = {
|
||||
google: "Google",
|
||||
apple: "Apple",
|
||||
@@ -649,6 +664,9 @@ var address = await wallet.LoginWithOtp("123456");`
|
||||
telegram: "Telegram",
|
||||
farcaster: "Farcaster",
|
||||
github: "Github",
|
||||
line: "Line",
|
||||
tiktok: "TikTok",
|
||||
epic: "Epic",
|
||||
};
|
||||
return (
|
||||
baseSetup +
|
||||
@@ -746,7 +764,10 @@ var wallet = await ThirdwebManager.Instance.ConnectWallet(options);`
|
||||
case "x":
|
||||
case "telegram":
|
||||
case "farcaster":
|
||||
case "github": {
|
||||
case "github":
|
||||
case "line":
|
||||
case "tiktok":
|
||||
case "epic": {
|
||||
const providerMap: Record<string, string> = {
|
||||
google: "Google",
|
||||
apple: "Apple",
|
||||
@@ -756,6 +777,9 @@ var wallet = await ThirdwebManager.Instance.ConnectWallet(options);`
|
||||
telegram: "Telegram",
|
||||
farcaster: "Farcaster",
|
||||
github: "Github",
|
||||
line: "Line",
|
||||
tiktok: "TikTok",
|
||||
epic: "Epic",
|
||||
};
|
||||
return (
|
||||
baseSetup +
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# wagmi-inapp
|
||||
|
||||
## 0.0.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`f964b8a`](https://github.com/thirdweb-dev/js/commit/f964b8aaff22d4078ff4c4c5e09c6ebb5fcb182c), [`424e6b8`](https://github.com/thirdweb-dev/js/commit/424e6b8f9b071d3629ff0291ae6875fa4e4c882e), [`763d3de`](https://github.com/thirdweb-dev/js/commit/763d3de5a8db19a8022b2eda420f13a6787de100)]:
|
||||
- thirdweb@5.111.10
|
||||
- @thirdweb-dev/wagmi-adapter@0.2.185
|
||||
|
||||
## 0.0.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`673e51b`](https://github.com/thirdweb-dev/js/commit/673e51b11cf2d06f00ab92bd4cbb45c51d95ec1f)]:
|
||||
- thirdweb@5.111.9
|
||||
- @thirdweb-dev/wagmi-adapter@0.2.184
|
||||
|
||||
## 0.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1f440d0`](https://github.com/thirdweb-dev/js/commit/1f440d0c97adf14d26d55666257b57541b2ef01a)]:
|
||||
- thirdweb@5.111.8
|
||||
- @thirdweb-dev/wagmi-adapter@0.2.183
|
||||
|
||||
## 0.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8), [`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8)]:
|
||||
- thirdweb@5.111.7
|
||||
- @thirdweb-dev/wagmi-adapter@0.2.182
|
||||
|
||||
## 0.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wagmi-inapp",
|
||||
"private": true,
|
||||
"version": "0.0.26",
|
||||
"version": "0.0.30",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# @thirdweb-dev/nebula
|
||||
|
||||
## 0.2.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`f964b8a`](https://github.com/thirdweb-dev/js/commit/f964b8aaff22d4078ff4c4c5e09c6ebb5fcb182c), [`424e6b8`](https://github.com/thirdweb-dev/js/commit/424e6b8f9b071d3629ff0291ae6875fa4e4c882e), [`763d3de`](https://github.com/thirdweb-dev/js/commit/763d3de5a8db19a8022b2eda420f13a6787de100)]:
|
||||
- thirdweb@5.111.10
|
||||
|
||||
## 0.2.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`673e51b`](https://github.com/thirdweb-dev/js/commit/673e51b11cf2d06f00ab92bd4cbb45c51d95ec1f)]:
|
||||
- thirdweb@5.111.9
|
||||
|
||||
## 0.2.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1f440d0`](https://github.com/thirdweb-dev/js/commit/1f440d0c97adf14d26d55666257b57541b2ef01a)]:
|
||||
- thirdweb@5.111.8
|
||||
|
||||
## 0.2.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8), [`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8)]:
|
||||
- thirdweb@5.111.7
|
||||
|
||||
## 0.2.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -57,5 +57,5 @@
|
||||
"type": "module",
|
||||
"types": "./dist/types/exports/thirdweb.d.ts",
|
||||
"typings": "./dist/types/exports/thirdweb.d.ts",
|
||||
"version": "0.2.84"
|
||||
"version": "0.2.88"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/react-native-adapter
|
||||
|
||||
## 1.5.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#8390](https://github.com/thirdweb-dev/js/pull/8390) [`f964b8a`](https://github.com/thirdweb-dev/js/commit/f964b8aaff22d4078ff4c4c5e09c6ebb5fcb182c) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Loosen peer dependency requirements for RN
|
||||
|
||||
## 1.5.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -34,12 +34,12 @@
|
||||
"peerDependencies": {
|
||||
"@biomejs/biome": "2.0.6",
|
||||
"@coinbase/wallet-mobile-sdk": "^1",
|
||||
"@react-native-async-storage/async-storage": "^1 || ^2",
|
||||
"@react-native-async-storage/async-storage": "*",
|
||||
"@react-native-community/netinfo": "^11",
|
||||
"@walletconnect/react-native-compat": "^2",
|
||||
"expo-application": "^5 || ^6",
|
||||
"expo-linking": "^6 || ^7",
|
||||
"expo-web-browser": "^13 || ^14",
|
||||
"expo-application": "*",
|
||||
"expo-linking": "*",
|
||||
"expo-web-browser": "*",
|
||||
"react-native": ">=0.70",
|
||||
"react-native-aes-gcm-crypto": "^0.2",
|
||||
"react-native-get-random-values": "^1",
|
||||
@@ -69,5 +69,5 @@
|
||||
"type": "module",
|
||||
"types": "./dist/types/exports/thirdweb.d.ts",
|
||||
"typings": "./dist/types/exports/thirdweb.d.ts",
|
||||
"version": "1.5.4"
|
||||
"version": "1.5.5"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# thirdweb
|
||||
|
||||
## 5.111.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#8390](https://github.com/thirdweb-dev/js/pull/8390) [`f964b8a`](https://github.com/thirdweb-dev/js/commit/f964b8aaff22d4078ff4c4c5e09c6ebb5fcb182c) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Loosen peer dependency requirements for RN
|
||||
|
||||
- [#8383](https://github.com/thirdweb-dev/js/pull/8383) [`424e6b8`](https://github.com/thirdweb-dev/js/commit/424e6b8f9b071d3629ff0291ae6875fa4e4c882e) Thanks [@kumaryash90](https://github.com/kumaryash90)! - deploy and install stylus modules
|
||||
|
||||
- [#8388](https://github.com/thirdweb-dev/js/pull/8388) [`763d3de`](https://github.com/thirdweb-dev/js/commit/763d3de5a8db19a8022b2eda420f13a6787de100) Thanks [@MananTank](https://github.com/MananTank)! - - Various UI/UX Improvements in Bridge components
|
||||
|
||||
## 5.111.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#8384](https://github.com/thirdweb-dev/js/pull/8384) [`673e51b`](https://github.com/thirdweb-dev/js/commit/673e51b11cf2d06f00ab92bd4cbb45c51d95ec1f) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Replace celo alfajores with celoSepoliaTestnet
|
||||
|
||||
## 5.111.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#8343](https://github.com/thirdweb-dev/js/pull/8343) [`1f440d0`](https://github.com/thirdweb-dev/js/commit/1f440d0c97adf14d26d55666257b57541b2ef01a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Added Epic Games Authentication
|
||||
|
||||
## 5.111.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#8373](https://github.com/thirdweb-dev/js/pull/8373) [`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Respect from address when simulating with Engine.serverWallet
|
||||
|
||||
- [#8373](https://github.com/thirdweb-dev/js/pull/8373) [`7f08161`](https://github.com/thirdweb-dev/js/commit/7f081618e16e7d5754a09780421e35b244186ce8) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose useInvalidateBalances() react hook
|
||||
|
||||
## 5.111.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
"dotenv-mono": "^1.3.14",
|
||||
"ethers5": "npm:ethers@5",
|
||||
"ethers6": "npm:ethers@6",
|
||||
"expo-linking": "7.0.5",
|
||||
"expo-web-browser": "14.0.2",
|
||||
"expo-linking": "8.0.8",
|
||||
"expo-web-browser": "15.0.9",
|
||||
"happy-dom": "17.4.4",
|
||||
"knip": "5.60.2",
|
||||
"msw": "2.7.5",
|
||||
@@ -256,10 +256,10 @@
|
||||
"@aws-sdk/credential-providers": "^3",
|
||||
"@coinbase/wallet-mobile-sdk": "^1",
|
||||
"@mobile-wallet-protocol/client": "^1",
|
||||
"@react-native-async-storage/async-storage": "^1 || ^2",
|
||||
"@react-native-async-storage/async-storage": "*",
|
||||
"ethers": "^5 || ^6",
|
||||
"expo-linking": "^6 || ^7",
|
||||
"expo-web-browser": "^13 || ^14",
|
||||
"expo-linking": "*",
|
||||
"expo-web-browser": "*",
|
||||
"react": "^18 || ^19",
|
||||
"react-native": "*",
|
||||
"react-native-aes-gcm-crypto": "^0.2",
|
||||
@@ -430,5 +430,5 @@
|
||||
}
|
||||
},
|
||||
"typings": "./dist/types/exports/thirdweb.d.ts",
|
||||
"version": "5.111.6"
|
||||
"version": "5.111.10"
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { defineChain } from "../utils.js";
|
||||
|
||||
/**
|
||||
* @chain
|
||||
*/
|
||||
export const celoAlfajoresTestnet = /* @__PURE__ */ defineChain({
|
||||
blockExplorers: [
|
||||
{
|
||||
name: "Alfajoresscan",
|
||||
url: "https://alfajores.celoscan.io",
|
||||
},
|
||||
],
|
||||
id: 44787,
|
||||
name: "Celo Alfajores Testnet",
|
||||
nativeCurrency: { decimals: 18, name: "CELO", symbol: "CELO" },
|
||||
testnet: true,
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { defineChain } from "../utils.js";
|
||||
|
||||
/**
|
||||
* @chain
|
||||
*/
|
||||
export const celoSepoliaTestnet = /* @__PURE__ */ defineChain({
|
||||
blockExplorers: [
|
||||
{
|
||||
name: "Celo Sepolia Explorer",
|
||||
url: "https://celo-sepolia.blockscout.com/",
|
||||
},
|
||||
],
|
||||
id: 11142220,
|
||||
name: "Celo Sepolia Testnet",
|
||||
nativeCurrency: { decimals: 18, name: "CELO", symbol: "CELO-S" },
|
||||
testnet: true,
|
||||
});
|
||||
@@ -60,6 +60,12 @@ export async function createStylusProject() {
|
||||
{ title: "Airdrop ERC1155", value: "airdrop1155" },
|
||||
{ title: "ZK ERC721", value: "zk-erc721" },
|
||||
{ title: "ZK ERC20", value: "zk-erc20" },
|
||||
{ title: "Mint module - ERC20", value: "mintable20" },
|
||||
{ title: "Transfer module - ERC20", value: "transferable20" },
|
||||
{ title: "Mint module - ERC721", value: "mintable721" },
|
||||
{ title: "Transfer module - ERC721", value: "transferable721" },
|
||||
{ title: "Mint module - ERC1155", value: "mintable1155" },
|
||||
{ title: "Transfer module - ERC1155", value: "transferable1155" },
|
||||
],
|
||||
message: "Select a template:",
|
||||
name: "projectType",
|
||||
@@ -133,6 +139,49 @@ export async function createStylusProject() {
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "mintable20") {
|
||||
const repoUrl = "[email protected]:thirdweb-example/stylus-mintable-erc20.git";
|
||||
spinner.start(`Creating new ERC20 Mintable module: ${projectName}...`);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "transferable20") {
|
||||
const repoUrl =
|
||||
"[email protected]:thirdweb-example/stylus-transferable-erc20.git";
|
||||
spinner.start(`Creating new ERC20 Transferable module: ${projectName}...`);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "mintable721") {
|
||||
const repoUrl =
|
||||
"[email protected]:thirdweb-example/stylus-mintable-erc721.git";
|
||||
spinner.start(`Creating new ERC721 Mintable module: ${projectName}...`);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "transferable721") {
|
||||
const repoUrl =
|
||||
"[email protected]:thirdweb-example/stylus-transferable-erc721.git";
|
||||
spinner.start(`Creating new ERC721 Transferable module: ${projectName}...`);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "mintable1155") {
|
||||
const repoUrl =
|
||||
"[email protected]:thirdweb-example/stylus-mintable-erc1155.git";
|
||||
spinner.start(`Creating new ERC1155 Mintable module: ${projectName}...`);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
} else if (projectType === "transferable1155") {
|
||||
const repoUrl =
|
||||
"[email protected]:thirdweb-example/stylus-transferable-erc1155.git";
|
||||
spinner.start(
|
||||
`Creating new ERC1155 Transferable module: ${projectName}...`,
|
||||
);
|
||||
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
}
|
||||
|
||||
if (!newProject || newProject.status !== 0) {
|
||||
|
||||
@@ -9,48 +9,44 @@ import { getContract } from "../contract.js";
|
||||
import { deployCloneFactory } from "./utils/bootstrap.js";
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("deploy dynamic", () => {
|
||||
// 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,
|
||||
});
|
||||
it.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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -332,6 +332,7 @@ export function serverWallet(options: ServerWalletOptions): ServerWallet {
|
||||
if (args.simulate) {
|
||||
serializedTransaction = await toSerializableTransaction({
|
||||
transaction: args.transaction,
|
||||
from: address,
|
||||
});
|
||||
} else {
|
||||
const [to, data, value] = await Promise.all([
|
||||
|
||||
@@ -24,7 +24,7 @@ export { blastSepolia } from "../chains/chain-definitions/blast-sepolia.js";
|
||||
export { bsc } from "../chains/chain-definitions/bsc.js";
|
||||
export { bscTestnet } from "../chains/chain-definitions/bsc-testnet.js";
|
||||
export { celo } from "../chains/chain-definitions/celo.js";
|
||||
export { celoAlfajoresTestnet } from "../chains/chain-definitions/celo-alfajores-testnet.js";
|
||||
export { celoSepoliaTestnet } from "../chains/chain-definitions/celo-sepolia-testnet.js";
|
||||
export { coreMainnet } from "../chains/chain-definitions/core-mainnet.js";
|
||||
export { coreTestnet } from "../chains/chain-definitions/core-testnet.js";
|
||||
export { cronos } from "../chains/chain-definitions/cronos.js";
|
||||
|
||||
@@ -22,6 +22,7 @@ export { useContractEvents } from "../react/core/hooks/contract/useContractEvent
|
||||
// contract
|
||||
export { useReadContract } from "../react/core/hooks/contract/useReadContract.js";
|
||||
export { useWaitForReceipt } from "../react/core/hooks/contract/useWaitForReceipt.js";
|
||||
export { useInvalidateBalances } from "../react/core/hooks/others/useInvalidateBalances.js";
|
||||
export { useInvalidateContractQuery } from "../react/core/hooks/others/useInvalidateQueries.js";
|
||||
export { useWalletBalance } from "../react/core/hooks/others/useWalletBalance.js";
|
||||
export {
|
||||
|
||||
@@ -29,6 +29,7 @@ export { useReadContract } from "../react/core/hooks/contract/useReadContract.js
|
||||
export { useWaitForReceipt } from "../react/core/hooks/contract/useWaitForReceipt.js";
|
||||
// chain hooks
|
||||
export { useChainMetadata } from "../react/core/hooks/others/useChainQuery.js";
|
||||
export { useInvalidateBalances } from "../react/core/hooks/others/useInvalidateBalances.js";
|
||||
export { useInvalidateContractQuery } from "../react/core/hooks/others/useInvalidateQueries.js";
|
||||
export { useWalletBalance } from "../react/core/hooks/others/useWalletBalance.js";
|
||||
export {
|
||||
|
||||
+40
-44
@@ -11,54 +11,50 @@ import { sendTransaction } from "../../../transaction/actions/send-transaction.j
|
||||
import { installPublishedExtension } from "./installPublishedExtension.js";
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("install extension", () => {
|
||||
// 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,
|
||||
});
|
||||
it.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,8 +11,7 @@ import { deployPublishedContract } from "../../prebuilts/deploy-published.js";
|
||||
import { uninstallExtension } from "./uninstallExtension.js";
|
||||
|
||||
describe.runIf(process.env.TW_SECRET_KEY)("uninstall extension", () => {
|
||||
// TODO: Fix this test
|
||||
it.skip.sequential(
|
||||
it.sequential(
|
||||
"should uninstall extension from a dynamic contract",
|
||||
async () => {
|
||||
await deployCloneFactory({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ThirdwebContract } from "../../../contract/contract.js";
|
||||
import { getOrDeployInfraForPublishedContract } from "../../../contract/deployment/utils/bootstrap.js";
|
||||
import type { Account } from "../../../wallets/interfaces/wallet.js";
|
||||
import { deployPublishedContract } from "../../prebuilts/deploy-published.js";
|
||||
import { installModule } from "../__generated__/IModularCore/write/installModule.js";
|
||||
|
||||
/**
|
||||
@@ -49,17 +50,35 @@ export function installPublishedModule(options: InstallPublishedModuleOptions) {
|
||||
|
||||
return installModule({
|
||||
asyncParams: async () => {
|
||||
const deployedModule = await getOrDeployInfraForPublishedContract({
|
||||
account,
|
||||
chain: contract.chain,
|
||||
client: contract.client,
|
||||
constructorParams,
|
||||
contractId: moduleName,
|
||||
publisher,
|
||||
});
|
||||
let implementationAddress: string;
|
||||
|
||||
if (moduleName.toLowerCase().includes("stylus")) {
|
||||
// TODO: switch to deterministic / create2 when available
|
||||
|
||||
implementationAddress = await deployPublishedContract({
|
||||
account,
|
||||
chain: contract.chain,
|
||||
client: contract.client,
|
||||
contractParams: constructorParams,
|
||||
contractId: moduleName,
|
||||
publisher,
|
||||
});
|
||||
} else {
|
||||
const deployedModule = await getOrDeployInfraForPublishedContract({
|
||||
account,
|
||||
chain: contract.chain,
|
||||
client: contract.client,
|
||||
constructorParams,
|
||||
contractId: moduleName,
|
||||
publisher,
|
||||
});
|
||||
|
||||
implementationAddress = deployedModule.implementationContract
|
||||
.address as string;
|
||||
}
|
||||
return {
|
||||
data: moduleData || "0x",
|
||||
moduleContract: deployedModule.implementationContract.address as string,
|
||||
moduleContract: implementationAddress,
|
||||
};
|
||||
},
|
||||
contract,
|
||||
|
||||
@@ -215,7 +215,7 @@ export async function deployContractfromDeployMetadata(
|
||||
if (
|
||||
deployMetadata.routerType === "dynamic" &&
|
||||
deployMetadata.defaultExtensions &&
|
||||
!isZkSyncChain(chain)
|
||||
!(await isZkSyncChain(chain))
|
||||
) {
|
||||
for (const e of deployMetadata.defaultExtensions) {
|
||||
await getOrDeployInfraForPublishedContract({
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { invalidateWalletBalance } from "../../providers/invalidateWalletBalance.js";
|
||||
|
||||
/**
|
||||
* Invalidate the balances for a given chainId. If no chainId is provided, invalidate all balances.
|
||||
* @example
|
||||
* ```ts
|
||||
* const invalidateBalances = useInvalidateBalances();
|
||||
* invalidateBalances();
|
||||
* ```
|
||||
*/
|
||||
export function useInvalidateBalances() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return ({ chainId }: { chainId?: number }) => {
|
||||
invalidateWalletBalance(queryClient, chainId);
|
||||
};
|
||||
}
|
||||
@@ -55,6 +55,10 @@ describe("walletIcon", () => {
|
||||
expect(getSocialIcon("tiktok")).toBe(socialIcons.tiktok);
|
||||
});
|
||||
|
||||
it("should return epic icon", () => {
|
||||
expect(getSocialIcon("epic")).toBe(socialIcons.epic);
|
||||
});
|
||||
|
||||
it("should return farcaster icon", () => {
|
||||
expect(getSocialIcon("farcaster")).toBe(socialIcons.farcaster);
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@ import {
|
||||
APPLE_ICON,
|
||||
DISCORD_ICON,
|
||||
EMAIL_ICON,
|
||||
EPIC_ICON,
|
||||
FACEBOOK_ICON,
|
||||
FARCASTER_ICON,
|
||||
GITHUB_ICON,
|
||||
@@ -106,6 +107,8 @@ export function getAuthProviderImage(authProvider: string | null): string {
|
||||
return X_ICON;
|
||||
case "tiktok":
|
||||
return TIKTOK_ICON;
|
||||
case "epic":
|
||||
return EPIC_ICON;
|
||||
case "farcaster":
|
||||
return FARCASTER_ICON;
|
||||
case "telegram":
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
APPLE_ICON,
|
||||
COINBASE_ICON,
|
||||
DISCORD_ICON,
|
||||
EPIC_ICON,
|
||||
FACEBOOK_ICON,
|
||||
FARCASTER_ICON,
|
||||
GITHUB_ICON,
|
||||
@@ -69,6 +70,7 @@ const socialIcons = {
|
||||
twitch: TWITCH_ICON,
|
||||
x: X_ICON,
|
||||
tiktok: TIKTOK_ICON,
|
||||
epic: EPIC_ICON,
|
||||
};
|
||||
|
||||
type InAppWalletFormUIProps = {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -219,7 +219,7 @@ export function TransactionPayment({
|
||||
}}
|
||||
>
|
||||
{/* USD Value */}
|
||||
<Text color="primaryText" size="xl" weight={600}>
|
||||
<Text color="primaryText" size="xl" weight={500}>
|
||||
{costToDisplay}
|
||||
</Text>
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export function TokenAndChain({
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
weight={600}
|
||||
weight={500}
|
||||
trackingTight
|
||||
>
|
||||
{token.name}
|
||||
|
||||
@@ -68,18 +68,9 @@ export function TokenBalanceRow({
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<FiatValue
|
||||
currency={currency}
|
||||
chain={chain}
|
||||
client={client}
|
||||
<Text
|
||||
color="primaryText"
|
||||
size="sm"
|
||||
token={token}
|
||||
tokenAmount={amount}
|
||||
/>
|
||||
<Text
|
||||
color="secondaryText"
|
||||
size="xs"
|
||||
style={{
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
@@ -91,6 +82,16 @@ export function TokenBalanceRow({
|
||||
minimumFractionDigits: 0,
|
||||
})} ${token.symbol}`}
|
||||
</Text>
|
||||
|
||||
<FiatValue
|
||||
currency={currency}
|
||||
chain={chain}
|
||||
client={client}
|
||||
color="secondaryText"
|
||||
size="sm"
|
||||
token={token}
|
||||
tokenAmount={amount}
|
||||
/>
|
||||
</Container>
|
||||
</Container>
|
||||
</StyledButton>
|
||||
|
||||
@@ -100,13 +100,23 @@ export function SelectedTokenButton(props: {
|
||||
}}
|
||||
>
|
||||
<Img
|
||||
src={props.chain?.icon}
|
||||
src={props.chain?.icon || ""}
|
||||
client={props.client}
|
||||
width={iconSize.sm}
|
||||
height={iconSize.sm}
|
||||
style={{
|
||||
borderRadius: radius.full,
|
||||
}}
|
||||
fallback={
|
||||
<Container
|
||||
style={{
|
||||
background: `linear-gradient(45deg, white, ${theme.colors.accentText})`,
|
||||
borderRadius: radius.full,
|
||||
width: `${iconSize.sm}px`,
|
||||
height: `${iconSize.sm}px`,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
@@ -63,7 +63,7 @@ export function PaymentOverview(props: {
|
||||
<WalletRow
|
||||
address={sender}
|
||||
client={props.client}
|
||||
iconSize="md"
|
||||
iconSize="lg"
|
||||
textSize="sm"
|
||||
/>
|
||||
</Container>
|
||||
@@ -91,13 +91,14 @@ export function PaymentOverview(props: {
|
||||
py="sm"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
{/* left */}
|
||||
<Container center="y" flex="row" gap="sm">
|
||||
{getFiatCurrencyIcon({
|
||||
currency: props.paymentMethod.currency,
|
||||
size: "lg",
|
||||
})}
|
||||
<Container center="y" flex="column" gap="3xs">
|
||||
<Text color="primaryText" size="sm" style={{ fontWeight: 600 }}>
|
||||
<Text color="primaryText" size="sm" weight={500}>
|
||||
{props.paymentMethod.currency}
|
||||
</Text>
|
||||
<Text color="secondaryText" size="xs">
|
||||
@@ -106,7 +107,8 @@ export function PaymentOverview(props: {
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
<Text color="primaryText" size="sm" style={{ fontWeight: 600 }}>
|
||||
{/* right */}
|
||||
<Text color="primaryText" size="sm">
|
||||
{props.fromAmount}
|
||||
</Text>
|
||||
</Container>
|
||||
@@ -136,11 +138,12 @@ export function PaymentOverview(props: {
|
||||
<WalletRow
|
||||
address={props.receiver}
|
||||
client={props.client}
|
||||
iconSize="md"
|
||||
iconSize="lg"
|
||||
textSize="sm"
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{props.modeInfo.mode === "direct_payment" && (
|
||||
<Container
|
||||
center="y"
|
||||
@@ -150,7 +153,7 @@ export function PaymentOverview(props: {
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Container center="y" flex="column" gap="3xs" style={{ flex: 1 }}>
|
||||
<Text color="primaryText" size="sm" style={{ fontWeight: 600 }}>
|
||||
<Text color="primaryText" size="sm" weight={500}>
|
||||
{props.metadata.title || "Payment"}
|
||||
</Text>
|
||||
{props.metadata.description && (
|
||||
@@ -173,7 +176,6 @@ export function PaymentOverview(props: {
|
||||
size="sm"
|
||||
token={props.toToken}
|
||||
tokenAmount={props.modeInfo.paymentInfo.amount}
|
||||
weight={600}
|
||||
/>
|
||||
<Text color="secondaryText" size="xs">
|
||||
{props.modeInfo.paymentInfo.amount} {props.toToken.symbol}
|
||||
@@ -285,7 +287,7 @@ const TransactionOverViewCompact = (props: {
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Container center="y" flex="column" gap="3xs" style={{ flex: 1 }}>
|
||||
<Text color="primaryText" size="sm" style={{ fontWeight: 600 }}>
|
||||
<Text color="primaryText" size="sm" weight={500}>
|
||||
{props.metadata.title || "Transaction"}
|
||||
</Text>
|
||||
{props.metadata.description && (
|
||||
|
||||
@@ -89,7 +89,7 @@ function PaymentMethodTokenRow({
|
||||
<Text
|
||||
color="primaryText"
|
||||
size="sm"
|
||||
style={{ fontWeight: 600, textWrap: "nowrap" }}
|
||||
style={{ fontWeight: 500, textWrap: "nowrap" }}
|
||||
>
|
||||
{formatCurrencyAmount(
|
||||
currency || "USD",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"use client";
|
||||
import { CopyIcon } from "@radix-ui/react-icons";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useCallback } from "react";
|
||||
import type { Token } from "../../../../../bridge/types/Token.js";
|
||||
import type { ChainMetadata } from "../../../../../chains/types.js";
|
||||
import {
|
||||
@@ -10,22 +8,18 @@ import {
|
||||
getChainMetadata,
|
||||
} from "../../../../../chains/utils.js";
|
||||
import { shortenHex } from "../../../../../utils/address.js";
|
||||
import { formatExplorerTxUrl } from "../../../../../utils/url.js";
|
||||
import type { WindowAdapter } from "../../../../core/adapters/WindowAdapter.js";
|
||||
import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
|
||||
import {
|
||||
iconSize,
|
||||
radius,
|
||||
spacing,
|
||||
} from "../../../../core/design-system/index.js";
|
||||
import { radius, spacing } from "../../../../core/design-system/index.js";
|
||||
import type { BridgePrepareResult } from "../../../../core/hooks/useBridgePrepare.js";
|
||||
import type { CompletedStatusResult } from "../../../../core/hooks/useStepExecutor.js";
|
||||
import { formatTokenAmount } from "../../ConnectWallet/screens/formatTokenBalance.js";
|
||||
import { Container, ModalHeader } from "../../components/basic.js";
|
||||
import { Container, Line, ModalHeader } from "../../components/basic.js";
|
||||
import { shorterChainName } from "../../components/ChainName.js";
|
||||
import { CopyIcon } from "../../components/CopyIcon.js";
|
||||
import { Skeleton } from "../../components/Skeleton.js";
|
||||
import { Spacer } from "../../components/Spacer.js";
|
||||
import { Text } from "../../components/text.js";
|
||||
import { Link, Text } from "../../components/text.js";
|
||||
|
||||
interface TransactionInfo {
|
||||
type: "paymentId" | "transactionHash";
|
||||
@@ -128,7 +122,6 @@ interface CompletedStepDetailCardProps {
|
||||
status: CompletedStatusResult;
|
||||
preparedQuote: BridgePrepareResult;
|
||||
windowAdapter: WindowAdapter;
|
||||
onCopyToClipboard: (text: string) => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,29 +130,12 @@ interface CompletedStepDetailCardProps {
|
||||
function CompletedStepDetailCard({
|
||||
status,
|
||||
preparedQuote,
|
||||
windowAdapter,
|
||||
onCopyToClipboard,
|
||||
}: CompletedStepDetailCardProps) {
|
||||
const theme = useCustomTheme();
|
||||
const { data: txInfo, isLoading } = useTransactionInfo(status, preparedQuote);
|
||||
const { data: txInfo, isPending } = useTransactionInfo(status, preparedQuote);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Container
|
||||
flex="column"
|
||||
gap="sm"
|
||||
style={{
|
||||
backgroundColor: theme.colors.tertiaryBg,
|
||||
border: `1px solid ${theme.colors.borderColor}`,
|
||||
borderRadius: radius.sm,
|
||||
padding: spacing.md,
|
||||
}}
|
||||
>
|
||||
<Skeleton height="30px" />
|
||||
<Skeleton height="30px" />
|
||||
<Skeleton height="30px" />
|
||||
</Container>
|
||||
);
|
||||
if (isPending) {
|
||||
return <Skeleton height="200px" style={{ borderRadius: radius.lg }} />;
|
||||
}
|
||||
|
||||
if (!txInfo) {
|
||||
@@ -174,7 +150,7 @@ function CompletedStepDetailCard({
|
||||
style={{
|
||||
backgroundColor: theme.colors.tertiaryBg,
|
||||
border: `1px solid ${theme.colors.borderColor}`,
|
||||
borderRadius: radius.sm,
|
||||
borderRadius: radius.lg,
|
||||
padding: spacing.md,
|
||||
}}
|
||||
>
|
||||
@@ -182,27 +158,37 @@ function CompletedStepDetailCard({
|
||||
<Container
|
||||
flex="row"
|
||||
gap="sm"
|
||||
py="4xs"
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Text color="primaryText" size="sm">
|
||||
<Text color="primaryText" size="sm" weight={500}>
|
||||
{txInfo.label}
|
||||
</Text>
|
||||
<Container
|
||||
style={{
|
||||
backgroundColor: theme.colors.success,
|
||||
borderRadius: radius.sm,
|
||||
backgroundColor: theme.colors.modalBg,
|
||||
borderRadius: radius.full,
|
||||
border: `1px solid ${theme.colors.borderColor}`,
|
||||
padding: `${spacing["3xs"]} ${spacing.xs}`,
|
||||
}}
|
||||
>
|
||||
<Text size="xs" style={{ color: theme.colors.primaryButtonText }}>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.colors.success,
|
||||
fontSize: 10,
|
||||
letterSpacing: "0.025em",
|
||||
}}
|
||||
>
|
||||
COMPLETED
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
|
||||
<Line />
|
||||
|
||||
{/* Amount Paid */}
|
||||
{txInfo.amountPaid && (
|
||||
<Container
|
||||
@@ -219,22 +205,6 @@ function CompletedStepDetailCard({
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{/* Origin Chain */}
|
||||
{txInfo.originChain && (
|
||||
<Container
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
Origin Chain
|
||||
</Text>
|
||||
<Text color="primaryText" size="sm">
|
||||
{shorterChainName(txInfo.originChain.name)}
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{/* Amount Received */}
|
||||
{txInfo.amountReceived && (
|
||||
<Container
|
||||
@@ -251,14 +221,30 @@ function CompletedStepDetailCard({
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{/* Chain */}
|
||||
{/* Origin Chain */}
|
||||
{txInfo.originChain && (
|
||||
<Container
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
Origin Chain
|
||||
</Text>
|
||||
<Text color="primaryText" size="sm">
|
||||
{shorterChainName(txInfo.originChain.name)}
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{/* Destination Chain */}
|
||||
<Container
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
Chain
|
||||
Destination Chain
|
||||
</Text>
|
||||
<Text color="primaryText" size="sm">
|
||||
{shorterChainName(txInfo.chain.name)}
|
||||
@@ -266,58 +252,59 @@ function CompletedStepDetailCard({
|
||||
</Container>
|
||||
|
||||
{/* Transaction Info */}
|
||||
<Container
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
{txInfo.type === "paymentId" ? "Payment ID" : "Transaction Hash"}
|
||||
</Text>
|
||||
<Container flex="row" gap="sm" style={{ alignItems: "center" }}>
|
||||
<Text
|
||||
color="accentText"
|
||||
onClick={
|
||||
txInfo.type === "paymentId"
|
||||
? () => onCopyToClipboard(txInfo.id)
|
||||
: () => {
|
||||
const explorer = txInfo.chain.explorers?.[0];
|
||||
if (explorer) {
|
||||
windowAdapter.open(
|
||||
formatExplorerTxUrl(explorer.url, txInfo.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
size="sm"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: "monospace",
|
||||
}}
|
||||
>
|
||||
{shortenHex(txInfo.id)}
|
||||
{txInfo.type === "paymentId" && (
|
||||
<Container
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
Payment ID
|
||||
</Text>
|
||||
<Container flex="row" gap="3xs" center="y">
|
||||
<CopyIcon text={txInfo.id} iconSize={12} tip="Copy Payment ID" />
|
||||
|
||||
{txInfo.type === "paymentId" ? (
|
||||
<button
|
||||
onClick={() => onCopyToClipboard(txInfo.id)}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
padding: 0,
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<CopyIcon
|
||||
color={theme.colors.primaryText}
|
||||
height={iconSize.sm}
|
||||
width={iconSize.sm}
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
<Text color="primaryText" weight={400} size="sm">
|
||||
{shortenHex(txInfo.id)}
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{status.transactions.map((tx) => {
|
||||
const explorerLink = `https://thirdweb.com/${tx.chainId}/tx/${tx.transactionHash}`;
|
||||
return (
|
||||
<Container
|
||||
key={tx.transactionHash}
|
||||
center="y"
|
||||
flex="row"
|
||||
style={{ justifyContent: "space-between" }}
|
||||
>
|
||||
<Text color="secondaryText" size="sm">
|
||||
Transaction Hash
|
||||
</Text>
|
||||
<Container flex="row" gap="3xs" center="y">
|
||||
<CopyIcon
|
||||
text={tx.transactionHash}
|
||||
iconSize={12}
|
||||
tip="Copy Transaction Hash"
|
||||
/>
|
||||
|
||||
<Link
|
||||
href={explorerLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
color="primaryText"
|
||||
hoverColor="accentText"
|
||||
weight={400}
|
||||
size="sm"
|
||||
>
|
||||
{shortenHex(tx.transactionHash)}
|
||||
</Link>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
})}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -350,34 +337,23 @@ export function PaymentReceipt({
|
||||
onBack,
|
||||
windowAdapter,
|
||||
}: PaymentReceitProps) {
|
||||
// Copy to clipboard
|
||||
const copyToClipboard = useCallback(async (text: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
// Could add a toast notification here
|
||||
} catch (error) {
|
||||
console.warn("Failed to copy to clipboard:", error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container
|
||||
flex="column"
|
||||
fullHeight
|
||||
p="md"
|
||||
style={{ maxHeight: "500px", minHeight: "250px", overflowY: "auto" }}
|
||||
>
|
||||
<Container flex="column" fullHeight px="md">
|
||||
<Spacer y="md+" />
|
||||
<ModalHeader onBack={onBack} title="Payment Receipt" />
|
||||
<Spacer y="md+" />
|
||||
|
||||
<Spacer y="lg" />
|
||||
|
||||
<Container flex="column" gap="lg">
|
||||
<Container
|
||||
flex="column"
|
||||
gap="lg"
|
||||
scrollY
|
||||
style={{ maxHeight: "500px", minHeight: "400px" }}
|
||||
>
|
||||
{/* Status Results */}
|
||||
<Container flex="column" gap="md">
|
||||
<Container flex="column" gap="sm">
|
||||
{completedStatuses.map((status, index) => (
|
||||
<CompletedStepDetailCard
|
||||
key={`${status.type}-${index}`}
|
||||
onCopyToClipboard={copyToClipboard}
|
||||
preparedQuote={preparedQuote}
|
||||
status={status}
|
||||
windowAdapter={windowAdapter}
|
||||
@@ -385,6 +361,7 @@ export function PaymentReceipt({
|
||||
))}
|
||||
</Container>
|
||||
</Container>
|
||||
<Spacer y="md+" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvid
|
||||
import { iconSize, spacing } from "../../../../core/design-system/index.js";
|
||||
import type { BridgePrepareResult } from "../../../../core/hooks/useBridgePrepare.js";
|
||||
import type { CompletedStatusResult } from "../../../../core/hooks/useStepExecutor.js";
|
||||
import { Container, ModalHeader } from "../../components/basic.js";
|
||||
import { Container } from "../../components/basic.js";
|
||||
import { Button } from "../../components/buttons.js";
|
||||
import { Spacer } from "../../components/Spacer.js";
|
||||
import { Text } from "../../components/text.js";
|
||||
@@ -91,9 +91,7 @@ export function SuccessScreen({
|
||||
|
||||
return (
|
||||
<Container flex="column" fullHeight px="md" pb="md" pt="md+">
|
||||
<ModalHeader title="Payment Complete" />
|
||||
|
||||
<Spacer y="xxl" />
|
||||
<Spacer y="3xl" />
|
||||
|
||||
<Container center="x" flex="column" gap="md">
|
||||
{/* Success Icon with Animation */}
|
||||
@@ -130,7 +128,7 @@ export function SuccessScreen({
|
||||
marginBottom: spacing.xxs,
|
||||
}}
|
||||
>
|
||||
Payment Successful!
|
||||
Payment Successful
|
||||
</Text>
|
||||
|
||||
<Text center color="secondaryText" size="sm">
|
||||
@@ -143,7 +141,7 @@ export function SuccessScreen({
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<Spacer y="xxl" />
|
||||
<Spacer y="3xl" />
|
||||
|
||||
{/* Action Buttons */}
|
||||
<Container flex="column" gap="sm" style={{ width: "100%" }}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import type { Chain as BridgeChain } from "../../../../../bridge/index.js";
|
||||
import type { ThirdwebClient } from "../../../../../client/client.js";
|
||||
import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
|
||||
import {
|
||||
fontSize,
|
||||
iconSize,
|
||||
@@ -170,6 +171,8 @@ function ChainButton(props: {
|
||||
isSelected: boolean;
|
||||
isMobile: boolean;
|
||||
}) {
|
||||
const theme = useCustomTheme();
|
||||
const iconSizeValue = props.isMobile ? iconSize.lg : iconSize.md;
|
||||
return (
|
||||
<Button
|
||||
variant={props.isSelected ? "secondary" : "ghost-solid"}
|
||||
@@ -185,13 +188,25 @@ function ChainButton(props: {
|
||||
gap="sm"
|
||||
>
|
||||
<Img
|
||||
src={props.chain.icon}
|
||||
src={props.chain.icon || ""}
|
||||
client={props.client}
|
||||
width={props.isMobile ? iconSize.lg : iconSize.md}
|
||||
height={props.isMobile ? iconSize.lg : iconSize.md}
|
||||
width={iconSizeValue}
|
||||
height={iconSizeValue}
|
||||
style={{
|
||||
borderRadius: radius.full,
|
||||
}}
|
||||
fallback={
|
||||
<Container color="secondaryText">
|
||||
<Container
|
||||
style={{
|
||||
background: `linear-gradient(45deg, white, ${theme.colors.accentText})`,
|
||||
borderRadius: radius.full,
|
||||
width: `${iconSizeValue}px`,
|
||||
height: `${iconSizeValue}px`,
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
}
|
||||
/>
|
||||
{cleanedChainName(props.chain.name)}
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import type { Token } from "../../../../../bridge/index.js";
|
||||
import type { BridgeChain } from "../../../../../bridge/types/Chain.js";
|
||||
import type { ThirdwebClient } from "../../../../../client/client.js";
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
radius,
|
||||
spacing,
|
||||
} from "../../../../core/design-system/index.js";
|
||||
import { CoinsIcon } from "../../ConnectWallet/icons/CoinsIcon.js";
|
||||
import { WalletDotIcon } from "../../ConnectWallet/icons/WalletDotIcon.js";
|
||||
import { Container, noScrollBar } from "../../components/basic.js";
|
||||
import { Button } from "../../components/buttons.js";
|
||||
import { Img } from "../../components/Img.js";
|
||||
@@ -52,14 +50,18 @@ function findChain(chains: BridgeChain[], activeChainId: number | undefined) {
|
||||
return chains.find((chain) => chain.chainId === activeChainId);
|
||||
}
|
||||
|
||||
const ALL_TOKENS_LIMIT = 100;
|
||||
const OWNED_TOKENS_LIMIT = 50;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function SelectToken(props: SelectTokenUIProps) {
|
||||
const chainQuery = useBridgeChains(props.client);
|
||||
const [search, setSearch] = useState("");
|
||||
const [search, _setSearch] = useState("");
|
||||
const debouncedSearch = useDebouncedValue(search, 500);
|
||||
const [limit, setLimit] = useState(1000);
|
||||
const [allTokensLimit, setAllTokensLimit] = useState(ALL_TOKENS_LIMIT);
|
||||
const [ownedTokensLimit, setOwnedTokensLimit] = useState(OWNED_TOKENS_LIMIT);
|
||||
|
||||
const [_selectedChain, setSelectedChain] = useState<BridgeChain | undefined>(
|
||||
undefined,
|
||||
@@ -77,7 +79,7 @@ export function SelectToken(props: SelectTokenUIProps) {
|
||||
client: props.client,
|
||||
chainId: selectedChain?.chainId,
|
||||
search: debouncedSearch,
|
||||
limit,
|
||||
limit: allTokensLimit,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
@@ -85,7 +87,7 @@ export function SelectToken(props: SelectTokenUIProps) {
|
||||
const ownedTokensQuery = useTokenBalances({
|
||||
clientId: props.client.clientId,
|
||||
chainId: selectedChain?.chainId,
|
||||
limit,
|
||||
limit: ownedTokensLimit,
|
||||
page: 1,
|
||||
walletAddress: props.activeWalletInfo?.activeAccount.address,
|
||||
});
|
||||
@@ -102,43 +104,62 @@ export function SelectToken(props: SelectTokenUIProps) {
|
||||
});
|
||||
}, [ownedTokensQuery.data?.tokens, debouncedSearch]);
|
||||
|
||||
const isFetching = tokensQuery.isFetching || ownedTokensQuery.isFetching;
|
||||
const setSearch = useCallback((search: string) => {
|
||||
_setSearch(search);
|
||||
setAllTokensLimit(ALL_TOKENS_LIMIT);
|
||||
setOwnedTokensLimit(OWNED_TOKENS_LIMIT);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SelectTokenUI
|
||||
{...props}
|
||||
ownedTokens={filteredOwnedTokens || []}
|
||||
allTokens={tokensQuery.data || []}
|
||||
isFetching={isFetching}
|
||||
ownedTokens={{
|
||||
data: filteredOwnedTokens || [],
|
||||
isFetching: ownedTokensQuery.isFetching,
|
||||
showMore: ownedTokensQuery.data?.pagination.hasMore
|
||||
? () => {
|
||||
setOwnedTokensLimit(ownedTokensLimit + OWNED_TOKENS_LIMIT);
|
||||
}
|
||||
: undefined,
|
||||
}}
|
||||
allTokens={{
|
||||
data: tokensQuery.data || [],
|
||||
isFetching: tokensQuery.isFetching,
|
||||
showMore:
|
||||
tokensQuery.data?.length === allTokensLimit
|
||||
? () => {
|
||||
setAllTokensLimit(allTokensLimit + ALL_TOKENS_LIMIT);
|
||||
}
|
||||
: undefined,
|
||||
}}
|
||||
selectedChain={selectedChain}
|
||||
setSelectedChain={setSelectedChain}
|
||||
search={search}
|
||||
setSearch={setSearch}
|
||||
selectedToken={props.selectedToken}
|
||||
setSelectedToken={props.setSelectedToken}
|
||||
showMore={
|
||||
tokensQuery.data?.length === limit
|
||||
? () => {
|
||||
setLimit(limit * 2);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SelectTokenUI(
|
||||
props: SelectTokenUIProps & {
|
||||
ownedTokens: TokenBalance[];
|
||||
allTokens: Token[];
|
||||
isFetching: boolean;
|
||||
allTokens: {
|
||||
data: Token[];
|
||||
isFetching: boolean;
|
||||
showMore: (() => void) | undefined;
|
||||
};
|
||||
ownedTokens: {
|
||||
data: TokenBalance[];
|
||||
isFetching: boolean;
|
||||
showMore: (() => void) | undefined;
|
||||
};
|
||||
selectedChain: BridgeChain | undefined;
|
||||
setSelectedChain: (chain: BridgeChain) => void;
|
||||
search: string;
|
||||
setSearch: (search: string) => void;
|
||||
selectedToken: TokenSelection | undefined;
|
||||
setSelectedToken: (token: TokenSelection) => void;
|
||||
showMore: (() => void) | undefined;
|
||||
},
|
||||
) {
|
||||
const isMobile = useIsMobile();
|
||||
@@ -148,7 +169,7 @@ function SelectTokenUI(
|
||||
|
||||
// show tokens with icons first
|
||||
const sortedOwnedTokens = useMemo(() => {
|
||||
return props.ownedTokens.sort((a, b) => {
|
||||
return props.ownedTokens.data.sort((a, b) => {
|
||||
if (a.icon_uri && !b.icon_uri) {
|
||||
return -1;
|
||||
}
|
||||
@@ -157,23 +178,11 @@ function SelectTokenUI(
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}, [props.ownedTokens]);
|
||||
|
||||
const otherTokens = useMemo(() => {
|
||||
const ownedTokenSet = new Set(
|
||||
sortedOwnedTokens.map((t) =>
|
||||
`${t.token_address}-${t.chain_id}`.toLowerCase(),
|
||||
),
|
||||
);
|
||||
return props.allTokens.filter(
|
||||
(token) =>
|
||||
!ownedTokenSet.has(`${token.address}-${token.chainId}`.toLowerCase()),
|
||||
);
|
||||
}, [props.allTokens, sortedOwnedTokens]);
|
||||
}, [props.ownedTokens.data]);
|
||||
|
||||
// show tokens with icons first
|
||||
const sortedOtherTokens = useMemo(() => {
|
||||
return otherTokens.sort((a, b) => {
|
||||
const sortedAllTokens = useMemo(() => {
|
||||
return props.allTokens.data.sort((a, b) => {
|
||||
if (a.iconUri && !b.iconUri) {
|
||||
return -1;
|
||||
}
|
||||
@@ -182,7 +191,7 @@ function SelectTokenUI(
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}, [otherTokens]);
|
||||
}, [props.allTokens.data]);
|
||||
|
||||
// desktop
|
||||
if (!isMobile) {
|
||||
@@ -214,15 +223,20 @@ function SelectTokenUI(
|
||||
}}
|
||||
isMobile={false}
|
||||
selectedToken={props.selectedToken}
|
||||
isFetching={props.isFetching}
|
||||
ownedTokens={props.ownedTokens}
|
||||
otherTokens={sortedOtherTokens}
|
||||
showMore={props.showMore}
|
||||
ownedTokens={{
|
||||
...props.ownedTokens,
|
||||
data: sortedOwnedTokens,
|
||||
}}
|
||||
allTokens={{
|
||||
...props.allTokens,
|
||||
data: sortedAllTokens,
|
||||
}}
|
||||
selectedChain={props.selectedChain}
|
||||
onSelectChain={() => setScreen("select-chain")}
|
||||
client={props.client}
|
||||
search={props.search}
|
||||
setSearch={props.setSearch}
|
||||
isWalletConnected={props.activeWalletInfo !== undefined}
|
||||
/>
|
||||
</Container>
|
||||
</Container>
|
||||
@@ -232,15 +246,20 @@ function SelectTokenUI(
|
||||
if (screen === "select-token") {
|
||||
return (
|
||||
<TokenSelectionScreen
|
||||
isWalletConnected={props.activeWalletInfo !== undefined}
|
||||
onSelectToken={(token) => {
|
||||
props.setSelectedToken(token);
|
||||
props.onClose();
|
||||
}}
|
||||
selectedToken={props.selectedToken}
|
||||
isFetching={props.isFetching}
|
||||
ownedTokens={props.ownedTokens}
|
||||
otherTokens={sortedOtherTokens}
|
||||
showMore={props.showMore}
|
||||
ownedTokens={{
|
||||
...props.ownedTokens,
|
||||
data: sortedOwnedTokens,
|
||||
}}
|
||||
allTokens={{
|
||||
...props.allTokens,
|
||||
data: sortedAllTokens,
|
||||
}}
|
||||
selectedChain={props.selectedChain}
|
||||
isMobile={true}
|
||||
onSelectChain={() => setScreen("select-chain")}
|
||||
@@ -428,17 +447,36 @@ function TokenSelectionScreen(props: {
|
||||
client: ThirdwebClient;
|
||||
search: string;
|
||||
setSearch: (search: string) => void;
|
||||
isFetching: boolean;
|
||||
ownedTokens: TokenBalance[];
|
||||
otherTokens: Token[];
|
||||
showMore: (() => void) | undefined;
|
||||
selectedToken: TokenSelection | undefined;
|
||||
onSelectToken: (token: TokenSelection) => void;
|
||||
isWalletConnected: boolean;
|
||||
allTokens: {
|
||||
data: Token[];
|
||||
isFetching: boolean;
|
||||
showMore: (() => void) | undefined;
|
||||
};
|
||||
ownedTokens: {
|
||||
data: TokenBalance[];
|
||||
isFetching: boolean;
|
||||
showMore: (() => void) | undefined;
|
||||
};
|
||||
}) {
|
||||
const noTokensFound =
|
||||
!props.isFetching &&
|
||||
props.otherTokens.length === 0 &&
|
||||
props.ownedTokens.length === 0;
|
||||
const noAllTokensFound =
|
||||
!props.allTokens.isFetching && props.allTokens.data.length === 0;
|
||||
|
||||
const noOwnedTokensFound =
|
||||
!props.ownedTokens.isFetching && props.ownedTokens.data.length === 0;
|
||||
|
||||
const [activeTab, setActiveTab] = useState<"owned" | "all">("all");
|
||||
const showSkeletons =
|
||||
(props.allTokens.isFetching && activeTab === "all") ||
|
||||
(props.ownedTokens.isFetching && activeTab === "owned");
|
||||
|
||||
const showNoTokensFound =
|
||||
(activeTab === "all" && noAllTokensFound) ||
|
||||
(activeTab === "owned" && noOwnedTokensFound);
|
||||
|
||||
const theme = useCustomTheme();
|
||||
|
||||
return (
|
||||
<Container fullHeight flex="column">
|
||||
@@ -448,16 +486,14 @@ function TokenSelectionScreen(props: {
|
||||
</Text>
|
||||
<Spacer y="3xs" />
|
||||
<Text
|
||||
size="xs"
|
||||
size="sm"
|
||||
color="secondaryText"
|
||||
multiline
|
||||
style={{
|
||||
textWrap: "pretty",
|
||||
maxWidth: "70%",
|
||||
}}
|
||||
>
|
||||
Select a token from the list or search for a token by symbol or
|
||||
address
|
||||
Select a token from the list or use the search
|
||||
</Text>
|
||||
</Container>
|
||||
|
||||
@@ -467,7 +503,7 @@ function TokenSelectionScreen(props: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
minHeight: "400px",
|
||||
minHeight: "300px",
|
||||
}}
|
||||
>
|
||||
<Spinner color="secondaryText" size="xl" />
|
||||
@@ -498,7 +534,28 @@ function TokenSelectionScreen(props: {
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<Spacer y="xs" />
|
||||
<Spacer y="sm" />
|
||||
|
||||
{/* tabs */}
|
||||
{props.isWalletConnected && (
|
||||
<Container
|
||||
flex="row"
|
||||
gap="xs"
|
||||
px="md"
|
||||
style={{ borderBottom: `1px solid ${theme.colors.borderColor}` }}
|
||||
>
|
||||
<TabButton
|
||||
isSelected={activeTab === "all"}
|
||||
onSelect={() => setActiveTab("all")}
|
||||
label="All Tokens"
|
||||
/>
|
||||
<TabButton
|
||||
isSelected={activeTab === "owned"}
|
||||
onSelect={() => setActiveTab("owned")}
|
||||
label="Your Tokens"
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
<Container
|
||||
pb="md"
|
||||
@@ -507,43 +564,24 @@ function TokenSelectionScreen(props: {
|
||||
gap="xxs"
|
||||
flex="column"
|
||||
style={{
|
||||
minHeight: "400px",
|
||||
minHeight: "300px",
|
||||
maxHeight: props.isMobile ? "450px" : "none",
|
||||
overflowY: "auto",
|
||||
scrollbarWidth: "none",
|
||||
paddingBottom: spacing.md,
|
||||
}}
|
||||
>
|
||||
{props.isFetching &&
|
||||
{props.isWalletConnected && <Spacer y="xxs" />}
|
||||
|
||||
{showSkeletons &&
|
||||
new Array(20).fill(0).map((_, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: ok
|
||||
<TokenButtonSkeleton key={i} />
|
||||
))}
|
||||
|
||||
{!props.isFetching && props.ownedTokens.length > 0 && (
|
||||
<Container
|
||||
px="xs"
|
||||
py="xs"
|
||||
flex="row"
|
||||
gap="xs"
|
||||
center="y"
|
||||
color="secondaryText"
|
||||
>
|
||||
<WalletDotIcon size={iconSize.xs} />
|
||||
<Text
|
||||
size="sm"
|
||||
color="secondaryText"
|
||||
style={{
|
||||
overflow: "unset",
|
||||
}}
|
||||
>
|
||||
Your Tokens
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{!props.isFetching &&
|
||||
props.ownedTokens.map((token) => (
|
||||
{activeTab === "owned" &&
|
||||
!props.ownedTokens.isFetching &&
|
||||
props.ownedTokens.data.map((token) => (
|
||||
<TokenButton
|
||||
key={token.token_address}
|
||||
token={token}
|
||||
@@ -558,33 +596,9 @@ function TokenSelectionScreen(props: {
|
||||
/>
|
||||
))}
|
||||
|
||||
{!props.isFetching && props.ownedTokens.length > 0 && (
|
||||
<Container
|
||||
px="xs"
|
||||
py="xs"
|
||||
flex="row"
|
||||
gap="xs"
|
||||
center="y"
|
||||
color="secondaryText"
|
||||
style={{
|
||||
marginTop: spacing.sm,
|
||||
}}
|
||||
>
|
||||
<CoinsIcon size={iconSize.xs} />
|
||||
<Text
|
||||
size="sm"
|
||||
color="secondaryText"
|
||||
style={{
|
||||
overflow: "unset",
|
||||
}}
|
||||
>
|
||||
Other Tokens
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{!props.isFetching &&
|
||||
props.otherTokens.map((token) => (
|
||||
{activeTab === "all" &&
|
||||
!props.allTokens.isFetching &&
|
||||
props.allTokens.data.map((token) => (
|
||||
<TokenButton
|
||||
key={token.address}
|
||||
token={token}
|
||||
@@ -599,19 +613,31 @@ function TokenSelectionScreen(props: {
|
||||
/>
|
||||
))}
|
||||
|
||||
{props.showMore && (
|
||||
{activeTab === "all" && props.allTokens.showMore && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
props.showMore?.();
|
||||
props.allTokens.showMore?.();
|
||||
}}
|
||||
>
|
||||
Load More
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{noTokensFound && (
|
||||
{activeTab === "owned" && props.ownedTokens.showMore && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
props.ownedTokens.showMore?.();
|
||||
}}
|
||||
>
|
||||
Load More
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showNoTokensFound && (
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
@@ -620,7 +646,7 @@ function TokenSelectionScreen(props: {
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text size="md" color="secondaryText">
|
||||
<Text size="sm" color="secondaryText">
|
||||
No Tokens Found
|
||||
</Text>
|
||||
</div>
|
||||
@@ -643,3 +669,45 @@ const LeftContainer = /* @__PURE__ */ StyledDiv((_) => {
|
||||
position: "relative",
|
||||
};
|
||||
});
|
||||
|
||||
function TabButton(props: {
|
||||
isSelected: boolean;
|
||||
onSelect: () => void;
|
||||
label: string;
|
||||
}) {
|
||||
const theme = useCustomTheme();
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: "4px",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="ghost-solid"
|
||||
onClick={props.onSelect}
|
||||
style={{
|
||||
fontSize: fontSize.sm,
|
||||
padding: `10px ${spacing.xs}`,
|
||||
color: props.isSelected
|
||||
? theme.colors.primaryText
|
||||
: theme.colors.secondaryText,
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</Button>
|
||||
|
||||
{props.isSelected && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: "-1.5px",
|
||||
borderBottom: `2px solid ${theme.colors.primaryText}`,
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,17 +46,19 @@ export function WalletRow(props: {
|
||||
const addressOrENS = address
|
||||
? ensNameQuery.data || shortenAddress(address)
|
||||
: "";
|
||||
|
||||
const iconSizeValue = iconSize[props.iconSize || "md"];
|
||||
return (
|
||||
<Container flex="row" style={{ justifyContent: "space-between" }}>
|
||||
<Container center="y" color="secondaryText" flex="row" gap="md">
|
||||
<Container center="y" color="secondaryText" flex="row" gap="sm">
|
||||
{wallet ? (
|
||||
<WalletImage
|
||||
client={props.client}
|
||||
id={wallet.id}
|
||||
size={iconSize[props.iconSize || "md"]}
|
||||
size={iconSizeValue}
|
||||
/>
|
||||
) : (
|
||||
<OutlineWalletIcon size={iconSize[props.iconSize || "md"]} />
|
||||
<OutlineWalletIcon size={iconSizeValue} />
|
||||
)}
|
||||
<Container flex="column" gap="3xs">
|
||||
{props.label ? (
|
||||
|
||||
@@ -49,7 +49,7 @@ function formatMoney(
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: "currency",
|
||||
currency: currencyCode,
|
||||
maximumFractionDigits: 5,
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 0,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
import { CheckIcon, CopyIcon as CopyIconSVG } from "@radix-ui/react-icons";
|
||||
import { radius } from "../../../core/design-system/index.js";
|
||||
import { useClipboard } from "../hooks/useCopyClipboard.js";
|
||||
import { Container } from "./basic.js";
|
||||
import { Button } from "./buttons.js";
|
||||
import { ToolTip } from "./Tooltip.js";
|
||||
|
||||
/**
|
||||
@@ -19,25 +21,22 @@ export const CopyIcon: React.FC<{
|
||||
const showCheckIcon = props.hasCopied || hasCopied;
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: TODO
|
||||
<div
|
||||
<Button
|
||||
onClick={onCopy}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
onCopy();
|
||||
}
|
||||
}}
|
||||
variant="ghost-solid"
|
||||
style={{
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
padding: 2,
|
||||
justifyContent: "center",
|
||||
borderRadius: radius.sm,
|
||||
}}
|
||||
>
|
||||
<ToolTip align={props.align} side={props.side} tip={props.tip}>
|
||||
<div>
|
||||
<Container
|
||||
center="both"
|
||||
color={showCheckIcon ? "success" : undefined}
|
||||
color={showCheckIcon ? "success" : "secondaryText"}
|
||||
flex="row"
|
||||
>
|
||||
{showCheckIcon ? (
|
||||
@@ -56,6 +55,6 @@ export const CopyIcon: React.FC<{
|
||||
</Container>
|
||||
</div>
|
||||
</ToolTip>
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ export const Spacer: React.FC<{ y: keyof typeof spacing }> = ({ y }) => {
|
||||
<div
|
||||
style={{
|
||||
height: spacing[y],
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -119,6 +119,7 @@ export const ConnectWalletSocialOptions = (
|
||||
twitch: "Twitch",
|
||||
x: "X",
|
||||
tiktok: "TikTok",
|
||||
epic: "Epic",
|
||||
};
|
||||
|
||||
const { data: ecosystemAuthOptions, isLoading } = useQuery({
|
||||
|
||||
@@ -44,6 +44,7 @@ function getOauthLoginPath(
|
||||
case "line":
|
||||
case "x":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
case "guest":
|
||||
case "coinbase":
|
||||
case "discord":
|
||||
|
||||
@@ -89,6 +89,7 @@ export function AllInAppWalletAuthMethods() {
|
||||
"discord",
|
||||
"x",
|
||||
"tiktok",
|
||||
"epic",
|
||||
"coinbase",
|
||||
"farcaster",
|
||||
"telegram",
|
||||
|
||||
@@ -103,6 +103,7 @@ export type AuthProvider =
|
||||
| "Coinbase"
|
||||
| "X"
|
||||
| "TikTok"
|
||||
| "Epic"
|
||||
| "Line"
|
||||
| "Twitch"
|
||||
| "Steam"
|
||||
|
||||
@@ -200,6 +200,7 @@ export class InAppNativeConnector implements InAppConnector {
|
||||
case "line":
|
||||
case "x":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
case "apple": {
|
||||
const { socialAuth } = await import("./auth/native-auth.js");
|
||||
const ExpoLinking = require("expo-linking");
|
||||
|
||||
@@ -338,6 +338,7 @@ export class InAppWebConnector implements InAppConnector {
|
||||
case "line":
|
||||
case "x":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
case "steam":
|
||||
case "coinbase":
|
||||
case "discord": {
|
||||
@@ -412,6 +413,7 @@ export class InAppWebConnector implements InAppConnector {
|
||||
case "line":
|
||||
case "x":
|
||||
case "tiktok":
|
||||
case "epic":
|
||||
case "guest":
|
||||
case "coinbase":
|
||||
case "twitch":
|
||||
|
||||
@@ -27,6 +27,7 @@ export const socialAuthOptions = [
|
||||
"line",
|
||||
"x",
|
||||
"tiktok",
|
||||
"epic",
|
||||
"coinbase",
|
||||
"farcaster",
|
||||
"telegram",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @thirdweb-dev/wagmi-adapter
|
||||
|
||||
## 0.2.185
|
||||
|
||||
## 0.2.184
|
||||
|
||||
## 0.2.183
|
||||
|
||||
## 0.2.182
|
||||
|
||||
## 0.2.181
|
||||
|
||||
## 0.2.180
|
||||
|
||||
@@ -56,5 +56,5 @@
|
||||
"type": "module",
|
||||
"types": "./dist/types/exports/thirdweb.d.ts",
|
||||
"typings": "./dist/types/exports/thirdweb.d.ts",
|
||||
"version": "0.2.181"
|
||||
"version": "0.2.185"
|
||||
}
|
||||
|
||||
Generated
+71
-100
@@ -1317,9 +1317,9 @@ importers:
|
||||
version: 1.1.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
'@mobile-wallet-protocol/client':
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0(owhh2bkjdc6c7756g5hiphjqv4)
|
||||
version: 1.0.0(bg6kixppm6cc5zeazzvnc2tera)
|
||||
'@react-native-async-storage/async-storage':
|
||||
specifier: ^1 || ^2
|
||||
specifier: '*'
|
||||
version: 2.2.0([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
'@react-native-community/netinfo':
|
||||
specifier: ^11
|
||||
@@ -1328,14 +1328,14 @@ importers:
|
||||
specifier: ^2
|
||||
version: 2.17.3(rbspwdd7ndsri6hcpfkczthinq)
|
||||
expo-application:
|
||||
specifier: ^5 || ^6
|
||||
specifier: '*'
|
||||
version: 6.0.1([email protected](@babel/[email protected])([email protected])([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]))
|
||||
expo-linking:
|
||||
specifier: ^6 || ^7
|
||||
version: 7.0.5([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
specifier: '*'
|
||||
version: 8.0.8([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
expo-web-browser:
|
||||
specifier: ^13 || ^14
|
||||
version: 14.0.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
specifier: '*'
|
||||
version: 15.0.9([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
react-native:
|
||||
specifier: '>=0.70'
|
||||
version: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
@@ -1512,7 +1512,7 @@ importers:
|
||||
version: 1.1.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
'@mobile-wallet-protocol/client':
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0(owhh2bkjdc6c7756g5hiphjqv4)
|
||||
version: 1.0.0(bg6kixppm6cc5zeazzvnc2tera)
|
||||
'@react-native-async-storage/async-storage':
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
@@ -1580,11 +1580,11 @@ importers:
|
||||
specifier: npm:ethers@6
|
||||
version: [email protected]([email protected])([email protected])
|
||||
expo-linking:
|
||||
specifier: 7.0.5
|
||||
version: 7.0.5([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
specifier: 8.0.8
|
||||
version: 8.0.8([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
|
||||
expo-web-browser:
|
||||
specifier: 14.0.2
|
||||
version: 14.0.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
specifier: 15.0.9
|
||||
version: 15.0.9([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
happy-dom:
|
||||
specifier: 17.4.4
|
||||
version: 17.4.4
|
||||
@@ -3473,30 +3473,30 @@ packages:
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-IMYCxBOcnuFStuK0Ay+FzEIBKrwW8OVUMc65+v0+i7YFIIe8aL342l7T4F8lR4oCfhXn7d6M5QPgXvjtc/gAcw==}
|
||||
|
||||
'@expo/config-plugins@9.0.17':
|
||||
resolution: {integrity: sha512-m24F1COquwOm7PBl5wRbkT9P9DviCXe0D7S7nQsolfbhdCWuvMkfXeoWmgjtdhy7sDlOyIgBrAdnB6MfsWKqIg==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==}
|
||||
'@expo/config-plugins@54.0.2':
|
||||
resolution: {integrity: sha512-jD4qxFcURQUVsUFGMcbo63a/AnviK8WUGard+yrdQE3ZrB/aurn68SlApjirQQLEizhjI5Ar2ufqflOBlNpyPg==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-kqZ0w44E+HEGBjy+Lpyn0BVL5UANg/tmNixxaRMLS6nf37YsDrLk2VMAmeKMMk5CKG0NmOdVv3ngeUjRQMsy9g==}
|
||||
|
||||
'@expo/config@10.0.11':
|
||||
resolution: {integrity: sha512-nociJ4zr/NmbVfMNe9j/+zRlt7wz/siISu7PjdWE4WE+elEGxWWxsGzltdJG0llzrM+khx8qUiFK5aiVcdMBww==}
|
||||
'@expo/config-types@54.0.8':
|
||||
resolution: {integrity: sha512-lyIn/x/Yz0SgHL7IGWtgTLg6TJWC9vL7489++0hzCHZ4iGjVcfZmPTUfiragZ3HycFFj899qN0jlhl49IHa94A==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-TnGb4u/zUZetpav9sx/3fWK71oCPaOjZHoVED9NaEncktAd0Eonhq5NUghiJmkUGt3gGSjRAEBXiBbbY9/B1LA==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-lJMof5Nqakq1DxGYlghYB/ogSBjmv4Fxn1ovyDmcjlRsQdFCXgu06gEUogkhPtc9wBt9WlTTfqENln5HHyLW6w==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-Uilcv3xGELD5t/b0eM4cxBFEKQRIivB3v7i+VhWLV/gL98aw810unLKKJbGAxAIhY6Ipyz8ChWibFsKFXYwstA==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-TgbCgvSk0Kq0e2fLoqHwEBL4M0ztFjnBEz0YCDm5boc1nvkV1VMuIMteVdeBwnTh8Z0oPJTwHCD49vhMEt1I6A==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-qSTEnwvuYJ3umapO9XJtrb1fAqiPlmUUg78N0IZXXGwQRt+bkp0OBls+Y5Mxw/Owj8waAM0Z3huKKskRADR5ow==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-MYfPYBTMfrrNr07DALuLhG6EaLVNVrY/PXjEzsjWdWE4ZFn0yqI0IdHNkJG7t1gePT8iztHc7qnsx+oo/rDo6w==}
|
||||
hasBin: true
|
||||
@@ -3504,8 +3504,8 @@ packages:
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-GKnMqC79+mo/1AFrmAcUcGfbsXXTRqOMNS1umebuevl3aaw+ztsYEFEiuNhHZW7PQ3Xs3URNT513ZxKhznDscw==}
|
||||
|
||||
'@expo/json-file@9.0.2':
|
||||
resolution: {integrity: sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==}
|
||||
'@expo/json-file@10.0.7':
|
||||
resolution: {integrity: sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==}
|
||||
@@ -3520,12 +3520,12 @@ packages:
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-gcdICLuL+nHKZagPIDC5tX8UoDDB8vNA5/+SaQEqz8D+T2C4KrEJc2Vi1gPAlDnKif834QS6YluHWyxjk0yZlQ==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-9RYVU1iGyCJ7vWfg3e7c/NVyMFs8wbl+dMWZphtFtsqyN9zppGREU3ctlD3i8KUE0sCUTVnLjCWr+VeUIDep2g==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA==}
|
||||
|
||||
'@expo/[email protected]':
|
||||
resolution: {integrity: sha512-0DsxhhixRbCCvmYskBTq8czsU0YOBsntYURhWPNpkl0IPVpeP9haE5W4OwtHGzXEbmHdzaoDwNmVcWjS/mqbDw==}
|
||||
|
||||
@@ -8359,11 +8359,6 @@ packages:
|
||||
'@webassemblyjs/[email protected]':
|
||||
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
|
||||
|
||||
'@xmldom/[email protected]':
|
||||
resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
deprecated: this version is no longer supported, please update to at least 0.8.*
|
||||
|
||||
'@xmldom/[email protected]':
|
||||
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -10561,14 +10556,14 @@ packages:
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
expo-constants@17.0.8:
|
||||
resolution: {integrity: sha512-XfWRyQAf1yUNgWZ1TnE8pFBMqGmFP5Gb+SFSgszxDdOoheB/NI5D4p7q86kI2fvGyfTrxAe+D+74nZkfsGvUlg==}
|
||||
expo-constants@17.1.7:
|
||||
resolution: {integrity: sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA==}
|
||||
peerDependencies:
|
||||
expo: '*'
|
||||
react-native: '*'
|
||||
|
||||
expo-constants@17.1.7:
|
||||
resolution: {integrity: sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA==}
|
||||
expo-constants@18.0.10:
|
||||
resolution: {integrity: sha512-Rhtv+X974k0Cahmvx6p7ER5+pNhBC0XbP1lRviL2J1Xl4sT2FBaIuIxF/0I0CbhOsySf0ksqc5caFweAy9Ewiw==}
|
||||
peerDependencies:
|
||||
expo: '*'
|
||||
react-native: '*'
|
||||
@@ -10591,8 +10586,8 @@ packages:
|
||||
expo: '*'
|
||||
react: '*'
|
||||
|
||||
expo-linking@7.0.5:
|
||||
resolution: {integrity: sha512-3KptlJtcYDPWohk0MfJU75MJFh2ybavbtcSd84zEPfw9s1q3hjimw3sXnH03ZxP54kiEWldvKmmnGcVffBDB1g==}
|
||||
expo-linking@8.0.8:
|
||||
resolution: {integrity: sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
@@ -10604,8 +10599,8 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-RCb0wniYCJkxwpXrkiBA/WiNGxzYsEpL0sB50gTnS/zEfX3DImS2npc4lfZ3hPZo1UF9YC6OSI9Do+iacV0NUg==}
|
||||
|
||||
expo-web-browser@14.0.2:
|
||||
resolution: {integrity: sha512-Hncv2yojhTpHbP6SGWARBFdl7P6wBHc1O8IKaNsH0a/IEakq887o1eRhLxZ5IwztPQyRDhpqHdgJ+BjWolOnwA==}
|
||||
expo-web-browser@15.0.9:
|
||||
resolution: {integrity: sha512-Dj8kNFO+oXsxqCDNlUT/GhOrJnm10kAElH++3RplLydogFm5jTzXYWDEeNIDmV+F+BzGYs+sIhxiBf7RyaxXZg==}
|
||||
peerDependencies:
|
||||
expo: '*'
|
||||
react-native: '*'
|
||||
@@ -11026,10 +11021,6 @@ packages:
|
||||
resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -16378,9 +16369,6 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
@@ -16481,10 +16469,6 @@ packages:
|
||||
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
|
||||
engines: {node: '>=8.0'}
|
||||
@@ -19715,15 +19699,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/config-plugins@9.0.17':
|
||||
'@expo/config-plugins@54.0.2':
|
||||
dependencies:
|
||||
'@expo/config-types': 52.0.5
|
||||
'@expo/json-file': 9.0.2
|
||||
'@expo/plist': 0.2.2
|
||||
'@expo/config-types': 54.0.8
|
||||
'@expo/json-file': 10.0.7
|
||||
'@expo/plist': 0.4.7
|
||||
'@expo/sdk-runtime-versions': 1.0.0
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
getenv: 1.0.0
|
||||
getenv: 2.0.0
|
||||
glob: 10.4.5
|
||||
resolve-from: 5.0.0
|
||||
semver: 7.7.2
|
||||
@@ -19734,18 +19718,18 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/[email protected]': {}
|
||||
|
||||
'@expo/[email protected]': {}
|
||||
|
||||
'@expo/config@10.0.11':
|
||||
'@expo/config[email protected]': {}
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.10.4
|
||||
'@expo/config-plugins': 9.0.17
|
||||
'@expo/config-types': 52.0.5
|
||||
'@expo/config-plugins': 10.1.2
|
||||
'@expo/config-types': 53.0.5
|
||||
'@expo/json-file': 9.1.5
|
||||
deepmerge: 4.3.1
|
||||
getenv: 1.0.0
|
||||
getenv: 2.0.0
|
||||
glob: 10.4.5
|
||||
require-from-string: 2.0.2
|
||||
resolve-from: 5.0.0
|
||||
@@ -19756,12 +19740,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/config@11.0.13':
|
||||
'@expo/config@12.0.10':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.10.4
|
||||
'@expo/config-plugins': 10.1.2
|
||||
'@expo/config-types': 53.0.5
|
||||
'@expo/json-file': 9.1.5
|
||||
'@expo/config-plugins': 54.0.2
|
||||
'@expo/config-types': 54.0.8
|
||||
'@expo/json-file': 10.0.7
|
||||
deepmerge: 4.3.1
|
||||
getenv: 2.0.0
|
||||
glob: 10.4.5
|
||||
@@ -19782,17 +19766,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/env@0.4.2':
|
||||
'@expo/env@1.0.7':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
dotenv: 16.4.7
|
||||
dotenv-expand: 11.0.7
|
||||
getenv: 1.0.0
|
||||
getenv: 2.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@expo/env@1.0.7':
|
||||
'@expo/env@2.0.7':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
@@ -19831,11 +19815,10 @@ snapshots:
|
||||
temp-dir: 2.0.0
|
||||
unique-string: 2.0.0
|
||||
|
||||
'@expo/json-file@9.0.2':
|
||||
'@expo/json-file@10.0.7':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.10.4
|
||||
json5: 2.2.3
|
||||
write-file-atomic: 2.4.3
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
@@ -19880,18 +19863,18 @@ snapshots:
|
||||
ora: 3.4.0
|
||||
resolve-workspace-root: 2.0.0
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.7.13
|
||||
base64-js: 1.5.1
|
||||
xmlbuilder: 14.0.0
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.8.10
|
||||
base64-js: 1.5.1
|
||||
xmlbuilder: 15.1.1
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
'@xmldom/xmldom': 0.8.10
|
||||
base64-js: 1.5.1
|
||||
xmlbuilder: 15.1.1
|
||||
|
||||
'@expo/[email protected]':
|
||||
dependencies:
|
||||
'@expo/config': 11.0.13
|
||||
@@ -20785,7 +20768,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@mobile-wallet-protocol/[email protected](owhh2bkjdc6c7756g5hiphjqv4)':
|
||||
'@mobile-wallet-protocol/[email protected](bg6kixppm6cc5zeazzvnc2tera)':
|
||||
dependencies:
|
||||
'@noble/ciphers': 0.5.3
|
||||
'@noble/curves': 1.8.2
|
||||
@@ -20793,7 +20776,7 @@ snapshots:
|
||||
'@react-native-async-storage/async-storage': 2.2.0([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
eventemitter3: 5.0.1
|
||||
expo: 53.0.17(@babel/[email protected])([email protected])([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])
|
||||
expo-web-browser: 14.0.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
expo-web-browser: 15.0.9([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
fflate: 0.8.2
|
||||
react: 19.1.0
|
||||
react-native: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
@@ -30650,8 +30633,6 @@ snapshots:
|
||||
'@webassemblyjs/ast': 1.14.1
|
||||
'@xtuc/long': 4.2.2
|
||||
|
||||
'@xmldom/[email protected]': {}
|
||||
|
||||
'@xmldom/[email protected]': {}
|
||||
|
||||
'@xtuc/[email protected]': {}
|
||||
@@ -33312,19 +33293,19 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
expo-constants@17.0.8([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
expo-constants@17.1.7([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
dependencies:
|
||||
'@expo/config': 10.0.11
|
||||
'@expo/env': 0.4.2
|
||||
'@expo/config': 11.0.13
|
||||
'@expo/env': 1.0.7
|
||||
expo: 53.0.17(@babel/[email protected])([email protected])([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])
|
||||
react-native: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
expo-constants@17.1.7([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
expo-constants@18.0.10([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
dependencies:
|
||||
'@expo/config': 11.0.13
|
||||
'@expo/env': 1.0.7
|
||||
'@expo/config': 12.0.10
|
||||
'@expo/env': 2.0.7
|
||||
expo: 53.0.17(@babel/[email protected])([email protected])([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])
|
||||
react-native: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
transitivePeerDependencies:
|
||||
@@ -33346,9 +33327,9 @@ snapshots:
|
||||
expo: 53.0.17(@babel/[email protected])([email protected])([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])
|
||||
react: 19.1.0
|
||||
|
||||
expo-linking@7.0.5([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected]):
|
||||
expo-linking@8.0.8([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected]):
|
||||
dependencies:
|
||||
expo-constants: 17.0.8([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
expo-constants: 18.0.10([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))
|
||||
invariant: 2.2.4
|
||||
react: 19.1.0
|
||||
react-native: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
@@ -33370,7 +33351,7 @@ snapshots:
|
||||
dependencies:
|
||||
invariant: 2.2.4
|
||||
|
||||
expo-web-browser@14.0.2([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
expo-web-browser@15.0.9([email protected](@babel/[email protected])([email protected])([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]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])):
|
||||
dependencies:
|
||||
expo: 53.0.17(@babel/[email protected])([email protected])([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])
|
||||
react-native: 0.78.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected])
|
||||
@@ -33831,8 +33812,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
@@ -41102,12 +41081,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
imurmurhash: 0.1.4
|
||||
signal-exit: 3.0.7
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
imurmurhash: 0.1.4
|
||||
@@ -41276,8 +41249,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
Reference in New Issue
Block a user