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

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.

Related

Thirdweb error: This action requires a connected wallet to sign the transaction. Please pass a valid signer to the SDK

So I have this code:
import { ThirdwebSDK } from "#thirdweb-dev/sdk";
import { ConnectWallet, useAddress } from "#thirdweb-dev/react";
export default function DonationPage() {
let address = useAddress()
async function sendCoins() {
try {
if (selectedCoin == 'eth') {
} else {
const sdk = new ThirdwebSDK("mainnet");
const contract = await sdk.getContract('ADDRESS_TO_USDC_CONTRACT');
await contract.erc20.transferFrom(address || '0x',CORPORATE_WALLET || '0x', donationAmount);
}
} catch (error: any) {
alert(error.message)
}
}
return (<>...</>)
}
So i'm using ConnectWallet button to get address and using TrustWallet for this, and trying to transfer tokens from 'address' to a corporate wallet, with a specified donation amount. However, I receive this error "This action requires a connected wallet to sign the transaction. Please pass a valid signer to the SDK.".
There seems to be no documentation on this online, and ChatGPT won't help either. Does someone know how to fix this?
Also, how to send Eth to CORPORATE_WALLET using Thirdweb?
There seems to be no documentation on this online
Well there is complete documentation on the SDK on ThirdWeb website.
Since you're performing transaction on the contract you cannot initialize the SDK without a private key or signer. Using neither returns read-only contracts
Because you're performing the operations in the frontend, fromSigner method is recommended. Access the user's wallet and call for the signer, use this to initialize the SDK instance with the .fromSigner() method.
fromSigner documentation
ThirdWeb official fromSigner example with React
Also linking documentation for private key method, just in case you wish to approach that way.

Gnosis Safe MultiSig wallet automatically denying the transfer of Ether to another account

I've been integrating my dApp that has Gnosis Safe Wallet Support, but I've having an issue when I try and request Ether to be transferred from the Gnosis Wallet to another contract.
Here's the code that makes the request:
const signer = web3reactContext.library.getSigner(web3reactContext.account).connectUnchecked();
const tx = await signer.sendTransaction({
to: "...",
value: ethers.utils.parseEther(".005")
});
I'm getting this error in the console when the user hits the button that calls this function:
Object { code: -32000, message: "Request rejected" }
Anyone experienced this error before>

How can I allow ERC20 token payment inside javascript function

I'm working on a smart contract that allows users to pay for monthly subscriptions like Netflix, Amazon, etc... with ERC20 tokens.
Everything works well when I tested it. but when I implemented the pay function inside the front-end, so users can interact with the smart contract. the problem that I've faced is that when I clicked on a button to fire off the pay function, I had Metamask asking to confirm the approval, then once the approval is confirmed, I had to confirm the transfer of tokens. **I had to confirm the transfer of tokens. then I got another transaction to confirm the equivalent amount of tokens in ETH.
I made a lot of research but I got stuck, on can I allow the pay function to transfer tokens between two addresses only once.
am I missing something?
PS: blockchain.account & blockchain.smartContract are imported from redux object state.
solidity function
function pay(uint planId) external {
Subscription storage subscription = subscriptions[subscriber][planId];
Plan storage plan = plans[planId];
IERC20 token = IERC20(plan.token);
require(
block.timestamp > subscription.nextPayment,
'not due yet'
);
token.transferFrom(subscriber, plan.merchant, plan.amount);
emit PaymentSent(
msg.sender,
plan.merchant,
plan.amount,
planId,
block.timestamp
);
subscription.nextPayment = subscription.nextPayment + plan.frequency;
}
React function
async function pay() {
showAlert(true, "Happy to see you, Your payment is processing...!");
const data = await blockchain.smartContract.methods.subscriptions(Id).call();
let monthlyPayment = String(data.monthlyPayment);
let tokenAddress = data.amount;
// instance of ERC20 contract
let currency = new web3.eth.Contract(tokenIbi, tokenAddress);
currency.methods.approve("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
amount).send({from: blockchain.account})
.then(
await currency.methods.transfer("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
amount).send({from:blockchain.account})
)
blockchain.smartContract.methods.pay(Id).send({from: blockchain.account})
.once("error", (err)=> {
console.log(err);
showAlert(true, "Something went wrong...!");
})
.then((receipt)=> {
console.log(receipt);
showAlert(true, "Congratulations, You monthly payment has been submitted successfully");
dispatch(fetchData(blockchain.account));
})
}
Pay button
<button className="btn"
id="launchApp-btn"
onClick={(e)=> {
e.preventDefault();
pay();
}}>
You are making two transaction on each monthly purchase
Approve the allowance of erc20 tokens
Transfer of erc20 token
Each one has its own gas cost. You may multiply the allowance of erc20 on user's first purchase (Which is not fair and brings the security issue) or continue doing the same scenario Approve&Transfer

