How to send solana via my app in vanilla js? - javascript

Trying to do a simple send and receive function in Solana with vanilla JS.
Below is my send function that works fine, and now I want a receive function.
Where the provider would get Solana transferred from my treasury wallet.
I'm not sure what approach I should have, just starting out. Is there a way to just move things around in this function? Or do I have to have a totally different approach?
Thanks!
async function transferSOL() {
// Detecing and storing the phantom wallet of the user (creator in this case)
var provider = phantom;
// Establishing connection
var connection = new web3.Connection(
web3.clusterApiUrl('devnet'),
);
var transaction = new web3.Transaction().add(
web3.SystemProgram.transfer({
fromPubkey: provider.publicKey,
toPubkey: treasuryWallet.publicKey,
lamports: 0.1 * web3.LAMPORTS_PER_SOL - 100
}),
);
// Setting the variables for the transaction
transaction.feePayer = await provider.publicKey;
let blockhashObj = await connection.getRecentBlockhash();
transaction.recentBlockhash = await blockhashObj.blockhash;
// Request creator to sign the transaction (allow the transaction)
let signed = await provider.signTransaction(transaction);
let signature = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(signature);
}

If you want to transfer from your treasury to the user, then you must sign for treasuryWallet somehow. In your case, it looks like you already have the keypair available to you in your app, so you can simply do:
var transaction = new web3.Transaction().add(
web3.SystemProgram.transfer({
toPubkey: provider.publicKey,
fromPubkey: treasuryWallet.publicKey,
lamports: 0.1 * web3.LAMPORTS_PER_SOL - 100
}),
);
// Setting the variables for the transaction
transaction.feePayer = treasuryWallet.publicKey;
let blockhashObj = await connection.getRecentBlockhash();
transaction.recentBlockhash = await blockhashObj.blockhash;
// Request creator to sign the transaction (allow the transaction)
transaction.sign(treasuryWallet);
let signature = await connection.sendRawTransaction(transaction.serialize());
await connection.confirmTransaction(signature);
Note that this is very unsafe! Everyone who uses your web app has full access to the treasury funds if the keypair is exposed, since they can just sign all the transactions that they want with it.

Related

Node.js script running automatically on the first and last of the month

What I am trying to create is a script in node.js that will be added in a server of a web page that we are building. I want the script to do the following: In the first of every month to count the users inside my database and then create a set of tokens based on the amount of users logged in that current date. Specifically: 0,8 * numberOfUsers * 100. Then in the last day of every month count the users again and refund the tokens that were generated in the first of the month back to all the users (old and new). I also want to try that this runs automatically every time the server goes live and not have to execute it every time.
I am using the cron.schedule method and to check if it works I run for every minute the block that counts the first users(the one that is supposed to be in the first of the month) and every two minutes the block that gets again the new number of users and refunds the Tokens (the one that is supposed to be in the last of the month) . Here is my code:
const { Pool } = require('pg');
const cron = require('node-cron');
// Connection configuration
const config = {
user: '',
password: '',
host: '',
database: ''
};
// Create a new connection pool
const pool = new Pool(config);
async function countUsers() {
// Get number of users
const query = `SELECT COUNT(*) as count FROM users`;
const res = await pool.query(query);
const numUsers = +res.rows[0].count;
console.log(numUsers);
return numUsers;
}
async function refundTokens(tokensToRefund,numUsers) {
console.log("I am here");
console.log(numUsers);
if (numUsers !== 0 && !isNaN(tokensToRefund)) {
// Calculate amount of tokens to give to each user
const tokensPerUser = Math.round(tokensToRefund / numUsers);
// Update tokens for all users
const query = `UPDATE users SET tokens = tokens + $1`;
await pool.query(query, [tokensPerUser]);
}
}
cron.schedule('* * * * *', async () => {
const numUsers = await countUsers();
// Calculate number of tokens to refund (80% of tokens)
const tokensToRefund = 0.8 * numUsers * 100;
// Store number of tokens to refund
localStorage.setItem('tokensToRefund', tokensToRefund);
});
cron.schedule('*/2 * * * *', async () => {
// Get number of tokens to refund from local storage
const tokensToRefund = localStorage.getItem('tokensToRefund');
// Get number of users
const numUsers = await countUsers();
refundTokens(tokensToRefund, numUsers);
});
The first block seems to be running correctly every minute but the second one never does run. I see it because the console.log i have inside the refundTokens() function are not given. What seems to be the problem?
Your problem might be that there's no localStorage in NodeJS, as Marc said in comments. Try saving your tokens in some tmp file using Node's.
const fs = require('fs')
const numUsers = await countUsers();
// Calculate number of tokens to refund (80% of tokens)
const tokensToRefund = 0.8 * numUsers * 100;
// Store number of tokens to refund
await fs.writeFile('tmp', tokensToRefund);
// Get number of tokens to refund from local storage
const tokensFromFile = await fs.readFile('tmp');
const tokensToRefund = parseInt(tokensFromFile);
// Get number of users
const numUsers = await countUsers();
refundTokens(tokensToRefund, numUsers);
I would also write some separate helper function for this and add an Error handling, because both write and read functions could throw an error
P.S.: I would also recommend not to schedule your cronjobs in code for those small tasks, but to set them directly in console or in your backend panel. It would save you a huge amount of time and it will be much easier to configure them later. For your case, I consider it will be better. Just write your code as normal and add a cronjob to execute your js whenever you want

