3MIKAN
仮想通貨DeFi直コン

Tokenを送れない・売れない原因|transfer fee・blacklist・rebasingを確認

ERC-20の送金・swap失敗をfee-on-transfer、rebasing、pause、blacklist、maxTx、sell restriction、liquidity、slippageへ分けてread-onlyで確認します。

3MIKANのブランドキャラクターが、同じ外箱でも通常配送・手数料分岐・残高伸縮・停止ゲートなど内部条件が異なるtokenを検査する記事画像

tokenを送れない、受取額が減った、買えたのに売れない、swapの見積もりだけ失敗する。これらを全部「slippage」や「walletの不具合」にまとめると、原因が分からないままapprovalや失敗transactionを重ねることになります。

最初に分けるのは、token contract自身のtransfer条件poolのliquidityとprice impactrouterのminimum received・deadlinewalletやindexerの表示です。Explorerのverified表示、過去のbuy成功、symbol、detectorの単独判定は、現在のsellや送金が成功する証明になりません。

この記事は公式仕様とローカルの合成contractだけを使います。実在tokenの売買、wallet接続、approval、署名、送金、swap、bridge、公開transactionは行いません。

原因が分かるまで止める操作

次の操作は、調査ではなく新しいriskを増やします。

  • 失敗のたびにslippageを上げる
  • 別routerや未知の「sell専用site」へwalletを接続する
  • contractを直接callすれば回避できるという案内に従う
  • approvalを取り消しては付け直す操作を繰り返す
  • token名やerror messageに書かれたURLを開く
  • 「tax」「unlock fee」「回収用gas」を先払いする
  • seed phrase、private key、remote-control、screen sharingを渡す

まず公開情報だけを保存し、どの層で止まったかを分類します。秘密情報、署名、追加送金は証拠に含めません。

一つの調査メモへ固定する値

保存する値 混同しないもの
identity chain ID、full token contract、確認block number / hash symbol、name、icon
account full sender / recipient、raw balance、decimals walletの丸めた表示だけ
transaction tx hash、receipt status、called address、calldata selector、revert data 一覧の「失敗」表示だけ
transfer accounting sender減少、recipient増加、fee collector / burn、Transfer logs swapのslippage
authority proxy、implementation、owner、roles、admin、timelock、upgrade event verified badge
market pair / pool、route、active liquidity、price impact token contractのblacklist
router exact input / output、minimum received、deadline、allowance tokenのtransferability
observation RPC、block、fromto、amount、route、確認時刻 将来のtransaction保証

tokenがwalletへ出ないだけなら、先にchain・contract・decimalsから表示を確認する手順を使います。balanceOfはあるのに一覧へ出ない問題と、transfer自体がrevertする問題は別です。

standard ERC-20を基準に差を見る

ERC-20は、transfertransferFromboolを返すinterface、残高、allowance、Transfer / Approval eventを定めています。呼び出し側はfalseを無視してはいけません。

ただしERC-20という名前だけで、次の機能がないことまでは保証されません。

  • transfer時のfeeやburn
  • balanceを伸縮させるrebase / shares accounting
  • pause、blacklist、allowlist
  • 1回の最大量、cooldown、holding period
  • 特定pool、router、sender、recipientだけを対象にする条件
  • proxy upgradeで後から変わる実装

基準にする観測は単純です。固定blockの前後で、senderがamount減り、recipientが同じamount増え、成功を示すreturnと対応するeventがあるかを見ます。一つでも違えば、すぐに悪意や詐欺と断定せず、差の説明へ進みます。

6種類のtoken挙動を分ける

case decisive evidence 主な影響 一つのsignalで断定しないこと
standard sender debitとrecipient creditがamountに一致 通常のintegration前提 安全・公式・十分なliquidity
fee-on-transfer debit、credit、collector / burnが同じtransferで説明できる 受取減、quote差、router非対応 差額はすべて既知fee
rebasing / shares sharesやindexは固定・変化し、表示balanceがtransfer外で変わる wallet / indexer差、pool accounting差 eventがないから資産消失
pause / blacklist current state、role、specific revert、対象account 全体停止またはaccount別停止 revert文字列だけで対象contractを特定
maxTx / cooldown threshold、last action、block / time条件 amountや時点で結果が変わる 小額成功なら将来も成功
sell restriction configured pool / recipientでだけ条件が変わる buyと通常transferは通ってもsell失敗 1回の失敗だけでhoneypot確定

