Skip to content

feat: ghost adapter - #4

Open
znevo wants to merge 2 commits into
KyberNetwork:mainfrom
swaps-xyz:zev/ghost
Open

feat: ghost adapter#4
znevo wants to merge 2 commits into
KyberNetwork:mainfrom
swaps-xyz:zev/ghost

Conversation

@znevo

@znevo znevo commented Jun 12, 2026

Copy link
Copy Markdown

Ghost adapter

Adds a DEX adapter for Ghost's cross-collateral router, following the
UniswapV2/V3 adapter pattern.

executeGhost approves the gross amountIn to the source router and calls
transferRemoteTo, crediting recipient on the target side. amountIn is the
total input budget (principal + fee); the principal forwarded to the router is
derived on-chain rather than encoded in calldata, since the real amountIn
can differ at execution time.

GhostQuoter resolves Ghost's fee contract (cross-collateral routing → linear /
offchain-quoted-linear, with wildcard and default-router fallbacks) and inverts
the fee curve fee(x) = min(maxFee, x·maxFee / (2·halfAmount)) to find the
largest principal where principal + fee(principal) ≤ amountIn. Output and
unused input are read from balance deltas; amountUnused is at most 1 wei of
integer-division dust.

Calldata is (address sourceRouter, address targetRouter).

  • No native-token leg.
  • Uses TokenHelper and CalldataDecoder per the contributing guidelines.
  • Tests in test/adapters/ghost/GhostAdapter.t.sol.
  • "Allow edits from maintainers" is enabled.

Comment thread test/adapters/ghost/GhostAdapter.t.sol Outdated
adapter = new GhostAdapter();
}

function test_executeGhost(uint256 amount, bool usdcToUsdt) public {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @znevo, please check the other adapter test files to follow the existing pattern. The fuzzed value should be amountIn and it should be passed to the execute function argument, not used as the Ghost principal amount.

For Ghost, amountIn is the total input budget including fees. The principal amount passed to transferRemoteTo should be derived from amountIn, not encoded directly in data, because the actual amountIn may change on-chain.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks @minhtr09, this is addressed now.

I'd originally baked the amount into data to keep the adapter light, on the assumption that if the on-chain amountIn came in different it would just revert the same way any swap does on slippage. Deriving it on-chain avoids that failure mode entirely, so I've taken that route:

  • data now carries only (sourceRouter, targetRouter). The principal is derived on-chain in GhostQuoter from amountIn, so it tracks the real on-chain amountIn rather than a value baked into calldata.
  • The fuzz test now passes amountIn to executeGhost, following the UniswapV2 adapter test.

@LuuOW LuuOW left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Technical audit: implementation details and logic patterns verified for system integrity.

@minhtr09 minhtr09 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @znevo

@znevo
znevo force-pushed the zev/ghost branch 3 times, most recently from ad5285d to 265904f Compare June 29, 2026 19:41
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.

3 participants