Skip to content

Commit

Permalink
Start replacing internals with WarpCore
Browse files Browse the repository at this point in the history
  • Loading branch information
jmrossy committed Feb 20, 2024
1 parent c650553 commit 2842a8d
Show file tree
Hide file tree
Showing 49 changed files with 431 additions and 2,742 deletions.
9 changes: 2 additions & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
"[typescript]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
Expand All @@ -38,9 +38,4 @@
},
"editor.tabSize": 2,
"editor.detectIndentation": false,
"[typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
}
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,12 @@
},
"scripts": {
"clean": "rm -rf dist cache .next",
"dev": "yarn build:configs && next dev",
"build": "yarn build:configs && next build",
"build:configs": "./src/scripts/buildConfigs/build.sh",
"dev": "next dev",
"build": "next build",
"typecheck": "tsc",
"lint": "next lint",
"start": "next start",
"test": "yarn build:configs && jest",
"test": "jest",
"prettier": "prettier --write ./src"
},
"types": "dist/src/index.d.ts",
Expand Down
10 changes: 5 additions & 5 deletions src/components/buttons/ConnectAwareSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ import { useCallback } from 'react';

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

import { tryGetProtocolType } from '../../features/caip/chains';
import { tryGetChainProtocol } from '../../features/chains/utils';
import { useAccountForChain, useConnectFns } from '../../features/wallet/hooks/multiProtocol';
import { useTimeout } from '../../utils/timeout';

import { SolidButton } from './SolidButton';

interface Props {
chainCaip2Id: ChainCaip2Id;
chainName: ChainName;
text: string;
classes?: string;
}

