Merge branch 'main' into main-to-injective

pull/272/head
J M Rossy 1 month ago
commit 848f1eea9b
  1. 19
      CUSTOMIZE.md
  2. 6
      package.json
  3. 1
      public/backgrounds/main.svg
  4. BIN
      public/fonts/NeueHaasDisplayBold.woff
  5. BIN
      public/fonts/NeueHaasDisplayBold.woff2
  6. BIN
      public/fonts/NeueHaasDisplayLight.woff
  7. BIN
      public/fonts/NeueHaasDisplayLight.woff2
  8. BIN
      public/fonts/NeueHaasDisplayMedium.woff
  9. BIN
      public/fonts/NeueHaasDisplayMedium.woff2
  10. BIN
      public/fonts/NeueHaasDisplayRoman.woff
  11. BIN
      public/fonts/NeueHaasDisplayRoman.woff2
  12. BIN
      public/fonts/NeueHaasDisplayThin.woff
  13. BIN
      public/fonts/NeueHaasDisplayThin.woff2
  14. 4
      src/components/banner/FormWarningBanner.tsx
  15. 4
      src/components/buttons/ConnectAwareSubmitButton.tsx
  16. 37
      src/components/buttons/SolidButton.tsx
  17. 2
      src/components/icons/Chevron.tsx
  18. 2
      src/components/icons/Identicon.tsx
  19. 2
      src/components/icons/WideChevron.tsx
  20. 2
      src/components/input/TextField.tsx
  21. 14
      src/components/layout/AppLayout.tsx
  22. 9
      src/components/layout/Card.tsx
  23. 122
      src/components/nav/Footer.tsx
  24. 12
      src/components/tip/TipCard.tsx
  25. 9
      src/consts/app.ts
  26. 28
      src/consts/config.ts
  27. 4
      src/context/WarpContext.tsx
  28. 27
      src/features/chains/ChainSelectField.tsx
  29. 6
      src/features/chains/ChainSelectModal.tsx
  30. 2
      src/features/tokens/SelectTokenIdField.tsx
  31. 4
      src/features/tokens/TokenSelectField.tsx
  32. 57
      src/features/transfer/TransferTokenForm.tsx
  33. 10
      src/features/transfer/TransfersDetailsModal.tsx
  34. 19
      src/features/transfer/useTokenTransfer.ts
  35. 34
      src/features/wallet/SideBarMenu.tsx
  36. 4
      src/features/wallet/WalletControlBar.tsx
  37. 2
      src/features/wallet/WalletEnvSelectionModal.tsx
  38. 2
      src/features/wallet/context/EvmWalletContext.tsx
  39. 1
      src/images/backgrounds/footer-bg.svg
  40. 4
      src/images/backgrounds/main-bg.svg
  41. 3
      src/images/logos/github copy.svg
  42. BIN
      src/images/planets/planet-1.webp
  43. BIN
      src/images/planets/planet-2.webp
  44. 1
      src/pages/_app.tsx
  45. 4
      src/pages/_document.tsx
  46. 2
      src/pages/index.tsx
  47. 39
      src/styles/Color.ts
  48. 39
      src/styles/fonts.css
  49. 60
      tailwind.config.js
  50. 58
      yarn.lock

