Обмен криптовалют логика

Создано: 28 января 2025 г.

Отвечено с использованием O1 от Chat01

Вопрос

Добавить логики обмена один TON=100PAS одинTON=5USDT

<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <link rel="stylesheet" href="styles.css"> <link rel="icon" href="images/icon.png" type="image/png" /> <title>Обмен криптовалют</title> <script src="https://unpkg.com/@tonconnect/[email protected]/dist/tonconnect-ui.min.js"></script> <script src="scripts/main.js" defer></script> </head> <body> <div id="connect-button-root"></div> <script> // Инициализируем TonConnect UI const tonConnectUI = new TON_CONNECT_UI.TonConnectUI({ manifestUrl: 'https://ton-connect.github.io/demo-dapp-with-react-ui/tonconnect-manifest.json', buttonRootId: 'connect-button-root' });
text
// Отслеживаем изменение статуса (подключения кошелька) tonConnectUI.onStatusChange((walletInfo) => { console.log("TonConnect status:", walletInfo); const addressElement = document.getElementById('userAddress'); if (walletInfo?.account?.address) { addressElement.innerText = "Ваш адрес: " + walletInfo.account.address; } else { addressElement.innerText = "Кошелёк не подключён"; } }); </script> <div class="container"> <h1>Обмен криптовалют</h1> <div class="exchange-form"> <input type="text" id="amount" placeholder="Сумма для обмена" /> <select id="currencyFrom"> <option value="TON">TON</option> <option value="USDT">USDT</option> <option value="PAS">PAS</option> </select> <select id="currencyTo"> <option value="TON">TON</option> <option value="USDT">USDT</option> <option value="PAS">PAS</option> </select> <button onclick="exchange()">Обменять</button> </div> </div> <div id="markets-info"></div> <div id="userAddress">Кошелёк не подключён</div> <div id="exchangeResult"></div> <script> async function exchange() { const amount = document.getElementById('amount').value; const currencyFrom = document.getElementById('currencyFrom').value; const currencyTo = document.getElementById('currencyTo').value; // Проверка суммы if (!amount || isNaN(amount) || amount <= 0) { alert('Пожалуйста, введите корректную сумму для обмена.'); return; } // Проверка подключения кошелька const walletInfo = tonConnectUI.wallet; if (!walletInfo?.account?.address) { alert('Кошелёк не подключён. Пожалуйста, подключите кошелёк.'); return; } // Проверка на обмен валюты на саму себя if (currencyFrom === currencyTo) { alert('Вы пытаетесь обменять валюту на саму себя. Пожалуйста, выберите другую валюту.'); return; } // Адреса кошельков для обмена const exchangeWallets = { TON: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw", PAS: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw", USDT: "UQDF5gjmaMrHjquTr75vnDj35VBbv4S8nuB8IoiGfAnZNsAy" }; // Комиссия за сделку (например, 1% от суммы) const commissionRate = 0.01; const commission = amount * commissionRate; const amountAfterCommission = amount - commission; // Формируем транзакцию const transaction = { validUntil: Math.floor(Date.now() / 1000) + 600, // 10 минут messages: [ { address: exchangeWallets[currencyTo], // Адрес кошелька для получения amount: amountAfterCommission * 1e9, // Сумма в наноТонах (для TON) payload: 'te6cckEBAQEAAgAAAEysucg=' // Полезная нагрузка (уточните у разработчика контракта) }, { address: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw", // Адрес кошелька для комиссии amount: commission * 1e9, // Сумма комиссии в наноТонах payload: 'te6cckEBAQEAAgAAAEysucg=' // Полезная нагрузка (уточните у разработчика контракта) } ] }; try { // Отправляем транзакцию через TonConnect const result = await tonConnectUI.sendTransaction(transaction); console.log("Транзакция успешно отправлена:", result); // Обновляем интерфейс document.getElementById('exchangeResult').innerText = `Обмен успешно выполнен! Сумма: ${amountAfterCommission} ${currencyTo}, комиссия: ${commission} ${currencyFrom}`; } catch (error) { console.error("Ошибка при отправке транзакции:", error); document.getElementById('exchangeResult').innerText = `Ошибка при обмене: ${error.message}`; } } </script> <script> // Логика обмена (пример для TON -> USDT) if (currencyFrom === 'TON' && currencyTo === 'USDT') { const transaction = { validUntil: Math.floor(Date.now() / 1000) + 600, // 10 минут messages: [ { address: 'UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw', // Адрес контракта для обмена amount: amount * 1e9, // Комиссия 0.01 TON в наноТонах } ] }; async function exchange() { const amount = document.getElementById('amount').value; const currencyFrom = document.getElementById('currencyFrom').value; const currencyTo = document.getElementById('currencyTo').value; // Проверка суммы if (!amount || isNaN(amount) || amount <= 0) { alert('Пожалуйста, введите корректную сумму для обмена.'); return; } // Проверка подключения кошелька const walletInfo = tonConnectUI.account; if (!walletInfo?.address) { alert('Кошелёк не подключён. Пожалуйста, подключите кошелёк.'); return; } // Логика обмена let transaction; try { if (currencyFrom === 'TON' && currencyTo === 'USDT') { transaction = { validUntil: Math.floor(Date.now() / 1000) + 600, // 10 минут messages: [ { address: TOKEN_ADDRESSES.USDT, amount: (amount * 1e9).toString(), // Комиссия 0.01 TON в наноТонах payload: begin_cell() .store_uint(0x18, 6) .store_slice(walletInfo.address) .store_coins(0) .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) .store_uint(op::transfer_notification(), 32) .store_uint(0, 64) .end_cell() } ] }; } else if (currencyFrom === 'USDT' && currencyTo === 'TON') { transaction = { validUntil: Math.floor(Date.now() / 1000) + 600, messages: [ { address: TOKEN_ADDRESSES.TON, amount: (amount * 1e6).toString(), // Комиссия 0.01 TON в наноТонах payload: begin_cell() .store_uint(0x18, 6) .store_slice(walletInfo.address) .store_coins(0) .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) .store_uint(op::transfer_notification(), 32) .store_uint(0, 64) .end_cell() } ] }; } // Отправка транзакции через TonConnect const result = await tonConnectUI.sendTransaction(transaction); console.log("Транзакция успешно отправлена:", result); alert("Транзакция успешно отправлена!"); } catch (error) { console.error("Ошибка при отправке транзакции:", error); alert("Ошибка при отправке транзакции. Пожалуйста, попробуйте снова."); }

}
</script>

