How to Approve spend of one Token with Web3.js? - javascript

I seek via my web page of test to create a button which allows to authorize the expenditure of a Contract (Token)..
If I go to the page and I click I would like the script to load web3 (it loads well) then if I press the button that Metamask authorizes the spending of the contract.
Metamask opens fine and does request the connection for my test site on the BSC in Web3js. However I can't find the exact code for the approve function.
Here is the code:
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/web3/1.7.0/web3.min.js'></script>
</head>
<button onclick="approvebutton();">Approve button to authorize tokens to be spent</button>
<script type="text/javascript">
if (typeof window.ethereum !== 'undefined') {
ethereum.request({ method: 'eth_requestAccounts' });
} else {
alert('Please install metamask')
}
var Web3 = require('web3');
const web3 = new Web3('https://bsc-dataseed1.binance.org:443');
async function approvebutton() {
/// APPROVE FUNCTION WITH THE CONTRACT
}
</script>
I tried this approach but it doesn't work (metamask confirmation won't show up):
if (typeof window.ethereum !== 'undefined') {
ethereum.request({ method: 'eth_requestAccounts' });
} else {
alert('Please install metamask')
}
var Web3 = require('web3');
const web3 = new Web3('https://bsc-dataseed1.binance.org:443');
const Contract = ('0xContractAddress');
const spenderAdr = ('0xSpenderAddress');
const amount = ('AmountTokensNumber')
async function approvebutton(Contract,spenderAdr){
Contract.methods.approve(spenderAddr, amount).send({
from: ownerAddr
})
}
Metamask won't show up to confirm the TX.

First of all, the approve method takes 2 parameters, the spender and the amount so it will be something like this:
Contract.methods.approve(spenderAddr, amount).send({
from: ownerAddr
})
The gas parameter is optional.

