Solana: Can't make createAccount work, createAccountWithSeed works - solana

I am currently trying to learn on writing programs for Solana and interacting with those.
I am using the example-helloworld code. In order to interact with the helloworld program, the nodejs code creates an account with seed:
const transaction = new Transaction().add(
SystemProgram.createAccountWithSeed({
fromPubkey: payer.publicKey,
basePubkey: payer.publicKey,
seed: GREETING_SEED,
newAccountPubkey: greetedPubkey,
lamports,
space: GREETING_SIZE,
programId,
})
)
tx = client.send_transaction(transaction, payer)
My understanding is, that it creates a data account, owned by the program with the programId. Not sure why with seed yet.
I tried to replace this piece of code with the following:
const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: greetedPubkey,
lamports,
space: GREETING_SIZE,
programId,
})
)
tx = client.send_transaction(transaction, payer)
but it is not working. Once the transaction is send, I get the following error:
{'code': -32602, 'message': 'invalid transaction: Transaction failed to sanitize accounts offsets correctly'}
Anyone who can explain the difference and what I am doing wrong??

When creating an account with createAccount, you must provide a signature from that new account. So in your case, greetedPubkey must be a Keypair, just like payer. You can do:
const greeted = Keypair.generate();
const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: greeted.publicKey,
lamports,
space: GREETING_SIZE,
programId,
})
);
tx = client.send_transaction(transaction, payer, greeted);
createAccountWithSeed is special because it derives a new address from the base, meaning that you don't need to sign with the new address, only with the base address.

Related

Estimate gas cost for transaction to custom Solana Program written with Anchor

