Skip to content

chore: merge main into ezeth #511

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

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
44a27e6
chore: upgrade hyperlane packages (#508)
Xaroz Apr 16, 2025
94e924a
feat: include transport fallback for wagmiConfig (#515)
Xaroz Apr 17, 2025
e9f7b07
feat: check for all RPC healths in ChainConnectionWarning for EVM cha…
Xaroz Apr 18, 2025
4e0da55
chore: partial registry and change image and RPC url logic (#521)
Xaroz Apr 21, 2025
7611078
chore: bump registry to latest version
xeno097 Apr 21, 2025
1ade9b6
Merge pull request #524 from hyperlane-xyz/xeno/update-registry-package
xeno097 Apr 21, 2025
3b7b43c
chore: increase rpc health check timeout (#527)
Xaroz Apr 22, 2025
0623f7e
Bump registry
Apr 22, 2025
5846cdc
Merge pull request #529 from hyperlane-xyz/ltyu/registry-13.7.1
ltyu Apr 22, 2025
4b85aaa
Up Registry to 13.9.0
Apr 23, 2025
6067416
Merge pull request #532 from hyperlane-xyz/ltyu/registry-13.9.0
ltyu Apr 23, 2025
01cdd74
feat: updated sdk package version + added support for revoke txs on t…
xeno097 Apr 24, 2025
49f48e0
Merge pull request #535 from hyperlane-xyz/xeno/update-packages-to-fi…
xeno097 Apr 25, 2025
c6cc0c9
chore: cosmos addresses (#536)
troykessler Apr 26, 2025
9bd34e5
chore: update .gitignore to exclude all .env files except .env.example
ljankovic-txfusion Apr 28, 2025
88ccd24
fix: set origin during effect instead of render (#541)
Xaroz Apr 28, 2025
c0b364d
chore: Update registry to 13.13.0 and hyperlane packages to 12.4.0 (#…
ltyu Apr 28, 2025
ff22a4d
feat: add connector to binance wallet (#486)
Xaroz Apr 28, 2025
2c183ed
Merge remote-tracking branch 'origin/main' into feat/gitignore-env-pr…
ljankovic-txfusion Apr 29, 2025
170360b
Merge pull request #540 from hyperlane-xyz/feat/gitignore-env-prefix
ljankovic-txfusion Apr 29, 2025
b4a5d97
chore: increase refetch interval for useBalance (#545)
Xaroz Apr 29, 2025
787a20b
Merge remote-tracking branch 'origin/main' into ezeth
github-actions[bot] Apr 29, 2025
cd09120
feat: search full list token (#544)
Xaroz Apr 30, 2025
5b8dbcd
chore: bump hyperlane packages (#555)
Xaroz May 7, 2025
6682edf
chore: slightly increase width for token symbol and name (#558)
Xaroz May 7, 2025
b8f170f
chore: upgrade registry to 15.0.0 (#562)
Xaroz May 8, 2025
85a108c
chore: bump registry to 15.1.0 (#564)
Xaroz May 12, 2025
1f7728c
Merge branch 'main' into main-to-ezeth
Xaroz May 12, 2025
c342a6b
chore: bump registry (#568)
xeno097 May 12, 2025
a9ef6b1
chore: Bump up registry to 15.4.0 (#574)
ltyu May 19, 2025
ec5ecc6
chore: Bump up registry to 15.5.0 (#577)
ltyu May 19, 2025
f65a1a5
Merge branch 'main' into main-to-ezeth
Xaroz May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
branches: [main, nautilus, nexus, injective, ezeth, trump, ousdt]
pull_request:
branches: [main, nautilus, nexus, injective, ezeth, trump, ousdt]
merge_group:

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local
.env*
!.env.example

# vercel
.vercel
Expand Down
7 changes: 4 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
},
"files.exclude": {
"**/*.js.map": true,
"**/*.js": {"when": "$(basename).ts"},
"**/*.map": {"when": "$(basename).map"},
"**/*.js": { "when": "$(basename).ts" },
"**/*.map": { "when": "$(basename).map" }
},
"files.watcherExclude": {
"**/.git/objects/**": true,
Expand Down Expand Up @@ -38,5 +38,6 @@
},
"editor.tabSize": 2,
"editor.detectIndentation": false,
"tailwindCSS.experimental.classRegex": [["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]]
"tailwindCSS.experimental.classRegex": [["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]],
"typescript.tsdk": "node_modules/typescript/lib"
}
42 changes: 26 additions & 16 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
/** @type {import('next').NextConfig} */

const { version } = require('./package.json')
const { withSentryConfig } = require("@sentry/nextjs");
const { version } = require('./package.json');
const { withSentryConfig } = require('@sentry/nextjs');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
});

const isDev = process.env.NODE_ENV !== 'production'
const isDev = process.env.NODE_ENV !== 'production';

// Sometimes useful to disable this during development
const ENABLE_CSP_HEADER = true;
const FRAME_SRC_HOSTS = ['https://*.walletconnect.com', 'https://*.walletconnect.org','https://*.solflare.com'];
const STYLE_SRC_HOSTS = []
const IMG_SRC_HOSTS = ['https://*.walletconnect.com', 'https://*.githubusercontent.com', 'https://*.hyperlane.xyz'];
const FRAME_SRC_HOSTS = [
'https://*.walletconnect.com',
'https://*.walletconnect.org',
'https://cdn.solflare.com',
];
const STYLE_SRC_HOSTS = [];
const IMG_SRC_HOSTS = [
'https://*.walletconnect.com',
'https://*.githubusercontent.com',
'https://cdn.jsdelivr.net/gh/hyperlane-xyz/hyperlane-registry@main/',
];
const cspHeader = `
default-src 'self';
script-src 'self'${isDev ? " 'unsafe-eval'" : ''};
Expand All @@ -27,7 +35,9 @@ const cspHeader = `
frame-ancestors 'none';
${!isDev ? 'block-all-mixed-content;' : ''}
${!isDev ? 'upgrade-insecure-requests;' : ''}
`.replace(/\s{2,}/g, ' ').trim();
`
.replace(/\s{2,}/g, ' ')
.trim();

const securityHeaders = [
{
Expand All @@ -54,8 +64,8 @@ const securityHeaders = [
value: cspHeader,
},
]
: [])
]
: []),
];

const nextConfig = {
webpack(config) {
Expand All @@ -72,27 +82,27 @@ const nextConfig = {
source: '/(.*)',
headers: securityHeaders,
},
]
];
},

env: {
NEXT_PUBLIC_VERSION: version,
},

reactStrictMode: true,
}
};

const sentryOptions = {
org: "hyperlane",
project: "warp-ui",
org: 'hyperlane',
project: 'warp-ui',
authToken: process.env.SENTRY_AUTH_TOKEN,
hideSourceMaps: true,
tunnelRoute: "/monitoring-tunnel",
tunnelRoute: '/monitoring-tunnel',
bundleSizeOptimizations: {
excludeDebugStatements: true,
excludeReplayIframe: true,
excludeReplayShadowDom: true,
},
};

module.exports = withBundleAnalyzer(withSentryConfig(nextConfig, sentryOptions));
module.exports = withBundleAnalyzer(withSentryConfig(nextConfig, sentryOptions));
10 changes: 5 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": "8.5.0",
"version": "12.1.0",
"author": "J M Rossy",
"dependencies": {
"@chakra-ui/next-js": "^2.4.2",
Expand All @@ -17,10 +17,10 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@headlessui/react": "^2.2.0",
"@hyperlane-xyz/registry": "12.1.0",
"@hyperlane-xyz/sdk": "9.2.1",
"@hyperlane-xyz/utils": "9.2.1",
"@hyperlane-xyz/widgets": "9.2.1",
"@hyperlane-xyz/registry": "15.5.0",
"@hyperlane-xyz/sdk": "12.5.0",
"@hyperlane-xyz/utils": "12.5.0",
"@hyperlane-xyz/widgets": "12.5.0",
"@interchain-ui/react": "^1.23.28",
"@metamask/post-message-stream": "6.1.2",
"@metamask/providers": "10.2.1",
Expand Down
11 changes: 5 additions & 6 deletions src/components/icons/TokenIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { IRegistry } from '@hyperlane-xyz/registry';
import { IToken } from '@hyperlane-xyz/sdk';
import { isHttpsUrl, isRelativeUrl } from '@hyperlane-xyz/utils';
import { Circle } from '@hyperlane-xyz/widgets';
import { useState } from 'react';
import { useStore } from '../../features/store';
import { links } from '../../consts/links';

interface Props {
token?: IToken | null;
Expand All @@ -16,8 +15,7 @@ export function TokenIcon({ token, size = 32 }: Props) {
const fontSize = Math.floor(size / 2);

const [fallbackToText, setFallbackToText] = useState(false);
const registry = useStore((s) => s.registry);
const imageSrc = getImageSrc(registry, token);
const imageSrc = getImageSrc(token);
const bgColorSeed =
token && (!imageSrc || fallbackToText)
? (Buffer.from(token.addressOrDenom).at(0) || 0) % 5
Expand All @@ -32,6 +30,7 @@ export function TokenIcon({ token, size = 32 }: Props) {
height={size}
className="p-0.5"
onError={() => setFallbackToText(true)}
loading="lazy"
/>
) : (
<div className={`text-[${fontSize}px]`}>{character}</div>
Expand All @@ -40,11 +39,11 @@ export function TokenIcon({ token, size = 32 }: Props) {
);
}

function getImageSrc(registry: IRegistry, token?: IToken | null) {
function getImageSrc(token?: IToken | null) {
if (!token?.logoURI) return null;
// If it's a valid, direct URL, return it
if (isHttpsUrl(token.logoURI)) return token.logoURI;
// Otherwise assume it's a relative URL to the registry base
if (isRelativeUrl(token.logoURI)) return registry.getUri(token.logoURI);
if (isRelativeUrl(token.logoURI)) return `${links.imgPath}${token.logoURI}`;
return null;
}
13 changes: 12 additions & 1 deletion src/consts/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {
eclipsemainnetAddresses,
solanamainnet,
solanamainnetAddresses,
sonicsvm,
sonicsvmAddresses,
soon,
soonAddresses,
} from '@hyperlane-xyz/registry';
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk';

Expand All @@ -15,12 +19,19 @@ export const chains: ChainMap<ChainMetadata & { mailbox?: Address }> = {
...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
},
eclipsemainnet: {
...eclipsemainnet,
mailbox: eclipsemainnetAddresses.mailbox,
},
soon: {
...soon,
mailbox: soonAddresses.mailbox,
},
sonicsvm: {
...sonicsvm,
mailbox: sonicsvmAddresses.mailbox,
},
// mycustomchain: {
// protocol: ProtocolType.Ethereum,
// chainId: 123123,
Expand Down
1 change: 1 addition & 0 deletions src/consts/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export const links = {
privacyPolicy: 'https://hyperlane.xyz/privacy-policy',
bounty:
'https://github.com/search?q=org:hyperlane-xyz+label:bounty+is:open+is:issue&type=issues&s=&o=desc',
imgPath: 'https://cdn.jsdelivr.net/gh/hyperlane-xyz/hyperlane-registry@main',
};
64 changes: 64 additions & 0 deletions src/features/chains/ChainConnectionWarning.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ChainMetadata, isRpcHealthy } from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { checkRpcHealth } from './ChainConnectionWarning';

vi.mock('@hyperlane-xyz/sdk', async (importOriginal) => {
const actual = (await importOriginal()) as { MultiProtocolProvider: any };
return {
...actual,
MultiProtocolProvider: vi.fn().mockImplementation(() => ({
getProvider: vi.fn(),
})),
isRpcHealthy: vi.fn(),
};
});

const mockRpcUrl = 'http://mock.test.rpc.com';

const mockEvmChainMetadata: ChainMetadata = {
name: 'TestChain',
protocol: ProtocolType.Ethereum,
rpcUrls: [{ http: mockRpcUrl }, { http: mockRpcUrl }],
chainId: 10000000,
domainId: 10000000,
};
const mockSvmChainMetadata = {
name: 'TestChain',
protocol: ProtocolType.Sealevel,
rpcUrls: [{ http: mockRpcUrl }, { http: mockRpcUrl }],
chainId: 10000000,
domainId: 10000000,
};

describe('checkRpcHealth', () => {
beforeEach(() => {
vi.clearAllMocks();
});

test('should call isRpcHealthy as many times as rpcUrls length when chain protocol is Ethereum', async () => {
(isRpcHealthy as ReturnType<typeof vi.fn>).mockImplementation(() => Promise.resolve(true));
await checkRpcHealth(mockEvmChainMetadata);
expect(isRpcHealthy).toHaveBeenCalledTimes(mockEvmChainMetadata.rpcUrls.length);
});

test('should call isRpcHealthy only once for non Ethereum chains', async () => {
(isRpcHealthy as ReturnType<typeof vi.fn>).mockImplementation(() => Promise.resolve(true));
await checkRpcHealth(mockSvmChainMetadata);
expect(isRpcHealthy).toHaveBeenCalledTimes(1);
});

test('should return true if at least one Ethereum RPC is healthy', async () => {
(isRpcHealthy as ReturnType<typeof vi.fn>).mockImplementation((_, i) =>
i === 1 ? Promise.resolve(true) : Promise.reject(),
);
const result = await checkRpcHealth(mockEvmChainMetadata);
expect(result).toBe(true);
});

test('should return false if no RPCs are healthy', async () => {
(isRpcHealthy as ReturnType<typeof vi.fn>).mockImplementation(() => Promise.resolve(false));
const result = await checkRpcHealth(mockEvmChainMetadata as any);
expect(result).toBe(false);
});
});
22 changes: 15 additions & 7 deletions src/features/chains/ChainConnectionWarning.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChainMetadata, isRpcHealthy } from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { FormWarningBanner } from '../../components/banner/FormWarningBanner';
Expand All @@ -25,7 +26,7 @@ export function ChainConnectionWarning({
const isDestinationHealthy = await checkRpcHealth(destinationMetadata);
return { isOriginHealthy, isDestinationHealthy };
},
refetchInterval: 5000,
refetchInterval: 300000, // 5 minutes
});

const unhealthyChain =
Expand Down Expand Up @@ -62,14 +63,21 @@ export function ChainConnectionWarning({
);
}

async function checkRpcHealth(chainMetadata: ChainMetadata) {
export async function checkRpcHealth(chainMetadata: ChainMetadata) {
try {
// Note: this currently checks the health of only the first RPC,
// which is what wallets and wallet libs (e.g. wagmi) will use
const isHealthy = await isRpcHealthy(chainMetadata, 0);
return isHealthy;
// Note: this currently checks the health of only the first RPC for non EVM chains,
// which is what wallets and wallet libs will use
// for EVM chains it will use a fallback RPC, that is why we need to check if any RPC are healthy instead
if (chainMetadata.protocol === ProtocolType.Ethereum) {
const healthChecks = chainMetadata.rpcUrls.map((_, i) =>
isRpcHealthy(chainMetadata, i).then((result) => (result ? true : Promise.reject())),
);
return await Promise.any(healthChecks);
} else return await isRpcHealthy(chainMetadata, 0);
} catch (error) {
logger.warn('Error checking RPC health', error);
if (error instanceof AggregateError)
logger.warn(`No healthy RPCs found for ${chainMetadata.name}`);
else logger.warn('Error checking RPC health', error);
return false;
}
}
33 changes: 25 additions & 8 deletions src/features/chains/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import {
mergeChainMetadataMap,
RpcUrlSchema,
} from '@hyperlane-xyz/sdk';
import { objFilter, objMap, promiseObjAll, tryParseJsonOrYaml } from '@hyperlane-xyz/utils';
import {
objFilter,
objMap,
promiseObjAll,
ProtocolType,
tryParseJsonOrYaml,
} from '@hyperlane-xyz/utils';
import { z } from 'zod';
import { chains as ChainsTS } from '../../consts/chains.ts';
import ChainsYaml from '../../consts/chains.yaml';
import { config } from '../../consts/config.ts';
import { links } from '../../consts/links.ts';
import { logger } from '../../utils/logger.ts';

export async function assembleChainMetadata(
Expand Down Expand Up @@ -49,7 +56,7 @@ export async function assembleChainMetadata(
registryChainMetadata,
async (chainName, metadata): Promise<ChainMetadata> => ({
...metadata,
logoURI: (await registry.getChainLogoUri(chainName)) || undefined,
logoURI: `${links.imgPath}/chains/${chainName}/logo.svg`,
}),
),
);
Expand All @@ -63,13 +70,23 @@ export async function assembleChainMetadata(
logger.warn('Invalid RPC overrides config', rpcOverrides.error);
}

const chainMetadata = objMap(mergedChainMetadata, (chainName, metadata) => ({
...metadata,
rpcUrls:
const chainMetadata = objMap(mergedChainMetadata, (chainName, metadata) => {
const overridesUrl =
rpcOverrides.success && rpcOverrides.data[chainName]
? [rpcOverrides.data[chainName], ...metadata.rpcUrls]
: metadata.rpcUrls,
}));
? rpcOverrides.data[chainName]
: undefined;

if (!overridesUrl) return metadata;

// Only EVM supports fallback transport, so we are putting the override at the end
const rpcUrls =
metadata.protocol === ProtocolType.Ethereum
? [...metadata.rpcUrls, overridesUrl]
: [overridesUrl, ...metadata.rpcUrls];

return { ...metadata, rpcUrls };
});

const chainMetadataWithOverrides = mergeChainMetadataMap(chainMetadata, storeMetadataOverrides);
return { chainMetadata, chainMetadataWithOverrides };
}
Loading
Loading