「honeypot」は市場やdetectorで広く使われますが、この記事では一つのlabelにしません。pool宛だけのrestriction、可変fee、owner変更可能なstate、router非対応、浅いliquidityを別々に保存します。

fee-on-transferはamount・受取・feeを3列にする

fee-on-transferでは、1,000を指定してもrecipientが975、collectorまたはburnが25になるような実装があります。確認式は次の形です。

sender debit = recipient credit + explained fee or burn + unexplained difference

ローカル例では1,000 = 975 + 25 + 0となりました。ところがrecipientが950で、確認できるfeeが25しかなければ、残る25を「たぶんtax」と補完しません。fee率、対象address、除外list、collector、burn、event、block-specific stateを読み、未説明差として止めます。

token feeとDEX pool feeも別です。token contractがtransfer時に徴収するfeeを、poolが価格曲線やprotocol feeとして取る額と合算しないでください。

Uniswap V2 Router02にはfee-on-transfer向けの別functionがありますが、どのtoken実装も扱える保証ではありません。Uniswap V3 coreはsender減少とrecipient増加がtransfer amountに一致するtokenを前提としており、Uniswap Labsはfee-on-transfer、rebase、reflection tokenをV3 / V4で非対応と案内しています。routerを変える前に、対象protocolとversionの公式support境界を確認します。

rebasingはshareと表示balanceを分ける

rebasing tokenには複数の設計があります。ローカル例ではaccountがshareを持ち、次の式で表示balanceを計算しました。

visible balance = shares × scaling factor

同じ1,000 sharesでもfactorが変わると、表示balanceが1,250または800になります。この例ではuser transferを実行しなくてもbalanceが変わります。すべてのrebase、reflection、interest-bearing tokenがこの式やevent設計を使うわけではありません。

確認するのはtotal supplyだけではなく、accountのshares / credits、scaling factor / index、rebase event、walletとindexerの更新時点、pool contractが保持するbalanceです。Transfer eventがない変化を即座に盗難とみなさず、逆にwallet表示だけをon-chain balanceの正本にしません。

pause・blacklist・maxTx・cooldownをstateへ対応させる

OpenZeppelinのERC20Pausableはtransfer、mint、burnをpause条件へ結び付けますが、公開pause関数と権限設計は利用側contractが定義します。OwnableAccessControl、custom modifierでは、同じ「admin」に見えても変更できる項目が異なります。

次の順でspecific stateとfailureを対応させます。

  1. call先がtoken、router、proxyのどれかを確認する
  2. proxyならcurrent implementationを固定blockで解決する
  3. revert dataをdecodeし、source内の条件へ対応させる
  4. pause、blacklist、allowlist、maxTx、cooldown、pool設定を同じblockで読む
  5. owner、role admin、timelock、upgrade authorityが誰かを確認する
  6. 過去eventから、設定がいつ変更されたかを確認する

「pause functionがABIにない」だけでは、pause相当のcustom条件がない証拠になりません。反対にpaused()が存在しても、それが今回通ったtransfer pathへ適用されるかはsourceとcall pathで確認します。

buyできてもsellできる証明にはならない

buyとsellでは、token transferのsenderとrecipientが逆になります。custom logicがconfigured pool、router、recipient、sender、amount、blockを条件にすると、poolからuserへのbuyは通り、userからpoolへのsellだけrevertすることがあります。

同様に、wallet間の小額transferが通っても、pool宛、取引所deposit contract宛、bridge contract宛が通る保証にはなりません。過去成功を安全証明にせず、現在のimplementationとstateを固定します。

