Skip to content

Conversation

@aguxez
Copy link
Contributor

@aguxez aguxez commented Jan 21, 2026

Summary by CodeRabbit

  • New Features

    • Automatic probing of accounts after KYC to detect ETH/ERC‑20 balances and trigger asset pokes (new exported probe function).
  • Chores

    • Simplified "received" notification text.
    • Per-account external IDs (pandaId) included and preserved across account flows; account creation, funding and auto‑credit flows streamlined.
    • Reduced per-transfer processing and removed multi‑asset/market messaging.
  • Tests

    • Added expanded on‑chain probing tests; removed several legacy activity integration cases.
  • Changelog

    • Patch release entry added.

✏️ Tip: You can customize this high-level summary in your review settings.


Open with Devin

@aguxez aguxez requested a review from nfmelendez as a code owner January 21, 2026 20:34
@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: 7db2555

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds exported pokeAccountAssets(accountAddress) in persona to discover on‑chain ETH/ERC‑20 balances and dispatch non‑blocking poke/pokeETH calls via keeper; simplifies activity hook by removing multi‑asset market/poke logic and surfaces pandaId in per-account credential mappings; tests adjusted accordingly.

Changes

Cohort / File(s) Summary
Activity Hook Simplification
server/hooks/activity.ts
Removed ETH/WETH market/poke orchestration and market-specific messaging; propagate pandaId into per-account credential mapping; use parsedAccount for deployment/funding/auto-credit checks; invoke persona poke asynchronously when pandaId exists.
Asset Poking Feature (persona)
server/hooks/persona.ts
Added exported pokeAccountAssets(accountAddress) that queries previewer for assets, reads ETH/ERC‑20 balances via publicClient, assembles poke/pokeETH calls and invokes keeper exaSend with retry/error capture; integrated async poke call into persona flow and added robust parse/error handling.
Persona Tests Expansion
server/test/hooks/persona.test.ts
Added mocks for getBalance, readContract, keeper.exaSend and new scenarios: ETH+token pokes, ETH-only, skip WETH when ETH present, and zero-balance skip; adjusted payloads, headers, and setup/teardown.
Activity Tests Removal
server/test/hooks/activity.test.ts
Removed poke-related tests and mocks (keeper, onesignal, mock ERC‑20 ABI) and deleted several poke-focused test cases.
Changeset
.changeset/silly-yaks-divide.md
New patch changeset entry: "✨ poke account after kyc".

Sequence Diagram(s)

sequenceDiagram
    participant Persona as Persona Service
    participant Previewer as exaPreviewer
    participant PublicClient as PublicClient
    participant Keeper as Keeper Service
    participant Chain as Blockchain

    Persona->>Previewer: request asset/market mappings for account
    Previewer-->>Persona: asset list

    Persona->>PublicClient: getBalance(account) for ETH
    PublicClient->>Chain: eth_getBalance
    Chain-->>PublicClient: ETH balance
    PublicClient-->>Persona: ETH balance

    Persona->>PublicClient: readContract(token, balanceOf, [account])
    PublicClient->>Chain: eth_call (balanceOf)
    Chain-->>PublicClient: token balance
    PublicClient-->>Persona: token balance

    alt ETH balance > 0
        Persona->>Keeper: exaSend(pokeETH, ...)
        Keeper->>Chain: broadcast tx
        Chain-->>Keeper: tx receipt
        Keeper-->>Persona: tx result
    end

    alt ERC‑20 balance > 0
        Persona->>Keeper: exaSend(poke, ...)
        Keeper->>Chain: broadcast tx
        Chain-->>Keeper: tx receipt
        Keeper-->>Persona: tx result
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • cruzdanilo
  • nfmelendez
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a new poke account mechanism triggered after KYC completion, which is the core objective of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kyc-poke

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @aguxez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the server-side logic for interacting with user accounts, specifically concerning the 'poking' of assets to potentially activate yield or other on-chain functionalities. The previous implementation, which attempted to poke accounts upon any fund receipt, has been replaced with a more targeted approach. Now, accounts will be automatically 'poked' for their relevant assets immediately following a successful Know Your Customer (KYC) verification through the Persona integration. This change streamlines the user onboarding experience by ensuring that accounts are properly initialized for asset interaction right after identity verification, while also simplifying the general activity tracking mechanism.

