Closed jimlon-xyz closed 11 months ago
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! ! !
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.
` // ...
`