# 直コンでV2 LPを解除する前に｜removeLiquidityの引数と確認順

V2型RouterのremoveLiquidityを直接呼ぶ前に、LP残高・allowance・reserve・totalSupply・minimum amounts・deadline・シミュレーション・receiptと回収不能条件を確認する順番を解説します。

正規URL: https://3mikan.com/archives/1788

著者: みかん
公開: 2021-07-09T19:40:57.000Z
更新: 2026-08-28T16:00:10.000Z

V2型LPを解除すると、LP tokenをburnし、Pairが保有する2種類のtokenを持分に応じて受け取ります。ただし、walletにLPがない、別versionのRouterを見ている、minimum amountsや期限が合わない、token transfer自体が失敗する、といった条件では実行できません。

この記事では、Uniswap V2 Router02の`removeLiquidity`を公開sourceの例に使います。**LPの所在 → contractとABI → 受取見込み → allowance → minimum amountsとdeadline → シミュレーション → receiptと最終balance**の順に確認します。直コンによる資金回収やRug pull回避を保証するものではありません。

<figure>
  <picture>
    <source media="(max-width: 640px)" srcset="/diagrams/direct-liquidity/1788-v2-remove-flow-mobile.svg" type="image/svg+xml" />
    <img src="/diagrams/direct-liquidity/1788-v2-remove-flow.svg" alt="LPの所在確認、受取見込みとminimum amounts、シミュレーション、receiptと最終balanceへ進むV2 LP解除フロー" width="1120" height="520" loading="eager" decoding="async" />
  </picture>
</figure>

## 解除する前にLPの所在を確認する

同じ「流動性position」でも、解除方法は所有stateで変わります。

| 読み取れたstate | 何を持っているか | 次の入口 |
| --- | --- | --- |
| Pairの`balanceOf(user) > 0` | wallet内のV2 LP token | V2 Routerのremove系function候補 |
| MasterChef等の`userInfo(pid,user).amount > 0` | farmへstakedしたV2 LP | farmのwithdraw条件を確認し、LPをwalletへ戻す |
| NFT owner / positionが確認できる | V3型position | Position Manager系contractを確認 |
| どれも確認できない | chain、account、version、移転履歴が違う可能性 | transaction履歴とaddressを再照合 |

MasterChefのpool IDは[deposit記録から`_pid`を照合する方法](/archives/1791)で確認します。stakedしたままのLPをV2 Routerへ渡すことはできません。

## chain・Router・Pair・tokenを固定する

次を一つの確認メモへまとめます。

1. network名とchain ID
2. signing accountとLP owner
3. project公式情報にある対象versionのRouter address
4. Routerが参照するFactory address
5. Pair address、`token0()`、`token1()`
6. token A / Bのofficial addressと`decimals()`
7. Router、Factory、Pair、tokenのverified source
8. proxyなら現在のimplementation、admin、ABI