Stripe Create Payment Intents Promise Never Resolves

I signed up for a Stripe account and followed some simple steps to get up and running with Node. I just installed the package and tested a payment Intents with my test key:
const Stripe = require('stripe');
const handleStripe = async () => {
const stripe = Stripe(testKeyString);
console.log(“we make it here”);
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment_method_types: ['card'],
receipt_email: 'jenny.rosen#example.com',
});
//we never make it here
console.log(paymentIntent);
}
catch(err){
//we never make it here either
console.log(err);
}
}
The console logs “we make it here”, but nothing else. The promise is never resolved.
I suspect that this might be a bug with the stripe npm package. Anybody have any thoughts on why the promise is never returned?
EDIT: sorry, I wasted everyone’s time here. I was following the docs QuickStart where it said “install a client library” and I assumed it was for the front end. So a very silly mistake on my part thinking that it was a good idea to make a payment intent from the front end with a secret key. Just getting going with the Stripe API and I’m off to a bad start. Thanks for your comments and answer
Thanks
What happens if you run it without the try/catch? Also what do you get if you try https://status.stripe.com/reachability from that server - are you sure you can reach Stripe's servers?

Unreliable Google Firebase transactions

In my (greatly simplified) model I have users, accounts and account_types. Each user can have multiple accounts of each account_type. When an account of type TT is created I'm updating the "users" field of that object so it keeps the users which have accounts of that types, and the number of such accounts they have.
users: {
some fields
},
accounts: {
userID: UU,
type: TT
},
account_type:
users: { UU: 31 }
}
I use the onCreate and onDelete cloud triggers for accounts to update the account_type object. Since multiple accounts can be created simultaneously I have to use transactions:
exports.onCreateAccount = functions.firestore
.document('accounts/{accountID}')
.onCreate((account, context) => {
const acc_user = account.data().userID;
const acc_type = account.data().type;
return admin.firestore().runTransaction(transaction => {
// This code may get re-run multiple times if there are conflicts.
const accountTypeRef = admin.firestore().doc("account_types/"+acc_type);
return transaction.get(accountTypeRef).then(accTypeDoc => {
var users = accTypeDoc.data().users;
if (users === undefined) {
users = {};
}
if (users[acc_user] === undefined) {
users[acc_user] = 1;
} else {
users[acc_user]++;
}
transaction.update(accountTypeRef, {users: users});
return;
})
})
.catch(error => {
console.log("AccountType create transaction failed. Error: "+error);
});
});
In my tests I'm first populating the database with some data so I'm also adding a user and 30 accounts of the same type. With the local emulator this works just fine and at the end of the addition I see that the account_type object contains the user with the counter at 30. But when deployed to Firebase and running the same functions the counter gets to less than 30. My suspicion is that since Firebase is much slower and transactions take longer, more of them are conflicted and fail and eventually don't execute at all. The transaction failure documentation (https://firebase.google.com/docs/firestore/manage-data/transactions) says:
"The transaction read a document that was modified outside of the transaction. In this case, the transaction automatically runs again. The transaction is retried a finite number of times."
So my questions:
What does "finite" mean?
Any way to control this number?
How can I make sure my transactions are executed at some point and don't get dropped like that so my data is consistent?
Any other idea as to why I'm not getting the correct results when deployed to the cloud?
What does "finite" mean?
It's the opposite of "unlimited". It will retry no more than a set number of times.
Any way to control this number?
Other than modifying the source code of the SDK, no. The SDK itself advertise a specific number, as it might change.
How can I make sure my transactions are executed at some point and don't get dropped like that so my data is consistent?
Detect the error and retry in your app. If you aren't seeing the transaction fail with an error, then nothing went wrong.
Any other idea as to why I'm not getting the correct results when deployed to the cloud?
Since we can't see what exactly you're doing to trigger the function, and have no specific expected results to compare to, it's not really possible to say.

Resources