Getting address of a token account given a Solana wallet address - solana

I've been trying the code below to get the spl-token account address for a specific token in a Solana wallet from the Solana wallet address, but I am having issues getting the result I am looking for. I run:
const web3 = require('#solana/web3.js');
(async () => {
const solana = new web3.Connection("https://api.mainnet-beta.solana.com");
//the public solana address
const accountPublicKey = new web3.PublicKey(
"2B1Uy1UTnsaN1rBNJLrvk8rzTf5V187wkhouWJSApvGT"
);
//mintAccount = the token mint address
const mintAccount = new web3.PublicKey(
"GLmaRDRmYd4u3YLfnj9eq1mrwxa1YfSweZYYZXZLTRdK"
);
console.log(
await solana.getTokenAccountsByOwner(accountPublicKey, {
mint: mintAccount,
})
);
})();
I'm looking for the token account address in the return, 6kRT2kAVsBThd5cz6gaQtomaBwLxSp672RoRPGizikH4. I get:
{ context: { slot: 116402202 }, value: [ { account: [Object],
pubkey: [PublicKey] } ] }
I can drill down through this a bit using .value[0].pubkey or .value[0].account but ultimately can't get to the information i'm looking for, which is a return of 6kRT2kAVsBThd5cz6gaQtomaBwLxSp672RoRPGizikH4
Does anyone know what is going wrong?
(Note I do not want to use the getOrCreateAssociatedAccountInfo() method, i'm trying to get the token account address without handling the wallets keypair)

ISSUE SOLVED:
I needed to grab the correct _BN data and convert, solution below.
const web3 = require('#solana/web3.js');
(async () => {
const solana = new web3.Connection("https://api.mainnet-beta.solana.com");
//the public solana address
const accountPublicKey = new web3.PublicKey(
"2B1Uy1UTnsaN1rBNJLrvk8rzTf5V187wkhouWJSApvGT"
);
//mintAccount = the token mint address
const mintAccount = new web3.PublicKey(
"GLmaRDRmYd4u3YLfnj9eq1mrwxa1YfSweZYYZXZLTRdK"
);
const account = await solana.getTokenAccountsByOwner(accountPublicKey, {
mint: mintAccount});
console.log(account.value[0].pubkey.toString());
})();

That will certainly work as a heavy-handed approach. As an alternative, you can try to just search for the associated token account owned by that wallet without creating it if it doesn't exist.
You would essentially follow the logic at https://github.com/solana-labs/solana-program-library/blob/0a61bc4ea30f818d4c86f4fe1863100ed261c64d/token/js/client/token.js#L539
except without the whole catch block.

Related

Sign a function call using private key solana/web3

I am trying to call a certain function of my custom smart contract on the solana blockchain using solana web3. I am trying to sign the function call using my private key instead of signing it through the phantom wallet. Is there any possible way to do that?
You can always compose a call for a solana program manually. Someting like this:
import * as fs from "fs";
import * as web3 from "#solana/web3.js";
const rawPayerKeypair = JSON.parse(fs.readFileSync("PATH_TO_KEYPAIR", "utf-8"));
const payerKeypair = web3.Keypair.fromSecretKey(Buffer.from(rawPayerKeypair));
const programId = new web3.Pubkey("CALLED_PROGRAM_ID");
const url = web3.clusterApiUrl("devnet");
const connection = new web3.Connection(url);
const instruction = new web3.TransactionInstruction({
keys: [
{ pubkey: payerKeypair.publicKey, isSigner: true, isWritable: true }
],
programId,
// Put the transaction instruction here.
data: Buffer.from([])
});
const transaction = new web3.Transaction().add(instruction);
const response = await web3.sendAndConfirmTransaction(connection, transaction, [payerKeypair]);
console.log(response);

How to make an auction with the new metaplex js sdk?

I am working on building a solana marketplace and trying to update from the old solana marketplace to auction house.
code:
const authority = metaplex.identity();
const auctionHouse = await metaplex
.auctions()
.createAuctionHouse({
sellerFeeBasisPoints: 200,
requiresSignOff: false,
treasuryMint: WRAPPED_SOL_MINT,
authority: authority.publicKey,
canChangeSalePrice: true,
feeWithdrawalDestination: publicKey,
treasuryWithdrawalDestinationOwner: publicKey,
})
.run();
const listNft = async () => {
try {
const NFTs = await metaplex.nfts().findAllByOwner(publicKey).run();
console.log(NFTs[4].mintAddress.toString(), " nft");
const auctionHouse = await metaplex
.auctions()
.findAuctionHouseByCreatorAndMint(publicKey, WRAPPED_SOL_MINT)
.run();
const listtx = metaplex
.auctions()
.builders()
.createListing({
mintAccount: NFTs[4].mintAddress,
price: lamports(1 * LAMPORTS_PER_SOL),
auctionHouse: auctionHouse,
});
var rpcClient = await metaplex.rpc().sendTransaction(listtx);
console.log(rpcClient, "listed nft");
} catch (error) {
console.log(error);
}
};
const getListing = async () => {
const NFTs = await metaplex.nfts().findAllByOwner(publicKey).run();
const retrievedAuctionHouse = await metaplex
.auctions()
.findAuctionHouseByCreatorAndMint(publicKey, WRAPPED_SOL_MINT)
.run();
console.log(retrievedAuctionHouse?.address?.toString(), "auction pubkey");
const retrieveListing = await metaplex
.auctions()
.for(retrievedAuctionHouse)
.findListingByAddress(NFTs[4].mintAddress)
.run();
console.log(retrieveListing, "retrieving listings");
};
const bidNft = async () => {
const NFTs = await metaplex.nfts().findAllByOwner(publicKey).run();
const auctionHouse = await metaplex
.auctions()
.findAuctionHouseByCreatorAndMint(publicKey, WRAPPED_SOL_MINT)
.run();
const { bid, buyerTradeState } = await metaplex
.auctions()
.for(auctionHouse)
.bid({
mintAccount: NFTs[5].mintAddress,
price: lamports(0.5 * LAMPORTS_PER_SOL),
});
console.log(bid, buyerTradeState, "bidding");
};
errors:
when trying to list nft:
MetaplexError: AuctionHouseProgram > Account is already initialized"
Source: Program > AuctionHouseProgram [hausS13jsjafwWwGqZTUQRmWyvyxn9EQpqMwV1PBBmk]
Problem: The program [AuctionHouseProgram] at address [hausS13jsjafwWwGqZTUQRmWyvyxn9EQpqMwV1PBBmk] raised an error of code [1] that translates to "Account is already initialized"".
Solution: Check the error message provided by the program.
Caused By: TokenLendingError#AlreadyInitialized: Account is already initialized"
at RpcClient.parseProgramError (RpcClient.mjs?e98e:206:1)
at RpcClient.sendTransaction (RpcClient.mjs?e98e:48:1)
When trying to fetch listings:
Account.mjs?9f45:47 Uncaught (in promise) MetaplexError: Account Not Found
Source: SDK
Problem: The account of type [ListingReceipt] was not found at the provided address [3m517hu6UuV5HjdLmb2GxZkttKTtQ8VseHRCPuUTDJmz].
Solution: Ensure the provided address is correct and that an account exists at this address.
at assertAccountExists (Account.mjs?9f45:47:1)
at eval (Account.mjs?9f45:39:1)
at Object.handle (findListingByAddress.mjs?1810:23:44)
at async eval (Task.mjs?1340:58:1)
at async Disposable.run (Disposable.mjs?b308:22:1)
Found a way around
Instead of using metaplex.auction().builder().createListing i used metaplex.auctions().for(auctionHouse).list().
Then funded the auction fee payer account which is a pda.
Finally in findListingByAddress i passed the sellerTradeState
const { listing, sellerTradeState } = await metaplex
.auctions()
.for(auctionHouse)
.list({
mintAccount: NFTs[1].mintAddress,
price: sol(1.5),
})
.run();
For fetching the listed nft
const retrieveListing = await metaplex
.auctions()
.for(retrievedAuctionHouse)
.findListingByAddress(
sellerTradeState
)
.run();
Just from the information provided above, there are two issues:
You might have run the createAuctionHouse instruction already once which created the Auction house and now that you are running the listNft function which also runs createAuctionHouse instruction, it throws an Account already exists error. You can see why this happens here: https://stackoverflow.com/a/72808405/19264629
You are using the NFTs mint address in findListingByAddress instruction, which is wrong. You have to create a ListingReceipt as stated here: https://docs.metaplex.com/guides/auction-house/#auction-house-receipts while creating the listing itself (this needs to be done in one single transaction). Once this is done, you need to plugin the receipt address in the findListingByAddress to fetch the listings.
Got it to work. The trick was to fund the auction house "Fee Account" as explained above and in Auction House CLI: I cannot execute the sale instruction to list my NFT
const ah = await mx
.auctionHouse()
.findByAddress({ address:ahKey })
.run();
const nft = await mx
.nfts()
.create({
name:"TESTNFT",
symbol: "TESTNFT",
uri: "https://blah.com",
sellerFeeBasisPoints : 100,
creators: [
{
address: wallet.publicKey,
share: 100,
}]
})
.run();
const price = sol(0.1);
const { listing, sellerTradeState } = await mx
.auctionHouse()
.list({
auctionHouse: ah,
mintAccount: nft.mintAddress,
price
})
.run()

How to create an account on NEAR protocol?

I would like to learn how to create an account using RPC or REST calls on NEAR protocol.
If you want to create a subaccount (a.frol.near when you own frol.near): Submit a transaction with CREATE_ACCOUNT, TRANSFER, ADD_KEY actions. Here is an example of such a transaction.
If you want to create *.near account, you need to submit a transaction with create_account function call on near contract. Here is an example of such a transaction, and here is a code snippet from the tutorial in the docs using near-api-js JS library:
const HELP = `Please run this script in the following format:
node create-testnet-account.js CREATOR_ACCOUNT.testnet NEW_ACCOUNT.testnet AMOUNT
`;
const { connect, KeyPair, keyStores, utils } = require("near-api-js");
const path = require("path");
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
if (process.argv.length !== 5) {
console.info(HELP);
process.exit(1);
}
createAccount(process.argv[2], process.argv[3], process.argv[4]);
async function createAccount(creatorAccountId, newAccountId, amount) {
const near = await connect({ ...config, keyStore });
const creatorAccount = await near.account(creatorAccountId);
const keyPair = KeyPair.fromRandom("ed25519");
const publicKey = keyPair.publicKey.toString();
await keyStore.setKey(config.networkId, newAccountId, keyPair);
return await creatorAccount.functionCall({
contractId: "testnet",
methodName: "create_account",
args: {
new_account_id: newAccountId,
new_public_key: publicKey,
},
gas: "300000000000000",
attachedDeposit: utils.format.parseNearAmount(amount),
});
}
If you don't need a named account, you can just generate a new ed25519 key-pair, and the hex representation of the public key will be your account id (it won't be recorded on chain until you/someone transfers some NEAR tokens to it, and so it is called "implicit" account). Example for such an account.
Here is a detailed tutorial on how to construct a transaction. Ultimately, you will submit your transaction via JSON RPC broadcast_tx* endpoints.

