mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-25 00:03:11 +00:00
feat(amm): byte-encode swap instruction for QtRO + AMM_DEBUG tracing + token config
This commit is contained in:
parent
caf53d0409
commit
c0568e3a88
22
README.md
22
README.md
@ -30,16 +30,30 @@ mkdir -p ~/.config/nix && echo "experimental-features = nix-command flakes" >> ~
|
||||
|
||||
### Example (`apps/amm`)
|
||||
|
||||
```bash
|
||||
cd apps/amm
|
||||
The AMM UI is built from the **repository-root** flake (which also provides the
|
||||
`amm_client_ffi` library it links). Run it from the repo root by its named
|
||||
attribute — there is no bare `nix run .` default, so future apps are
|
||||
`nix run .#<name>`:
|
||||
|
||||
# Run the app
|
||||
nix run .
|
||||
```bash
|
||||
# Run the app (from the repo root)
|
||||
nix run .#amm-ui
|
||||
|
||||
# Update pinned dependencies
|
||||
nix flake update
|
||||
```
|
||||
|
||||
To use the Swap view, also set `AMM_PROGRAM_BIN` (your deployed `amm.bin`) and
|
||||
`TOKENS_CONFIG` (your token list) — use absolute paths, from the repo root:
|
||||
|
||||
```bash
|
||||
AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
|
||||
TOKENS_CONFIG=$(pwd)/amm-tokens.json \
|
||||
nix run .#amm-ui
|
||||
```
|
||||
|
||||
See `apps/amm/README.md` for full details on both variables.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Rust** — install via [rustup](https://rustup.rs/). The pinned toolchain version is set in `rust-toolchain.toml`.
|
||||
|
||||
@ -131,6 +131,20 @@ core module from `result-core/` and the UI plugin from `result-lgx/`:
|
||||
4. Choose the core module `.lgx` from `result-core/`, then the UI plugin `.lgx`
|
||||
from `result-lgx/`
|
||||
|
||||
To actually use the Swap view you must also set `AMM_PROGRAM_BIN` and
|
||||
`TOKENS_CONFIG` (both explained below). Run this **from the repo root** — use
|
||||
absolute paths (`$(pwd)/…`), because `nix run` may not preserve the working
|
||||
directory, so relative paths won't resolve:
|
||||
|
||||
```bash
|
||||
AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
|
||||
TOKENS_CONFIG=$(pwd)/amm-tokens.json \
|
||||
nix run .#amm-ui
|
||||
```
|
||||
|
||||
Without `AMM_PROGRAM_BIN` the Swap view stays disabled; without `TOKENS_CONFIG`
|
||||
the token picker is empty. Each is detailed below.
|
||||
|
||||
### AMM program binary (required for swaps)
|
||||
|
||||
To execute a swap, the app must submit a transaction against the **exact AMM
|
||||
@ -178,10 +192,16 @@ account the wallet will sign transfers from/to for that token):
|
||||
```
|
||||
|
||||
If `TOKENS_CONFIG` is unset, unreadable, or not a valid JSON array, the token
|
||||
picker stays empty (a `qWarning` is logged; no swap can be started).
|
||||
picker stays empty (a `qWarning` naming the exact cause is logged to stderr; no
|
||||
swap can be started). `definitionId`/`holding` may be given as base58 (as the
|
||||
wallet/runbook display them) or hex — the app normalizes both to hex.
|
||||
|
||||
Full command with both variables set (absolute paths, from the repo root):
|
||||
|
||||
```bash
|
||||
AMM_PROGRAM_BIN=/abs/path/to/amm.bin TOKENS_CONFIG=/abs/path/to/tokens.json nix run .#amm-ui
|
||||
AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
|
||||
TOKENS_CONFIG=$(pwd)/amm-tokens.json \
|
||||
nix run .#amm-ui
|
||||
```
|
||||
|
||||
## Updating Dependencies
|
||||
|
||||
@ -97,8 +97,10 @@ Rectangle {
|
||||
root.poolReserveA = (pool && pool.reserveA) || "0"
|
||||
root.poolReserveB = (pool && pool.reserveB) || "0"
|
||||
root.poolDefAHex = (pool && pool.defAHex) || ""
|
||||
root.poolFeeBps = (pool && pool.feeBps) || 30
|
||||
root.poolError = ""
|
||||
// feeBps === 0 is a legitimate zero-fee pool; only fall back
|
||||
// to the 30bps default when the backend didn't send a value.
|
||||
root.poolFeeBps = (pool && pool.feeBps !== undefined) ? pool.feeBps : 30
|
||||
root.poolError = (pool && pool.error && pool.error !== "no_pool") ? pool.error : ""
|
||||
},
|
||||
function (error) {
|
||||
console.warn("resolvePool error:", error)
|
||||
@ -294,6 +296,9 @@ Rectangle {
|
||||
amount: root.sellDisplay
|
||||
token: root.sellToken
|
||||
active: root.editingSide === "sell"
|
||||
// Sell amount is sent to the backend as a raw base-units integer
|
||||
// string; reject fractional entry rather than fail opaquely.
|
||||
digitsOnly: true
|
||||
onInputEdited: function(v) {
|
||||
root.sellInput = v
|
||||
if (root.editingSide !== "sell") root.editingSide = "sell"
|
||||
|
||||
@ -11,6 +11,11 @@ Rectangle {
|
||||
property string usdValue: ""
|
||||
property var token: null
|
||||
property bool active: true
|
||||
// When true, restrict input to digits only — used for the sell-amount
|
||||
// field, whose value is sent to the backend as a raw base-units integer
|
||||
// string (see decimalToU128Le in AmmUiBackend.cpp); fractional/decimal
|
||||
// input there fails opaquely rather than being scaled.
|
||||
property bool digitsOnly: false
|
||||
|
||||
signal tokenClicked()
|
||||
signal inputEdited(string newValue)
|
||||
@ -57,9 +62,21 @@ Rectangle {
|
||||
font.weight: Font.Bold
|
||||
selectionColor: theme.colors.selection
|
||||
clip: true
|
||||
onTextEdited: root.inputEdited(text)
|
||||
onTextEdited: {
|
||||
if (root.digitsOnly) {
|
||||
// Amounts are base units (integers) — strip any
|
||||
// character that slips past the validator (e.g.
|
||||
// via paste) before it reaches the backend.
|
||||
var filtered = text.replace(/[^0-9]/g, "")
|
||||
if (filtered !== text)
|
||||
text = filtered // does not re-trigger onTextEdited
|
||||
root.inputEdited(filtered)
|
||||
} else {
|
||||
root.inputEdited(text)
|
||||
}
|
||||
}
|
||||
validator: RegularExpressionValidator {
|
||||
regularExpression: /^[0-9]*\.?[0-9]*$/
|
||||
regularExpression: root.digitsOnly ? /^[0-9]*$/ : /^[0-9]*\.?[0-9]*$/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,28 @@ extern "C" {
|
||||
#include "amm_client_ffi.h"
|
||||
}
|
||||
|
||||
// Debug tracing for the swap path (resolvePool / swapExactInput), gated behind
|
||||
// the AMM_DEBUG env var so normal runs stay quiet. When disabled, the streamed
|
||||
// arguments — including the account_id_to_base58 round-trips used to render
|
||||
// accounts — are NOT evaluated, so there's no per-call overhead.
|
||||
//
|
||||
// NOTE: `send_generic_public_transaction` takes `instruction` and
|
||||
// `program_dependencies` as byte strings (`bstr`). Declaring them as Vec<u32> /
|
||||
// Vec<Vec<u8>> makes the module's Qt/QtRO glue downgrade them to an opaque `any`
|
||||
// the separate module process can't deserialize, silently dropping every
|
||||
// argument. We therefore send `instruction` as the little-endian bytes of the
|
||||
// u32 words and `program_dependencies` empty. Requires the wallet module built
|
||||
// with byte-string params; see docs/amm-swap-qtro-serialization-bug.md.
|
||||
static bool ammDebugEnabled()
|
||||
{
|
||||
static const bool on = qEnvironmentVariableIsSet("AMM_DEBUG");
|
||||
return on;
|
||||
}
|
||||
// AMM_DBG() << ... behaves like qWarning().noquote() but only when AMM_DEBUG is
|
||||
// set; otherwise the whole statement (and its arguments) is skipped.
|
||||
#define AMM_DBG() \
|
||||
if (ammDebugEnabled()) qWarning().noquote()
|
||||
|
||||
namespace {
|
||||
const char SETTINGS_ORG[] = "Logos";
|
||||
const char SETTINGS_APP[] = "AmmUI";
|
||||
@ -98,11 +120,15 @@ namespace {
|
||||
if (trimmed.isEmpty())
|
||||
return false;
|
||||
|
||||
const unsigned __int128 kMax = ~static_cast<unsigned __int128>(0);
|
||||
unsigned __int128 value = 0;
|
||||
for (const QChar ch : trimmed) {
|
||||
if (!ch.isDigit())
|
||||
return false;
|
||||
value = value * 10 + static_cast<unsigned int>(ch.digitValue());
|
||||
const unsigned int d = static_cast<unsigned int>(ch.digitValue());
|
||||
if (value > (kMax - d) / 10)
|
||||
return false; // would overflow u128
|
||||
value = value * 10 + d;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
@ -246,16 +272,16 @@ void AmmUiBackend::openOrAdoptWallet()
|
||||
|
||||
bool AmmUiBackend::sharedWalletIsOpen()
|
||||
{
|
||||
// list_accounts() is non-empty only once the wallet holds accounts, so it
|
||||
// can't distinguish "no wallet open" from "open but empty" (a wallet that
|
||||
// was just created and hasn't had an account added yet). Fall back to a
|
||||
// handle-dependent, account-independent signal: an open wallet always has a
|
||||
// sequencer address (from its config, defaulted on open), while a closed
|
||||
// core returns an empty string. This lets us adopt a freshly-created shared
|
||||
// wallet instead of falling through and re-opening it from disk.
|
||||
if (!QJsonArray::fromVariantList(m_logos->logos_execution_zone.list_accounts()).isEmpty())
|
||||
return true;
|
||||
return !m_logos->logos_execution_zone.get_sequencer_addr().isEmpty();
|
||||
// Treat the shared core as "already open" ONLY when it actually holds
|
||||
// accounts. We used to also treat a non-empty sequencer address as proof of
|
||||
// an open wallet ("a closed core returns an empty string"), but the wallet
|
||||
// module now reports a DEFAULT sequencer (e.g. http://localhost:…) even on a
|
||||
// CLOSED core. That made standalone launches wrongly take the adopt path —
|
||||
// mirroring an empty account list and returning early — instead of opening
|
||||
// the real on-disk wallet, so the user saw no accounts. Keying off accounts
|
||||
// alone means a genuinely-open shared wallet (Basecamp) is still adopted,
|
||||
// while a closed standalone core correctly falls through to open-from-disk.
|
||||
return !QJsonArray::fromVariantList(m_logos->logos_execution_zone.list_accounts()).isEmpty();
|
||||
}
|
||||
|
||||
QString AmmUiBackend::createNewDefault(QString password)
|
||||
@ -507,6 +533,27 @@ QString AmmUiBackend::normalizeAccountId(const QString& id)
|
||||
return hex.toLower(); // account_id_from_base58 returns "" on failure
|
||||
}
|
||||
|
||||
QByteArray AmmUiBackend::loadAmmElf()
|
||||
{
|
||||
const QByteArray binPath = qgetenv(AMM_PROGRAM_BIN_ENV);
|
||||
if (binPath.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::loadAmmElf: AMM_PROGRAM_BIN not set";
|
||||
return QByteArray();
|
||||
}
|
||||
QFile elfFile(QString::fromLocal8Bit(binPath));
|
||||
if (!elfFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "AmmUiBackend::loadAmmElf: cannot read AMM_PROGRAM_BIN at" << elfFile.fileName();
|
||||
return QByteArray();
|
||||
}
|
||||
const QByteArray elf = elfFile.readAll();
|
||||
elfFile.close();
|
||||
if (elf.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::loadAmmElf: AMM_PROGRAM_BIN is empty";
|
||||
return QByteArray();
|
||||
}
|
||||
return elf;
|
||||
}
|
||||
|
||||
QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
{
|
||||
QVariantMap out;
|
||||
@ -514,20 +561,9 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
|
||||
// 1. Load the deployed AMM program's ELF. We can't derive this ourselves —
|
||||
// it must match whatever is actually deployed on the target sequencer.
|
||||
const QByteArray binPath = qgetenv(AMM_PROGRAM_BIN_ENV);
|
||||
if (binPath.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: AMM_PROGRAM_BIN not set";
|
||||
return out;
|
||||
}
|
||||
QFile elfFile(QString::fromLocal8Bit(binPath));
|
||||
if (!elfFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: cannot read AMM_PROGRAM_BIN at" << elfFile.fileName();
|
||||
return out;
|
||||
}
|
||||
const QByteArray elf = elfFile.readAll();
|
||||
elfFile.close();
|
||||
const QByteArray elf = loadAmmElf();
|
||||
if (elf.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: AMM_PROGRAM_BIN is empty";
|
||||
out[QStringLiteral("error")] = QStringLiteral("no_program_bin");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -536,6 +572,7 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
if (!amm_client_program_id_from_elf(reinterpret_cast<const uint8_t*>(elf.constData()),
|
||||
static_cast<uintptr_t>(elf.size()), &ammId)) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: amm_client_program_id_from_elf failed";
|
||||
out[QStringLiteral("error")] = QStringLiteral("bad_program_bin");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -546,10 +583,26 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
amm_client_config_pda(&ammId, &configPda);
|
||||
const QString configHex = bytes32ToHex(configPda);
|
||||
|
||||
// Debug: log every derived account (base58 + hex) and every on-chain read so
|
||||
// a failed resolve can be traced directly against `spel inspect`.
|
||||
// account_id_to_base58 is a LOCAL encode (safe even when on-chain reads
|
||||
// time out).
|
||||
auto b58 = [this](const QString& hex) {
|
||||
const QString s = m_logos->logos_execution_zone.account_id_to_base58(hex);
|
||||
return s.isEmpty() ? hex : QStringLiteral("%1 (hex %2)").arg(s, hex);
|
||||
};
|
||||
AMM_DBG() << "[amm-debug] resolvePool: defA=" << b58(defAHex)
|
||||
<< "defB=" << b58(defBHex);
|
||||
AMM_DBG() << "[amm-debug] resolvePool: reading config account" << b58(configHex);
|
||||
|
||||
const QString configJson = m_logos->logos_execution_zone.get_account_public(configHex);
|
||||
AMM_DBG() << "[amm-debug] resolvePool: config get_account_public ->"
|
||||
<< configJson.size() << "chars; raw:" << configJson.left(400);
|
||||
const QString configDataHex = accountDataHex(configJson);
|
||||
if (configDataHex.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: AMM config account has no data (not initialized)";
|
||||
qWarning() << "AmmUiBackend::resolvePool: AMM config read returned no data"
|
||||
<< "(a failed/timed-out RPC or an uninitialized account)";
|
||||
out[QStringLiteral("error")] = QStringLiteral("amm_not_initialized");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -558,6 +611,7 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
if (!amm_client_decode_config(reinterpret_cast<const uint8_t*>(configBytes.constData()),
|
||||
static_cast<uintptr_t>(configBytes.size()), &configView)) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: amm_client_decode_config failed";
|
||||
out[QStringLiteral("error")] = QStringLiteral("bad_config");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -567,6 +621,7 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
uint8_t defB[32];
|
||||
if (!hexToBytes32(defAHex, defA) || !hexToBytes32(defBHex, defB)) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: invalid defAHex/defBHex";
|
||||
out[QStringLiteral("error")] = QStringLiteral("bad_config");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -574,10 +629,14 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
amm_client_pool_pda(&ammId, &defA, &defB, &poolPda);
|
||||
const QString poolHex = bytes32ToHex(poolPda);
|
||||
|
||||
AMM_DBG() << "[amm-debug] resolvePool: reading pool account" << b58(poolHex);
|
||||
const QString poolJson = m_logos->logos_execution_zone.get_account_public(poolHex);
|
||||
AMM_DBG() << "[amm-debug] resolvePool: pool get_account_public ->"
|
||||
<< poolJson.size() << "chars; raw:" << poolJson.left(400);
|
||||
const QString poolDataHex = accountDataHex(poolJson);
|
||||
if (poolDataHex.isEmpty()) {
|
||||
// Not a warning: this is the normal "no liquidity yet" state.
|
||||
out[QStringLiteral("error")] = QStringLiteral("no_pool");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -586,6 +645,7 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
if (!amm_client_decode_pool(reinterpret_cast<const uint8_t*>(poolBytes.constData()),
|
||||
static_cast<uintptr_t>(poolBytes.size()), &poolView)) {
|
||||
qWarning() << "AmmUiBackend::resolvePool: amm_client_decode_pool failed";
|
||||
out[QStringLiteral("error")] = QStringLiteral("bad_config");
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -605,6 +665,17 @@ QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
|
||||
out[QStringLiteral("reserveA")] = u128leToDecimal(poolView.reserve_a);
|
||||
out[QStringLiteral("reserveB")] = u128leToDecimal(poolView.reserve_b);
|
||||
out[QStringLiteral("feeBps")] = static_cast<int>(poolView.fees);
|
||||
|
||||
AMM_DBG() << "[amm-debug] resolvePool: RESOLVED"
|
||||
<< "pool=" << b58(poolHex)
|
||||
<< "poolDefA=" << b58(out[QStringLiteral("defAHex")].toString())
|
||||
<< "poolDefB=" << b58(out[QStringLiteral("defBHex")].toString())
|
||||
<< "vaultA=" << b58(out[QStringLiteral("vaultAHex")].toString())
|
||||
<< "vaultB=" << b58(out[QStringLiteral("vaultBHex")].toString())
|
||||
<< "currentTick=" << b58(out[QStringLiteral("currentTickHex")].toString())
|
||||
<< "reserveA=" << out[QStringLiteral("reserveA")].toString()
|
||||
<< "reserveB=" << out[QStringLiteral("reserveB")].toString()
|
||||
<< "feeBps=" << out[QStringLiteral("feeBps")].toInt();
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -612,6 +683,13 @@ QString AmmUiBackend::swapExactInput(QString defAHex, QString defBHex, QString u
|
||||
QString userOutputHoldingHex, QString amountInDecimal,
|
||||
QString minOutDecimal, QString deadlineDecimal)
|
||||
{
|
||||
AMM_DBG() << "[amm-debug] swapExactInput: ARGS"
|
||||
<< "defA=" << defAHex << "defB=" << defBHex
|
||||
<< "userInputHolding=" << userInputHoldingHex
|
||||
<< "userOutputHolding=" << userOutputHoldingHex
|
||||
<< "amountIn=" << amountInDecimal << "minOut=" << minOutDecimal
|
||||
<< "deadline=" << deadlineDecimal;
|
||||
|
||||
// 1. Resolve the pool's PDAs; refuse if there's no pool (or no liquidity)
|
||||
// for this token pair.
|
||||
const QVariantMap pool = resolvePool(defAHex, defBHex);
|
||||
@ -623,20 +701,9 @@ QString AmmUiBackend::swapExactInput(QString defAHex, QString defBHex, QString u
|
||||
// 2. Load the deployed AMM program's ELF (must match resolvePool's — both
|
||||
// read the same AMM_PROGRAM_BIN — since the instruction is proven against
|
||||
// this exact binary's image id).
|
||||
const QByteArray binPath = qgetenv(AMM_PROGRAM_BIN_ENV);
|
||||
if (binPath.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::swapExactInput: AMM_PROGRAM_BIN not set";
|
||||
return QString();
|
||||
}
|
||||
QFile elfFile(QString::fromLocal8Bit(binPath));
|
||||
if (!elfFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "AmmUiBackend::swapExactInput: cannot read AMM_PROGRAM_BIN at" << elfFile.fileName();
|
||||
return QString();
|
||||
}
|
||||
const QByteArray elf = elfFile.readAll();
|
||||
elfFile.close();
|
||||
const QByteArray elf = loadAmmElf();
|
||||
if (elf.isEmpty()) {
|
||||
qWarning() << "AmmUiBackend::swapExactInput: AMM_PROGRAM_BIN is empty";
|
||||
qWarning() << "AmmUiBackend::swapExactInput: failed to load AMM_PROGRAM_BIN";
|
||||
return QString();
|
||||
}
|
||||
|
||||
@ -688,12 +755,37 @@ QString AmmUiBackend::swapExactInput(QString defAHex, QString defBHex, QString u
|
||||
};
|
||||
const QVariantList signers = { false, false, false, false, true, false, false, false };
|
||||
|
||||
// Debug: dump the exact accounts/signers we submit (base58 for `spel`
|
||||
// comparison), plus the instruction/elf sizes.
|
||||
auto b58s = [this](const QString& hex) {
|
||||
const QString s = m_logos->logos_execution_zone.account_id_to_base58(hex);
|
||||
return s.isEmpty() ? hex : QStringLiteral("%1 (hex %2)").arg(s, hex);
|
||||
};
|
||||
static const char* const kSlot[] = { "config", "pool",
|
||||
"vault_a", "vault_b",
|
||||
"user_input_holding", "user_output_holding",
|
||||
"current_tick", "clock" };
|
||||
AMM_DBG() << "[amm-debug] swapExactInput: SUBMIT"
|
||||
<< "instruction_words=" << static_cast<int>(instruction.size())
|
||||
<< "elf_bytes=" << elf.size();
|
||||
for (int i = 0; i < accounts.size(); ++i) {
|
||||
AMM_DBG() << "[amm-debug] account[" << i << "]"
|
||||
<< (i < 8 ? kSlot[i] : "?") << "=" << b58s(accounts[i])
|
||||
<< "signer=" << (i < signers.size() && signers[i].toBool());
|
||||
}
|
||||
|
||||
// 6. Submit through the wallet module's generic public-transaction entry
|
||||
// point. program_dependencies is empty: the sequencer resolves the AMM's
|
||||
// chained token/twap calls on-chain for a public tx.
|
||||
// point. `instruction` is sent as the little-endian bytes of the u32 words
|
||||
// (see the note atop this file); program_dependencies is empty because the
|
||||
// sequencer resolves the AMM's chained token/twap calls on-chain for a
|
||||
// public tx.
|
||||
const QByteArray instructionBytes(reinterpret_cast<const char*>(instruction.data()),
|
||||
static_cast<int>(instruction.size() * sizeof(uint32_t)));
|
||||
const QString resultJson = m_logos->logos_execution_zone.send_generic_public_transaction(
|
||||
accounts, signers, QVariant::fromValue(instruction), QVariant::fromValue(elf),
|
||||
QVariant::fromValue(std::vector<std::vector<uint8_t>>()));
|
||||
accounts, signers, QVariant::fromValue(instructionBytes), QVariant::fromValue(elf),
|
||||
QVariant::fromValue(QByteArray()));
|
||||
AMM_DBG() << "[amm-debug] swapExactInput: send_generic_public_transaction ->"
|
||||
<< resultJson.size() << "chars; raw:" << resultJson.left(600);
|
||||
|
||||
const QJsonObject obj = QJsonDocument::fromJson(resultJson.toUtf8()).object();
|
||||
if (!obj.value(QStringLiteral("success")).toBool()) {
|
||||
|
||||
@ -81,6 +81,10 @@ private:
|
||||
void refreshSequencerAddr();
|
||||
void saveWallet();
|
||||
|
||||
// Returns the deployed AMM program ELF bytes from $AMM_PROGRAM_BIN, or an empty
|
||||
// QByteArray (with a qWarning) if the env var is unset/unreadable/empty.
|
||||
QByteArray loadAmmElf();
|
||||
|
||||
// Probe the configured sequencer over HTTP and update sequencerReachable.
|
||||
void checkReachability();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user