ただしsell失敗一回だけで、malicious honeypot、違法、回収不能と断定もしません。allowance不足、deadline、minimum output、route変更、liquidity不足、network fee不足、paused poolなど別layerの失敗もあります。detectorの結果は調査候補であり、contract identityとraw evidenceの代わりではありません。

liquidity・price impact・slippage・deadlineはmarket / router層

signal layer 確認する値 token restrictionとの違い
insufficient liquidity market pool、active liquidity、route、pair identity token transfer前にquoteが成立しない場合がある
high price impact market input size、mid-price、average execution price 注文自身がquoteを動かす
minimum received router quote、許容差、expected / minimum output 実行時outputが境界を下回る
deadline router signed requestのdeadline、block timestamp 時間条件でrouterが止める
pause / blacklist / maxTx token implementation state、account、amount、revert slippageを変えても条件自体は直らない
transfer fee token + integration debit、credit、fee、router対応 quote計算と実受取の前提が変わる

Price impactとslippageの計算、minimum output、MEVはDEXのPrice impactとslippageを分ける手順で確認できます。oracleやTWAPがtoken transfer制限を示すわけでもないため、価格sourceを調べる場合はdecimals・staleness・TWAP・L2停止の検証へ分けます。

false・empty return・revertは同じ失敗ではない

EVM callの結果には少なくとも次の違いがあります。

  • callがrevertし、revert dataやcustom errorを返す
  • call自体はrevertしないが、ERC-20のboolとしてfalseを返す
  • callはrevertせずreturn dataが空で、古いnon-standard tokenとしてintegration側が扱う
  • trueを返しても、受取balanceやeventが想定と一致しない

ERC-20はcallerがfalseを処理するよう求めます。OpenZeppelinのSafeERC20falseをfailureとして扱い、return dataが空でrevertしないtokenもsupportします。したがって「transaction statusがsuccess」「EVMがrevertしなかった」「returnがtrue」「recipient balanceが増えた」を別々に保存します。

custom errorの名前も、信頼できるsourceとselector対応があって初めて意味を持ちます。walletやsiteが整形した文字列だけで、どのcontractがどの条件を判定したか決めません。

verified sourceからproxy・権限・upgradeを追う順番

Explorerのverified sourceが直接示すのは、提示sourceとdeployed bytecodeの対応です。売却可能、安全、公式、upgrade不能という保証ではありません。再構築とmatchの境界はcompiler・constructor・proxyまで確認する手順で扱っています。

調査順は次のとおりです。

  1. chain ID、full token address、block number / hashを固定する
  2. eth_getCodeでproxyかdirect implementationかを確認する
  3. ERC-1967ならimplementation、beacon、admin slotとupgrade eventを読む
  4. implementationのverified source、compiler、runtime bytecodeを照合する
  5. transfer / transferFromから内部update path、fee、restrictionを追う
  6. owner、roles、role admin、timelock、multisig、upgrade authorityを列挙する
  7. pool / router address、liquidity、price impact、minimum outputを別recordへ分ける
  8. fixed inputのread-onlyシミュレーションを行い、block identityとreturn / revertを保存する

proxyのstorageはproxy側にあり、logicはimplementation側にあります。implementation sourceだけを読み、proxy stateやadminを見ない調査は不完全です。upgrade後は、以前のシミュレーションやsource reviewを現在の保証として使いません。

シミュレーション成功は将来のtransaction成功ではない

eth_callは公開transactionを送らずに、指定blockのstateに対してcallを評価できます。再現条件には次を含めます。

chainId / RPC identity / block number / block hash
from / to / value / calldata / gas assumptions
token implementation / token state / balance / allowance
pool / route / input / minimum output / deadline
raw return data or raw revert data

同じinputでも、ownerがfeeやblacklistを変更した、proxyがupgradeされた、balanceやallowanceが変わった、pool priceが動いた、deadlineを過ぎた、別block producer環境になった場合は結果が変わります。シミュレーション成功は、記録した条件でrevertしなかったという証拠であり、mempoolへの受理、block inclusion、実行順、最終成功、受取額を保証しません。

