x0-react-sdk
Version:
React SDK for X0Pay Hyperlane token bridging with MetaMask and Safe wallet integration
194 lines (168 loc) • 6.89 kB
text/typescript
// Internal configuration for X0Pay SDK
import { ethers } from 'ethers';
import type { ChainConfig, TokenConfig, ChainTokenConfig } from './types';
export const CHAINS: ChainConfig[] = [
{
chainId: '0x89', // 137 in hex
name: 'Polygon',
rpcUrls: ['https://polygon-rpc.com/'],
nativeCurrency: { name: 'Matic', symbol: 'MATIC', decimals: 18 },
blockExplorerUrls: ['https://polygonscan.com/'],
},
{
chainId: '0xa86a', // 43114 in hex
name: 'Avalanche',
rpcUrls: ['https://ava-mainnet.public.blastapi.io/ext/bc/C/rpc'],
nativeCurrency: { name: 'Avalanche', symbol: 'AVAX', decimals: 18 },
blockExplorerUrls: ['https://snowtrace.io/'],
},
];
// Token configurations for each chain
export const CHAIN_TOKEN_CONFIGS: ChainTokenConfig[] = [
{
chainId: '0x89', // Polygon
chainName: 'Polygon',
tokens: [
{
symbol: 'USDT',
name: 'Tether USD',
address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // Polygon USDT
decimals: 6,
hypColAddress: '0x6316043b1EE191538A3903d95723506229B1e985', // Polygon USDT Hyperlane collateral
},
{
symbol: 'USDC',
name: 'USDC',
address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // Polygon USDC
decimals: 6,
hypColAddress: '0xD1eB0Be5890bEBDE62E249162738C6cA53a4A240', // Polygon USDC Hyperlane collateral
},
],
hookAddress: '0x9B31d0eb6D852939Fe5A2BB8a66F3b5E6679A3a5', // Replace with actual address
destinationDomain: 170845,
},
// Add Avalanche token configs
{
chainId: '0xa86a', // Avalanche
chainName: 'Avalanche',
tokens: [
{
symbol: 'USDT',
name: 'Tether USD',
address: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', // Avalanche USDT
decimals: 6,
hypColAddress: '0x9532B3ba9F7794A39495F3b37cC94309F8b97C25', // Avalanche USDT Hyperlane collateral
},
],
hookAddress: '0xf5Ceb644825D3b98093090A88deeF0Ab1c8Ac04E', // Replace with actual address
destinationDomain: 170845,
},
];
// Configuration manager class
export class X0ConfigManager {
private clientChains: ChainConfig[] = [];
private clientChainTokenConfigs: ChainTokenConfig[] = [];
constructor(clientChains?: ChainConfig[], clientChainTokenConfigs?: ChainTokenConfig[]) {
this.clientChains = clientChains || [];
this.clientChainTokenConfigs = clientChainTokenConfigs || [];
}
// Get merged chains (client configs override defaults)
getChains(): ChainConfig[] {
const mergedChains = [...CHAINS];
this.clientChains.forEach(clientChain => {
const existingIndex = mergedChains.findIndex(chain => chain.chainId === clientChain.chainId);
if (existingIndex >= 0) {
mergedChains[existingIndex] = clientChain;
} else {
mergedChains.push(clientChain);
}
});
return mergedChains;
}
// Get merged chain token configs (client configs override defaults)
getChainTokenConfigs(): ChainTokenConfig[] {
const mergedConfigs = [...CHAIN_TOKEN_CONFIGS];
this.clientChainTokenConfigs.forEach(clientConfig => {
const existingIndex = mergedConfigs.findIndex(config => config.chainId === clientConfig.chainId);
if (existingIndex >= 0) {
mergedConfigs[existingIndex] = clientConfig;
} else {
mergedConfigs.push(clientConfig);
}
});
return mergedConfigs;
}
// Get chain token config by chain ID
getChainTokenConfig(chainId: string): ChainTokenConfig | undefined {
const configs = this.getChainTokenConfigs();
return configs.find(config => config.chainId === chainId);
}
// Get token config by chain ID and symbol
getTokenConfig(chainId: string, symbol: string): TokenConfig | undefined {
const chainConfig = this.getChainTokenConfig(chainId);
return chainConfig?.tokens.find(token => token.symbol === symbol);
}
// Get default config for a chain and token (without recipient)
getDefaultConfig(chainId: string, tokenSymbol: string) {
const chainConfig = this.getChainTokenConfig(chainId);
const tokenConfig = this.getTokenConfig(chainId, tokenSymbol);
if (!chainConfig || !tokenConfig) {
throw new Error(`Configuration not found for chain ${chainId} and token ${tokenSymbol}`);
}
// Use token-specific destination domain if provided, otherwise fall back to chain's destination domain
const destinationDomain = tokenConfig.destinationDomain ?? chainConfig.destinationDomain;
return {
erc20TokenAddress: tokenConfig.address,
hypColAddress: tokenConfig.hypColAddress,
hookAddress: chainConfig.hookAddress,
destinationDomain: destinationDomain,
};
}
// Get recipient from hook contract
async getRecipientFromHook(provider: any, hookAddress: string): Promise<string> {
try {
// ABI for the vaultAddress function
const hookABI = [
"function vaultAddress() external view returns (address)"
];
const hookContract = new ethers.Contract(hookAddress, hookABI, provider);
const recipient = await hookContract.vaultAddress();
// Convert address to bytes32 format for Hyperlane message
return ethers.utils.hexZeroPad(recipient, 32);
} catch (error) {
throw new Error(`Failed to get recipient from hook contract: ${error}`);
}
}
// Get complete config with recipient from hook contract
async getCompleteConfig(provider: any, chainId: string, tokenSymbol: string) {
const baseConfig = this.getDefaultConfig(chainId, tokenSymbol);
const recipient = await this.getRecipientFromHook(provider, baseConfig.hookAddress);
return {
...baseConfig,
recipient,
};
}
// Update client configurations
updateClientConfigs(clientChains?: ChainConfig[], clientChainTokenConfigs?: ChainTokenConfig[]) {
if (clientChains) {
this.clientChains = clientChains;
}
if (clientChainTokenConfigs) {
this.clientChainTokenConfigs = clientChainTokenConfigs;
}
}
}
// Global config manager instance
export const globalConfigManager = new X0ConfigManager();
// Helper function to get chain token config by chain ID (backward compatibility)
export const getChainTokenConfig = (chainId: string): ChainTokenConfig | undefined => {
return globalConfigManager.getChainTokenConfig(chainId);
};
// Helper function to get token config by chain ID and symbol (backward compatibility)
export const getTokenConfig = (chainId: string, symbol: string): TokenConfig | undefined => {
return globalConfigManager.getTokenConfig(chainId, symbol);
};
// Helper function to get default config for a chain and token (backward compatibility)
export const getDefaultConfig = (chainId: string, tokenSymbol: string) => {
return globalConfigManager.getDefaultConfig(chainId, tokenSymbol);
};