ERC 20 Token transferFrom isn't working in my smart contract. Doesn't throw any Exception either - reactjs

I wrote a contract in Solidity with the following task in mind:
The user approves the smart contract to transfer a certain amount of tokens from their wallet.
The smart contract uses this amount to transfer to an address given as a parameter. But it also takes 1 token from this amount to transfer to the developer of the contract.
If everything succeeds, the user receives a "Payment successful!" message.
The first step (the approval) was made using React and Web3:
const derc20contract = '0x36850b80ad73fe66216609B9796ed6ceae8BE29F';
const handleClick = async (e) => {
e.preventDefault();
const prtmp = await detectEthereumProvider();
// -------------------- Approve Part ------------------------------//
const web3 = new Web3(prtmp);
const erc20contract = new web3.eth.Contract(
erc20abi,
derc20token,
{ from: '0xFromAddress' }
);
await erc20contract.methods.approve(derc20contract, web3.utils.toHex(3e18)).send();
// ---------------------------------------------------------------//
const contract = new web3.eth.Contract(derc20abi, derc20contract);
const response = await contract.methods.send_usdt(
'0xToAddress',
web3.utils.toHex(3e18)
)
.call({ from: '0xFromAddress'});
console.log(response);
};
Once the approval succeeds the second part of the function resolves. The contract I deployed has a function called send_usdt. Through this function is my smart contract able to transfer the amount approved.
/ SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
interface IERC20 {
function transfer(address _to, uint256 _value) external returns(bool);
function transferFrom(address _from, address _to, uint _amount) external returns (bool);
function allowance(address _owner, address _contract) external returns (uint256);
function balanceOf(address _buyer) external view returns (uint256);
function approve(address _contract, uint tokens) external returns (bool);
}
contract DPortalSubscription {
address private owner;
mapping(address => uint32) subscription_fees;
constructor()
{
owner = msg.sender;
}
function check_balance() external view returns (uint256)
{
// TestToken ( Plasma Bridge ) in Mumbai Network
IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
uint256 balance = usdt.balanceOf(msg.sender);
return balance;
}
function check_sender_address() external view returns (address)
{
return msg.sender;
}
function check_allowance()external returns(uint256)
{
// TestToken ( Plasma Bridge ) in Mumbai Network
IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
uint256 allowance = usdt.allowance(msg.sender, address(this));
return allowance;
}
function send_usdt(address _to, uint256 _amount) external returns (string memory)
{
// TestToken ( Plasma Bridge ) in Mumbai Network
IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
require(_amount > 1, "Purchases must be higher than 1 usdt");
require(usdt.balanceOf(msg.sender) >= _amount, "The buyer doesn't have enough funds");
require(usdt.allowance(msg.sender, address(this)) >= _amount, "Permissions to transfer tokens denied");
require(usdt.transferFrom(msg.sender, _to, _amount-1) == true, "Couldn't transfer tokens to seller");
require(usdt.transferFrom(msg.sender, owner, 1) == true, "Couldn't transfer tokens to support");
return "Payment successful!";
}
}
Once the second part of the function in React resolves, I receive the confirmation "Payment successful!" But the tokens weren't transferred. I keep the same amount in my "From Wallet", "To Wallet" and "Deploy Wallet" as before.
The problem was inside the react snippet. By changing the method call to sell the money could be successfully transferred.
const response = await contract.methods.send_usdt
(
'0xToAddress',
web3.utils.toHex(3e18)
)
.call({ from: '0xFromAddress' });
was changed to:
const response = await contract.methods.send_usdt
(
'0xToAddress',
web3.utils.toHex(3e18),
{ from: '0xFromAddress' }
)
.send();
which worked but raised another question. When I use the previous call method, the function returns "Payment Successful!" and with the sell method I receive an Object.
Since "Payment Successful" isn't true, I was wondering if it could be possible to throw an Exception when someone interacts with my contract using the call method.
Is that possible?

Following #Kuly14's answer and in order to make it clearer, you may want to create the following function instead:
function send_usdt(address _to, uint256 _amount) external returns (string memory) {
// TestToken ( Plasma Bridge ) in Mumbai Network
IERC20 usdt = IERC20(address(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1));
require(_amount > 1, "Purchases must be higher than 1 usdt");
usdt.transferFrom(msg.sender, owner, 1);
usdt.transferFrom(msg.sender, _to, _amount-1);
return "Payment successful!";
}
Notes:
By just calling the transferFrom method, it will jump its corresponding error if something goes wrong. You do not need to handle them.
I have changed the order in which you transfer the tokens. First, I send them to the owner and then to the desired recipient to avoid fees payments problems, but if you prefer the other way round, it is possible.

Related

How to correctly mint a NFT with any ERC20 token using react app & smart contract? Code w/ explanation provided below

Problem/Clarification:
I have a NFT minting DApp that mints with multiple ERC20 tokens.
I'm having an issue with the react code which calls the smart contract minting function multiple times.
When a user mints with their preferred ERC20 token, the react code is structured in a way that a user would see 3 meta-mask popups.
The first metamask popup asks the user to approve the NFT smart contract to access their ERC20 token.
The second metamask popup asks the user to approve the transfer of the ERC20 tokens into the smart contract.
The third and final popup allows the user to go ahead and mint by calling the mintWithERCToken(mintAmount, tokenID) function in the smart contract. This part is problematic because once the ERC20 has been transferred, and then the user decides to cancel/reject the mint, the token would have already been transferred into the smart contract.
All three metamask calls requires the spending of gas.
What is the correct sequence of events? What is the correct way to write the react code?
Could someone help restructure the react code?
React Code
async function mintWithCrypto(tokenId) {
Web3EthContract.setProvider(ethereum);
let web3 = new Web3(ethereum);
//get erc20 contract address
var erc20address = await blockchain.smartContract.methods.getCryptotoken(tokenId).call();
//get token contract information
var currency = new web3.eth.Contract(TOKENABI, erc20address);
//get NFT cost
var mintRate = await blockchain.smartContract.methods.getNFTCost(tokenId).call();
//get mint amount and convert to int
var _mintAmount = Number(mintAmount);
//get total cost of NFTs minted
var totalAmount = mintRate * _mintAmount;
let gasLimit = 285000;
//get total gas
let totalGasLimit = String(gasLimit * _mintAmount);
setFeedback(`Minting your NFT, please hold on...`);
//approve contract address for ERC20 token
currency.methods.approve(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account, gasLimit: String(totalGasLimit)})
//transfer ERC20 token to smart contract. **Problematic code**
.then(await currency.methods.transfer(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account},
async function (error, transactionHash) {
// console.log("Transfer Submitted, Hash: ", transactionHash)
let transactionReceipt = null
while (transactionReceipt == null) {
transactionReceipt = await web3.eth.getTransactionReceipt(transactionHash);
await sleep(10000)
}
}))
//mint NFT **Problematic code**
.then(blockchain.smartContract.methods.mintWithERCToken(_mintAmount, tokenId).send({from: blockchain.account, gasLimit: String(totalGasLimit)})
.once("error", (err) => {
if (err.message == "MetaMask Tx Signature: User denied transaction signature.") {
setFeedback("Transaction cancelled.");
} else {
setFeedback("Sorry, something went wrong please try again later.");
}
})
.then((receipt) => {
console.log(receipt);
setFeedback(`Congratulations! You've minted a ${CONFIG.NFT_NAME}.`);
dispatch(fetchData(blockchain.account));
})
)
}
Smart Contract code
function mintWithERCToken(uint256 mintAmount, uint256 tokenID) public payable {
CryptoTokenInfo storage tokens = permittedCrypto[tokenID];
IERC20 paytoken;
paytoken = tokens.paytoken;
uint256 costval;
costval = tokens.costvalue;
uint256 supply = totalSupply();
require(mintAmount > 0, "You need to mint at least 1 NFT");
require(mintAmount <= maxMintAmount, "Max mint amount per session exceeded");
require(supply + mintAmount <= maxSupply, "Max NFT exceeded");
if (msg.sender != owner()) {
//check if the user is whitelisted
if(onlyWhitelisted == true) {
require(isWhitelisted(msg.sender), "Sorry, address is not whitelisted");
}
require(msg.value == costval * mintAmount, "Insufficient funds. Please add more funds to address");
}
for (uint256 i = 1; i <= mintAmount; i++) {
require(paytoken.transferFrom(msg.sender, address(this), costval));
_safeMint(msg.sender, supply + i);
}
}

How specific Cost Value to ethersJs in React

I need to implement in the code below "value" the cost in ETH because in Solidity I require msg.value and in order to be able to lie I have to put a parameter "value" in the code in js, but I don't know how, you can it helps? I also attached the Smart Contract part with mint.
React JS file
async function handleMint() {
if (mintFinish === true) {
setMintFinish(false);
} else {
setMintFinish(true);
}
if (window.ethereum) {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(
mintExampleAddress,
mintExampleABI.abi,
signer
);
console.log(signer)
try {
// console.log(contract)
const response = await contract.mint(mint);
console.log(response);
} catch (err) {
console.log(err);
}
}
}
Solidity file
function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) {
require(!paused, "The contract is paused!");
require(msg.value >= cost * _mintAmount, "Insufficient funds!");
_mintLoop(msg.sender, _mintAmount);
}
how you are using etherjs and seeing the smart contract code inside the mint function, i suppose that cost is a public variable and can be read from outside the contract, you get the cost and multiply the value by the mint amount parameter, something like this
const cost = await contract.cost();
const response = await contract.mint(mint,{value:cost.mul(mint)});
you have to remember to use bignumbers and that all return values that are numbers will be returned as one, what i'm doing here is getting the cost from the contract and multiply that for mint and pass that to value as the last parameter in the function call

Calling withdraw method with web3 ethereum