<!-- Code injected by live-server --> <script> // <![CDATA[ <-- For SVG support if ('WebSocket' in window) { (function () { function refreshCSS() { var sheets = [].slice.call(document.getElementsByTagName("link")); var head = document.getElementsByTagName("head")[0]; for (var i = 0; i < sheets.length; ++i) { var elem = sheets[i]; var parent = elem.parentElement || head; parent.removeChild(elem); var rel = elem.rel; if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") { var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, ''); elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf()); } parent.appendChild(elem); } } var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://'; var address = protocol + window.location.host + window.location.pathname + '/ws'; var socket = new WebSocket(address); socket.onmessage = function (msg) { if (msg.data == 'reload') window.location.reload(); else if (msg.data == 'refreshcss') refreshCSS(); }; if (sessionStorage && !sessionStorage.getItem('IsThisFirstTime_Log_From_LiveServer')) { console.log('Live reload enabled.'); sessionStorage.setItem('IsThisFirstTime_Log_From_LiveServer', true); } })(); } else { console.error('Upgrade your browser. This Browser is NOT supported WebSocket for Live-Reloading.'); } // ]]> </script> </body> </html>

Мысль

Shifting focus

The user plans to integrate 1 TON = 100 PAS and 1 TON = 5 USDT into their web exchange, enabling TON to PAS and PAS to TON transactions.

Mapping the exchange routes

I’m detailing the conversion between PAS and USDT through TON, detailing exchange rates and conversion steps, including commissions. This ensures precise pricing based on defined ratios.

Evaluating options

I'm considering whether to integrate the new conversion logic into the existing code or to keep things simpler by adding a direct computation for results.

Mapping out conversion paths

I'm piecing together a function to handle currency conversions, ensuring it accurately tracks TON, PAS, and USDT exchanges, reflecting each rate's impact on the final amount.

Laying out the transaction

I'm finalizing the currency exchange function, including calculating commissions and setting up the final transaction with the computed amounts for accurate processing.

Thought about cryptocurrency exchange logic для 23 секунд

Ответ

Ниже приведён пример упрощённой реализации логики конвертации по заданным курсам:

  1. 1 TON = 100 PAS
  2. 1 TON = 5 USDT