@ -27,10 +27,15 @@ Or it can be hidden entirely with the `showTipBox` setting in `./src/consts/conf
## Branding
## App name
## App name and description
The values to describe the app itself (e.g. to WalletConnect) are in `./src/consts/app.ts`
### Color Scheme
To update the color scheme, make changes in the Tailwind config file at `./tailwind.config.js`
To modify just the background color, that can be changed in `./src/consts/app.ts`
### Metadata
The HTML metadata tags are located in `./src/pages/_document.tsx`
@ -55,15 +60,3 @@ The links used in the footer can be found here: `./src/consts/links.ts`
### Public assets / Favicons
The images and manifest files under `./public` should also be updated.
### Fonts
The web-formatted font files are located in `./public/fonts`
And the CSS to configure them is in `./src/styles/fonts.css`
### Color Scheme
To update the color scheme, make changes in the Tailwind config and Color consts file:
- `./tailwind.config.js`
- `./src/styles/Color.ts`

@ -18,9 +18,9 @@
"@emotion/styled": "^11.13.0",
"@headlessui/react": "^1.7.14",
"@hyperlane-xyz/registry": "4.3.6",
"@hyperlane-xyz/sdk": "5.2.1",
"@hyperlane-xyz/utils": "5.2.1",
"@hyperlane-xyz/widgets": "5.2.1",
"@hyperlane-xyz/sdk": "5.3.0",
"@hyperlane-xyz/utils": "5.3.0",
"@hyperlane-xyz/widgets": "5.3.0",
"@interchain-ui/react": "^1.23.28",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@metamask/post-message-stream": "6.1.2",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

@ -1,12 +1,12 @@
import { ComponentProps } from 'react';
import { WarningBanner } from '../../components/banner/WarningBanner';
import { cardStyles } from '../layout/Card';
export function FormWarningBanner({ className, ...props }: ComponentProps<typeof WarningBanner>) {
return (
<WarningBanner
// The margins here should be the inverse of those in Card.tsx
className={`z-20 -m-1.5 mb-0 sm:-m-3 sm:mb-0 md:-m-3.5 md:mb-0 ${className}`}
className={`z-20 ${cardStyles.inverseMargin} mb-0 sm:mb-0 md:mb-0 ${className}`}
{...props}
/>
);

@ -28,8 +28,8 @@ export function ConnectAwareSubmitButton<FormValues = any>({ chainName, text, cl
const hasError = Object.keys(touched).length > 0 && Object.keys(errors).length > 0;
const firstError = `${Object.values(errors)[0]}` || 'Unknown error';
const color = hasError ? 'red' : 'blue';
const content = hasError ? firstError : isAccountReady ? text : 'Connect Wallet';
const color = hasError ? 'red' : 'primary';
const content = hasError ? firstError : isAccountReady ? text : 'Connect wallet';
const type = isAccountReady ? 'submit' : 'button';
const onClick = isAccountReady ? undefined : connectFn;

@ -2,7 +2,7 @@ import { PropsWithChildren, ReactElement } from 'react';
interface ButtonProps {
type?: 'submit' | 'reset' | 'button';
color?: 'white' | 'blue' | 'green' | 'red' | 'gray' | 'pink' | 'mint'; // defaults to blue
color?: 'white' | 'primary' | 'accent' | 'green' | 'red' | 'gray'; // defaults to primary
bold?: boolean;
classes?: string;
icon?: ReactElement;
@ -22,42 +22,33 @@ export function SolidButton(
title,
...passThruProps
} = props;
const color = _color ?? 'blue';
const color = _color ?? 'primary';
const base = 'flex items-center justify-center rounded-full transition-all duration-500';
let baseColors, onHover, onActive;
if (color === 'blue') {
baseColors = 'bg-blue-500 text-white';
onHover = 'hover:bg-blue-600';
onActive = 'active:bg-blue-700';
} else if (color === 'pink') {
baseColors = 'bg-pink-500 text-white';
onHover = 'hover:bg-pink-600';
onActive = 'active:bg-pink-700';
const base =
'flex items-center justify-center rounded-lg transition-all duration-500 active:scale-95';
let baseColors, onHover;
if (color === 'primary') {
baseColors = 'bg-primary-500 text-white';
onHover = 'hover:bg-primary-600';
} else if (color === 'accent') {
baseColors = 'bg-accent-500 text-white';
onHover = 'hover:bg-accent-600';
} else if (color === 'green') {
baseColors = 'bg-green-500 text-white';
onHover = 'hover:bg-green-600';
onActive = 'active:bg-green-700';
} else if (color === 'mint') {
baseColors = 'bg-mint-500 text-white';
onHover = 'hover:bg-mint-600';
onActive = 'active:bg-mint-700';
} else if (color === 'red') {
baseColors = 'bg-red-600 text-white';
onHover = 'hover:bg-red-500';
onActive = 'active:bg-red-400';
} else if (color === 'white') {
baseColors = 'bg-white text-black';
onHover = 'hover:bg-blue-100';
onActive = 'active:bg-blue-200';
onHover = 'hover:bg-primary-100';
} else if (color === 'gray') {
baseColors = 'bg-gray-100 text-blue-500';
baseColors = 'bg-gray-100 text-primary-500';
onHover = 'hover:bg-gray-200';
onActive = 'active:bg-gray-300';
}
const onDisabled = 'disabled:bg-gray-300 disabled:text-gray-500';
const weight = bold ? 'font-semibold' : '';
const allClasses = `${base} ${baseColors} ${onHover} ${onDisabled} ${onActive} ${weight} ${classes}`;
const allClasses = `${base} ${baseColors} ${onHover} ${onDisabled} ${weight} ${classes}`;
return (
<button

@ -40,7 +40,7 @@ function _ChevronIcon({ width, height, direction, color, classes }: Props) {
<path
d="M1 1l6 6 6-6"
strokeWidth="2"
stroke={color || Color.primaryBlack}
stroke={color || Color.black}
fill="none"
fillRule="evenodd"
strokeLinecap="round"

@ -21,7 +21,7 @@ function _Identicon({ address, size: _size }: Props) {
// TODO better handling of non-evm addresses here
if (!address || !isValidAddressEvm(address)) {
return <Circle size={size} classes="bg-blue-500" title="" />;
return <Circle size={size} classes="bg-primary-500" title="" />;
}
const jazziconResult = jazzicon(size, addressToSeed(address));

@ -5,7 +5,7 @@ import { Color } from '../../styles/Color';
export function WideChevron({ classes }: { classes?: string }) {
return (
<WideChevronInner
width="17"
width="16"
height="100%"
direction="e"
color={Color.lightGray}

@ -26,4 +26,4 @@ export function TextInput({ onChange, classes, ...props }: InputProps) {
}
const defaultInputClasses =
'mt-1.5 px-2.5 py-2 text-sm rounded-full border border-blue-300 focus:border-blue-500 disabled:bg-gray-150 outline-none transition-all duration-300';
'mt-1.5 px-2.5 py-2.5 text-sm rounded-lg border border-primary-300 focus:border-primary-500 disabled:bg-gray-150 outline-none transition-all duration-300';

@ -1,7 +1,7 @@
import Head from 'next/head';
import { PropsWithChildren } from 'react';
import { APP_NAME } from '../../consts/app';
import { APP_NAME, MAIN_FONT } from '../../consts/app';
import { Footer } from '../nav/Footer';
import { Header } from '../nav/Header';
@ -15,7 +15,7 @@ export function AppLayout({ children }: PropsWithChildren) {
</Head>
<div
id="app-content"
className="relative flex flex-col justify-between h-full min-h-screen w-full min-w-screen"
className={`relative flex flex-col justify-between h-full min-h-screen w-full min-w-screen ${MAIN_FONT.className}`}
>
<Header />
<div className="sm:px-4 mx-auto grow flex items-center max-w-screen-xl">
@ -26,3 +26,13 @@ export function AppLayout({ children }: PropsWithChildren) {
</>
);
}
// const styles = {
// container: {
// backgroundColor: BACKGROUND_COLOR,
// backgroundImage: BACKGROUND_IMAGE,
// backgroundSize: 'cover',
// backgroundRepeat: 'no-repeat',
// backgroundPosition: 'center',
// },
// };

@ -7,9 +7,16 @@ interface Props {
export function Card({ className, children }: PropsWithChildren<Props>) {
return (
<div
className={`p-1.5 sm:p-3 md:p-3.5 relative bg-white rounded-3xl overflow-auto ${className}`}
className={`${cardStyles.padding} relative bg-white rounded-2xl overflow-auto ${className}`}
>
{children}
</div>
);
}
export const cardStyles = {
padding: 'p-1.5 xs:p-2 sm:p-3 md:p-4',
// Should be inverse of cardPadding, used when something
// should be flush with card edge
inverseMargin: '-m-1.5 xs:-m-2 sm:-m-3 md:-m-4',
};

@ -30,67 +30,79 @@ export function Footer() {
<footer className="text-gray-800 opacity-90 relative">
<div className="relative z-10 px-8 pb-3 pt-2 sm:pt-0">
<div className="flex flex-col sm:flex-row gap-6 sm:gap-10 items-center sm:items-end justify-between">
<div className="py-1 flex items-center justify-center space-x-2">
<div className="flex items-center font-medium space-x-1">
<span>Built with</span>
<Link href={links.about} className="flex items-center space-x-1" target="_blank">
<Image src={HyperlaneLogo} alt="" width={17} height={17} />
<span>Hyperlane</span>
</Link>
<span>and</span>
<Link href={links.caldera} className="flex items-center space-x-1" target="_blank">
<Image src="/logos/caldera.png" alt="" width={24} height={18} />
<span>Caldera</span>
</Link>
</div>
</div>
<nav className="flex text-md font-medium space-x-10">
<ul className={`${styles.linkCol}`}>
{footerLinks1.map((item) => (
<li className="" key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
<div className="text-sm">{item.title}</div>
</Link>
</li>
))}
</ul>
<ul className={`${styles.linkCol}`}>
{footerLinks2.map((item) => (
<li className="" key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
<div className="text-sm">{item.title}</div>
</Link>
</li>
))}
</ul>
<ul className={`${styles.linkCol}`}>
{footerLinks3.map((item) => (
<li key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
{item?.icon && <div className="mt-1 mr-3 w-4">{item?.icon}</div>}
</Link>
</li>
))}
</ul>
</nav>
<FooterLogo />
<FooterNav />
</div>
</div>
</footer>
);
}
function FooterLogo() {
return (
<div className="py-1 flex items-center justify-center space-x-2">
<div className="flex items-center font-medium space-x-1">
<span>Built with</span>
<Link href={links.about} className="flex items-center space-x-1" target="_blank">
<Image src={HyperlaneLogo} alt="" width={17} height={17} />
<span>Hyperlane</span>
</Link>
<span>and</span>
<Link href={links.caldera} className="flex items-center space-x-1" target="_blank">
<Image src="/logos/caldera.png" alt="" width={24} height={18} />
<span>Caldera</span>
</Link>
</div>
</div>
);
}
function FooterNav() {
return (
<nav className="flex text-md font-medium space-x-10">
<ul className={`${styles.linkCol}`}>
{footerLinks1.map((item) => (
<li className="" key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
<div className="text-sm">{item.title}</div>
</Link>
</li>
))}
</ul>
<ul className={`${styles.linkCol}`}>
{footerLinks2.map((item) => (
<li className="" key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
<div className="text-sm">{item.title}</div>
</Link>
</li>
))}
</ul>
<ul className={`${styles.linkCol}`}>
{footerLinks3.map((item) => (
<li key={item.title}>
<Link
className={styles.linkItem}
target={item.external ? '_blank' : '_self'}
href={item.url}
>
{item?.icon && <div className="mt-1 mr-3 w-4">{item?.icon}</div>}
</Link>
</li>
))}
</ul>
</nav>
);
}
const styles = {
linkCol: 'flex flex-col gap-1.5',
linkItem: 'flex items-center capitalize text-decoration-none hover:underline underline-offset-2',

@ -12,10 +12,10 @@ export function TipCard() {
const [show, setShow] = useState(config.showTipBox);
if (!show) return null;
return (
<Card className="w-100 sm:w-[31rem]">
<h2 className="text-blue-500 sm:text-lg">Bridge Tokens with Hyperlane Warp Routes!</h2>
<Card className="w-100 sm:w-[31rem] p-2">
<h2 className="text-primary-500">Bridge Tokens with Hyperlane Warp Routes!</h2>
<div className="flex items-end justify-between">
<p className="mt-1 text-xs sm:text-sm max-w-[70%]">
<p className="mt-1 text-xs max-w-[75%]">
Warp Routes make it easy to permissionlessly take your tokens interchain. Fork this
template to get started!
</p>
@ -23,10 +23,10 @@ export function TipCard() {
href={links.github}
target="_blank"
rel="noopener noreferrer"
className="ml-2 px-3 py-1.5 flex items-center bg-gray-100 hover:bg-gray-200 active:bg-gray-300 text-xs sm:text-sm text-blue-500 rounded-full transition-all"
className="ml-2 px-3 py-1.5 flex items-center bg-gray-100 hover:bg-gray-200 active:bg-gray-300 text-xs sm:text-sm text-primary-500 rounded-lg transition-all"
>
<Image src={InfoCircle} width={16} alt="" />
<span className="ml-1.5">Learn More</span>
<Image src={InfoCircle} width={12} alt="" />
<span className="hidden sm:inline ml-1.5 text-sm">More</span>
</a>
</div>
<div className="absolute right-3 top-3">

@ -1,5 +1,12 @@
import { Inter } from 'next/font/google';
import { Color } from '../styles/Color';
export const MAIN_FONT = Inter({ subsets: ['latin'] });
export const APP_NAME = 'Injective Token Bridge';
export const APP_DESCRIPTION = 'A token bridge for Injective powered by Hyperlane';
export const APP_URL = 'https://inevmbridge.com';
export const APP_BRAND_COLOR = '#0082FA';
export const BRAND_COLOR = Color.primary;
export const BACKGROUND_COLOR = Color.primary;
export const BACKGROUND_IMAGE = 'url(/backgrounds/main.svg)';
export const PROXY_DEPLOYED_URL = 'https://proxy.hyperlane.xyz';

@ -12,31 +12,31 @@ const transferBlacklist = process?.env?.NEXT_PUBLIC_TRANSFER_BLACKLIST || '';
const chainWalletWhitelists = JSON.parse(process?.env?.NEXT_PUBLIC_CHAIN_WALLET_WHITELISTS || '{}');
interface Config {
addressBlacklist: string[]; // A list of addresses that are blacklisted and cannot be used in the app
chainWalletWhitelists: ChainMap<string[]>; // A map of chain names to a list of wallet names that work for it
enableExplorerLink: boolean; // Include a link to the hyperlane explorer in the transfer modal
explorerApiKeys: Record<string, string>; // Optional map of API keys for block explorer
isDevMode: boolean; // Enables some debug features in the app
version: string; // Matches version number in package.json
registryUrl: string | undefined; // Optional URL to use a custom registry instead of the published canonical version
explorerApiKeys: Record<string, string>; // Optional map of API keys for block explorer
showTipBox: boolean; // Show/Hide the blue tip box above the transfer form
showDisabledTokens: boolean; // Show/Hide invalid token options in the selection modal
showTipBox: boolean; // Show/Hide the blue tip box above the transfer form
transferBlacklist: string; // comma-separated list of routes between which transfers are disabled. Expects Caip2Id-Caip2Id (e.g. ethereum:1-sealevel:1399811149)
version: string; // Matches version number in package.json
walletConnectProjectId: string; // Project ID provided by walletconnect
withdrawalWhitelist: string; // comma-separated list of CAIP2 chain IDs to which transfers are supported
transferBlacklist: string; // comma-separated list of routes between which transfers are disabled. Expects Caip2Id-Caip2Id (e.g. ethereum:1-sealevel:1399811149)
enableExplorerLink: boolean; // Include a link to the hyperlane explorer in the transfer modal
addressBlacklist: string[]; // A list of addresses that are blacklisted and cannot be used in the app
chainWalletWhitelists: ChainMap<string[]>; // A map of chain names to a list of wallet names that work for it
}
export const config: Config = Object.freeze({
addressBlacklist: ADDRESS_BLACKLIST.map((address) => address.toLowerCase()),
chainWalletWhitelists,
enableExplorerLink: false,
explorerApiKeys,
isDevMode,
version,
registryUrl,
explorerApiKeys,
showTipBox: false,
showDisabledTokens: true,
showTipBox: false,
version,
transferBlacklist,
walletConnectProjectId,
withdrawalWhitelist,
transferBlacklist,
enableExplorerLink: false,
addressBlacklist: ADDRESS_BLACKLIST.map((address) => address.toLowerCase()),
chainWalletWhitelists,
});

@ -31,8 +31,8 @@ export function WarpContext({ children }: PropsWithChildren<unknown>) {
if (isLoading || !warpContext)
return (
<div className="flex items-center justify-center h-screen">
<Spinner classes="opacity-50" white />
<div className="flex items-center justify-center h-screen bg-white">
<Spinner classes="opacity-50" />
</div>
);

@ -37,24 +37,23 @@ export function ChainSelectField({ name, label, chains, onChange, disabled }: Pr
};
return (
<div className="flex flex-col items-center">
<div className="flex flex-col items-center justify-center rounded-full bg-gray-100 h-[5.5rem] w-[5.5rem] p-1.5">
<div className="flex items-end h-11">
<ChainLogo chainName={field.value} size={34} />
</div>
<label htmlFor={name} className="mt-2 mb-1 text-sm text-gray-500 uppercase">
{label}
</label>
</div>
<div className="flex-[4]">
<button
type="button"
name={field.name}
className={`${styles.base} ${disabled ? styles.disabled : styles.enabled}`}
onClick={onClick}
>
<div className="flex items-center">
<ChainLogo chainName={field.value} size={14} />
<span className="ml-2">{getChainDisplayName(field.value, true)}</span>
<div className="flex items-center gap-3">
<div className="max-w-[1.4rem] sm:max-w-fit">
<ChainLogo chainName={field.value} size={32} />
</div>
<div className="flex flex-col gap-1 items-start">
<label htmlFor={name} className="text-xs text-gray-600">
{label}
</label>
{getChainDisplayName(field.value, true)}
</div>
</div>
<Image src={ChevronIcon} width={12} height={8} alt="" />
</button>
@ -69,7 +68,7 @@ export function ChainSelectField({ name, label, chains, onChange, disabled }: Pr
}
const styles = {
base: 'w-36 px-2.5 py-2 relative -top-1.5 flex items-center justify-between text-sm bg-white rounded-full border border-blue-300 outline-none transition-colors duration-500',
enabled: 'hover:bg-gray-50 active:bg-gray-100 focus:border-blue-500',
base: 'px-2 py-1.5 w-full flex items-center justify-between text-sm bg-white rounded-lg border border-primary-300 outline-none transition-colors duration-500',
enabled: 'hover:bg-gray-100 active:scale-95 focus:border-primary-500',
disabled: 'bg-gray-150 cursor-default',
};

@ -1,3 +1,5 @@
import { useMemo } from 'react';
import { ChainLogo } from '../../components/icons/ChainLogo';
import { Modal } from '../../components/layout/Modal';
@ -21,10 +23,12 @@ export function ChainSelectListModal({
};
};
const sortedChains = useMemo(() => chains.sort(), [chains]);
return (
<Modal isOpen={isOpen} title="Select Chain" close={close}>
<div className="mt-2 flex flex-col space-y-1">
{chains.map((c) => (
{sortedChains.map((c) => (
<button
key={c}
className="py-1.5 px-2 text-sm flex items-center rounded hover:bg-gray-100 active:bg-gray-200 transition-all duration-200"

@ -100,6 +100,6 @@ export function SelectTokenIdModal({
const styles = {
base: 'mt-1.5 w-full px-2.5 py-2 flex items-center justify-between text-sm bg-white rounded border border-gray-400 outline-none transition-colors duration-500',
enabled: 'hover:bg-gray-50 active:bg-gray-100 focus:border-blue-500',
enabled: 'hover:bg-gray-50 active:bg-gray-100 focus:border-primary-500',
disabled: 'bg-gray-150 cursor-default',
};

@ -105,7 +105,7 @@ function TokenButton({
}
const styles = {
base: 'mt-1.5 w-full px-2.5 py-2 flex items-center justify-between text-sm rounded-full border border-blue-300 outline-none transition-colors duration-500',
enabled: 'hover:bg-gray-50 active:bg-gray-100 focus:border-blue-500',
base: 'mt-1.5 w-full px-2.5 py-2.5 flex items-center justify-between text-sm rounded-lg border border-primary-300 outline-none transition-colors duration-500',
enabled: 'hover:bg-gray-100 active:scale-95 focus:border-primary-500',
disabled: 'bg-gray-100 cursor-default',
};

@ -11,7 +11,6 @@ import { ConnectAwareSubmitButton } from '../../components/buttons/ConnectAwareS
import { IconButton } from '../../components/buttons/IconButton';
import { SolidButton } from '../../components/buttons/SolidButton';
import { ChevronIcon } from '../../components/icons/Chevron';
import { WideChevron } from '../../components/icons/WideChevron';
import { TextField } from '../../components/input/TextField';
import { getIndexForToken, getTokenByIndex, getTokens, getWarpCore } from '../../context/context';
import SwapIcon from '../../images/icons/swap.svg';
@ -67,7 +66,7 @@ export function TransferTokenForm() {
<Form className="flex flex-col items-stretch w-full">
<ChainWalletWarning originChain={values.origin} />
<ChainSelectSection isReview={isReview} />
<div className="mt-3 flex justify-between items-end space-x-4">
<div className="mt-3.5 flex justify-between items-end space-x-4">
<TokenSection setIsNft={setIsNft} isReview={isReview} />
<AmountSection isNft={isNft} isReview={isReview} />
</div>
@ -100,8 +99,8 @@ function SwapChainsButton({ disabled }: { disabled?: boolean }) {
return (
<IconButton
imgSrc={SwapIcon}
width={22}
height={22}
width={20}
height={20}
title="Swap chains"
classes={!disabled ? 'hover:rotate-180' : undefined}
onClick={onClick}
@ -114,14 +113,9 @@ function ChainSelectSection({ isReview }: { isReview: boolean }) {
const chains = useMemo(() => getWarpCore().getTokenChains(), []);
return (
<div className="mt-2 flex items-center justify-center space-x-7 sm:space-x-10">
<div className="mt-4 flex items-center justify-between gap-4">
<ChainSelectField name="origin" label="From" chains={chains} disabled={isReview} />
<div className="flex flex-col items-center">
<div className="flex mb-6 sm:space-x-1.5">
<WideChevron classes="hidden sm:block" />
<WideChevron />
<WideChevron />
</div>
<div className="flex flex-col items-center flex-1">
<SwapChainsButton disabled={isReview} />
</div>
<ChainSelectField name="destination" label="To" chains={chains} disabled={isReview} />
@ -138,7 +132,7 @@ function TokenSection({
}) {
return (
<div className="flex-1">
<label htmlFor="tokenIndex" className="block uppercase text-sm text-gray-500 pl-0.5">
<label htmlFor="tokenIndex" className="block text-sm text-gray-600 pl-0.5">
Token
</label>
<TokenSelectField name="tokenIndex" disabled={isReview} setIsNft={setIsNft} />
@ -153,7 +147,7 @@ function AmountSection({ isNft, isReview }: { isNft: boolean; isReview: boolean
return (
<div className="flex-1">
<div className="flex justify-between pr-1">
<label htmlFor="amount" className="block uppercase text-sm text-gray-500 pl-0.5">
<label htmlFor="amount" className="block text-sm text-gray-600 pl-0.5">
Amount
</label>
<TokenBalance label="My balance" balance={balance} />
@ -185,8 +179,8 @@ function RecipientSection({ isReview }: { isReview: boolean }) {
return (
<div className="mt-4">
<div className="flex justify-between pr-1">
<label htmlFor="recipient" className="block uppercase text-sm text-gray-500 pl-0.5">
Recipient Address
<label htmlFor="recipient" className="block text-sm text-gray-600 pl-0.5">
Recipient address
</label>
<TokenBalance label="Remote balance" balance={balance} />
</div>
@ -205,7 +199,7 @@ function RecipientSection({ isReview }: { isReview: boolean }) {
function TokenBalance({ label, balance }: { label: string; balance?: TokenAmount | null }) {
const value = balance?.getDecimalFormattedAmount().toFixed(4) || '0';
return <div className="text-xs text-gray-500 text-right">{`${label}: ${value}`}</div>;
return <div className="text-xs text-gray-600 text-right">{`${label}: ${value}`}</div>;
}
function ButtonSection({
@ -235,6 +229,7 @@ function ButtonSection({
if (isSanctioned) {
return;
}
setIsReview(false);
setTransferLoading(true);
await triggerTransactions(values);
};
@ -253,16 +248,16 @@ function ButtonSection({
<div className="mt-4 flex items-center justify-between space-x-4">
<SolidButton
type="button"
color="gray"
color="primary"
onClick={() => setIsReview(false)}
classes="px-6 py-1.5"
icon={<ChevronIcon direction="w" width={10} height={6} color={Color.primaryBlue} />}
icon={<ChevronIcon direction="w" width={10} height={6} color={Color.white} />}
>
<span>Edit</span>
</SolidButton>
<SolidButton
type="button"
color="blue"
color="primary"
onClick={triggerTransactionsHandler}
classes="flex-1 px-3 py-1.5"
>
@ -291,16 +286,16 @@ function MaxButton({ balance, disabled }: { balance?: TokenAmount; disabled?: bo
<SolidButton
type="button"
onClick={onClick}
color="gray"
color="primary"
disabled={disabled}
classes="text-xs absolute right-0.5 top-2 bottom-0.5 px-2"
classes="text-xs absolute right-1 top-2.5 bottom-1 px-2 opacity-90 all:rounded"
>
{isLoading ? (
<div className="flex items-center">
<SmallSpinner />
<SmallSpinner className="text-white" />
</div>
) : (
'MAX'
'Max'
)}
</SolidButton>
);
@ -323,11 +318,11 @@ function SelfButton({ disabled }: { disabled?: boolean }) {
<SolidButton
type="button"
onClick={onClick}
color="gray"
color="primary"
disabled={disabled}
classes="text-xs absolute right-0.5 top-2 bottom-0.5 px-2"
classes="text-xs absolute right-1 top-2.5 bottom-1 px-2 opacity-90 all:rounded"
>
SELF
Self
</SolidButton>
);
}
@ -358,7 +353,7 @@ function ReviewDetails({ visible }: { visible: boolean }) {
visible ? 'max-h-screen duration-1000 ease-in' : 'max-h-0 duration-500'
} overflow-hidden transition-all`}
>
<label className="mt-4 block uppercase text-sm text-gray-500 pl-0.5">Transactions</label>
<label className="mt-4 block text-sm text-gray-600 pl-0.5">Transactions</label>
<div className="mt-1.5 px-2.5 py-2 space-y-2 rounded border border-gray-400 bg-gray-150 text-sm break-all">
{isLoading ? (
<div className="py-6 flex items-center justify-center">
@ -382,17 +377,17 @@ function ReviewDetails({ visible }: { visible: boolean }) {
<div className="mt-1.5 ml-1.5 pl-2 border-l border-gray-300 space-y-1.5 text-xs">
{destinationToken?.addressOrDenom && (
<p className="flex">
<span className="min-w-[7rem]">Remote Token</span>
<span className="min-w-[6.5rem]">Remote Token</span>
<span>{destinationToken.addressOrDenom}</span>
</p>
)}
<p className="flex">
<span className="min-w-[7rem]">{isNft ? 'Token ID' : 'Amount'}</span>
<span className="min-w-[6.5rem]">{isNft ? 'Token ID' : 'Amount'}</span>
<span>{`${amount} ${originTokenSymbol}`}</span>
</p>
{fees?.localQuote && fees.localQuote.amount > 0n && (
<p className="flex">
<span className="min-w-[7rem]">Local Gas (est.)</span>
<span className="min-w-[6.5rem]">Local Gas (est.)</span>
<span>{`${fees.localQuote.getDecimalFormattedAmount().toFixed(4) || '0'} ${
fees.localQuote.token.symbol || ''
}`}</span>
@ -400,7 +395,7 @@ function ReviewDetails({ visible }: { visible: boolean }) {
)}
{fees?.interchainQuote && fees.interchainQuote.amount > 0n && (
<p className="flex">
<span className="min-w-[7rem]">Interchain Gas</span>
<span className="min-w-[6.5rem]">Interchain Gas</span>
<span>{`${fees.interchainQuote.getDecimalFormattedAmount().toFixed(4) || '0'} ${
fees.interchainQuote.token.symbol || ''
}`}</span>

@ -120,7 +120,7 @@ export function TransfersDetailsModal({
<h2 className="text-gray-600 font-medium">{date}</h2>
<div className="flex items-center font-medium">
{isSent ? (
<h3 className="text-blue-500">Sent</h3>
<h3 className="text-primary-500">Sent</h3>
) : (
<h3 className="text-red-500">Failed</h3>
)}
@ -135,7 +135,7 @@ export function TransfersDetailsModal({
</div>
)}
<div className="mt-4 p-3 flex items-center justify-center w-full rounded-full bg-blue-200">
<div className="mt-4 p-3 flex items-center justify-center w-full rounded-full bg-primary-200">
<TokenIcon token={token} size={30} />
<div className="ml-2 flex items items-baseline">
<span className="text-xl font-medium">{amount}</span>
@ -204,7 +204,8 @@ export function TransfersDetailsModal({
</div>
{showSignWarning && (
<div className="mt-3 text-sm text-center text-gray-600">
If your wallet does not show a transaction request, please try the transfer again.
If your wallet does not show a transaction request or never confirms, please try the
transfer again.
</div>
)}
</div>
@ -265,7 +266,8 @@ function TransferProperty({ name, value, url }: { name: string; value: string; u
function useSignIssueWarning(status: TransferStatus) {
const [showWarning, setShowWarning] = useState(false);
const warningCallback = useCallback(() => {
if (status === TransferStatus.SigningTransfer) setShowWarning(true);
if (status === TransferStatus.SigningTransfer || status === TransferStatus.ConfirmingTransfer)
setShowWarning(true);
}, [status, setShowWarning]);
useTimeout(warningCallback, 20_000);
return showWarning;

@ -7,6 +7,7 @@ import { toTitleCase, toWei } from '@hyperlane-xyz/utils';
import { toastTxSuccess } from '../../components/toast/TxSuccessToast';
import { getTokenByIndex, getWarpCore } from '../../context/context';
import { logger } from '../../utils/logger';
import { getChainDisplayName } from '../chains/utils';
import { AppState, useStore } from '../store';
import {
getAccountAddressForChain,
@ -18,6 +19,10 @@ import {
import { TransferContext, TransferFormValues, TransferStatus } from './types';
import { tryGetMsgIdFromTransferReceipt } from './utils';
const CHAIN_MISMATCH_ERROR = 'ChainMismatchError';
const TRANSFER_TIMEOUT_ERROR1 = 'block height exceeded';
const TRANSFER_TIMEOUT_ERROR2 = 'timeout';
export function useTokenTransfer(onDone?: () => void) {
const { transfers, addTransfer, updateTransferStatus } = useStore((s) => ({
transfers: s.transfers,
@ -90,8 +95,8 @@ async function executeTransfer({
let transferStatus: TransferStatus = TransferStatus.Preparing;
updateTransferStatus(transferIndex, transferStatus);
const { origin, destination, tokenIndex, amount, recipient } = values;
try {
const { origin, destination, tokenIndex, amount, recipient } = values;
const originToken = getTokenByIndex(tokenIndex);
const connection = originToken?.getConnectionForChain(destination);
if (!originToken || !connection) throw new Error('No token route found between chains');
@ -161,12 +166,20 @@ async function executeTransfer({
originTxHash: hashes.at(-1),
msgId,
});
} catch (error) {
} catch (error: any) {
logger.error(`Error at stage ${transferStatus}`, error);
const errorDetails = error.message || error.toString();
updateTransferStatus(transferIndex, TransferStatus.Failed);
if (JSON.stringify(error).includes('ChainMismatchError')) {
if (errorDetails.includes(CHAIN_MISMATCH_ERROR)) {
// Wagmi switchNetwork call helps prevent this but isn't foolproof
toast.error('Wallet must be connected to origin chain');
} else if (
errorDetails.includes(TRANSFER_TIMEOUT_ERROR1) ||
errorDetails.includes(TRANSFER_TIMEOUT_ERROR2)
) {
toast.error(
`Transaction timed out, ${getChainDisplayName(origin)} may be busy. Please try again.`,
);
} else {
toast.error(errorMessages[transferStatus] || 'Unable to transfer tokens.');
}

@ -83,27 +83,27 @@ export function SideBarMenu({
</button>
)}
<div className="w-full h-full flex flex-col overflow-y-auto">
<div className="w-full rounded-t-md bg-blue-500 py-2 px-3.5 text-white text-base font-normal tracking-wider">
<div className="w-full rounded-t-md bg-primary-500 py-2 px-3.5 text-white text-base font-normal tracking-wider">
Connected Wallets
</div>
<div className="my-3 px-3 space-y-3">
{readyAccounts.map((acc, i) => (
<AccountSummary key={i} account={acc} />
))}
<button onClick={onConnectWallet} className={styles.btn}>
<button onClick={onConnectWallet} className={`${styles.btn} pl-2.5`}>
<Icon src={Wallet} alt="" size={18} />
<div className="ml-2">Connect wallet</div>
</button>
<button onClick={onClickDisconnect} className={styles.btn}>
<button onClick={onClickDisconnect} className={`${styles.btn} pl-2.5`}>
<Icon src={Logout} alt="" size={20} />
<div className="ml-2">Disconnect all wallets</div>
</button>
</div>
<div className="w-full bg-blue-500 py-2 px-3.5 mb-4 text-white text-base font-normal tracking-wider">
<div className="w-full bg-primary-500 py-2 px-3.5 mb-4 text-white text-base font-normal tracking-wider">
Transfer History
</div>
<div className="flex grow flex-col px-3.5">
<div className="grow flex flex-col w-full">
<div className="grow flex flex-col w-full divide-y">
{sortedTransfers?.length > 0 &&
sortedTransfers.map((t, i) => (
<TransferSummary
@ -117,7 +117,7 @@ export function SideBarMenu({
))}
</div>
{sortedTransfers?.length > 0 && (
<button onClick={resetTransfers} className="my-5 mx-2 flex flex-row items-center">
<button onClick={resetTransfers} className={`${styles.btn} my-5 mx-2`}>
<Image className="mr-4" src={ResetIcon} width={17} height={17} alt="" />
<span className="text-gray-900 text-sm font-normal">Reset transaction history</span>
</button>
@ -154,9 +154,7 @@ function AccountSummary({ account }: { account: AccountInfo }) {
return (
<button
onClick={onClickCopy}
className={`${styles.btn} border border-gray-200 rounded-xl ${
numAddresses > 1 && 'cursor-default'
}`}
className={`${styles.btn} ${numAddresses > 1 && 'all:cursor-default'}`}
>
<div className="shrink-0">
<WalletLogo walletDetails={walletDetails} size={42} />
@ -182,13 +180,9 @@ function TransferSummary({
const token = tryFindToken(origin, originTokenAddressOrDenom);
return (
<button
key={timestamp}
onClick={onClick}
className="flex justify-between items-center rounded-xl border border-gray-200 px-2.5 py-2 mb-2.5 hover:bg-gray-200 active:bg-gray-300 transition-all duration-500"
>
<div className="flex">
<div className="mr-2.5 flex flex-col items-center justify-center rounded-full bg-gray-100 h-[2.25rem] w-[2.25rem] p-1.5">
<button key={timestamp} onClick={onClick} className={`${styles.btn} justify-between py-3`}>
<div className="flex gap-2.5">
<div className="px-1.5 flex flex-col items-center justify-center rounded-full bg-gray-100 h-[2.25rem] w-[2.25rem]">
<ChainLogo chainName={origin} size={20} />
</div>
<div className="flex flex-col">
@ -198,18 +192,18 @@ function TransferSummary({
<span className="text-gray-800 text-sm font-normal ml-1">{token?.symbol || ''}</span>
</div>
<div className="mt-1 flex flex-row items-center">
<span className="text-thin text-gray-900 font-normal tracking-wide">
<span className="text-xxs text-gray-900 font-normal tracking-wide">
{getChainDisplayName(origin, true)}
</span>
<Image className="mx-1" src={ArrowRightIcon} width={10} height={10} alt="" />
<span className="text-thin text-gray-900 font-normal tracking-wide">
<span className="text-xxs text-gray-900 font-normal tracking-wide">
{getChainDisplayName(destination, true)}
</span>
</div>
</div>
</div>
</div>
<div className="flex w-6 h-6">
<div className="flex w-5 h-5">
{STATUSES_WITH_ICON.includes(status) ? (
<Image src={getIconByTransferStatus(status)} width={25} height={25} alt="" />
) : (
@ -239,5 +233,5 @@ function Icon({
}
const styles = {
btn: 'w-full flex items-center px-2.5 py-2 text-sm hover:bg-gray-200 active:bg-gray-300 rounded transition-all duration-500',
btn: 'w-full flex items-center px-1 py-2 text-sm hover:bg-gray-200 active:scale-95 transition-all duration-500 cursor-pointer rounded-sm',
};

@ -41,7 +41,7 @@ export function WalletControlBar() {
icon={<Image src={Wallet} alt="" width={16} height={16} />}
color="white"
>
<div className="ml-1.5 text-xs sm:text-sm">Connect Wallet</div>
<div className="ml-1.5 text-xs sm:text-sm">Connect wallet</div>
</SolidButton>
)}
@ -66,7 +66,7 @@ export function WalletControlBar() {
<div className="flex items-center justify-center">
<div
style={{ height: 26, width: 26 }}
className="bg-blue-500 text-white flex items-center justify-center rounded-full"
className="bg-primary-500 text-white flex items-center justify-center rounded-full"
>
{numReady}
</div>

@ -70,7 +70,7 @@ function EnvButton({
className="w-full py-3.5 space-y-2.5 flex flex-col items-center rounded-lg border border-gray-200 hover:bg-gray-100 hover:border-gray-200 active:bg-gray-200 transition-all"
>
{logo}
<div className="uppercase text-gray-800 tracking-wide">{children}</div>
<div className="text-gray-800 tracking-wide">{children}</div>
<div className="text-sm text-gray-500">{`Connect to ${subTitle} compatible wallet`}</div>
</button>
);

@ -78,7 +78,7 @@ export function EvmWalletContext({ children }: PropsWithChildren<unknown>) {
<RainbowKitProvider
chains={chains}
theme={lightTheme({
accentColor: Color.primaryBlue,
accentColor: Color.primary,
borderRadius: 'small',
fontStack: 'system',
})}

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="10 0 1430 135.87"><defs><style>.cls-1{stroke-width:8px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-3{fill:#0082FA;}.cls-2{stroke-width:4px;}</style></defs><path class="cls-3" d="m3.23,20.37l83.5,24.75,127.5,27.5,140.5,23,190,19.5,185,6.75h119l111.5-4,145-10.25,139-15.5,137-21.75,61.98-12.2v77.69H3.23V20.37Z"/><path class="cls-2" d="m1449.18,1.9c-28.89,9.34-56.79,16.73-56.79,16.73C859.76,144.83,458.49,146.53,5.87,20.51"/><path class="cls-1" d="m1449.99,56.89c-2.24.46-4.47.92-6.71,1.37C911.32,166.5,318.58,125.03,1.27,18.54"/></svg>

Before

Width:  |  Height:  |  Size: 583 B

@ -1,4 +0,0 @@
<svg width="886" height="114" viewBox="0 0 886 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M783.5 -3.5C621.953 133.053 158.722 87.2984 8 -3.5" stroke="white" stroke-width="2"/>
<path d="M884.5 -5.5C586 201.5 152.904 88.164 2 -5.5" stroke="white" stroke-width="4"/>
</svg>

Before

Width:  |  Height:  |  Size: 290 B

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

@ -14,7 +14,6 @@ import { WarpContext } from '../context/WarpContext';
import { CosmosWalletContext } from '../features/wallet/context/CosmosWalletContext';
import { EvmWalletContext } from '../features/wallet/context/EvmWalletContext';
import { SolanaWalletContext } from '../features/wallet/context/SolanaWalletContext';
import '../styles/fonts.css';
import '../styles/globals.css';
import { useIsSsr } from '../utils/ssr';

@ -1,6 +1,6 @@
import { Head, Html, Main, NextScript } from 'next/document';
import { APP_BRAND_COLOR, APP_DESCRIPTION, APP_NAME, APP_URL } from '../consts/app';
import { APP_DESCRIPTION, APP_NAME, APP_URL, BRAND_COLOR } from '../consts/app';
export default function Document() {
return (
@ -12,7 +12,7 @@ export default function Document() {
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color={APP_BRAND_COLOR} />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color={BRAND_COLOR} />
<link rel="shortcut icon" href="/favicon.png" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="theme-color" content="#ffffff" />

@ -5,7 +5,7 @@ import { TransferTokenCard } from '../features/transfer/TransferTokenCard';
const Home: NextPage = () => {
return (
<div className="pt-4 sm:pt-8 space-y-3">
<div className="pt-4 space-y-3">
<TipCard />
<TransferTokenCard />
</div>

@ -1,27 +1,14 @@
// Should match tailwind.config.js
export enum Color {
primaryBlack = '#010101',
primaryWhite = '#FFFFFF',
primaryGray = '#6B7280',
lightGray = '#D0D4DB',
primaryBlue = '#0082FA',
primaryPink = '#D631B9',
primaryBeige = '#F1EDE9',
primaryRed = '#BF1B15',
primaryMint = '#31D99C',
}
// @ts-ignore
import { theme } from '../../tailwind.config';
// Useful for cases when using class names isn't convenient
// such as in svg fills
export function classNameToColor(className) {
switch (className) {
case 'bg-blue-500':
return Color.primaryBlue;
case 'bg-red-500':
return Color.primaryRed;
case 'bg-gray-500':
return Color.primaryGray;
default:
throw new Error('Missing color for className: ' + className);
}
}
const themeColors = theme.extend.colors as unknown as Record<string, string>;
export const Color = {
black: themeColors.black,
white: themeColors.white,
gray: themeColors.gray[500],
lightGray: themeColors.gray[200],
primary: themeColors.primary[500],
accent: themeColors.accent[500],
red: themeColors.red[500],
} as const;

@ -1,39 +0,0 @@
@font-face {
font-family: 'Neue Haas Grotesk';
font-style: normal;
font-weight: 200;
src: url('/fonts/NeueHaasDisplayThin.woff2') format('woff2'),
url('/fonts/NeueHaasDisplayThin.woff') format('woff');
}
@font-face {
font-family: 'Neue Haas Grotesk';
font-style: normal;
font-weight: 300;
src: url('/fonts/NeueHaasDisplayLight.woff2') format('woff2'),
url('/fonts/NeueHaasDisplayLight.woff') format('woff');
}
@font-face {
font-family: 'Neue Haas Grotesk';
font-style: normal;
font-weight: 400;
src: url('/fonts/NeueHaasDisplayRoman.woff2') format('woff2'),
url('/fonts/NeueHaasDisplayRoman.woff') format('woff');
}
@font-face {
font-family: 'Neue Haas Grotesk';
font-style: normal;
font-weight: 500;
src: url('/fonts/NeueHaasDisplayMedium.woff2') format('woff2'),
url('/fonts/NeueHaasDisplayMedium.woff') format('woff');
}
@font-face {
font-family: 'Neue Haas Grotesk';
font-style: normal;
font-weight: 600;
src: url('/fonts/NeueHaasDisplayBold.woff2') format('woff2'),
url('/fonts/NeueHaasDisplayBold.woff') format('woff');
}

@ -6,31 +6,43 @@ module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
fontFamily: {
sans: ['Neue Haas Grotesk', 'Helvetica', 'sans-serif'],
sans: ['sans-serif'],
serif: ['Garamond', 'serif'],
mono: ['Courier New', 'monospace'],
},
screens: {
all: '1px',
xs: '480px',
...defaultTheme.screens,
},
extend: {
colors: {
black: '#010101',
black2: '#15171a',
white: '#ffffff',
gray: {...defaultTheme.colors.gray, 150: '#EBEDF0', 250: '#404040', 350: '#6B6B6B'},
blue: {
primary: {
50: '#E6EDF9',
100: '#CCE6FE',
200: '#99CDFD',
300: '#66B4FC',
400: '#339BFB',
500: '#0082FA',
600: '#006FD6',
700: '#005CB2',
800: '#004A8E',
900: '#00376B',
100: '#CDDCF4',
200: '#A7C2EC',
300: '#82A8E4',
400: '#5385D2',
500: '#2764c1',
600: '#1D4685',
700: '#162A4A',
800: '#11213B',
900: '#0D192C',
},
accent: {
50: '#FAEAF8',
100: '#F2C1EA',
200: '#EA98DC',
300: '#E26ECE',
400: '#DA45C0',
500: '#D631B9',
600: '#C02CA6',
700: '#952281',
800: '#6B185C',
900: '#400E37',
},
red: {
100: '#EBBAB8',
@ -55,22 +67,12 @@ module.exports = {
800: '#17462E',
900: '#0F2F1E',
},
pink: {
50: '#FAEAF8',
100: '#F2C1EA',
200: '#EA98DC',
300: '#E26ECE',
400: '#DA45C0',
500: '#D631B9',
600: '#C02CA6',
700: '#952281',
800: '#6B185C',
900: '#400E37',
},
},
fontSize: {
xxs: '0.7rem',
xs: '0.775rem',
sm: '0.85rem',
md: '0.95rem',
thin: '0.625rem',
},
spacing: {
88: '22rem',
@ -81,10 +83,10 @@ module.exports = {
},
borderRadius: {
none: '0',
sm: '0.25rem',
DEFAULT: '0.35rem',
md: '0.45rem',
lg: '0.55rem',
sm: '0.20rem',
DEFAULT: '0.30rem',
md: '0.40rem',
lg: '0.50rem',
full: '9999px',
},
blur: {

@ -3957,13 +3957,13 @@ __metadata:
languageName: node
linkType: hard
"@hyperlane-xyz/core@npm:5.2.1":
version: 5.2.1
resolution: "@hyperlane-xyz/core@npm:5.2.1"
"@hyperlane-xyz/core@npm:5.3.0":
version: 5.3.0
resolution: "@hyperlane-xyz/core@npm:5.3.0"
dependencies:
"@arbitrum/nitro-contracts": "npm:^1.2.1"
"@eth-optimism/contracts": "npm:^0.6.0"
"@hyperlane-xyz/utils": "npm:5.2.1"
"@hyperlane-xyz/utils": "npm:5.3.0"
"@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2"
"@openzeppelin/contracts": "npm:^4.9.3"
"@openzeppelin/contracts-upgradeable": "npm:^v4.9.3"
@ -3972,17 +3972,7 @@ __metadata:
"@ethersproject/abi": "*"
"@ethersproject/providers": "*"
"@types/sinon-chai": "*"
checksum: df515d545c3a174dbadef13132a63874f0fc1e2c9cf891bf9c874ab2d4b31cc1d2cb33d660f9e99d547cc6d508f527d4dd5e1e758fc8d141de56401f4616010d
languageName: node
linkType: hard
"@hyperlane-xyz/registry@npm:4.3.2":
version: 4.3.2
resolution: "@hyperlane-xyz/registry@npm:4.3.2"
dependencies:
yaml: "npm:2.4.5"
zod: "npm:^3.21.2"
checksum: 7b1ff07074e4499f74a4c75dbbf0b7e641b3610bfc2a67785db724748d887d7b03c9dc9738b790cb56e008d6453789432c863f3b251498f77c931f196b9dab86
checksum: f3d4ca187d59cb3f7f6c7767a7f4204b5ee17b9593b1f36272cbbaf34261b3055f5d51f55ecc590591b817b8f3a4c2aa8b63af9aee16864519df50c6faf5707a
languageName: node
linkType: hard
@ -3996,16 +3986,16 @@ __metadata:
languageName: node
linkType: hard
"@hyperlane-xyz/sdk@npm:5.2.1":
version: 5.2.1
resolution: "@hyperlane-xyz/sdk@npm:5.2.1"
"@hyperlane-xyz/sdk@npm:5.3.0":
version: 5.3.0
resolution: "@hyperlane-xyz/sdk@npm:5.3.0"
dependencies:
"@arbitrum/sdk": "npm:^4.0.0"
"@aws-sdk/client-s3": "npm:^3.74.0"
"@cosmjs/cosmwasm-stargate": "npm:^0.32.4"
"@cosmjs/stargate": "npm:^0.32.4"
"@hyperlane-xyz/core": "npm:5.2.1"
"@hyperlane-xyz/utils": "npm:5.2.1"
"@hyperlane-xyz/core": "npm:5.3.0"
"@hyperlane-xyz/utils": "npm:5.3.0"
"@safe-global/api-kit": "npm:1.3.0"
"@safe-global/protocol-kit": "npm:1.3.0"
"@safe-global/safe-deployments": "npm:1.37.8"
@ -4024,13 +4014,13 @@ __metadata:
peerDependencies:
"@ethersproject/abi": "*"
"@ethersproject/providers": "*"
checksum: 94912ab970d911d77590709f78f43139717abf2d1d1ffd4f97137d2b739a0de785517a38108e2c47e4a6090c8c7e2c63f5665aa77333ca243e5ab610b0ce2585
checksum: f72eb29ddceff027c9c6a2a85679aaca0beff94ea171cc5653bc3f57e53350e69ba5ab11484b9fff3e3f764a9cbcf51db0fa3ab71fbe92707e2dc209e61af3e6
languageName: node
linkType: hard
"@hyperlane-xyz/utils@npm:5.2.1":
version: 5.2.1
resolution: "@hyperlane-xyz/utils@npm:5.2.1"
"@hyperlane-xyz/utils@npm:5.3.0":
version: 5.3.0
resolution: "@hyperlane-xyz/utils@npm:5.3.0"
dependencies:
"@cosmjs/encoding": "npm:^0.32.4"
"@solana/web3.js": "npm:^1.78.0"
@ -4039,7 +4029,7 @@ __metadata:
lodash-es: "npm:^4.17.21"
pino: "npm:^8.19.0"
yaml: "npm:2.4.5"
checksum: 67c725cbb0581f6a8b42723974986b9c7beee69a51dcc3261efc9c4961f27493eb10108407b1d54329eb95c19c5595ca6cc581137f925773ec600eb72c761fe1
checksum: 2ae64331824f21c8741c2e94eadd1e863c5ce6f0b4d84e35b03eeae47b2473403636eda0f5aa648523a312b605de2a69fb9fff73949e26e3410e85b4c2fe0d27
languageName: node
linkType: hard
@ -4061,9 +4051,9 @@ __metadata:
"@emotion/styled": "npm:^11.13.0"
"@headlessui/react": "npm:^1.7.14"
"@hyperlane-xyz/registry": "npm:4.3.6"
"@hyperlane-xyz/sdk": "npm:5.2.1"
"@hyperlane-xyz/utils": "npm:5.2.1"
"@hyperlane-xyz/widgets": "npm:5.2.1"
"@hyperlane-xyz/sdk": "npm:5.3.0"
"@hyperlane-xyz/utils": "npm:5.3.0"
"@hyperlane-xyz/widgets": "npm:5.3.0"
"@interchain-ui/react": "npm:^1.23.28"
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6"
"@metamask/post-message-stream": "npm:6.1.2"
@ -4115,16 +4105,16 @@ __metadata:
languageName: unknown
linkType: soft
"@hyperlane-xyz/widgets@npm:5.2.1":
version: 5.2.1
resolution: "@hyperlane-xyz/widgets@npm:5.2.1"
"@hyperlane-xyz/widgets@npm:5.3.0":
version: 5.3.0
resolution: "@hyperlane-xyz/widgets@npm:5.3.0"
dependencies:
"@hyperlane-xyz/registry": "npm:4.3.2"
"@hyperlane-xyz/sdk": "npm:5.2.1"
"@hyperlane-xyz/registry": "npm:4.3.6"
"@hyperlane-xyz/sdk": "npm:5.3.0"
peerDependencies:
react: ^18
react-dom: ^18
checksum: 9ee820be26d51cf59285ca370c88867c02b828473a6ffb136590d9d7987a109b9b956620833945ca32df2345fda3d5b1a4352d2a3e94a519b633fb2fd6f1342f
checksum: 231d03f88451dc0b0defa1002c6f0da058fb999a75b80457d7be9e1f6b381bc6e5ba9745aab54f56d3efb3be07682eb58301fa6c61b32ba2b8920aa46b6aa5d5
languageName: node
linkType: hard

Loading…
Cancel
Save