Merge
Two Snowmen on the same pair become one. The rules the contract enforces, and why.
Rules
| Rule | Reason |
|---|---|
| same collection | LP positions on different pools cannot be combined |
| caller owns both | no approvals, no third-party merges |
| survivor keeps id, gene, history | generation = max + 1, merges = a + b + 1 |
| donor burned, id never reusable | a reminted id would inherit the old account |
The sweep
The collection takes custody of the donor NFT at the top of the call, which makes it the account's owner and therefore a valid signer for execute. It then moves every listed ERC-20, every listed ERC-721 position and any native ETH to the survivor's account, and burns the donor.
merge(survivor, donor) // native ETH only
merge(survivor, donor, address[] erc20s, NftPosition[] positions)
for each asset: before = balance(survivor)
donorAccount.execute(transfer)
require(balance(survivor) - before == expected) // not the execute boolERC-6551 footguns
An uninitialised proxy account's execute returns success while moving nothing, so the contract verifies by post-transfer balance and reverts with PhantomTransfer. Positions move with transferFrom, not safeTransferFrom, because a receiver hook on the account could run arbitrary code mid-merge; the ownerOf check proves delivery anyway. Sweeping the collection itself is refused to avoid ownership cycles, which brick an account rather than merely lock it.