Update to SDK 1.4.1 (#32)

- Update SDK, token, and widgets to 1.4.1
- Use SDK values for Solana chains
- Change format of Solana CAIP2 IDs to use numbers
- Fix bug with tx prep for Zebec chain
pull/36/head
J M Rossy 1 year ago committed by GitHub
parent f4e82cd014
commit 46a5493e5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      package.json
  2. 3
      src/components/buttons/ConnectAwareSubmitButton.tsx
  3. 41
      src/consts/chains.ts
  4. 100
      src/consts/solanaChains.ts
  5. 42
      src/consts/tokens.ts
  6. 18
      src/features/chains/caip2.ts
  7. 14
      src/features/chains/metadata.ts
  8. 32
      src/features/chains/types.ts
  9. 14
      src/features/chains/utils.ts
  10. 19
      src/features/multiProvider.ts
  11. 3
      src/features/tokens/adapters/AdapterFactory.ts
  12. 8
      src/features/tokens/metadata.ts
  13. 3
      src/features/tokens/native.ts
  14. 8
      src/features/tokens/routes/hooks.ts
  15. 6
      src/features/tokens/types.ts
  16. 2
      src/features/transfer/TransferTokenForm.tsx
  17. 13
      src/features/transfer/TransfersStatusBar.tsx
  18. 5
      src/features/transfer/useTokenTransfer.ts
  19. 16
      src/features/wallet/WalletEnvSelectionModal.tsx
  20. 47
      src/features/wallet/hooks.tsx
  21. 1
      src/images/logos/solana.svg
  22. 2
      src/utils/addresses.ts
  23. 60
      yarn.lock

@ -1,13 +1,13 @@
{
"name": "@hyperlane-xyz/warp-ui-template",
"description": "A web app template for building Hyperlane Warp Route UIs",
"version": "1.0.0",
"version": "1.4.1",
"author": "J M Rossy",
"dependencies": {
"@headlessui/react": "^1.7.14",
"@hyperlane-xyz/hyperlane-token": "^1.3.4",
"@hyperlane-xyz/sdk": "^1.3.4",
"@hyperlane-xyz/widgets": "^1.3.5-beta0",
"@hyperlane-xyz/hyperlane-token": "^1.4.1",
"@hyperlane-xyz/sdk": "^1.4.1",
"@hyperlane-xyz/widgets": "^1.4.1",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@rainbow-me/rainbowkit": "0.12.16",
"@solana/spl-token": "^0.3.8",

@ -1,8 +1,9 @@
import { useFormikContext } from 'formik';
import { useCallback } from 'react';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { tryGetProtocolType } from '../../features/chains/caip2';
import { ProtocolType } from '../../features/chains/types';
import { useAccountForChain, useConnectFns } from '../../features/wallet/hooks';
import { useTimeout } from '../../utils/timeout';

@ -1,13 +1,19 @@
import { ChainMap } from '@hyperlane-xyz/sdk';
import { CustomChainMetadata } from '../features/chains/types';
import { ChainMap, ChainMetadataWithArtifacts } from '@hyperlane-xyz/sdk';
import {
solana,
solanadevnet,
solanatestnet,
zbctestnet,
} from '@hyperlane-xyz/sdk/dist/consts/chainMetadata';
// A map of chain names to ChainMetadata
export const chains: ChainMap<CustomChainMetadata> = {
export const chains: ChainMap<ChainMetadataWithArtifacts> = {
// ----------- Add your chains here -----------------
// Chains already in the SDK need not be included here. Example custom chain:
// mycustomchain: {
// protocol: ProtocolType.Ethereum,
// chainId: 1234,
// domainId: 1234,
// name: 'mycustomchain',
// displayName: 'My Chain',
// nativeToken: { name: 'Ether', symbol: 'ETH', decimals: 18 },
@ -27,4 +33,31 @@ export const chains: ChainMap<CustomChainMetadata> = {
// },
// logoURI: '/logo.svg',
// },
// Including configs for some Solana chains by default
solana: {
...solana,
mailbox: 'TODO',
interchainGasPaymaster: '',
validatorAnnounce: '',
},
solanatestnet: {
...solanatestnet,
mailbox: 'TODO',
interchainGasPaymaster: '',
validatorAnnounce: '',
},
solanadevnet: {
...solanadevnet,
mailbox: '4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn',
interchainGasPaymaster: '',
validatorAnnounce: '',
},
zbctestnet: {
...zbctestnet,
mailbox: '4hW22NXtJ2AXrEVbeAmxjhvxWPSNvfTfAphKXdRBZUco',
interchainGasPaymaster: '',
validatorAnnounce: '',
logoURI: '/logos/zebec.png',
},
};

@ -1,100 +0,0 @@
import type { Cluster } from '@solana/web3.js';
import { ChainMap, ExplorerFamily } from '@hyperlane-xyz/sdk';
import { CustomChainMetadata } from '../features/chains/types';
// TODO move to SDK
export const solanaChains: ChainMap<CustomChainMetadata> = {
solanamainnet: {
protocol: 'sealevel',
chainId: 1399811149, // https://www.alchemy.com/chain-connect/chain/solana
domainId: 1399811149,
name: 'solanamainnet',
displayName: 'Solana',
nativeToken: { name: 'Sol', symbol: 'SOL', decimals: 9 },
publicRpcUrls: [{ http: 'https://api.mainnet-beta.solana.com' }],
blockExplorers: [
{
name: 'SolScan',
url: 'https://solscan.io',
apiUrl: 'https://public-api.solscan.io',
family: ExplorerFamily.Other,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 1,
},
mailbox: 'TODO',
logoURI: '/logos/solana.svg',
},
solanatestnet: {
protocol: 'sealevel',
chainId: 13998111450,
domainId: 13998111450,
name: 'solanatestnet',
displayName: 'Sol Testnet',
nativeToken: { name: 'Sol', symbol: 'SOL', decimals: 9 },
publicRpcUrls: [{ http: 'https://api.testnet.solana.com' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 1,
},
mailbox: 'TODO',
logoURI: '/logos/solana.svg',
},
solanadevnet: {
protocol: 'sealevel',
chainId: 1399811151,
domainId: 1399811151,
name: 'solanadevnet',
displayName: 'Sol Devnet',
nativeToken: { name: 'Sol', symbol: 'SOL', decimals: 9 },
publicRpcUrls: [{ http: 'https://api.devnet.solana.com' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 1,
},
mailbox: '4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn',
logoURI: '/logos/solana.svg',
},
zbctestnet: {
protocol: 'sealevel',
chainId: 2053254516,
domainId: 2053254516,
name: 'zbctestnet',
displayName: 'Zbc Testnet',
nativeToken: { name: 'Sol', symbol: 'SOL', decimals: 9 },
publicRpcUrls: [{ http: 'https://api.zebec.eclipsenetwork.xyz:8899' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 1,
},
mailbox: '4hW22NXtJ2AXrEVbeAmxjhvxWPSNvfTfAphKXdRBZUco',
logoURI: '/logos/zebec.png',
},
};
// For general use in UI
export function getSolanaChainName(rpcEndpoint: string) {
if (!rpcEndpoint) return {};
if (rpcEndpoint?.includes('devnet')) return { name: 'solanadevnet', displayName: 'Sol Devnet' };
if (rpcEndpoint?.includes('testnet'))
return { name: 'solanatestnet', displayName: 'Sol Testnet' };
return { name: 'solanamainnet', displayName: 'Solana' };
}
// For use in when interacting with solana/web3.js Connection class
export function getSolanaClusterName(chainName: string): Cluster {
if (chainName?.includes('devnet')) return 'devnet';
if (chainName?.includes('testnet')) return 'testnet';
return 'mainnet-beta';
}

@ -37,27 +37,27 @@ export const tokenList: WarpTokenConfig = [
},
// Example native token for a Sealevel (Solana) chain
// {
// type: 'native',
// protocol: 'sealevel',
// chainId: 1399811151,
// hypNativeAddress: '3s6afZYk3EmjsZQ33N9yPTdSk4cY5CKeQ5wtoBcWjFUn',
// name: 'Sol',
// symbol: 'SOL',
// decimals: 9,
// logoURI: '/logos/solana.svg',
// },
{
type: 'native',
protocol: 'sealevel',
chainId: 1399811151,
hypNativeAddress: '3s6afZYk3EmjsZQ33N9yPTdSk4cY5CKeQ5wtoBcWjFUn',
name: 'Sol',
symbol: 'SOL',
decimals: 9,
logoURI: '/logos/solana.svg',
},
// Example collateral token for a Sealevel (Solana) chain
// {
// type: 'collateral',
// protocol: 'sealevel',
// chainId: 1399811151,
// address: 'Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr',
// hypCollateralAddress: 'Hsb2PdnUvd7VvZJ1svS8TrVLfsRDdDTWoHK5r2RwGZBS',
// name: 'dUSDC',
// symbol: 'dUSDC',
// decimals: 6,
// isSpl2022: false,
// },
{
type: 'collateral',
protocol: 'sealevel',
chainId: 1399811151,
address: 'Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr',
hypCollateralAddress: 'Hsb2PdnUvd7VvZJ1svS8TrVLfsRDdDTWoHK5r2RwGZBS',
name: 'dUSDC',
symbol: 'dUSDC',
decimals: 6,
isSpl2022: false,
},
];

@ -1,24 +1,14 @@
import { logger } from '../../utils/logger';
import { isNumeric } from '../../utils/string';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { ProtocolType } from './types';
import { logger } from '../../utils/logger';
// Based on https://chainagnostic.org/CAIPs/caip-2
export function getCaip2Id(protocol: ProtocolType, reference: string | number): Caip2Id {
if (!Object.values(ProtocolType).includes(protocol)) {
throw new Error(`Invalid chain environment: ${protocol}`);
}
if (!reference) {
throw new Error(`Reference required: ${reference}`);
}
if (protocol === ProtocolType.Ethereum && (typeof reference !== 'number' || reference < 0)) {
throw new Error(`Invalid evm chain reference (id): ${reference}`);
}
if (
protocol === ProtocolType.Sealevel &&
(typeof reference !== 'string' || isNumeric(reference))
) {
throw new Error(`Invalid solana chain reference: ${reference}`);
if (typeof reference !== 'number' || reference <= 0) {
throw new Error(`Invalid chain reference: ${reference}`);
}
return `${protocol}:${reference}`;
}

@ -1,19 +1,22 @@
import type { Chain as WagmiChain } from '@wagmi/core';
import { z } from 'zod';
import {
ChainMap,
ChainMetadata,
ChainMetadataWithArtifacts,
ChainMetadataWithArtifactsSchema,
ProtocolType,
chainMetadata,
chainMetadataToWagmiChain,
} from '@hyperlane-xyz/sdk';
import { chains } from '../../consts/chains';
import { solanaChains } from '../../consts/solanaChains';
import { logger } from '../../utils/logger';
import { ChainConfigSchema, CustomChainMetadata, ProtocolType } from './types';
let chainConfigs: ChainMap<ChainMetadata | ChainMetadataWithArtifacts>;
let chainConfigs: ChainMap<ChainMetadata | CustomChainMetadata>;
export const ChainConfigSchema = z.record(ChainMetadataWithArtifactsSchema);
export function getChainConfigs() {
if (!chainConfigs) {
@ -22,8 +25,8 @@ export function getChainConfigs() {
logger.error('Invalid chain config', result.error);
throw new Error(`Invalid chain config: ${result.error.toString()}`);
}
const customChainConfigs = result.data as ChainMap<CustomChainMetadata>;
chainConfigs = { ...chainMetadata, ...solanaChains, ...customChainConfigs };
const customChainConfigs = result.data as ChainMap<ChainMetadataWithArtifacts>;
chainConfigs = { ...chainMetadata, ...customChainConfigs };
}
return chainConfigs;
}
@ -31,7 +34,6 @@ export function getChainConfigs() {
// Metadata formatted for use in Wagmi config
export function getWagmiChainConfig(): WagmiChain[] {
const evmChains = Object.values(getChainConfigs()).filter(
// @ts-ignore TODO add optional protocol field to ChainMetadata in SDK
(c) => !c.protocol || c.protocol === ProtocolType.Ethereum,
);
return evmChains.map(chainMetadataToWagmiChain);

@ -1,32 +0,0 @@
import { z } from 'zod';
import { ChainMetadata, ChainMetadataSchema } from '@hyperlane-xyz/sdk';
export enum ProtocolType {
Ethereum = 'ethereum',
Sealevel = 'sealevel',
}
export const ProtocolSmallestUnit = {
[ProtocolType.Ethereum]: 'wei',
[ProtocolType.Sealevel]: 'lamports',
};
export const ChainMetadataExtensionSchema = z.object({
// Extended chain metadata for multi-env support
// TODO move to SDK (see https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/2203)
protocol: z.nativeEnum(ProtocolType).optional(),
mailbox: z.string().nonempty().optional(),
interchainGasPaymaster: z.string().nonempty().optional(),
validatorAnnounce: z.string().nonempty().optional(),
// Additional extensions for use just in UI here
logoURI: z.string().nonempty().optional(),
});
export type CustomChainMetadata = ChainMetadata &
Omit<z.infer<typeof ChainMetadataExtensionSchema>, 'protocol'> & {
// Loosen loose protocol type to allow literal strings
protocol?: `${ProtocolType}`;
};
export const ChainConfigSchema = z.record(ChainMetadataSchema.merge(ChainMetadataExtensionSchema));

@ -1,4 +1,4 @@
import { chainIdToMetadata } from '@hyperlane-xyz/sdk';
import { ProtocolType, chainIdToMetadata } from '@hyperlane-xyz/sdk';
import { toTitleCase } from '../../utils/string';
import { getMultiProvider } from '../multiProvider';
@ -16,10 +16,18 @@ export function getChainDisplayName(id: Caip2Id, shortName = false) {
export function isPermissionlessChain(id: Caip2Id) {
if (!id) return true;
const { reference } = parseCaip2Id(id);
return !chainIdToMetadata[reference];
const { protocol, reference } = parseCaip2Id(id);
return protocol !== ProtocolType.Ethereum || !chainIdToMetadata[reference];
}
export function hasPermissionlessChain(ids: Caip2Id[]) {
return !ids.every((c) => !isPermissionlessChain(c));
}
export function getChainByRpcEndpoint(endpoint?: string) {
if (!endpoint) return undefined;
const allMetadata = Object.values(getMultiProvider().metadata);
return allMetadata.find(
(m) => !!m.rpcUrls.find((rpc) => rpc.http.toLowerCase().includes(endpoint.toLowerCase())),
);
}

@ -1,16 +1,21 @@
import { Signer, providers } from 'ethers';
import { ChainMetadata, ChainName, MultiProvider } from '@hyperlane-xyz/sdk';
import {
ChainMetadata,
ChainName,
HyperlaneDeploymentArtifacts,
MultiProvider,
ProtocolType,
} from '@hyperlane-xyz/sdk';
import { isNumeric } from '../utils/string';
import { parseCaip2Id } from './chains/caip2';
import { getChainConfigs } from './chains/metadata';
import { CustomChainMetadata, ProtocolType } from './chains/types';
// A ProtocolType-aware MultiProvider
class MultiProtocolProvider extends MultiProvider {
override tryGetChainMetadata(chainNameOrId: ChainName | number): CustomChainMetadata | null {
override tryGetChainMetadata(chainNameOrId: ChainName | number): ChainMetadata | null {
let chainMetadata: ChainMetadata | undefined;
if (isNumeric(chainNameOrId)) {
chainMetadata = Object.values(this.metadata).find(
@ -22,20 +27,20 @@ class MultiProtocolProvider extends MultiProvider {
return chainMetadata || null;
}
override getChainMetadata(chainNameOrId: ChainName | number): CustomChainMetadata {
return super.getChainMetadata(chainNameOrId) as CustomChainMetadata;
// Custom chains include deployment artifacts so this gets metadata type with optional contract addresses
getChainMetadataWithArtifacts(chainNameOrId: ChainName | number) {
const metadata = super.getChainMetadata(chainNameOrId);
return metadata as ChainMetadata & Partial<HyperlaneDeploymentArtifacts>;
}
override tryGetProvider(chainNameOrId: ChainName | number): providers.Provider | null {
const metadata = this.tryGetChainMetadata(chainNameOrId);
// @ts-ignore TODO add optional protocol field to ChainMetadata in SDK
if (metadata?.protocol && metadata.protocol !== ProtocolType.Ethereum) return null;
return super.tryGetProvider(chainNameOrId);
}
override tryGetSigner(chainNameOrId: ChainName | number): Signer | null {
const metadata = this.tryGetChainMetadata(chainNameOrId);
// @ts-ignore TODO add optional protocol field to ChainMetadata in SDK
if (metadata?.protocol && metadata.protocol !== ProtocolType.Ethereum) return null;
return super.tryGetSigner(chainNameOrId);
}

@ -1,9 +1,10 @@
import { Connection } from '@solana/web3.js';
import { providers } from 'ethers';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { convertToProtocolAddress } from '../../../utils/addresses';
import { parseCaip2Id } from '../../chains/caip2';
import { ProtocolType } from '../../chains/types';
import { getMultiProvider, getProvider } from '../../multiProvider';
import { isNativeToken } from '../native';
import { Route, RouteType } from '../routes/types';

@ -1,10 +1,9 @@
import { TokenType } from '@hyperlane-xyz/hyperlane-token';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { tokenList } from '../../consts/tokens';
import { logger } from '../../utils/logger';
import { getCaip2Id } from '../chains/caip2';
import { ProtocolType } from '../chains/types';
import { getMultiProvider } from '../multiProvider';
import { getNativeTokenAddress } from './native';
import { TokenMetadata, WarpTokenConfig, WarpTokenConfigSchema } from './types';
@ -25,7 +24,6 @@ function parseTokenConfigs(configList: WarpTokenConfig): TokenMetadata[] {
throw new Error(`Invalid token config: ${result.error.toString()}`);
}
const multiProvider = getMultiProvider();
const parsedConfig = result.data;
const tokenMetadata: TokenMetadata[] = [];
for (const token of parsedConfig) {
@ -38,9 +36,7 @@ function parseTokenConfigs(configList: WarpTokenConfig): TokenMetadata[] {
decimals,
logoURI,
} = token;
const reference =
protocol === ProtocolType.Ethereum ? chainId : multiProvider.getChainName(chainId);
const caip2Id = getCaip2Id(protocol, reference);
const caip2Id = getCaip2Id(protocol, chainId);
const commonFields = { caip2Id, chainId, name, symbol, decimals, logoURI };
if (type == TokenType.collateral) {
tokenMetadata.push({

@ -1,8 +1,9 @@
import { ethers } from 'ethers';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { SOL_ZERO_ADDRESS } from '../../consts/values';
import { isZeroishAddress } from '../../utils/addresses';
import { ProtocolType } from '../chains/types';
export function isNativeToken(tokenAddress: Address) {
return isZeroishAddress(tokenAddress);

@ -2,10 +2,10 @@ import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { TokenType } from '@hyperlane-xyz/hyperlane-token';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { logger } from '../../../utils/logger';
import { getCaip2Id, getProtocolType } from '../../chains/caip2';
import { ProtocolType } from '../../chains/types';
import { getMultiProvider, getProvider } from '../../multiProvider';
import { AdapterFactory } from '../adapters/AdapterFactory';
import { EvmTokenAdapter } from '../adapters/EvmTokenAdapter';
@ -94,11 +94,7 @@ async function fetchRemoteHypTokens(
const hypTokens = remoteRouters.map((router) => {
const destMetadata = multiProvider.getChainMetadata(router.domain);
const protocol: ProtocolType = (destMetadata.protocol as ProtocolType) || ProtocolType.Ethereum;
const reference =
protocol === ProtocolType.Ethereum
? multiProvider.getChainId(router.domain)
: multiProvider.getChainName(router.domain);
const caip2Id = getCaip2Id(protocol, reference);
const caip2Id = getCaip2Id(protocol, multiProvider.getChainId(router.domain));
return {
address: router.address,
caip2Id,

@ -1,9 +1,7 @@
import { z } from 'zod';
import { TokenType } from '@hyperlane-xyz/hyperlane-token';
import { ERC20Metadata } from '@hyperlane-xyz/hyperlane-token/dist/config';
import { ProtocolType } from '../chains/types';
import { ERC20Metadata, TokenType } from '@hyperlane-xyz/hyperlane-token';
import { ProtocolType } from '@hyperlane-xyz/sdk';
export type MinimumTokenMetadata = Omit<ERC20Metadata, 'totalSupply'>;

@ -1,6 +1,7 @@
import { Form, Formik, useFormikContext } from 'formik';
import { useMemo, useState } from 'react';
import { ProtocolSmallestUnit } from '@hyperlane-xyz/sdk';
import { WideChevron } from '@hyperlane-xyz/widgets';
import { ConnectAwareSubmitButton } from '../../components/buttons/ConnectAwareSubmitButton';
@ -15,7 +16,6 @@ import { fromWei, fromWeiRounded, toWei, tryParseAmount } from '../../utils/amou
import { logger } from '../../utils/logger';
import { ChainSelectField } from '../chains/ChainSelectField';
import { getProtocolType } from '../chains/caip2';
import { ProtocolSmallestUnit } from '../chains/types';
import { getChainDisplayName } from '../chains/utils';
import { AppState, useStore } from '../store';
import { SelectOrInputTokenIds } from '../tokens/SelectOrInputTokenIds';

@ -20,11 +20,12 @@ export function TransfersStatusBar() {
if (!transfers.length) return null;
const totalTxs = transfers.length;
// const totalTxs = transfers.length;
const pendingTxs = transfers.filter(
(tx) => ![TransferStatus.Delivered, TransferStatus.Failed].includes(tx.status),
).length;
const deliveredTxs = transfers.filter((tx) => tx.status === TransferStatus.Delivered).length;
// TODO re-enable when fixed for PI chains
// const deliveredTxs = transfers.filter((tx) => tx.status === TransferStatus.Delivered).length;
return (
<div className="relative flex justify-center py-1 px-1 bg-white shadow-md rounded-md">
@ -42,8 +43,12 @@ export function TransfersStatusBar() {
/>
</div>
<div className="ml-2.5 flex flex-col items-start">
<div className="text-xs text-gray-500">Transfers</div>
<div className="text-xs">{`Delivered: ${deliveredTxs} / ${totalTxs}`}</div>
<div className="text-left text-xs text-gray-500">
Transfer
<br />
History
</div>
{/* <div className="text-xs">{`Delivered: ${deliveredTxs} / ${totalTxs}`}</div> */}
</div>
</button>
<TransfersStatusModal

@ -3,13 +3,12 @@ import { BigNumber, PopulatedTransaction as EvmTransaction, providers } from 'et
import { useCallback, useState } from 'react';
import { toast } from 'react-toastify';
import { HyperlaneCore } from '@hyperlane-xyz/sdk';
import { HyperlaneCore, ProtocolType } from '@hyperlane-xyz/sdk';
import { toastTxSuccess } from '../../components/toast/TxSuccessToast';
import { toWei } from '../../utils/amount';
import { logger } from '../../utils/logger';
import { getProtocolType, parseCaip2Id } from '../chains/caip2';
import { ProtocolType } from '../chains/types';
import { getMultiProvider } from '../multiProvider';
import { AppState, useStore } from '../store';
import { AdapterFactory } from '../tokens/adapters/AdapterFactory';
@ -109,7 +108,7 @@ async function executeTransfer({
const multiProvider = getMultiProvider();
const destinationDomainId = multiProvider.getDomainId(destReference);
const originMetadata = multiProvider.getChainMetadata(originReference);
const originMetadata = multiProvider.getChainMetadataWithArtifacts(originReference);
const originMailbox = originMetadata.mailbox;
const tokenRoute = getTokenRoute(originCaip2Id, destinationCaip2Id, tokenAddress, tokenRoutes);

@ -1,11 +1,9 @@
import Image from 'next/image';
import { PropsWithChildren } from 'react';
import EthereumLogo from '@hyperlane-xyz/sdk/logos/color/ethereum.svg';
import { ProtocolType, chainMetadata } from '@hyperlane-xyz/sdk';
import { ChainLogo } from '@hyperlane-xyz/widgets';
import { Modal } from '../../components/layout/Modal';
import SolanaLogo from '../../images/logos/solana.svg';
import { ProtocolType } from '../chains/types';
import { useConnectFns } from './hooks';
@ -24,14 +22,14 @@ export function WalletEnvSelectionModal({ isOpen, close }: { isOpen: boolean; cl
<EnvButton
onClick={onClickEnv(ProtocolType.Ethereum)}
subTitle="an EVM"
imgSrc={EthereumLogo}
logoChainId={chainMetadata.ethereum.chainId}
>
Ethereum
</EnvButton>
<EnvButton
onClick={onClickEnv(ProtocolType.Sealevel)}
subTitle="a Solana"
imgSrc={SolanaLogo}
logoChainId={chainMetadata.solanadevnet.chainId}
>
Solana
</EnvButton>
@ -43,15 +41,15 @@ export function WalletEnvSelectionModal({ isOpen, close }: { isOpen: boolean; cl
function EnvButton({
onClick,
subTitle,
imgSrc,
logoChainId,
children,
}: PropsWithChildren<{ subTitle: string; imgSrc: any; onClick?: () => void }>) {
}: PropsWithChildren<{ subTitle: string; logoChainId: number; onClick?: () => void }>) {
return (
<button
onClick={onClick}
className="w-full py-6 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"
>
<Image src={imgSrc} width={34} height={34} alt="" />
<ChainLogo chainId={logoChainId} size={34} />
<div className="uppercase text-gray-800 tracking-wide">{children}</div>
<div className="text-sm text-gray-500">{`Connect to ${subTitle} compatible wallet`}</div>
</button>

@ -1,7 +1,7 @@
import { useConnectModal as useEvmodal } from '@rainbow-me/rainbowkit';
import { useConnection, useWallet as useSolanaWallet } from '@solana/wallet-adapter-react';
import { useWalletModal as useSolanaModal } from '@solana/wallet-adapter-react-ui';
import { Connection as SolConnection, clusterApiUrl } from '@solana/web3.js';
import { Connection } from '@solana/web3.js';
import {
sendTransaction as sendEvmTransaction,
switchNetwork as switchEvmNetwork,
@ -15,7 +15,8 @@ import {
useNetwork as useEvmNetwork,
} from 'wagmi';
import { getSolanaChainName, getSolanaClusterName } from '../../consts/solanaChains';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { logger } from '../../utils/logger';
import { sleep } from '../../utils/timeout';
import {
@ -24,7 +25,8 @@ import {
getEthereumChainId,
tryGetProtocolType,
} from '../chains/caip2';
import { ProtocolType } from '../chains/types';
import { getChainByRpcEndpoint } from '../chains/utils';
import { getMultiProvider } from '../multiProvider';
export interface AccountInfo {
protocol: ProtocolType;
@ -86,6 +88,7 @@ export function useAccounts(): {
accounts: {
[ProtocolType.Ethereum]: evmAccountInfo,
[ProtocolType.Sealevel]: solAccountInfo,
[ProtocolType.Fuel]: { protocol: ProtocolType.Fuel, isReady: false },
},
readyAccounts,
}),
@ -113,6 +116,7 @@ export function useConnectFns(): Record<ProtocolType, () => void> {
() => ({
[ProtocolType.Ethereum]: onConnectEthereum,
[ProtocolType.Sealevel]: onConnectSolana,
[ProtocolType.Fuel]: () => alert('TODO'),
}),
[onConnectEthereum, onConnectSolana],
);
@ -140,6 +144,9 @@ export function useDisconnectFns(): Record<ProtocolType, () => Promise<void>> {
() => ({
[ProtocolType.Ethereum]: onClickDisconnect(ProtocolType.Ethereum, disconnectEvm),
[ProtocolType.Sealevel]: onClickDisconnect(ProtocolType.Sealevel, disconnectSol),
[ProtocolType.Fuel]: onClickDisconnect(ProtocolType.Sealevel, () => {
'TODO';
}),
}),
[disconnectEvm, disconnectSol],
);
@ -166,16 +173,16 @@ export function useActiveChains(): {
// Solana
const { connection } = useConnection();
const { name: solName, displayName: solDisplayName } = getSolanaChainName(
connection?.rpcEndpoint,
);
const solChain: ActiveChainInfo = useMemo(
() => ({
chainDisplayName: solDisplayName,
caip2Id: solName ? getCaip2Id(ProtocolType.Sealevel, solName) : undefined,
}),
[solDisplayName, solName],
);
const connectionEndpoint = connection?.rpcEndpoint;
const solChain: ActiveChainInfo = useMemo(() => {
const metadata = getChainByRpcEndpoint(connectionEndpoint);
if (!metadata) return {};
return {
chainDisplayName: metadata.displayName,
caip2Id: getCaip2Id(ProtocolType.Sealevel, metadata.chainId),
};
}, [connectionEndpoint]);
const readyChains = useMemo(
() => [evmChain, solChain].filter((c) => !!c.chainDisplayName),
@ -187,6 +194,7 @@ export function useActiveChains(): {
chains: {
[ProtocolType.Ethereum]: evmChain,
[ProtocolType.Sealevel]: solChain,
[ProtocolType.Fuel]: {},
},
readyChains,
}),
@ -248,9 +256,7 @@ export function useTransactionFns(): Record<
const { sendTransaction: sendSolTransaction } = useSolanaWallet();
const onSwitchSolNetwork = useCallback(async (caip2Id: Caip2Id) => {
toast.error(`Solana wallet must be connected to origin chain ${caip2Id}}`);
// TODO re-enable when devnet->devnet transfers are no longer needed
// throw new Error(`Auto network switching not supported for Solana`);
toast.warn(`Solana wallet must be connected to origin chain ${caip2Id}}`);
}, []);
const onSendSolTx = useCallback(
@ -264,9 +270,8 @@ export function useTransactionFns(): Record<
activeCap2Id?: Caip2Id;
}) => {
if (activeCap2Id && activeCap2Id !== caip2Id) await onSwitchSolNetwork(caip2Id);
const reference = getChainReference(caip2Id);
const cluster = getSolanaClusterName(reference);
const connection = new SolConnection(clusterApiUrl(cluster), 'confirmed');
const rpcUrl = getMultiProvider().getRpcUrl(getChainReference(caip2Id));
const connection = new Connection(rpcUrl, 'confirmed');
const {
context: { slot: minContextSlot },
value: { blockhash, lastValidBlockHeight },
@ -285,6 +290,10 @@ export function useTransactionFns(): Record<
() => ({
[ProtocolType.Ethereum]: { sendTransaction: onSendEvmTx, switchNetwork: onSwitchEvmNetwork },
[ProtocolType.Sealevel]: { sendTransaction: onSendSolTx, switchNetwork: onSwitchSolNetwork },
[ProtocolType.Fuel]: {
sendTransaction: () => alert('TODO') as any,
switchNetwork: () => alert('TODO') as any,
},
}),
[onSendEvmTx, onSendSolTx, onSwitchEvmNetwork, onSwitchSolNetwork],
);

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="enable-background:new 0 0 397.7 311.7" viewBox="0 0 397.7 311.7"><linearGradient id="a" x1="360.88" x2="141.21" y1="351.46" y2="-69.29" gradientTransform="matrix(1 0 0 -1 0 314)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#00ffa3"/><stop offset="1" style="stop-color:#dc1fff"/></linearGradient><path d="M64.6 237.9c2.4-2.4 5.7-3.8 9.2-3.8h317.4c5.8 0 8.7 7 4.6 11.1l-62.7 62.7c-2.4 2.4-5.7 3.8-9.2 3.8H6.5c-5.8 0-8.7-7-4.6-11.1l62.7-62.7z" style="fill:url(#a)"/><linearGradient id="b" x1="264.83" x2="45.16" y1="401.6" y2="-19.15" gradientTransform="matrix(1 0 0 -1 0 314)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#00ffa3"/><stop offset="1" style="stop-color:#dc1fff"/></linearGradient><path d="M64.6 3.8C67.1 1.4 70.4 0 73.8 0h317.4c5.8 0 8.7 7 4.6 11.1l-62.7 62.7c-2.4 2.4-5.7 3.8-9.2 3.8H6.5c-5.8 0-8.7-7-4.6-11.1L64.6 3.8z" style="fill:url(#b)"/><linearGradient id="c" x1="312.55" x2="92.88" y1="376.69" y2="-44.06" gradientTransform="matrix(1 0 0 -1 0 314)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#00ffa3"/><stop offset="1" style="stop-color:#dc1fff"/></linearGradient><path d="M333.1 120.1c-2.4-2.4-5.7-3.8-9.2-3.8H6.5c-5.8 0-8.7 7-4.6 11.1l62.7 62.7c2.4 2.4 5.7 3.8 9.2 3.8h317.4c5.8 0 8.7-7 4.6-11.1l-62.7-62.7z" style="fill:url(#c)"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,10 +1,10 @@
import { getAddress, isAddress } from '@ethersproject/address';
import { PublicKey } from '@solana/web3.js';
import { ProtocolType } from '@hyperlane-xyz/sdk';
import { utils } from '@hyperlane-xyz/utils';
import { SOL_ZERO_ADDRESS } from '../consts/values';
import { ProtocolType } from '../features/chains/types';
import { logger } from './logger';

@ -856,36 +856,36 @@ __metadata:
languageName: node
linkType: hard
"@hyperlane-xyz/core@npm:1.3.4":
version: 1.3.4
resolution: "@hyperlane-xyz/core@npm:1.3.4"
"@hyperlane-xyz/core@npm:1.4.1":
version: 1.4.1
resolution: "@hyperlane-xyz/core@npm:1.4.1"
dependencies:
"@hyperlane-xyz/utils": 1.3.4
"@hyperlane-xyz/utils": 1.4.1
"@openzeppelin/contracts": ^4.8.0
"@openzeppelin/contracts-upgradeable": ^4.8.0
checksum: aaea49039a37dc093fb785ba79480be28a41b62936dd2910ac25fbe07e8275ea6a1f8f477e0872c8d5880dede24e2fb5bce5f0153626a36113de18578e4032f7
checksum: 76471f2446c9539888a8120d72f2ec2e0a548026956aca45f4f96bb4aaa28fc68a501b2ca29e3faca053aa33ee2fe92afc3175573f9e12cc8698b9f11aa83454
languageName: node
linkType: hard
"@hyperlane-xyz/hyperlane-token@npm:^1.3.4":
version: 1.3.4
resolution: "@hyperlane-xyz/hyperlane-token@npm:1.3.4"
"@hyperlane-xyz/hyperlane-token@npm:^1.4.1":
version: 1.4.1
resolution: "@hyperlane-xyz/hyperlane-token@npm:1.4.1"
dependencies:
"@hyperlane-xyz/core": 1.3.4
"@hyperlane-xyz/sdk": 1.3.4
"@hyperlane-xyz/utils": 1.3.4
"@hyperlane-xyz/core": 1.4.1
"@hyperlane-xyz/sdk": 1.4.1
"@hyperlane-xyz/utils": 1.4.1
"@openzeppelin/contracts-upgradeable": ^4.8.0
ethers: ^5.7.2
checksum: 3dfa26bcc23783ae430f8b16fe15f5e15c5e3f2de633f9002c8210e6011fdb7a1612dbcbb05f22e5ab1ed4575d254a2c55d39e6d1b11e2deb161c6803c9f0fc5
checksum: e1bbe74190a543ab70fb59e054983b22520227d9fc72cab554a2f092a3b6bb1f5b6255367941993cfffd64977ad25a28d7d5fbc7da79536fa156f70213c292b8
languageName: node
linkType: hard
"@hyperlane-xyz/sdk@npm:1.3.4, @hyperlane-xyz/sdk@npm:^1.3.4":
version: 1.3.4
resolution: "@hyperlane-xyz/sdk@npm:1.3.4"
"@hyperlane-xyz/sdk@npm:1.4.1, @hyperlane-xyz/sdk@npm:^1.4.1":
version: 1.4.1
resolution: "@hyperlane-xyz/sdk@npm:1.4.1"
dependencies:
"@hyperlane-xyz/core": 1.3.4
"@hyperlane-xyz/utils": 1.3.4
"@hyperlane-xyz/core": 1.4.1
"@hyperlane-xyz/utils": 1.4.1
"@types/coingecko-api": ^1.0.10
"@types/debug": ^4.1.7
"@wagmi/chains": ^0.2.6
@ -894,16 +894,16 @@ __metadata:
debug: ^4.3.4
ethers: ^5.7.2
zod: ^3.21.2
checksum: c881b51ea1e0aada4cf12a485a0b614ee0fbb6095941c63ec0ba7bc41a59b29e7a05498ab8ac3e89a50430b2973218dece6f2cbd09fd8697bead49be99b3663a
checksum: 108d19dbc050cb6693660344bd239a7b0f4f89bda0c6ab47d47dbac7f633553200a251b6b1897c232cdb572b8b6b2c3e5b446904d98cdb7e08ca7389e6bae7a4
languageName: node
linkType: hard
"@hyperlane-xyz/utils@npm:1.3.4":
version: 1.3.4
resolution: "@hyperlane-xyz/utils@npm:1.3.4"
"@hyperlane-xyz/utils@npm:1.4.1":
version: 1.4.1
resolution: "@hyperlane-xyz/utils@npm:1.4.1"
dependencies:
ethers: ^5.7.2
checksum: 7bed3465604dd3e20d31ebc2b4d101cfe6a934df9d0625a11e9fb5dfa39dbd009c1fc4ad269a32662d4c4a3abadacf5195f53462be021494ba15896036a53f7a
checksum: 04897ef9f1681348d68b8a8c75cdfd0d50b7c7702739d316e287f552652e36c5845abd0cbf767188e7438b419d98ccba0b9aa7b0ef6e972e67c0e1f0b13289ef
languageName: node
linkType: hard
@ -912,9 +912,9 @@ __metadata:
resolution: "@hyperlane-xyz/warp-ui-template@workspace:."
dependencies:
"@headlessui/react": ^1.7.14
"@hyperlane-xyz/hyperlane-token": ^1.3.4
"@hyperlane-xyz/sdk": ^1.3.4
"@hyperlane-xyz/widgets": ^1.3.5-beta0
"@hyperlane-xyz/hyperlane-token": ^1.4.1
"@hyperlane-xyz/sdk": ^1.4.1
"@hyperlane-xyz/widgets": ^1.4.1
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6"
"@rainbow-me/rainbowkit": 0.12.16
"@solana/spl-token": ^0.3.8
@ -953,14 +953,14 @@ __metadata:
languageName: unknown
linkType: soft
"@hyperlane-xyz/widgets@npm:^1.3.5-beta0":
version: 1.3.5-beta0
resolution: "@hyperlane-xyz/widgets@npm:1.3.5-beta0"
"@hyperlane-xyz/widgets@npm:^1.4.1":
version: 1.4.1
resolution: "@hyperlane-xyz/widgets@npm:1.4.1"
peerDependencies:
"@hyperlane-xyz/sdk": ^1.3.4
"@hyperlane-xyz/sdk": ^1.4
react: ^18
react-dom: ^18
checksum: c2e0b83037a7175d76f01630fab9d51a324aab8b1829097c39212ad342292653197d248b9001979826b6a1e4b9418129f4a806181138eaf675970896aebd02de
checksum: ca96f2a426a76e82763c64e821d3c5809b2eb25acfbdddee2df4c123511492aca8406c21f161139787b335c9743f0a2dc242920b5335c3bad55be605c8defb5c
languageName: node
linkType: hard

Loading…
Cancel
Save