シミュレーションfailureも原因を自動確定しません。RPCのstate、from、gas、block parameter、router、calldataが本番requestと違う可能性を先に排除します。

ローカルの合成contractで再現した範囲

Solidity 0.8.36、Prague EVMの自己完結したlocal contractをcompileし、次を分けました。

case local observation 記事で使う境界
standard debit 1,000 / credit 1,000 基準であって安全評価ではない
fee-on-transfer debit 1,000 / credit 975 / fee 25 差額をaccountingで説明する
shares rebase shares一定、visible balance 1,250または800 一つの設計例に限定する
restricted pause、blacklist、maxTx、cooldown、pool宛sellを別custom error restriction同士を混ぜない
non-standard return empty returnとfalseを別contractで再現 EVM revertとreturn valueを分ける

この検証は実在token detectorではありません。公開RPC、real token、wallet、account、asset、approval、signature、broadcast、swapを使わず、攻撃的deploymentやrestriction回避方法も含みません。

公式supportやsecurity reviewerへ渡すevidence package

checked at and timezone:
chain name / chain ID:
token full contract address:
block number / block hash:
proxy / implementation / beacon / admin:
owner / roles / timelock / recent upgrade:
sender / recipient or pool role, without secrets:
raw amount / decimals:
sender debit / recipient credit / explained fee / difference:
receipt status / called address / function selector:
raw return or revert data:
pause / blacklist / maxTx / cooldown / pool state:
pool / route / active liquidity / price impact:
expected output / minimum received / deadline:
simulation conditions: RPC identity / from / to / calldata / block:
wallet, app, and browser versions if a UI was involved:

public addressやtransactionもticketの公開範囲を確認します。seed phrase、private key、password、2FA code、session cookie、API key、full device backup、remote accessは渡しません。

症状別の停止条件

症状 次のread-only確認 停止する操作
受取額が少ない debit / credit / collector / burn / events feeと決めつけて再送しない
balanceが勝手に変わった shares / index / rebase / fixed-block balance eventなしを盗難確定にしない
全送金がrevert implementation / pause / role / recent upgrade direct callで回避しない
特定accountだけrevert blacklist / allowlist / sender・recipient条件 別accountで試してrestrictionを回避しない
大きなamountだけrevert maxTx / limit unit / current state 小分け送金を自動推奨しない
sellだけrevert pool設定 / fee / router support / allowance / liquidity slippageや未知routerを反復しない
quoteが出ない pair identity / route / active liquidity 同symbolの別tokenを選ばない
シミュレーションだけ成功 fixed inputs / block / mutable state 実transaction成功と表示しない

「売却代行」「凍結解除」「税金を払えばunlock」「必ず回収できる」という未依頼DMは二次被害のsignalです。この記事にはtoken detector、wallet、DEX、取引所、audit、回収serviceのaffiliateやsponsor導線を置いていません。

最終チェックリスト

  • chain IDとfull token contractを固定した
  • symbol、name、iconをidentityにしていない
  • wallet表示とfixed-block balanceOfを分けた
  • sender debit、recipient credit、fee / burn、未説明差を保存した
  • rebaseのshares / index / event設計を対象sourceで確認した
  • pause、blacklist、maxTx、cooldown、pool restrictionを別stateとして読んだ
  • liquidity、price impact、minimum received、deadlineをtoken restrictionと分けた
  • false、empty return、revert、balance変化を別結果として扱った
  • proxy、implementation、owner / roles、upgrade eventを同じblockへ対応させた
  • シミュレーションのfromto、calldata、block、routeを保存した
  • シミュレーションを将来のtransaction成功保証にしていない
  • approval、署名、送金、swap、別router、direct callを追加していない
  • detector一つで詐欺・honeypot・回収不能と断定していない
  • secret、remote access、解除料、回収代行を渡していない

tokenを送れない・売れないときの最短経路は、設定を緩めることではありません。chainとcontractを固定し、transfer accounting、restriction state、market、router、シミュレーションを順番に分け、最初の未説明差で追加操作を止めることです。

確認した一次情報