2026-06-24 14:50:17 +02:00
|
|
|
#include "AmmUiBackend.h"
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
2026-07-15 11:55:39 -03:00
|
|
|
#include "LogosWalletProvider.h"
|
|
|
|
|
#include "WalletController.h"
|
2026-06-24 14:50:17 +02:00
|
|
|
#include "logos_api.h"
|
|
|
|
|
#include "logos_sdk.h"
|
|
|
|
|
|
|
|
|
|
namespace {
|
2026-07-22 13:56:42 +02:00
|
|
|
const char NEW_POSITION_SCHEMA[] = "new-position.v1";
|
2026-07-13 23:41:09 +02:00
|
|
|
|
2026-07-22 13:56:42 +02:00
|
|
|
// The new-position context placeholder published before the module
|
|
|
|
|
// connection is up (matches the module's "loading" contextState).
|
|
|
|
|
QVariantMap loadingContext()
|
|
|
|
|
{
|
|
|
|
|
return QVariantMap {
|
|
|
|
|
{ QStringLiteral("schema"), QString::fromLatin1(NEW_POSITION_SCHEMA) },
|
|
|
|
|
{ QStringLiteral("status"), QStringLiteral("loading") },
|
|
|
|
|
{ QStringLiteral("networkId"), QStringLiteral("lez") },
|
|
|
|
|
{ QStringLiteral("networkFingerprint"), QString() },
|
|
|
|
|
{ QStringLiteral("tokens"), QVariantList() },
|
|
|
|
|
{ QStringLiteral("feeTiers"), QVariantList() },
|
|
|
|
|
{ QStringLiteral("warnings"), QVariantList() },
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A new-position.v1 error envelope (matches the module's publicError), for
|
|
|
|
|
// the backend-side failure paths (e.g. LP-account creation failing).
|
|
|
|
|
QVariantMap newPositionError(const QString& code)
|
|
|
|
|
{
|
|
|
|
|
return QVariantMap {
|
|
|
|
|
{ QStringLiteral("schema"), QString::fromLatin1(NEW_POSITION_SCHEMA) },
|
|
|
|
|
{ QStringLiteral("status"), QStringLiteral("error") },
|
|
|
|
|
{ QStringLiteral("canSubmit"), false },
|
|
|
|
|
{ QStringLiteral("code"), code },
|
|
|
|
|
{ QStringLiteral("errors"), QVariantList { QVariantMap {
|
|
|
|
|
{ QStringLiteral("code"), code },
|
|
|
|
|
{ QStringLiteral("recoverable"), true },
|
|
|
|
|
{ QStringLiteral("blockingFields"), QVariantList() },
|
|
|
|
|
{ QStringLiteral("details"), QVariantMap() },
|
|
|
|
|
} } },
|
|
|
|
|
{ QStringLiteral("warnings"), QVariantList() },
|
|
|
|
|
{ QStringLiteral("accountPreview"), QVariantList() },
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AmmUiBackend::AmmUiBackend(LogosAPI* logosAPI, QObject* parent)
|
|
|
|
|
: AmmUiBackendSimpleSource(parent),
|
|
|
|
|
m_logosAPI(logosAPI ? logosAPI : new LogosAPI("amm_ui", this)),
|
2026-07-15 11:55:39 -03:00
|
|
|
m_logos(std::make_unique<LogosModules>(m_logosAPI)),
|
|
|
|
|
m_wallet(std::make_unique<LogosWalletProvider>(m_logosAPI)),
|
|
|
|
|
m_walletController(std::make_unique<WalletController>(
|
2026-07-22 13:56:42 +02:00
|
|
|
*m_wallet, QStringLiteral("AmmUI")))
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-06-26 09:22:42 +02:00
|
|
|
setWalletStateReady(false);
|
|
|
|
|
|
2026-07-15 11:55:39 -03:00
|
|
|
connect(m_walletController.get(), &WalletController::stateChanged,
|
|
|
|
|
this, &AmmUiBackend::syncWalletState);
|
2026-07-22 13:56:42 +02:00
|
|
|
// Publishes an initial "loading" context (walletStateReady is still false,
|
|
|
|
|
// so it does not yet reach the module).
|
2026-07-15 11:55:39 -03:00
|
|
|
syncWalletState();
|
|
|
|
|
m_walletController->start();
|
2026-06-26 09:22:42 +02:00
|
|
|
QTimer::singleShot(0, this, [this]() {
|
|
|
|
|
setWalletStateReady(true);
|
|
|
|
|
syncWalletState();
|
|
|
|
|
});
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-15 11:55:39 -03:00
|
|
|
AmmUiBackend::~AmmUiBackend() = default;
|
|
|
|
|
|
|
|
|
|
WalletAccountModel* AmmUiBackend::accountModel() const
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
return m_walletController->accountModel();
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-26 09:22:42 +02:00
|
|
|
QString AmmUiBackend::createNewDefault(QString password)
|
|
|
|
|
{
|
|
|
|
|
setWalletStateReady(false);
|
|
|
|
|
const QString mnemonic = m_walletController->createDefaultWallet(password);
|
|
|
|
|
setWalletStateReady(true);
|
|
|
|
|
syncWalletState();
|
|
|
|
|
return mnemonic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AmmUiBackend::createNew(QString configPath, QString storagePath, QString password)
|
|
|
|
|
{
|
|
|
|
|
setWalletStateReady(false);
|
|
|
|
|
const QString mnemonic =
|
|
|
|
|
m_walletController->createWallet(configPath, storagePath, password);
|
|
|
|
|
setWalletStateReady(true);
|
|
|
|
|
syncWalletState();
|
|
|
|
|
return mnemonic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AmmUiBackend::openExisting()
|
|
|
|
|
{
|
|
|
|
|
setWalletStateReady(false);
|
|
|
|
|
const bool opened = m_walletController->open();
|
|
|
|
|
setWalletStateReady(true);
|
|
|
|
|
syncWalletState();
|
|
|
|
|
return opened;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AmmUiBackend::disconnectWallet()
|
|
|
|
|
{
|
|
|
|
|
m_walletController->disconnect();
|
|
|
|
|
setWalletStateReady(true);
|
|
|
|
|
refreshNewPositionContext(QVariantMap());
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 14:50:17 +02:00
|
|
|
QString AmmUiBackend::createAccountPublic()
|
|
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
return m_walletController->createAccount(true);
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AmmUiBackend::createAccountPrivate()
|
|
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
return m_walletController->createAccount(false);
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AmmUiBackend::refreshAccounts()
|
|
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
m_walletController->refresh();
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AmmUiBackend::refreshBalances()
|
|
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
m_walletController->refresh();
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AmmUiBackend::getBalance(QString accountIdHex, bool isPublic)
|
|
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
return m_walletController->balance(accountIdHex, isPublic);
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-26 09:22:42 +02:00
|
|
|
void AmmUiBackend::refreshNewPositionContext(QVariantMap request)
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-06-26 09:22:42 +02:00
|
|
|
const bool refreshWalletAccounts =
|
|
|
|
|
request.take(QStringLiteral("refreshWalletAccounts")).toBool();
|
|
|
|
|
if (request.contains(QStringLiteral("recentTokenIds"))
|
|
|
|
|
|| request.contains(QStringLiteral("resolvedTokenIds"))) {
|
|
|
|
|
m_newPositionHints = request;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
request = m_newPositionHints;
|
|
|
|
|
}
|
2026-07-22 13:56:42 +02:00
|
|
|
if (!walletStateReady()) {
|
|
|
|
|
setNewPositionContext(loadingContext());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setNewPositionContext(m_logos->amm_module.newPositionContext(
|
|
|
|
|
request, isWalletOpen(), refreshWalletAccounts));
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-26 09:22:42 +02:00
|
|
|
QVariantMap AmmUiBackend::quoteNewPosition(QVariantMap request)
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-07-22 13:56:42 +02:00
|
|
|
return m_logos->amm_module.quoteNewPosition(request, isWalletOpen());
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-26 09:22:42 +02:00
|
|
|
QVariantMap AmmUiBackend::submitNewPosition(QVariantMap request, QString quoteHash)
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-07-22 13:56:42 +02:00
|
|
|
// First attempt with no LP account. If the module needs a fresh LP holding
|
|
|
|
|
// it returns "requires_fresh_lp" without submitting; we own wallet-keyset
|
|
|
|
|
// mutation, so create the account here (keeping the account model + on-disk
|
|
|
|
|
// storage coherent) and resubmit with its id.
|
|
|
|
|
QVariantMap result = m_logos->amm_module.submitNewPosition(
|
|
|
|
|
request, quoteHash, isWalletOpen(), QString());
|
|
|
|
|
|
|
|
|
|
if (result.value(QStringLiteral("status")).toString()
|
|
|
|
|
== QStringLiteral("requires_fresh_lp")) {
|
|
|
|
|
const QString lpId = m_walletController->createAccount(true);
|
|
|
|
|
if (lpId.isEmpty())
|
|
|
|
|
return newPositionError(QStringLiteral("wallet_submission_failed"));
|
|
|
|
|
result = m_logos->amm_module.submitNewPosition(
|
|
|
|
|
request, quoteHash, isWalletOpen(), lpId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.value(QStringLiteral("status")).toString() == QStringLiteral("submitted"))
|
|
|
|
|
refreshBalances();
|
|
|
|
|
return result;
|
2026-06-24 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-15 11:55:39 -03:00
|
|
|
void AmmUiBackend::syncWalletState()
|
2026-06-24 14:50:17 +02:00
|
|
|
{
|
2026-07-15 11:55:39 -03:00
|
|
|
const WalletUiState& state = m_walletController->state();
|
2026-06-26 09:22:42 +02:00
|
|
|
|
2026-07-15 11:55:39 -03:00
|
|
|
setIsWalletOpen(state.isWalletOpen);
|
|
|
|
|
setWalletExists(state.walletExists);
|
|
|
|
|
setConfigPath(state.configPath);
|
|
|
|
|
setStoragePath(state.storagePath);
|
|
|
|
|
setWalletHome(state.walletHome);
|
|
|
|
|
setLastSyncedBlock(state.lastSyncedBlock);
|
|
|
|
|
setCurrentBlockHeight(state.currentBlockHeight);
|
|
|
|
|
setSequencerAddr(state.sequencerAddress);
|
|
|
|
|
setSequencerReachable(state.sequencerReachable);
|
2026-06-26 09:22:42 +02:00
|
|
|
|
|
|
|
|
publishNetworkContext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AmmUiBackend::publishNetworkContext()
|
|
|
|
|
{
|
2026-07-24 17:33:37 +02:00
|
|
|
if (!walletStateReady()) {
|
2026-07-22 13:56:42 +02:00
|
|
|
setNewPositionContext(loadingContext());
|
|
|
|
|
return;
|
2026-07-24 17:33:37 +02:00
|
|
|
}
|
2026-07-22 13:56:42 +02:00
|
|
|
setNewPositionContext(m_logos->amm_module.newPositionContext(
|
|
|
|
|
m_newPositionHints, isWalletOpen(), false));
|
2026-07-16 14:10:50 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-13 23:41:09 +02:00
|
|
|
QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
|
|
|
|
{
|
2026-07-22 13:56:42 +02:00
|
|
|
return m_logos->amm_module.resolvePool(defAHex, defBHex);
|
2026-07-13 23:41:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AmmUiBackend::swapExactInput(QString defAHex, QString defBHex, QString userInputHoldingHex,
|
|
|
|
|
QString userOutputHoldingHex, QString amountInDecimal,
|
|
|
|
|
QString minOutDecimal, QString deadlineDecimal)
|
|
|
|
|
{
|
2026-07-22 13:56:42 +02:00
|
|
|
// This app's connected state is the authoritative submit guard. disconnectWallet()
|
|
|
|
|
// only locks this UI and leaves the shared logos_execution_zone wallet open (another
|
|
|
|
|
// app may keep it open, or this app opened-then-disconnected), and the QML submit path
|
|
|
|
|
// doesn't check isWalletOpen — so without this a swap could sign/submit while the UI
|
|
|
|
|
// shows "Connect". Mirrors the guard the old SwapRuntime::swap() enforced.
|
|
|
|
|
if (!isWalletOpen())
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
const QString txHash = m_logos->amm_module.swapExactInput(
|
|
|
|
|
defAHex, defBHex, userInputHoldingHex, userOutputHoldingHex,
|
|
|
|
|
amountInDecimal, minOutDecimal, deadlineDecimal);
|
2026-07-27 16:01:37 +02:00
|
|
|
if (!txHash.isEmpty())
|
|
|
|
|
refreshBalances();
|
|
|
|
|
return txHash;
|
2026-07-13 23:41:09 +02:00
|
|
|
}
|
2026-07-14 00:07:12 +02:00
|
|
|
|
|
|
|
|
QVariantList AmmUiBackend::tokenList()
|
|
|
|
|
{
|
2026-07-22 13:56:42 +02:00
|
|
|
return m_logos->amm_module.tokenList();
|
2026-07-14 00:07:12 +02:00
|
|
|
}
|