I'm calling a withdrawal method from a contract written in solidity from a react.js app using metamask. The transaction is failing and I don't know why. I think is something related to the gas fee because when I change it I get different errors. This is the react code:
contract.methods
.withdrawDividends()
.send({
from: userAddress,
value: availableToWithdraw,
})
.on("transactionHash", function (hash: string) {
console.log(hash);
})
.on(
"confirmation",
function (confirmationNumber: number, receipt: unknown) {
console.log(confirmationNumber);
console.log(receipt);
}
)
.on("receipt", function (receipt: unknown) {
// receipt example
console.log(receipt);
})
.on("error", function (error: unknown, receipt: unknown) {
// If the transaction was rejected by the network with a receipt, the second parameter will be the receipt.
console.log(error);
console.log(receipt);
});
This is my solidity method:
function withdrawDividends() public {
User storage user = users[_msgSender()];
uint256 totalAmount = getUserDividends(_msgSender());
require(totalAmount > 0, "User has no dividends");
user.checkpoint = block.timestamp;
(bool success, ) = _msgSender().call.value(totalAmount)("");
require(success, "Transfer failed.");
emit Withdrawn(_msgSender(), totalAmount);
}
The method is not crashing but I see the following error in metamask:
What I'm doing wrong? Is something related to the gas?
If you want your function to accept ETH value, it needs to have the payable modifier.
In your case:
function withdrawDividends() public payable {
You can read more about the payable modifier in the docs. It links to the receive function, which is a slightly different topic. But it also covers the payable modifier.

Solidity smart contract not returning correct value

I have the following function in an ERC20 solidity smart contract:
function getSummary() public view returns (string, string, address, uint, uint){
return(
symbol,
name,
creator,
balances[msg.sender],
_totalSupply
);
}
When I call this function in remix I get a non zero balance for the account I'm on in metamask. That value is correct.
But when I call this function in a react next.js app the the balance returns a zero. Here is that code:
const accounts = await web3.eth.getAccounts();
const account = accounts[0];
let tokenContract = new web3.eth.Contract(abi, props.query.address);
let tokenSummary = await tokenContract.methods.getSummary().call();
let balance = await tokenContract.methods.balanceOf(account).call();
console.log(tokenSummary);
When I console.log tokenSummary, the balance is 0. But then if I call balanceOf I get a non-zero value (which is correct). What could be causing this problem ?

Before Interceptor: store variable in Request

I need to check for the existence of a YMD uri date param on every request, and, if exist, store it in the Request for later access in various parts of the application where implicit request has been made available.
Request interception seems an obvious choice:
https://github.com/playframework/Play20/wiki/ScalaInterceptors
However, I am not seeing a way to add to the Request (actually RequestHeader is what's available), assume it's read only/immutable.
I am able to add data to the Request via Action Composition; however, this approach is limited to the composed action, and not every action (why I'd like the above global Before Interceptor approach to somehow work). For example, an Authentiucated action wrapper allows me to store the logged in user's id in the Request.
trait Secured {
private case class Success[A](uid: Int, r: Request[A]) extends WrappedRequest(r)
def Authenticated[A](p: BodyParser[A])
(f: Success[A] => Result)(implicit group: RoleGroup) = {
def apply(maybeUser: Option[String])(implicit r: Request[A]) = {
maybeUser map {id =>
Cache.orElse(group.to_s+"-"+id, 3600){
repo.user.get(id.int, group) map(_.active) getOrElse false
} fold ( onFail, f(Success(id.int, r)) )
}
}
I would like to do the same for a possible uri date param, but have it apply to all Actions, or, again, have it applied before the Play routes mechanism is triggered via global interceptor.
Ideas much appreciated, thanks
To simulate injecting arbitrary data into the Request, you can extend WrappedRequest:
case class Outcome[A](
r: Request[A],
uid: Int,
startDate: String,
endDate: String,
uriDate: JodaTime) extends WrappedRequest(r)
and then create Action provider traits that return an instance of your custom request wrapper. For example, a base Task action could look like:
trait TaskRequest
extends DateParser {
def Task[A](p: BodyParser[A])(f: Outcome[A] => Result) = {
Action(p) { implicit r =>
f( toOutcome[A](r, r.session.get("userID").map(_.toInt) getOrElse 0) )
}
}
def Task(f: Outcome[AnyContent] => Result): Action[AnyContent] = {
import play.api.mvc.BodyParsers._
Task(parse.anyContent)(f)
}
protected def toOutcome[A](r: Request[A], uid: Int) = {
val(start,uriDate) = (startDate(r), toDate(r.uri))
val end = seasonEnd(start)
Outcome(r, uid, start.toString("yyyyMMdd"), end.toString("yyyyMMdd"), uriDate)
}
val ymdMatcher = "\\d{8}".r
protected def startDate(uri: String) = {
ymdMatcher.findFirstIn(uri). // set season start based on ymd URI param if exist
map(_.startDate) getOrElse seasonStart(new JodaTime)
}
protected def toDate(uri: String) =
ymdMatcher.findFirstIn(r.uri).map(_.to_date) getOrElse new JodaTime
}
and then derive an Authenticated action from Task:
trait Secured
extends TaskRequest {
def Authenticated[A](p: BodyParser[A])
(f: Outcome[A] => Result)(implicit group: RoleGroup) = {
def apply(maybeUser: Option[String])(implicit r: Request[A]) = {
maybeUser map {id =>
Cache.orElse(group.to_s+"-"+id, 3600){
repo.user.get(id.int, group) map(_.active) getOrElse false
} fold ( onFail, f( toOutcome(r, id.int)) )
}
}
and then use in your controllers:
def index = Authenticated { implicit request=>
// voila, current season start date since no uri date param exist
request.startDate
}
I could call the date manipulations functions on a per action basis, sure, but that would only apply to Action scope. How would I access the startDate in the template layer? I couldn't, but with a custom request wrapper you can inject anything you want and easily reference the data anywhere. So instead of bringing play's request into scope:
#(model: Foo, title: String)(implicit request: play.api.mvc.Request[_])
you reference your custom request wrapper:
#(model: Foo, title: String)(implicit request: controllers.Outcome[_])
Does the job quite nicely, global interception I have yet to find a use for (maybe re-route some legacy URIs, but nothing as powerful as the WrappedRequest approach)

Resources