我一直在花几个小时试图理解为什么我的ATA地址所有者突然更改为令牌程序而不是我的帐户地址。我正在使用PDA从PDA ATA帐户转移令牌,但由于PDA不再是ATA所有者,因此无法这样做。
我试着测试Anchor来剖析问题并找到解决方案,下面是我的测试控制台日志:
薄荷测试结果:
Mint: A2ojTC6aQZYP6bwUq1FmWN9kwaQTB7NKQmMs89j4FUkx
Sender ATA: 2KcR41e2NxnYY5DWDzvgzHiKpSoaZJ55kvBiqU111DaY
Sender ATA owner: 7QzoE1okkpgsn7Rx5pxyGDkXMSc3nsqhWitDHc6c8rKb
program ATA: H9SEYZsU5ao1WoUNoVTQjVMBbJLNjJmKA5N1cGfjxLqE
Supply: 100
PDA: 10
User: 90
✔ Mint token! (7001ms)
Mint测试脚本:
it("Mint token!", async () => {
mintPubkey = await createMint(
program.provider.connection, // conneciton
user, // fee payer
user.publicKey, // mint authority
user.publicKey, // freeze authority (you can use `null` to disable it. when you disable it, you can't turn it on again)
9 // decimals
);
console.log("Mint:", mintPubkey.toBase58())
let tokenAccountPubkeyUser = await getOrCreateAssociatedTokenAccount(program.provider.connection, user, mintPubkey, user.publicKey)
console.log("Sender ATA:", tokenAccountPubkeyUser.address.toBase58())
let tokenAuth = await program.provider.connection.getAccountInfo(tokenAccountPubkeyUser.address);
console.log("Sender ATA owner:", tokenAccountPubkeyUser.owner.toBase58())
let tokenAccountPubkeyPda = await getOrCreateAssociatedTokenAccount(program.provider.connection, user, mintPubkey, program.programId)
console.log("program ATA:", tokenAccountPubkeyPda.address.toBase58())
let txhash = await mintToChecked(
program.provider.connection, // connection
user, // fee payer
mintPubkey, // mint
tokenAccountPubkeyUser.address, // receiver (sholud be a token account)
user, // mint authority
100e9, // amount. if your decimals is 9, you mint 10^9 for 1 token.
9 // decimals
);
let tokenSupply = await program.provider.connection.getTokenSupply(mintPubkey);
console.log("Supply:", tokenSupply.value.uiAmount)
txhash = await transferChecked(
program.provider.connection, // connection
user, // payer
tokenAccountPubkeyUser.address, // from (should be a token account)
mintPubkey, // mint
tokenAccountPubkeyPda.address, // to (should be a token account)
user, // from's owner
10e9, // amount, if your deciamls is 9, send 10^9 for 1 token
9 // decimals
);
let tokenAmount = await program.provider.connection.getTokenAccountBalance(tokenAccountPubkeyPda.address);
console.log("PDA:", tokenAmount.value.uiAmount)
let tokenAmountUser = await program.provider.connection.getTokenAccountBalance(tokenAccountPubkeyUser.address);
console.log("User:", tokenAmountUser.value.uiAmount)
})
Remove vault test result:
Mint: A2ojTC6aQZYP6bwUq1FmWN9kwaQTB7NKQmMs89j4FUkx
userProfilePDA CanbMWdj5UT8KWCAUwsmMyZFeyG8kWQER2tZQdxTohEK
Last vault: 3
vaultAccountPDA: 8tvqq4zWMGZuoe4tsjuC85WRQY8n5qxZeoyY2Ro7UwGi
vaultInfoPDA: A5E257kztkqdwxeqrjgFzjG2uPmECNX7LD96Vp6Tve7z
tokenProgram: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
receiver ATA: 2KcR41e2NxnYY5DWDzvgzHiKpSoaZJ55kvBiqU111DaY
receiver (user) ATA owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
sender (VaultInfo) ATA: BQhNK47ygEYARanGqJnSjKBco3Crot9ihDMJzT8u7yLU
VaultInfo ATA supply: 10
VaultATA owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Program owner: BPFLoaderUpgradeab1e11111111111111111111111
vaultInfoPDA owner: GsCu69BThDsobWHorHkNf8h8zobN6VsexiYkwkH2VtfV
删除vault测试脚本:
it("User Vault removed!", async () => {
// Add your test here.
// const tx = await program.methods.initializeUser().rpc();
console.log("user:", user.publicKey)
console.log("program:", program.programId)
console.log("token program:", TOKEN_PROGRAM_ID)
console.log("Mint:", mintPubkey.toBase58())
const [userProfilePDA] = await anchor.web3.PublicKey.findProgramAddress([
utf8.encode("USER_STATE"),
user.publicKey.toBuffer(),
],
program.programId
);
console.log("userProfilePDA", userProfilePDA.toBase58());
const userProfile = await program.account.userProfile.fetch(userProfilePDA);
// console.log("UserProfile:", userProfile)
console.log("Last vault:", userProfile.lastVault)
const [vaultAccountPDA] = await anchor.web3.PublicKey.findProgramAddress([
utf8.encode("VAULT_STATE"),
user.publicKey.toBuffer(),
new anchor.BN(0).toBuffer()
],
program.programId
);
console.log("vaultAccountPDA:", vaultAccountPDA.toBase58());
const [vaultInfoPDA] = await anchor.web3.PublicKey.findProgramAddress([
utf8.encode("INFO_STATE"),
// user.publicKey.toBuffer(),
],
program.programId
);
console.log("vaultInfoPDA:", vaultInfoPDA.toBase58());
let tokenAccountPubkeyUser = await getOrCreateAssociatedTokenAccount(program.provider.connection, user, mintPubkey, user.publicKey)
console.log("tokenProgram:", TOKEN_PROGRAM_ID.toBase58())
console.log("receiver ATA:", tokenAccountPubkeyUser.address.toBase58())
let tokenAuth = await program.provider.connection.getAccountInfo(tokenAccountPubkeyUser.address);
console.log("receiver (user) ATA owner:", tokenAuth.owner.toBase58())
let tokenAccountPubkeyVault = await getOrCreateAssociatedTokenAccount(program.provider.connection, user, mintPubkey, vaultInfoPDA, true)
console.log("sender (VaultInfo) ATA:", tokenAccountPubkeyVault.address.toBase58())
let txhash = await transferChecked(
program.provider.connection, // connection
user, // payer
tokenAccountPubkeyUser.address, // from (should be a token account)
mintPubkey, // mint
tokenAccountPubkeyVault.address, // to (should be a token account)
user, // from's owner
10e9, // amount, if your deciamls is 9, send 10^9 for 1 token
9 // decimals
);
let tokenAmount = await program.provider.connection.getTokenAccountBalance(tokenAccountPubkeyVault.address);
console.log("VaultInfo ATA supply:", tokenAmount.value.uiAmount)
tokenAuth = await program.provider.connection.getAccountInfo(tokenAccountPubkeyVault.address);
console.log("VaultATA owner:", tokenAuth.owner.toBase58())
tokenAuth = await program.provider.connection.getAccountInfo(program.programId);
console.log("Program owner:", tokenAuth.owner.toBase58())
tokenAuth = await program.provider.connection.getAccountInfo(vaultInfoPDA);
console.log("vaultInfoPDA owner:", tokenAuth.owner.toBase58())
const tx = await program.rpc.removeVault(0, {
accounts: {
authority: user.publicKey,
userProfile: userProfilePDA,
vaultAccount: vaultAccountPDA,
vaultInfo: vaultInfoPDA,
systemProgram: anchor.web3.SystemProgram.programId,
tokenProgram: TOKEN_PROGRAM_ID,
from: tokenAccountPubkeyVault.address,
to: tokenAccountPubkeyUser.address,
owner: vaultInfoPDA,
// sender: vaultInfoPDA
},
signers: []
})
console.log("Your transaction signature", tx);
});
我希望使用vaultInfoPDA(它应该是ATA的所有者)将令牌发送给删除其保险库的用户。
谢谢!
术语owner
在SPL令牌上下文中被重载,这经常引起混淆。
当您在调用getAccountInfo
之后记录帐户的owner
时,它会给您拥有该帐户的程序,该程序必须是SPL令牌程序。SPL Token程序有权更改帐户中的数据。
在该帐户中也是data。在该数据中,32-64字节定义了可以授权来自帐户(SPL令牌"所有者")的移动的公开密钥。因此,一个帐户中有两个所有者,一个由Solana运行时定义,另一个由SPL Token程序定义。
您可以在https://docs.solana.com/developing/programming-model/accounts#ownership-and-assignment-to-programs
阅读更多关于所有权模型的信息。