Transaction is getting reverted in the `refundToInsurer()` function call - ethereum

The deployment of InsuranceProvider is working fine and the calling of newContract() with the required parameters is successfully creating/deploying the InsuranceConsumer contract. Even, the payOutContract() is working correctly in terms of transferring the ETH balance from the InsuranceConsumer to the client's wallet.
The issue is with the refundToInsurer() function, as it's expected to transfer the ETH balance from the InsuranceConsumer to the insurer's wallet, but it's transaction is getting failed/reverted.
Here's the code:
SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract InsuranceProvider {
address payable public insurer;
AggregatorV3Interface internal priceFeed;
modifier onlyOwner() {
require(insurer == msg.sender, "Only Insurance provider can do this");
_;
}
constructor() payable {
priceFeed = AggregatorV3Interface(
0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
);
insurer = payable(msg.sender);
}
function newContract(
address payable _client,
uint256 _premium,
uint256 _payoutValue
) public payable onlyOwner returns (address) {
//create contract, send payout amount so contract is fully funded plus a small buffer
InsuranceConsumer i = (new InsuranceConsumer){
value: ((_payoutValue * 1 ether) / (uint256(getLatestPrice())))
}(_client, _premium, _payoutValue);
return address(i);
}
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 timeStamp, ) = priceFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
return price;
}
function payOutContract(address _contract) public {
InsuranceConsumer i = InsuranceConsumer(_contract);
// Transfer agreed amount to client
i.payOutContract();
}
function refundToInsurer(address _contract) public onlyOwner {
InsuranceConsumer i = InsuranceConsumer(_contract);
// Transfer back the amount to insurer
i.refundToInsurer();
}
}
contract InsuranceConsumer {
AggregatorV3Interface internal priceFeed;
address payable public insurer;
address payable client;
uint256 startDate;
uint256 premium;
uint256 payoutValue;
constructor(
address payable _client,
uint256 _premium,
uint256 _payoutValue
) payable {
//set ETH/USD Price Feed
priceFeed = AggregatorV3Interface(
0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
);
//first ensure insurer has fully funded the contract
require(
msg.value >= _payoutValue / uint256(getLatestPrice()),
"Not enough funds sent to contract"
);
//now initialize values for the contract
insurer = payable(msg.sender);
client = _client;
startDate = block.timestamp; //contract will be effective immediately on creation
premium = _premium;
payoutValue = _payoutValue;
}
function payOutContract() public {
//Transfer agreed amount to client
client.transfer(address(this).balance);
}
function refundToInsurer() public {
// Transfer back the amount to insurer
insurer.transfer(address(this).balance);
}
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 timeStamp, ) = priceFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
return price;
}
}
Can anyone please help by pointing out the logical mistake that I'm doing in the refundToInsurer() function ?

