thirdweb 5.78.0 → 5.79.0-nightly-f91f6310e9396918d0ffc5217eeb4a44cef0b8c8-20241215000412
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/exports/react.js +8 -1
- package/dist/cjs/exports/react.js.map +1 -1
- package/dist/cjs/exports/wallets/in-app.js.map +1 -1
- package/dist/cjs/exports/wallets/in-app.native.js.map +1 -1
- package/dist/cjs/exports/wallets.js.map +1 -1
- package/dist/cjs/exports/wallets.native.js.map +1 -1
- package/dist/cjs/extensions/prebuilts/deploy-published.js +22 -16
- package/dist/cjs/extensions/prebuilts/deploy-published.js.map +1 -1
- package/dist/cjs/react/web/ui/MediaRenderer/useResolvedMediaType.js +1 -1
- package/dist/cjs/react/web/ui/MediaRenderer/useResolvedMediaType.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/Chain/icon.js +1 -1
- package/dist/cjs/react/web/ui/prebuilt/Chain/icon.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/Chain/name.js +15 -11
- package/dist/cjs/react/web/ui/prebuilt/Chain/name.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/Chain/provider.js +1 -0
- package/dist/cjs/react/web/ui/prebuilt/Chain/provider.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/Wallet/icon.js +79 -0
- package/dist/cjs/react/web/ui/prebuilt/Wallet/icon.js.map +1 -0
- package/dist/cjs/react/web/ui/prebuilt/Wallet/name.js +105 -0
- package/dist/cjs/react/web/ui/prebuilt/Wallet/name.js.map +1 -0
- package/dist/cjs/react/web/ui/prebuilt/Wallet/provider.js +47 -0
- package/dist/cjs/react/web/ui/prebuilt/Wallet/provider.js.map +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/esm/exports/react.js +4 -0
- package/dist/esm/exports/react.js.map +1 -1
- package/dist/esm/exports/wallets/in-app.js.map +1 -1
- package/dist/esm/exports/wallets/in-app.native.js.map +1 -1
- package/dist/esm/exports/wallets.js.map +1 -1
- package/dist/esm/exports/wallets.native.js.map +1 -1
- package/dist/esm/extensions/prebuilts/deploy-published.js +22 -16
- package/dist/esm/extensions/prebuilts/deploy-published.js.map +1 -1
- package/dist/esm/react/web/ui/MediaRenderer/useResolvedMediaType.js +1 -1
- package/dist/esm/react/web/ui/MediaRenderer/useResolvedMediaType.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/Chain/icon.js +1 -1
- package/dist/esm/react/web/ui/prebuilt/Chain/icon.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/Chain/name.js +14 -11
- package/dist/esm/react/web/ui/prebuilt/Chain/name.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/Chain/provider.js +1 -0
- package/dist/esm/react/web/ui/prebuilt/Chain/provider.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/Wallet/icon.js +75 -0
- package/dist/esm/react/web/ui/prebuilt/Wallet/icon.js.map +1 -0
- package/dist/esm/react/web/ui/prebuilt/Wallet/name.js +100 -0
- package/dist/esm/react/web/ui/prebuilt/Wallet/name.js.map +1 -0
- package/dist/esm/react/web/ui/prebuilt/Wallet/provider.js +42 -0
- package/dist/esm/react/web/ui/prebuilt/Wallet/provider.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/types/exports/react.d.ts +3 -0
- package/dist/types/exports/react.d.ts.map +1 -1
- package/dist/types/exports/wallets/in-app.d.ts +2 -1
- package/dist/types/exports/wallets/in-app.d.ts.map +1 -1
- package/dist/types/exports/wallets/in-app.native.d.ts +2 -1
- package/dist/types/exports/wallets/in-app.native.d.ts.map +1 -1
- package/dist/types/exports/wallets.d.ts +2 -1
- package/dist/types/exports/wallets.d.ts.map +1 -1
- package/dist/types/exports/wallets.native.d.ts +2 -1
- package/dist/types/exports/wallets.native.d.ts.map +1 -1
- package/dist/types/extensions/prebuilts/deploy-published.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/Chain/name.d.ts +10 -1
- package/dist/types/react/web/ui/prebuilt/Chain/name.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/Chain/provider.d.ts +1 -0
- package/dist/types/react/web/ui/prebuilt/Chain/provider.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/Wallet/icon.d.ts +81 -0
- package/dist/types/react/web/ui/prebuilt/Wallet/icon.d.ts.map +1 -0
- package/dist/types/react/web/ui/prebuilt/Wallet/name.d.ts +111 -0
- package/dist/types/react/web/ui/prebuilt/Wallet/name.d.ts.map +1 -0
- package/dist/types/react/web/ui/prebuilt/Wallet/provider.d.ts +41 -0
- package/dist/types/react/web/ui/prebuilt/Wallet/provider.d.ts.map +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.d.ts.map +1 -1
- package/dist/types/wallets/in-app/core/wallet/types.d.ts +3 -3
- package/dist/types/wallets/in-app/core/wallet/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/exports/react.ts +14 -0
- package/src/exports/wallets/in-app.native.ts +6 -0
- package/src/exports/wallets/in-app.ts +6 -0
- package/src/exports/wallets.native.ts +6 -0
- package/src/exports/wallets.ts +6 -0
- package/src/extensions/prebuilts/deploy-published.ts +27 -20
- package/src/react/core/hooks/wallets/useAddConnectedWallet.test.tsx +52 -0
- package/src/react/core/hooks/wallets/useConnect.test.tsx +105 -0
- package/src/react/core/hooks/wallets/useSetActiveWallet.test.tsx +53 -0
- package/src/react/web/ui/MediaRenderer/useResolvedMediaType.ts +1 -1
- package/src/react/web/ui/prebuilt/Chain/icon.tsx +1 -1
- package/src/react/web/ui/prebuilt/Chain/name.test.tsx +19 -1
- package/src/react/web/ui/prebuilt/Chain/name.tsx +19 -13
- package/src/react/web/ui/prebuilt/Chain/provider.tsx +1 -0
- package/src/react/web/ui/prebuilt/Wallet/icon.test.tsx +30 -0
- package/src/react/web/ui/prebuilt/Wallet/icon.tsx +120 -0
- package/src/react/web/ui/prebuilt/Wallet/name.test.tsx +55 -0
- package/src/react/web/ui/prebuilt/Wallet/name.tsx +164 -0
- package/src/react/web/ui/prebuilt/Wallet/provider.test.tsx +61 -0
- package/src/react/web/ui/prebuilt/Wallet/provider.tsx +65 -0
- package/src/version.ts +1 -1
- package/src/wallets/in-app/core/wallet/types.ts +7 -8
- package/src/wallets/manager/connection-manager.test.ts +133 -133
@@ -143,7 +143,7 @@ export function ChainIcon({
|
|
143
143
|
}
|
144
144
|
// Check if the chain object already has "icon"
|
145
145
|
if (chain.icon?.url) {
|
146
|
-
return chain.icon.url;
|
146
|
+
return resolveScheme({ uri: chain.icon.url, client });
|
147
147
|
}
|
148
148
|
const possibleUrl = await getChainMetadata(chain).then(
|
149
149
|
(data) => data.icon?.url,
|
@@ -2,7 +2,8 @@ import { describe, expect, it } from "vitest";
|
|
2
2
|
import { render, screen, waitFor } from "~test/react-render.js";
|
3
3
|
import { ethereum } from "../../../../../chains/chain-definitions/ethereum.js";
|
4
4
|
import { defineChain } from "../../../../../chains/utils.js";
|
5
|
-
import {
|
5
|
+
import { getFunctionId } from "../../../../../utils/function-id.js";
|
6
|
+
import { ChainName, fetchChainName, getQueryKeys } from "./name.js";
|
6
7
|
import { ChainProvider } from "./provider.js";
|
7
8
|
|
8
9
|
describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
|
@@ -97,4 +98,21 @@ describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
|
|
97
98
|
});
|
98
99
|
expect(res).toBe("eth_mainnet");
|
99
100
|
});
|
101
|
+
|
102
|
+
it("getQueryKeys should work without nameResolver", () => {
|
103
|
+
expect(getQueryKeys({ chainId: 1 })).toStrictEqual([
|
104
|
+
"_internal_chain_name_",
|
105
|
+
1,
|
106
|
+
]);
|
107
|
+
});
|
108
|
+
|
109
|
+
it("getQueryKeys should work WITH nameResolver", () => {
|
110
|
+
const nameResolver = () => "tw";
|
111
|
+
const fnId = getFunctionId(nameResolver);
|
112
|
+
expect(getQueryKeys({ chainId: 1, nameResolver })).toStrictEqual([
|
113
|
+
"_internal_chain_name_",
|
114
|
+
1,
|
115
|
+
{ resolver: fnId },
|
116
|
+
]);
|
117
|
+
});
|
100
118
|
});
|
@@ -48,7 +48,7 @@ export interface ChainNameProps
|
|
48
48
|
* name was not fetched succesfully
|
49
49
|
* @example
|
50
50
|
* ```tsx
|
51
|
-
* <ChainName fallbackComponent={
|
51
|
+
* <ChainName fallbackComponent={<span>Failed to load</span>}
|
52
52
|
* />
|
53
53
|
* ```
|
54
54
|
*/
|
@@ -157,18 +157,7 @@ export function ChainName({
|
|
157
157
|
}: ChainNameProps) {
|
158
158
|
const { chain } = useChainContext();
|
159
159
|
const nameQuery = useQuery({
|
160
|
-
queryKey:
|
161
|
-
"_internal_chain_name_",
|
162
|
-
chain.id,
|
163
|
-
{
|
164
|
-
resolver:
|
165
|
-
typeof nameResolver === "string"
|
166
|
-
? nameResolver
|
167
|
-
: typeof nameResolver === "function"
|
168
|
-
? getFunctionId(nameResolver)
|
169
|
-
: undefined,
|
170
|
-
},
|
171
|
-
] as const,
|
160
|
+
queryKey: getQueryKeys({ chainId: chain.id, nameResolver }),
|
172
161
|
queryFn: async () => fetchChainName({ chain, nameResolver }),
|
173
162
|
...queryOptions,
|
174
163
|
});
|
@@ -205,3 +194,20 @@ export async function fetchChainName(props: {
|
|
205
194
|
}
|
206
195
|
return getChainMetadata(chain).then((data) => data.name);
|
207
196
|
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* @internal Exported for tests
|
200
|
+
*/
|
201
|
+
export function getQueryKeys(props: {
|
202
|
+
chainId: number;
|
203
|
+
nameResolver?: string | (() => string) | (() => Promise<string>);
|
204
|
+
}) {
|
205
|
+
if (typeof props.nameResolver === "function") {
|
206
|
+
return [
|
207
|
+
"_internal_chain_name_",
|
208
|
+
props.chainId,
|
209
|
+
{ resolver: getFunctionId(props.nameResolver) },
|
210
|
+
] as const;
|
211
|
+
}
|
212
|
+
return ["_internal_chain_name_", props.chainId] as const;
|
213
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { render, waitFor } from "~test/react-render.js";
|
3
|
+
import { WalletIcon, fetchWalletImage } from "./icon.js";
|
4
|
+
import { WalletProvider } from "./provider.js";
|
5
|
+
|
6
|
+
describe("WalletIcon", () => {
|
7
|
+
it("should fetch wallet image", async () => {
|
8
|
+
expect(await fetchWalletImage({ id: "io.metamask" })).toBe(
|
9
|
+
"data:image/webp;base64,UklGRsgFAABXRUJQVlA4ILwFAAAwHgCdASqAAIAAPm0ylEWkIyIXTGzMQAbEoAzQSPDffG8qhx7LpfeDUfnDee/tP6gP2u9Yf0a/7L0/+pc3nivc0JlTXBvMVYW1Wt7JbdRJ1a5UhpH9+sdhAvGug20WzG+wRD+P/7/raQ1NqZ5EfcOwosQsP7NRVflPNArBLCbFJWE3EvTLNOmMfR2f+r+E2y6y1lWanc3n9RtF4lYbcW38t3UG/W2jXng4loXFOy3Vjm/r/1KHwXlpAuhAIzK/XF+ChayRFhBD+lbRrgQ3CSVjdmXw6agYPoMQ+tpjPUN/3qdGcvnN1sKWt57OGozQVSjMu0aA58MPKVWiAP77l9VfptKhZ7in3+r1DqZZQEYqO1p+MENah/WS/QQDvPuew8P0OXfahcjbCg7h4QEEEjDyfoFUbQHdw+meAsydryxVs/Ij+eB619Uj/sMgtwIifIyTieCT/hdsDmpLqGD+vOIArZfzSkordRkngojxVgvWRht84IaCHCs5Dn208UHFmyQE4KCxHef1iLYeAEPxnIAovGHIl7rCjiYE12obAM3ZCnt+RFqcT3q2rorCswc4/8f8TOhCSo63/dszpMkNQonGwtyhAFnm1EKqLPDcy99ggKQL95VYePoHcv8sHOG5zJ7lircX7VPpxkloNVWnlp/drJjQrp0h5BsOnqYn756+wcFw5qapAOnYCKUHujsoSz2BlLs1702rFpvi1iVczo2aO9GN1TaM3zBqMX0NN0YEU8pz/3+xwkK5M5q+Qb4FNJeugjdOp2kcYoOCbyAg/0OyGswYHPwDN/opVaDHWj0FcKzLq43uEjUZMG8t7O6BkoK0FmpYOeTQrinuF5F6W6ENly78daVmGPYTU22R9No5+xr8F2ESYTaJzOR3UoouY15xHrsxhDCukfebOiHljS6jUjF0TWaIAb58k57DZ3gdjkpxnJDyEmCsCOlASNWMn1ay3G8PDriQOdwsL7bgx/jZXN6XZHVx2hst/6Qcljnnn4um8eD1NcP1rTV7HMZzAb/d7ntki9zU1IFl695zPs3YT+h0PS4JuQYLzYoxpmMUQ35sVx/3KkL4Pm/TlvuNpnopM0+l2b/0OS6+5GwJYEMpzM4peYZS3qZkz/kxWNPTLv3UcCguDqpGxZ39l/VQJhSImKPZBa1RWCtSj29VCzCEZlX/eAdAKgZwhMKR4C5osznEbxjfRdHHPxjZwstCwugFa5w/WykOTFGAzk0sLSuvxO9kw/f++0WvG5La11GQaqbDDJ/ks7bcELqEGtvLHiJgxOXW9PqCk5Ap9EYelPTmdhHZgzWxtz4idN+JX+DGyYtJYjtW+Ay4kJ4Hol+Iavfje1f/22S69z1XSZ8OOcrDHKQdO1JlGaTkKQmCg5Tr05Qy9NQSfh67Vpui6OyMu38BnbG+cfWPRx/MjGQITY8w8sb1GUGd5hD9eVqycVtz8yFlYrXYQSBkAxMYgkBaiWe6kk24SoOCgt74nNs3pyTWUERw9ENESB6PyO9HjcsUZWIh97RKlf6thPtqxdlfr9i3McON9zvI7M2TtYcneXhOnqN8V4wqqII2J3DQ6/DZFNj5eNkCp2ijt7UWuKduhEmbZlLahfxD8eqBHAZR/H5rulzc4oVlyXr6qPXf9LCEuDRSDE8VNFY4NuTcaRTZO33RrWmWzbXAwpEKeH/Xf78XMoeynLBSyB+pS6y8Fqh7ExdmnvtW2gW3pwNrLc5lXlZJW8VcBzaSpy1Dmqj6Xll9BS8RyWqNx0O3fY8NJpf1exbNYMWA8juddzn2d9lHypEbiym0ASxI/jgGkbis5fecZ60QtJusJgdC8HAJEh64A9EuOCAnnISc5CElwpsvJTdEZhYv3t7MtLDvHp20lAtylt5l9yxqfCy/sqC6qZ/8+tsTAHzziGF6NKoaD0FVhZ2CER5AonJdBz5sg0rcD7arFe96uzujFDCQAAAA",
|
10
|
+
);
|
11
|
+
});
|
12
|
+
|
13
|
+
it("should throw error if WalletId is not supported", async () => {
|
14
|
+
await expect(() =>
|
15
|
+
// @ts-ignore For test
|
16
|
+
fetchWalletImage({ id: "__undefined__" }),
|
17
|
+
).rejects.toThrowError("Wallet with id __undefined__ not found");
|
18
|
+
});
|
19
|
+
|
20
|
+
it("should render an image", async () => {
|
21
|
+
const { container } = render(
|
22
|
+
<WalletProvider id="io.cosmostation">
|
23
|
+
<WalletIcon />
|
24
|
+
</WalletProvider>,
|
25
|
+
);
|
26
|
+
await waitFor(() => {
|
27
|
+
expect(container.querySelector("img")).not.toBe(null);
|
28
|
+
});
|
29
|
+
});
|
30
|
+
});
|
@@ -0,0 +1,120 @@
|
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
|
4
|
+
import type { JSX } from "react";
|
5
|
+
import { getWalletInfo } from "../../../../../wallets/__generated__/getWalletInfo.js";
|
6
|
+
import type { WalletId } from "../../../../../wallets/wallet-types.js";
|
7
|
+
import { useWalletContext } from "./provider.js";
|
8
|
+
|
9
|
+
export interface WalletIconProps
|
10
|
+
extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> {
|
11
|
+
/**
|
12
|
+
* This component will be shown while the icon of the wallet is being fetched
|
13
|
+
* If not passed, the component will return `null`.
|
14
|
+
*
|
15
|
+
* You can/should pass a loading sign or spinner to this prop.
|
16
|
+
* @example
|
17
|
+
* ```tsx
|
18
|
+
* <WalletIcon loadingComponent={<Spinner />} />
|
19
|
+
* ```
|
20
|
+
*/
|
21
|
+
loadingComponent?: JSX.Element;
|
22
|
+
/**
|
23
|
+
* This component will be shown if the icon fails to be retreived
|
24
|
+
* If not passed, the component will return `null`.
|
25
|
+
*
|
26
|
+
* You can/should pass a descriptive text/component to this prop, indicating that the
|
27
|
+
* icon was not fetched succesfully
|
28
|
+
* @example
|
29
|
+
* ```tsx
|
30
|
+
* <WalletIcon fallbackComponent={<span>Failed to load</span>}
|
31
|
+
* />
|
32
|
+
* ```
|
33
|
+
*/
|
34
|
+
fallbackComponent?: JSX.Element;
|
35
|
+
/**
|
36
|
+
* Optional `useQuery` params
|
37
|
+
*/
|
38
|
+
queryOptions?: Omit<UseQueryOptions<string>, "queryFn" | "queryKey">;
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* This component tries to resolve the icon of a given wallet, then return an image.
|
43
|
+
* @returns an <img /> with the src of the wallet icon
|
44
|
+
*
|
45
|
+
* @example
|
46
|
+
* ### Basic usage
|
47
|
+
* ```tsx
|
48
|
+
* import { WalletProvider, WalletIcon } from "thirdweb/react";
|
49
|
+
*
|
50
|
+
* <WalletProvider id="io.metamask">
|
51
|
+
* <WalletIcon />
|
52
|
+
* </WalletProvider>
|
53
|
+
* ```
|
54
|
+
*
|
55
|
+
* Result: An <img /> component with the src of the icon
|
56
|
+
* ```html
|
57
|
+
* <img src="metamask-icon.png" />
|
58
|
+
* ```
|
59
|
+
*
|
60
|
+
* ### Show a loading sign while the icon is being loaded
|
61
|
+
* ```tsx
|
62
|
+
* <WalletIcon loadingComponent={<Spinner />} />
|
63
|
+
* ```
|
64
|
+
*
|
65
|
+
* ### Fallback to a dummy image if the wallet icon fails to resolve
|
66
|
+
* ```tsx
|
67
|
+
* <WalletIcon fallbackComponent={<img src="blank-image.png" />} />
|
68
|
+
* ```
|
69
|
+
*
|
70
|
+
* ### Usage with queryOptions
|
71
|
+
* WalletIcon uses useQuery() from tanstack query internally.
|
72
|
+
* It allows you to pass a custom queryOptions of your choice for more control of the internal fetching logic
|
73
|
+
* ```tsx
|
74
|
+
* <WalletIcon queryOptions={{ enabled: someLogic, retry: 3, }} />
|
75
|
+
* ```
|
76
|
+
*
|
77
|
+
* @component
|
78
|
+
* @wallet
|
79
|
+
* @beta
|
80
|
+
*/
|
81
|
+
export function WalletIcon({
|
82
|
+
loadingComponent,
|
83
|
+
fallbackComponent,
|
84
|
+
queryOptions,
|
85
|
+
...restProps
|
86
|
+
}: WalletIconProps) {
|
87
|
+
const imageQuery = useWalletIcon({ queryOptions });
|
88
|
+
if (imageQuery.isLoading) {
|
89
|
+
return loadingComponent || null;
|
90
|
+
}
|
91
|
+
if (!imageQuery.data) {
|
92
|
+
return fallbackComponent || null;
|
93
|
+
}
|
94
|
+
return <img src={imageQuery.data} {...restProps} alt={restProps.alt} />;
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* @internal
|
99
|
+
*/
|
100
|
+
function useWalletIcon(props: {
|
101
|
+
queryOptions?: Omit<UseQueryOptions<string>, "queryFn" | "queryKey">;
|
102
|
+
}) {
|
103
|
+
const { id } = useWalletContext();
|
104
|
+
const imageQuery = useQuery({
|
105
|
+
queryKey: ["walletIcon", id],
|
106
|
+
queryFn: async () => fetchWalletImage({ id }),
|
107
|
+
...props.queryOptions,
|
108
|
+
});
|
109
|
+
return imageQuery;
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* @internal Exported for tests only
|
114
|
+
*/
|
115
|
+
export async function fetchWalletImage(props: {
|
116
|
+
id: WalletId;
|
117
|
+
}) {
|
118
|
+
const image_src = await getWalletInfo(props.id, true);
|
119
|
+
return image_src;
|
120
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { render, waitFor } from "~test/react-render.js";
|
3
|
+
import { getFunctionId } from "../../../../../utils/function-id.js";
|
4
|
+
import { WalletName, fetchWalletName, getQueryKeys } from "./name.js";
|
5
|
+
import { WalletProvider } from "./provider.js";
|
6
|
+
|
7
|
+
describe.runIf(process.env.TW_SECRET_KEY)("WalletName", () => {
|
8
|
+
it("fetchWalletName: should fetch wallet name from id", async () => {
|
9
|
+
const name = await fetchWalletName({ id: "io.metamask" });
|
10
|
+
expect(name).toBe("MetaMask");
|
11
|
+
});
|
12
|
+
|
13
|
+
it("fetchWalletName should throw error if failed to get name", async () => {
|
14
|
+
// @ts-ignore for test
|
15
|
+
await expect(() => fetchWalletName({ id: "test___" })).rejects.toThrowError(
|
16
|
+
"Wallet with id test___ not found",
|
17
|
+
);
|
18
|
+
});
|
19
|
+
|
20
|
+
it("fetchWalletName should work with formatFn", async () => {
|
21
|
+
const formatFn = (str: string) => `${str} Wallet`;
|
22
|
+
expect(await fetchWalletName({ id: "io.metamask", formatFn })).toBe(
|
23
|
+
"MetaMask Wallet",
|
24
|
+
);
|
25
|
+
});
|
26
|
+
|
27
|
+
it("getQueryKeys should work without a formatFn", () => {
|
28
|
+
expect(getQueryKeys({ id: "ai.hacken" })).toStrictEqual([
|
29
|
+
"walletName",
|
30
|
+
"ai.hacken",
|
31
|
+
]);
|
32
|
+
});
|
33
|
+
|
34
|
+
it("getQueryKeys should work WITH a formatFn", () => {
|
35
|
+
const fn = (str: string) => `test:${str}`;
|
36
|
+
const fnId = getFunctionId(fn);
|
37
|
+
expect(getQueryKeys({ id: "ai.hacken", formatFn: fn })).toStrictEqual([
|
38
|
+
"walletName",
|
39
|
+
"ai.hacken",
|
40
|
+
{ resolver: fnId },
|
41
|
+
]);
|
42
|
+
});
|
43
|
+
|
44
|
+
it("should render a span", async () => {
|
45
|
+
const { container } = render(
|
46
|
+
<WalletProvider id="io.metamask">
|
47
|
+
<WalletName />
|
48
|
+
</WalletProvider>,
|
49
|
+
);
|
50
|
+
|
51
|
+
await waitFor(() => {
|
52
|
+
expect(container.querySelector("span")).not.toBe(null);
|
53
|
+
});
|
54
|
+
});
|
55
|
+
});
|
@@ -0,0 +1,164 @@
|
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
|
4
|
+
import type { JSX } from "react";
|
5
|
+
import { getFunctionId } from "../../../../../utils/function-id.js";
|
6
|
+
import { getWalletInfo } from "../../../../../wallets/__generated__/getWalletInfo.js";
|
7
|
+
import type { WalletId } from "../../../../../wallets/wallet-types.js";
|
8
|
+
import { useWalletContext } from "./provider.js";
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Props for the WalletName component
|
12
|
+
* @component
|
13
|
+
* @wallet
|
14
|
+
*/
|
15
|
+
export interface WalletNameProps
|
16
|
+
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
17
|
+
/**
|
18
|
+
* This component will be shown while the name of the wallet name is being fetched
|
19
|
+
* If not passed, the component will return `null`.
|
20
|
+
*
|
21
|
+
* You can/should pass a loading sign or spinner to this prop.
|
22
|
+
* @example
|
23
|
+
* ```tsx
|
24
|
+
* <WalletName loadingComponent={<Spinner />} />
|
25
|
+
* ```
|
26
|
+
*/
|
27
|
+
loadingComponent?: JSX.Element;
|
28
|
+
/**
|
29
|
+
* This component will be shown if the name fails to be retreived
|
30
|
+
* If not passed, the component will return `null`.
|
31
|
+
*
|
32
|
+
* You can/should pass a descriptive text/component to this prop, indicating that the
|
33
|
+
* name was not fetched succesfully
|
34
|
+
* @example
|
35
|
+
* ```tsx
|
36
|
+
* <WalletName fallbackComponent={<span>Failed to load</span>}
|
37
|
+
* />
|
38
|
+
* ```
|
39
|
+
*/
|
40
|
+
fallbackComponent?: JSX.Element;
|
41
|
+
/**
|
42
|
+
* Optional `useQuery` params
|
43
|
+
*/
|
44
|
+
queryOptions?: Omit<UseQueryOptions<string>, "queryFn" | "queryKey">;
|
45
|
+
/**
|
46
|
+
* A function to format the name's display value
|
47
|
+
* ```tsx
|
48
|
+
* <WalletName formatFn={(str: string) => doSomething()} />
|
49
|
+
* ```
|
50
|
+
*/
|
51
|
+
formatFn?: (str: string) => string;
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* This component fetches then shows the name of a wallet.
|
56
|
+
* It inherits all the attributes of a HTML <span> component, hence you can style it just like how you would style a normal <span>
|
57
|
+
*
|
58
|
+
* @example
|
59
|
+
* ### Basic usage
|
60
|
+
* ```tsx
|
61
|
+
* import { WalletProvider, WalletName } from "thirdweb/react";
|
62
|
+
*
|
63
|
+
* <WalletProvider id="io.metamask">
|
64
|
+
* <WalletName />
|
65
|
+
* </WalletProvider>
|
66
|
+
* ```
|
67
|
+
* Result:
|
68
|
+
* ```html
|
69
|
+
* <span>MetaMask</span>
|
70
|
+
* ```
|
71
|
+
*
|
72
|
+
* ### Show a loading sign when the name is being fetched
|
73
|
+
* ```tsx
|
74
|
+
* import { WalletProvider, WalletName } from "thirdweb/react";
|
75
|
+
*
|
76
|
+
* <WalletProvider {...props}>
|
77
|
+
* <WalletName loadingComponent={<Spinner />} />
|
78
|
+
* </WalletProvider>
|
79
|
+
* ```
|
80
|
+
*
|
81
|
+
* ### Fallback to something when the name fails to resolve
|
82
|
+
* ```tsx
|
83
|
+
* <WalletProvider {...props}>
|
84
|
+
* <WalletName fallbackComponent={<span>Failed to load</span>} />
|
85
|
+
* </WalletProvider>
|
86
|
+
* ```
|
87
|
+
*
|
88
|
+
* ### Custom query options for useQuery
|
89
|
+
* This component uses `@tanstack-query`'s useQuery internally.
|
90
|
+
* You can use the `queryOptions` prop for more fine-grained control
|
91
|
+
* ```tsx
|
92
|
+
* <WalletName
|
93
|
+
* queryOptions={{
|
94
|
+
* enabled: isEnabled,
|
95
|
+
* retry: 4,
|
96
|
+
* }}
|
97
|
+
* />
|
98
|
+
* @component
|
99
|
+
* @beta
|
100
|
+
* @wallet
|
101
|
+
*/
|
102
|
+
export function WalletName({
|
103
|
+
loadingComponent,
|
104
|
+
fallbackComponent,
|
105
|
+
queryOptions,
|
106
|
+
formatFn,
|
107
|
+
...restProps
|
108
|
+
}: WalletNameProps) {
|
109
|
+
const nameQuery = useWalletName({ queryOptions, formatFn });
|
110
|
+
if (nameQuery.isLoading) {
|
111
|
+
return loadingComponent || null;
|
112
|
+
}
|
113
|
+
if (!nameQuery.data) {
|
114
|
+
return fallbackComponent || null;
|
115
|
+
}
|
116
|
+
return <span {...restProps}>{nameQuery.data}</span>;
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* @internal
|
121
|
+
*/
|
122
|
+
function useWalletName(props: {
|
123
|
+
formatFn?: (str: string) => string;
|
124
|
+
queryOptions?: Omit<UseQueryOptions<string>, "queryFn" | "queryKey">;
|
125
|
+
}) {
|
126
|
+
const { id } = useWalletContext();
|
127
|
+
const nameQuery = useQuery({
|
128
|
+
queryKey: getQueryKeys({ id, formatFn: props.formatFn }),
|
129
|
+
queryFn: async () => fetchWalletName({ id, formatFn: props.formatFn }),
|
130
|
+
...props.queryOptions,
|
131
|
+
});
|
132
|
+
return nameQuery;
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* @internal Exported for tests only
|
137
|
+
*/
|
138
|
+
export function getQueryKeys(props: {
|
139
|
+
id: WalletId;
|
140
|
+
formatFn?: (str: string) => string;
|
141
|
+
}) {
|
142
|
+
if (typeof props.formatFn === "function") {
|
143
|
+
return [
|
144
|
+
"walletName",
|
145
|
+
props.id,
|
146
|
+
{ resolver: getFunctionId(props.formatFn) },
|
147
|
+
] as const;
|
148
|
+
}
|
149
|
+
return ["walletName", props.id] as const;
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* @internal Exported for tests only
|
154
|
+
*/
|
155
|
+
export async function fetchWalletName(props: {
|
156
|
+
id: WalletId;
|
157
|
+
formatFn?: (str: string) => string;
|
158
|
+
}) {
|
159
|
+
const info = await getWalletInfo(props.id);
|
160
|
+
if (typeof props.formatFn === "function") {
|
161
|
+
return props.formatFn(info.name);
|
162
|
+
}
|
163
|
+
return info.name;
|
164
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { type FC, useContext } from "react";
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
3
|
+
import { render, renderHook, screen } from "~test/react-render.js";
|
4
|
+
import {
|
5
|
+
WalletProvider,
|
6
|
+
WalletProviderContext,
|
7
|
+
useWalletContext,
|
8
|
+
} from "./provider.js";
|
9
|
+
|
10
|
+
describe.runIf(process.env.TW_SECRET_KEY)("WalletProvider", () => {
|
11
|
+
it("useWalletContext should throw an error when used outside of WalletProvider", () => {
|
12
|
+
const consoleErrorSpy = vi
|
13
|
+
.spyOn(console, "error")
|
14
|
+
.mockImplementation(() => {});
|
15
|
+
|
16
|
+
expect(() => {
|
17
|
+
renderHook(() => useWalletContext());
|
18
|
+
}).toThrow(
|
19
|
+
"WalletProviderContext not found. Make sure you are using WalletIcon, WalletName, etc. inside a <WalletProvider /> component",
|
20
|
+
);
|
21
|
+
|
22
|
+
consoleErrorSpy.mockRestore();
|
23
|
+
});
|
24
|
+
|
25
|
+
it("useWalletContext should return the context value when used within WalletProvider", () => {
|
26
|
+
const wrapper: FC = ({ children }: React.PropsWithChildren) => (
|
27
|
+
<WalletProvider id="io.metamask">{children}</WalletProvider>
|
28
|
+
);
|
29
|
+
|
30
|
+
const { result } = renderHook(() => useWalletContext(), { wrapper });
|
31
|
+
|
32
|
+
expect(result.current.id).toStrictEqual("io.metamask");
|
33
|
+
});
|
34
|
+
|
35
|
+
it("should render children correctly", () => {
|
36
|
+
render(
|
37
|
+
<WalletProvider id="io.metamask">
|
38
|
+
<div>Child Component</div>
|
39
|
+
</WalletProvider>,
|
40
|
+
);
|
41
|
+
|
42
|
+
expect(screen.getByText("Child Component")).toBeInTheDocument();
|
43
|
+
});
|
44
|
+
|
45
|
+
it("should provide context values to children", () => {
|
46
|
+
function WalletConsumer() {
|
47
|
+
const context = useContext(WalletProviderContext);
|
48
|
+
if (!context) {
|
49
|
+
return <div>No context</div>;
|
50
|
+
}
|
51
|
+
return <div>{String(context.id)}</div>;
|
52
|
+
}
|
53
|
+
render(
|
54
|
+
<WalletProvider id="io.metamask">
|
55
|
+
<WalletConsumer />
|
56
|
+
</WalletProvider>,
|
57
|
+
);
|
58
|
+
|
59
|
+
expect(screen.getByText("io.metamask")).toBeInTheDocument();
|
60
|
+
});
|
61
|
+
});
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import type React from "react";
|
4
|
+
import { createContext, useContext } from "react";
|
5
|
+
import type { WalletId } from "../../../../../wallets/wallet-types.js";
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Props for the WalletProvider component
|
9
|
+
* @component
|
10
|
+
* @wallet
|
11
|
+
*/
|
12
|
+
export type WalletProviderProps = {
|
13
|
+
id: WalletId;
|
14
|
+
};
|
15
|
+
|
16
|
+
/**
|
17
|
+
* @internal Exported for tests only
|
18
|
+
*/
|
19
|
+
export const WalletProviderContext = /* @__PURE__ */ createContext<
|
20
|
+
WalletProviderProps | undefined
|
21
|
+
>(undefined);
|
22
|
+
|
23
|
+
/**
|
24
|
+
/**
|
25
|
+
* A React context provider component that supplies Wallet-related data to its child components.
|
26
|
+
*
|
27
|
+
* This component serves as a wrapper around the `WalletProviderContext.Provider` and passes
|
28
|
+
* the provided wallet data down to all of its child components through the context API.
|
29
|
+
*
|
30
|
+
* @example
|
31
|
+
* ### Basic usage
|
32
|
+
* ```tsx
|
33
|
+
* import { WalletProvider, WalletIcon, WalletName } from "thirdweb/react";
|
34
|
+
*
|
35
|
+
* <WalletProvider id="io.metamask">
|
36
|
+
* <WalletIcon />
|
37
|
+
* <WalletName />
|
38
|
+
* </WalletProvider>
|
39
|
+
* ```
|
40
|
+
* @beta
|
41
|
+
* @component
|
42
|
+
* @wallet
|
43
|
+
*/
|
44
|
+
export function WalletProvider(
|
45
|
+
props: React.PropsWithChildren<WalletProviderProps>,
|
46
|
+
) {
|
47
|
+
return (
|
48
|
+
<WalletProviderContext.Provider value={props}>
|
49
|
+
{props.children}
|
50
|
+
</WalletProviderContext.Provider>
|
51
|
+
);
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* @internal
|
56
|
+
*/
|
57
|
+
export function useWalletContext() {
|
58
|
+
const ctx = useContext(WalletProviderContext);
|
59
|
+
if (!ctx) {
|
60
|
+
throw new Error(
|
61
|
+
"WalletProviderContext not found. Make sure you are using WalletIcon, WalletName, etc. inside a <WalletProvider /> component",
|
62
|
+
);
|
63
|
+
}
|
64
|
+
return ctx;
|
65
|
+
}
|
package/src/version.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export const version = "5.
|
1
|
+
export const version = "5.79.0-nightly-f91f6310e9396918d0ffc5217eeb4a44cef0b8c8-20241215000412";
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import type { Chain } from "../../../../chains/types.js";
|
2
2
|
import type { ThirdwebClient } from "../../../../client/client.js";
|
3
3
|
import type { SupportedSmsCountry } from "../../../../react/web/wallets/in-app/supported-sms-countries.js";
|
4
|
+
import type { Prettify } from "../../../../utils/type-utils.js";
|
4
5
|
import type { SmartWalletOptions } from "../../../smart/types.js";
|
5
6
|
import type {
|
6
7
|
AuthOption,
|
@@ -20,14 +21,12 @@ export type Ecosystem = {
|
|
20
21
|
partnerId?: string;
|
21
22
|
};
|
22
23
|
|
23
|
-
export type InAppWalletConnectionOptions =
|
24
|
-
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
redirect?: boolean;
|
30
|
-
};
|
24
|
+
export type InAppWalletConnectionOptions = Prettify<
|
25
|
+
(MultiStepAuthArgsType | SingleStepAuthArgsType) & {
|
26
|
+
client: ThirdwebClient;
|
27
|
+
chain?: Chain;
|
28
|
+
}
|
29
|
+
>;
|
31
30
|
|
32
31
|
export type InAppWalletAutoConnectOptions = {
|
33
32
|
client: ThirdwebClient;
|