Из этих двух правил легко вывести все остальные комбинации:

  • TON → PAS: умножаем на 100.
  • TON → USDT: умножаем на 5.
  • PAS → TON: делим на 100.
  • USDT → TON: делим на 5.
  • PAS → USDT: сначала перевести PAS в TON (делим на 100), затем TON в USDT (умножаем на 5). Итог: amount * 5/100 = amount * 0.05.
  • USDT → PAS: сначала перевести USDT в TON (делим на 5), затем TON в PAS (умножаем на 100). Итог: amount * 100/5 = amount * 20.

Ниже пример кода, где мы добавляем логику обмена в функцию exchange(). Обратите внимание, что в данном примере для простоты показан только расчёт итогового количества токенов. Логика реального формирования транзакции (адреса, payload и т.д.) может отличаться в зависимости от вашего смарт-контракта и требует уточнения.

html
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <link rel="stylesheet" href="styles.css"> <link rel="icon" href="images/icon.png" type="image/png" /> <title>Обмен криптовалют</title> <!-- Подключение TonConnect UI --> <script src="https://unpkg.com/@tonconnect/[email protected]/dist/tonconnect-ui.min.js"></script> <script src="scripts/main.js" defer></script> </head> <body> <!-- Кнопка для подключения кошелька TonConnect --> <div id="connect-button-root"></div> <script> // Инициализируем TonConnect UI const tonConnectUI = new TON_CONNECT_UI.TonConnectUI({ manifestUrl: 'https://ton-connect.github.io/demo-dapp-with-react-ui/tonconnect-manifest.json', buttonRootId: 'connect-button-root' }); // Отслеживаем изменение статуса (подключения кошелька) tonConnectUI.onStatusChange((walletInfo) => { console.log("TonConnect status:", walletInfo); const addressElement = document.getElementById('userAddress'); if (walletInfo?.account?.address) { addressElement.innerText = "Ваш адрес: " + walletInfo.account.address; } else { addressElement.innerText = "Кошелёк не подключён"; } }); </script> <div class="container"> <h1>Обмен криптовалют</h1> <div class="exchange-form"> <input type="text" id="amount" placeholder="Сумма для обмена" /> <select id="currencyFrom"> <option value="TON">TON</option> <option value="USDT">USDT</option> <option value="PAS">PAS</option> </select> <select id="currencyTo"> <option value="TON">TON</option> <option value="USDT">USDT</option> <option value="PAS">PAS</option> </select> <button onclick="exchange()">Обменять</button> </div> </div> <div id="markets-info"></div> <div id="userAddress">Кошелёк не подключён</div> <div id="exchangeResult"></div> <script> // Пример адресов, на которые вы будете отправлять монеты const exchangeWallets = { TON: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw", USDT: "UQDF5gjmaMrHjquTr75vnDj35VBbv4S8nuB8IoiGfAnZNsAy", PAS: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw" }; // Процент комиссии (пример: 1%) const commissionRate = 0.01; async function exchange() { const amountInput = document.getElementById('amount').value; const currencyFrom = document.getElementById('currencyFrom').value; const currencyTo = document.getElementById('currencyTo').value; // Проверка суммы const amount = parseFloat(amountInput); if (!amount || isNaN(amount) || amount <= 0) { alert('Пожалуйста, введите корректную сумму для обмена.'); return; } // Проверка подключения кошелька const walletInfo = tonConnectUI.wallet; if (!walletInfo?.account?.address) { alert('Кошелёк не подключён. Пожалуйста, подключите кошелёк.'); return; } // Проверка на обмен валюты на саму себя if (currencyFrom === currencyTo) { alert('Вы пытаетесь обменять валюту на саму себя. Пожалуйста, выберите другую валюту.'); return; } // 1. Расчёт итогового количества, исходя из заданных курсов let resultAmount = 0; switch (true) { case (currencyFrom === 'TON' && currencyTo === 'PAS'): // 1 TON = 100 PAS resultAmount = amount * 100; break; case (currencyFrom === 'TON' && currencyTo === 'USDT'): // 1 TON = 5 USDT resultAmount = amount * 5; break; case (currencyFrom === 'PAS' && currencyTo === 'TON'): // 100 PAS = 1 TON => делим на 100 resultAmount = amount / 100; break; case (currencyFrom === 'USDT' && currencyTo === 'TON'): // 5 USDT = 1 TON => делим на 5 resultAmount = amount / 5; break; case (currencyFrom === 'PAS' && currencyTo === 'USDT'): // PAS -> TON -> USDT => (amount / 100) * 5 = amount * 0.05 resultAmount = (amount / 100) * 5; break; case (currencyFrom === 'USDT' && currencyTo === 'PAS'): // USDT -> TON -> PAS => (amount / 5) * 100 = amount * 20 resultAmount = (amount / 5) * 100; break; } // 2. Расчёт комиссии const commission = amount * commissionRate; const amountAfterCommission = amount - commission; // 3. Формируем "транзакции" для отправки (пример!) // Обратите внимание, что в реальном обменнике вам нужно будет: // - сначала отправлять исходную валюту на смарт-контракт обменника // - смарт-контракт должен вернуть вам нужное количество целевой валюты // - комиссия может сниматься автоматически контрактом // Здесь для примера мы показываем одну транзакцию на сам обмен и одну – на комиссию. // ВАЖНО: Здесь мы исходим из того, что "TON" – это основной токен блокчейна и его // мы можем отправлять напрямую в нанотонах. Для USDT/PAS как кастомных токенов // обычно нужен отдельный механизм (Jetton transfer или сторонний контракт). // Код ниже просто демонстрирует общий подход и потребует доработки под конкретный смарт-контракт. const transaction = { validUntil: Math.floor(Date.now() / 1000) + 600, // 10 минут messages: [ { // Адрес кошелька/контракта, куда отправляем "целевую" валюту // (в реальном кейсе сначала отправляем currencyFrom в обменник, который уже выдаст currencyTo) address: exchangeWallets[currencyTo], // Сумма в нанотонах: если это TON, умножаем на 1e9. // Если это не TON (USDT/PAS), потребуются дополнительные шаги по формированию Jetton-транзакции. amount: (amountAfterCommission * 1e9).toFixed(0), payload: 'te6cckEBAQEAAgAAAEysucg=' }, { // Адрес кошелька для комиссии (например, наш сервис) address: "UQBQPunc6ZKNaHzTNG-6e-taSer8EPfZSVjcEBUKBLcQMWKw", amount: (commission * 1e9).toFixed(0), payload: 'te6cckEBAQEAAgAAAEysucg=' } ] }; try { // Отправляем транзакцию через TonConnect const result = await tonConnectUI.sendTransaction(transaction); console.log("Транзакция успешно отправлена:", result); // Обновляем интерфейс document.getElementById('exchangeResult').innerText = `Обмен успешно выполнен!\n` + `Вы меняете: ${amount} ${currencyFrom}\n` + `Ожидаемая сумма на выходе: ~${resultAmount.toFixed(4)} ${currencyTo}\n` + `С учётом комиссии 1% (отправили исходной валюты - ${amountAfterCommission.toFixed(4)})\n` + `Комиссия составила: ${commission.toFixed(4)} ${currencyFrom}`; } catch (error) { console.error("Ошибка при отправке транзакции:", error); document.getElementById('exchangeResult').innerText = `Ошибка при обмене: ${error.message}`; } } </script> </body> </html>