As, we're creating the InsuranceConsumer using the newContract() function of InsuranceProvider. Therefore, the msg.sender of the InsuranceConsumer is going to be the InsuranceProvider itself, not the insurer's wallet.
So, when we're calling the refundInsurer() of InsuranceConsumer via InsuranceProvider, then it's doing:
insurer.transfer(address(this).balance);
It means to transfer the available ETH in the InsuranceConsumer to the InsuranceProvider (not the insurer's wallet), and since InsuranceProvider is not expected to be a receiver, that's why it's reverting the transaction.
So, the corrected code will be:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract InsuranceProvider {
address payable public insurer;
AggregatorV3Interface internal priceFeed;
modifier onlyOwner() {
require(insurer == msg.sender, "Only Insurance provider can do this");
_;
}
constructor() payable {
priceFeed = AggregatorV3Interface(
0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
);
insurer = payable(msg.sender);
}
function newContract(
address payable _client,
uint256 _premium,
uint256 _payoutValue
) public payable onlyOwner returns (address) {
//create contract, send payout amount so contract is fully funded plus a small buffer
InsuranceConsumer i = (new InsuranceConsumer){
value: ((_payoutValue * 1 ether) / (uint256(getLatestPrice())))
}(_client, _premium, _payoutValue);
return address(i);
}
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 timeStamp, ) = priceFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
return price;
}
function payOutContract(address _contract) public onlyOwner {
// Transfer agreed amount to client
InsuranceConsumer i = InsuranceConsumer(_contract);
i.payOutContract();
}
function refundToInsurer(address _contract) public onlyOwner {
// Transfer back the amount to insurer
InsuranceConsumer i = InsuranceConsumer(_contract);
i.refundToInsurer(insurer);
}
}
contract InsuranceConsumer {
AggregatorV3Interface internal priceFeed;
address payable public insurer;
address payable client;
uint256 startDate;
uint256 premium;
uint256 payoutValue;
constructor(
address payable _client,
uint256 _premium,
uint256 _payoutValue
) payable {
//set ETH/USD Price Feed
priceFeed = AggregatorV3Interface(
0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
);
//first ensure insurer has fully funded the contract
require(
msg.value >= _payoutValue / uint256(getLatestPrice()),
"Not enough funds sent to contract"
);
//now initialize values for the contract
insurer = payable(msg.sender);
client = _client;
startDate = block.timestamp; //contract will be effective immediately on creation
premium = _premium;
payoutValue = _payoutValue;
}
function payOutContract() public {
//Transfer agreed amount to client
client.transfer(address(this).balance);
}
function refundToInsurer(address payable _insurer) public {
// Transfer back the amount to insurer
_insurer.transfer(address(this).balance);
}
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 timeStamp, ) = priceFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
return price;
}
}
Now, while calling the refundInsurer(), we're explicitly passing the insurer as the parameter, and it's taking the value of msg.sender from InsuranceProvider, so the msg.sender is going to be the insurer's wallet (with which the InsuranceProvider is being deployed) in this case.
And now, when we're calling the refundInsurer() of InsuranceConsumer via InsuranceProvider, then it's doing:
_insurer.transfer(address(this).balance);
It means to transfer the available ETH in the InsuranceConsumer to the insurer's wallet. So, the transaction will be successful resulting in the withdrawal of the funds from the InsuranceConsumer to the insurer's wallet.

Related

"Not Enough Fund" when doing buying token transaction in Solidity with Remix IDE

