Solana - How to get the balance from my Phantom wallet? - javascript

I'm working on a web app that can connect to Phantom Wallet. I've established the connection and have successfully retrieved the wallet's public key. The problem is, I can't seem to find any solution to get the account balance.
For reference, I wanted to display the account balance just like how solanart.io displays it.
Note that I've gone through all related docs (Solana/web3.js, Solana JSON RPC API etc). Please guide me as I'm still new to JSON RPC API.
For a heads up, I'm using vanilla js.
try {
const resp = window.solana.request({
method: "getAccountTokenBalance",
params: [
id, //wallet's public key
{
encoding: "base58",
},
],
});
console.log(resp);
} catch(err) {
// error message
}

The RPC method that you're using does not exist. You'll want to use getBalance to get the SOL on the wallet: https://docs.solana.com/developing/clients/jsonrpc-api#getbalance
To get all of the non-SOL token balances owned by that wallet, you'll have to use getTokenAccountsByOwner using that wallet id: https://docs.solana.com/developing/clients/jsonrpc-api#gettokenaccountsbyowner

import { useWallet } from '#solana/wallet-adapter-react'
import { LAMPORTS_PER_SOL,clusterApiUrl } from '#solana/web3.js'
import * as anchor from '#project-serum/anchor'
const wallet = useWallet()
const SOLANA_HOST = clusterApiUrl("devnet")
const connection = new anchor.web3.Connection(SOLANA_HOST)
let lamportBalance
if (wallet?.publicKey) {
const balance = await connection.getBalance(wallet.publicKey)
lamportBalance=(balance / LAMPORTS_PER_SOL)
}

This method works for me to get SOL Balance
const [userSOLBalance, setSOLBalance] = useState<number>()
if (wallet.publicKey) {
const SOL = connection.getAccountInfo(wallet.publicKey)
SOL.then((res) => setSOLBalance(res.lamports / LAMPORTS_PER_SOL))
}

Related

Update 'profiles' on Supabase with RLS

I'm currently attempting to use Supabase's JavaScript API to update a row in my 'profiles' database, which has RLS on, via my backend.
This is being done following Stripe sending me a webhook indicating a payment has been successful.
I won't put the full API call in, but here is my Supabase code:
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY
const supabase = createClient(supabaseUrl, supabaseAnonKey)
module.exports = async (req, res) => {
if (event.type === "checkout.session.completed") {
const userId = String(event.data.object.client_reference_id)
const { error } = await supabase.from('profiles').update({ premium: 'true' }).eq('id', userId)
if (error) {
console.log(error)
}
}
}
However, every time I try to run this, I get a 404 error. This seems to be because I have RLS on.
As a result, I have two questions:
Is it safe for me to turn RLS off?
How can I adjust my code / apply a new database policy to allow this to be accepted?

How to get a user's keypair from their public key only (Solana)?

I'm making a dApp and I want to add a button where a user (the one with their wallet connected) can send exactly 0.01 SOL to another user. I already wrote the function in my Rust program and after testing it with anchor test it seems to be working when I use my own personal wallet's Keypair to sign the transaction. However, now I am writing the event handler function in my web app's frontend and I'm not sure what to pass for the signers parameter if I want the user to sign the transaction. What do I pass if I don't know their secret key? Is there a way that I can generate a user's Keypair from their public key alone or would I need to use the Solana Wallet Adapter for this? Any help would be appreciated. This is my first time working with Solana!
This is the function:
const tipSol = async (receiverAddress) => {
try {
const provider = getProvider();
const program = new Program(idl, programID, provider);
const lamportsToSend = LAMPORTS_PER_SOL / 100;
const amount = new anchor.BN(lamportsToSend);
await program.rpc.sendSol(amount, {
accounts: {
from: walletAddress,
to: receiverAddress,
systemProgram: SystemProgram.programId,
},
signers: ?
})
console.log('Successfully sent 0.01 SOL!')
window.alert(`You successfully tipped ${receiverAddress} 0.01 SOL!`)
} catch (error) {
console.error('Failed to send SOL:', error);
window.alert('Failed to send SOL:', error);
}
}
Frontends never access private keys. Instead the flow is something like:
Frontend creates the transaction
Frontend sends the transaction to the wallet
Wallet signs the transaction
Wallet returns the signed transaction to the frontend
Frontend send the transaction
You can use the #solana/wallet-adapter to implement this on your frontend https://github.com/solana-labs/wallet-adapter
In practice it would be something like this in your frontend
export const Component = () => {
const { connection } = useConnection();
const { sendTransaction } = useWallet();
const handle = async () => {
const ix: TransactionInstruction = await tipSol(receiverKey);
const tx = new Transaction().add(ix);
const sig = await sendTransaction(tx, connection);
};
// ...
};

How do I remove the minting authority from my custom token in Solana using #solana/web3.js?

I have been able to create custom tokens using custom wallets generated using web3.Keypair.generate(), but how do I now cap the supply of these tokens or remove the minting authority of these SPL tokens?
To prevent any more minting, you'll need set the minting authority to None. In JS, you can simply set the newAuthority to null during your call to setAuthority with authorityType = MintTokens in [1]
[1] https://github.com/solana-labs/solana-program-library/blob/36e886392b8c6619b275f6681aed6d8aae6e70f9/token/js/client/token.js#L985
Expanding on Jon Cinque's answer
import { Connection, clusterApiUrl, Keypair, PublicKey } from '#solana/web3.js'
import { Token, TOKEN_PROGRAM_ID } from '#solana/spl-token'
import * as bs58 from 'bs58';
(async () => {
const connection = new Connection(clusterApiUrl('mainnet-beta'))
const bytes = bs58.decode(process.env.PRIVATE_KEY)
const account = Keypair.fromSecretKey(bytes)
const tokenMint = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
const token = new Token(connection, tokenMint, TOKEN_PROGRAM_ID, account)
await token.setAuthority(tokenMint, null, 'MintTokens', account.publicKey, [account])
})()

