Solidity Contract A Calling Contract B Payable Function - reactjs

In the simplified (incomplete) code below, the runIt() function in contract B makes a transfer to contract A. This works fine, I can check the balance of contract A afterwards and it receives the funds fine at this stage alone.
Immediately after this transfer, the runIt() function in contract B calls the randomEvent() function in contract A. This function in contract A is meant to make another transfer, sending the entire contract A balance to contract C (not shown). For some reason I can never get this part to work. I tried to simplify the code by changing the target of randomEvent() to a wallet address instead of a contract address but I experience the same problem there. MetaMask flags the transaction as having issues and it won't process if you try.
In this code example it might seem like I'm sending funds in circles but that's just because I've simplified it for ease of explanation. Can anyone please help me figure out how I can execute a second transfer from contract A to contract C, following the receiving of funds from contract B? I initially tried to call the randomEvent() function from contract A directly, inside the payable function so that it fires every time it receives a transfer from anywhere. This would work perfectly fine, too, except I ran into the same issue and error. It didn't work that way, either.
I'm new to Solidity so I'm probably missing something stupid but any advice would be much appreciated. Yes, I know I'm using a very old version of Solidity, I'll eventually refactor it to something more modern.
pragma solidity ^0.4.17;
contract A {
function () public payable {
}
function randomEvent() public payable {
address wallet = 0x0000; // Random Contract C Address
wallet.transfer(address(this).balance);
}
}
}
contract B {
address public platform;
platform = 0x000; // Contract A Address
uint someValue = 1000000; // Irrelevant Example Amount
function runIt() public payable {
platform.transfer(someValue); // This works fine, funds in Contract A are received.
A ContractA = A(platform);
ContractA.randomEvent(); // This transaction fails and won't process, funds aren't moved from Contract A to Contract C.
}
}

Related

Firestore Security rules with payment form