I am completely new to this blockchain world so I want to try learn it. While I am following a tutorial to create a simple ERC-1155 contract to mint a token and set its price. When I am trying to run the buyToken function from another address, it keeps saying "Not Enough Fund". When I debug the msg.value value is 0, even though I have 100 ethers balance on each of my address. When I change the amount of token to 0, it works successfully. What should I need to do to be able to do the transaction?
Log
This is my contract that I have modified on:
// Contract based on https://docs.openzeppelin.com/contracts/4.x/erc1155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC1155/ERC1155.sol";
/**
* #title NFTMinter
* #dev NFT Contract Minter
* #custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract NFTMinter is ERC1155 {
constructor() ERC1155("https://raw.githubusercontent.com/noopmood/TutorialNFTInGo/main/metadata/{id}.json") payable {}
// Define the mapping of addresses to balances
mapping(address => mapping(uint256 => uint256)) public _balances;
// Define the mapping of address to tokenIds owned
mapping(address => uint256[]) public _tokenIds;
// Define the mapping of tokenId to price
mapping(uint256 => uint256) public tokenPrice;
// Define the sender to address payable type
address payable public sender;
struct Token {
uint256 tokenId;
uint256 balance;
}
function mintCaller(uint256 tokenId, uint256 amount) public {
_mint(msg.sender, tokenId, amount, "");
}
// Mints new tokens and sets the price for each token.
function mintAddress(uint256 tokenId, uint256 amount, address addr, uint256 price) public{
_mint(addr, tokenId, amount, "");
// Update the balance of the recipient
_balances[addr][tokenId] += amount;
// Add the tokenId to the address
_tokenIds[addr].push(tokenId);
// Set the price of the token
tokenPrice[tokenId] = price;
}
// Get all tokenIds from its owner address
function getTokenIdsByAddress(address addr) public view returns (uint[] memory) {
return _tokenIds[addr];
}
// Get the balance / amount of the tokenId
function getTokenByIdAndAddress(address addr, uint256 tokenId) public view returns (Token memory) {
Token memory result;
result.tokenId = tokenId;
result.balance = _balances[addr][tokenId];
return result;
}
// Get the tokenIds along with its corresponding balances/amount
function getAllTokenByAddress(address holder) public view returns (Token[] memory) {
Token[] memory result = new Token[](_tokenIds[holder].length);
for (uint i = 0; i < _tokenIds[holder].length; i++) {
result[i].tokenId = _tokenIds[holder][i];
result[i].balance = _balances[holder][_tokenIds[holder][i]];
}
return result;
}
// Transfers the tokens from one address to another.
function transfer(address addr, uint256 tokenId, uint256 amount) public {
require(_balances[msg.sender][tokenId] >= amount, "Not enough balance");
// Transfer the token
_safeTransferFrom(msg.sender, addr, tokenId, amount, "");
// Update the sender's balance
_balances[msg.sender][tokenId] -= amount;
// Update the recipient's balance
_balances[addr][tokenId] += amount;
}
// Allows a buyer to purchase a token by sending the required amount to the contract and updating the balance of the buyer.
function buyToken(uint256 tokenId, uint256 amount) public payable {
require(msg.value >= amount * tokenPrice[tokenId], "Not enough funds");
// Deduct the amount from the buyer
sender = payable(msg.sender);
sender.transfer(amount * tokenPrice[tokenId]);
// Transfer the token to the buyer
_safeTransferFrom(address(0), msg.sender, tokenId, amount, "");
// Update the buyer's balance
_balances[msg.sender][tokenId] += amount;
}
}
On what the step to reproduce this:
Deploy the contract with value, for me I pass 20 Ether
Run the mintAddress function by passing, tokenId: 1, amount: 10, price: 10, address: the address an account
Run the transfer function by passing address: the address of account you want to transfer the token into, tokenId: 1, amount: 10
Change the account in upper part to the address you use in transfer function
Run buyToken function, tokenId: 1, amount: 10
It will said Not Enough Fund
buyToken
What I have tried so far:
I read in another post, that I should deploy the contract with the value in it. But it still doesn't work
I have checked that the balance of my account in remix have 100 ethers, but when I try to doing the buy token with it, it still said 'Not Enough Fund'
What I am expecting:
To be able to buy the token with the price that has been set when minting the token.
Your error is failing from here:
// require is like if statement. if condition does not satisfied, it shows the passed message
require(msg.value >= amount * tokenPrice[tokenId], "Not enough funds")
When a user sends funds alongside the transaction, not as an function argument, solidity will catch it with msg.value. Looks like you are not sending any value alongside the transaction or not sending enough funds. in Remix IDE
you need to pass the necessary value in the above red borderd box

ERC721 Invalid token id

I am building a NFT Marketplace
I use 2 smart contracts to :
first, mint the token
then, call setApprovalForAll() to authorize the marketplace contract to transfer the token
strangely, the owner is address(0) when it should be the msg.sender when I created the token last
" The transaction has been reverted to the initial state.
Reason provided by the contract: "ERC721: invalid token ID". "
This the source code of the 2 smart contracts below :
smart contract : NFT
smart contract : NFTMarketplace
Thanks for the help
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./#openzeppelin/contracts/utils/Counters.sol";
import "./#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./#openzeppelin/contracts/security/ReentrancyGuard.sol";
contract NFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
address contractAddress;
constructor(address marketplaceAddress) ERC721("NFTMarketplace", "NFTM") {
contractAddress = marketplaceAddress;
}
event TokenMinted (
uint256 indexed tokenId,
string tokenURI
);
function createToken(string memory tokenURI) public returns (uint) {
uint256 currentTokenId = _tokenIds.current();
_safeMint(msg.sender, currentTokenId);
_setTokenURI(currentTokenId, tokenURI);
setApprovalForAll(contractAddress, true);
_tokenIds.increment();
emit TokenMinted(currentTokenId, tokenURI);
return currentTokenId;
}
function getCurrentToken() public view returns (uint256) {
return _tokenIds.current();
}
}
contract NFTMarketplace is ReentrancyGuard, ERC721 {
using Counters for Counters.Counter;
//_tokenIds variable has the most recent minted tokenId
//Keeps track of the number of items sold on the marketplace
Counters.Counter private _itemsSold;
//owner is the contract address that created the smart contract
address payable owner;
//The fee charged by the marketplace to be allowed to list an NFT
uint256 listPrice = 0.01 ether;
constructor() ERC721("NFTMarketplace", "NFTM") {}
//The structure to store info about a listed token
struct Token {
uint256 tokenId;
string tokenURI;
address nftContract;
string name;
address payable owner;
uint256 price;
bool isListed;
}
//the event emitted when a token is successfully listed
event TokenListedSuccess (
uint256 indexed tokenId,
address nftContract,
address owner,
uint256 price,
bool isListed
);
//This mapping maps tokenId to token info and is helpful when retrieving details about a tokenId
mapping(uint256 => Token) private idToToken;
function updateListPrice(uint256 _listPrice) public payable {
require(owner == msg.sender, "Only owner can update listing price");
listPrice = _listPrice;
}
//The first time a token is created, it is listed here
// make it payable with money -- add require
function listToken(address nftContract, uint256 currentTokenId, string memory tokenURI, string memory name, uint256 price) public payable {
require(msg.value > 0, "Price must be at least 1 wei");
require(msg.value == listPrice, "Price must be equal to listing price");
idToToken[currentTokenId] = Token(
currentTokenId,
tokenURI,
nftContract,
name,
payable(address(this)),
listPrice,
true
);
emit TokenListedSuccess(
currentTokenId,
nftContract,
msg.sender,
listPrice,
true
);
}
function buyNFT(address nftContract, uint256 itemId) public payable nonReentrant {
uint price = idToToken[itemId].price;
uint tokenId = idToToken[itemId].tokenId;
address seller = ERC721.ownerOf(tokenId);
address buyer = msg.sender;
require(msg.value > 0, "You need to send some ether");
require(buyer != seller,"You already own this nft");
require(msg.value == price, "Please submit the asking price in order to complete the purchase");
idToToken[itemId].isListed = false;
idToToken[itemId].owner = payable(buyer);
payable(seller).transfer(price);
_itemsSold.increment();
IERC721(nftContract).transferFrom(seller, buyer, tokenId);
}
/* ...rest of smart contract */
}
I think you forget to assign "owner" value.
Typically, we set it in constructor.
like
constructor() {
i_owner = msg.sender;
}

