diff --git a/packages/common-sdk/src/web3/ata-util.ts b/packages/common-sdk/src/web3/ata-util.ts index f29e6e5..2c5a2ed 100644 --- a/packages/common-sdk/src/web3/ata-util.ts +++ b/packages/common-sdk/src/web3/ata-util.ts @@ -97,6 +97,12 @@ export async function resolveOrCreateATAs( const ataAddress = nonNativeAddresses[index]!; let resolvedInstruction; if (tokenAccount) { + // ATA whose owner has been changed is abnormal entity. + // To prevent to send swap/withdraw/collect output to the ATA, an error should be thrown. + if (!tokenAccount.owner.equals(ownerAddress)) { + throw new Error(`ATA with change of ownership detected: ${ataAddress.toBase58()}`); + } + resolvedInstruction = { address: ataAddress, ...EMPTY_INSTRUCTION }; } else { const createAtaInstruction = createAssociatedTokenAccountInstruction( diff --git a/packages/common-sdk/tests/ata-util.test.ts b/packages/common-sdk/tests/ata-util.test.ts index 9866da2..e350b0f 100644 --- a/packages/common-sdk/tests/ata-util.test.ts +++ b/packages/common-sdk/tests/ata-util.test.ts @@ -242,4 +242,51 @@ describe("ata-util", () => { expect(postAccountData[1]).not.toBeNull(); expect(postAccountData[2]).not.toBeNull(); }); + + it("resolveOrCreateATA, owner changed ATA detected", async () => { + const anotherWallet = Keypair.generate(); + const mint = await createNewMint(); + + const ata = await mint.createAssociatedTokenAccount(wallet.publicKey); + + // should be ok + const preOwnerChanged = await resolveOrCreateATA( + connection, + wallet.publicKey, + mint.publicKey, + () => connection.getMinimumBalanceForRentExemption(AccountLayout.span), + ); + expect(preOwnerChanged.address.equals(ata)).toBeTruthy(); + + // owner change + const builder = new TransactionBuilder(connection, wallet); + builder.addInstruction({ + instructions: [ + Token.createSetAuthorityInstruction( + TOKEN_PROGRAM_ID, + ata, + anotherWallet.publicKey, + "AccountOwner", + wallet.publicKey, + [] + ) + ], + cleanupInstructions: [], + signers: [], + }); + await builder.buildAndExecute(); + + // verify that owner have been changed + const changed = await mint.getAccountInfo(ata); + expect(changed.owner.equals(anotherWallet.publicKey)).toBeTruthy(); + + // should be failed + const postOwnerChangedPromise = resolveOrCreateATA( + connection, + wallet.publicKey, + mint.publicKey, + () => connection.getMinimumBalanceForRentExemption(AccountLayout.span), + ); + await expect(postOwnerChangedPromise).rejects.toThrow(/ATA with change of ownership detected/); + }); });