Highlights

  • Relocation of Account Poking Logic: The mechanism responsible for 'poking' accounts (triggering on-chain interactions for assets) has been moved from the general activity hook to the persona (KYC) hook.
  • KYC-Triggered Asset Poking: User accounts will now be automatically 'poked' for their held assets (ETH and other supported ERC20s) immediately after a successful Know Your Customer (KYC) completion via the Persona integration.
  • Optimized Asset Poking Strategy: The new logic in the persona hook intelligently checks for positive balances of ETH and other relevant assets, ensuring that only necessary 'poke' operations are performed and avoiding redundant interactions (e.g., skipping WETH if ETH is already present).
  • Simplified Activity Hook: The activity hook has been streamlined by removing the asset poking logic, making it solely responsible for sending push notifications upon fund receipts and handling initial account creation.
  • Enhanced Test Coverage for Persona Hook: New unit tests have been added to the persona hook to thoroughly validate the correct behavior of the asset poking mechanism under various balance conditions, including scenarios with mixed assets, ETH-only, and zero balances.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the account poking logic, moving it from the activity hook to the persona hook to be triggered after KYC completion. While adding a poke after KYC is beneficial, removing this logic entirely from the activity hook is a critical regression, as it prevents subsequent deposits from automatically earning yield. Additionally, the new implementation in persona.ts lacks the robustness of the original, specifically the retry mechanism.

@sentry
Copy link

sentry bot commented Jan 21, 2026

Codecov Report

❌ Patch coverage is 38.33333% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.03%. Comparing base (907ade0) to head (7db2555).

Files with missing lines Patch % Lines
server/hooks/persona.ts 27.08% 31 Missing and 4 partials ⚠️
server/hooks/activity.ts 83.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #666      +/-   ##
==========================================
+ Coverage   63.66%   66.03%   +2.36%     
==========================================
  Files         169       53     -116     
  Lines        5882     2679    -3203     
  Branches     1753      606    -1147     