V2互換を名乗っていても、functionやfee-on-transfer対応、permit、proxy構成が違う場合があります。[contract verification](https://docs.etherscan.io/contract-verification/whats-contract-verification)はsource照合の入口であり、安全証明ではありません。

直コン共通のaddress・proxy・ABI確認は[verified contractとシミュレーションの確認順](/archives/1005)も参照してください。

## `removeLiquidity`の7引数を読む

[Uniswap V2 Router02](https://github.com/Uniswap/v2-periphery/blob/master/contracts/UniswapV2Router02.sol)のERC-20同士の関数は次の形です。

```solidity
function removeLiquidity(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
) public returns (uint amountA, uint amountB);
```

| 引数 | 意味 | 確認する値 |
| --- | --- | --- |
| `tokenA` / `tokenB` | Pairを構成する2 token | Pairの`token0` / `token1`とofficial address |
| `liquidity` | burnするLPのraw amount | LP `decimals()`と`balanceOf(user)` |
| `amountAMin` / `amountBMin` | 受取量の下限 | reserve、total supply、token仕様、quote時block |
| `to` | 2 tokenの受取先 | 意図したaccount / contract |
| `deadline` | 実行を許す期限 | 最新block timestampと意図した有効時間 |

Router02では、PairのLP tokenをRouterからPairへ移し、Pairの`burn(to)`を呼びます。返り値のtoken順を`tokenA` / `tokenB`へ対応させ、各minimumと比較します。

片方がnative tokenなら`removeLiquidityETH`系、fee-on-transfer tokenなら対応variantが用意される場合があります。名前が似ていてもsignature、返り値、処理が違うため、対象RouterのsourceとABIを基準にします。

## LP残高とallowanceをraw amountで読む

通常の`removeLiquidity`では、PairというERC-20 contractでRouterへのallowanceが必要です。

```text
Pair.balanceOf(user)                 >= liquidity
Pair.allowance(user, Router)         >= liquidity
transaction.to                       = Router
approve transaction.to               = Pair
approve argument spender             = Router
```

LP tokenの表示桁を18と決め打ちせず、Pairの`decimals()`を読みます。unlimited approveを既定にせず、解除量、既存allowance、token固有のapprove動作を確認します。

`removeLiquidityWithPermit`系は、LP allowanceの代わりにpermit署名を使うvariantです。署名だから安全、gas表示がないから無料、とは判断できません。domain、chain ID、verifying contract、owner、spender、value、nonce、deadlineと、対象Pairがpermitを実装しているかを確認します。

## reserveとtotal supplyから受取見込みを読む

単純化したV2 Pairでは、持分比率は次の形で確認できます。

```text
share        = burnするLP / total LP supply
amountAQuote = reserveA × share
amountBQuote = reserveB × share
```

固定例を使います。

| state | 値 |
| --- | ---: |
| reserve A / B | 1,000 A / 500 B |
| total LP supply | 1,000 LP |
| burnするLP | 100 LP |
| 単純な受取見込み | 100 A / 50 B |

token Aが6 decimals、token Bが18 decimalsならraw amountはそれぞれ`100000000`と`50000000000000000000`です。

これは確認用の基準値です。実行前のswapでreservesが変わる、feeがPairへ蓄積する、tokenがtransfer feeやrebasingを持つ、protocol固有処理がある場合は結果が変わります。Pairの現在stateと対象Router sourceを読みます。

## minimum amountsは受取下限として作る

`amountAMin` / `amountBMin`を0にすると、そのtoken側の受取下限を外します。

先ほどのquoteへ「1%の差を許容する」という仮定だけを置くと、次の計算です。

```text
amountAMin = 100 × (1 - 0.01) = 99 A
amountBMin =  50 × (1 - 0.01) = 49.5 B
```

1%は推奨値ではありません。許容範囲を広げればrevertしにくくなる一方、少ない受取量を受け入れる幅も広がります。quoteのblock、reserves、tokenのtransfer仕様、市場変動を確認し、raw unitsへ変換します。

slippageを上げて不明なtoken制限を解決しようとせず、[Price impact・slippage・MEVの違い](/archives/741)も分けて確認してください。

## `deadline`は古い入力を止める条件

Router02は`deadline < block.timestamp`ならrevertします。Explorerへ遠い未来のtimestampを貼るのではなく、対象chainの最新block timestampへ意図した有効時間を加えます。

- Unix秒か
- 対象chainのblock timestampを基準にしたか
- quoteや確認内容を有効とみなせる時間か
- 署名直前にまだ期限内か

期限を延ばすことは、minimum amountsやcontractの安全性を改善しません。

## 固定例で成功・失敗を比較する

固定したV2 stateで100 LPを解除すると、単純計算では100 A / 50 Bです。minimumが99 A / 49.5 B、LP allowanceが100 LP、期限内なら、入力条件を満たします。

次の条件は別々に止めます。

| 変更した条件 | 判定 |
| --- | --- |
| Pair allowanceが0 | RouterがLPを移せず失敗 |
| walletのLP balanceが99、解除量が100 | LP不足 |
| `amountAMin`が101 A | 見込み100 Aが下限未満 |
| `deadline`が最新blockと同じか過去 | 期限切れ |
| Pairのtoken addressが確認メモと違う | 対象contract不一致として中止 |

この成功判定は、その入力とstateを使ったV2型の計算結果です。実transactionのreceiptでも、deployed contractの安全性証明でもありません。

## シミュレーションでrevertと入力を先に確認する

対象accountを`from`に指定し、同じRouter address、calldata、`value`、block条件で`eth_call`します。

確認するものは次の通りです。

- RPCのchain IDとblock number
- Router addressとcurrent ABI
- decodeした`removeLiquidity`の7引数
- LP balance / allowance、reserves、total supply
- token A / Bの受取見込みとminimum amounts
- resultまたはrevert data

シミュレーション成功後にもreserves、allowance、deadline、proxy implementationは変わりえます。成功は本番実行や資金回収の保証ではありません。

## 署名前・receipt・final stateを別々に見る

署名前はnetwork、account、transaction `to`、function、7引数、native tokenの`value`、spending cap、warningを照合します。walletがcalldataをdecodeできない場合は先へ進まず、別のdecoderとverified ABIで確認します。

送信後は次の順に確認します。

1. receiptの`status`、block number、transaction hash
2. transaction `to`とinput calldata
3. Pair addressが発行した`Burn`、`Transfer`等のlogs
4. walletのLP balance減少
5. recipientのtoken A / B balance増加
6. Pair reservesと残allowance

receipt成功だけ、eventだけ、画面表示だけでは証拠が足りません。[receipt・calldata・logs・stateの確認順](/archives/6001)で発行元addressも含めて確認します。

## 解除できない条件を分ける

front-endが停止していても、contractが動作し、正しいLPをwalletが所有し、Router / Pairの条件を満たせば、公開interfaceから状態を確認できる場合はあります。しかし次の状態では、直コンでも回収を保証できません。

- LPがfarmやvaultへ移っており、walletが所有していない
- withdraw経路がない、paused、role制限、lock期間がある
- proxy implementationやRouterが悪意あるcodeへ変わった
- Pair reservesがdrainされ、LPに対応する資産がない
- tokenがblacklist、transfer停止、fee、malicious callbackを持つ
- Pair / Router / token sourceが未検証で、ABIを安全に特定できない
- LP token自体が別Pairまたは偽物
- gas tokenがなく、transactionを送れない

[ethereum.orgのsmart contract security](https://ethereum.org/developers/docs/smart-contracts/security/)が示すように、access controlやemergency stopなどの実装条件はcontractごとに異なります。追加approveや不明な署名を急がず、chain、address、source、transaction履歴、receipt、allowanceを保存して状況を分けます。

## 症状別チェック表

| 症状 | 最初に読む値 | 次の確認 |
| --- | --- | --- |
| LPが0 | Pair `balanceOf(user)` | chain、account、farm `userInfo`、NFT position |
| allowance不足 | Pair `allowance(user, Router)` | spender、解除量、permit variant |
| minimum系revert | reserves、total supply、quote block | decimals、token順、transfer fee |
| transfer失敗 | token source、paused / blacklist | Pair balance、Router variant、revert data |
| receipt成功だが残高が違う | logsの発行元とrecipient | token fee、別account、最終state |
| sourceが違う | official address、proxy implementation | 書き込みを止め、ABIを推測しない |

## 最終チェックリスト

- wallet内V2 LP、staked LP、V3 NFT positionを区別した
- chain、Router、Factory、Pair、token A / Bを公式情報で照合した
- verified source、proxy、implementation、ABI、adminを確認した
- LP balance、decimals、allowance、reserves、total supplyを読んだ
- 受取見込みとminimum amountsをraw unitsで記録した
- minimum amountsを0、deadlineを任意の遠い値にしていない
- unlimited approveを既定にしていない
- シミュレーション、署名、receipt、logs、最終balanceを分けた
- front-end停止やRug pull時の回収を保証していない

V2 LPを作る側は[addLiquidityの引数と確認順](/archives/1663)、LPをfarmから戻す前のpool ID確認は[MasterChefの`_pid`を照合する方法](/archives/1791)へ進んでください。

## 確認した一次情報

- [ethereum.org Interacting with smart contracts](<https://ethereum.org/developers/docs/smart-contracts/interacting/>): 確認日 2026-08-29
- [ethereum.org Smart contract security](<https://ethereum.org/developers/docs/smart-contracts/security/>): 確認日 2026-08-29
- [Etherscan What’s Contract Verification](<https://docs.etherscan.io/contract-verification/whats-contract-verification>): 確認日 2026-08-29
- [ERC-20 Token Standard](<https://eips.ethereum.org/EIPS/eip-20>): 確認日 2026-08-29
- [Uniswap V2 Router02 source](<https://github.com/Uniswap/v2-periphery/blob/master/contracts/UniswapV2Router02.sol>): 確認日 2026-08-29
- [Uniswap V2 Pair source](<https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol>): 確認日 2026-08-29
