Skip to content

fix(dapp): say what a cash-out is short of, and count relayed evm transfers - #159

Merged
0xDeon merged 6 commits into
mainfrom
fix/cashout-shortfall-and-relayed-evm
Nov 6, 2026
Merged

0xDeon merged 6 commits into
mainfrom
fix/cashout-shortfall-and-relayed-evm

Conversation

@0xDeon

@0xDeon 0xDeon commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

A Solana cash-out short of SOL or USDC only said "insufficient balance"

On a Solana cash-out the deposit pays the network fee. When the payout address has no USDC account yet, it also pays about 0.00204 SOL rent to open one. Nothing checked either cost, so a wallet short of SOL or USDC showed only Solflare's "Insufficient balance". That was the report: a SOL→USDC swap, then a cash-out of 0.69952 USDC.

Fix: the cash-out build route now calls the chain deposit's assertCanPay before building. On Solana it reads:

  • the USDC held
  • the SOL held
  • whether the payout address already has its USDC account

It then names the shortfall against what is held, for example "Sending this needs about 0.002054 SOL: 0.000015 for the network fee and 0.00203928 to open the payout address's USDC account. You hold 0.0004 SOL."

A read that fails doesn't refuse. Sends and 1Click swaps already have these checks in prepare.ts, so only the cash-out path changes.

Every chain

Chain Check before the wallet is asked
Avalanche Already checked AVAX for gas in its build
Solana Fixed here
Sui, NEAR, Stellar Not yet; a follow-up adds assertCanPay for each

A relayed or smart-account EVM transfer was misread

Some wallets send through a relayer or an EntryPoint, for example a smart account, or MetaMask paying gas in a token. The receipt's from and to are then not the account and the token, so:

  • the Avalanche cash-out answered "not this order's deposit" while the USDC had in fact moved
  • the 1Click transfer stayed pending

Fix: a token transfer now counts when the token contract's own Transfer log shows exactly this amount from the account to the deposit address, whoever sent it. A native-coin send is unchanged: it has no log to read, so it stays pending when relayed.

Found by checking every chain for the Phantom issue. Sui, NEAR and Stellar don't compare gas, fees, nonce or sequence, so they aren't affected by that issue.

Checks

  • typecheck passes, and lint and prettier pass on the changed files
  • 352 unit test files, 8,206 tests: all pass except one timing-sensitive route test, which passes when run alone on old and new code
  • new tests cover:
    • Solana: USDC shortfall, SOL shortfall with and without the payout account, and an unreadable balance
    • EVM: relayed and smart-account transfers counted by their log, with no-log cases still refused

…s asked

A Solana cash-out's deposit pays the network fee and, when the payout address has no USDC
account yet, about 0.00204 SOL to open one. Nothing checked either, so a wallet short of SOL or
USDC only said "insufficient balance".

The cash-out build route now asks the chain's deposit first. On Solana it reads the token held,
the SOL held, and whether the payout account exists, and says what is needed against what is
held. A read that fails doesn't refuse. Avalanche's build already checked AVAX for gas.
… transfer log

A smart account, or MetaMask paying gas in a token, sends through a relayer or an EntryPoint,
so the receipt's from and to are not the account and the token. The Avalanche cash-out then
answered "not this order's deposit" while the USDC had moved, and the 1Click transfer stayed
pending.

A token transfer now counts when the token contract's own Transfer log shows exactly this
amount from the account to the deposit address, whoever sent the transaction. A native coin
send is unchanged, since it has no log to read, and stays pending when relayed.
@netlify

netlify Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for useintentadapp ready!

Name Link
🔨 Latest commit 5ffdd1c
🔍 Latest deploy log https://app.netlify.com/projects/useintentadapp/deploys/6aedfdd60ab14d4d20fa67fa
😎 Deploy Preview https://deploy-preview-159--useintentadapp.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for useintent ready!

Name Link
🔨 Latest commit 5ffdd1c
🔍 Latest deploy log https://app.netlify.com/projects/useintent/deploys/6aedfdd6a102e05e78fb84fa
😎 Deploy Preview https://deploy-preview-159--useintent.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

… it was made with

A Sui cash-out of 0.689418 USDC showed ₦904.04 paid, while ₦877.08 reached the bank. Weave's
own order record has both: its bridge to Base took 3.4% where it quoted 2%, and the payout rate
came in lower, so it paid about 3% under its estimate.

The order's paid amount is now read from Weave once it is paid, kept beside the estimate, and
shown on the receipt, with the estimate as its own row. The review screen no longer says the
final amount "can differ slightly".
…from

Weave runs cash-outs on Stellar, Solana, Avalanche, Sui and NEAR, and was listed on none of
them. Each now has a Weave row with its logo, marked as running. A cash-out runs through its own
route rather than a chain venue, so the checks that tie running rows to wired venues leave Weave
out, and a new test holds its rows to exactly the cash-out chains.
The receipt now shows the one figure Weave says reached the account, after every fee, and no
estimate beside it. Until Weave gives that figure it reads "Confirming the amount paid", and the
card reads the order a few more times for it. While a payout is still on its way the figure is
marked approximate, since no provider knows the exact amount before it pays.
…account

nairaOffers asks each provider that can pay naira from a chain, and bestNairaOffer picks the one
paying most, preferring Linq's locked amount over a Weave quote within half a percent.

Nothing calls it yet, so no screen or step changes. On Stellar only Linq can pay: Weave's Stellar
orders route through LI.FI with no deposit address. Linq on Sui and Solana joins once an order on
each has been seen, since its docs show no response for them.
@0xDeon
0xDeon merged commit 14cf6a7 into main Nov 6, 2026
11 checks passed
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.

1 participant