From the example code, I think you're missing the ABI (or Json Interface) for the contract and the instantiation of the contract via web3.eth.Contract() with the ABI and the contract address.
var Contract = new web3.eth.Contract(jsonInterface[, address][, options])
From that Contract instance you can then call the methods in the ABI, and that should trigger the Metamask modal.
From the docs:
Contract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
.then(function(receipt){
// receipt can also be a new contract instance, when coming from a "contract.deploy({...}).send()"
});
Or in your case, something along the lines of:
await Contract.methods.approve(spenderAddr, amount).send({ from: ownerAddr })
I also think that you're missing an await inside the approvebutton() function, to await the "promisevent" that the method call on the contract returns.
(Source https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#, https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#id26)

Related

Calling a smart contract function using metamask with ether.js

I'm completely new to both blockchain and JavaScript.
I'm trying to create a simple web page where people could generate a "wedding" smart contract that basically store their 2 names. For this I have created a WeddingCerficate contract which store the names and have a getter function, and a WeddingCertificateFactory That enable me to generate a WeddingCertificate. You can find the code of the smart contracts in solidity below.
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract WeddingCertificate{
string private spouse1;
string private spouse2;
constructor(string memory _spouse1, string memory _spouse2) {
spouse1 = _spouse1;
spouse2 = _spouse2;
}
function getSpouses() public view returns (string memory,string memory) {
return (spouse1,spouse2);
}
}
contract WeddingCertificateFactory{
event Wedding(string _spouse1, string _spouse2, address indexed contract_adress );
function Unite(string memory _spouse1, string memory _spouse2)public returns (bool success) {
WeddingCertificate wedding = new WeddingCertificate(_spouse1, _spouse2);
emit Wedding(_spouse1,_spouse2 , address(wedding));
return true ;
}
}
I deployed the WeddingCertificateFactory on Goerli Tesnet. And now I'm trying to make a function in javascript (using ether.js) to enable a user to create his own weddingCertificate directly on a web interface.
For this I wrote the function below but for some reasons this only generates the new Wedding certificate once out 20. And even when it actually work, the two last print aren't visible in the console.
I do not get any error (at least that I can see in the console) when I test the function and nothing happen.
I'm not familiar with async in JavaScript, I also tried the .then( syntax but I didn't notice any difference.
async function CreateWedding(){
const spouse1 = document.getElementById("spouse1").value;
const spouse2 = document.getElementById("spouse2").value;
if (spouse1.length > 0 && spouse2.length >0) {
console.log(`spouse 1: ${spouse1} , spouse2 : ${spouse2} `);
const ethereum = window.ethereum ;
const accounts = await ethereum.request({
method: "eth_requestAccounts",
});
const provider = new ethers.providers.Web3Provider(ethereum, "any");
const walletAddress = accounts[0];
const signer = provider.getSigner(walletAddress);
let abi = [
" function Unite(string memory _spouse1, string memory _spouse2)"
];
const contractAddress = "0x2556Ff7f7F1c013bBB60bD120E1828032Cd84cc4"; //WeddingFactory Contract
const contract = new ethers.Contract(contractAddress, abi, signer);
console.log("sending the contract");
tx = await contract.Unite(spouse1,spouse2);
console.log(tx);
console.log("finished");
} else {
alert("Please enter 2 names");
}
}
Try creating your abi method in web3client.app.
It has code generater for ether.js which yoy can use directly in your app.
If there is an actual issue in your contract it would fail there itself.

.allowance method for importing erc20 token to metamask

I was trying out the .allowance method to import erc20 token in my case PKS to metamask.
The issue is I don't get the metamask popup for importing the erc20 token but I get the balances PKS token for the particular account without importing .
need this output before fetching balances :
Is there any way to adjust the code it should first ask for import and then fetch the balance.
here's my code so far and I am calling getErcBal in useEffect :
async function getErcBal(){
var WEB3 = await connection();
var currAddr = WEB3.address;
var web3 = WEB3.web3Conn;
if(WEB3.isMeta){
setuseraddress(currAddr);
setContractAdr(dethContractAddr);
if(dethContractAddr){
var dethContract = new web3.eth.Contract(DETH, dethContractAddr);
var getAllowance = await dethContract.methods
.allowance(currAddr, dethContractAddr)
.call();
}
var tokenDethBln = await dethContract.methods.balanceOf(currAddr).call();
}
var PksVal = 0;
if (tokenDethBln && tokenDethBln > 0) {
PksVal = tokenDethBln / 1000000000000000000;
}
setPksbalance(PksVal.toFixed(4))
}
}
Allowance method is not for importing funds. Allowance function uses two addresses, first one is owner address, and second one is spender address. If you previously approved someone to use your funds then it will return some value, otherwise it will give zero.
If you want to import token to metamask, choose the account and you will see the import token option:
Get the ERC20 token address and paste it to the field. It will automatically populate the "symbol" field:
Click on "Add Custom Token" then you will get next screen:
click on "Import Tokens"

Web3 not treating web3.eth.Contract as a contructor

