Skip to content

feat(meteora): auto-create destination ATA in DAMMv1Pool.invoke_swap #64

Description

@anil-rome

Context

Companion to #63 (SPL_ERC20 transfer / mint_to auto-ensure recipient ATA). Same UX bug, different code path.

When a user swaps on a Meteora pool and the swap output token's destination ATA doesn't exist for the user, the Meteora-side CPI fails. The pool's invoke_swap accepts the destination ATA as a caller-supplied parameter and delegates the actual SPL move to Meteora's on-chain Solana program — so the auto-ensure logic added to SPL_ERC20._transfer in #63 doesn't kick in here. Romeswap routes through SPL_ERC20.transfer and is auto-fixed by #63; Meteora is the only remaining path.

Reproducer

A user with no prior WUSDC / WETH ATA on a chain swaps an unrelated token → wUSDC on a Meteora pool. The Meteora CPI fails because the user's wUSDC destination ATA doesn't exist. Same root cause as #63's MetaMask grey-out symptom — the difference is that the failure is inside the Meteora program rather than at the Solidity require().

Proposed fix

contracts/meteora/damm_v1_pool.sol:

  1. Add immutables address wrapper_a and address wrapper_b (the EVM addresses of the SPL_ERC20 wrappers for both pool tokens). Set in the constructor.
  2. MeteoraDAMMv1Factory.create_pool (or wherever the pool is constructed) looks the wrappers up via ERC20SPLFactory.token_by_mint(mint) and passes them into the pool constructor.
  3. In invoke_swap, before the Meteora CPI:
    address out_wrapper = (in_token == PoolToken.A) ? wrapper_b : wrapper_a;
    SPL_ERC20(out_wrapper).ensure_token_account(msg.sender);
    Idempotent — no-op when the recipient ATA already exists; ~0.002 SOL one-time rent paid by msg.sender when it doesn't. Same model as fix(erc20spl): auto-create recipient ATA on transfer / mint_to #63.
  4. If invoke_add_liquidity / invoke_remove_liquidity accept LP-token destination ATAs, apply the same ensure for the LP wrapper.

Implications

  • Pool constructor signature changes. Every existing pool needs redeployment (marcus has 2 pools per deployments/marcus.json) and the factory needs a new bytecode pin.
  • LP positions in current pools. Holders need to either be migrated or notified — same constraint as any pool redeploy.
  • Tests. Add a swap-to-fresh-address case to tests/damm_v1_pool.integration.ts mirroring the new cases in tests/erc20spl_factory.integration.ts from fix(erc20spl): auto-create recipient ATA on transfer / mint_to #63.

Out of scope

Triggered separately for review by Ivan — keeping #63 focused on the wrapper-side fix so it can ship today without the redeploy churn.

🤖 This response was generated by Claude Code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions