ネストしたATAからのトークン救出
ついに自分も GOX してしまったか、と焦った事象が発生しました。あがいた結果、なんとかトークンを回収できたので、対応方法を記録します。
発生事象
Solana のウォレットは、ウォレットの公開アドレスとなるシステムアカウント1つと、トークンごとの残高を扱う複数のトークンアカウントの集合体として実装されています。
そのトークンアカウントのオーナーはシステムアカウントになるのですが、紆余曲折があって、トークンアカウントがオーナーになるトークンアカウントが作成され、そこに着金してしまいました。
下図の D で着金した状況です。
本来は C で受け取りしたかったのですが、C のアドレスをお伝えした結果、送金ツールが C のアドレスをウォレットとして扱い、ATA を作ってしまったという経緯です。
トークンはオーナーの署名があれば移動させることができます。ウォレットがオーナーであれば当然署名は可能です。
しかし、トークンアカウントがオーナーになってしまうと、例外的なケースを除いて署名できません。特に、Associated Token Account (ATA) として作られたトークンアカウントは、Program Derived Address (PDA) であり、プログラム的にしか署名ができません。
そのため、D で着金した USDC は GOX なのでは、と軽く絶望したのです。
対処方法
ATA を作成するプログラムである、Associated Token Program のコードを見たところ、この D を救済する命令が実装されていました。
RecoverNested 命令です。
この命令を使うことで、アカウント D の残高を C に移動させた上で、D をクローズすることができます。
実装
RecoverNested 命令を使えばよいことはわかりましたが、spl-token パッケージでは命令を作ってくれそうなメソッドはありません。滅多に使わない機能だからかと思います。
そのため、直接命令を呼び出すことになります。
こちらが実際に救出に使ったコードです。
実行結果
こちらは Devnet で事前検証したときのトランザクションログです。
Associated Token Program の RecoverNested 命令が呼ばれていることがわかります。また、トークンを転送した後、アカウントをクローズする動作も想定通りです。
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]
Program log: RecoverNested
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program log: Instruction: TransferChecked
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6082 of 182061 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program log: Instruction: CloseAccount
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3023 of 173088 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 30231 of 200000 compute units
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success
なんでCのアドレスをお伝えしたのよ?
たぶんここがツッコミポイントだと思います。
ウォレットのアドレスである A をお伝えすれば、ATA として C が使われたと思います。
C をお伝えした理由はこのアドレスが請求書にエビデンスとして残るアドレスだったということです。
ウォレットのアドレスに着金するわけではないので、ここはトークンアカウントのアドレスを記載すべきでは?と考えすぎた結果ハマりました。
前回は Phantom で送金頂いたらしく、Phantom は賢く ATA を認識してくれていました。今回の送金ツールはそういう実装ではなかったようですorz
結果的に RecoverNested という小技を発見できたので、よかった・・・のかな 笑
この記事が気に入ったらサポートをしてみませんか?