Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

main > nexus #368

Closed
wants to merge 10 commits into from
Closed
2 changes: 1 addition & 1 deletion CUSTOMIZE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The logo images you should change are:
- `./src/images/logos/app-name.svg`
- `./src/images/logos/app-title.svg`

These are images are primarily used in the header and footer files:
These images are primarily used in the header and footer files:

- `./src/components/nav/Header.tsx`
- `./src/components/nav/Footer.tsx`
Expand Down
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Hyperlane Warp Route UI Template

This repo contains an example web interface for interchain tokens built with [Hyperlane Warp Route](https://docs.hyperlane.xyz/docs/reference/applications/warp-routes). Warp is a framework to permisionlessly bridge tokens to any chain.
This repo contains an example web interface for interchain tokens built with [Hyperlane Warp Route](https://docs.hyperlane.xyz/docs/reference/applications/warp-routes). Warp is a framework to permissionlessly bridge tokens to any chain.

## Architecture

Expand All @@ -18,7 +18,12 @@ See [CUSTOMIZE.md](./CUSTOMIZE.md) for details about adjusting the tokens and br

### Setup

#### Configure

You need a `projectId` from the WalletConnect Cloud to run the Hyperlane Warp Route UI. Sign up to [WalletConnect Cloud](https://cloud.walletconnect.com) to create a new project.

#### Build

```sh
# Install dependencies
yarn
Expand All @@ -27,22 +32,14 @@ yarn
yarn build
```

#### Configure

You need a `projectId` from the WalletConnect Cloud to run the Hyperlane Warp Route UI successfully.
Sign up to [WalletConnect Cloud](https://cloud.walletconnect.com/), create
new project with AppKit and Next.js and copy the `projectId` from there.

### Run

You can add `.env.local` file next to `.env.example` where you set `projectId` copied from WalletConnect Cloud.

```sh
# Start the Next dev server
yarn dev
```

Or you can set the WalletConnect Cloud `projectId` to use as follows:
```
# Or with a custom projectId
NEXT_PUBLIC_WALLET_CONNECT_ID=<projectId> yarn dev
```

Expand Down
9 changes: 7 additions & 2 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,18 @@ export default [
},

rules: {
'camelcase': ['error'],
'guard-for-in': ['error'],
'import/no-cycle': ['error'],
'import/no-self-import': ['error'],
'no-console': ['warn'],
'no-eval': ['error'],
'no-ex-assign': ['error'],
'no-extra-boolean-cast': ['error'],
'no-constant-condition': ['off'],
'guard-for-in': ['error'],
'no-multiple-empty-lines': ['error'],
'jsx-a11y/alt-text': ['off'],

'@typescript-eslint/ban-ts-comment': ['off'],
'@typescript-eslint/explicit-module-boundary-types': ['off'],
'@typescript-eslint/no-explicit-any': ['off'],
Expand All @@ -77,7 +83,6 @@ export default [
},
],

'jsx-a11y/alt-text': ['off'],
'@next/next/no-img-element': ['off'],
},
},
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@headlessui/react": "^2.2.0",
"@hyperlane-xyz/registry": "6.1.0",
"@hyperlane-xyz/registry": "6.11.0",
"@hyperlane-xyz/sdk": "7.1.0",
"@hyperlane-xyz/utils": "7.1.0",
"@hyperlane-xyz/widgets": "7.1.0",
"@interchain-ui/react": "^1.23.28",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@metamask/post-message-stream": "6.1.2",
"@metamask/providers": "10.2.1",
"@rainbow-me/rainbowkit": "^2.2.0",
Expand Down
1 change: 0 additions & 1 deletion public/logos/cosmos.svg

This file was deleted.

1 change: 0 additions & 1 deletion public/logos/cosmwasm.svg

This file was deleted.

1 change: 0 additions & 1 deletion public/logos/solana.svg

This file was deleted.

Binary file removed public/logos/weth.png
Binary file not shown.
41 changes: 0 additions & 41 deletions src/components/icons/Identicon.tsx

This file was deleted.

6 changes: 0 additions & 6 deletions src/components/icons/types.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/consts/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Space_Grotesk } from 'next/font/google';
import { Space_Grotesk as SpaceGrotesk } from 'next/font/google';
import { Color } from '../styles/Color';

export const MAIN_FONT = Space_Grotesk({
export const MAIN_FONT = SpaceGrotesk({
subsets: ['latin'],
variable: '--font-main',
preload: true,
Expand All @@ -10,6 +10,6 @@ export const MAIN_FONT = Space_Grotesk({
export const APP_NAME = 'Hyperlane Warp UI Template';
export const APP_DESCRIPTION = 'A DApp for Hyperlane Warp Route transfers';
export const APP_URL = 'hyperlane-warp-template.vercel.app';
export const BRAND_COLOR = Color.primary;
export const BACKGROUND_COLOR = Color.primary;
export const BRAND_COLOR = Color.primary['500'];
export const BACKGROUND_COLOR = Color.primary['500'];
export const BACKGROUND_IMAGE = 'url(/backgrounds/main.svg)';
9 changes: 3 additions & 6 deletions src/consts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,38 @@ import { ADDRESS_BLACKLIST } from './blacklist';
const isDevMode = process?.env?.NODE_ENV === 'development';
const version = process?.env?.NEXT_PUBLIC_VERSION || '0.0.0';
const registryUrl = process?.env?.NEXT_PUBLIC_REGISTRY_URL || undefined;
const registryBranch = process?.env?.NEXT_PUBLIC_REGISTRY_BRANCH || undefined;
const registryProxyUrl = process?.env?.NEXT_PUBLIC_GITHUB_PROXY || 'https://proxy.hyperlane.xyz';
const explorerApiKeys = JSON.parse(process?.env?.EXPLORER_API_KEYS || '{}');
const walletConnectProjectId = process?.env?.NEXT_PUBLIC_WALLET_CONNECT_ID || '';
const withdrawalWhitelist = process?.env?.NEXT_PUBLIC_BLOCK_WITHDRAWAL_WHITELIST || '';
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
registryUrl: string | undefined; // Optional URL to use a custom registry instead of the published canonical version
registryBranch?: string | undefined; // Optional customization of the registry branch instead of main
registryProxyUrl?: string; // Optional URL to use a custom proxy for the GithubRegistry
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
}

export const config: Config = Object.freeze({
addressBlacklist: ADDRESS_BLACKLIST.map((address) => address.toLowerCase()),
chainWalletWhitelists,
enableExplorerLink: false,
explorerApiKeys,
isDevMode,
registryUrl,
registryBranch,
registryProxyUrl,
showDisabledTokens: false,
showTipBox: true,
version,
transferBlacklist,
walletConnectProjectId,
withdrawalWhitelist,
});
2 changes: 1 addition & 1 deletion src/consts/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const links = {
warpDocs: 'https://docs.hyperlane.xyz/docs/reference/applications/warp-routes',
gasDocs: 'https://docs.hyperlane.xyz/docs/reference/hooks/interchain-gas',
chains: 'https://docs.hyperlane.xyz/docs/resources/domains',
twitter: 'https://twitter.com/hyperlane',
twitter: 'https://x.com/hyperlane',
blog: 'https://medium.com/hyperlane',
tos: 'https://hyperlane.xyz/terms-of-service',
privacyPolicy: 'https://hyperlane.xyz/privacy-policy',
Expand Down
8 changes: 0 additions & 8 deletions src/features/chains/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ export function hasPermissionlessChain(multiProvider: MultiProtocolProvider, ids
return !ids.every((c) => !isPermissionlessChain(multiProvider, c));
}

export function getChainByRpcUrl(multiProvider: MultiProtocolProvider, url?: string) {
if (!url) return undefined;
const allMetadata = Object.values(multiProvider.metadata);
return allMetadata.find(
(m) => !!m.rpcUrls.find((rpc) => rpc.http.toLowerCase().includes(url.toLowerCase())),
);
}

/**
* Returns an object that contains the amount of
* routes from a single chain to every other chain
Expand Down
6 changes: 5 additions & 1 deletion src/features/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ export const useStore = create<AppState>()(
set({ chainMetadataOverrides: filtered, multiProvider });
},
multiProvider: new MultiProtocolProvider({}),
registry: new GithubRegistry({ uri: config.registryUrl, proxyUrl: config.registryProxyUrl }),
registry: new GithubRegistry({
uri: config.registryUrl,
branch: config.registryBranch,
proxyUrl: config.registryProxyUrl,
}),
warpCore: new WarpCore(new MultiProtocolProvider({}), []),
setWarpContext: ({ registry, chainMetadata, multiProvider, warpCore }) => {
logger.debug('Setting warp context in store');
Expand Down
2 changes: 1 addition & 1 deletion src/features/transfer/TransferTokenForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ function RecipientSection({ isReview }: { isReview: boolean }) {
}

function TokenBalance({ label, balance }: { label: string; balance?: TokenAmount | null }) {
const value = balance?.getDecimalFormattedAmount().toFixed(4) || '0';
const value = balance?.getDecimalFormattedAmount().toFixed(5) || '0';
return <div className="text-right text-xs text-gray-600">{`${label}: ${value}`}</div>;
}

Expand Down
12 changes: 6 additions & 6 deletions src/features/transfer/TransfersDetailsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ import { Color } from '../../styles/Color';
import { formatTimestamp } from '../../utils/date';
import { getHypExplorerLink } from '../../utils/links';
import { logger } from '../../utils/logger';
import { useMultiProvider } from '../chains/hooks';
import { getChainDisplayName, hasPermissionlessChain } from '../chains/utils';
import { tryFindToken, useWarpCore } from '../tokens/hooks';
import { TransferContext, TransferStatus } from './types';
import {
getIconByTransferStatus,
getTransferStatusLabel,
isTransferFailed,
isTransferSent,
} from '../../utils/transfer';
import { useMultiProvider } from '../chains/hooks';
import { getChainDisplayName, hasPermissionlessChain } from '../chains/utils';
import { tryFindToken, useWarpCore } from '../tokens/hooks';
import { TransferContext, TransferStatus } from './types';
} from './utils';

export function TransfersDetailsModal({
isOpen,
Expand Down Expand Up @@ -263,7 +263,7 @@ function WideChevron() {
width="16"
height="100%"
direction="e"
color={Color.lightGray}
color={Color.gray['300']}
rounded={true}
/>
);
Expand Down
66 changes: 66 additions & 0 deletions src/features/transfer/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
import ConfirmedIcon from '../../images/icons/confirmed-icon.svg';
import DeliveredIcon from '../../images/icons/delivered-icon.svg';
import ErrorCircleIcon from '../../images/icons/error-circle.svg';
import { FinalTransferStatuses, SentTransferStatuses, TransferStatus } from './types';

export function getTransferStatusLabel(
status: TransferStatus,
connectorName: string,
isPermissionlessRoute: boolean,
isAccountReady: boolean,
) {
let statusDescription = '...';
if (!isAccountReady && !FinalTransferStatuses.includes(status))
statusDescription = 'Please connect wallet to continue';
else if (status === TransferStatus.Preparing)
statusDescription = 'Preparing for token transfer...';
else if (status === TransferStatus.CreatingTxs) statusDescription = 'Creating transactions...';
else if (status === TransferStatus.SigningApprove)
statusDescription = `Sign approve transaction in ${connectorName} to continue.`;
else if (status === TransferStatus.ConfirmingApprove)
statusDescription = 'Confirming approve transaction...';
else if (status === TransferStatus.SigningTransfer)
statusDescription = `Sign transfer transaction in ${connectorName} to continue.`;
else if (status === TransferStatus.ConfirmingTransfer)
statusDescription = 'Confirming transfer transaction...';
else if (status === TransferStatus.ConfirmedTransfer)
if (!isPermissionlessRoute)
statusDescription = 'Transfer transaction confirmed, delivering message...';
else
statusDescription =
'Transfer confirmed, the funds will arrive when the message is delivered.';
else if (status === TransferStatus.Delivered)
statusDescription = 'Delivery complete, transfer successful!';
else if (status === TransferStatus.Failed)
statusDescription = 'Transfer failed, please try again.';

return statusDescription;
}

export function isTransferSent(status: TransferStatus) {
return SentTransferStatuses.includes(status);
}

export function isTransferFailed(status: TransferStatus) {
return status === TransferStatus.Failed;
}

export const STATUSES_WITH_ICON = [
TransferStatus.Delivered,
TransferStatus.ConfirmedTransfer,
TransferStatus.Failed,
];

export function getIconByTransferStatus(status: TransferStatus) {
switch (status) {
case TransferStatus.Delivered:
return DeliveredIcon;
case TransferStatus.ConfirmedTransfer:
return ConfirmedIcon;
case TransferStatus.Failed:
return ErrorCircleIcon;
default:
return ErrorCircleIcon;
}
}

import {
ChainMap,
CoreAddresses,
Expand Down
Loading
Loading