From fab26b58c95cb3855c4cf98aa8164d8ca27b0ac2 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Sat, 18 Jul 2026 03:20:03 -0300 Subject: [PATCH] perf(liquidity): refresh context accounts after inclusion --- apps/amm/client/src/api/plan.rs | 5 +++++ apps/amm/client/src/api/position.rs | 20 +++++++++++++++++++ apps/amm/client/src/api/tests.rs | 18 +++++++++++++++++ apps/amm/src/AmmUiBackend.cpp | 7 +++++-- apps/amm/src/NewPositionRuntime.cpp | 8 +++++++- apps/amm/tests/cpp/NewPositionRuntimeTest.cpp | 10 +++++++++- 6 files changed, 64 insertions(+), 4 deletions(-) diff --git a/apps/amm/client/src/api/plan.rs b/apps/amm/client/src/api/plan.rs index 97edcf2..2144ed1 100644 --- a/apps/amm/client/src/api/plan.rs +++ b/apps/amm/client/src/api/plan.rs @@ -78,6 +78,7 @@ pub(super) fn plan(input: PlanRequest) -> Result { } let NewPositionPlan { accounts, branch } = plan; let affected_account_ids = accounts.writable_account_ids(fresh_lp)?; + let context_affected_account_ids = accounts.context_affected_account_ids(fresh_lp)?; let (account_ids, signing_requirements) = accounts.wallet_args(fresh_lp)?; let instruction = match branch { QuoteBranch::Missing { amount_a, amount_b } => { @@ -121,6 +122,10 @@ pub(super) fn plan(input: PlanRequest) -> Result { .into_iter() .map(account_id_hex) .collect::>(), + "contextAffectedAccountIds": context_affected_account_ids + .into_iter() + .map(account_id_hex) + .collect::>(), "signingRequirements": signing_requirements, "instruction": instruction, "deadlineMs": deadline.to_string(), diff --git a/apps/amm/client/src/api/position.rs b/apps/amm/client/src/api/position.rs index d36ad05..f5e2ffa 100644 --- a/apps/amm/client/src/api/position.rs +++ b/apps/amm/client/src/api/position.rs @@ -210,6 +210,26 @@ impl AccountPlan { }) .collect() } + + pub(super) fn context_affected_account_ids( + &self, + fresh_lp: Option, + ) -> Result, String> { + self.rows + .iter() + .filter(|row| { + row.action != "read" + && (row.role == "lp_definition" || row.role.starts_with("user_holding_")) + }) + .map(|row| match row.account_id { + Some(account_id) => Ok(account_id), + None if row.role == "user_holding_lp" => { + fresh_lp.ok_or_else(|| String::from("transaction plan has no LP holding")) + } + None => Err(String::from("transaction plan has an unresolved account")), + }) + .collect() + } } impl AccountPlanRow { diff --git a/apps/amm/client/src/api/tests.rs b/apps/amm/client/src/api/tests.rs index eaa2eff..300cea2 100644 --- a/apps/amm/client/src/api/tests.rs +++ b/apps/amm/client/src/api/tests.rs @@ -594,6 +594,15 @@ fn missing_pool_quote_and_plan_use_current_account_order() { assert_eq!(affected.len(), 9); assert!(!affected.contains(&json!(account_id_hex(config_id)))); assert!(!affected.contains(&json!(account_id_hex(clock_id)))); + assert_eq!( + plan_value["contextAffectedAccountIds"].as_array().unwrap(), + &vec![ + plan_value["accountIds"][4].clone(), + plan_value["accountIds"][6].clone(), + plan_value["accountIds"][7].clone(), + plan_value["accountIds"][8].clone(), + ] + ); assert_preview_matches_plan("e_value, &plan_value, Some(fresh_lp)); } @@ -741,6 +750,15 @@ fn active_pool_quote_uses_ratio_and_existing_lp_holding() { assert_eq!(plan_value["accountIds"].as_array().unwrap().len(), 10); assert_eq!(plan_value["accountIds"][7], account_id_hex(lp_holding)); assert_eq!(plan_value["signingRequirements"][7], false); + assert_eq!( + plan_value["contextAffectedAccountIds"].as_array().unwrap(), + &vec![ + plan_value["accountIds"][4].clone(), + plan_value["accountIds"][5].clone(), + plan_value["accountIds"][6].clone(), + plan_value["accountIds"][7].clone(), + ] + ); assert_preview_matches_plan("e_value, &plan_value, None); } diff --git a/apps/amm/src/AmmUiBackend.cpp b/apps/amm/src/AmmUiBackend.cpp index a120f82..06440e3 100644 --- a/apps/amm/src/AmmUiBackend.cpp +++ b/apps/amm/src/AmmUiBackend.cpp @@ -432,9 +432,12 @@ void AmmUiBackend::watchTransaction(const QVariantMap& result) bool deadlineValid = false; const qint64 deadline = result.value(QStringLiteral("deadlineMs")) .toString().toLongLong(&deadlineValid); + const QVariantList affectedValues = result.contains( + QStringLiteral("contextAffectedAccountIds")) + ? result.value(QStringLiteral("contextAffectedAccountIds")).toList() + : result.value(QStringLiteral("affectedAccountIds")).toList(); QStringList affected; - for (const QVariant& value : result.value( - QStringLiteral("affectedAccountIds")).toList()) { + for (const QVariant& value : affectedValues) { affected.append(value.toString()); } if (nativeHash.isEmpty() || !deadlineValid || affected.isEmpty()) diff --git a/apps/amm/src/NewPositionRuntime.cpp b/apps/amm/src/NewPositionRuntime.cpp index af15959..56c3dfa 100644 --- a/apps/amm/src/NewPositionRuntime.cpp +++ b/apps/amm/src/NewPositionRuntime.cpp @@ -929,7 +929,9 @@ void NewPositionRuntime::submitPlanAsync(QJsonObject input, [guard, submitGeneration, walletGeneration, freshLpAccountId = std::move(freshLpAccountId), deadlineMs = plan.value(QStringLiteral("deadlineMs")), affectedAccountIds = - plan.value(QStringLiteral("affectedAccountIds"))]( + plan.value(QStringLiteral("affectedAccountIds")), + contextAffectedAccountIds = + plan.value(QStringLiteral("contextAffectedAccountIds"))]( WalletSubmission submission) mutable { if (!guard) return; @@ -962,6 +964,8 @@ void NewPositionRuntime::submitPlanAsync(QJsonObject input, { QStringLiteral("deadlineMs"), deadlineMs }, { QStringLiteral("affectedAccountIds"), affectedAccountIds }, + { QStringLiteral("contextAffectedAccountIds"), + contextAffectedAccountIds }, }.toVariantMap()); }); } @@ -1172,5 +1176,7 @@ QVariantMap NewPositionRuntime::submit(const QVariantMap& request, { QStringLiteral("transactionId"), transactionId }, { QStringLiteral("nativeTransactionHash"), submission.nativeHash }, { QStringLiteral("deadlineMs"), plan.value(QStringLiteral("deadlineMs")) }, + { QStringLiteral("contextAffectedAccountIds"), + plan.value(QStringLiteral("contextAffectedAccountIds")) }, }.toVariantMap(); } diff --git a/apps/amm/tests/cpp/NewPositionRuntimeTest.cpp b/apps/amm/tests/cpp/NewPositionRuntimeTest.cpp index e0cdbc8..269d9e6 100644 --- a/apps/amm/tests/cpp/NewPositionRuntimeTest.cpp +++ b/apps/amm/tests/cpp/NewPositionRuntimeTest.cpp @@ -360,6 +360,8 @@ namespace { { QStringLiteral("accountIds"), QJsonArray { QStringLiteral("account") } }, { QStringLiteral("affectedAccountIds"), QJsonArray { QStringLiteral("account") } }, + { QStringLiteral("contextAffectedAccountIds"), + QJsonArray { QStringLiteral("account") } }, { QStringLiteral("signingRequirements"), QJsonArray { true } }, { QStringLiteral("instruction"), QJsonArray { 1 } }, { QStringLiteral("programId"), QStringLiteral("program") }, @@ -487,6 +489,10 @@ int main(int argc, char** argv) == wallet.transactionHash, "submitted result should preserve the native hash for polling")) return 1; + if (!expect(result.value(QStringLiteral("contextAffectedAccountIds")).toList() + == QVariantList { QStringLiteral("account") }, + "submitted result should preserve context refresh account IDs")) + return 1; if (!expect(wallet.createdAccounts == 1 && client.sawFreshLp, "fresh LP account should enter the plan")) return 1; @@ -1205,7 +1211,9 @@ int main(int argc, char** argv) asyncWallet.finishSubmission(); if (!expect(asyncCallbackCount == 1 && asyncResult.value(QStringLiteral("status")).toString() - == QStringLiteral("submitted"), + == QStringLiteral("submitted") + && asyncResult.value(QStringLiteral("contextAffectedAccountIds")) + .toList() == QVariantList { QStringLiteral("account") }, "async wallet completion should finish exactly once")) return 1;