How to create account/signer using Phantom Wallet provider? - javascript

I am following the inner instructions of a Solana NFT to create the process of listing an NFT for sale.
According to this NFT, https://solscan.io/tx/25g9L7rDCn8ZbZAZoCvyVNe5woZUoK2PVU3VEXftqySa2EYsLUJB2GA42qDwxsezUBRH2A9eJX1iUUD2LCK9Fua9, the first instruction is to create an account.
The instruction shows
NewAccount - E61SDPzHP61C4KwwiSqG4FAHXof852wsaSjh3F4kLbwmicon
Source - H2eud1RCJu8u8vEQ8jJLQ32jM5oKfARGxz5LwEKKfTLuicon
TransferAmount(SOL) - 0.00223416
ProgramOwner - M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7Kicon
New Account seems to be a newly generated account and Source is the wallet which was used when the user connected their wallet to the client.
I am using the this code from the Solana Cookbook for reference
import { clusterApiUrl, Connection, PublicKey, Keypair, Transaction, SystemProgram } from "#solana/web3.js";
import { Token, TOKEN_PROGRAM_ID, AccountLayout } from "#solana/spl-token";
import * as bs58 from "bs58";
(async () => {
// connection
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// 5YNmS1R9nNSCDzb5a7mMJ1dwK9uHeAAF4CmPEwKgVWr8
const feePayer = Keypair.fromSecretKey(
bs58.decode("588FU4PktJWfGfxtzpAAXywSNt74AvtroVzGfKkVN1LwRuvHwKGr851uH8czM5qm4iqLbs1kKoMKtMJG4ATR7Ld2")
);
// G2FAbFQPFa5qKXCetoFZQEvF9BVvCKbvUZvodpVidnoY
const alice = Keypair.fromSecretKey(
bs58.decode("4NMwxzmYj2uvHuq8xoqhY8RXg63KSVJM1DXkpbmkUY7YQWuoyQgFnnzn6yo3CMnqZasnNPNuAT2TLwQsCaKkUddp")
);
const mintPubkey = new PublicKey("54dQ8cfHsW1YfKYpmdVZhWpb9iSi6Pac82Nf7sg3bVb");
// generate a new keypair for token account
const tokenAccount = Keypair.generate();
console.log(`token account: ${tokenAccount.publicKey.toBase58()}`);
let tx = new Transaction().add(
// create token account
SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: AccountLayout.span,
lamports: await Token.getMinBalanceRentForExemptAccount(connection),
programId: TOKEN_PROGRAM_ID,
}),
/**... some other instruction*/
);
console.log(`txhash: ${await connection.sendTransaction(tx, [feePayer, tokenAccount])}`);
})();
From my understanding, in this scenario the feePayer is the user who connected their wallet. (Source from the instructions), tokenAccount is the account that we want to transfer the SOL to (NewAccount from the instructions), and programID is ProgramOwner from the instructions.
When I run this exact code from the Solana Cookbook it works.
When I try using the provider as the feePayer (wallet that was connected in the client) it doesn't work.
I have a function to get my provider
getProvider = () => {
if ("solana" in window) {
const anyWindow = window;
const provider = anyWindow.solana;
if (provider.isPhantom) {
return provider;
}
}
window.open("https://phantom.app/", "_blank");
};
Then I grab my provider with
const provider = this.getProvider();
I replace the original feePayer created with Keypair.fromSecretKey with the provider.
My assumption as to why provider isn't working as one of the signers in await connection.sendTransaction(tx, [provider, tokenAccount]) is because the signer requires a secret key.
So what I try to do instead is sign the transaction myself using some code I've found online on how to sign a transaction.
const blockHash = await connection.getRecentBlockhash()
tx.feePayer = provider.publicKey
tx.recentBlockhash = await blockHash.blockhash
const signed = await provider.signTransaction(tx);
This runs fine and I'm able to see the results when I do console.log(signed);
The problem I'm getting is that there are 2 signatures and one of them is null. One is the signature of the provider and the other is the signature of the tokenAccount.
When I use console.log() to show the public key of the provider and the public key of the tokenAccount and try to match it to the public keys within the signature, I find that the signature of the tokenAccount is the one that is returning null.
So when it's time to run const signature = await connection.sendRawTransaction(signed.serialize()); I get an error from signed.serialize() saying Signature verification failed
What am I doing wrong here?
Can I create a Signer from a user connecting their phantom wallet? If not, how can I sign the transaction so that neither of the signatures come out null?