Despite using the receive() function, my contract is not receiving payment in Remix

I am writing a smart contract in Remix with Solidity. The purpose of the contract is to allow a user to mint an NFT for 1 ETH. At present, the user is able to mint and the contract accepts the payment (ie. the user's balance is properly subtracted). But when I check the address(this).balance of the contract with my accountBalance() function, the function returns 0. I have included the receive() function as per the Solidity docs:
event Received(address, uint);
receive() external payable {
emit Received(msg.sender, msg.value);
}
Can someone explain why this is happening and what I need to change about my contract? Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// imports
import '#openzeppelin/contracts/token/ERC721/ERC721.sol';
import '#openzeppelin/contracts/access/Ownable.sol';
import '#openzeppelin/contracts/security/PullPayment.sol';
// contract
contract RobocopPoster is ERC721, Ownable, PullPayment {
// constants
uint256 public mintPrice;
uint256 public totalSupply;
uint256 public maxSupply;
uint256 public maxPerWallet;
bool public mintEnabled;
mapping (address => uint256) public walletMints;
// constructor
// initialize variables
constructor() payable ERC721('RobocopPoster', 'SFFPC') {
mintPrice = 1 ether;
totalSupply = 0;
maxSupply = 1000;
maxPerWallet = 3;
}
event Received(address, uint);
receive() external payable {
emit Received(msg.sender, msg.value);
}
// functions
function setMintEnabled(bool mintEnabled_) external onlyOwner {
mintEnabled = mintEnabled_;
}
function withdrawPayments(address payable payee) public override onlyOwner virtual {
super.withdrawPayments(payee);
}
function accountBalance() public view returns (uint256) {
return (address(this).balance);
}
function mint(uint256 quantity_) public payable {
require(mintEnabled, 'Minting not enabled.');
require(msg.value == quantity_ * mintPrice, 'wrong mint value');
require(totalSupply + quantity_ <= maxSupply, 'sold out');
require(walletMints[msg.sender] + quantity_ <= maxPerWallet, 'exceed max wallet');
walletMints[msg.sender] += quantity_;
_asyncTransfer(address(this), msg.value);
for (uint i = 0; i < quantity_; i++) {
uint256 newTokenId = totalSupply + 1;
totalSupply++;
_safeMint(msg.sender, newTokenId);
}
}
}
You need to call withdrawPayments to receive the fund, because _asyncTransfer from PullPayment in your contract minting sent the fund to the escrow contract. That's why you saw zero balance in ERC721 contract.

How to send correctly ETH from the manager's deposit to user if an event occurs?

I have this smart contract that I am trying to test. The manager deposits a certain amount of ether. Users can use the vault to earn extra ethers by locking own ether. Users deposit the amount of ether they want (ex 0.10 ETH) and set the seconds for locking. When users deposit, the price of ethereum is recorded via chainlink. If at the end of the locking period the price of ethereum is less than 2000$ the user receives the amount of locked ETH (0.10 ETH) + 2x (0.20ETH) the amount of locked ethers. The extra ethers are taken from the manager's deposit.
The code seems to work fine and the console returns no errors. I am testing the smart contract on the Kovan network. The problem is encountered when the user tries to withdraw the ethereums. When he withdraws, only the deposited ones are returned without the extra ethers being added.
I am new to Solidity so I appreciate any criticism or advice. If there is something already existing similar to what I am trying to create please let me know.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
import "#chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
interface EACAggregatorProxy {
function latestAnswer() external view returns (int256);
}
contract oracleLink {
address public manager;
uint256 public managerDeposit;
uint256[] public managerDeposits;
constructor() payable {
manager = msg.sender;
managerDeposit = msg.value;
managerDeposits.push(managerDeposit);
}
function depositVault() public payable {
require(msg.sender == manager);
}
address public user;
uint256 public userContribution;
uint256 public userCount;
uint256 deadline;
uint256 lockAmount = lockAmounts[msg.sender];
mapping(address => uint) lockAmounts;
uint256 startTime = startTimes[block.timestamp];
mapping(uint => uint) startTimes;
address public chainLinkETHUSDAddress = 0x9326BFA02ADD2366b30bacB125260Af641031331;
uint public ethPrice = 0;
uint256 public price = ethPrice;
function deposit(uint256 numberOfSeconds) public payable {
lockAmounts[msg.sender] = msg.value;
startTimes[block.timestamp] = block.timestamp;
user = msg.sender;
userContribution = msg.value;
userCount++;
deadline = block.timestamp + (numberOfSeconds * 1 seconds);
int256 chainLinkEthPrice = EACAggregatorProxy(chainLinkETHUSDAddress).latestAnswer();
ethPrice = uint(chainLinkEthPrice / 100000000);
}
function withdraw() public payable {
if (ethPrice <= 2000) {
uint256 toWithdraw = lockAmounts[msg.sender];
uint256 amountOfToken = toWithdraw * 2;
payable(manager).transfer(amountOfToken);
}
require(block.timestamp >= deadline);
uint256 amountToWithdraw = lockAmounts[msg.sender];
lockAmounts[msg.sender] = 0;
payable(msg.sender).transfer(amountToWithdraw);
}
}
So the one thing that I noticed is that when you try to send the additional ETH to the recipient's address you're actually trying to send it back to the manager and not the recipient. Also note that you should avoid using the transfer method and use the call method instead: call{value: amount}("") should now be used for transferring ether (Do not use send or transfer.) as of May 2021.
So your withdraw method should look something like this instead:
function withdraw() public payable {
address recipient = msg.sender;
uint256 additionalToken;
if (ethPrice <= 2000) {
additionalToken = lockAmounts[msg.sender] * 2;
}
require(block.timestamp >= deadline);
uint256 amountToWithdraw = lockAmounts[msg.sender] + additionalToken;
lockAmounts[msg.sender] = 0;
(bool success, ) = payable(recipient).call{value: amountToWithdraw}("");
require(success, "Transfer failed.");
}
Hopefully this helps.

Check which tokens came to the contract

Solidity. How do I check which tokens were sent to the contract? How do I check the USDT token?
contract DEX {
event Bought(uint256 amount);
event Sold(uint256 amount);
IERC20 public token;
constructor() public {
token = new ERC20Basic();
}
function buy() payable public {
uint256 amountTobuy = msg.value;
uint256 dexBalance = token.balanceOf(address(this));
require(amountTobuy > 0, "You need to send some Ether");
require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
token.transfer(msg.sender, amountTobuy);
emit Bought(amountTobuy);
}
function sell(uint256 amount) public {
require(amount > 0, "You need to sell at least some tokens");
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
msg.sender.transfer(amount);
emit Sold(amount);
}
}
You're looking for the balanceOf function, you could use OpenZepplin's implementation of this as well.
uint balance = token.balanceOf(account);
For each token you want to check, you'll need the tokens address too. So for USDT, you'd need the token address of USDT. Then you could do something like (nodejs syntax):
pragma solidity ^0.6.0;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
contract GetBalance{
constructor() public {
}
function getBalanceOfToken(address _USDT_ADDRESS) public returns (uint){
usdt = IERC20(_USDT_ADDRESS);
uint balance = usdt.balanceOf(address(this));
return balance;
}
}