Menu
Home
About
Our Role
Goals
The Team
Roadmap
Tokenomics
How To Buy
Knowledge Base
Contacts
Sitemap & Links
A.I.
Chart
Shop
IMMORTALITY
š
ā¬ļø
Trade Updated
New name
B
I
U
S
link
H1
H2
H3
list
1.
x
2
x
2
page
image
code
pre
HTML
center
hr
table
note
notes
desc
▲
▼
Show page
Syntax
{html} <iframe id="dextools-widget" title="DEXTools Immortality Trading Chart" width="100%" height="700" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" style="display:block;width:100%;max-width:100%;border:0;" src="https://www.dextools.io/widget-chart/en/bnb/pe-light/0xfa56e9abcaa45207be5e43cf475ee061768ca915?theme=light&chartType=2&chartResolution=30&drawingToolbars=false"> </iframe> <p id="taxInfo"><strong>Trading information:</strong> IMT has a 10% reflection on every buy and sell. The reflection is taken from the amount transferred and redistributed to IMT holders, so you receive about 10% less than the raw pool price suggests. Slippage must be higher than 10%; 15% is the recommended setting. Keep BNB in your wallet for network fees.</p> <script src="https://cdn.jsdelivr.net/npm/ethers@5.7.2/dist/ethers.umd.min.js" defer></script> <style> #imtTrade { font-family: Arial, sans-serif; background: #fff; padding: 1.5rem 0; } #imtTrade .imt-card { background: #fff; padding: clamp(1rem, 4vw, 2rem); border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,.1); width: 100%; max-width: 100%; box-sizing: border-box; } #imtTrade h3 { color: #000; margin: 0 0 1rem; } #imtTrade label { display: block; font-weight: 600; } #imtTrade input { box-sizing: border-box; width: 100%; padding: .85rem; margin: .5rem 0 1rem; border-radius: 4px; border: 1px solid #ccc; font-size: 1rem; } #imtTrade input:focus { outline: 2px solid rgba(0,123,255,.25); border-color: #007bff; } #imtTrade input[readonly] { background: #f5f5f5; } #imtTrade small { display: block; color: #555; line-height: 1.5; margin: -.5rem 0 1rem; } #imtTrade .imt-row { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1rem; } #imtTrade button { font: inherit; font-size: 1rem; padding: .8rem; border-radius: 4px; border: 0; color: #fff; cursor: pointer; flex: 1 1 140px; } #imtTrade button:focus-visible { outline: 3px solid rgba(0,123,255,.45); outline-offset: 2px; } #imtTrade #maxImt { background: #17a2b8; } #imtTrade #maxBnb { background: #28a745; } #imtTrade #connect { background: #007bff; flex: 2 1 220px; } #imtTrade #addToken { background: #fff; color: #0056b3; border: 1px solid #007bff; flex: 1 1 160px; } #imtTrade #swap, #imtTrade #reverse { background: #007bff; width: 100%; flex: none; display: block; margin: .35rem 0; padding: .85rem; } /* Not ready (no wallet, wrong network, invalid or unaffordable amount, or busy): grey with the prohibited cursor. Active once everything checks out. */ #imtTrade button:disabled { background: #aaa !important; color: #fff !important; border-color: #aaa !important; cursor: not-allowed !important; } #imtTrade #connect.is-connected:disabled { background: #28a745 !important; cursor: default !important; } #imtTrade #status { min-height: 1.5em; white-space: pre-line; overflow-wrap: anywhere; margin: .8rem 0 .25rem; } #imtTrade #summary { min-height: 2.5em; white-space: pre-line; overflow-wrap: anywhere; margin: .25rem 0 0; color: #333; } </style> <div id="imtTrade" data-busy="0"> <div class="imt-card"> <h3>Swap IMT ā BNB</h3> <label for="amount">Amount</label> <input type="text" id="amount" inputmode="decimal" autocomplete="off" spellcheck="false" placeholder="e.g. 0.004" value="0.004"> <small id="amountHint">Buying: enter BNB to spend. Selling: enter IMT to sell. Both quotes are shown below.</small> <div class="imt-row"> <button id="maxImt" type="button" disabled>Max IMT</button> <button id="maxBnb" type="button" disabled>Max BNB</button> </div> <label for="slippage">Slippage tolerance (%)</label> <input type="number" id="slippage" inputmode="decimal" min="10.01" max="50" step="0.01" value="15"> <small id="slippageHint">Total tolerance includes the 10% IMT reflection; the remainder is price-movement headroom. Recommended: 15%. Maximum: 50%.</small> <div class="imt-row" id="walletActions"> <button id="connect" type="button">Connect Wallet</button> <button id="addToken" type="button">ļ¼ Add Immortality</button> </div> <button id="swap" type="button" disabled>Sell IMT ā BNB</button> <button id="reverse" type="button" disabled>Buy IMT ā BNB</button> <p id="status" role="status" aria-live="polite"></p> <p id="summary" aria-live="polite"></p> </div> </div> <script> (function () { 'use strict'; var ETHERS_FALLBACK_URL = 'https://cdnjs.cloudflare.com/ajax/libs/ethers/5.7.2/ethers.umd.min.js'; function onReady(fn) { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn, { once: true }); else fn(); } function loadScript(src) { return new Promise(function (resolve, reject) { var s = document.createElement('script'); s.src = src; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); } function showLibraryError() { var el = document.getElementById('status'); if (el) el.textContent = 'The trading library could not be loaded. Please refresh the page or use PancakeSwap directly (link below).'; } onReady(function () { if (typeof ethers !== 'undefined') { initializeTradingPage(); return; } // Primary CDN failed or was blocked: try a second CDN before giving up. loadScript(ETHERS_FALLBACK_URL).then(function () { if (typeof ethers !== 'undefined') initializeTradingPage(); else showLibraryError(); }).catch(showLibraryError); }); function initializeTradingPage() { var root = document.getElementById('imtTrade'); var statusEl = document.getElementById('status'); var summaryEl = document.getElementById('summary'); var taxInfoEl = document.getElementById('taxInfo'); var slippageHintEl = document.getElementById('slippageHint'); var amountInput = document.getElementById('amount'); var slippageInput = document.getElementById('slippage'); var connectBtn = document.getElementById('connect'); var addTokenBtn = document.getElementById('addToken'); var sellBtn = document.getElementById('swap'); var buyBtn = document.getElementById('reverse'); var maxImtBtn = document.getElementById('maxImt'); var maxBnbBtn = document.getElementById('maxBnb'); if (!root || !statusEl || !summaryEl || !amountInput || !slippageInput || !connectBtn || !sellBtn || !buyBtn) return; // ---------- Constants (BNB Smart Chain / PancakeSwap V2) ---------- var CHAIN_ID = 56; var CHAIN_ID_HEX = '0x38'; var BSC_RPC = 'https://bsc-dataseed.binance.org/'; var BSC_PARAMS = { chainId: CHAIN_ID_HEX, chainName: 'BNB Smart Chain', nativeCurrency: { name: 'BNB', symbol: 'BNB', decimals: 18 }, rpcUrls: [BSC_RPC], blockExplorerUrls: ['https://bscscan.com'] }; var IMT_ADDRESS = ethers.utils.getAddress('0x2bf2141ed175f3236903cf07de33d7324871802d'); var WBNB_ADDRESS = ethers.utils.getAddress('0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c'); var ROUTER_ADDRESS = ethers.utils.getAddress('0x10ed43c718714eb63d5aa57b78b54704e256024e'); var TOKEN_DECIMALS = 8; var BNB_DECIMALS = 18; // IMT reflection: 10% of every buy/sell transfer (1,000 basis points), redistributed to holders. // All page copy, expected-output maths and the minimum slippage derive from this one value. var REFLECTION_FEE_BPS = 1000; var BPS_DENOMINATOR = 10000; var MIN_SLIPPAGE_BPS = REFLECTION_FEE_BPS + 1; // must be strictly greater than the reflection var MAX_SLIPPAGE_BPS = 5000; // never allow 100% / zero minimum output var GAS_MARGIN_NUM = 125; var GAS_MARGIN_DEN = 100; var MIN_GAS_BUFFER = ethers.utils.parseEther('0.00005'); var GAS_BUFFER_PERCENT = 30; var TX_DEADLINE_SECONDS = 10 * 60; var ERC20_ABI = [ 'function approve(address spender, uint256 amount) external returns (bool)', 'function allowance(address owner, address spender) external view returns (uint256)', 'function balanceOf(address account) external view returns (uint256)', 'function decimals() external view returns (uint8)' ]; var ROUTER_ABI = [ 'function getAmountsOut(uint256 amountIn, address[] path) external view returns (uint256[] amounts)', 'function swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) external', 'function swapExactETHForTokensSupportingFeeOnTransferTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline) external payable' ]; var TOKEN_DETAILS = { address: IMT_ADDRESS, symbol: 'IMT', decimals: TOKEN_DECIMALS, image: 'https://imt.cx/imtmm.png' }; // Read-only provider: lets the page show live quotes before (or without) a wallet. var readProvider = new ethers.providers.StaticJsonRpcProvider(BSC_RPC, { chainId: CHAIN_ID, name: 'bnb' }); var readRouter = new ethers.Contract(ROUTER_ADDRESS, ROUTER_ABI, readProvider); // ---------- State ---------- var provider = null, signer = null, imt = null, router = null; var walletAddress = null; var isConnected = false; var connectionInProgress = false; var operationInProgress = false; var ready = { buy: false, sell: false }; // set only after a successful quote + balance check var activeTrade = null; // 'buy' or 'sell' while a swap is running var listenedProvider = null; var quoteRequestNumber = 0; var quoteTimer = null; var initSeq = 0; // ---------- Small helpers ---------- function setStatus(message, linkUrl, linkText) { statusEl.textContent = message || ''; if (linkUrl) { statusEl.appendChild(document.createTextNode('\n')); var a = document.createElement('a'); a.href = linkUrl; a.target = '_blank'; a.rel = 'noopener noreferrer'; a.textContent = linkText || linkUrl; statusEl.appendChild(a); } } function setSummary(message) { summaryEl.textContent = message || ''; } function shortAddress(address) { return address ? address.slice(0, 6) + 'ā¦' + address.slice(-4) : ''; } function formatPercent(bps) { return (bps / 100).toFixed(2).replace(/\.00$/, '') + '%'; } function formatUnitsCompact(value, decimals, maxFractionDigits) { var full = ethers.utils.formatUnits(value, decimals); var parts = full.split('.'); var fraction = (parts[1] || '').slice(0, maxFractionDigits).replace(/0+$/, ''); return fraction ? parts[0] + '.' + fraction : parts[0]; } function getErrorMessage(error) { if (!error) return 'Unknown error.'; // ethers v5 reports a wallet rejection as ACTION_REJECTED; raw wallets use 4001. if (error.code === 4001 || error.code === 'ACTION_REJECTED') return 'Request rejected in your wallet.'; if (Number(error.code) === -32002) return 'A wallet request is already pending. Open your wallet and complete or cancel it.'; var raw = error.reason || (error.error && error.error.message) || (error.data && error.data.message) || error.errorMessage || error.message || String(error); raw = String(raw); if (/INSUFFICIENT_OUTPUT_AMOUNT/i.test(raw)) { return 'The price moved beyond your slippage setting. Increase slippage (the 10% reflection must fit inside it) or reduce the amount.'; } if (/EXPIRED/i.test(raw)) return 'The transaction deadline passed. Please try again.'; if (error.code === 'UNPREDICTABLE_GAS_LIMIT') { return 'The swap would fail with the current settings. Try a smaller amount or a higher slippage. (' + raw.slice(0, 160) + ')'; } return raw.length > 240 ? raw.slice(0, 240) + 'ā¦' : raw; } function isMobileDevice() { return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent || ''); } function openMetaMaskDapp() { // MetaMask's deep link wants host/path unencoded. var dappPath = window.location.host + window.location.pathname + window.location.search; window.location.href = 'https://metamask.app.link/dapp/' + dappPath; } function normalizeChainId(chainId) { if (typeof chainId === 'number') return chainId; if (typeof chainId !== 'string') return NaN; return chainId.indexOf('0x') === 0 ? parseInt(chainId, 16) : parseInt(chainId, 10); } function deadlineFromNow() { return Math.floor(Date.now() / 1000) + TX_DEADLINE_SECONDS; } // ---------- Wallet provider discovery ---------- function getEth() { var e = window.ethereum; if (!e) return null; if (Array.isArray(e.providers) && e.providers.length) { var mm = e.providers.filter(function (p) { return p && p.isMetaMask && !p.isBraveWallet; })[0]; return mm || e.providers[0]; } return e; } function hasEth() { var e = getEth(); return !!(e && typeof e.request === 'function'); } // Mobile in-app wallet browsers can inject window.ethereum after the page loads. function waitForEthereum(timeoutMs) { return new Promise(function (resolve) { if (hasEth()) { resolve(getEth()); return; } var finished = false, interval, timeout; function finish() { if (finished) return; finished = true; window.removeEventListener('ethereum#initialized', finish); clearInterval(interval); clearTimeout(timeout); resolve(hasEth() ? getEth() : null); } window.addEventListener('ethereum#initialized', finish, { once: true }); interval = setInterval(function () { if (hasEth()) finish(); }, 100); timeout = setTimeout(finish, timeoutMs); }); } async function ensureBscNetwork(eth) { var current = await eth.request({ method: 'eth_chainId' }); if (normalizeChainId(current) === CHAIN_ID) return; try { await eth.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: CHAIN_ID_HEX }] }); } catch (switchError) { if (Number(switchError.code) !== 4902) throw switchError; await eth.request({ method: 'wallet_addEthereumChain', params: [BSC_PARAMS] }); await eth.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: CHAIN_ID_HEX }] }); } current = await eth.request({ method: 'eth_chainId' }); if (normalizeChainId(current) !== CHAIN_ID) { throw new Error('Your wallet is not connected to BNB Smart Chain (chain ID 56).'); } } // ---------- Gas helpers ---------- function addGasMargin(estimate) { return estimate.mul(GAS_MARGIN_NUM).add(GAS_MARGIN_DEN - 1).div(GAS_MARGIN_DEN); } function getGasBuffer(fee) { var pct = fee.mul(GAS_BUFFER_PERCENT).div(100); return pct.gt(MIN_GAS_BUFFER) ? pct : MIN_GAS_BUFFER; } async function assertGasBudget(gasLimit, address, additionalNative) { var results = await Promise.all([provider.getBalance(address), provider.getGasPrice()]); var balance = results[0], gasPrice = results[1]; var fee = gasLimit.mul(gasPrice); var required = fee.add(getGasBuffer(fee)).add(additionalNative || ethers.constants.Zero); if (balance.lt(required)) { throw new Error('Not enough BNB for the estimated network fee and safety reserve. Keep additional BNB in the wallet and reduce the swap amount.'); } } // ---------- UI state ---------- function updateUi() { var busy = connectionInProgress || operationInProgress; root.setAttribute('data-busy', busy ? '1' : '0'); connectBtn.disabled = busy || isConnected; connectBtn.className = isConnected ? 'is-connected' : ''; if (addTokenBtn) addTokenBtn.disabled = busy; // Trade and Max buttons stay grey until the wallet is connected on BSC and every check passes. sellBtn.disabled = busy || !isConnected || !ready.sell; buyBtn.disabled = busy || !isConnected || !ready.buy; maxImtBtn.disabled = busy || !isConnected; maxBnbBtn.disabled = busy || !isConnected; amountInput.readOnly = busy; slippageInput.readOnly = busy; connectBtn.textContent = isConnected ? 'ā Connected Ā· ' + shortAddress(walletAddress) : (connectionInProgress ? 'Connectingā¦' : 'Connect Wallet'); sellBtn.textContent = (operationInProgress && activeTrade === 'sell') ? 'Processingā¦' : 'Sell IMT ā BNB'; buyBtn.textContent = (operationInProgress && activeTrade === 'buy') ? 'Processingā¦' : 'Buy IMT ā BNB'; } function clearWalletState() { initSeq += 1; quoteRequestNumber += 1; if (quoteTimer) { clearTimeout(quoteTimer); quoteTimer = null; } provider = signer = imt = router = null; walletAddress = null; isConnected = false; updateUi(); scheduleQuoteRefresh(); } // ---------- Parsing ---------- function parseAmount(rawValue, decimals, label) { var value = String(rawValue == null ? '' : rawValue).trim(); if (/^\.\d+$/.test(value)) value = '0' + value; if (/^\d+\.$/.test(value)) value = value.slice(0, -1); if (!/^\d+(?:\.\d+)?$/.test(value)) { throw new Error('Enter a valid positive ' + label + ' amount. Do not use commas, signs or exponent notation.'); } var fraction = value.split('.')[1] || ''; if (fraction.length > decimals) throw new Error(label + ' supports a maximum of ' + decimals + ' decimal places.'); var parsed = ethers.utils.parseUnits(value, decimals); if (parsed.isZero()) throw new Error('The amount must be greater than zero.'); return parsed; } function parseSlippageBps() { var raw = String(slippageInput.value || '').trim(); if (!/^\d+(?:\.\d{1,2})?$/.test(raw)) { throw new Error('Enter slippage as a percentage with no more than two decimal places.'); } var bps = Math.round(Number(raw) * 100); if (bps < MIN_SLIPPAGE_BPS) { throw new Error('Slippage must be greater than the ' + formatPercent(REFLECTION_FEE_BPS) + ' IMT reflection. Use at least ' + formatPercent(MIN_SLIPPAGE_BPS) + '; 15% is recommended.'); } if (bps > MAX_SLIPPAGE_BPS) throw new Error('Slippage cannot exceed ' + formatPercent(MAX_SLIPPAGE_BPS) + '.'); return bps; } // Page copy derived from the constants above. var reflectionText = formatPercent(REFLECTION_FEE_BPS); slippageInput.min = (MIN_SLIPPAGE_BPS / 100).toFixed(2); slippageInput.max = (MAX_SLIPPAGE_BPS / 100).toFixed(2); if (taxInfoEl) { taxInfoEl.textContent = ''; var strong = document.createElement('strong'); strong.textContent = 'Trading information: '; taxInfoEl.appendChild(strong); taxInfoEl.appendChild(document.createTextNode( 'IMT has a ' + reflectionText + ' reflection on every buy and sell. The reflection is taken from the amount transferred ' + 'and redistributed to IMT holders, so you receive about ' + reflectionText + ' less than the raw pool price suggests. ' + 'Slippage must be higher than ' + reflectionText + '; 15% is the recommended setting. ' + 'Actual output also varies with pool price impact and timing. Keep BNB in your wallet for network fees.')); } if (slippageHintEl) { slippageHintEl.textContent = 'Total tolerance includes the ' + reflectionText + ' IMT reflection; the remainder is price-movement headroom. Minimum: ' + formatPercent(MIN_SLIPPAGE_BPS) + '. Recommended: 15%. Maximum: ' + formatPercent(MAX_SLIPPAGE_BPS) + '.'; } // ---------- Quotes ---------- function getDirectionDetails(dir) { if (dir === 'buy') { return { path: [WBNB_ADDRESS, IMT_ADDRESS], inputDecimals: BNB_DECIMALS, outputDecimals: TOKEN_DECIMALS, inputLabel: 'BNB', outputLabel: 'IMT' }; } return { path: [IMT_ADDRESS, WBNB_ADDRESS], inputDecimals: TOKEN_DECIMALS, outputDecimals: BNB_DECIMALS, inputLabel: 'IMT', outputLabel: 'BNB' }; } // minOut comes from the raw pool quote, so the user's tolerance is a TOTAL tolerance // that must exceed the 10% reflection. A second, reflection-adjusted quote is shown as the expected receipt. async function getTradeQuote(dir, amountIn, slippageBps) { var details = getDirectionDetails(dir); var quoter = router || readRouter; var grossAmounts = await quoter.getAmountsOut(amountIn, details.path); var grossOut = grossAmounts[1]; if (!grossOut || grossOut.isZero()) throw new Error('The pool returned a zero-output quote.'); var expectedNetOut; if (dir === 'buy') { expectedNetOut = grossOut.mul(BPS_DENOMINATOR - REFLECTION_FEE_BPS).div(BPS_DENOMINATOR); } else { var effectiveInput = amountIn.mul(BPS_DENOMINATOR - REFLECTION_FEE_BPS).div(BPS_DENOMINATOR); if (effectiveInput.isZero()) throw new Error('The amount is too small after the IMT reflection.'); var netAmounts = await quoter.getAmountsOut(effectiveInput, details.path); expectedNetOut = netAmounts[1]; } var minOut = grossOut.mul(BPS_DENOMINATOR - slippageBps).div(BPS_DENOMINATOR); if (minOut.isZero()) throw new Error('The calculated minimum output is zero. Increase the amount or reduce slippage.'); if (expectedNetOut.lt(minOut)) { throw new Error('The current quote, including the ' + formatPercent(REFLECTION_FEE_BPS) + ' IMT reflection and pool impact, is outside the selected ' + formatPercent(slippageBps) + ' total tolerance. Reduce the amount or increase slippage.'); } return { grossOut: grossOut, expectedNetOut: expectedNetOut, minOut: minOut, details: details }; } // Evaluates one direction: valid amount, live quote inside slippage, and (when connected) enough balance. async function evaluateTrade(dir, rawAmount, slippageBps, balances) { var details = getDirectionDetails(dir); var name = dir === 'buy' ? 'Buy IMT' : 'Sell IMT'; try { var amount = parseAmount(rawAmount, details.inputDecimals, details.inputLabel); var quote = await getTradeQuote(dir, amount, slippageBps); var digits = dir === 'buy' ? 4 : 8; var line = name + ': ' + formatUnitsCompact(amount, details.inputDecimals, 8) + ' ' + details.inputLabel + ' ā about ' + formatUnitsCompact(quote.expectedNetOut, details.outputDecimals, digits) + ' ' + details.outputLabel + ' after the ' + formatPercent(REFLECTION_FEE_BPS) + ' reflection (minimum ' + formatUnitsCompact(quote.minOut, details.outputDecimals, digits) + ').'; if (balances) { if (dir === 'buy' && balances.bnb.lt(amount.add(MIN_GAS_BUFFER))) { return { ok: false, text: name + ': not enough BNB in your wallet (keep some for network fees).' }; } if (dir === 'sell') { if (balances.imt.lt(amount)) return { ok: false, text: name + ': not enough IMT in your wallet.' }; if (balances.bnb.lt(MIN_GAS_BUFFER)) return { ok: false, text: name + ': you need a little BNB for network fees.' }; } } return { ok: true, text: line }; } catch (error) { return { ok: false, text: name + ': ' + getErrorMessage(error) }; } } async function refreshQuote() { var requestNumber = ++quoteRequestNumber; ready.buy = false; ready.sell = false; updateUi(); var raw = amountInput.value.trim(); if (!raw) { setSummary('Enter an amount to see live quotes.'); return; } var slippageBps; try { slippageBps = parseSlippageBps(); } catch (error) { setSummary(getErrorMessage(error)); return; } var balances = null; if (isConnected && provider && imt && walletAddress) { try { var b = await Promise.all([provider.getBalance(walletAddress), imt.balanceOf(walletAddress)]); balances = { bnb: b[0], imt: b[1] }; } catch (error) { balances = null; } if (requestNumber !== quoteRequestNumber) return; } var results = await Promise.all([ evaluateTrade('sell', raw, slippageBps, balances), evaluateTrade('buy', raw, slippageBps, balances) ]); if (requestNumber !== quoteRequestNumber) return; var sell = results[0], buy = results[1]; ready.sell = sell.ok && !!balances; ready.buy = buy.ok && !!balances; setSummary( buy.text + '\n' + sell.text + (isConnected ? '' : '\nConnect your wallet to enable trading.') ); updateUi(); } function scheduleQuoteRefresh() { quoteRequestNumber += 1; // invalidate any in-flight quote immediately if (quoteTimer) clearTimeout(quoteTimer); quoteTimer = setTimeout(refreshQuote, 250); } // ---------- Wallet connection ---------- async function ensureActiveWallet() { var eth = getEth(); if (!eth || !isConnected || !provider || !signer || !walletAddress) { throw new Error('Connect your wallet before swapping.'); } var results = await Promise.all([eth.request({ method: 'eth_chainId' }), eth.request({ method: 'eth_accounts' })]); var chainId = results[0], accounts = results[1]; if (normalizeChainId(chainId) !== CHAIN_ID) { clearWalletState(); throw new Error('Your wallet has changed network. Switch back to BNB Smart Chain and reconnect.'); } if (!accounts || !accounts.length || accounts[0].toLowerCase() !== walletAddress.toLowerCase()) { clearWalletState(); throw new Error('Your active wallet account changed. Reconnect before continuing.'); } var network = await provider.getNetwork(); if (network.chainId !== CHAIN_ID) { clearWalletState(); throw new Error('The wallet provider is not on BNB Smart Chain. Reconnect and try again.'); } return walletAddress; } async function initializeWallet(account) { var eth = getEth(); var seq = ++initSeq; var chainId = await eth.request({ method: 'eth_chainId' }); if (normalizeChainId(chainId) !== CHAIN_ID) throw new Error('Please switch your wallet to BNB Smart Chain (chain ID 56).'); var nextProvider = new ethers.providers.Web3Provider(eth, 'any'); var network = await nextProvider.getNetwork(); if (network.chainId !== CHAIN_ID) throw new Error('Could not verify BNB Smart Chain.'); var nextSigner = nextProvider.getSigner(account); var signerAddress = await nextSigner.getAddress(); if (signerAddress.toLowerCase() !== account.toLowerCase()) throw new Error('The wallet returned a different account. Please reconnect.'); var nextImt = new ethers.Contract(IMT_ADDRESS, ERC20_ABI, nextSigner); var nextRouter = new ethers.Contract(ROUTER_ADDRESS, ROUTER_ABI, nextSigner); var onChainDecimals = Number(await nextImt.decimals()); if (onChainDecimals !== TOKEN_DECIMALS) throw new Error('Unexpected IMT decimals. Trading has been disabled to prevent an incorrect amount.'); if (seq !== initSeq) return; // a newer connect/disconnect superseded this one provider = nextProvider; signer = nextSigner; imt = nextImt; router = nextRouter; walletAddress = signerAddress; isConnected = true; updateUi(); setStatus('Connected to BNB Smart Chain: ' + shortAddress(walletAddress)); scheduleQuoteRefresh(); } async function connectWallet() { if (connectionInProgress || operationInProgress) return false; connectionInProgress = true; updateUi(); try { setStatus('Looking for a walletā¦'); var eth = await waitForEthereum(1500); if (!eth) { if (isMobileDevice()) { openMetaMaskDapp(); } else setStatus('No compatible wallet detected. Install MetaMask or use the PancakeSwap link below.'); return false; } attachWalletListeners(eth); setStatus('Connecting walletā¦'); var accounts = await eth.request({ method: 'eth_requestAccounts' }); if (!accounts || !accounts.length) throw new Error('No wallet account was made available.'); await ensureBscNetwork(eth); accounts = await eth.request({ method: 'eth_accounts' }); if (!accounts || !accounts.length) throw new Error('Wallet account access was not granted.'); await initializeWallet(accounts[0]); return isConnected; } catch (error) { clearWalletState(); setStatus('Wallet connection failed: ' + getErrorMessage(error)); return false; } finally { connectionInProgress = false; updateUi(); } } async function addTokenToWallet() { if (connectionInProgress || operationInProgress) return; connectionInProgress = true; updateUi(); try { var eth = await waitForEthereum(1500); if (!eth) { if (isMobileDevice()) openMetaMaskDapp(); else setStatus('No compatible wallet detected.'); return; } attachWalletListeners(eth); await ensureBscNetwork(eth); var added = await eth.request({ method: 'wallet_watchAsset', params: { type: 'ERC20', options: TOKEN_DETAILS } }); setStatus(added ? 'IMT was added to your wallet.' : 'The wallet did not add IMT.'); } catch (error) { setStatus('Could not add IMT: ' + getErrorMessage(error)); } finally { connectionInProgress = false; updateUi(); } } async function handleAccountsChanged(accounts) { if (connectionInProgress) return; if (operationInProgress) { clearWalletState(); setStatus('Wallet account changed during the operation. Reconnect before another swap.'); return; } if (!accounts || !accounts.length) { clearWalletState(); setStatus('Wallet disconnected.'); return; } try { var chainId = await getEth().request({ method: 'eth_chainId' }); if (normalizeChainId(chainId) !== CHAIN_ID) { clearWalletState(); setStatus('Account changed. Switch to BNB Smart Chain and reconnect.'); return; } await initializeWallet(accounts[0]); } catch (error) { clearWalletState(); setStatus('Could not refresh the wallet account: ' + getErrorMessage(error)); } } async function handleChainChanged(chainId) { if (connectionInProgress) return; var wasBusy = operationInProgress; clearWalletState(); if (wasBusy) { setStatus('Wallet network changed during the operation. Reconnect to BNB Smart Chain before trading again.'); return; } if (normalizeChainId(chainId) !== CHAIN_ID) { setStatus('Wrong network. Press Connect Wallet to switch to BNB Smart Chain (chain ID 56).'); return; } try { var accounts = await getEth().request({ method: 'eth_accounts' }); if (accounts && accounts.length) await initializeWallet(accounts[0]); else setStatus('BNB Smart Chain selected. Connect your wallet to trade.'); } catch (error) { setStatus('Could not refresh the network connection: ' + getErrorMessage(error)); } } // Attached lazily: wallets that inject late no longer miss their account/network events. function attachWalletListeners(eth) { if (!eth || typeof eth.on !== 'function' || listenedProvider === eth) return; listenedProvider = eth; eth.on('accountsChanged', handleAccountsChanged); eth.on('chainChanged', handleChainChanged); eth.on('disconnect', function () { clearWalletState(); setStatus('Wallet disconnected.'); }); } async function detectExistingConnection() { var eth = await waitForEthereum(1500); if (!eth) return; attachWalletListeners(eth); try { var results = await Promise.all([eth.request({ method: 'eth_accounts' }), eth.request({ method: 'eth_chainId' })]); var accounts = results[0], chainId = results[1]; if (accounts && accounts.length && normalizeChainId(chainId) === CHAIN_ID) { await initializeWallet(accounts[0]); } else if (accounts && accounts.length) { setStatus('Wallet detected on another network. Press Connect Wallet to switch to BNB Smart Chain.'); } } catch (error) { /* stay disconnected; the user can press Connect Wallet */ } } // ---------- Max buttons ---------- async function estimateBuyBudget(amount, address, slippageBps, gasPrice) { var quote = await getTradeQuote('buy', amount, slippageBps); var gasLimit = await estimateBuyGasLimit(amount, quote.minOut, address, deadlineFromNow()); var fee = gasLimit.mul(gasPrice); return fee.add(getGasBuffer(fee)); // total BNB to keep back } async function estimateBuyGasLimit(amountIn, minOut, recipient, deadline) { var estimate = await router.estimateGas.swapExactETHForTokensSupportingFeeOnTransferTokens( minOut, [WBNB_ADDRESS, IMT_ADDRESS], recipient, deadline, { value: amountIn }); return addGasMargin(estimate); } async function estimateSellGasLimit(amountIn, minOut, recipient, deadline) { var estimate = await router.estimateGas.swapExactTokensForETHSupportingFeeOnTransferTokens( amountIn, minOut, [IMT_ADDRESS, WBNB_ADDRESS], recipient, deadline); return addGasMargin(estimate); } // Gas use barely depends on the amount, so two estimates are enough and the result is deterministic // (the earlier loop waited for two estimates to match exactly and could fail to settle). async function calculateMaxBnb() { var slippageBps = parseSlippageBps(); var address = await ensureActiveWallet(); var balance = await provider.getBalance(address); if (balance.isZero()) throw new Error('No BNB balance is available.'); var gasPrice = await provider.getGasPrice(); var firstReserve = await estimateBuyBudget(balance.div(2), address, slippageBps, gasPrice); var candidate = balance.sub(firstReserve); if (candidate.lte(0)) throw new Error('Not enough BNB remains after reserving estimated gas and the safety buffer.'); var secondReserve = await estimateBuyBudget(candidate, address, slippageBps, gasPrice); if (secondReserve.gt(firstReserve)) { candidate = balance.sub(secondReserve); if (candidate.lte(0)) throw new Error('Not enough BNB remains after reserving estimated gas and the safety buffer.'); } return candidate; } async function handleMaxImt() { if (operationInProgress || connectionInProgress || !isConnected) return; operationInProgress = true; updateUi(); try { var address = await ensureActiveWallet(); var balance = await imt.balanceOf(address); if (balance.isZero()) throw new Error('Your wallet has no IMT balance.'); amountInput.value = ethers.utils.formatUnits(balance, TOKEN_DECIMALS); setStatus('Maximum IMT balance loaded. The ' + reflectionText + ' reflection is accounted for in the quote.'); } catch (error) { setStatus('Max IMT failed: ' + getErrorMessage(error)); } finally { operationInProgress = false; updateUi(); scheduleQuoteRefresh(); } } async function handleMaxBnb() { if (operationInProgress || connectionInProgress || !isConnected) return; operationInProgress = true; updateUi(); setStatus('Estimating the maximum BNB amount while keeping a gas reserveā¦'); try { var maximum = await calculateMaxBnb(); amountInput.value = ethers.utils.formatEther(maximum); setStatus('Maximum BNB amount set, with an estimated gas fee and safety reserve kept back.'); } catch (error) { setStatus('Max BNB failed: ' + getErrorMessage(error)); } finally { operationInProgress = false; updateUi(); scheduleQuoteRefresh(); } } // ---------- Swap ---------- async function executeSwap(dir) { if (operationInProgress || connectionInProgress || !isConnected) return; activeTrade = dir; operationInProgress = true; updateUi(); setStatus(dir === 'sell' ? 'Preparing IMT ā BNB swapā¦' : 'Preparing BNB ā IMT swapā¦'); try { var address = await ensureActiveWallet(); var details = getDirectionDetails(dir); var amountIn = parseAmount(amountInput.value, details.inputDecimals, details.inputLabel); var slippageBps = parseSlippageBps(); var quote, gasLimit, tx; if (dir === 'sell') { var tokenBalance = await imt.balanceOf(address); if (tokenBalance.lt(amountIn)) throw new Error('Insufficient IMT balance for this sale.'); var allowance = await imt.allowance(address, ROUTER_ADDRESS); if (allowance.lt(amountIn)) { var approvalGasLimit = addGasMargin(await imt.estimateGas.approve(ROUTER_ADDRESS, amountIn)); await assertGasBudget(approvalGasLimit, address); setStatus('Approval required. Confirm the IMT approval in your walletā¦'); var approvalTx = await imt.approve(ROUTER_ADDRESS, amountIn, { gasLimit: approvalGasLimit }); setStatus('IMT approval submitted. Waiting for confirmationā¦', 'https://bscscan.com/tx/' + approvalTx.hash, 'View approval on BscScan'); await approvalTx.wait(); await ensureActiveWallet(); allowance = await imt.allowance(address, ROUTER_ADDRESS); if (allowance.lt(amountIn)) throw new Error('IMT approval was not recorded. No swap was submitted.'); } quote = await getTradeQuote('sell', amountIn, slippageBps); // fresh quote right before the swap gasLimit = await estimateSellGasLimit(amountIn, quote.minOut, address, deadlineFromNow()); await assertGasBudget(gasLimit, address); await ensureActiveWallet(); setStatus('Confirm the IMT ā BNB swap in your walletā¦'); tx = await router.swapExactTokensForETHSupportingFeeOnTransferTokens( amountIn, quote.minOut, [IMT_ADDRESS, WBNB_ADDRESS], address, deadlineFromNow(), { gasLimit: gasLimit }); } else { var bnbBalance = await provider.getBalance(address); if (bnbBalance.lt(amountIn)) throw new Error('Insufficient BNB balance.'); quote = await getTradeQuote('buy', amountIn, slippageBps); gasLimit = await estimateBuyGasLimit(amountIn, quote.minOut, address, deadlineFromNow()); await assertGasBudget(gasLimit, address, amountIn); await ensureActiveWallet(); setStatus('Confirm the BNB ā IMT swap in your walletā¦'); tx = await router.swapExactETHForTokensSupportingFeeOnTransferTokens( quote.minOut, [WBNB_ADDRESS, IMT_ADDRESS], address, deadlineFromNow(), { value: amountIn, gasLimit: gasLimit }); } var txUrl = 'https://bscscan.com/tx/' + tx.hash; setStatus('Swap submitted. Waiting for BNB Chain confirmationā¦', txUrl, 'View on BscScan'); var receipt = await tx.wait(); if (!receipt || receipt.status !== 1) throw new Error('The transaction did not confirm successfully.'); setStatus((dir === 'sell' ? 'IMT ā BNB swap confirmed.' : 'BNB ā IMT swap confirmed.'), txUrl, 'View on BscScan'); } catch (error) { console.error('IMT swap error:', error); setStatus('Swap failed: ' + getErrorMessage(error)); } finally { operationInProgress = false; activeTrade = null; updateUi(); scheduleQuoteRefresh(); } } // ---------- Wiring ---------- connectBtn.addEventListener('click', connectWallet); if (addTokenBtn) addTokenBtn.addEventListener('click', addTokenToWallet); sellBtn.addEventListener('click', function () { executeSwap('sell'); }); buyBtn.addEventListener('click', function () { executeSwap('buy'); }); maxImtBtn.addEventListener('click', handleMaxImt); maxBnbBtn.addEventListener('click', handleMaxBnb); amountInput.addEventListener('input', scheduleQuoteRefresh); slippageInput.addEventListener('input', scheduleQuoteRefresh); updateUi(); scheduleQuoteRefresh(); detectExistingConnection(); } })(); </script> <!-- Independent PancakeSwap V2 fallback. --> <hr style="border:none;height:2px;width:60%;margin:16px auto;background:linear-gradient(to right,transparent,#ffb300,#ff6b00,#ffb300,transparent);border-radius:2px;"> <p style="text-align:center;">The official Immortality liquidity pool is PancakeSwap V2. You can also trade IMT directly on <a href="https://pancakeswap.finance/swap?inputCurrency=BNB&outputCurrency=0x2bf2141ed175f3236903cf07de33d7324871802d" target="_blank" rel="noopener noreferrer">PancakeSwap</a>.</p> <p style="text-align:center;"> <a href="https://pancakeswap.finance/swap?inputCurrency=BNB&outputCurrency=0x2bf2141ed175f3236903cf07de33d7324871802d" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:10px;border-radius:12px;"> <img src="/images/pancake-logo-text.png" alt="PancakeSwap" loading="lazy" style="display:block;width:230px;max-width:100%;height:auto;border-radius:12px;"> </a> </p> <p><strong>Using PancakeSwap directly:</strong></p> <ul> <li>You need BNB both for the swap and for network fees.</li> <li>Set slippage above the 10% IMT reflection; 15% is a reasonable starting point.</li> <li>Large trades may need higher slippage because of pool price impact. Higher slippage also increases the price movement you are willing to accept.</li> <li>Confirm that the wallet is using BNB Smart Chain and that the output token address is <code>0x2bf2141ed175f3236903cf07de33d7324871802d</code>.</li> </ul> <p>ā”ļø <a href="https://imtcoin.com/guide.html">Guide to setting up for crypto</a></p> {/html}
Password
Summary of changes
š
ā±ļø
ā¬ļø