Skip to content

Commit

Permalink
Merge pull request #243 from hyperlane-xyz/main-to-injective
Browse files Browse the repository at this point in the history
Main to injective
  • Loading branch information
cmcewen authored Sep 18, 2024
2 parents 246a171 + 99afc8f commit d6d632b
Show file tree
Hide file tree
Showing 18 changed files with 480 additions and 125 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"caughtErrorsIgnorePattern": "^_"
}
],
"jsx-a11y/alt-text": ["off"]
"jsx-a11y/alt-text": ["off"],
"@next/next/no-img-element": ["off"]
}
}
46 changes: 46 additions & 0 deletions .github/workflows/create-merge-prs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Merge Main to Other Branches

on:
push:
branches:
- main

jobs:
create-merge-prs:
runs-on: ubuntu-latest
strategy:
matrix:
name: [ezeth, injective, nexus]
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Configure Git
run: |
git config user.name "GitHub Actions Bot"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create and push merge branch
run: |
git checkout ${{ matrix.name }}
git merge origin/main
git checkout -b main-to-${{ matrix.name }}
git push -fu origin main-to-${{ matrix.name }}
- name: Check and Create Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_EXISTS=$(gh pr list --base ${{ matrix.name }} --head main-to-${{ matrix.name }} --json number --jq length)
if [ "$PR_EXISTS" -eq "0" ]; then
gh pr create \
--base ${{ matrix.name }} \
--head main-to-${{ matrix.name }} \
--title "chore: merge main into ${{ matrix.name }}" \
--body "This PR was automatically created to merge changes from \`main\` into \`${{ matrix.name }}\`." \
--draft
else
echo "Pull request already exists. Skipping creation."
fi
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hyperlane-xyz/warp-ui-template",
"description": "A web app template for building Hyperlane Warp Route UIs",
"version": "5.0.0",
"version": "5.1.0",
"author": "J M Rossy",
"dependencies": {
"@chakra-ui/next-js": "^2.2.0",
Expand All @@ -13,13 +13,14 @@
"@cosmos-kit/keplr": "^2.12.2",
"@cosmos-kit/leap": "^2.12.2",
"@cosmos-kit/react": "^2.18.0",
"@drift-labs/snap-wallet-adapter": "^0.3.0",
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@headlessui/react": "^1.7.14",
"@hyperlane-xyz/registry": "2.4.0",
"@hyperlane-xyz/sdk": "5.1.0",
"@hyperlane-xyz/utils": "5.1.0",
"@hyperlane-xyz/widgets": "5.1.0",
"@hyperlane-xyz/registry": "4.3.3",
"@hyperlane-xyz/sdk": "5.2.0",
"@hyperlane-xyz/utils": "5.2.0",
"@hyperlane-xyz/widgets": "5.2.0",
"@interchain-ui/react": "^1.23.28",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@metamask/post-message-stream": "6.1.2",
Expand Down Expand Up @@ -92,6 +93,7 @@
},
"types": "dist/src/index.d.ts",
"resolutions": {
"@solana/web3.js": "^1.78.4",
"axios": "0.27.2",
"bn.js": "^5.2",
"cosmjs-types": "0.9",
Expand Down
13 changes: 13 additions & 0 deletions src/components/banner/FormWarningBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ComponentProps } from 'react';

