how to get meta mask public key on javascript code - javascript

web3 = new Web3(web3.currentProvider);
account = await web3.eth.getAccounts();
these code bring me metamask account address. In this situation, how can i get my metamask account's public key??? (on javascript code)

Make sure Metamask wallet extension is installed first
You can get public key of loggedin account address only. ie. not someones account address.
if (window.ethereum) {
try {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
const account = accounts[0]
const publicKey = await window.ethereum.request({
method: 'eth_getEncryptionPublicKey',
params: [account],
})
} catch (error) {
console.log({ error })
}
}
Note: accounts is your metamask wallet address
publicKey is your accounts public key

You cannot get the full public key from the wallet, you can get only the address. There is no API for it.

Related

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);
};
// ...
};

Why is a transaction consuming gas for a public view function?

I deployed the following smart contract on Ropsten ethereum test network and afterwards I tried making a transaction using #alch/alchemy-web3 npm package (Yes, I am using Alchemy API) but as you can see I got a fee on the transaction. Why is that? Aren't public view function calls supposed to cost 0 gas?
deployed smart contract
// SPDX-Lincense-Identifier: MIT
pragma solidity ^0.8.11;
contract VendingMachine {
address public owner;
mapping(address => uint256) public donutBalances;
constructor() {
owner = msg.sender;
donutBalances[address(this)] = 100;
}
function getVendingMachineBalance() public view returns (uint256) {
return donutBalances[address(this)];
}
function restock(uint amount) public {
require(msg.sender == owner, "Only the owner can restock this machine.");
donutBalances[address(this)] += amount;
}
function purchase(uint amount) public payable {
require(msg.sender == owner, "Only the owner can restock this machine.");
require(donutBalances[address(this)] >= amount, "Not enough donuts in stock to fulfill purchase request.");
require(msg.value >= amount*2 ether, "You must pay at least 2 ether / donut.");
donutBalances[address(this)] -= amount;
donutBalances[msg.sender] += amount;
}
}
transaction code javascript
const { API_URL, METAMASK_ACCOUNT_PRIVATE_KEY, METAMASK_ACCOUNT_PUBLIC_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(`${API_URL}`);
const contractAddress = '0xc7E286A86e4c5b8F7d52fA3F4Fe7D9DE6601b6F9'
const contractAPI = new web3.eth.Contract(contract.abi, contractAddress)
const nonce = await web3.eth.getTransactionCount(METAMASK_ACCOUNT_PUBLIC_KEY, 'latest');
const transaction = {
to: contractAddress, // faucet address to return eth
gas: 500000,
nonce: nonce,
data: contractAPI.methods.getVendingMachineBalance().encodeABI()
// optional data field to send message or execute smart contract
};
const signedTx = await web3.eth.accounts.signTransaction(transaction, METAMASK_ACCOUNT_PRIVATE_KEY)
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function (error, hash) {
if (!error) {
console.log("🎉 The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("❗Something went wrong while submitting your transaction:", error)
}
});
Transaction: https://ropsten.etherscan.io/tx/0x8ce3a288072809a804adac2206dc566dfb3eb3ddba3330bcb52ca6be71963b71
There are two ways to interact with a smart contract. A transaction (read-write, costs gas fees) and a call (read-only, gas free).
Your JS snippet sends a transaction. If you want to invoke the getVendingMachineBalance() function using a (gas free) call, you can use the .call() web3js method.
const contractAPI = new web3.eth.Contract(contract.abi, contractAddress);
const response = await contractAPI.methods.getVendingMachineBalance().call();

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

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))
}

Accessing a private key in a MetaMask wallet

I have a simple Dapp and I want to sign a transaction but I don't have the private key as a string.
The user is using a MetaMask wallet. After they grant web3 access to their account, how can I access the private key to sign a transaction?
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
// PRIVATE_KEY is what I'm trying to get.
Metamask doesn't share the private key directly for security reasons. And sometimes it doesn't even have the key - for instance when the user is using the Metamask UI to operate a hardware wallet account.
You'll need to construct the transaction object and pass it to the ethereum.request() method. This will open the Metamask window where the user can sign or decline the transaction request.
Code example is pretty straightforward and is in the linked documentation.
MetaMask does not give you access to a private key and never will.
The whole point of the wallet is to protect the user against malicious Dapps.
Here's an example of how you would sign your Metamask transaction:
export const mintNFT = async(url, name, description) => {
//error handling
if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) {
return {
success: false,
status: "❗Please make sure all fields are completed before minting.",
}
}
//make metadata
const metadata = new Object();
metadata.name = name;
metadata.image = url;
metadata.description = description;
//pinata pin request
const pinataResponse = await pinJSONToIPFS(metadata);
if (!pinataResponse.success) {
return {
success: false,
status: "😢 Something went wrong while uploading your tokenURI.",
}
}
const tokenURI = pinataResponse.pinataUrl;
//load smart contract
window.contract = await new web3.eth.Contract(contractABI, contractAddress);//loadContract();
//set up your Ethereum transaction
const transactionParameters = {
to: contractAddress, // Required except during contract publications.
from: window.ethereum.selectedAddress, // must match user's active address.
'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI() //make call to NFT smart contract
};
//sign transaction via Metamask
try {
const txHash = await window.ethereum
.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
return {
success: true,
status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash
}
} catch (error) {
return {
success: false,
status: "😥 Something went wrong: " + error.message
}
}
}
In this example, we're signing a transaction to mint an NFT. You can check out more details here: https://docs.alchemyapi.io/alchemy/tutorials/nft-minter#step-8-implement-the-mintnft-function

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')
}
}

Categories