Skip to content

Commit 4fc329e

Browse files
Corey-CodeCopilotCopilot
authored
Feature/changes and improvements (#52)
* Feature - Integrate MoonPay SDK for buy/sell functionality in web app (#47) (#50) * Feature - Integrate MoonPay SDK for buy/sell functionality in web app * Update src/popup/components/MoonPaySDKWidget.tsx * Update Withdraw.tsx * Fix MoonPay extension documentation to reflect new tab implementation (#48) * Initial plan * Update MoonPayWidget documentation to reflect new tab approach --------- * Fix MoonPay widget documentation to reflect new tab implementation (#49) * Initial plan * Update MoonPay integration documentation to reflect new tab behavior --------- --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> * feat: add BIP32 and BIP84 derivation tests for Bitcoin addresses - Implemented BIP32 derivation logic in test-bip32.cjs for generating child keys and addresses. - Added BIP84 path derivation for native SegWit addresses in test-bip32.cjs. - Created test-keplr-path.cjs to validate Keplr-compatible derivation paths for Bitcoin. - Introduced comprehensive tests for UTXO transactions in transaction.test.ts, covering fee estimation, transaction building, and error handling. - Enhanced crypto tests to validate Bitcoin key derivation and address generation in keyring.test.ts. - Updated chainRegistry tests to mock cosmos-registry module for better isolation. * feat: implement IBC transfer functionality and modal in the dashboard * Add fee validation for sweepAll transactions to prevent excessive fees (#53) * Fix memory safety in deriveBitcoinKeyPairFromSeed key cleanup (#55) * Secure pubKey cleanup in BIP32 child key derivation (#54) * Update transaction.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update bitcoin.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update encrypted-storage.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update transaction.test.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update walletStore.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ibc-connections.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update IBCTransferModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update IBCTransferModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update IBCTransferModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update IBCTransferModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Dashboard.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update chainRegistry.test.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update transaction.test.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Simplify secureZero function in evm.ts to remove ineffective random overwrite (#56) * Add BIP32 hardened derivation indicator documentation (#57) * Fix memory leak in EVM BIP32 child key derivation (#58) * Use actual UTXO count for Bitcoin max amount fee estimation (#59) * Remove forceReDerive parameter from address derivation (#60) * Refactor IBC connections to use pre-bundled data; remove runtime fetching and caching logic - Updated `fetchIBCConnections` to utilize `getIBCChannelsForChainId` for fetching IBC connections. - Removed caching mechanism and related functions for IBC connections. - Re-exported `IBCChannel` type for backward compatibility. - Cleaned up code and comments for clarity. - Updated Cosmos registry file with new formatting and removed unnecessary comments. * Optimize address caching with two-level structure to persist across network switches (#63) * Initial plan * Implement two-level cache for Bitcoin and EVM addresses - Change cache structure from Map<cosmosAddress, address> to Map<cosmosAddress, Map<networkId, address>> - Addresses now persist across network switches - Only clear display state on network change, not the entire cache - Check cache before re-deriving addresses to avoid redundant work - Improves efficiency for users switching between networks Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Fix infinite loop in cache dependency arrays - Remove cache state from useEffect dependencies to prevent infinite loops - Use functional state updates to access previous cache state - Separate cache checking and updating into distinct phases - Maintains cache persistence while avoiding re-render cycles Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Refactor cache to use useRef instead of setState for reads - Replace useState cache with useRef to avoid anti-pattern - Use trigger state to force re-renders when cache updates - Read directly from ref without needing setState callbacks - Cleaner code that follows React best practices Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Add runtime Buffer polyfill checks to prevent initialization order issues (#62) * Initial plan * Add runtime Buffer availability checks to prevent initialization order issues Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Refactor: Extract ensureBuffer to shared utility module Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Optimize Buffer checks: call ensureBuffer once at module level Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> * Add documentation explaining defensive Buffer check in ensureBuffer Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Corey-Code <37006206+Corey-Code@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent f8e3d12 commit 4fc329e

25 files changed

+9613
-3850
lines changed

scripts/sync-chain-registry.ts

Lines changed: 216 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,53 @@ interface WalletAssetConfig {
158158
type?: 'native' | 'ibc' | 'cw20' | 'factory';
159159
}
160160

161+
/**
162+
* IBC Channel info for cross-chain transfers
163+
*/
164+
interface IBCChannelConfig {
165+
sourceChainId: string;
166+
sourceChainName: string;
167+
sourceChannelId: string;
168+
sourcePort: string;
169+
destChainId: string;
170+
destChainName: string;
171+
destChannelId: string;
172+
destPort: string;
173+
status: 'ACTIVE' | 'INACTIVE' | 'UNKNOWN';
174+
}
175+
176+
/**
177+
* Raw IBC data from chain registry (_IBC/*.json)
178+
*/
179+
interface ChainRegistryIBCData {
180+
chain_1: {
181+
chain_name: string;
182+
client_id: string;
183+
connection_id: string;
184+
};
185+
chain_2: {
186+
chain_name: string;
187+
client_id: string;
188+
connection_id: string;
189+
};
190+
channels: Array<{
191+
chain_1: {
192+
channel_id: string;
193+
port_id: string;
194+
};
195+
chain_2: {
196+
channel_id: string;
197+
port_id: string;
198+
};
199+
ordering: string;
200+
version: string;
201+
tags?: {
202+
preferred?: boolean;
203+
status?: string;
204+
};
205+
}>;
206+
}
207+
161208
/**
162209
* Fetch JSON from URL with error handling
163210
*/
@@ -224,13 +271,14 @@ function transformChain(chain: ChainRegistryChain): WalletChainConfig | null {
224271
// Extract relative paths from explorer URLs if they are absolute
225272
let explorerAccountPath = explorer?.account_page?.replace('${accountAddress}', '{address}');
226273
let explorerTxPath = explorer?.tx_page?.replace('${txHash}', '{txHash}');
227-
274+
228275
// If the paths are absolute URLs and match the base explorerUrl, extract the relative part
229276
// This standardizes configs to use relative paths for consistency
230277
// If the absolute URL has a different domain than explorerUrl, leave it as-is (the helper
231278
// functions in registry.ts will detect it's absolute and return it directly)
232279
if (explorer?.url && explorerAccountPath) {
233-
const isAbsoluteUrl = explorerAccountPath.startsWith('http://') || explorerAccountPath.startsWith('https://');
280+
const isAbsoluteUrl =
281+
explorerAccountPath.startsWith('http://') || explorerAccountPath.startsWith('https://');
234282
if (isAbsoluteUrl) {
235283
const baseUrl = explorer.url.replace(/\/$/, ''); // Remove trailing slash
236284
if (explorerAccountPath.startsWith(baseUrl)) {
@@ -239,9 +287,10 @@ function transformChain(chain: ChainRegistryChain): WalletChainConfig | null {
239287
// else: Different domain - leave as absolute URL, will be handled by helper functions
240288
}
241289
}
242-
290+
243291
if (explorer?.url && explorerTxPath) {
244-
const isAbsoluteUrl = explorerTxPath.startsWith('http://') || explorerTxPath.startsWith('https://');
292+
const isAbsoluteUrl =
293+
explorerTxPath.startsWith('http://') || explorerTxPath.startsWith('https://');
245294
if (isAbsoluteUrl) {
246295
const baseUrl = explorer.url.replace(/\/$/, '');
247296
if (explorerTxPath.startsWith(baseUrl)) {
@@ -424,6 +473,157 @@ export function getNativeAsset(chainName: string): RegistryAssetConfig | undefin
424473
`;
425474
}
426475

476+
/**
477+
* Generate TypeScript code for IBC channels
478+
*/
479+
function generateIBCCode(ibcChannels: IBCChannelConfig[]): string {
480+
const ibcJson = JSON.stringify(ibcChannels, null, 2)
481+
.replace(/"([^"]+)":/g, '$1:')
482+
.replace(/"/g, "'");
483+
484+
return `/**
485+
* Cosmos IBC Channel Registry - Auto-generated
486+
*
487+
* This file is generated by scripts/sync-chain-registry.ts
488+
* Source: https://github.com/cosmos/chain-registry/_IBC
489+
*
490+
* DO NOT EDIT MANUALLY - Run \`npm run sync:chains\` to update
491+
*
492+
* Generated: ${new Date().toISOString()}
493+
*/
494+
495+
/**
496+
* IBC Channel info for cross-chain transfers
497+
*/
498+
export interface IBCChannelConfig {
499+
sourceChainId: string;
500+
sourceChainName: string;
501+
sourceChannelId: string;
502+
sourcePort: string;
503+
destChainId: string;
504+
destChainName: string;
505+
destChannelId: string;
506+
destPort: string;
507+
status: 'ACTIVE' | 'INACTIVE' | 'UNKNOWN';
508+
}
509+
510+
/**
511+
* Pre-bundled IBC channels between enabled chains
512+
*/
513+
export const IBC_CHANNELS: IBCChannelConfig[] = ${ibcJson};
514+
515+
/**
516+
* Get IBC channels for a source chain
517+
*/
518+
export function getIBCChannelsForChain(sourceChainName: string): IBCChannelConfig[] {
519+
return IBC_CHANNELS.filter(c => c.sourceChainName === sourceChainName);
520+
}
521+
522+
/**
523+
* Get IBC channel between two chains
524+
*/
525+
export function getIBCChannel(sourceChainName: string, destChainName: string): IBCChannelConfig | undefined {
526+
return IBC_CHANNELS.find(
527+
c => c.sourceChainName === sourceChainName && c.destChainName === destChainName
528+
);
529+
}
530+
531+
/**
532+
* Get IBC channels by chain ID
533+
*/
534+
export function getIBCChannelsForChainId(sourceChainId: string): IBCChannelConfig[] {
535+
return IBC_CHANNELS.filter(c => c.sourceChainId === sourceChainId);
536+
}
537+
`;
538+
}
539+
540+
/**
541+
* Fetch IBC connections between chains
542+
*/
543+
async function fetchIBCConnections(
544+
chains: WalletChainConfig[],
545+
chainIdMap: Map<string, string>
546+
): Promise<IBCChannelConfig[]> {
547+
const ibcChannels: IBCChannelConfig[] = [];
548+
const chainNames = chains.map((c) => c.chainName);
549+
const fetchedPairs = new Set<string>();
550+
551+
console.log('\n🔗 Fetching IBC connections...\n');
552+
553+
for (let i = 0; i < chainNames.length; i++) {
554+
for (let j = i + 1; j < chainNames.length; j++) {
555+
const chain1 = chainNames[i];
556+
const chain2 = chainNames[j];
557+
558+
// IBC files are named alphabetically
559+
const names = [chain1, chain2].sort();
560+
const pairKey = `${names[0]}-${names[1]}`;
561+
562+
if (fetchedPairs.has(pairKey)) continue;
563+
fetchedPairs.add(pairKey);
564+
565+
const ibcFileName = `${pairKey}.json`;
566+
const url = `${CHAIN_REGISTRY_BASE}/_IBC/${ibcFileName}`;
567+
568+
const ibcData = await fetchJson<ChainRegistryIBCData>(url);
569+
if (!ibcData) continue;
570+
571+
// Find the preferred/active transfer channel
572+
const transferChannel =
573+
ibcData.channels.find(
574+
(ch) =>
575+
ch.chain_1.port_id === 'transfer' &&
576+
ch.chain_2.port_id === 'transfer' &&
577+
ch.tags?.status === 'live'
578+
) ||
579+
ibcData.channels.find(
580+
(ch) => ch.chain_1.port_id === 'transfer' && ch.chain_2.port_id === 'transfer'
581+
);
582+
583+
if (!transferChannel) continue;
584+
585+
// Get chain IDs from our map
586+
const chain1Id = chainIdMap.get(ibcData.chain_1.chain_name);
587+
const chain2Id = chainIdMap.get(ibcData.chain_2.chain_name);
588+
589+
if (!chain1Id || !chain2Id) continue;
590+
591+
// Add both directions
592+
ibcChannels.push({
593+
sourceChainId: chain1Id,
594+
sourceChainName: ibcData.chain_1.chain_name,
595+
sourceChannelId: transferChannel.chain_1.channel_id,
596+
sourcePort: transferChannel.chain_1.port_id,
597+
destChainId: chain2Id,
598+
destChainName: ibcData.chain_2.chain_name,
599+
destChannelId: transferChannel.chain_2.channel_id,
600+
destPort: transferChannel.chain_2.port_id,
601+
status: (transferChannel.tags?.status === 'live'
602+
? 'ACTIVE'
603+
: 'UNKNOWN') as IBCChannelConfig['status'],
604+
});
605+
606+
ibcChannels.push({
607+
sourceChainId: chain2Id,
608+
sourceChainName: ibcData.chain_2.chain_name,
609+
sourceChannelId: transferChannel.chain_2.channel_id,
610+
sourcePort: transferChannel.chain_2.port_id,
611+
destChainId: chain1Id,
612+
destChainName: ibcData.chain_1.chain_name,
613+
destChannelId: transferChannel.chain_1.channel_id,
614+
destPort: transferChannel.chain_1.port_id,
615+
status: (transferChannel.tags?.status === 'live'
616+
? 'ACTIVE'
617+
: 'UNKNOWN') as IBCChannelConfig['status'],
618+
});
619+
620+
process.stdout.write(` ${pairKey} ✅\n`);
621+
}
622+
}
623+
624+
return ibcChannels;
625+
}
626+
427627
/**
428628
* Main sync function
429629
*/
@@ -471,9 +671,18 @@ async function syncChainRegistry(chainNames: string[] = DEFAULT_CHAINS) {
471671
`\n📊 Summary: ${chains.length} chains, ${[...assetsByChain.values()].flat().length} assets\n`
472672
);
473673

674+
// Build chain name -> chain ID map for IBC fetching
675+
const chainIdMap = new Map<string, string>();
676+
chains.forEach((c) => chainIdMap.set(c.chainName, c.id));
677+
678+
// Fetch IBC connections between chains
679+
const ibcChannels = await fetchIBCConnections(chains, chainIdMap);
680+
console.log(`\n📊 IBC Summary: ${ibcChannels.length} channel directions\n`);
681+
474682
// Generate code
475683
const chainsCode = generateChainsCode(chains);
476684
const assetsCode = generateAssetsCode(assetsByChain);
685+
const ibcCode = generateIBCCode(ibcChannels);
477686

478687
// Write files
479688
const srcDir = path.join(process.cwd(), 'src/lib');
@@ -484,6 +693,9 @@ async function syncChainRegistry(chainNames: string[] = DEFAULT_CHAINS) {
484693
await fs.writeFile(path.join(srcDir, 'assets/cosmos-registry.ts'), assetsCode, 'utf-8');
485694
console.log('✅ Generated src/lib/assets/cosmos-registry.ts');
486695

696+
await fs.writeFile(path.join(srcDir, 'assets/ibc-registry.ts'), ibcCode, 'utf-8');
697+
console.log('✅ Generated src/lib/assets/ibc-registry.ts');
698+
487699
console.log('\n🎉 Chain registry sync complete!');
488700
}
489701

0 commit comments

Comments
 (0)