diff --git a/builder/public/index.html b/builder/index.html
similarity index 91%
rename from builder/public/index.html
rename to builder/index.html
index baca02011..cb3ed2c60 100644
--- a/builder/public/index.html
+++ b/builder/index.html
@@ -2,7 +2,7 @@
-
+
-
+
-
+
Customize Connect
+
diff --git a/builder/package.json b/builder/package.json
index 7f91cad8a..30d1c2dc8 100644
--- a/builder/package.json
+++ b/builder/package.json
@@ -14,7 +14,7 @@
"@types/node": "^16.18.25",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
- "@wormhole-foundation/wormhole-connect": "^0.1.4",
+ "@wormhole-foundation/wormhole-connect": "0.3.3",
"react": "^18.2.0",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
@@ -26,10 +26,8 @@
},
"scripts": {
"lint:ci": "prettier -c ./src && eslint --max-warnings=0 ./src",
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
- "eject": "react-scripts eject"
+ "start": "vite",
+ "build": "NODE_OPTIONS=--max-old-space-size=8192 vite build"
},
"eslintConfig": {
"extends": [
@@ -50,6 +48,12 @@
]
},
"devDependencies": {
- "@types/react-color": "^3.0.7"
+ "@types/react-color": "^3.0.7",
+ "vite": "^5.0.8",
+ "vite-bundle-visualizer": "^0.11.0",
+ "vite-plugin-checker": "^0.6.2",
+ "vite-plugin-dts": "^3.7.3",
+ "vite-plugin-node-polyfills": "^0.17.0",
+ "@vitejs/plugin-react-swc": "^3.5.0"
}
}
diff --git a/builder/src/App.tsx b/builder/src/App.tsx
index ec5ffe160..d71829299 100644
--- a/builder/src/App.tsx
+++ b/builder/src/App.tsx
@@ -20,9 +20,7 @@ import {
DialogTitle,
Divider,
Drawer,
- FormControl,
FormControlLabel,
- FormLabel,
Grid,
IconButton,
ListItemText,
@@ -36,44 +34,40 @@ import {
Typography,
useMediaQuery,
} from "@mui/material";
-import WormholeBridge, {
+import WormholeConnect, {
ChainName,
- MAINNET_CHAINS,
- MainnetChainName,
- Rpcs,
- TESTNET_CHAINS,
- TestnetChainName,
- Theme,
WormholeConnectConfig,
- defaultTheme,
+ dark,
+ MAINNET,
+ TESTNET,
} from "@wormhole-foundation/wormhole-connect";
+import type { WormholeConnectPartialTheme } from "@wormhole-foundation/wormhole-connect";
+
import { useCallback, useEffect, useMemo, useState } from "react";
import { useDebounce } from "use-debounce";
import Background from "./Background";
import ColorPicker from "./components/ColorPicker";
import RouteCard from "./components/RouteCard";
-import {
- DEFAULT_MAINNET_RPCS,
- DEFAULT_TESTNET_RPCS,
- MAINNET_TOKEN_KEYS,
- NETWORKS,
- ROUTE_INFOS,
- TESTNET_TOKEN_KEYS,
-} from "./consts";
+import ErrorBoundary from "./components/ErrorBoundary";
+import { ROUTE_INFOS } from "./consts";
import {
copyTextToClipboard,
getObjectPath,
setObjectPathImmutable,
} from "./utils";
-const version = "0.1.4";
+export type Rpcs = {
+ [chain in ChainName]?: string;
+};
+
+const MAINNET_TOKENS = Object.keys(MAINNET.tokens);
+const TESTNET_TOKENS = Object.keys(TESTNET.tokens);
+const VERSION = "0.3.3";
// generated with https://www.srihash.org/
-const versionScriptIntegrity =
- "sha384-NUeZzmcZSVf4i3sUHD1rhtihig08WkCKu42too+Na8ll/2Sxr8v8D1i9IuVrF70A";
-const versionLinkIntegrity =
- "sha384-KGZI5sQxWDSIe8Xzhvu4eO0fi8KYtEmDnYS2Qn5xrtw667xfxFINL3uN48d/djuY";
-const nonBreakingTag = "latest-v0.1";
-const latestTag = "latest";
+const SCRIPT_INTEGRITY_HASH =
+ "sha384-be7tSjGKf3akqV+FFKLt4241MoVYmg6rZKe9k3uQLePzk4lEY0t9VZvLjduwsVgG";
+const STYLESHEET_INTEGRITY_HASH =
+ "sha384-BTkX2AhTeIfxDRFsJbLtR26TQ9QKKpi7EMe807JdfQQBTAkUT9a2mSGwf/5CJ4bF";
// registerRpcProvider throws on invalid RPCs
const isValidRpc = (rpc?: string) =>
@@ -165,7 +159,7 @@ const ScreenButton = ({
);
};
-const customized = defaultTheme;
+const customized = dark;
customized.background.default = "transparent";
const defaultThemeJSON = JSON.stringify(customized, undefined, 2);
@@ -215,17 +209,16 @@ function App() {
};
}
}, [debouncedFontHref]);
- const [_customTheme, setCustomTheme] = useState(undefined);
- const [debouncedCustomTheme] = useDebounce(_customTheme, 1000);
- const customTheme =
- _customTheme === undefined ? undefined : debouncedCustomTheme;
+ const [customTheme, setCustomTheme] = useState<
+ WormholeConnectPartialTheme | undefined
+ >(undefined);
const [customThemeText, setCustomThemeText] = useState(defaultThemeJSON);
// const [customThemeError, setCustomThemeError] = useState(false);
const handleModeChange = useCallback(
(e: any, value: string) => {
if (value === "dark" || value === "light") {
setMode(value);
- setCustomTheme(undefined);
+ setCustomTheme({ mode: value });
} else {
setMode(undefined);
try {
@@ -266,17 +259,12 @@ function App() {
}, []);
// END ROUTES
// BEGIN ENV
- const [env, setEnv] = useState<"testnet" | "mainnet">("testnet");
- const [_networkIndexes, setNetworkIndexes] = useState(
+ const [env, setEnv] = useState<"testnet" | "mainnet">("mainnet");
+ const [selectedChains, setSelectedChains] = useState(
undefined
);
- const [networkIndexes] = useDebounce(_networkIndexes, 1000);
const [_tokens, setTokens] = useState(undefined);
const [tokens] = useDebounce(_tokens, 1000);
- const testnetTokens = useMemo(
- () => tokens && TESTNET_TOKEN_KEYS.filter((t) => tokens?.includes(t)),
- [tokens]
- );
const [defaultFromNetwork, setDefaultFromNetwork] = useState<
ChainName | undefined
>(undefined);
@@ -296,6 +284,7 @@ function App() {
const [defaultToken, setDefaultToken] = useState(
undefined
);
+ const [hideConnect, setHideConnect] = useState(false);
const handleDefaultTokenChange = useCallback((e: any) => {
e.target.value
? setDefaultToken(e.target.value)
@@ -311,21 +300,21 @@ function App() {
}, []);
// networks and tokens handlers come after defaults so they can appropriately reset them
const handleClearNetworks = useCallback(() => {
- setNetworkIndexes(undefined);
+ setSelectedChains(undefined);
}, []);
const handleNoneNetworks = useCallback(() => {
// clear defaults to avoid bugs (could be smarter)
setDefaultFromNetwork(undefined);
setDefaultToNetwork(undefined);
setRequiredNetwork(undefined);
- setNetworkIndexes([]);
+ setSelectedChains([]);
}, []);
const handleNetworksChange = useCallback((e: any) => {
// clear defaults to avoid bugs (could be smarter)
setDefaultFromNetwork(undefined);
setDefaultToNetwork(undefined);
setRequiredNetwork(undefined);
- setNetworkIndexes(
+ setSelectedChains(
typeof e.target.value === "string"
? e.target.value
.split(",")
@@ -352,7 +341,18 @@ function App() {
);
}, []);
const [_rpcs, setRpcs] = useState(undefined);
- const [deRpcs] = useDebounce(_rpcs, 1000);
+ const rpcs = useMemo(() => {
+ let valid: Rpcs = {};
+ for (let key in _rpcs) {
+ let cn = key as ChainName;
+ if (isValidRpc(_rpcs[cn])) {
+ valid[cn] = _rpcs[cn];
+ }
+ }
+ if (Object.keys(valid).length === 0) return undefined;
+ return valid;
+ }, [_rpcs]);
+
const handleRpcChange = useCallback((e: any) => {
setRpcs((prevState) => ({
...prevState,
@@ -366,58 +366,35 @@ function App() {
const handleRpcsClose = useCallback(() => {
setRpcsOpen(false);
}, []);
- const [rpcs, testnetRpcs] = useMemo(() => {
- // Connect doesn't like if you specify an rpc for a chain name that isn't in the env
- // For now, filter the config so it doesn't cause an error
- // TODO: change Connect so that the chain names are the same for testnet and mainnet
- return !deRpcs
- ? [undefined, undefined]
- : Object.entries(deRpcs).reduce<
- [
- { [chain in ChainName]?: string } | undefined,
- { [chain in TestnetChainName]?: string } | undefined
- ]
- >(
- ([rpcs, testnetRpcs], [n, rpc]) => [
- isValidRpc(rpc) &&
- (env === "mainnet"
- ? MAINNET_CHAINS[n as MainnetChainName]
- : TESTNET_CHAINS[n as TestnetChainName])
- ? { ...rpcs, [n]: rpc }
- : rpcs,
- isValidRpc(rpc) && TESTNET_CHAINS[n as TestnetChainName]
- ? { ...testnetRpcs, [n]: rpc }
- : testnetRpcs,
- ],
- [undefined, undefined]
- );
- }, [deRpcs, env]);
const handleEnvChange = useCallback((e: any, value: string) => {
if (value === "testnet" || value === "mainnet") {
- // TODO: keep tokens that exist in both envs, for now clear it before it doesn't match the options
setTokens(undefined);
- // clear defaults to avoid bugs (could be smarter)
+ setSelectedChains(undefined);
setDefaultFromNetwork(undefined);
setDefaultToNetwork(undefined);
setDefaultToken(undefined);
setRequiredNetwork(undefined);
- // set env last
- setEnv(value);
+
+ // These timeouts are necessary because the config
+ // is used to reset the transferInput redux store
+ // and if we change the network before that can happen
+ // we get all kinds of fun errors
+ setTimeout(() => {
+ setHideConnect(true);
+
+ setTimeout(() => {
+ setEnv(value);
+
+ setTimeout(() => {
+ setHideConnect(false);
+ }, 50);
+ }, 50);
+ }, 50);
}
}, []);
- const [networks, testnetNetworks] = useMemo(() => {
- return !networkIndexes
- ? [undefined, undefined]
- : NETWORKS.filter((v, i) => networkIndexes.indexOf(i) > -1).reduce<
- [ChainName[], TestnetChainName[]]
- >(
- ([networks, testnetNetworks], v) => [
- [...networks, v[env]],
- [...testnetNetworks, v.testnet],
- ],
- [[], []]
- );
- }, [networkIndexes, env]);
+
+ const chains = env === "mainnet" ? MAINNET.chains : TESTNET.chains;
+
// END ENV
// START BRIDGE COMPLETE
const [_ctaText, setCtaText] = useState("");
@@ -441,7 +418,7 @@ function App() {
setDefaultToNetwork(undefined);
setDefaultToken(undefined);
setRequiredNetwork(undefined);
- setNetworkIndexes(undefined);
+ setSelectedChains(undefined);
setTokens(undefined);
setRpcs(undefined);
setEnv("testnet");
@@ -458,16 +435,13 @@ function App() {
},
[]
);
- // NOTE: the WormholeBridge component is keyed by the stringified version of config
+ // NOTE: the WormholeConnect component is keyed by the stringified version of config
// because otherwise the component did not update on changes
const config: WormholeConnectConfig = useMemo(
() => ({
- env: "testnet", // always testnet for the builder
- rpcs: testnetRpcs,
- networks: testnetNetworks, // always testnet for the builder
- tokens: testnetTokens, // always testnet for the builder
- mode,
- customTheme,
+ env,
+ rpcs,
+ tokens, // always testnet for the builder
cta:
ctaText && ctaLink
? {
@@ -489,14 +463,14 @@ function App() {
: undefined,
routes,
pageHeader,
+ networks: selectedChains,
showHamburgerMenu,
}),
[
- testnetRpcs,
- testnetNetworks,
- testnetTokens,
- mode,
- customTheme,
+ env,
+ rpcs,
+ tokens,
+ selectedChains,
ctaText,
ctaLink,
defaultFromNetwork,
@@ -508,35 +482,31 @@ function App() {
showHamburgerMenu,
]
);
- const [versionOrTag, setVersionOrTag] = useState(version);
- const handleVersionOrTagChange = useCallback((e: any, value: string) => {
- setVersionOrTag(value);
- }, []);
+
const [htmlCode, jsxCode] = useMemo(() => {
- const realConfig = { ...config, env, rpcs, networks, tokens };
- const realConfigString = JSON.stringify(realConfig);
+ const configString = JSON.stringify(config);
+ const themeStringHTML = customTheme
+ ? ` data-theme='${JSON.stringify(customTheme)}'`
+ : "";
+ const themeStringJSX = customTheme
+ ? ` theme={${JSON.stringify(customTheme)}}`
+ : "";
+
return [
- `
-
-`,
- `import WormholeBridge from '@wormhole-foundation/wormhole-connect';
+ // Hosted version (CDN)
+ `
+
+`,
+
+ // React version
+ `import WormholeConnect from '@wormhole-foundation/wormhole-connect';
function App() {
return (
-
+
);
}`,
];
- }, [config, env, rpcs, networks, tokens, versionOrTag]);
+ }, [config, customTheme]);
const [openCopySnack, setOpenCopySnack] = useState(false);
const handleCopySnackClose = useCallback(() => {
setOpenCopySnack(false);
@@ -706,7 +676,7 @@ function App() {
Widget
@@ -818,7 +788,7 @@ function App() {
Action
Configure RPCs
- {NETWORKS.map((n) => (
-
- ))}
+ {Object.keys(
+ env === "testnet" ? TESTNET.chains : MAINNET.chains
+ ).map((cn) => {
+ const chain = cn as ChainName;
+ return (
+
+ );
+ })}