Google login with React and Node.js does not return requested scope

I need a additional scope data from google api in login in my app. I use react-google-login to get token in React app using this scopes:
scope='https://www.googleapis.com/auth/user.birthday.read https://www.googleapis.com/auth/user.addresses.read https://www.googleapis.com/auth/user.organization.read'
When I log in with my credentials and allow app to access requested scopes, I successfully get token.
I send this token to backend (Node.js) where I use google-auth-library to got payload from token:
import { OAuth2Client } from 'google-auth-library'
export const validateGoogleAccessTokenOAuth2 = async (idToken: string): Promise<any> => {
const CLIENT_ID = 'MY_ID'
const client = new OAuth2Client(CLIENT_ID)
const ticket = await client.verifyIdToken({
idToken,
audience: CLIENT_ID
})
const payload = ticket.getPayload()
return payload
}
Here I receive only data from profile and email scope, there is no data from requested scopes. Especially I need birthday, I also check it is allowed in my google profile to be accessed by anyone but didn't help.
Is there something what I do wrong or is there another way how to get requested scope variable from token?
After some research I found that
birthday and gender attributes are not included in payload of public token
client.verifyIdToken function just verify idToken and return its payload
to get additional information about user, in this case you have to use People API
To communicate with People API you can use googleapis npm package, also you have to meet this conditions:
Add People API to your project in Google Developer Console
Add additional scope to your FE call to google (in my case https://www.googleapis.com/auth/user.birthday.read)
you have to send idToken and AccessToken to your backend service
user have to allow share gender with your application
user's gender must be set as public in user's google profile
BE example
import { google } from 'googleapis'
import { OAuth2Client } from 'google-auth-library'
export const validateGoogleAccessToken = async (idToken, accessToken) => {
try {
const CLIENT_ID = 'YOUR_GOOGLE_APP_CLIENT_ID'
const client = new OAuth2Client(CLIENT_ID)
const ticket = await client.verifyIdToken({
idToken,
audience: CLIENT_ID
})
const payload = ticket.getPayload()
const { OAuth2 } = google.auth
const oauth2Client = new OAuth2()
oauth2Client.setCredentials({ access_token: accessToken })
const peopleAPI = google.people({
version: 'v1',
auth: oauth2Client
})
const { data } = await peopleAPI.people.get({
resourceName: 'people/me',
personFields: 'birthdays,genders',
})
const { birthdays, gender } = data
return {
...payload // email, name, tokens
birthdates, // array of birthdays
genders, // array of genders
}
} catch (error) {
throw new Error('Google token validation failed')
}
}

How to sign and send transfer transaction using waves-transaction JS library?

Please help me understand https://testnodes.wavesnodes.com/api-docs/index.html I use this api and this library https://github.com/wavesplatform/waves-transactions
I cannot send a transaction using the manual to the library or directly by POST request for api.
common mistakes:
Error: State check failed. Reason: Script doesn’t exist and proof
Error: State check failed. Reason: Transactions from non-scripted accounts must have exactly 1 proof
A POST request for url / addresses also gives an error. Provided API key is not correct.
Here is my code:
const { transfer, broadcast } = require("#waves/waves-transactions");
const seed =
"ride flee tenant tuna share buyer work west amateur review time kick";
const signedTranserTx = transfer(
{
amount: 1,
recipient: "3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8"
},
seed
);
const nodeUrl = "http://testnodes.wavesnodes.com";
broadcast(signedTranserTx , nodeUrl)
.then(resp => console.log(resp))
.catch(err => console.error(err));
If you use Waves transactions api, the request should be signed already and you can post it to /transactions/broadcast. Then you don't need your own node and you don't need your own API Key.
in your code, I see several mistakes here:
You're transferring to MAINNET address using testnet node. you
should use TESTNET address instead. in the reciepent change the
address to an address in testnet and let me know if you still get
any errors. you can create new accounts here
https://testnet.ide.wavesplatform.com/ in the tab accounts on the
top right.
Use https instead of http, const nodeUrl = "https://testnodes.wavesnodes.com/";
Add the chain id('T' for testnet and 'W' for mainnet)
Here is the code:
const { transfer, broadcast } = require("#waves/waves-transactions");
const seed =
"ride flee tenant tuna share buyer work west amateur review time kick";
const signedTranserTx = transfer(
{
amount: 100,
recipient: "3N3pJ8xAnbaSBFdAbnaKe4yu4ZXbYkatMcN"
},
seed
);
const nodeUrl = "https://testnodes.wavesnodes.com";
broadcast({ ...signedTranserTx, chainId: "T" }, nodeUrl)
.then(resp => console.log(resp))
.catch(err => console.error(err));
[Update]
The above code working good. Just a quick update since I see the new testnet URI is below link:
https://nodes-testnet.wavesnodes.com
I mean I have replace from https://testnodes.wavesnodes.com to https://nodes-testnet.wavesnodes.com then it's working maybe because we created the account from the different place.
So this is the final code:
const { transfer, broadcast } = require("#waves/waves-transactions");
const seed =
"ride flee tenant tuna share buyer work west amateur review time kick";
const signedTranserTx = transfer(
{
amount: 100,
recipient: "3N3pJ8xAnbaSBFdAbnaKe4yu4ZXbYkatMcN"
},
seed
);
const nodeUrl = "https://nodes-testnet.wavesnodes.com";
broadcast({ ...signedTranserTx, chainId: "T" }, nodeUrl)
.then(resp => console.log(resp))
.catch(err => console.error(err));

Categories