Instruction 0: incorrect program id for instruction. Works on devnet but not mainnet

I am trying to make a spl-token transfer from a user wallet. The code below works perfectly on the devnet but throws Transaction simulation failed: Error processing Instruction 0: incorrect program id for instruction . It creates an associated token account but fails on instruction.
How would the program id be incorrect if solana's token program id never changes?
let mint = new web3.PublicKey(mintAccount)
let toWallet = new web3.PublicKey("ADDRESS")
let owner = new web3.PublicKey(fromWallet.publicKey)
const createTransaction = async (mint, minerWallet, owner) => {
const transaction = new web3.Transaction()
const destinationAccount = await splToken.Token.getAssociatedTokenAddress(
splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
splToken.TOKEN_PROGRAM_ID,
mint,
toWallet,
false
)
const sourceAccount = await splToken.Token.getAssociatedTokenAddress(
splToken.ASSOCIATED_TOKEN_PROGRAM_ID,
splToken.TOKEN_PROGRAM_ID,
mint,
owner,
false
)
console.log("sending from " + sourceAccount.toBase58() + "to " + destinationAccount.toBase58())
transaction.feePayer = fromWallet.publicKey
let blockhashObj = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhashObj.blockhash;
let signature = '';
try {
const destinationAccountInfo = await connection.getAccountInfo(destinationAccount)
const destTokenAccountMissing = !destinationAccountInfo
if (destTokenAccountMissing) {
console.log("creating token account cuz it needs one")
transaction.add(
splToken.Token.createAssociatedTokenAccountInstruction(
splToken.ASSOCIATED_TOKEN_PROGRAM_ID, // always associated token program id
splToken.TOKEN_PROGRAM_ID, // always token program id
mint, // mint (which we used to calculate ata)
destinationAccount, // the ata we calcualted early
toWallet, // token account owner (which we used to calculate ata)
fromWallet.publicKey // payer, fund account, like SystemProgram.createAccount's from
)
);
}
transaction.add(
splToken.Token.createTransferInstruction(
splToken.TOKEN_PROGRAM_ID,
sourceAccount,
destinationAccount,
fromWallet.publicKey,
[],
1
)
)
signature = await sendTransaction(transaction, connection);
console.log('info', 'Transaction sent:', signature)
await connection.confirmTransaction(signature, 'processed');
console.log('success', 'Transaction successful!', signature);
return true
} catch (error) {
console.log('error', `Transaction failed! ${error?.message}`, signature);
return false
}
}
IncorrectProgramId from the token program means that the owner of the account wasn't the expected spl-token program.
In your case, it might be that sourceAccount hasn't been created on mainnet yet.
Make sure the program id in your lib.rs (if you're using Anchor) is correct, and that this program id matches the one provided in Anchor.toml.
Also print the program-id in your provider to cross-check that your Typescript frontend actually uses the right program.
If you use the anchor framework, you can get the program-idea (to be inserted in all the spots mentioned above!), by running solana-address -k target/deploy/program-keypair.json from within the directory where your Anchor.toml is stored.

Question about signature verification using Cloud KMS

I'm trying to verify a signature generated with Google's cloud KMS, but I keep getting invalid responses.
Here's how I'm testing it:
const versionName = client.cryptoKeyVersionPath(
projectId,
locationId,
keyRingId,
keyId,
versionId
)
const [publicKey] = await client.getPublicKey({
name: versionName,
})
const valueToSign = 'hola, que tal'
const digest = crypto.createHash('sha256').update(valueToSign).digest()
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest,
},
})
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
if (!valid) return console.log('INVALID SIGNATURE')
console.log('SIGNATURE IS VALID!')
// output: INVALID SIGNATURE
This code will always log 'INVALID SIGNATURE' unless I use the original message instead of its hash:
const valid = crypto.createVerify('sha256').update(valueToSign).verify(publicKey.pem, signResponse.signature) // true
But using a local private key, I'm able to sign messages and verify them using their hashes:
const valueToSign = 'hola, the tal'
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
Why is it? Is there something different about kms signatures?
Based on this example from the crypto module documentation and your observations, I'd say that you might've misunderstood how client.asymmetricSign works. Let's analyze what happens:
Your local private key code:
const valueToSign = 'hola, the tal'
// Create sha256 hash
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
// Let signer sign sha256(hash)
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
// We now got sign(sha256(hash))
// Let verifier verify sha256(hash)
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
We are verifying sign(sha256(hash)) using verify(sha256(hash)).
Your KMS code:
const valueToSign = 'hola, que tal'
// Create sha256 hash
const digest = crypto.createHash('sha256').update(valueToSign).digest()
// Let KMS sign the hash
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest, // we already say "we hashed our data using sha256"
},
});
// We now got `sign(hash)`, NOT `sign(sha256(hash))` (where hash == digest)
// Let verifier verify sha256(hash)
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
We are verifying sign(hash) using verify(sha256(hash)).
Basically, locally you are signing your hash and verifying the signed hash. With KMS you are signing your data and verifying the signed hash, which is actually your signed data, hence your 2nd attempt with .update(valueToSign) works.
Solution? Hash your sha256 hash again before letting KMS sign it, since KMS expects the sha256 hash of the to-be-signed data, while the crypto expects the to-be-signed data (which it'll hash itself given the algorithm you passed to createSign).
The answer is very similar to the one from Kevin but from a different point of view, in other words.
When you use crypto.createSign(<algorithm>) and crypto.createVerify(<algorithm>) you are indicating the digest algorithm that will be used for signature creation and verification, respectively.
When you call update on the returned Sign and Verify objects you need to provide your data as is, crypto will take care of digesting that information as appropriate when you sign or verify it later.
In contrast, the GCP KMS asymmetricSign operation requires a message digest produced with the designated algorithm over your original data as argument. This is why you need to calculate the message digest with crypto.createHash first.
But please, as indicated, be aware that this fact doesn't change the behavior of the crypto verification process, it always requires the original data as input, this is why your code works when you pass your original data without hashing.
Although you provided a working example in your question for reference the GCP documentation provides additional ones.

Uniswap JS SDK and Ethers js trying to swap eth for tokens

I am trying to swap tokens using a js script that can be used to swap ethereum for any token. The problem is that some of the tokens I try and swap for will provide the error "UnhandledPromiseRejectionWarning: InsufficientInputAmountError". However, if I try and swap for a different token it works as it should. I know the token that raises the error is compatible with uniswap since I have bought some through their website and received no error.
const {ChainId, Fetcher, WETH, Route, Trade, TokenAmount, TradeType, Percent, Token} = require('#uniswap/sdk');
const {ethers} = require("ethers");
let Web3 = require('web3');
let web3 = new Web3(new Web3.providers.HttpProvider("INFURA_KEY"));
function toHex(Amount) {
return `0x${Amount.raw.toString(16)}`;
}
const chainId = ChainId.MAINNET;
const tokenAddress = '0x094F00Cb5e31Ab6164E3CAcb654e8D6c2b3b471C';
const provider = new ethers.providers.EtherscanProvider('homestead', 'ETHERSCAN_KEYY');
const init = async () => {
const gas = await web3.eth.getGasPrice();
const token = await Fetcher.fetchTokenData(chainId, tokenAddress, provider);
const weth = WETH[token.chainId];
const pair = await Fetcher.fetchPairData(token, weth, provider);
const amountIn = '10000000000000000';
const route = new Route([pair], weth);
const trade = new Trade(route, new TokenAmount(weth, amountIn), TradeType.EXACT_INPUT);
const slippageTolerance = new Percent('1', '100');
const amountOutMin = toHex(trade.minimumAmountOut(slippageTolerance));
const path = [weth.address, token.address];
const to = 'MY_KEY';
const deadline = Math.floor(Date.now()/1000) + 60*20;
const value = toHex(trade.inputAmount);
const signer = new ethers.Wallet('MY_PRIVATE_KEY');
const account = signer.connect(provider);
const uniswap = new ethers.Contract(
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
['function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)'],
account
);
const tx = await uniswap.swapExactETHForTokens(
amountOutMin,
path,
to,
deadline,
{value, gasPrice: gas}
);
console.log(tx);
}
init();
Functioning Token Address: 0x6b175474e89094c44da98b954eedeac495271d0f
Non-Functioning Address: 0x094F00Cb5e31Ab6164E3CAcb654e8D6c2b3b471C
The issue seems to be when defining const trade since the script doesn't run beyond that. I have looked through and don't know of any reason that most tokens seem to work but a few don't (even if they work on the uniswap website). I am pretty new to JS and working with ethers/uniswap so any insight would be greatly appreciated.
You have to make a pool for that token on uniswap. I was also facing the same issue for Eth to usdc swap on the Goerli network. First, add the token into the pool and then make a pool with ether.

Generating Twillio Access Tokens using Node JS

I'm developing an application that uses Twillios Programmable Video API.
I'm new to using Node JS, but the documentation has been fairly straightforward, however I still have a few questions.
Here's code I am referencing.
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
// Used when generating any kind of tokens
const twilioAccountSid = 'ACxxxxxxxxxx';
const twilioApiKey = 'SKxxxxxxxxxx';
const twilioApiSecret = 'xxxxxxxxxxxx';
const identity = 'user';
// Create Video Grant
const videoGrant = new VideoGrant({
room: 'cool room'
});
// Create an access token which we will sign and return to the client,
// containing the grant we just created
const token = new AccessToken(twilioAccountSid, twilioApiKey, twilioApiSecret);
token.addGrant(videoGrant);
token.identity = identity;
// Serialize the token to a JWT string
console.log(token.toJwt());
In this specific example provided by Twillio, the video grant which I assume is mandatory is explicitly referenced, however that would mean for this specific token generator, the users can only enter rooms of that name.
I was wondering if it was possible to reference the room before configuring the token. Something similar to how the identity is a variable that's entered into the function before the token is output.
In addition, are there any required dependencies or libraries when creating tokens outside of Twillios own function environment?
Any answers, suggestions, or references are greatly appreciated.
Twilio developer evangelist here.
It is possible to supply the room name as a variable too. You might want to create a function that can take an identity and room name as arguments and returns an access token. Something like this:
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
// Used when generating any kind of tokens
const twilioAccountSid = 'ACxxxxxxxxxx';
const twilioApiKey = 'SKxxxxxxxxxx';
const twilioApiSecret = 'xxxxxxxxxxxx';
function generateToken(identity, roomName) {
const videoGrant = new VideoGrant({
room: roomName
});
const token = new AccessToken(twilioAccountSid, twilioApiKey, twilioApiSecret);
token.addGrant(videoGrant);
token.identity = identity;
return token.toJwt();
}
Then you can use the function like:
const token = generateToken("Stefan", "StefansRoom");
Let me know if that helps at all.

Categories

Resources