==========================================
- Hits         3745     1769    -1976     
+ Misses       1964      744    -1220     
+ Partials      173      166       -7     
Flag Coverage Δ
e2e 66.03% <38.33%> (+3.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/hooks/persona.ts (1)

320-345: Redundant address parsing - parse once and reuse.

The same credential.account is parsed twice with different methods:

  • Line 322: v.parse(Address, credential.account) (throws on failure)
  • Line 327: safeParse(Address, credential.account) (returns result)

Consider consolidating to parse once before both operations.

♻️ Proposed refactor to eliminate redundant parsing
-    Promise.resolve()
-      .then(async () => {
-        const accountAddress = v.parse(Address, credential.account);
-        await pokeAccountAssets(accountAddress);
-      })
-      .catch((error: unknown) => captureException(error));
-
-    const accountParsed = safeParse(Address, credential.account);
-    if (accountParsed.success) {
+    const accountParsed = safeParse(Address, credential.account);
+    if (accountParsed.success) {
+      const accountAddress = accountParsed.output;
+      pokeAccountAssets(accountAddress).catch((error: unknown) => captureException(error));
+
       addCapita({
         birthdate: fields.birthdate.value,
         document: fields.identificationNumber.value,
         firstName: fields.nameFirst.value,
         lastName: fields.nameLast.value,
         email: fields.emailAddress.value,
         phone: fields.phoneNumber?.value ?? "",
-        internalId: deriveAssociateId(accountParsed.output),
+        internalId: deriveAssociateId(accountAddress),
         product: "travel insurance",
       }).catch((error: unknown) => {
         captureException(error, { extra: { pandaId: id, referenceId } });
       });
     } else {
       captureException(new Error("invalid account address"), {
         extra: { pandaId: id, referenceId, account: credential.account },
       });
     }
🤖 Fix all issues with AI agents
In `@server/hooks/activity.ts`:
- Around line 105-132: The code repeatedly calls v.parse(Address, account)
inside the startSpan async callback; parse the account once into a local
variable (e.g., parsedAccount) at the top of that callback and reuse it for all
uses (scope.setUser, scope.setTag, publicClient.getCode, keeper.exaSend
args/track, and autoCredit) to avoid duplicate parsing and improve clarity;
update all references of v.parse(Address, account) to use the new parsed
variable and ensure its type is the parsed Address type expected by
publicClient, keeper.exaSend, track, and autoCredit.

In `@server/hooks/persona.ts`:
- Around line 46-110: The duplicate concatenation of ABIs inside
pokeAccountAssets leads to repeated array construction; extract the combined ABI
into a single constant (e.g., const combinedAccountAbi = [...exaPluginAbi,
...upgradeableModularAccountAbi, ...auditorAbi, ...marketAbi]) and replace both
inline concatenations in the keeper.exaSend calls with combinedAccountAbi so
pokeAccountAssets uses the shared constant instead of building the same array
twice.

In `@server/test/hooks/persona.test.ts`:
- Around line 555-560: The current fragile fixed delay (setTimeout 100ms) before
asserting expect(exaSendSpy).not.toHaveBeenCalled() can flake in slow CI;
replace it with a deterministic wait using vitest's vi.waitFor (or equivalent
test-wait helper) to repeatedly check that exaSendSpy has not been called with a
reasonable timeout/interval, or increase the delay with a clear comment if you
must keep the sleep. Locate the assertion in persona.test.ts around the
setTimeout block and update the logic that waits prior to asserting on
exaSendSpy (the spy variable is exaSendSpy) to use vi.waitFor with an early-exit
negative assertion or a longer buffered timeout to make the test robust.

@aguxez aguxez force-pushed the kyc-poke branch 2 times, most recently from 6f7b98a to 9168653 Compare January 21, 2026 21:58
@aguxez aguxez requested a review from cruzdanilo as a code owner January 21, 2026 21:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@server/hooks/activity.ts`:
- Around line 106-136: The call inside keeper.exaSend uses v.parse(Address,
factory) redundantly—factory was already parsed to Address earlier—so remove the
extra parse and pass the existing parsed factory value (e.g., factory or
parsedFactory as used where it was parsed) directly to keeper.exaSend's address
field; update the keeper.exaSend invocation in the account activity span (inside
startSpan / withScope) to use the parsed factory variable and ensure types still
align.

In `@server/hooks/persona.ts`:
- Around line 48-70: In pokeAccountAssets wrap the initial
publicClient.readContract call that fetches assets from exaPreviewerAddress /
functionName "assets" with the existing withRetry helper (same retry/backoff
config you use for the poke operations) so transient RPC failures are retried;
keep the subsequent mapping to new Map<Address,Address>(...) and the parsing
with v.parse unchanged, but invoke withRetry around the readContract Promise
(referencing publicClient.readContract, exaPreviewerAddress, exaPreviewerAbi,
and withRetry) to make asset discovery resilient.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@server/hooks/activity.ts`:
- Around line 100-101: The notification string assembly currently uses `${value
? `${value} ` : ""}${assetSymbol} received` and can produce "undefined received"
when asset/assetSymbol is missing; update the construction in the block that
sets the `en` notification text to guard against missing asset by using a safe
fallback (e.g. use assetSymbol ?? "" or asset?.symbol ?? "" and if still empty
fall back to a generic word like "token" or omit the symbol) and trim or
conditionally include the trailing space so you never log "undefined received";
apply the same guard for any parallel localization keys if present.

In `@server/hooks/persona.ts`:
- Around line 65-79: The current Promise.all over ERC‑20 reads will reject on
any single read error and skip ETH & other balances; change to a best‑effort
collection by replacing the inner
Promise.all([...marketsByAsset.entries()].map(...)) with a Promise.allSettled
(or map that catches errors) so each publicClient.readContract call for ERC20
(using erc20Abi, publicClient.readContract, marketsByAsset, asset) is allowed to
fail individually and return a safe fallback (e.g., balance = 0 or null plus
asset/market) instead of throwing; keep the publicClient.getBalance call for ETH
(ethBalance) as-is and combine results so assetBalances contains entries for
every asset with error-safe balances.

In `@server/test/hooks/persona.test.ts`:
- Around line 262-266: The inline comment inside the afterEach block should be
lowercased for style compliance: locate the afterEach function (the cleanup hook
that calls database.update(credentials).set({ pandaId: null
}).where(eq(credentials.id, "persona-ref")) and vi.restoreAllMocks()) and change
the comment text "// Reset pandaId to null for next test" to use lowercase
(e.g., "// reset pandaId to null for next test").
♻️ Duplicate comments (1)
server/hooks/activity.ts (1)

106-137: Confirm poke gating by pandaId matches intended product behavior.

With this guard, deposits made before KYC won’t be poked until KYC completes. If that’s not the desired behavior, move the call outside the pandaId check or document the change explicitly.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View issue and 6 additional flags in Devin Review.

Open in Devin Review

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@server/hooks/persona.ts`:
- Around line 65-83: The current Promise.allSettled mapping loses asset/market
context and accesses result.reason unsafely; change to map over
[...marketsByAsset.entries()] with an async function that wraps each
publicClient.readContract call in a try/catch so each iteration always returns a
typed object { asset, market, balance } (on success use the returned balance, on
failure set balance to 0n and log the error), then await Promise.all over those
per-asset promises; update the code paths referencing marketsByAsset,
publicClient.readContract, and erc20Abi to use this per-asset try/catch pattern
to avoid unsafe result.reason access and preserve asset/market information.

In `@server/test/hooks/persona.test.ts`:
- Around line 555-556: Update inline comment text to use lowercase per style
guidelines: change the two comment lines that mention vi.waitFor and
pokeAccountAssets in persona.test.ts to start with lowercase letters (e.g., "use
vi.waitFor..." and "the wait is necessary...") so they comply with the project's
inline comment style; locate the comments near the test that references
vi.waitFor and pokeAccountAssets and adjust only the capitalization of those
comment sentences.
- Line 489: Change the inline comment "Should poke ETH and the other asset, but
skip WETH" to use lowercase initial letter for style compliance; locate that
comment in server/test/hooks/persona.test.ts (the inline test comment at the
marked diff) and update it to "should poke ETH and the other asset, but skip
WETH" (or fully lowercase asset names if your style prefers "eth" and "weth") so
it follows the project's lowercase-inline-comment guideline.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@server/hooks/persona.ts`:
- Around line 353-362: The code parses credential.account twice (via v.parse and
safeParse); parse it once into a single variable (e.g., accountParsed or
accountResult) before both uses and reuse that parsed Address when calling
deriveAssociateId and addCapita to avoid duplicated work and potential
inconsistencies; update references in the addCapita call to use the single
parsed output (from Address) and adjust error handling/guards to rely on that
one parse result (symbols: Address, safeParse, v.parse, deriveAssociateId,
addCapita, credential.account).

In `@server/test/hooks/persona.test.ts`:
- Line 489: The inline comment "should poke ETH and the other asset, but skip
WETH" should be converted to all lowercase to meet style guidelines; locate the
inline comment with that exact text in persona.test.ts (near the test case
containing that comment) and change it to "should poke eth and the other asset,
but skip weth".

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/hooks/persona.ts`:
- Around line 49-59: The variable names `p` and `m` in the `marketsByAsset`
assignment are cryptic; update the callback and mapping parameter names to be
descriptive (e.g., rename `p` to `assetsList` or `assetsArray` and `m` to
`assetEntry` or `assetRecord`) inside the `withRetry(...).then(...)` chain so
the call to `publicClient.readContract({ address: exaPreviewerAddress,
functionName: "assets", abi: exaPreviewerAbi })` and the mapping using
`v.parse(Address, ... )` reads clearly (e.g., `.then((assetsArray) => new
Map<Address, Address>(assetsArray.map((assetEntry) => [v.parse(Address,
assetEntry.asset), v.parse(Address, assetEntry.market)])))`). Ensure only the
variable names change, preserving `marketsByAsset`, `withRetry`,
`publicClient.readContract`, `exaPreviewerAddress`, `exaPreviewerAbi`, and
`v.parse(Address, ...)`.
♻️ Duplicate comments (1)
server/test/hooks/persona.test.ts (1)

489-489: Lowercase inline comment for style compliance.

✏️ Suggested tweak
-    // should poke ETH and the other asset, but skip WETH
+    // should poke eth and the other asset, but skip weth

As per coding guidelines, ...

Comment on lines +49 to +60
const marketsByAsset = await withRetry(
() => publicClient.readContract({ address: exaPreviewerAddress, functionName: "assets", abi: exaPreviewerAbi }),
{
delay: 2000,
retryCount: 5,
shouldRetry: ({ error }) => {
captureException(error, { level: "error" });
return true;
},
},
).then((p) => new Map<Address, Address>(p.map((m) => [v.parse(Address, m.asset), v.parse(Address, m.market)])));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use descriptive names for asset mapping variables.

p/m are cryptic; use clearer names for readability and style compliance.

♻️ Suggested rename
-  ).then((p) => new Map<Address, Address>(p.map((m) => [v.parse(Address, m.asset), v.parse(Address, m.market)])));
+  ).then((assetMarkets) =>
+    new Map<Address, Address>(
+      assetMarkets.map((assetMarket) => [
+        v.parse(Address, assetMarket.asset),
+        v.parse(Address, assetMarket.market),
+      ]),
+    ),
+  );

As per coding guidelines, ...

🤖 Prompt for AI Agents
In `@server/hooks/persona.ts` around lines 49 - 59, The variable names `p` and `m`
in the `marketsByAsset` assignment are cryptic; update the callback and mapping
parameter names to be descriptive (e.g., rename `p` to `assetsList` or
`assetsArray` and `m` to `assetEntry` or `assetRecord`) inside the
`withRetry(...).then(...)` chain so the call to `publicClient.readContract({
address: exaPreviewerAddress, functionName: "assets", abi: exaPreviewerAbi })`
and the mapping using `v.parse(Address, ... )` reads clearly (e.g.,
`.then((assetsArray) => new Map<Address, Address>(assetsArray.map((assetEntry)
=> [v.parse(Address, assetEntry.asset), v.parse(Address,
assetEntry.market)])))`). Ensure only the variable names change, preserving
`marketsByAsset`, `withRetry`, `publicClient.readContract`,
`exaPreviewerAddress`, `exaPreviewerAbi`, and `v.parse(Address, ...)`.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 10 additional flags in Devin Review.

Open in Devin Review

Comment on lines +136 to +138
pokeAccountAssets(parsedAccount).catch((error: unknown) => captureException(error));
}
autoCredit(account)
autoCredit(parsedAccount)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Race condition between pokeAccountAssets and autoCredit causes incorrect credit mode detection

In activity.ts, pokeAccountAssets and autoCredit are both called as fire-and-forget operations that run concurrently. This is a behavioral regression from the old code where pokes were awaited before autoCredit was called.

Click to expand

How the bug triggers

  1. pokeAccountAssets(parsedAccount).catch(...) is called at line 136 (fire-and-forget)
  2. autoCredit(parsedAccount).then(...) is called immediately after at line 138 (also fire-and-forget)
  3. Both run concurrently, but autoCredit reads floatingDepositAssets from the chain (server/utils/panda.ts:280-293)
  4. If autoCredit reads before pokeAccountAssets completes depositing assets into markets, it sees floatingDepositAssets === 0
  5. autoCredit incorrectly returns false instead of true

Old vs New behavior

Old code awaited pokes:

const results = await Promise.allSettled([...assets].filter(...).map(...));
// pokes complete here
autoCredit(account).then(...); // reads correct state

New code fires both concurrently:

pokeAccountAssets(parsedAccount).catch(...); // fire-and-forget
autoCredit(parsedAccount).then(...); // runs before poke completes

Impact

Users who should have credit mode automatically enabled may not get it because autoCredit reads stale on-chain state before the poke transaction deposits their assets into the market.

Recommendation: Either await pokeAccountAssets before calling autoCredit, or chain autoCredit to run after pokeAccountAssets completes: pokeAccountAssets(parsedAccount).then(() => autoCredit(parsedAccount)).catch(...)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants