The PDA address cannot be transferred, what should I do?
jimlon-xyz opened this issue · 8 comments
PDA address cannot transfer out lamports, what should I do? This contract program setting is really uncomfortable.
If it can be like EVM' solidity features, it will be very convenient and developers will also like it very much.
`
// ...
@mutableAccount(pdaAccount)
@mutableSigner(sender)
function mint(string memory text) external returns(bool) {
// ...
// Here is working
SystemInstruction.transfer(tx.accounts.sender.key, tx.accounts.pdaAccount.key, _mint_fee);
bool failure = true;
if (failure) {
SystemInstruction.transfer(tx.accounts.pdaAccount.key, tx.accounts.sender.key, _mint_fee / 2);
}
return true;
}
@mutableSigner(sender)
function for_back() external returns(bool) {
address pda_account = get_pda_account();
return true;
}
// ...
`
What error do you have when you execute your contract?
What error do you have when you execute your contract?
Execution failed and instruction missing
Can you provide us with a minimal reproducible example of your error containing a Solidity contract, Typescript code and the command to execute?
From the Solidity code you provided I cannot see any problem, so there might be an issue in how you are creating the PDA.
When I asked the error message, I wanted to know exactly what your terminal says. instruction missing
is not an error Solana can produce, and I guess you wanted to say account missing
.
Can you provide us with a minimal reproducible example of your error containing a Solidity contract, Typescript code and the command to execute?
From the Solidity code you provided I cannot see any problem, so there might be an issue in how you are creating the PDA.
When I asked the error message, I wanted to know exactly what your terminal says.
instruction missing
is not an error Solana can produce, and I guess you wanted to sayaccount missing
.
`
import {try_find_program_address} from 'solana';
import "./lib/system_instruction.sol";
@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")
contract sol_nft {
uint64 private _mint_fee = 1 * 1000000000;
uint64 private lucky_awards = 5 * 1000000000;
mapping(uint => address) private _tokenOwners;
@space(1024 * 8)
@payer(payer)
constructor() {
print("Hello, World!");
}
function get_pda_account() public view returns(address) {
(address addr,) = try_find_program_address(["sol_nft"], address(this));
return addr;
}
@mutableAccount(pdaAccount)
@mutableSigner(sender)
function mint(string memory text) external returns(bool) {
address sender = tx.accounts.sender.key;
uint256 tokenId = uint256(keccak256(abi.encodePacked(text)));
require(_tokenOwners[tokenId] == address(0), "Inscription key duplicated");
print("signer.sender={}".format(sender));
_tokenOwners[tokenId] = sender;
// Current signer send to PDA account mint fee
SystemInstruction.transfer(tx.accounts.sender.key, tx.accounts.pdaAccount.key, _mint_fee);
uint randomNumber = uint256(keccak256(abi.encodePacked(address(this), block.number, block.timestamp, sender, tokenId))) % 100;
if (randomNumber > 50) {
// lucky awards
// from PDA acount send to current signer account
SystemInstruction.transfer(tx.accounts.pdaAccount.key, tx.accounts.sender.key, lucky_awards);
}
return true;
}
}
`
`
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { SolNft } from "../target/types/sol_nft";
import * as borsh from "borsh";
describe("sol_nft", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const dataAccount = anchor.web3.Keypair.generate()
const wallet = provider.wallet;
const program = anchor.workspace.SolNft as Program<SolNft>;
it("Is initialized!", async () => {
let tx, programIx
console.log( "wallet.address=", wallet.publicKey.toBase58() )
console.log( "dataAccount.address=", dataAccount.publicKey.toBase58() )
console.log( "wallet.before=", await provider.connection.getBalance(wallet.publicKey, { commitment: "confirmed" }) )
tx = await program.methods
.new()
.accounts({ dataAccount: dataAccount.publicKey })
.signers([dataAccount])
.rpc({ commitment: "confirmed" })
console.log("tx1=", tx)
console.log("wallet.after=", await provider.connection.getBalance(wallet.publicKey, { commitment: "confirmed" }) )
let pdaAccount = await program.methods
.getPdaAccount()
.accounts({ dataAccount: dataAccount.publicKey })
.view()
console.log("pda_account=", pdaAccount.toBase58())
// Cannot send out lamports from the PDA to mutable signer account, Execute failure
tx = await program.methods
.mint("hello")
.accounts({ dataAccount: dataAccount.publicKey, pdaAccount })
.rpc({ commitment: "confirmed" })
console.log("tx2=", tx)
});
});
`
You've declared your solidity function like this:
@mutableAccount(pdaAccount)
@mutableSigner(sender)
function mint(string memory text) external returns(bool)
When you call it from Typescript, you must pass the accounts in the call:
tx = await program.methods
.mint("hello")
.accounts({ dataAccount: dataAccount.publicKey,
pdaAccount: pdaAccount,
sender: YOUR_SENDER_HERE})
.signer([SENDER_KEYPAIR])
.rpc({ commitment: "confirmed" })
When you transfer tokens out of a PDA, you must sign the transaction with the PDA seeds. You'll need to modify SystemInstruction.transfer
, so that the external call receives your seeds here:
if (randomNumber > 50) {
// lucky awards
// from PDA acount send to current signer account
SystemInstruction.transfer(tx.accounts.pdaAccount.key, tx.accounts.sender.key, lucky_awards);
}
There is no way we could make this look like Ethereum, because Ethereum does not have PDAs or seeds. We are open for suggestions if you can think of a better way to handle PDAs more closely to Ethereum.
try_find_program_address(["sol_nft"], address(this))
The code cannot continue to execute at this line and seems to have reached a deadlock. This is the function that creates the PDA address try_find_program_address(["sol_nft"], address(this));
What do I need to do to be able to transfer money and carry seeds? Is there any sample code that I don't know how to write?
Samples: Is it correct?
SystemInstruction.transfer{seed: bytes("sol_nft")}(tx.accounts.pdaAccount.key, tx.accounts.sender.key, lucky_awards);
try_find_program_address(["sol_nft"], address(this))
There is no deadlock in your program. try_find_program_address
calls create_program_address
iteratively until it can find an off-curve address: https://edge.docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses. That function is slow for the blockchain and often used directly in Typescript.
The code cannot continue to execute at this line and seems to have reached a deadlock. This is the function that creates the PDA address try_find_program_address(["sol_nft"], address(this));
What do I need to do to be able to transfer money and carry seeds? Is there any sample code that I don't know how to write?
Samples: Is it correct?
SystemInstruction.transfer{seed: bytes("sol_nft")}(tx.accounts.pdaAccount.key, tx.accounts.sender.key, lucky_awards);
No, it is not. Here is an example: https://github.com/valory-xyz/autonolas-registries/blob/2ce07e89e070230675727cf9c91d223d5f8e8ada/integrations/solana/contracts/SystemInstruction.sol#L57-L66
try_find_program_address(["sol_nft"], address(this))
There is no deadlock in your program.
try_find_program_address
callscreate_program_address
iteratively until it can find an off-curve address: https://edge.docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses. That function is slow for the blockchain and often used directly in Typescript.The code cannot continue to execute at this line and seems to have reached a deadlock. This is the function that creates the PDA address try_find_program_address(["sol_nft"], address(this));
What do I need to do to be able to transfer money and carry seeds? Is there any sample code that I don't know how to write?
Samples: Is it correct?SystemInstruction.transfer{seed: bytes("sol_nft")}(tx.accounts.pdaAccount.key, tx.accounts.sender.key, lucky_awards);
No, it is not. Here is an example: https://github.com/valory-xyz/autonolas-registries/blob/2ce07e89e070230675727cf9c91d223d5f8e8ada/integrations/solana/contracts/SystemInstruction.sol#L57-L66
That’s it! thank you so much! I've been looking for it, thank you sooooooooooo much! ! !