Stripe returns a "No such token" error (Plaid Integration)

I am getting this error from Stripe when I try to create a new source for the selected bank account. I am using the new (beta) version of the Plaid Node SDK. Here is my code:
let user;
const mode = "sandbox";
const dsService = new CaspioDsService();
// Load the user if not already loaded by cognitoAuth
if (!req.user) {
user = new User(
dsService,
new CaspioRefDataService(),
new AuthUserService({
organizationId: res.locals.organization.Organization_ID,
isAuthenticated: res.locals.isAuthenticated,
})
);
await user.load(req.params.userId);
} else {
user = req.user.userObject;
}
const configuration = new Configuration({
basePath: PlaidEnvironments[mode],
baseOptions: {
headers: {
"PLAID-CLIENT-ID": config.plaid.clientId,
"PLAID-SECRET": mode === "sandbox" ? config.plaid.secretSandbox : config.plaid.secretProduction,
"Plaid-Version": "2020-09-14",
},
},
});
const plaidClient = new PlaidApi(configuration);
console.log(configuration.basePath); // https://sandbox.plaid.com
// Exchange the public token for the Plaid access token
const plaidTokenRes = await plaidClient.itemPublicTokenExchange({
public_token: req.body.publicToken,
});
const accessToken = plaidTokenRes.data.access_token;
console.log(accessToken); // access-sandbox-d92396c2-1f49-4780-9ae9-23d50645f364
// Get the Stripe bank account token from Plaid
const stripeTokenRes = await plaidClient.processorStripeBankAccountTokenCreate({
access_token: accessToken,
account_id: req.body.accountId
});
const bankAccountToken = stripeTokenRes.data.stripe_bank_account_token;
console.log(bankAccountToken); // btok_1JFMGwGq7ejZoSiwGmM8WSSm
let stripeCustomerId = user.getStripeToken();
const stripeClient = await StripeHelper.getStripeClient(mode); // Get Stripe client in sandbox mode
console.log(stripeCustomerId); // cus_Jt7AWZjC8rHPzt
// Add the source to the Stripe customer and get the bank account info
const bankAccount = await stripeClient.customers.createSource(stripeCustomerId, {
source: bankAccountToken,
}); // Error: No such token: 'btok_1JFMGwGq7ejZoSiwGmM8WSSm'
Any ideas what I might be doing wrong? I expect the issue is with my code, or possibly Plaid (I don't think it is a Stripe problem).
It sounds like you're getting a token from Plaid, but Stripe is rejecting it, which suggests a problem with the relationship between your Plaid and Stripe setups. Are you sure that you enabled the Plaid/Stripe integration in the Plaid dashboard and that the client id / secret you're using matches the Plaid account where the integration is enabled? The Plaid docs also suggest that this error can be caused by using a mismatched set of environments (e.g. using Production with Stripe but Sandbox with Plaid).
The problem was that we had the wrong Stripe account connected. Silly one, but I'm posting this in case anyone else makes the same mistake.

How to revoke a function-call access key?

"Access keys are stored as account_id,public_key in a trie state."
how do I revoke an function-call access key from the blockchain?
You can remove an access key by sending a DeleteKey transaction. For more details on transactions, please checkout this page.
Here is how you would delete an access key for example.testnet:
const { KeyPair, keyStore, connect } = require("near-api-js");
const CREDENTIALS_DIR = "~/.near-credentials";
const ACCOUNT_ID = "example.testnet";
const PUBLIC_KEY = "8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc";
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(CREDENTIALS_DIR);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
deleteAccessKey(ACCOUNT_ID, PUBLIC_KEY);
async function deleteAccessKey(accountId, publicKey) {
const near = await connect(config);
const account = await near.account(accountId);
await account.deleteKey(publicKey);
}
You will need to make sure you have credentials for your account stored locally to complete this function. Do this by running the following near-cli command:
near login
For more info on rotating access keys, check out this doc:
https://docs.near.org/docs/api/naj-cookbook#access-key-rotation

Resources