import { WarningBanner } from '../../components/banner/WarningBanner';

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}`}
{...props}
/>
);
}
37 changes: 37 additions & 0 deletions src/components/banner/WarningBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Image from 'next/image';
import { PropsWithChildren, ReactNode } from 'react';

import WarningIcon from '../../images/icons/warning.svg';

export function WarningBanner({
isVisible,
cta,
onClick,
className,
children,
}: PropsWithChildren<{
isVisible: boolean;
cta: ReactNode;
onClick: () => void;
className?: string;
}>) {
return (
<div
className={`flex items-center justify-between gap-2 px-4 bg-amber-400 text-sm ${
isVisible ? 'max-h-28 py-2 mb-2' : 'max-h-0 mb-0'
} overflow-hidden transition-all duration-500 ${className}`}
>
<div className="flex items-center gap-2">
<Image src={WarningIcon} width={20} height={20} alt="Warning:" />
{children}
</div>
<button
type="button"
onClick={onClick}
className="bg-white/30 rounded-full px-2.5 py-1 text-center hover:bg-white/50 active:bg-white/60"
>
{cta}
</button>
</div>
);
}
3 changes: 1 addition & 2 deletions src/components/icons/TokenIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Image from 'next/image';
import { memo } from 'react';

import { IToken } from '@hyperlane-xyz/sdk';
Expand Down Expand Up @@ -26,7 +25,7 @@ function _TokenIcon({ token, size = 32 }: Props) {
<Circle size={size} bgColorSeed={bgColorSeed} title={title}>
{imageSrc ? (
<ErrorBoundary hideError={true}>
<Image src={imageSrc} alt="" width={size} height={size} className="p-0.5" />
<img src={imageSrc} width={size} height={size} className="p-0.5" />
</ErrorBoundary>
) : (
<div className={`text-[${fontSize}px]`}>{character}</div>
Expand Down
45 changes: 44 additions & 1 deletion src/consts/chains.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
import {
eclipsemainnet,
eclipsemainnetAddresses,
solanamainnet,
solanamainnetAddresses,
} from '@hyperlane-xyz/registry';
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk';

// A map of chain names to ChainMetadata
// Chains can be defined here, in chains.json, or in chains.yaml
// Chains already in the SDK need not be included here unless you want to override some fields
// Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts
export const chains: ChainMap<ChainMetadata & { mailbox?: Address }> = {};
export const chains: ChainMap<ChainMetadata & { mailbox?: Address }> = {
solanamainnet: {
...solanamainnet,
// SVM chains require mailbox addresses for the token adapters
mailbox: solanamainnetAddresses.mailbox,
// Including a convenient rpc override because the Solana public RPC does not allow browser requests from localhost
rpcUrls: process.env.NEXT_PUBLIC_SOLANA_RPC_URL
? [{ http: process.env.NEXT_PUBLIC_SOLANA_RPC_URL }, ...solanamainnet.rpcUrls]
: solanamainnet.rpcUrls,
},
eclipsemainnet: {
...eclipsemainnet,
mailbox: eclipsemainnetAddresses.mailbox,
},
// mycustomchain: {
// protocol: ProtocolType.Ethereum,
// chainId: 123123,
// domainId: 123123,
// name: 'mycustomchain',
// displayName: 'My Chain',
// nativeToken: { name: 'Ether', symbol: 'ETH', decimals: 18 },
// rpcUrls: [{ http: 'https://mycustomchain-rpc.com' }],
// blockExplorers: [
// {
// name: 'MyCustomScan',
// url: 'https://mycustomchain-scan.com',
// apiUrl: 'https://api.mycustomchain-scan.com/api',
// family: ExplorerFamily.Etherscan,
// },
// ],
// blocks: {
// confirmations: 1,
// reorgPeriod: 1,
// estimateBlockTime: 10,
// },
// logoURI: '/logo.svg',
// },
};
5 changes: 5 additions & 0 deletions src/consts/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ChainMap } from '@hyperlane-xyz/sdk';

import { ADDRESS_BLACKLIST } from './blacklist';

const isDevMode = process?.env?.NODE_ENV === 'development';
Expand All @@ -7,6 +9,7 @@ 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 {
isDevMode: boolean; // Enables some debug features in the app
Expand All @@ -20,6 +23,7 @@ interface Config {
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({
Expand All @@ -34,4 +38,5 @@ export const config: Config = Object.freeze({
transferBlacklist,
enableExplorerLink: false,
addressBlacklist: ADDRESS_BLACKLIST.map((address) => address.toLowerCase()),
chainWalletWhitelists,
});
48 changes: 48 additions & 0 deletions src/features/chains/ChainWalletWarning.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useMemo } from 'react';

import { toTitleCase } from '@hyperlane-xyz/utils';

import { FormWarningBanner } from '../../components/banner/FormWarningBanner';
import { config } from '../../consts/config';
import { logger } from '../../utils/logger';
import { useConnectFns, useDisconnectFns, useWalletDetails } from '../wallet/hooks/multiProtocol';

import { getChainDisplayName, tryGetChainProtocol } from './utils';

export function ChainWalletWarning({ originChain }: { originChain: ChainName }) {
const wallets = useWalletDetails();
const connectFns = useConnectFns();
const disconnectFns = useDisconnectFns();

const { isVisible, chainDisplayName, walletWhitelist, connectFn, disconnectFn } = useMemo(() => {
const protocol = tryGetChainProtocol(originChain);
const walletWhitelist = config.chainWalletWhitelists[originChain]?.map((w) =>
w.trim().toLowerCase(),
);
if (!protocol || !walletWhitelist?.length)
return { isVisible: false, chainDisplayName: '', walletWhitelist: [] };

const chainDisplayName = getChainDisplayName(originChain, true);
const walletName = wallets[protocol]?.name?.trim()?.toLowerCase();
const connectFn = connectFns[protocol];
const disconnectFn = disconnectFns[protocol];
const isVisible = !!walletName && !walletWhitelist.includes(walletName);

return { isVisible, chainDisplayName, walletWhitelist, connectFn, disconnectFn };
}, [originChain, wallets, connectFns, disconnectFns]);

const onClickChange = () => {
if (!connectFn || !disconnectFn) return;
disconnectFn()
.then(() => connectFn())
.catch((err) => logger.error('Error changing wallet connection', err));
};

return (
<FormWarningBanner isVisible={isVisible} cta="Change" onClick={onClickChange}>
{`${chainDisplayName} requires one of the following wallets: ${walletWhitelist
.map((w) => toTitleCase(w))
.join(', ')}`}
</FormWarningBanner>
);
}
22 changes: 14 additions & 8 deletions src/features/chains/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { AssetList, Chain as CosmosChain } from '@chain-registry/types';
import type { Chain as WagmiChain } from '@wagmi/core';

import { ChainName, chainMetadataToWagmiChain } from '@hyperlane-xyz/sdk';
import { ChainMetadata, ChainName, chainMetadataToWagmiChain } from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';

import { getWarpContext } from '../../context/context';
import { getTokens, getWarpContext } from '../../context/context';

import { cosmosDefaultChain } from './cosmosDefault';

// Metadata formatted for use in Wagmi config
export function getWagmiChainConfig(): WagmiChain[] {
Expand All @@ -15,9 +17,7 @@ export function getWagmiChainConfig(): WagmiChain[] {
}

export function getCosmosKitConfig(): { chains: CosmosChain[]; assets: AssetList[] } {
const cosmosChains = Object.values(getWarpContext().chains).filter(
(c) => c.protocol === ProtocolType.Cosmos,
);
const cosmosChains = getCosmosChains();
const chains = cosmosChains.map((c) => ({
chain_name: c.name,
status: 'live',
Expand Down Expand Up @@ -96,7 +96,13 @@ export function getCosmosKitConfig(): { chains: CosmosChain[]; assets: AssetList
}

export function getCosmosChainNames(): ChainName[] {
return Object.values(getWarpContext().chains)
.filter((c) => c.protocol === ProtocolType.Cosmos)
.map((c) => c.name);
return getCosmosChains().map((c) => c.name);
}

export function getCosmosChains(): ChainMetadata[] {
const tokens = getTokens();
const chains = Object.values(getWarpContext().chains).filter(
(c) => c.protocol === ProtocolType.Cosmos && tokens.some((t) => t.chainName === c.name),
);
return [...chains, cosmosDefaultChain];
}
14 changes: 8 additions & 6 deletions src/features/chains/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { CoreChain, CoreChains } from '@hyperlane-xyz/registry';
import { ChainNameOrId } from '@hyperlane-xyz/sdk';
import { ProtocolType, toTitleCase } from '@hyperlane-xyz/utils';

import { getMultiProvider } from '../../context/context';

const ABACUS_WORKS_DEPLOYER_NAME = 'abacus works';

export function getChainDisplayName(chain: ChainName, shortName = false) {
if (!chain) return 'Unknown';
const metadata = tryGetChainMetadata(chain);
Expand All @@ -14,21 +15,22 @@ export function getChainDisplayName(chain: ChainName, shortName = false) {

export function isPermissionlessChain(chain: ChainName) {
if (!chain) return true;
const metadata = tryGetChainMetadata(chain);
return (
getChainMetadata(chain).protocol !== ProtocolType.Ethereum ||
!CoreChains.includes(chain as CoreChain)
metadata?.protocol !== ProtocolType.Ethereum ||
metadata.deployer?.name.trim().toLowerCase() !== ABACUS_WORKS_DEPLOYER_NAME
);
}

export function hasPermissionlessChain(ids: ChainName[]) {
return !ids.every((c) => !isPermissionlessChain(c));
}

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

Expand Down
Loading

0 comments on commit d6d632b

Please sign in to comment.