So I would recommend using the wallet-adapter to connect and send transactions from wallets.
This will make the code above look something like this:
const { publicKey, sendTransaction } = useWallet();
// G2FAbFQPFa5qKXCetoFZQEvF9BVvCKbvUZvodpVidnoY
const alice = Keypair.fromSecretKey(
bs58.decode("4NMwxzmYj2uvHuq8xoqhY8RXg63KSVJM1DXkpbmkUY7YQWuoyQgFnnzn6yo3CMnqZasnNPNuAT2TLwQsCaKkUddp")
);
const mintPubkey = new PublicKey("54dQ8cfHsW1YfKYpmdVZhWpb9iSi6Pac82Nf7sg3bVb");
// generate a new keypair for token account
const tokenAccount = Keypair.generate();
console.log(`token account: ${tokenAccount.publicKey.toBase58()}`);
let tx = new Transaction().add(
// create token account
SystemProgram.createAccount({
fromPubkey: publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: AccountLayout.span,
lamports: await Token.getMinBalanceRentForExemptAccount(connection),
programId: TOKEN_PROGRAM_ID,
}),
/**... some other instruction*/
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, 'processed');

Hi i have similar issue,
There are two signers admin and user,
admin is signing the tx from node and serializing tx.
this i send to frontend
and user signs it.
But as initially the signature is null, so wallet.signTransaction(tx);
gives the Signature verification failed

You have to partially sign with the tokenAccount before sending the transaction.
const blockHash = await connection.getRecentBlockhash()
tx.feePayer = provider.publicKey
tx.recentBlockhash = await blockHash.blockhash
const signed = await provider.signTransaction(tx);
// now sign with the tokenAccount
signed.partialSign(tokenAccount);

Related

Transaction has been reverted by the EVM ( PANCAKESWAP manual trade bot in js)

Guys i'm trying to do an manual trade bot in pancake for study , but i can't understand what it's wrong with my code now , i'm trying for a few days understand what is wrong but i really don't know what is happen.
const Web3 = require('web3');
async function swapToken(privateKeys, tokenAddress, amount, bnbAddress) {
// made an web3 instance with URL from Binance Smart Chain
const web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org/'));
// Get atual gás price
const gasPrice = await web3.eth.getGasPrice();
// Loop to get all pk's
for (const privateKey of privateKeys) {
// get the account of my pk
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
// define transaction options
const options = {
from: account.address,
to: tokenAddress,
value: 0,
gasPrice: gasPrice,
gas: '200000',
data: bnbAddress,
};
// assign and send the transaction to trade token
const signedTransaction = await web3.eth.accounts.signTransaction(options, privateKey);
web3.eth.sendSignedTransaction(signedTransaction.rawTransaction);
}
}
// all pk ( multi wallet system)
const privateKeys = [
'my private key'
];
// token address on Binance Smart Chain
const tokenAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56';
// Quantity of tokens to be traded
const amount = '2';
//token output
const bnbAddress = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
swapToken(privateKeys, tokenAddress, amount, bnbAddress);
and the error is :
error after run the code
i tried some changes in code variables but nothing really works ... I'm really need help with this error and understand what is wrong to do this error

How to do 'signTransaction' after authenticated by 'signPersonalMessage' by WalletConnect of '#walletconnect/react-native-dapp'?

