Summary
Sponsored writes (save_deposit, repay_debt, send_transfer, swap_execute) fail at the Enoki execute step with:
gas_station_error: Request to gas station failed
{"error":"Invalid bcs bytes for TransactionData"}
…whenever the user's source funds live in their Sui address balance (the new gasless-stablecoin / Address Balances feature) rather than as coin objects.
Root cause
@mysten/sui@2.17.0's coinWithBalance resolver (transactions/intents/CoinWithBalance.mjs) sources from the address balance when addressBalance >= totalRequired, emitting 0x2::coin::redeem_funds + a FundsWithdrawal reservation input. That FundsWithdrawal is a new TransactionData field. Enoki's sponsor endpoint accepts the kind bytes (200), but its gas station can't deserialize the resulting TransactionData → Invalid bcs bytes.
Notably coinWithBalance takes the address-balance branch even when coin objects exist, as long as the address balance alone covers the amount.
Evidence
devInspectTransactionBlock of the exact composed save_deposit kind bytes succeeds on mainnet → the PTB is valid on-chain; the fullnode parses FundsWithdrawal fine. Only Enoki's gas station rejects it.
- Repro with a wallet holding coin objects + $0 address balance → clean PTB (
MergeCoins/SplitCoins), no FundsWithdrawal, would sponsor fine.
- Repro wallet with 100% address-balance USDC → PTB contains
coin::redeem_funds + FundsWithdrawal → Enoki rejects.
- Enoki app config verified healthy (keys unchanged, gas pool funded, 47 prior sponsored txs OK) — not a config/credit issue.
Why this matters
Gasless stablecoin transfers (Audric Pay receives, bridges, Circle) deposit USDC into the recipient's address balance. As these spread, more wallets become address-balance-only, and every sponsored write against those funds silently breaks.
Options
- A. Enoki adds
FundsWithdrawal TransactionData support — the real, permanent fix. On Mysten's side (escalation filed separately). Tracks the address-balance-only case.
- B. Self-funded fallback when the user holds SUI (sign + submit to fullnode directly, bypass Enoki). Stopgap; breaks gasless promise; most zkLogin users have no SUI.
- C. SDK coin-object-only sponsored path (this issue's deliverable). In
sponsoredContext, source from coin objects only; if insufficient, throw a clear typed error (ADDRESS_BALANCE_UNSPONSORABLE) instead of the cryptic Enoki failure.
Fix C — planned scope
- Centralize in
packages/sdk/src/wallet/coinSelection.ts (selectAndSplitCoin): when sponsoredContext, fetch coin objects via getCoins → merge → split; never call coinWithBalance.
- Throw
ADDRESS_BALANCE_UNSPONSORABLE with actionable copy when coin objects don't cover the amount.
- Engine preflight/guard surfaces the message before the confirm card.
- Covers
save_deposit / repay_debt / send_transfer (all via selectAndSplitCoin). Follow-ups: swap_execute (Cetus addSwapToTx uses coinWithBalance directly) + SUI path (selectSuiCoin).
Limitation: C does NOT unblock address-balance-only wallets (no coin objects to use). Those depend on A or B.
Refs
Summary
Sponsored writes (
save_deposit,repay_debt,send_transfer,swap_execute) fail at the Enoki execute step with:…whenever the user's source funds live in their Sui address balance (the new gasless-stablecoin / Address Balances feature) rather than as coin objects.
Root cause
@mysten/sui@2.17.0'scoinWithBalanceresolver (transactions/intents/CoinWithBalance.mjs) sources from the address balance whenaddressBalance >= totalRequired, emitting0x2::coin::redeem_funds+ aFundsWithdrawalreservation input. ThatFundsWithdrawalis a newTransactionDatafield. Enoki's sponsor endpoint accepts the kind bytes (200), but its gas station can't deserialize the resultingTransactionData→Invalid bcs bytes.Notably
coinWithBalancetakes the address-balance branch even when coin objects exist, as long as the address balance alone covers the amount.Evidence
devInspectTransactionBlockof the exact composedsave_depositkind bytes succeeds on mainnet → the PTB is valid on-chain; the fullnode parsesFundsWithdrawalfine. Only Enoki's gas station rejects it.MergeCoins/SplitCoins), noFundsWithdrawal, would sponsor fine.coin::redeem_funds+FundsWithdrawal→ Enoki rejects.Why this matters
Gasless stablecoin transfers (Audric Pay receives, bridges, Circle) deposit USDC into the recipient's address balance. As these spread, more wallets become address-balance-only, and every sponsored write against those funds silently breaks.
Options
FundsWithdrawalTransactionData support — the real, permanent fix. On Mysten's side (escalation filed separately). Tracks the address-balance-only case.sponsoredContext, source from coin objects only; if insufficient, throw a clear typed error (ADDRESS_BALANCE_UNSPONSORABLE) instead of the cryptic Enoki failure.Fix C — planned scope
packages/sdk/src/wallet/coinSelection.ts(selectAndSplitCoin): whensponsoredContext, fetch coin objects viagetCoins→ merge → split; never callcoinWithBalance.ADDRESS_BALANCE_UNSPONSORABLEwith actionable copy when coin objects don't cover the amount.save_deposit/repay_debt/send_transfer(all viaselectAndSplitCoin). Follow-ups:swap_execute(CetusaddSwapToTxusescoinWithBalancedirectly) + SUI path (selectSuiCoin).Limitation: C does NOT unblock address-balance-only wallets (no coin objects to use). Those depend on A or B.
Refs
Invalid bcs byteserror class)