Основные моменты:

  1. Расчёт итоговой суммы
    Мы используем простые математические операции, основанные на заданных курсах. Для каждого случая (currencyFrom → currencyTo) выбираем соответствующую формулу.

  2. Комиссия
    В примере взята комиссия 1% от исходной суммы, чтобы показать, как она может вычитаться из отправляемой суммы. В реальных сценариях комиссия может сниматься иначе (например, за счёт контракта или отдельной транзакции).

  3. Формирование транзакции

    • При обмене именно TON (как основной токен блокчейна) мы указываем amount * 1e9 (наноТОНы).
    • Если нужно отправлять Jetton (USDT/PAS), обычно требуется сформировать специальный payload для перевода токена. В примере выше payload одинаковый te6cckEBAQEAAgAAAEysucg=, но в реальном проекте он будет отличаться для каждого типа токена.
    • Настройка validUntil даёт время (в секундах), в течение которого транзакция считается валидной (например, 10 минут).
  4. Подключение к кошельку

    • Мы проверяем, что кошелёк подключён (через tonConnectUI.wallet). Если не подключён, выдаём предупреждение.
  5. В реальном обменнике

    • Скорее всего, сначала нужно отправлять исходную валюту (TON/Jetton) на адрес смарт-контракта обменника.
    • Смарт-контракт, получив средства, возвращает пользователю целевую валюту.
    • Обычно подобные операции требуют двух транзакций (либо сложной логики внутри одного контракта), а также правильного формирования payload/данных для контракта.

Пример выше — упрощённая демонстрация того, как можно использовать логику курсов и отправку транзакции через TonConnect. Для полноценного обмена необходимо дописать/подключить смарт-контракт, который будет хранить резервы и автоматически выдавать нужную валюту в ответ на входящую транзакцию.

Поделиться этим Q&A