I am developing a react native mobile app where user can connect their existing wallet(rainbow, metamask and many more) to app.
I have already done the authentication part, like user can successfully connect their app wallet to app by this code.
import { useWalletConnect } from '#walletconnect/react-native-dapp';
const connector = useWalletConnect();
await connector.connect();
const message = `App name is XYZ - ${new Date().toUTCString()}`;
const hexMsg = convertUtf8ToHex(message);
const address = connector.accounts[0];
await setItemToStorage('address', address);
const msgParams = [hexMsg, address];
connector.signPersonalMessage(msgParams).then(async result => {
let data = {
message,
signature: result,
};
Now every thing is working as expected.
And then I have to transfer my wallet amount to other address, and to achieve this I know I have to get permission from wallet app,
To achieve this I am trying to do like
let txObj = {
gas: Web3js.utils.toHex(100000),
to: receiver_address!,
data: data,
from: userWallet,
};
console.log('Start transaction...');
const singTrasaction = await connector.signTransaction(txObj);
The connector.signTransaction(txObj) open the wallet app but not prompted me to confirm.
I am just confused and nothing get help me.
Please help me anyone I am getting stuck on this since a week.

Sending a custom token with web3 + metamask

I'm trying to send USDT (custom token) with the metamask API using web3. I'm developing on ReactJS and I could detected correctly the user account from metamask.
My code:
const web3 = new Web3(window.web3.currentProvider);
const contractInstance = new web3.eth.Contract(abiUSDT, addressUSDT);
const amount = 200;
const tx = {
from: 'PERSON_SENDER',
to: contractInstance._address,
data: contractInstance.methods.transfer('PERSON_RECIPIENT', web3.utils.toWei( amount.toString() ) ).encodeABI(),
}
web3.eth.sendTransaction(tx).then(res => {
console.log("res",res)
}).catch(err => {
console.log("err",err)
});
obviously, I replace the fields called: PERSON_SENDER and PERSON_RECIPIENT with:
PERSON_SENDER: The user account
PERSON_RECIPIENT: My personal account (I want to deposit in this account)
Using this config, the transaction doesn't send to my account when I check in etherscan (and the amount was sent to the address of the contract), and a user communicate that if he uses real values for him real account, metamask doesn't touch his amount.
I made the same code and it works. But check the correct ABI, and adrresses format. The address "FROM" must be tha same address that your metamask is conected.
My code:
var contractABI = [{"YOUR TOKEN ABI HERE"}]
addressUSDT = "0xf80e1C5e28226cAfc7B0Ee6729E553ef54A7774F"; //IT'S OTHER CONTRACT
const web3 = new Web3(window.web3.currentProvider);
const contractInstance = new web3.eth.Contract(contractABI, addressUSDT);
const amount = 200;
const tx = {
from: '0x2acd4F50ee814d5092d9FC892d2BDEab91f5594d',
to: contractInstance._address,
data: contractInstance.methods.transfer('0xe54356F4e1aD4215973E7180BeBb4644a07DF508', web3.utils.toWei( amount.toString() ) ).encodeABI(),
}
web3.eth.sendTransaction(tx).then(res => {
console.log("res",res)
}).catch(err => {
console.log("err",err)
});

How can I registrate users in my my google actions webhook?

I am trying to allow users to register by voice when they use my Google action. How can I do it?
I have tried to use Account linking with Google Sign-In using the Client ID, but I do not know how to collect the user data in my webhook.
Looking for information I found that:
app.intent("Show User Profile", conv => {
const payload = conv.user.profile.payload;
if (payload) {
const userId = payload.aud;
const name = payload.name;
const givenName = payload.given_name;
const familyName = payload.family_name;
const email = payload.email;
const emailVerified = payload.email_verified;
const picture = payload.picture;
.....
But I have implemented my intents in the following way and I can't use the conv parameter:
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
async function welcome(agent) {
agent.add(`Welcome`);
...
}
. . .
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
agent.handleRequest(intentMap);
});
I need to get the user information as the case above but using my variable agent instead of conv
Thanks all of you
In your second code snippet, you seem to be using the dialogflow-fulfillment library. You cannot use this library to do Google Sign-In. You will need to switch over to the actions-on-google library, which has the pattern in the first snippet.

Google cloud dialogflow intent detection nodejs example not working

I am trying to implement a very simple dialogflow agent integration with nodejs.
Here is what I did so far
I followed the code from Intent detection
I added the service account private key file .json to my server.
I added the environment variable GOOGLE_APPLICATION_CREDENTIALS with the path to my .json private key file.
Here is the code I am trying to run right now:
require('dotenv').config()
const projectId = 'gg-chatbot-216808';
const sessionId = 'quickstart-session-id';
const query = 'hello';
const languageCode = 'en-US';
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient();
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
},
},
};
// This prints the private key path correctly.
console.log(process.env.GOOGLE_APPLICATION_CREDENTIALS);
// Send request and log result
sessionClient
.detectIntent(request)
.then(responses => {
console.log('Detected intent');
const result = responses[0].queryResult;
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
})
.catch(err => {
console.error('ERROR:', err);
});
Then I get this error in the console when I run this file
Auth error:Error: invalid_user: Robot is disabled.
ERROR: { Error: 14 UNAVAILABLE: Getting metadata from plugin failed with error: invalid_user: Robot is disabled.
at Object.exports.createStatusError (/var/www/html/google_auth/node_modules/grpc/src/common.js:87:15)
at Object.onReceiveStatus (/var/www/html/google_auth/node_modules/grpc/src/client_interceptors.js:1188:28)
at InterceptingListener._callNext (/var/www/html/google_auth/node_modules/grpc/src/client_interceptors.js:564:42)
at InterceptingListener.onReceiveStatus (/var/www/html/google_auth/node_modules/grpc/src/client_interceptors.js:614:8)
at callback (/var/www/html/google_auth/node_modules/grpc/src/client_interceptors.js:841:24)
code: 14,
metadata: Metadata { _internal_repr: {} },
details: 'Getting metadata from plugin failed with error: invalid_user: Robot is disabled.' }
i also faced a similar issue for my angular bot.
What i did was, instead of using using the google_credentials from the json file, i created an object with private_key,client_email {these values can be taken from the service account private key file .json}, and passed the object while setting up the session client.
var config = {
credentials: {
private_key: "YOUR_PRIVATE_KEY",
client_email: "YOUR_CLIENT_EMAIL"
}
}
const sessionClient = new dialogflow.SessionsClient(config);
note: do copy the full private_key string from .json. It will start as "-----BEGIN PRIVATE KEY-----\n......" .
Also, in GCP go to the project->IAM then try setting role for the service as DIALOGLOW API ADMIN. Check if this works.
If this has not been resolved yet , the solution is to provide "fileKey" inside sessionClient.
const sessionClient = new dialogflow.SessionsClient({
fileKey:" path of your credentials.json file"
});
or
let filePath = process.env.GOOGLE_APPLICATION_CREDENTIALS ="Location of credentials file".
const sessionClient = new dialogflow.SessionsClient({
fileKey:filePath
});
this will even work if there is no system env variable is set as GOOGLE_APPLICATION_CREDENTIALS.
Hope this is helpful.

Categories

Resources