I'm creating a raffle website. The user connects his wallet and pays for a raffle ticket. After the blockchain transaction confirmation, I add his raffle ticket in a collection in firestore.
It causes a security issue because if I allow the user to write to the raffle ticket collection in my firebase security rules, he could create his own tickets without paying.
I need tickets to be added to the database only if payment has been successfully made.
I don't know how websites that have means of payment do it. Maybe firebase isn't a good solution ?
My project is in react/typescript.
You say you do the payment over the blockchain and I assume you use solidity as your smart contract language?
Why don't you emit an event in your smart contract?
You then listen for these events on a (seperate) server.
That updates your (firebase) database whenever an event was emitted.
(Untested) Sample Code:
How do you emit events in solidity? (raffle.sol)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Raffle {
event PaymentCompletion(address buyer, uint256 amountOfTickets);
function buyTickets() external payable {
emit PaymentCompletion(msg.sender, msg.value)
}
}
How do you listen to these events?
when using web3js:
const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);
const lastBlock = await web3.eth.getBlockNumber()
// paymentEvents is an array containing the payments of the last 500 blocks.
const paymentEvents = await contract.getPastEvents(
'PaymentCompletion', // change if your looking for a different event
{ fromBlock: latestBlock - 500, toBlock: 'latest' }
);
now iterate through these events and put them into your database. You can also set up a subscription which notifies you whenever a new block was created, so you can check if new events were inside of the current block.
This is what it would look like if you add the first blockchain event to the firebase realtime database.
var db = admin.database();
var ref = db.ref("/payments");
// ...
ref.child("path/to/transaction").set({
buyer: paymentEvents[0].buyer,
amountOfTickets: paymentEvents[0].amountOfTickets,
// put the rest of your data here
}, (err) => {
if (err) {
console.error(err)
}
})
Alternatively (if you don't want to handle the payment on the blockchain) you could also take a look at stripe, it also has a firebase plugin for easy integration. (but I've never tried it out). However, imo using the blockchain for handling the payment would be the cleanest solution. (+ you don't have the handling fees stripe uses).
I hope I could give you some good clues! Firebase should be definitely suitable for this.

Ethers js transferring ERC20 between contracts

I have 2 contracts, first one is openzeppelin ERC20 token and second one is a lottery contract where players can bet on a number.
lottery.sol
pragma solidity ^0.8.4;
import "./Token.sol"; //import ERC20 token
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract Lottery is Ownable {
Token token;
constructor(Token _token) public {
token = _token;
}
// store information about player's bet
struct PlayersStruct {
uint betAmount;
uint betOnNumber;
}
mapping(address => PlayersStruct) public balances;
function enterLottery(string memory _betOnNumber) public payable {
address player = msg.sender;
uint amount = msg.value;
// transfer token from player's wallet to lottery contract
token.transferFrom(player, address(this), betAmount);
balances[player].betAmount += amount ;
balances[player].betOnNumber = _betOnNumber;
}
And this is how I call it from ReactJS
async function stakeBet() {
const amount = ethers.utils.parseEther("10");
const maxAmount = ethers.utils.parseEther("1000000");
// approve token once so player can save on gas in future
await token.approve(stakingContract.address, maxAmount);
// bet 10 tokens on number 20
await lottery.enterLottery(20, {value: amount,});
}
There are 2 problems with this code:
I have to approve the contract every time, even I'm approving maxAmount higher than betting amount. How do I let Metamask know that contract was already approved?
After approving the ERC20 token, the token for transfer is actually ETH and not ERC20 token defined in Token.sol, how do I specify that ERC20 is the one to transfer?
I'm testing on Kovan test net.
In your stakeBet function, you are calling those functions in order:
await token.approve(stakingContract.address, maxAmount);
// bet 10 tokens on number 20
await lottery.enterLottery(20, {value: amount,});
When you call approve, you are actually updating the allowance mapping. Let the contract know that, you are allowing certain amount for the allowed address. It should be implemented like this:
function approve(address _spender, uint _value)public returns (bool success){
// allowance tells how many tokens can be sent
allowance[msg.sender][_spender]=_value;
// This event must trigger when a successful call is made to the approve function.
emit Approval(msg.sender,_spender,_value);
return true;
}
Token transfer or coin transfer is actually updating the state inside the contracts. With the approve function you updated the allowance. Now
token.transferFrom should be implemented like this:
// my address is allowing your address for this much token
mapping(address=>mapping(address=>uint)) public allowance;
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success){
// check the allowance
require(_value <=allowance[_from][msg.sender]);
// update the balances
balanceOf[_to]+=_value;
balanceOf[_from]-=_value;
allowance[_from][msg.sender]-=_value;
// emitting an event
emit Transfer(_from,_to,_value);
return true;
}
ETH is not ERC20 token. Instead you have to implement WETH token and transfer WETH token. WETH is wrapped version of eth. https://weth.io/
All Ethereum wallet addresses are ERC20 compatible. Moreover, this means that every ERC20 transfer can happen between two Ethereum wallet addresses, or ERC20-compatible addresses. This typically includes all EVM-compatible blockchains. You send weth token and user can swap in metamask:
For the 1st part, you have done what you intended to do, i.e, you have set the allowance to the maximum amount so that the user doesn't have to pay for calling transaction for allow() each time.
The MetaMask is asking your permission for gas fees (in ETH) for sending "betAmount" amount of tokens to the contract.
I am new to ethereum, but I have faced similar circumstances in my projects. So, this is according to my understanding.
Also, for the 2nd problem, as I said earlier, MetaMask is asking for your permission for paying the gas fees (which it takes in ETH, but actual transfer of tokens must also be taking place. Only, some amount of ETH is spent for the "Gas Fees".
I got a real good article discussing the same thing.
Link: https://medium.com/ethex-market/erc20-approve-allow-explained-88d6de921ce9
You can see the demonstration of what I just said. You can see that the gas fees are taken in ETH. Also, if you had not set the allowance earlier to maximum, you would have to pay gas fees two times for a transaction to your contract to happen, first to call approve() for allowance, then to get the "betAmount" amount of tokens transferred to the contract.

How to call Solidity Function to return Ether from Smart Contract?

I have deployed a smart contract on a local truffle project and I am trying to interact with it in a React project using web3. The following solidity function should send Ether what was previously deposited in the contract to a user address on a boolean condition:
function Payout() public{
require( voteEndTime< block.timestamp, "Voting Time is not up. Please come back later" );
Voter storage sender = voters[msg.sender];
if (negativeVotes > positiveVotes){
require(!sender.option, "Wrong Vote. Stake is distributed among winners");
payable(address(msg.sender)).transfer((stakes*sender.amount) / negativeStakes);
}
else if (positiveVotes > negativeVotes){
require(sender.option, "Wrong Vote. Stake is distributed among winners");
payable(address(msg.sender)).transfer((stakes*sender.amount) / positiveStakes);
}
else{
payable(address(msg.sender)).transfer((stakes*sender.amount) / stakes);
}
}
The contract is definitely able to read the user's address using msg.sender because it has worked in the other functions I have. Every other function in the contract is also working fine. I can interact with it and I am able to send Ether to it. The problem occurs when I am trying to return the Ether stored in the contract to an account. I am trying to call my Payout() function using the following web3 call in React on button click:
var response = await BallotContract.methods.Payout().send({ from: account, gas: 310000 })
I have specified a higher gas limit, because the contract runs out of gas if I try to use the gas estimation seen below. The function this call is present in looks like this:
const giveMeMoney = async (e) => {
const web3 = await new Web3(window.ethereum);
await window.ethereum.enable();
var Accounts = await web3.eth.getAccounts()
account = Accounts[0]
console.log(account)
const gas = await BallotContract.methods.Payout().estimateGas();
console.log(gas)
var response = await BallotContract.methods.Payout().send({ from: account, gas: 310000 })
}
I am able to access the function from the frontend and it is returning the correct string if a "require" condition is not met. My problem is that the contract does not return any Ether if the conditions are met and this line:
payable(address(msg.sender)).transfer((stakes*sender.amount) / positiveStakes);
...is accessed. I am getting the following error:
Uncaught (in promise) Error: Returned error: VM Exception while processing transaction: revert
at Object.ErrorResponse (errors.js:30)
at onJsonrpcResult (index.js:162)
at XMLHttpRequest.request.onreadystatechange (index.js:123)
ErrorResponse # errors.js:30
Now I am unsure what could be the problem, because the contract is running perfectly fine if I test it in Remix. Does anybody see the problem or have a workaround for this kind of problem?
The "out of gas" error is caused by the transfer function. This function has a gas limit of 2100; I recommend you use call, you can check how here.
Also the boolean value of the Voter struct defaults to false. So if the negative votes win, but the user didn't vote, they still can try to claim the reward, even if the amount is 0. I recommend you check how to use an enum, it can be very useful.

BEP-20 smart contract with airdrop feature

I'm creating my own BEP20 token and want to implement a function to airdrop tokens to multiple addresses at once to reduce gas fees. Use case would be a giveaway of free tokens to selected users after the launch.
This is the code that I have so far, however there seems to be something missing for it to work properly:
contract Airdrop is Ownable {
IERC20 token;
struct PaymentInfo {
address payable payee;
uint256 amount;
}
constructor(address _token) public {
token = IERC20(_token);
}
function batchPayout(PaymentInfo[] calldata info) external onlyOwner {
for (uint i=0; i < info.length; i++) {
token.transfer(info[i].payee,info[i].amount);
}
}
function transfer(address to, uint256 amount) external onlyOwner {
token.transfer(to, amount);
}
}
Can I use code snippets from ERC20 examples? Will they work with BEP20?
Ethereum and Binance Smart Chain use slightly different token standards, so most of the Solidity code designed for Ethereum virtual machine need minor changes, including replacing mentions of IERC20 with IBEP20 and using the correct Solidity file for IBEP20 interface.
If you use correct version of Solidity compiler, it should tell if the code needs further changes. For real life testing, it's better to test the code on testnet of Binance Smart Chain.
You do not need to include batch send in the token itself. Because smart contracts are composable, there exist third-party smart contracts that can do batch send on behalf of any token.
One example service with open-source smart contracts is Token BulkSender. The source for the bulk send smart contract is here.

Web3 JS Library (& Metamask), Get Token Balance

I want to query the eth balance and all token balances of a public key. So far I managed to load web3 and call the getBalance method.
Now, I want to see the balance for ERC20 tokens which are stored with the same public key? E.g. I want to see the balance of OMG tokens?
So far I found that each token contract address has to be queried:
https://ethereum.stackexchange.com/questions/15372/how-can-i-view-all-the-tokens-and-contracts-associated-with-an-ethereum-address
omgContractAddress = "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07"
OmgContract = web3.eth.contract(abi)
omgContract = OmgContract.at(omgContractAddress)
someUsersAddress = "0x75087d9faa28d653750f3e4c86e7dcf6aff0a916"
omgContract.balanceOf someUsersAddress, (err, balance)-> #some owner
console.error err
console.info balance.toNumber()
Questions:
1) Do I need the abi of each token? Or can I use a standardized abi for ERC20 tokens as long as I just want to use standardized methods?
2) Where do I find the abi? Or do I need to recompile each contract?
E.g. OMG: https://etherscan.io/token/OmiseGo
I could find the abis of several tokens on https://etherscan.io/address/<token_address>#code so far they have the same method common in their abi. I just copied the method directly into my source code without reading the original abi. E.g.
abi = [{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}]

Resources