How can I get a gasFee estimate for a transaction of my custom contract?
For a normal transaction execution from the SPL library I can do so like this:
import { Transaction } from '#solana/web3.js';
const transaction = new Transaction({
recentBlockhash: recentBlockhash.blockhash,
feePayer: wallet.publicKey
}).add(
// someFunctionCall
);
const estimatedGas = await transaction.getEstimatedFee(connection);
But I do not use new Transaction() to call my custom Program's methods. It's done like:
const tx = await program.methods
.myCustomMethod(...)
.accounts(...)
.rpc();
How can I estimate the gas for the tx w/o actually calling it?
A demo example to print estimated gas cost for transaction created usign Anchor:
const txn = await program.methods
.exchange10(sBump, BNF(exchangeBal1))
.accounts({
feeRecipient: feeRecipient.publicKey,
seller: sellerPDA,
sellerTokenAccount: sellerTknAcc.address,
buyer: wallet2.publicKey,
buyerTokenAccount: buyerTknAcc.address,
tokensForSale: tokensForSalePDA,
tokenProgram: TOKEN_PROGRAM_ID,
})
.signers([wallet2])
.transaction();
txn.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
txn.feePayer = wallet2.publicKey;
console.log(await txn.getEstimatedFee(connection))
I found out you can replace .rpc() with .transaction() which will return an object of type Transaction (from #solana/web3.js).
Then you can exercise the same logic for gas estimation on that object as in the first example.
And, of course, you will need to sendAndConfirm the transaction as an extra step, because otherwise the .rpc() call was taking care of that.

Why is there an error in this contract call?

new to nearprotocol! Trying to do a little hello world using near-api-js, here is my issue ...
const { keyStores, connect } = require("near-api-js");
const fs = require("fs");
const path = require("path");
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const ACCOUNT_ID = "acct.testnet";
const WASM_PATH = "./contract/target/wasm32-unknown-unknown/release/counter.wasm";
const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
deployContract(ACCOUNT_ID, WASM_PATH);
async function deployContract(accountId, wasmPath) {
const near = await connect(config);
const account = await near.account(accountId);
const result = await account.deployContract(fs.readFileSync(wasmPath));
}
I am deploying a wasm contract with this methodology, however, when I try to call the contract using
const nearAPI = require("near-api-js");
const keyStores = nearAPI.keyStores;
const connect = nearAPI.connect;
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const credentialsPath = require("path").join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
networkId: "testnet",
keyStore,
nodeUrl: "https://rpc.testnet.near.org",
walletUrl: "https://wallet.testnet.near.org",
helperUrl: "https://helper.testnet.near.org",
explorerUrl: "https://explorer.testnet.near.org",
};
call();
async function call() {
// gets the state of the account
const near = await connect(config);
const account = await near.account("acct.testnet");
const contract = new nearAPI.Contract(
account, // the account object that is connecting
"acct.testnet",
{
contractID : "counter.acct.testnet",
changeMethods: ["increment", "decrement", "reset"], // view methods do not change state but usually return a value
viewMethods: ["get_num"], // change methods modify state
sender: account, // account object to initialize and sign transactions.
}
);
let response = await contract.reset(
{
args: {
//arg_name: "value" // argument name and value - pass empty object if no args required
},
gas: 300000000000000 // attached GAS (optional)
}
);
console.log(response);
}
Now, the response says that the contract does not exist: ServerTransactionError: Can't complete the action because account counter.acct.testnet doesn't exist.
However, when using acct.testnet, instead of counter.acct.testnet, it works.
Which leaves the question: how can I specify the exact contract that I want to interact with, on the near blockchain, under a specific account?
Thanks!
There are two different ways you can go about using NAJ (near-api-js) and interacting with contracts. The first way, which is the one you're using, is to create a contract object (new nearAPI.Contract()) and connect to the contract using an account object (either from a wallet connection or the native near.account() method):
const contract = new nearAPI.Contract(
accountObject, // the account object that is connecting
...
This method allows you to pass in the account ID of the contract you wish to interact with:
const contract = new nearAPI.Contract(
accountObject,
"CONTRACT_ACCOUNT_ID_HERE.testnet",
...
In your case, simply change that account ID to be whatever you want and that will allow you to interact with different contracts. These account IDs must exist and also have a contract deployed to them otherwise you'll run into errors. Don't forget that the methods you outline when creating the nearAPI contract object must also live on the contract or else when you try to use them, it will throw as well.
The second way you can interact with contracts, which is my preferred way of doing things, is to create the account object in the same manner as you did previously but instead of creating a contract object and doing contract.methodName, you just take the account object and do account.functionCall or account.viewFunction where you can pass in the contract ID, method name etc...
This allows you to interact with any contract on-chain without having to create a new contract object for each contract you want to interact with.

What's "ATA" mean in Solana/Anchor?

Noticed this code comment in an Anchor test:
What's the ATA stand for there and why does it matter for DAO controlled authorities?
ATA is an associated token address.
Associated token addresses can be derived from another public key using findProgramAddress.
function getAssociatedTokenAddress(
mint: PublicKey,
owner: PublicKey,
allowOwnerOffCurve = false,
programId = TOKEN_PROGRAM_ID,
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
): Promise<PublicKey> {
if (!allowOwnerOffCurve && !PublicKey.isOnCurve(owner.toBuffer())) throw new TokenOwnerOffCurveError();
const [address] = await PublicKey.findProgramAddress(
[owner.toBuffer(), programId.toBuffer(), mint.toBuffer()],
associatedTokenProgramId
);
return address;
}
There is another type of token account that predates the associated token account, an ancillary token account. These legacy accounts still exist and can be created today.
The reason that the code says it can be either ATA or something else is because it is validating that the account is a token account that it expects.

solana web3 API: how to obtain data created with splToken.createTransferInstruction()

Would appreciate guidance on how to obtain the data back in the variables, which was entered into splToken.createTransferInstruction(), step-by-step, from the buffer variable in the code snippet below:
var transaction = new web3.Transaction();
transaction.add(
splToken.createTransferInstruction(
fromTokenAccount,
toTokenAccount,
senderPublicKey,
amount,
[],
splToken.TOKEN_PROGRAM_ID,
)
);
// Setting the variables for the transaction
transaction.feePayer = provider.publicKey;
let blockhashObj = await connection.getRecentBlockhash();
transaction.recentBlockhash = await blockhashObj.blockhash;
// Transaction constructor initialized successfully
if(transaction) { console.log('Txn created successfully'); }
// Request creator to sign the transaction (allow the transaction)
let signed = await provider.signTransaction(transaction);
let buffer = signed.serialize();
Using web3.Transaction.from(buffer) I obtained a Transaction object - see image from the browser's console:
I need to do something with instructions[0].data, I suppose to break it into byte lengths that will allow me to repopulate from the signed transaction:
fromTokenAccount,
toTokenAccount,
senderPublicKey,
amount,
[], // would this be a variable or zero-byte?
splToken.TOKEN_PROGRAM_ID,
from the splToken.createTransferInstruction() above.
Much appreciated!
From the TransactionInstruction, you can use decodeTransferInstruction to get back the initial parameters. In your case, you can call:
let tx = web3.Transaction.from(buffer);
let decodedIx = decodeTransferInstruction(tx.instructions[0]);
console.log(decodedIx.keys.source);
console.log(decodedIx.keys.destination);
console.log(decodedIx.keys.owner);
console.log(decodedIx.data.amount);
Full source code available at: https://github.com/solana-labs/solana-program-library/blob/24baf875e9e19c26d694d28c557d33848c3a9180/token/js/src/instructions/transfer.ts#L87

mintTo - is there a way to get the transaxtion signature?

After I create my own token program I am thne minting some supply into another tokenaccount.
I notice the mintTo is a void function, is there any easy way to get the associated transaction signature ?
const token = new splToken.Token(
connection,
new web3.PublicKey(token_type.token_address),
splToken.TOKEN_PROGRAM_ID,
mint_authority_wallet
);
const destinationTokenAccount = await token.getOrCreateAssociatedAccountInfo(
new web3.PublicKey(to_public_address)
);
console.log("destinationTokenAccount>>", destinationTokenAccount);
const test = await token.mintTo(
destinationTokenAccount.address,
mint_authority_wallet.publicKey,
[],
100
);
console.log("test>>",test)
The fix for this is quite simple: you just need to return the result from sendAndConfirmTransaction. Check out the source code at https://github.com/solana-labs/solana-program-library/blob/ab05e4e597c0b538d855c18da3850df84ad6a49a/token/js/client/token.js#L1027
You could always hack your version to return the signature. Better yet, PRs are always welcome!

Resources