export function ConnectAwareSubmitButton<FormValues = any>({ chainCaip2Id, text, classes }: Props) {
const protocol = tryGetProtocolType(chainCaip2Id) || ProtocolType.Ethereum;
export function ConnectAwareSubmitButton<FormValues = any>({ chainName, text, classes }: Props) {
const protocol = tryGetChainProtocol(chainName) || ProtocolType.Ethereum;
const connectFns = useConnectFns();
const connectFn = connectFns[protocol];

const account = useAccountForChain(chainCaip2Id);
const account = useAccountForChain(chainName);
const isAccountReady = account?.isReady;

const { errors, setErrors, touched, setTouched } = useFormikContext<FormValues>();
Expand Down
53 changes: 19 additions & 34 deletions src/components/icons/ChainLogo.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,27 @@
import Image from 'next/image';
import { ComponentProps, useMemo } from 'react';

import { isNumeric } from '@hyperlane-xyz/utils';
import { ChainLogo as ChainLogoInner } from '@hyperlane-xyz/widgets';

import { parseCaip2Id } from '../../features/caip/chains';
import { getChainDisplayName } from '../../features/chains/utils';
import { getMultiProvider } from '../../features/multiProvider';
import { logger } from '../../utils/logger';
import { getChainDisplayName, tryGetChainMetadata } from '../../features/chains/utils';

type Props = Omit<ComponentProps<typeof ChainLogoInner>, 'chainId' | 'chainName'> & {
chainCaip2Id?: ChainCaip2Id;
};
export function ChainLogo(props: ComponentProps<typeof ChainLogoInner>) {
const { chainName, ...rest } = props;
const { chainId, chainDisplayName, icon } = useMemo(() => {
if (!chainName) return {};
const chainDisplayName = getChainDisplayName(chainName);
const logoUri = tryGetChainMetadata(chainName)?.logoURI;
const icon = logoUri
? (props: { width: number; height: number; title?: string }) => (
<Image src={logoUri} alt="" {...props} />
)
: undefined;
return {
chainId,
chainDisplayName,
icon,
};
}, [chainName]);

export function ChainLogo(props: Props) {
const { chainCaip2Id, ...rest } = props;
const { chainId, chainName, icon } = useMemo(() => {
if (!chainCaip2Id) return {};
try {
const { reference } = parseCaip2Id(chainCaip2Id);
const chainId = isNumeric(reference) ? parseInt(reference, 10) : undefined;
const chainName = getChainDisplayName(chainCaip2Id);
const logoUri = getMultiProvider().tryGetChainMetadata(reference)?.logoURI;
const icon = logoUri
? (props: { width: number; height: number; title?: string }) => (
<Image src={logoUri} alt="" {...props} />
)
: undefined;
return {
chainId,
chainName,
icon,
};
} catch (error) {
logger.error('Failed to parse caip2 id', error);
return {};
}
}, [chainCaip2Id]);

return <ChainLogoInner {...rest} chainId={chainId} chainName={chainName} icon={icon} />;
return <ChainLogoInner {...rest} chainId={chainId} chainName={chainDisplayName} icon={icon} />;
}
9 changes: 3 additions & 6 deletions src/components/icons/TokenIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import Image from 'next/image';
import { memo } from 'react';

import { Token } from '@hyperlane-xyz/sdk';
import { Circle } from '@hyperlane-xyz/widgets';

import { getTokenAddress } from '../../features/caip/tokens';
import { TokenMetadata } from '../../features/tokens/types';
import { isValidUrl } from '../../utils/url';
import { ErrorBoundary } from '../errors/ErrorBoundary';

interface Props {
token?: TokenMetadata;
token?: Token;
size?: number;
}

Expand All @@ -20,9 +19,7 @@ function _TokenIcon({ token, size = 32 }: Props) {
const fontSize = Math.floor(size / 2);

const bgColorSeed =
token && !imageSrc
? (Buffer.from(getTokenAddress(token.tokenCaip19Id)).at(0) || 0) % 5
: undefined;
token && !imageSrc ? (Buffer.from(token.addressOrDenom).at(0) || 0) % 5 : undefined;

return (
<Circle size={size} bgColorSeed={bgColorSeed} title={title}>
Expand Down
16 changes: 7 additions & 9 deletions src/components/toast/TxSuccessToast.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { useMemo } from 'react';
import { toast } from 'react-toastify';

import { parseCaip2Id } from '../../features/caip/chains';
import { getMultiProvider } from '../../features/multiProvider';
import { getMultiProvider } from '../../context/context';

export function toastTxSuccess(msg: string, txHash: string, chainCaip2Id: ChainCaip2Id) {
toast.success(<TxSuccessToast msg={msg} txHash={txHash} chainCaip2Id={chainCaip2Id} />, {
export function toastTxSuccess(msg: string, txHash: string, chain: ChainName) {
toast.success(<TxSuccessToast msg={msg} txHash={txHash} chain={chain} />, {
autoClose: 12000,
});
}

export function TxSuccessToast({
msg,
txHash,
chainCaip2Id,
chain,
}: {
msg: string;
txHash: string;
chainCaip2Id: ChainCaip2Id;
chain: ChainName;
}) {
const url = useMemo(() => {
const { reference } = parseCaip2Id(chainCaip2Id);
return getMultiProvider().tryGetExplorerTxUrl(reference, { hash: txHash });
}, [chainCaip2Id, txHash]);
return getMultiProvider().tryGetExplorerTxUrl(chain, { hash: txHash });
}, [chain, txHash]);

return (
<div>
Expand Down
2 changes: 1 addition & 1 deletion src/consts/tokens.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[]
{}
31 changes: 5 additions & 26 deletions src/consts/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import { WarpTokenConfig } from '../features/tokens/types';
import { WarpCoreConfig } from '@hyperlane-xyz/sdk';

// A list of Warp UI token configs
// Tokens can be defined here, in tokens.json, or in tokens.yaml
// The input here is typically the output of the Hyperlane CLI warp deploy command
export const tokenList: WarpTokenConfig = [
// Example collateral token for an EVM chain
{
type: 'collateral',
chainId: 5,
address: '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6',
hypCollateralAddress: '0x145de8760021c4ac6676376691b78038d3DE9097',
name: 'Weth',
symbol: 'WETH',
decimals: 18,
logoURI: '/logos/weth.png', // See public/logos/
},

// Example NFT (ERC721) token for an EVM chain
{
chainId: 5,
name: 'Test721',
symbol: 'TEST721',
decimals: 0,
type: 'collateral',
address: '0x77566D540d1E207dFf8DA205ed78750F9a1e7c55',
hypCollateralAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B',
isNft: true,
},
];
export const tokenConfigs: WarpCoreConfig = {
tokens: [],
options: {},
};
34 changes: 22 additions & 12 deletions src/consts/tokens.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
# A list of Warp UI token configs
# Tokens can be defined here, in tokens.json, or in tokens.ts
# A list of Warp UI token configs and other options for the WarpCore
# Configs can be defined here, in tokens.json, or in tokens.ts
# The input here is typically the output of the Hyperlane CLI warp deploy command
---
# Replace this [] with your token list
[]
# Example using a native token:
# - type: native
# chainId: 11155111
# name: 'Ether'
# symbol: 'ETH'
# decimals: 18
# hypNativeAddress: '0xEa44A29da87B5464774978e6A4F4072A4c048949'
# logoURI: '/logos/weth.png'
tokens:
# Eth Mainnet HypNative token
- chainName: sepolia
standard: EvmHypNative
decimals: 18
symbol: ETH
name: Ether
addressOrDenom: '0x767C51a91CC9dEF2F24C35c340649411D6390320'
logoURI: '/logos/weth.png'
connectedTokens:
- ethereum|alfajores|0x8bF6Ca2Dca1DF703Cb9144cef6A4d86abA7776C4
- chainName: alfajores
standard: EvmHypSynthetic
decimals: 18
symbol: ETH
name: Ether
addressOrDenom: '0x8bF6Ca2Dca1DF703Cb9144cef6A4d86abA7776C4'
logoURI: '/logos/weth.png'
connectedTokens:
- ethereum|sepolia|0x767C51a91CC9dEF2F24C35c340649411D6390320
4 changes: 0 additions & 4 deletions src/context/README.md

This file was deleted.

29 changes: 29 additions & 0 deletions src/context/chains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { z } from 'zod';

import { ChainMap, ChainMetadata, ChainMetadataSchema, chainMetadata } from '@hyperlane-xyz/sdk';

import ChainsJson from '../consts/chains.json';
import { chains as ChainsTS } from '../consts/chains.ts';
import ChainsYaml from '../consts/chains.yaml';
import { cosmosDefaultChain } from '../features/chains/cosmosDefault';
import { logger } from '../utils/logger';

export const ChainConfigSchema = z.record(
ChainMetadataSchema.and(z.object({ mailbox: z.string().optional() })),
);

export function getChainConfigs() {
// Chains must include a cosmos chain or CosmosKit throws errors
const result = ChainConfigSchema.safeParse({
cosmoshub: cosmosDefaultChain,
...ChainsJson,
...ChainsYaml,
...ChainsTS,
});
if (!result.success) {
logger.warn('Invalid chain config', result.error);
throw new Error(`Invalid chain config: ${result.error.toString()}`);
}
const customChainConfigs = result.data as ChainMap<ChainMetadata & { mailbox?: Address }>;
return { ...chainMetadata, ...customChainConfigs };
}
40 changes: 25 additions & 15 deletions src/context/context.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
import { ChainMap, ChainMetadata, MultiProtocolProvider } from '@hyperlane-xyz/sdk';
import { ChainMap, ChainMetadata, MultiProtocolProvider, WarpCore } from '@hyperlane-xyz/sdk';

import type { RoutesMap } from '../features/routes/types';
import type { TokenMetadata } from '../features/tokens/types';

import Chains from './_chains.json';
import Routes from './_routes.json';
import Tokens from './_tokens.json';
import { getChainConfigs } from './chains';
import { getWarpCoreConfig } from './tokens';

export interface WarpContext {
chains: ChainMap<ChainMetadata & { mailbox?: Address }>;
tokens: TokenMetadata[];
routes: RoutesMap;
multiProvider: MultiProtocolProvider<{ mailbox?: Address }>;
warpCore: WarpCore;
}

let warpContext: WarpContext;

export function getWarpContext() {
if (!warpContext) {
warpContext = {
chains: Chains as any,
tokens: Tokens as any,
routes: Routes as any,
multiProvider: new MultiProtocolProvider<{ mailbox?: Address }>(Chains as any),
};
warpContext = initWarpContext();
}
return warpContext;
}

export function setWarpContext(context: WarpContext) {
warpContext = context;
}

export function initWarpContext() {
const chains = getChainConfigs();
const multiProvider = new MultiProtocolProvider<{ mailbox?: Address }>(chains);
const coreConfig = getWarpCoreConfig();
const warpCore = WarpCore.FromConfig(multiProvider, coreConfig);
return { chains, multiProvider, warpCore };
}

export function getMultiProvider() {
return getWarpContext().multiProvider;
}

export function getWarpCore() {
return getWarpContext().warpCore;
}

export function getTokens() {
return getWarpCore().tokens;
}
19 changes: 19 additions & 0 deletions src/context/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { WarpCoreConfig, WarpCoreConfigSchema } from '@hyperlane-xyz/sdk';

import TokensJson from '../consts/tokens.json';
import { tokenConfigs as TokensTS } from '../consts/tokens.ts';
import TokensYaml from '../consts/tokens.yaml';
import { validateZodResult } from '../utils/zod.ts';

export function getWarpCoreConfig(): WarpCoreConfig {
const resultJson = WarpCoreConfigSchema.safeParse({ TokensJson });
const configJson = validateZodResult(resultJson, 'warp core json config');
const resultYaml = WarpCoreConfigSchema.safeParse({ TokensYaml });
const configYaml = validateZodResult(resultYaml, 'warp core yaml config');
const resultTs = WarpCoreConfigSchema.safeParse({ TokensTS });
const configTs = validateZodResult(resultTs, 'warp core typescript config');

const tokens = [...configJson.tokens, ...configYaml.tokens, ...configTs.tokens];
const options = { ...configJson.options, ...configYaml.options, ...configTs.options };
return { tokens, options };
}
Loading

0 comments on commit 2842a8d

Please sign in to comment.