I'm writing a JS file to provide functionality to a smart contract. From what I can tell, MetaMask is injecting web3 into the webpage, but when I attempt to create a contract object, my browsers console (brave browser) is stating that web3.eth.Contract is not a constructor.
I've looked in the object provided by my browsers console, and I don't see the Contract constructor. Is this normal? Or do you think web3 may be incorrectly installed? I've hit a wal at this point.
var blockContractJSON = $.getJSON("../build/contracts/Blocks.json", function(data) {
return data;
});
console.log(blockContractJSON)
// console.log(blocksContract)
var blocksContract;
var currentUser;
var web3js;
console.log(web3js);
// console.log(blockContractJSON);
// defines contract address and initializes contract functions
var startApp = function() {
var contractAddress = "0x2520127E14E8A14C67Ee2B561714ADae53D48110";
console.log('got the contract'); <- web3 not passing to this line?
blocksContract = new web3js.eth.Contract(blockContractJSON, contractAddress);
// console.log(blocksContract);
var checkAccounts = setInterval(function() {
if (web3js.eth.accounts[0] !== currentUser) {
currentUser = web3js.eth.accounts[0];
}
}, 100)();
};
// adds in web3
window.addEventListener('load', function() {
console.log('item loaded from page is')
console.log()
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
console.log('using metamask');
console.log(web3)
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
console.log(web3js)
} else {
alert('install metamask')
}
startApp();
});
Works for me
const Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("infuralink"));
const ABI=[];
const Contract = new web3.eth.Contract(ABI,"contractaddress");
Try using Ethereum lib instead (web3-eth)
[https://web3js.readthedocs.io/en/v1.2.0/web3-eth.html][1]
var eth = new Eth('http://localhost:8545');
check first parameter is the abi node of the JSON not the abi metadata
new eth.Contract(contractAbi.abi, CONTRACT_ADDRESS);

Better pattern to detect web3 default account when using metamask

Context: I want to use blockies to render an identicon on the page, i get the defaultAccount from web3, for this, the user has to be logged on to metamask with a selected address from his wallet.
The problem: the web app seems to not detect the web3 object on the load event of the page, wchih is the recommended place to detect it.
The code: below is inspired from recommendations at:
https://github.com/MetaMask/metamask-plugin/issues/1158
https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#partly_sunny-web3---ethereum-browser-environment-check
I keep having intermittent behaviour, sometimes web3 is there and sometimes it is not, the only solution I can think of is to have a timer, but that seems to me a bit too simplistic, I would prefer something more elegant.
Question: Is there a better solution to detect the defaultAccount from web3 when the page loads?
function startApp() {
GenerateIdenticon();
}
window.addEventListener('load', function () {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
if (web3.currentProvider.isMetaMask === true) {
if (typeof web3.eth.defaultAccount === 'undefined') {
document.body.innerHTML = '<body><h1>Oops! Your browser does not support Ethereum Ðapps.</h1></body>';
}
else {
startApp();
}
}
else {
alert('No web3? Please use google chrome and metamask plugin to enter this Dapp!', null, null);
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
function _Connect(callback){
if(typeof web3 !== 'undefined') {
web3 = new Web3(window.web3.currentProvider);
web3.version.getNetwork((err, netId) => {
switch (netId) {
case "1":
callback('Switch Network', null);
break
case "2":
console.log('This is the deprecated Morden test network.');
callback('Switch Network', null);
break
case "3":
console.log('Connected to the ropsten test network.');
web3.eth.defaultAccount = web3.eth.accounts[0];
if(!web3.eth.defaultAccount){
console.log('Log into metamask');
_Connect(callback);
}else{
// Success
console.log(`Web3 ETH Account: ${web3.eth.defaultAccount}`);
callback(false, web3.eth.defaultAccount);
}
break
default:
console.log('This is an unknown network.');
callback('Switch Network', null);
}
});
} else {
console.log(`Failed: Web3 instance required, try using MetaMask.`);
callback('Install Metamask', null);
}
}
There is a delay when Chrome inserts the MetaMask Web3 library so the timeout is necessary (1 second timeout should be enough).
After the timeout, you check if the web3 global object exists and then read the default account.
If it doesn't exist, then insert your own web3 object.

How to use Google Dart with Firebase Simple login (pass function inside function)

Has anybody figured out how to use the Firebase Simple Login with Google Dart? I am trying to figure out how to define function(error, user){} when calling FirebaseSimpleLogin. Both error and user() are objects.
This is the sample javascript code from Firebase
var myDataRef = new js.Proxy(js.context.Firebase, 'https://johnstest1.firebaseIO.com/');
var myDataRef = new Firebase('https://johnstest1.firebaseIO.com/');
var auth = new FirebaseSimpleLogin(myDataRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
} else {
// user is logged out
}
});
This is the code added to the html file for use by both Dart and Firebase
<script type='text/javascript' src='https://cdn.firebase.com/v0/firebase.js'></script>
<script type='text/javascript' src='https://cdn.firebase.com/v0/firebase-simple-login.js'></script>
<script type="application/dart" src="firebasetestlogin.dart"></script>
<script src="packages/browser/dart.js"></script>
<script src="packages/browser/interop.js"></script>
In the .dart file the javascript library has been imported using pubspec.yaml
import 'package:js/js.dart' as js;
In the main() this line of code works fine and I am able to write data to the database. The line that is commented out is the original javascript line while the next line is the Dart version and it works.
js.scoped((){
// var myDataRef = new Firebase('https://johnstest1.firebaseio.com');
var myDataRef = new js.Proxy(js.context.Firebase, 'https://johnstest1.firebaseIO.com/');
});
This is the same code from main with the line for Firebase Simple Login Added. I have been trying to figure out how to write the code for function(error, user).
js.scoped((){
// var myDataRef = new Firebase('https://johnstest1.firebaseio.com');
var myDataRef = new js.Proxy(js.context.Firebase, 'https://johnstest1.firebaseIO.com/');
//var auth = new FirebaseSimpleLogin(js.context.Firebase(myDataRef, function(error, user){}{}));
var auth = new js.Proxy(js.context.FirebaseSimpleLogin(myDataRef, js.context.function(error, user)));
});
When you want to use Dart callback functions in Js you have to create a Callback and use it as paramter.
The dart equivalent of your first pasted js code is :
var myDataRef = new js.Proxy(js.context.Firebase,
'https://johnstest1.firebaseIO.com/');
var auth = new js.Proxy(js.context.FirebaseSimpleLogin, myDataRef,
new js.Callback.many((error, user) {
if (error != null) {
// an error occurred while attempting login
window.console.log(error);
} else if (user != null) {
// user authenticated with Firebase
window.console.log('User ID: ${user.id}, Provider: ${user.provider}');
} else {
// user is logged out
}
}));
Note : you can avoid js.scoped that is not needed since few versions of js package.
This is some sample code to use Dart to log into Firebase. This combines the answers from Alexandre Ardhuin into one post with some additional code.
The example will:
Get firebase reference and checks to see if user is online
Create a new Firebase user using email and password login
Login into Firebase
Adds a child_added listener and prints any data in the database to the console
Push some data to Firebase and trugger the child_added to display data.
Logout of Firebase
Add the js package http://pub.dartlang.org/packages/js to your program
Add these three lines to the HTML file.
<script src="packages/browser/interop.js"></script>
<script type='text/javascript' src='https://cdn.firebase.com/v0/firebase.js'></script>
<script type='text/javascript' src='https://cdn.firebase.com/v0/firebase-simple-login.js'></script>
Put this code in the .dart file.
var YOUR_FIREBASE_PATH = 'https://johnstest1.firebaseIO.com/';
var emailAddress = "emailAddress#xyz.com";
var password = "password";
var myDataRef = new js.Proxy(js.context.Firebase, YOUR_FIREBASE_PATH);
// Firebase
var auth = new js.Proxy(js.context.FirebaseSimpleLogin, myDataRef,
new js.Callback.many((error, user) {
if (error != null) {
window.console.log("Firebase login returned a null");
// an error occurred while attempting login
window.console.log(error);
} else if (user != null) {
// user authenticated with Firebase
window.console.log('User ID: ${user.id}, Provider: ${user.provider}');
} else {
window.console.log("User is logged out");
// user is logged out
}
})
);
Create a new user using email and password to login
// Create a new user using email and password
auth.createUser(emailAddress, password,
new js.Callback.many((error, user) {
if (error != null && user != null)
{
if (!error) {
window.console.log('User Id: ' + user.id + ', Email: ' + user.email);
}
}
})
);
Firebase login.
// Login to firebase
auth.login('password', js.map({'email': emailAddress, 'password': password}));
Add a child_added callback and if a new child gets added to the databes this will get called
// Add a listener for for child_added and gets all the data
myDataRef.on('child_added',
new js.Callback.many((snapshot, String previousChildName) {
try {
final message = snapshot.val();
print("${message.name} : ${message.text}");
}
catch (e) {}
}));
Push some data to Firebase and the child_added callback will print whatever is in the database to the console.
// Push some data to Firebase
myDataRef.push(js.map({"name": 'Mark', "text": 'Works with Dart test 2'}));
Log user out of Firebase
// Logout
auth.logout();
Now there is a dart-team-initiated dart wrapper for Firebase : https://pub.dartlang.org/packages/firebase .
With the usage of Future, dart version is way awesomer than js version.

Categories

Resources