How to handle approve/allowance when using custom ERC20 token? - ethereum

Suppose I want to design a game where the user places a bet (using the ERC20 token) for a coin toss, and suppose the solidity function within our game contract called to place the bet is bet(uint256 _betAmount, int256 pick).
When playing around with some code, I realized that if the bet() function contains the token.transfer() call (used to place the initial bet before simulating the coin toss), the funds would be transfered from the game contract, and not from the user, even if the caller of bet() is the user. Thus, I would need to use the token.transferFrom() function, but the user would need to set the allowance for the game contract to spend the funds on their behalf.
My question is, how could this be handled in the frontend? Is there a way for a pop-up to show up asking for permission to spend the tokens (thinking of somehting similar to metamask when executing a transaction)?

Basically the user will need to accept two transactions.
But before calling the bet function you need also to wait until the approve function is confirmed.
In a project i developed i also had to handle your exact same case and i did something like this (i used react-moralis to interact with the contract):
const handleBet = async () => {
await approve({
onSuccess: async (tx) => {
await tx.wait(1) // Wait until the transaction is mined
await bet() // Call the bet function
},
onError: () => {
console.log("error") // Handle error however you want
}
})
}

Related

Calling ethereum smart contracts with value using web3js and galanche

I'm fairly new to Ethereum smart contracts, so this might be a stupid question, but I need someone to help me out. I've set up Galanche on my machine (MacOS 11) and written a very simple currency smart contract (I don't intend to use it as an actual currency, I just want to learn about smart contracts) using truffle.
I've compiled the contract and deployed it to my Galanche blockchain successfully.
Now, I want to interact with it using web3.js. I have set up a nodejs project and installed web3. As a first test, I ran the following script:
const Web3 = require("web3");
const fs = require("fs");
const web3 = new Web3("http://192.168.178.49:7545");
const abi = JSON.parse(
fs.readFileSync("path/to/compiled/MyCoin.json").toString()
).abi;
const MyCoin = new web3.eth.Contract(
abi,
// My contract's address
"0x3265aA0A2c3ac15D0eDd67BC0fa62A446c112F98"
);
(async () => {
console.log("Starting!");
var coinCount = await MyCoin.methods
.getTotalCoins()
.call({ from: "0x2d0616BF48214513f70236D59000F1b4f395a2Fd" });
console.log("Current registered MyCoin tokens:", coinCount);
})();
The address 0x2d0616BF48214513f70236D59000F1b4f395a2Fd is the first address displayed to me in Galanche
It works as expected and returns the default amount of coins.
Now, I want to run a method called buyMyCoin which requires a payment. I tried running:
...
MyCoin
.methods
.buyMyCoin
.send(
{
from: '0x2d0616BF48214513f70236D59000F1b4f395a2Fd',
value: some_amount_of_wei
}
);
...
I'd expect that when I run this node.js script again, the first part would tell me that there are <n> total coins, but it doesn't. It just returns the same value as the last time.
Am I doing something wrong with web3.js or is this an issue with my contract?
BTW: I didn't see any funds leave the address 0x2d0616BF48214513f70236D59000F1b4f395a2Fd in Galanche, so I'm pretty sure it's not my contract...
I expect that somewhere I'd have to sign into this address using its public key, but I can't find anything about that in the web3.js docs that isn't very ambiguous...
Edit: Here's the code for my buyMyCoin method:
...
/**
* #dev Buy MyCoin
*/
function buyMyCoin() external payable {
require(msg.value > 1 gwei, "Minimum transaction is 1 gwei"); // Not very much
uint256 amount = convert(msg.value, conversionRate, true);
balances[msg.sender].owner = payable(msg.sender);
balances[msg.sender].amount += amount;
totalCoins += amount;
}
...
turns out it was just me being stupid! I was trying to run the transaction with an outrageously low amount of ether (4 gwei) which is, of course, so small that it can't be seen in the Ganache app.
The reason I was seeing no new coins being created was because my conversion code rounded the result to 0.

How to show event at frontend

I could successfully emitted event, but I don't know how to show those event.
My event is this
event Sell (
address indexed _buyer,
uint256 indexed _value
);
I could get all event in console.
const event = await tokenSale.events.Sell({}, {
fromBlock: 0,
toBlock: 'latest',
})
event.on("data", (data) => console.log("data", data))
I want to show _value in frontend.
Could you give me any advise, please?
There are lots of ways to render values in the front end. It depends on what language/library you are using. If you are using React, a nice tool for passing contract data to the front end is Drizzle

Watching Ethereum Contract State

I want to be able to watch the state/storage of an ethereum contract and know for instance when some conditions are satisfied. For instance, one account value is greater than 200, another map has more than 30 keys, etc.
What can I use for this? Is there a SaaS platform that can be used for this purpose?
EDIT: I forgot to mention that I am NOT the owner of the contract. I simply want to watch somebody's else contract.
Events are supported for this purpose.
Event Documentation
Example:
contract SomeContract {
struct AccountData {
address account;
uint value;
}
...
event AccountValueChange(address account, uint value, bytes msg);
...
function someAction() {
AccountData accountData;
// some activity with accountData...
if (accountData.value > 200) {
AccountValueChange(accountData.address, accountData.value, "Some message");
}
}
}
You can listen for the events in your client
const someContract = web3.eth.contract(abi).at(address);
myContract.events.AccountValueChange({
filter: {//Optional event filters},
fromBlock: 0
}, function(error, event){ console.log(event); })
.on('data', function(event){
console.log(event);
})
.on('error', console.error);
EDIT: Updated client code to listen to all events instead of just a single fired event.
Short answer is that you'll have to probe the EVM yourself to make this happen and then run your own node on the network that you can monitor. There is no public project I can find that will do this, and the RPC api does not provide any way to accomplish it. I used a modified Geth binary with custom transaction filters coded in and mailgun to send me emails when my conditions were met.
Events will not solve your issue because events must be included in the contract at the time of creation.

How to create an angular form that uses session storage that can be called throughout the html pages

I want to create a form on an index page that can store data via session storage. I also want to make sure that whatever data(let's say name) ... is remembered and used throughout the site with angular. I have researched pieces of this process but I do not understand how to write it or really even what it's called.
Any help in the right direction would be useful as I am in the infant stages of all of this angular business. Let me know.
The service you want is angular-local-storage.
Just configure it in your app.js file:
localStorageServiceProvider
.setStorageType('sessionStorage');
And then use it in the controller that contains whatever data you want to remember. Here is an example of a controller that loads the session storage data on initialization, and saves it when a user fires $scope.doSearch through the UI. This should give you a good place to start.
(function () {
angular.module("pstat")
.controller("homeCtrl", homeCtrl);
homeCtrl.$inject = ['$log', 'dataService', 'localStorageService', '$http'];
function homeCtrl ($log, dataService, localStorageService, $http) { {
if (localStorageService.get("query")) { //Returns null for missing 'query' cookie
//Or store the results directly if they aren't too large
//Do something with your saved query on page load, probably get data
//Example:
dataService.getData(query)
.success( function (data) {})
.error( function (err) {})
}
$scope.doSearch = function (query) {
vm.token = localStorageService.set("query", query);
//Then actually do your search
}
})
}()

Returning promise from reflux store

I'm working on my first react/reflux app so I may be approaching this problem in completely the wrong way. I'm trying to return a promise from a reflux store's action handler. This is the minimum code that represents how I'm trying to do this. If I display this in the browser, I get an error saying that the promise is never caught, because the result of the onLogin function is not passed back when the action is initiated. What is the best way to do this?
var Reflux = require('reflux');
var React = require('react/addons')
const Action = Reflux.createAction();
const Store = Reflux.createStore({
init: function() {
this.listenTo(Action, this.onAction);
},
onAction: function(username, password) {
var p = new Promise((resolve, reject) => {
reject('Bad password');
});
return p;
}
});
var LoginForm = React.createClass({
mixins: [Reflux.connect(Store, 'store')],
login: function() {
Action('nate', 'password1').catch(function(e) {
console.log(e); // This line is never executed
});
},
render: function() {
return (
<a onClick={this.login} href="#">login</a>
)
}
});
React.render(<LoginForm />, document.body);
Several things seem a bit confused here.
Reflux.connect(Store, 'store') is a shorthand for listening to the provided store, and automatically set the "store" property of your component state to whatever is passed in your store's this.trigger() call. However, your store never calls this.trigger so "store" in your component's state will never be updated. Returning a value from your store's action handlers doesn't trigger an update.
Stores should listen to actions to update their internal state, and typically then broadcast this state update by calling this.trigger. No component is going to get your returned promise from the store's onAction unless it explicitly calls Store.onAction (and then it doesn't matter if the actual action was invoked or not).
Async work should typically happen in the action's preEmit hook, not in the store. You should then also declare the action as async in createAction by setting the asyncResult option to true to automatically create "completed" and "failed" child actions. Check out the Reflux documentation here to learn about async events. Async actions automatically return promises, whose resolve and reject are called when the "completed" and "failed" sub-actions are called respectively. This is a bit opinionated, but that is definitely what I perceive is the intended Reflux way.