thirdweb 5.80.1-nightly-ce3e850fdbf34911e20919ecc2674e4a63f08fa3-20241226000321 → 5.80.1-nightly-a7b6cb3211e8b5232e77965cb8dd8ff96b369424-20241228000338
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/gas/fee-data.js +1 -1
- package/dist/cjs/gas/fee-data.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/ConnectButton.js +11 -0
- package/dist/cjs/react/web/ui/ConnectWallet/ConnectButton.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/Details.js +59 -16
- package/dist/cjs/react/web/ui/ConnectWallet/Details.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/NetworkSelector.js +48 -24
- package/dist/cjs/react/web/ui/ConnectWallet/NetworkSelector.js.map +1 -1
- package/dist/cjs/react/web/ui/components/ChainActiveDot.js +20 -0
- package/dist/cjs/react/web/ui/components/ChainActiveDot.js.map +1 -0
- package/dist/cjs/react/web/ui/components/ChainIcon.js +24 -29
- package/dist/cjs/react/web/ui/components/ChainIcon.js.map +1 -1
- package/dist/cjs/react/web/ui/components/fallbackChainIcon.js +9 -0
- package/dist/cjs/react/web/ui/components/fallbackChainIcon.js.map +1 -0
- package/dist/cjs/react/web/ui/prebuilt/Chain/icon.js +44 -28
- package/dist/cjs/react/web/ui/prebuilt/Chain/icon.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/gas/fee-data.js +1 -1
- package/dist/esm/gas/fee-data.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/ConnectButton.js +11 -0
- package/dist/esm/react/web/ui/ConnectWallet/ConnectButton.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/Details.js +59 -17
- package/dist/esm/react/web/ui/ConnectWallet/Details.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/NetworkSelector.js +45 -21
- package/dist/esm/react/web/ui/ConnectWallet/NetworkSelector.js.map +1 -1
- package/dist/esm/react/web/ui/components/ChainActiveDot.js +17 -0
- package/dist/esm/react/web/ui/components/ChainActiveDot.js.map +1 -0
- package/dist/esm/react/web/ui/components/ChainIcon.js +22 -28
- package/dist/esm/react/web/ui/components/ChainIcon.js.map +1 -1
- package/dist/esm/react/web/ui/components/fallbackChainIcon.js +6 -0
- package/dist/esm/react/web/ui/components/fallbackChainIcon.js.map +1 -0
- package/dist/esm/react/web/ui/prebuilt/Chain/icon.js +42 -28
- package/dist/esm/react/web/ui/prebuilt/Chain/icon.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/react/core/hooks/connection/ConnectButtonProps.d.ts +5 -0
- package/dist/types/react/core/hooks/connection/ConnectButtonProps.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/ConnectButton.d.ts +11 -0
- package/dist/types/react/web/ui/ConnectWallet/ConnectButton.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/Details.d.ts +34 -1
- package/dist/types/react/web/ui/ConnectWallet/Details.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/NetworkSelector.d.ts +46 -1
- package/dist/types/react/web/ui/ConnectWallet/NetworkSelector.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/ChainActiveDot.d.ts +10 -0
- package/dist/types/react/web/ui/components/ChainActiveDot.d.ts.map +1 -0
- package/dist/types/react/web/ui/components/ChainIcon.d.ts +8 -0
- package/dist/types/react/web/ui/components/ChainIcon.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/fallbackChainIcon.d.ts +6 -0
- package/dist/types/react/web/ui/components/fallbackChainIcon.d.ts.map +1 -0
- package/dist/types/react/web/ui/prebuilt/Chain/icon.d.ts +18 -0
- package/dist/types/react/web/ui/prebuilt/Chain/icon.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/gas/fee-data.ts +1 -1
- package/src/react/core/hooks/connection/ConnectButtonProps.ts +6 -0
- package/src/react/web/ui/ConnectWallet/ConnectButton.tsx +11 -0
- package/src/react/web/ui/ConnectWallet/Details.test.tsx +55 -0
- package/src/react/web/ui/ConnectWallet/Details.tsx +176 -98
- package/src/react/web/ui/ConnectWallet/NetworkSelector.test.tsx +189 -0
- package/src/react/web/ui/ConnectWallet/NetworkSelector.tsx +117 -76
- package/src/react/web/ui/components/ChainActiveDot.tsx +17 -0
- package/src/react/web/ui/components/ChainIcon.test.tsx +60 -0
- package/src/react/web/ui/components/ChainIcon.tsx +27 -31
- package/src/react/web/ui/components/fallbackChainIcon.ts +6 -0
- package/src/react/web/ui/prebuilt/Chain/icon.test.tsx +150 -0
- package/src/react/web/ui/prebuilt/Chain/icon.tsx +53 -31
- package/src/version.ts +1 -1
@@ -27,32 +27,35 @@ import {
|
|
27
27
|
radius,
|
28
28
|
spacing,
|
29
29
|
} from "../../../core/design-system/index.js";
|
30
|
-
import {
|
31
|
-
useChainIconUrl,
|
32
|
-
useChainName,
|
33
|
-
useChainsQuery,
|
34
|
-
} from "../../../core/hooks/others/useChainQuery.js";
|
30
|
+
import { useChainsQuery } from "../../../core/hooks/others/useChainQuery.js";
|
35
31
|
import { useActiveWalletChain } from "../../../core/hooks/wallets/useActiveWalletChain.js";
|
36
32
|
import { useSwitchActiveWalletChain } from "../../../core/hooks/wallets/useSwitchActiveWalletChain.js";
|
37
33
|
import { SetRootElementContext } from "../../../core/providers/RootElementContext.js";
|
38
|
-
import {
|
34
|
+
import { ChainActiveDot } from "../components/ChainActiveDot.js";
|
39
35
|
import { Modal } from "../components/Modal.js";
|
40
36
|
import { Skeleton } from "../components/Skeleton.js";
|
41
37
|
import { Spacer } from "../components/Spacer.js";
|
42
38
|
import { Spinner } from "../components/Spinner.js";
|
43
39
|
import { Container, Line, ModalHeader } from "../components/basic.js";
|
44
40
|
import { Button } from "../components/buttons.js";
|
41
|
+
import { fallbackChainIcon } from "../components/fallbackChainIcon.js";
|
45
42
|
import { Input } from "../components/formElements.js";
|
46
43
|
import { ModalTitle } from "../components/modalElements.js";
|
47
44
|
import { Text } from "../components/text.js";
|
48
45
|
import { StyledButton, StyledP, StyledUl } from "../design-system/elements.js";
|
49
46
|
import { useDebouncedValue } from "../hooks/useDebouncedValue.js";
|
50
47
|
import { useShowMore } from "../hooks/useShowMore.js";
|
48
|
+
import { ChainIcon } from "../prebuilt/Chain/icon.js";
|
49
|
+
import { ChainName } from "../prebuilt/Chain/name.js";
|
50
|
+
import { ChainProvider } from "../prebuilt/Chain/provider.js";
|
51
51
|
import type { LocaleId } from "../types.js";
|
52
52
|
import { getConnectLocale } from "./locale/getConnectLocale.js";
|
53
53
|
import type { ConnectLocale } from "./locale/types.js";
|
54
54
|
|
55
|
-
|
55
|
+
/**
|
56
|
+
* @internal
|
57
|
+
*/
|
58
|
+
export type NetworkSelectorChainProps = {
|
56
59
|
/**
|
57
60
|
* `Chain` object to be displayed
|
58
61
|
*/
|
@@ -548,7 +551,10 @@ type NetworkListProps = {
|
|
548
551
|
connectLocale: ConnectLocale;
|
549
552
|
};
|
550
553
|
|
551
|
-
|
554
|
+
/**
|
555
|
+
* @internal Exported for tests
|
556
|
+
*/
|
557
|
+
export const NetworkList = /* @__PURE__ */ memo(function NetworkList(
|
552
558
|
props: NetworkListProps,
|
553
559
|
) {
|
554
560
|
// show 10 items first, when reaching the last item, show 10 more
|
@@ -626,6 +632,9 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(
|
|
626
632
|
);
|
627
633
|
} as React.FC<NetworkListProps>);
|
628
634
|
|
635
|
+
/**
|
636
|
+
* @internal
|
637
|
+
*/
|
629
638
|
export const ChainButton = /* @__PURE__ */ memo(function ChainButton(props: {
|
630
639
|
chain: Chain;
|
631
640
|
onClick: () => void;
|
@@ -636,72 +645,95 @@ export const ChainButton = /* @__PURE__ */ memo(function ChainButton(props: {
|
|
636
645
|
}) {
|
637
646
|
const locale = props.connectLocale;
|
638
647
|
const { chain, confirming, switchingFailed } = props;
|
639
|
-
|
640
648
|
const activeChain = useActiveWalletChain();
|
641
649
|
|
642
|
-
const chainNameQuery = useChainName(chain);
|
643
|
-
const chainIconQuery = useChainIconUrl(chain);
|
644
|
-
|
645
|
-
let chainName: React.ReactNode;
|
646
|
-
if (chainNameQuery.name) {
|
647
|
-
chainName = <span>{chainNameQuery.name} </span>;
|
648
|
-
} else {
|
649
|
-
chainName = <Skeleton width="150px" height="20px" />;
|
650
|
-
}
|
651
|
-
|
652
650
|
return (
|
653
|
-
<
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
<
|
659
|
-
chainIconUrl={chainIconQuery.url}
|
660
|
-
size={iconSize.lg}
|
661
|
-
active={activeChain?.id === chain.id}
|
662
|
-
loading="lazy"
|
663
|
-
client={props.client}
|
664
|
-
/>
|
665
|
-
) : (
|
666
|
-
<Skeleton width={`${iconSize.lg}px`} height={`${iconSize.lg}px`} />
|
667
|
-
)}
|
668
|
-
|
669
|
-
{confirming || switchingFailed ? (
|
670
|
-
<div
|
651
|
+
<ChainProvider chain={chain}>
|
652
|
+
<NetworkButton
|
653
|
+
data-active={activeChain?.id === chain.id}
|
654
|
+
onClick={props.onClick}
|
655
|
+
>
|
656
|
+
<Container
|
671
657
|
style={{
|
658
|
+
position: "relative",
|
672
659
|
display: "flex",
|
673
|
-
|
674
|
-
|
660
|
+
flexShrink: 0,
|
661
|
+
alignItems: "center",
|
675
662
|
}}
|
676
663
|
>
|
677
|
-
|
678
|
-
|
679
|
-
{
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
{
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
664
|
+
<ChainIcon
|
665
|
+
client={props.client}
|
666
|
+
loadingComponent={
|
667
|
+
<Skeleton
|
668
|
+
width={`${iconSize.lg}px`}
|
669
|
+
height={`${iconSize.lg}px`}
|
670
|
+
/>
|
671
|
+
}
|
672
|
+
fallbackComponent={
|
673
|
+
<img
|
674
|
+
src={fallbackChainIcon}
|
675
|
+
alt=""
|
676
|
+
style={{
|
677
|
+
width: `${iconSize.lg}px`,
|
678
|
+
height: `${iconSize.lg}px`,
|
679
|
+
}}
|
680
|
+
/>
|
681
|
+
}
|
682
|
+
style={{
|
683
|
+
width: `${iconSize.lg}px`,
|
684
|
+
height: `${iconSize.lg}px`,
|
685
|
+
}}
|
686
|
+
loading="lazy"
|
687
|
+
/>
|
688
|
+
{activeChain?.id === chain.id && (
|
689
|
+
<ChainActiveDot className="tw-chain-active-dot-button-network-selector" />
|
690
|
+
)}
|
691
|
+
</Container>
|
692
|
+
{confirming || switchingFailed ? (
|
693
|
+
<div
|
694
|
+
style={{
|
695
|
+
display: "flex",
|
696
|
+
flexDirection: "column",
|
697
|
+
gap: spacing.xs,
|
698
|
+
}}
|
699
|
+
>
|
700
|
+
<ChainName
|
701
|
+
loadingComponent={<Skeleton width="150px" height="20px" />}
|
702
|
+
/>
|
703
|
+
<Container animate="fadein" flex="row" gap="xxs" center="y">
|
704
|
+
{confirming && (
|
705
|
+
<>
|
706
|
+
<Text size="xs" color="accentText">
|
707
|
+
{locale.confirmInWallet}
|
708
|
+
</Text>
|
709
|
+
<Spinner size="xs" color="accentText" />
|
710
|
+
</>
|
711
|
+
)}
|
712
|
+
|
713
|
+
{switchingFailed && (
|
714
|
+
<Container animate="fadein">
|
715
|
+
<Text size="xs" color="danger">
|
716
|
+
{locale.networkSelector.failedToSwitch}
|
717
|
+
</Text>
|
718
|
+
</Container>
|
719
|
+
)}
|
720
|
+
</Container>
|
721
|
+
</div>
|
722
|
+
) : (
|
723
|
+
<ChainName
|
724
|
+
loadingComponent={<Skeleton width="150px" height="20px" />}
|
725
|
+
className="tw-chain-icon-none-confirming"
|
726
|
+
/>
|
727
|
+
)}
|
728
|
+
</NetworkButton>
|
729
|
+
</ChainProvider>
|
701
730
|
);
|
702
731
|
});
|
703
732
|
|
704
|
-
|
733
|
+
/**
|
734
|
+
* @internal Exported for tests
|
735
|
+
*/
|
736
|
+
export const TabButton = /* @__PURE__ */ (() =>
|
705
737
|
styled.button((_) => {
|
706
738
|
const theme = useCustomTheme();
|
707
739
|
return {
|
@@ -722,7 +754,10 @@ const TabButton = /* @__PURE__ */ (() =>
|
|
722
754
|
};
|
723
755
|
}))();
|
724
756
|
|
725
|
-
|
757
|
+
/**
|
758
|
+
* @internal Exported for tests
|
759
|
+
*/
|
760
|
+
export const SectionLabel = /* @__PURE__ */ StyledP(() => {
|
726
761
|
const theme = useCustomTheme();
|
727
762
|
return {
|
728
763
|
fontSize: fontSize.sm,
|
@@ -743,7 +778,10 @@ const NetworkListUl = /* @__PURE__ */ StyledUl({
|
|
743
778
|
boxSizing: "border-box",
|
744
779
|
});
|
745
780
|
|
746
|
-
|
781
|
+
/**
|
782
|
+
* @internal Exported for tests
|
783
|
+
*/
|
784
|
+
export const NetworkButton = /* @__PURE__ */ StyledButton((_) => {
|
747
785
|
const theme = useCustomTheme();
|
748
786
|
return {
|
749
787
|
all: "unset",
|
@@ -769,16 +807,19 @@ const NetworkButton = /* @__PURE__ */ StyledButton((_) => {
|
|
769
807
|
};
|
770
808
|
});
|
771
809
|
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
810
|
+
/**
|
811
|
+
* @internal Exported for tests
|
812
|
+
*/
|
813
|
+
export const StyledMagnifyingGlassIcon = /* @__PURE__ */ styled(
|
814
|
+
MagnifyingGlassIcon,
|
815
|
+
)((_) => {
|
816
|
+
const theme = useCustomTheme();
|
817
|
+
return {
|
818
|
+
color: theme.colors.secondaryText,
|
819
|
+
position: "absolute",
|
820
|
+
left: spacing.sm,
|
821
|
+
};
|
822
|
+
});
|
782
823
|
|
783
824
|
/**
|
784
825
|
* Options for the `useNetworkSwitcherModal` hook's returned `open` function
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { StyledDiv } from "../design-system/elements.js";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* The greet dot that is placed at the corner of the chain icon -
|
5
|
+
* indicating that the chain is currently active (connected to)
|
6
|
+
* @internal
|
7
|
+
*/
|
8
|
+
export const ChainActiveDot = /* @__PURE__ */ StyledDiv({
|
9
|
+
width: "28%",
|
10
|
+
height: "28%",
|
11
|
+
borderRadius: "50%",
|
12
|
+
position: "absolute",
|
13
|
+
bottom: 0,
|
14
|
+
right: 0,
|
15
|
+
backgroundColor: "#00d395",
|
16
|
+
boxShadow: "0 0 0 2px var(--bg)",
|
17
|
+
});
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
|
3
|
+
import { render } from "~test/react-render.js";
|
4
|
+
import { TEST_CLIENT } from "~test/test-clients.js";
|
5
|
+
import { ChainIcon, getSrcChainIcon } from "./ChainIcon.js";
|
6
|
+
import { fallbackChainIcon } from "./fallbackChainIcon.js";
|
7
|
+
|
8
|
+
const client = TEST_CLIENT;
|
9
|
+
|
10
|
+
describe.runIf(process.env.TW_SECRET_KEY)("ChainIcon-legacy", () => {
|
11
|
+
it("getSrcChainIcon should return fallbackChainIcon if chainIconUrl does not exist", () => {
|
12
|
+
expect(getSrcChainIcon({ client })).toBe(fallbackChainIcon);
|
13
|
+
});
|
14
|
+
|
15
|
+
it("getSrcChainIcon should return the resolved url", () => {
|
16
|
+
const resolvedUrl = getSrcChainIcon({
|
17
|
+
client,
|
18
|
+
chainIconUrl: TEST_CONTRACT_URI,
|
19
|
+
});
|
20
|
+
expect(resolvedUrl.startsWith("https://")).toBe(true);
|
21
|
+
});
|
22
|
+
|
23
|
+
it("getSrcChainIcon should return the fallbackChainIcon if fails to resolve", () => {
|
24
|
+
expect(getSrcChainIcon({ client, chainIconUrl: "test" })).toBe(
|
25
|
+
fallbackChainIcon,
|
26
|
+
);
|
27
|
+
});
|
28
|
+
|
29
|
+
it("should render an image", () => {
|
30
|
+
const { container } = render(
|
31
|
+
<ChainIcon
|
32
|
+
chainIconUrl="ipfs://QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9/ethereum/512.png"
|
33
|
+
client={client}
|
34
|
+
size="lg"
|
35
|
+
/>,
|
36
|
+
);
|
37
|
+
|
38
|
+
const image = container.querySelector("img");
|
39
|
+
expect(image).toBeTruthy();
|
40
|
+
|
41
|
+
expect(
|
42
|
+
image?.src.includes("QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9"),
|
43
|
+
).toBe(true);
|
44
|
+
});
|
45
|
+
|
46
|
+
it("should render ChainActiveDot if the `active` prop is set to true", () => {
|
47
|
+
const { container } = render(
|
48
|
+
<ChainIcon
|
49
|
+
chainIconUrl="ipfs://QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9/ethereum/512.png"
|
50
|
+
client={client}
|
51
|
+
size="lg"
|
52
|
+
active
|
53
|
+
/>,
|
54
|
+
);
|
55
|
+
const activeDot = container.querySelector(
|
56
|
+
".tw-chain-active-dot-legacy-chain-icon",
|
57
|
+
);
|
58
|
+
expect(activeDot).toBeTruthy();
|
59
|
+
});
|
60
|
+
});
|
@@ -1,13 +1,12 @@
|
|
1
1
|
import type { ThirdwebClient } from "../../../../client/client.js";
|
2
2
|
import { resolveScheme } from "../../../../utils/ipfs.js";
|
3
|
-
import {
|
3
|
+
import { ChainActiveDot } from "./ChainActiveDot.js";
|
4
4
|
import { Img } from "./Img.js";
|
5
5
|
import { Container } from "./basic.js";
|
6
|
-
|
7
|
-
const fallbackChainIcon =
|
8
|
-
"data:image/svg+xml;charset=UTF-8,%3csvg width='15' height='14' viewBox='0 0 15 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M7 8.04238e-07C5.1435 8.04238e-07 3.36301 0.737501 2.05025 2.05025C0.7375 3.36301 0 5.1435 0 7C0 7.225 -1.52737e-07 7.445 0.0349998 7.665C0.16385 9.0151 0.68213 10.2988 1.52686 11.3598C2.37158 12.4209 3.50637 13.2137 4.79326 13.642C6.0801 14.0702 7.4637 14.1153 8.7758 13.7719C10.0879 13.4285 11.2719 12.7113 12.184 11.7075C13.0961 10.7038 13.6969 9.4567 13.9135 8.1178C14.1301 6.7789 13.9531 5.406 13.4039 4.16587C12.8548 2.92574 11.9573 1.87184 10.8204 1.13228C9.6835 0.392721 8.3563 -0.000649196 7 8.04238e-07ZM7 1C8.581 1.00137 10.0975 1.62668 11.22 2.74V3.24C9.2438 2.55991 7.0956 2.56872 5.125 3.265C4.96758 3.1116 4.76997 3.00586 4.555 2.96H4.43C4.37 2.75 4.315 2.54 4.27 2.325C4.225 2.11 4.2 1.92 4.175 1.715C5.043 1.24658 6.0137 1.00091 7 1ZM5.5 3.935C7.3158 3.32693 9.2838 3.34984 11.085 4C10.8414 5.2703 10.3094 6.4677 9.53 7.5C9.312 7.4077 9.0707 7.3855 8.8395 7.4366C8.6083 7.4877 8.3988 7.6094 8.24 7.785C8.065 7.685 7.89 7.585 7.74 7.47C6.7307 6.7966 5.8877 5.9023 5.275 4.855C5.374 4.73221 5.4461 4.58996 5.4866 4.43749C5.5271 4.28502 5.5351 4.12575 5.51 3.97L5.5 3.935ZM3.5 2.135C3.5 2.24 3.53 2.35 3.55 2.455C3.595 2.675 3.655 2.89 3.715 3.105C3.52353 3.21838 3.36943 3.38531 3.2717 3.58522C3.17397 3.78513 3.13688 4.00927 3.165 4.23C2.37575 4.7454 1.67078 5.3795 1.075 6.11C1.19455 5.3189 1.47112 4.55966 1.88843 3.87701C2.30575 3.19437 2.85539 2.60208 3.505 2.135H3.5ZM3.5 9.99C3.30481 10.0555 3.13037 10.1714 2.9943 10.3259C2.85822 10.4804 2.76533 10.6681 2.725 10.87H2.405C1.59754 9.9069 1.1146 8.7136 1.025 7.46L1.08 7.365C1.70611 6.3942 2.52463 5.562 3.485 4.92C3.62899 5.0704 3.81094 5.179 4.01162 5.2345C4.2123 5.2899 4.42423 5.2901 4.625 5.235C5.2938 6.3652 6.208 7.3306 7.3 8.06C7.505 8.195 7.715 8.32 7.925 8.44C7.9082 8.6312 7.9391 8.8237 8.015 9C7.1 9.7266 6.0445 10.256 4.915 10.555C4.78401 10.3103 4.57028 10.1201 4.31199 10.0184C4.05369 9.9167 3.76766 9.9102 3.505 10L3.5 9.99ZM7 12.99C5.9831 12.9903 4.98307 12.7304 4.095 12.235L4.235 12.205C4.43397 12.1397 4.61176 12.0222 4.74984 11.8648C4.88792 11.7074 4.98122 11.5158 5.02 11.31C6.2985 10.984 7.4921 10.3872 8.52 9.56C8.7642 9.7027 9.0525 9.75 9.3295 9.6927C9.6064 9.6355 9.8524 9.4778 10.02 9.25C10.7254 9.4334 11.4511 9.5275 12.18 9.53H12.445C11.9626 10.5673 11.1938 11.4451 10.2291 12.0599C9.2643 12.6747 8.144 13.0009 7 13V12.99ZM10.255 8.54C10.2545 8.3304 10.1975 8.1249 10.09 7.945C10.9221 6.8581 11.5012 5.5991 11.785 4.26C12.035 4.37667 12.2817 4.50667 12.525 4.65C13.0749 5.9495 13.1493 7.4012 12.735 8.75C11.9049 8.8142 11.0698 8.7484 10.26 8.555L10.255 8.54Z' fill='%23646D7A'/%3e%3c/svg%3e";
|
6
|
+
import { fallbackChainIcon } from "./fallbackChainIcon.js";
|
9
7
|
|
10
8
|
/**
|
9
|
+
* This component (file) will eventually be replaced with the ChainIcon prebuilt version.
|
11
10
|
* @internal
|
12
11
|
*/
|
13
12
|
export const ChainIcon: React.FC<{
|
@@ -18,21 +17,6 @@ export const ChainIcon: React.FC<{
|
|
18
17
|
loading?: "lazy" | "eager";
|
19
18
|
client: ThirdwebClient;
|
20
19
|
}> = (props) => {
|
21
|
-
const getSrc = () => {
|
22
|
-
const url = props.chainIconUrl;
|
23
|
-
if (!url) {
|
24
|
-
return fallbackChainIcon;
|
25
|
-
}
|
26
|
-
try {
|
27
|
-
return resolveScheme({
|
28
|
-
uri: url,
|
29
|
-
client: props.client,
|
30
|
-
});
|
31
|
-
} catch {
|
32
|
-
return fallbackChainIcon;
|
33
|
-
}
|
34
|
-
};
|
35
|
-
|
36
20
|
return (
|
37
21
|
<Container
|
38
22
|
style={{
|
@@ -43,24 +27,36 @@ export const ChainIcon: React.FC<{
|
|
43
27
|
}}
|
44
28
|
>
|
45
29
|
<Img
|
46
|
-
src={
|
30
|
+
src={getSrcChainIcon(props)}
|
47
31
|
width={props.size}
|
48
32
|
height={props.size}
|
49
33
|
fallbackImage={fallbackChainIcon}
|
50
34
|
client={props.client}
|
51
35
|
/>
|
52
|
-
{props.active &&
|
36
|
+
{props.active && (
|
37
|
+
<ChainActiveDot className="tw-chain-active-dot-legacy-chain-icon" />
|
38
|
+
)}
|
53
39
|
</Container>
|
54
40
|
);
|
55
41
|
};
|
56
42
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
43
|
+
/**
|
44
|
+
* @internal
|
45
|
+
*/
|
46
|
+
export const getSrcChainIcon = (props: {
|
47
|
+
client: ThirdwebClient;
|
48
|
+
chainIconUrl?: string;
|
49
|
+
}) => {
|
50
|
+
const url = props.chainIconUrl;
|
51
|
+
if (!url) {
|
52
|
+
return fallbackChainIcon;
|
53
|
+
}
|
54
|
+
try {
|
55
|
+
return resolveScheme({
|
56
|
+
uri: url,
|
57
|
+
client: props.client,
|
58
|
+
});
|
59
|
+
} catch {
|
60
|
+
return fallbackChainIcon;
|
61
|
+
}
|
62
|
+
};
|
@@ -0,0 +1,6 @@
|
|
1
|
+
/**
|
2
|
+
* The dummy image to be used in case the chain icon does not exist
|
3
|
+
* @internal
|
4
|
+
*/
|
5
|
+
export const fallbackChainIcon =
|
6
|
+
"data:image/svg+xml;charset=UTF-8,%3csvg width='15' height='14' viewBox='0 0 15 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M7 8.04238e-07C5.1435 8.04238e-07 3.36301 0.737501 2.05025 2.05025C0.7375 3.36301 0 5.1435 0 7C0 7.225 -1.52737e-07 7.445 0.0349998 7.665C0.16385 9.0151 0.68213 10.2988 1.52686 11.3598C2.37158 12.4209 3.50637 13.2137 4.79326 13.642C6.0801 14.0702 7.4637 14.1153 8.7758 13.7719C10.0879 13.4285 11.2719 12.7113 12.184 11.7075C13.0961 10.7038 13.6969 9.4567 13.9135 8.1178C14.1301 6.7789 13.9531 5.406 13.4039 4.16587C12.8548 2.92574 11.9573 1.87184 10.8204 1.13228C9.6835 0.392721 8.3563 -0.000649196 7 8.04238e-07ZM7 1C8.581 1.00137 10.0975 1.62668 11.22 2.74V3.24C9.2438 2.55991 7.0956 2.56872 5.125 3.265C4.96758 3.1116 4.76997 3.00586 4.555 2.96H4.43C4.37 2.75 4.315 2.54 4.27 2.325C4.225 2.11 4.2 1.92 4.175 1.715C5.043 1.24658 6.0137 1.00091 7 1ZM5.5 3.935C7.3158 3.32693 9.2838 3.34984 11.085 4C10.8414 5.2703 10.3094 6.4677 9.53 7.5C9.312 7.4077 9.0707 7.3855 8.8395 7.4366C8.6083 7.4877 8.3988 7.6094 8.24 7.785C8.065 7.685 7.89 7.585 7.74 7.47C6.7307 6.7966 5.8877 5.9023 5.275 4.855C5.374 4.73221 5.4461 4.58996 5.4866 4.43749C5.5271 4.28502 5.5351 4.12575 5.51 3.97L5.5 3.935ZM3.5 2.135C3.5 2.24 3.53 2.35 3.55 2.455C3.595 2.675 3.655 2.89 3.715 3.105C3.52353 3.21838 3.36943 3.38531 3.2717 3.58522C3.17397 3.78513 3.13688 4.00927 3.165 4.23C2.37575 4.7454 1.67078 5.3795 1.075 6.11C1.19455 5.3189 1.47112 4.55966 1.88843 3.87701C2.30575 3.19437 2.85539 2.60208 3.505 2.135H3.5ZM3.5 9.99C3.30481 10.0555 3.13037 10.1714 2.9943 10.3259C2.85822 10.4804 2.76533 10.6681 2.725 10.87H2.405C1.59754 9.9069 1.1146 8.7136 1.025 7.46L1.08 7.365C1.70611 6.3942 2.52463 5.562 3.485 4.92C3.62899 5.0704 3.81094 5.179 4.01162 5.2345C4.2123 5.2899 4.42423 5.2901 4.625 5.235C5.2938 6.3652 6.208 7.3306 7.3 8.06C7.505 8.195 7.715 8.32 7.925 8.44C7.9082 8.6312 7.9391 8.8237 8.015 9C7.1 9.7266 6.0445 10.256 4.915 10.555C4.78401 10.3103 4.57028 10.1201 4.31199 10.0184C4.05369 9.9167 3.76766 9.9102 3.505 10L3.5 9.99ZM7 12.99C5.9831 12.9903 4.98307 12.7304 4.095 12.235L4.235 12.205C4.43397 12.1397 4.61176 12.0222 4.74984 11.8648C4.88792 11.7074 4.98122 11.5158 5.02 11.31C6.2985 10.984 7.4921 10.3872 8.52 9.56C8.7642 9.7027 9.0525 9.75 9.3295 9.6927C9.6064 9.6355 9.8524 9.4778 10.02 9.25C10.7254 9.4334 11.4511 9.5275 12.18 9.53H12.445C11.9626 10.5673 11.1938 11.4451 10.2291 12.0599C9.2643 12.6747 8.144 13.0009 7 13V12.99ZM10.255 8.54C10.2545 8.3304 10.1975 8.1249 10.09 7.945C10.9221 6.8581 11.5012 5.5991 11.785 4.26C12.035 4.37667 12.2817 4.50667 12.525 4.65C13.0749 5.9495 13.1493 7.4012 12.735 8.75C11.9049 8.8142 11.0698 8.7484 10.26 8.555L10.255 8.54Z' fill='%23646D7A'/%3e%3c/svg%3e";
|
@@ -0,0 +1,150 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { render, waitFor } from "~test/react-render.js";
|
3
|
+
import { TEST_CLIENT } from "~test/test-clients.js";
|
4
|
+
import { ethereum } from "../../../../../chains/chain-definitions/ethereum.js";
|
5
|
+
import { defineChain } from "../../../../../chains/utils.js";
|
6
|
+
import { getFunctionId } from "../../../../../utils/function-id.js";
|
7
|
+
import { ChainIcon, fetchChainIcon, getQueryKeys } from "./icon.js";
|
8
|
+
import { ChainProvider } from "./provider.js";
|
9
|
+
|
10
|
+
const client = TEST_CLIENT;
|
11
|
+
|
12
|
+
describe.runIf(process.env.TW_SECRET_KEY)("ChainIcon", () => {
|
13
|
+
it("fetchChainIcon should respect iconResolver as a string", async () => {
|
14
|
+
expect(
|
15
|
+
await fetchChainIcon({ chain: ethereum, client, iconResolver: "test" }),
|
16
|
+
).toBe("test");
|
17
|
+
});
|
18
|
+
|
19
|
+
it("fetchChainIcon should respect iconResolver as a non-async function", async () => {
|
20
|
+
expect(
|
21
|
+
await fetchChainIcon({
|
22
|
+
chain: ethereum,
|
23
|
+
client,
|
24
|
+
iconResolver: () => "test",
|
25
|
+
}),
|
26
|
+
).toBe("test");
|
27
|
+
});
|
28
|
+
|
29
|
+
it("fetchChainIcon should respect iconResolver as an async function", async () => {
|
30
|
+
expect(
|
31
|
+
await fetchChainIcon({
|
32
|
+
chain: ethereum,
|
33
|
+
client,
|
34
|
+
iconResolver: async () => "test",
|
35
|
+
}),
|
36
|
+
).toBe("test");
|
37
|
+
});
|
38
|
+
|
39
|
+
it("fetchChainIcon should return a resolved url from the backend server, NOT ipfs uri", async () => {
|
40
|
+
const resolvedUrl = await fetchChainIcon({ chain: ethereum, client });
|
41
|
+
expect(
|
42
|
+
resolvedUrl.includes("QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9"),
|
43
|
+
).toBe(true);
|
44
|
+
expect(resolvedUrl.startsWith("https://")).toBe(true);
|
45
|
+
});
|
46
|
+
|
47
|
+
it("fetchChainIcon should return a resolved url from the chain object, NOT the ipfs uri", async () => {
|
48
|
+
const mockEthereum = defineChain({
|
49
|
+
id: 1,
|
50
|
+
name: "Ethereum",
|
51
|
+
nativeCurrency: {
|
52
|
+
name: "Ether",
|
53
|
+
symbol: "ETH",
|
54
|
+
decimals: 18,
|
55
|
+
},
|
56
|
+
blockExplorers: [
|
57
|
+
{
|
58
|
+
name: "Etherscan",
|
59
|
+
url: "https://etherscan.io",
|
60
|
+
},
|
61
|
+
],
|
62
|
+
icon: {
|
63
|
+
url: "ipfs://QmdwQDr6vmBtXmK2TmknkEuZNoaDqTasFdZdu3DRw8b2wt",
|
64
|
+
format: "png",
|
65
|
+
width: 100,
|
66
|
+
height: 100,
|
67
|
+
},
|
68
|
+
});
|
69
|
+
const resolvedUrl = await fetchChainIcon({ chain: mockEthereum, client });
|
70
|
+
expect(
|
71
|
+
resolvedUrl.endsWith(
|
72
|
+
".ipfscdn.io/ipfs/QmdwQDr6vmBtXmK2TmknkEuZNoaDqTasFdZdu3DRw8b2wt",
|
73
|
+
),
|
74
|
+
).toBe(true);
|
75
|
+
expect(resolvedUrl.startsWith("https://")).toBe(true);
|
76
|
+
});
|
77
|
+
|
78
|
+
it("fetchChainIcon should throw error if failed to resolve chain icon", async () => {
|
79
|
+
await expect(() =>
|
80
|
+
fetchChainIcon({ chain: defineChain(-1), client }),
|
81
|
+
).rejects.toThrowError("Failed to resolve icon for chain");
|
82
|
+
});
|
83
|
+
|
84
|
+
it("getQueryKeys should work without resolver", () => {
|
85
|
+
expect(getQueryKeys({ chainId: 1 })).toStrictEqual([
|
86
|
+
"_internal_chain_icon_",
|
87
|
+
1,
|
88
|
+
{
|
89
|
+
resolver: undefined,
|
90
|
+
},
|
91
|
+
]);
|
92
|
+
});
|
93
|
+
|
94
|
+
it("getQueryKeys should work with resolver being a string", () => {
|
95
|
+
expect(getQueryKeys({ chainId: 1, iconResolver: "tw" })).toStrictEqual([
|
96
|
+
"_internal_chain_icon_",
|
97
|
+
1,
|
98
|
+
{
|
99
|
+
resolver: "tw",
|
100
|
+
},
|
101
|
+
]);
|
102
|
+
});
|
103
|
+
|
104
|
+
it("getQueryKeys should work with resolver being a non-async fn that returns a string", () => {
|
105
|
+
const fn = () => "tw";
|
106
|
+
const fnId = getFunctionId(fn);
|
107
|
+
expect(getQueryKeys({ chainId: 1, iconResolver: fn })).toStrictEqual([
|
108
|
+
"_internal_chain_icon_",
|
109
|
+
1,
|
110
|
+
{
|
111
|
+
resolver: fnId,
|
112
|
+
},
|
113
|
+
]);
|
114
|
+
});
|
115
|
+
|
116
|
+
it("getQueryKeys should work with resolver being an async fn that returns a string", () => {
|
117
|
+
const fn = async () => {
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
119
|
+
return "tw";
|
120
|
+
};
|
121
|
+
const fnId = getFunctionId(fn);
|
122
|
+
expect(
|
123
|
+
getQueryKeys({
|
124
|
+
chainId: 1,
|
125
|
+
iconResolver: fn,
|
126
|
+
}),
|
127
|
+
).toStrictEqual([
|
128
|
+
"_internal_chain_icon_",
|
129
|
+
1,
|
130
|
+
{
|
131
|
+
resolver: fnId,
|
132
|
+
},
|
133
|
+
]);
|
134
|
+
});
|
135
|
+
|
136
|
+
it("should render an image", async () => {
|
137
|
+
const { container } = render(
|
138
|
+
<ChainProvider chain={ethereum}>
|
139
|
+
<ChainIcon client={client} />
|
140
|
+
</ChainProvider>,
|
141
|
+
);
|
142
|
+
await waitFor(() => {
|
143
|
+
const image = container.querySelector("img");
|
144
|
+
expect(image).toBeTruthy();
|
145
|
+
expect(
|
146
|
+
image?.src.includes("QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9"),
|
147
|
+
).toBe(true);
|
148
|
+
});
|
149
|
+
});
|
150
|
+
});
|