Files
2026-03-02 12:14:14 -08:00

105 lines
3.5 KiB
JavaScript

/**
* Wallet helpers for MetaMask and EIP-1193 providers.
* Add chains (138 + Mainnet), switch chain, add tokens.
*/
import { CHAINS, getChainByHex, getChainById } from './chains.js'
import { getTokensByChain, getToken } from './tokens.js'
/**
* Add both Chain 138 and Ethereum Mainnet to the wallet (if not already added).
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum or other EIP-1193 provider
* @param {{ chains?: number[] }} options - optional: { chains: [138, 1] } to add only specific chains
* @returns {Promise<{ added: number[], skipped: number[], errors: { chainId: number, error: Error }[] }>}
*/
export async function addChainsToWallet(ethereum, options = {}) {
const chainIds = options.chains ?? [138, 1]
const toAdd = CHAINS.filter((c) => chainIds.includes(c.chainIdDecimal))
const result = { added: [], skipped: [], errors: [] }
for (const chain of toAdd) {
try {
await ethereum.request({
method: 'wallet_addEthereumChain',
params: [{ ...chain }],
})
result.added.push(chain.chainIdDecimal)
} catch (err) {
if (err.code === 4902) {
result.added.push(chain.chainIdDecimal)
} else if (err.code === -32603 && /already exists/i.test(String(err.message))) {
result.skipped.push(chain.chainIdDecimal)
} else {
result.errors.push({ chainId: chain.chainIdDecimal, error: err })
}
}
}
return result
}
/**
* Switch the wallet to the given chain. If chain is not added, adds it first (wallet_addEthereumChain).
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
* @param {number} chainIdDecimal - 138 or 1
* @returns {Promise<void>}
*/
export async function switchChain(ethereum, chainIdDecimal) {
const chain = getChainById(chainIdDecimal)
if (!chain) throw new Error(`Unknown chain: ${chainIdDecimal}`)
try {
await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chain.chainId }],
})
} catch (err) {
if (err.code === 4902) {
await ethereum.request({
method: 'wallet_addEthereumChain',
params: [chain],
})
} else {
throw err
}
}
}
/**
* Add a token to the wallet (wallet_watchAsset).
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
* @param {{ chainId: number, address: string, symbol: string, decimals: number, name?: string }} token - token info (use getToken(chainId, address) for known tokens)
* @returns {Promise<boolean>} - true if user approved
*/
export async function addTokenToWallet(ethereum, token) {
const result = await ethereum.request({
method: 'wallet_watchAsset',
params: {
type: 'ERC20',
options: {
address: token.address,
symbol: token.symbol,
decimals: token.decimals,
...(token.name && { name: token.name }),
},
},
})
return !!result
}
/**
* Ensure the wallet is on the given chain; if not, switch (and add if needed).
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
* @param {number} chainIdDecimal - 138 or 1
* @returns {Promise<boolean>} - true if already on chain or switched successfully
*/
export async function ensureChain(ethereum, chainIdDecimal) {
const hex = await ethereum.request({ method: 'eth_chainId', params: [] })
const current = parseInt(hex, 16)
if (current === chainIdDecimal) return true
await switchChain(ethereum, chainIdDecimal)
return true
}
export { getTokensByChain, getToken }