mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 14:11:09 +00:00
feat(amm): source liquidity tokens app-side + add custom tokens by id
Move the liquidity token selector off the module's stateful newPositionContext
onto a lean, app-owned surface, and let users add unlisted tokens by id.
FFI: new stateless `resolve_tokens` op — the app passes an explicit id set and
gets uniform selector rows `{ definitionId (base58), name, totalSupply, holdingId,
balance }`, held tokens first, unresolvable/non-fungible ids omitted. Reuses the
per-token definition/holding logic from `context`, without the network/status
envelope. Unit-tested.
Module: `resolveTokens(request, wallet_open)` reads the definitions + wallet and
calls the op (ids wrapped in a map — the universal-module glue only marshals
map/scalar inputs, not bare lists).
Backend: the app owns the id set — configured tokens (TOKENS_CONFIG) plus the
user's persisted custom ids. Held-but-unlisted tokens are NOT auto-listed (the
list mirrors the swap side); a token you hold still shows its balance once listed.
`addCustomToken` validates a pasted id by resolving its on-chain definition, then
persists it to CUSTOM_TOKEN_CONFIG (defaulting to the per-user app-data store, with
a HOME fallback so persistence never silently no-ops on an empty path).
QML: NewPositionForm/LiquidityPage take tokens/walletReady/loadingTokens as inputs
and drive selection + custom-token resolution through the backend; dropped all
newPositionContext reads and the selectable/status/code row fields.
Tests: custom-token.mjs creates token D on-chain (left out of the token config)
and verifies pasting its id resolves, selects, and persists it across a reload.
The setup script mints token D and initializes/prints the isolated
CUSTOM_TOKEN_CONFIG store
This commit is contained in:
@@ -1328,6 +1328,62 @@ LogosList AmmModuleImpl::feeTiers() {
|
||||
return out;
|
||||
}
|
||||
|
||||
LogosList AmmModuleImpl::resolveTokens(const LogosMap& request, bool wallet_open) {
|
||||
const std::string amm_program_id = ammProgramId();
|
||||
if (amm_program_id.empty())
|
||||
return LogosList::array();
|
||||
|
||||
// The config gives the token_program_id the FFI needs to decode definitions/holdings.
|
||||
const FfiResult configResult =
|
||||
call(amm_config_id, json{{"ammProgramId", amm_program_id}});
|
||||
if (!configResult.ok)
|
||||
return LogosList::array();
|
||||
const json config = readPublicAccount(jStr(configResult.value, "configId"));
|
||||
|
||||
// Normalize the app-provided ids (base58 or hex) → hex, de-dup, and read each
|
||||
// definition account. The FFI is stateless, so it gets the reads pre-fetched.
|
||||
const auto token_ids_it = request.find("tokenIds");
|
||||
const json token_ids = (token_ids_it != request.end() && token_ids_it->is_array())
|
||||
? *token_ids_it
|
||||
: json::array();
|
||||
|
||||
std::vector<std::string> ids_vec;
|
||||
ids_vec.reserve(token_ids.size());
|
||||
for (const auto& raw : token_ids) {
|
||||
if (!raw.is_string()) continue;
|
||||
const std::string hex = normalizeAccountId(raw.get<std::string>());
|
||||
if (!hex.empty()) ids_vec.push_back(hex);
|
||||
}
|
||||
std::sort(ids_vec.begin(), ids_vec.end());
|
||||
ids_vec.erase(std::unique(ids_vec.begin(), ids_vec.end()), ids_vec.end());
|
||||
|
||||
json ids = json::array();
|
||||
json definitions = json::array();
|
||||
for (const auto& hex : ids_vec) {
|
||||
ids.push_back(hex);
|
||||
definitions.push_back(readPublicAccount(hex));
|
||||
}
|
||||
|
||||
// Fresh wallet read — the selector wants current holdings/balances.
|
||||
const json wallet_accounts = walletAccountReads(wallet_open, /*refresh=*/true);
|
||||
|
||||
const FfiResult result = call(amm_resolve_tokens, json{
|
||||
{"ammProgramId", amm_program_id},
|
||||
{"config", config},
|
||||
{"tokenIds", ids},
|
||||
{"walletAccounts", wallet_accounts},
|
||||
{"tokenDefinitions", definitions},
|
||||
});
|
||||
if (!result.ok)
|
||||
return LogosList::array();
|
||||
|
||||
LogosList out = LogosList::array();
|
||||
const auto it = result.value.find("tokens");
|
||||
if (it != result.value.end() && it->is_array())
|
||||
for (const auto& row : *it) out.push_back(row);
|
||||
return out;
|
||||
}
|
||||
|
||||
LogosMap AmmModuleImpl::newPositionContext(const LogosMap& request,
|
||||
bool wallet_open,
|
||||
bool refresh_wallet_accounts) {
|
||||
|
||||
Reference in New Issue
Block a user