I implemented the smart contract as follows.
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Trade {
enum TradeState {
Start,
Proceeding,
Shipping,
Cancel,
Complete,
Return
}
address payable public seller;
address payable public buyer;
uint256 public productID;
uint256 public price;
uint256 public trackingNumber;
uint256 public depositAmount;
TradeState public currentTradeState;
constructor(address _buyer, uint256 _productID, uint256 _price) payable {
seller = payable(msg.sender);
depositAmount = msg.value;
buyer = payable(_buyer);
productID = _productID;
price = _price; // * (10 ** 18);
trackingNumber = 0;
currentTradeState = TradeState.Start;
}
function setTrackingNumber(uint256 _trackingNumber) public {
require(msg.sender == seller);
trackingNumber = _trackingNumber;
currentTradeState = TradeState.Shipping;
}
function makePayment() public payable returns (bool result) {
require(msg.sender == buyer && msg.value == price, "Not enough ETH");
currentTradeState = TradeState.Proceeding;
return true;
}
function completeTrade() public payable {
require(msg.sender == buyer, "msg.sender is not buyer!");
require(trackingNumber != 0, "trackingNumber has not been set.");
seller.transfer(price + depositAmount);
if (address(this).balance > 0) {
buyer.transfer(address(this).balance);
}
currentTradeState = TradeState.Complete;
}
function cancel() public payable {
require(currentTradeState != TradeState.Shipping, "Already shipped.");
//buyer.transfer(price);
seller.transfer(depositAmount);
if (address(this).balance > 0) {
buyer.transfer(address(this).balance);
}
currentTradeState = TradeState.Cancel;
}
function returnProduct() public payable {
require(msg.sender == buyer, "caller must be buyer.");
buyer.transfer(address(this).balance);
currentTradeState = TradeState.Return;
}
function transferWithoutPayingFee(address payable addr, uint256 amount) internal {
addr.transfer(amount);
}
}
Then, the contract was deployed and accessed using the ethers.js library.
There is no problem with contract deployment and accessing other methods.
However, when sending a transaction that calls cancel() or returnProduct(), it is not executed normally with the following error.
The two methods are called as follows.
async function cancel(contractAddress, privateKey) {
let wallet = new ethers.Wallet(privateKey, provider);
let contract = new ethers.Contract(contractAddress, contractABI, provider);
let contractWithSigner = contract.connect(wallet);
let tx = await contractWithSigner.cancel(option);
await tx.wait();
}
async function returnProduct(contractAddress, privateKey) {
let wallet = new ethers.Wallet(privateKey, provider);
let contract = new ethers.Contract(contractAddress, contractABI, provider);
let contractWithSigner = contract.connect(wallet);
let tx = await contractWithSigner.returnProduct();
await tx.wait();
}
The error log that occurs is as follows.
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (error={"reason":"processing response error","code":"SERVER_ERROR","body":"{\"id\":65,\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"VM Exception while processing transaction: revert\",\"code\":-32000,\"data\":{\"stack\":\"RuntimeError: VM Exception while processing transaction: revert\\n at Function.RuntimeError.fromResults (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\utils\\\\runtimeerror.js:94:13)\\n at module.exports (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\utils\\\\gas\\\\guestimation.js:142:32)\",\"name\":\"RuntimeError\"}}}","error":{"code":-32000,"data":{"stack":"RuntimeError: VM Exception while processing transaction: revert\n at Function.RuntimeError.fromResults (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\utils\\runtimeerror.js:94:13)\n at module.exports (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\utils\\gas\\guestimation.js:142:32)","name":"RuntimeError"}},"requestBody":"{\"method\":\"eth_estimateGas\",\"params\":[{\"gasPrice\":\"0x4a817c800\",\"from\":\"0xb55a7a6d8cf909e938cd003c453ea7987fd4014a\",\"to\":\"0x21436e17d53fc0e34609883ad095c3c6d0ad79e5\",\"data\":\"0x056baaba\"}],\"id\":65,\"jsonrpc\":\"2.0\"}","requestMethod":"POST","url":"HTTP://127.0.0.1:7545"}, tx={"data":"0x056baaba","to":{},"from":"0xB55A7A6d8cf909E938cd003c453ea7987fd4014a","gasPrice":{"type":"BigNumber","hex":"0x04a817c800"},"type":0,"nonce":{},"gasLimit":{},"chainId":{}}, code=UNPREDICTABLE_GAS_LIMIT, version=abstract-signer/5.6.2)
at Logger.makeError (C:\Users\yang\Desktop\졸업과제\Offchain-Backend\node_modules\#ethersproject\logger\lib\index.js:233:21)
at Logger.throwError (C:\Users\yang\Desktop\졸업과제\Offchain-Backend\node_modules\#ethersproject\logger\lib\index.js:242:20)
at C:\Users\yang\Desktop\졸업과제\Offchain-Backend\node_modules\#ethersproject\abstract-signer\lib\index.js:365:47
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Promise.all (index 6) {
reason: 'cannot estimate gas; transaction may fail or may require manual gas limit',
code: 'UNPREDICTABLE_GAS_LIMIT',
...
When the test code was written and tested on the truffle framework, it worked normally. My guess is that require(...) seems to be causing the problem. Please advise on how to solve this problem.
Related
`import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
contract GoTekERC721 is ERC721, Ownable {
constructor() ERC721("Go NFT", "GoN") {}
//Owner to Phone
mapping(address => uint256[]) public phones;
//change
// Phone to Balance
mapping(uint256 => uint256) public balance;
//mapping(uint256 => uint256) public userBalances;
function register(uint256 phone, uint256 Balance) public {
_mint(msg.sender, phone);
phones[msg.sender].push(phone);
balance[phone] = Balance;
}
function details(address owner) public view returns(string[] memory) {
uint256[] memory ownerPhones = phones[owner];
string memory var1;
string memory var2;
string[] memory result;
for (uint256 i = 0; i < ownerPhones.length; i++) {
uint256 phone = ownerPhones[i];
mapping(uint256 => uint256) storage userBalances=balance;
var1=Strings.toString(userBalances[phone]);
var2=Strings.toString(balance[phone]);
result.push(string(bytes.concat(bytes(var1),":",bytes(var2))));
}
return result;
}
}
I'm trying to return an array but get an error ...if i return return string(bytes.concat(bytes(var1),":",bytes(var2))) and in returns (string memory) then only 1 string o/p i get i.e 199:199 balance:balance but i want phone:balance 9685968596:199,9865986598:299,
thanks in advance.`
`I'm trying to return an array but get an error ...if i return return string(bytes.concat(bytes(var1),":",bytes(var2))) and in returns (string memory) then only 1 string o/p i get i.e 199:199 balance:balance but i want phone:balance 9685968596:199,9865986598:299,
thanks in advance.``
I am working on smart contract for a simple payment system where u deposit and get a code. Then u can give a code to anyone and they can withdraw to any address.
I have already tested on all testnets and it works fine.
But on ethereum mainnet as soon as I deposit the eth, it is stolen and transferred by a mev bot.
//SPDX-License-Identifier: UNLICENSED
// It will be used by the Solidity compiler to validate its version.
pragma solidity ^0.8.9;
// We import this library to be able to use console.log
import "hardhat/console.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract Payment is Ownable {
address payable public feeWallet;
uint256 public feeAmount;
uint256 private numberOfDeposits = 0;
struct Deposit {
uint256 time;
bytes32 id;
uint256 amount;
}
// mapping(address => bytes32[]) public addressToHashes;
mapping(bytes32 => uint256) public hashToAmount;
mapping(address => Deposit[]) public addressToDeposits;
mapping(bytes32 => bool) public isWithdrawn;
event DepositCreated(address _payer, uint256 _amount);
event DepositWithdrawn(
address _withdrawer,
uint256 _amount,
address indexed _withdrawTo
);
constructor(address _feeWallet) {
feeWallet = payable(_feeWallet);
}
function createDeposit(string memory _id) public payable returns (bytes32) {
//Check if value is greater than 0
require(msg.value > 0, "Amount cannot be equal to 0");
//Fetch mapppings
Deposit[] storage deposits = addressToDeposits[msg.sender];
//Create Hash from id
bytes32 hash = generateId(_id);
//Update Mappings
hashToAmount[hash] = msg.value;
numberOfDeposits += 1;
deposits.push(Deposit(block.timestamp, hash, msg.value));
addressToDeposits[msg.sender] = deposits;
emit DepositCreated(msg.sender, msg.value);
return hash;
}
function generateId(string memory _id) internal view returns (bytes32) {
uint id = uint(
keccak256(
abi.encodePacked(
_id,
block.difficulty,
block.timestamp,
numberOfDeposits
)
)
);
return bytes32(id % 100000000000000);
}
function checkDepositExist(uint _id) public view returns (bool) {
bytes32 hash = bytes32(_id);
uint256 amount = hashToAmount[hash];
console.log("amount", amount);
if (amount > 0) {
return true;
} else {
return false;
}
}
function viewDeposit(uint _id) public view returns (uint256) {
bytes32 hash = bytes32(_id);
uint256 amount = hashToAmount[hash];
return amount;
}
function withdrawDeposit(uint _id, address _to) public {
require(checkDepositExist(_id), "ID invalid");
bytes32 hash = bytes32(_id);
uint256 amount = hashToAmount[hash] - feeAmount;
hashToAmount[hash] = 0;
(bool sent, bytes memory data) = payable(_to).call{value: amount}("");
require(sent, "Failed to send deposit amount");
require(sendFees(), "Failed to send fee amount");
isWithdrawn[hash] = true;
emit DepositWithdrawn(msg.sender, amount, _to);
}
function getUserDeposits() public view returns (Deposit[] memory) {
Deposit[] memory deposits = addressToDeposits[msg.sender];
return deposits;
}
function setFeeAmount(uint _amount) public onlyOwner {
feeAmount = _amount;
}
function setFeeWallet(address _wallet) public onlyOwner {
feeWallet = payable(_wallet);
}
function sendFees() internal returns (bool) {
(bool sent, bytes memory data) = payable(feeWallet).call{
value: feeAmount
}("");
return sent;
}
}
Here's the transaction of eth being stolen https://etherscan.io/tx/0xd4f92a3346ff51cf41c40b47b1270eda5ca57c4aaae2b3c9858298d8c6269725
I can point out many vulnerabilities that this smart contract has.
One of them is that there is no private data in a smart contract. Anybody can read the data in the storage of a smart contract. In this case, I see that hashToAmount has the hashes that you send to the user who created the deposit. But this can be read by anyone (using something like web3.eth.getStorageAt(contractAddress, storageIndex)) and call the withdrawDeposit function and steal the funds.
Something else, miners can temper with some blockchain data, like the timestamp, etc. Miners can see the _id that is being sent to the createDeposit and use it to immediately steal the funds from the contract.
Also, I see a lack of checks in the withdrawDeposit function.
I suggest that you use well known patterns and modifiers and other checks. Relying more on things like msg.sender since nobody can fake or tamper with msg.sender value. The sender is always the user that created the request, and if the sender is the owner of some ether, than only they can withdraw it.
Instead of checking the balance of that hash, save the msg.sender in the mapping instead when someone makes a deposit. Then, while withdrawing, check that the msg.sender has balance and if so, send the balance to the _to address.
Also, check for reentrancy attacks in your withdrawDeposit deposit function.
Trying to generate an id in a smart contract with the block data is not that secure. There are oracle libraries that can help you get a pseudo-random number from outside of the contract, but again, it can be read by a miner before the transaction is included in a block and compromise your funds.
I'm not sure if any of these are causing your problem exactly, but I'm sure that all of these could be potential issues for your contract.
I recommend you follow security standard patterns like from OpenZeppelin: https://www.openzeppelin.com/.
I am following along full blockchain solidity course.
I am trying to deploy fund_and_withdraw.py to Rinkeby using brownie but I run into the follwoing error:
ValueError: Gas estimation failed: 'execution reverted: You need to spend more ETH!'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually.
I previously tried deploying it on ganache-cli but get a "Index out of range" issue as described on my previous issue.
Now, I am trying to run it on Rinkeby (Ganache UI is not connecting to my Brownie and is throwing off the entire contract) but it returns the "ValueError".
Also, tried changing the decimals in FundMe.py.
I tried deploying on Rinkby and I no longer get "Index out of range" but instead i get "ValueError"
My code below:
fund_and_withdraw
from brownie import FundMe
from scripts.helpful_scripts import get_account
def fund():
fund_me = FundMe[-1]
account = get_account()
entrance_fee = fund_me.getEntranceFee()
print(entrance_fee)
print(f"The current entry fee is {entrance_fee}")
print("funding")
fund_me.fund(
{
"from": account,
"value": entrance_fee,
}
)
# 0.025000000000000000
def main():
fund()
Helpful_scripts.py
from brownie import network, config, accounts, MockV3Aggregator
from web3 import Web3
# Create Variable to store dev network list (Ex: ["development", "ganache", etc...])
LOCAL_BLOCKCHAIN_ENVIRONMENTS = "development"
DECIMALS = 8
STARTING_PRICE = 200000000000
def get_account():
# if network.show_active == "development":
if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
return accounts[0]
else:
return accounts.add(config["wallets"]["from_key"])
def deploy_mocks():
print(f"The active network is {network.show_active()}")
print("Deploying Mocks...")
if len(MockV3Aggregator) <= 0:
MockV3Aggregator.deploy(
# DECIMALS, Web3.toWei(STARTING_PRICE, "ether"), {"from": get_account()}
DECIMALS,
STARTING_PRICE,
{"from": get_account()},
)
print("Mocks Deployed!")
FundMe.py
contract FundMe {
using SafeMath96 for uint256;
mapping(address => uint256) public addressToAmountFunded;
address[] public funders;
address public owner;
AggregatorV3Interface public priceFeed;
//constructor(address _priceFeed) public {
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
owner = msg.sender;
}
function fund() public payable {
uint256 minimumUSD = 50 * 10**18;
require(
getConversionRate(msg.value) >= minimumUSD,
"You need to spend more ETH!"
);
addressToAmountFunded[msg.sender] += msg.value;
funders.push(msg.sender);
}
function getVersion() public view returns (uint256) {
return priceFeed.version();
}
function getPrice() public view returns (uint256) {
(, int256 answer, , , ) = priceFeed.latestRoundData();
//return uint256(answer * 10000000000);
return uint256(answer * 100000000);
}
// 1000000000
function getConversionRate(uint256 ethAmount)
public
view
returns (uint256)
{
uint256 ethPrice = getPrice();
//uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;
uint256 ethAmountInUsd = (ethPrice * ethAmount) / 100000000;
return ethAmountInUsd;
}
function getEntranceFee() public view returns (uint256) {
// minimumUSD
uint256 minimumUSD = 50 * 10**18;
uint256 price = getPrice();
uint256 precision = 1 * 10**18;
// return (minimumUSD * precision) / price;
// We fixed a rounding error found in the video by adding one!
return ((minimumUSD * precision) / price) + 1;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function withdraw() public payable onlyOwner {
payable(msg.sender).transfer(address(this).balance);
for (
uint256 funderIndex = 0;
funderIndex < funders.length;
funderIndex++
) {
address funder = funders[funderIndex];
addressToAmountFunded[funder] = 0;
}
funders = new address[](0);
}
}
Thank you!
Your require statement is failing in this line:
require(
getConversionRate(msg.value) >= minimumUSD,
"You need to spend more ETH!"
);
This probably means that you don't have enough ether in your wallet to send to the contract. So fund your wallet with more ether and it will probably work if you deployed the contract correctly.
The index out of range is probably a fault in your get_account() function it seems. The LOCAL_BLOCKCHAIN_ENVIRONMENTS should be an array not a string. It also doesn't contain the ganache-local so you are trying to use your private key for your local blockchain that is probably why you are getting index out of range.
So fix your LOCAL_BLOCKCHAIN_ENVIRONMENTS to look like this:
LOCAL_BLOCKCHAIN_ENVIRONMENTS = ["development", "ganache-local"]
I guess it should work then.
from brownie import FundMe
from scripts.helpful_scripts import get_account
def fund():
_priceFeed = 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e
account = get_account()
fund_me = FundMe.deploy(_priceFeed , {"from": account})
**_priceFeed is in your constructor , so you should pass it to your
contract for deploy, i added it as hardcode , but its better to use yaml
file for projects **
entrance_fee = fund_me.getEntranceFee()
print(entrance_fee)
print(f"The current entry fee is {entrance_fee}")
print("funding")
fund_tx = fund_me.fund(
{
"from": account,
"value": entrance_fee,
}
)
# 0.025000000000000000
fund_tx.wait(1)
** when you use functions that make transaction(not reading) , its better
to use wait() method , its help your transaction to write in block **
def main():
fund()
I'm Solidity Newbie.
I'm learning how to implement Transparent Proxy using Openzeppelin's TransparentUpgradeableProxy contract, but am having some problems.
Step 1: I tried to deploy a simple contract MyConV0, then implemented deploy and call method, everything is fine.
// File MyConV0.sol
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract MyConV0 {
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
console.log(_symbol);
}
function symbol() public view returns (string memory) {
console.log(_symbol);
return _symbol;
}
function name() public view returns (string memory) {
console.log('Name: ');
console.log(_name);
return _name;
}
function getVersion() pure external returns(uint256) {
return 0;
}
}
Step 2: I tried to upgrade to MyConV1 to be able to Upgradable with TransparentUpgradeableProxy but failed.
// File: MyConV1
pragma solidity ^0.8.0;
import "#openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "#openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "#openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "hardhat/console.sol";
contract MyConV1 is Initializable, OwnableUpgradeable {
string private _name;
string private _symbol;
function initialize(string memory name_, string memory symbol_) initializer public {
__Ownable_init();
_name = name_;
_symbol = symbol_;
console.log(_symbol);
}
function symbol() public view returns (string memory) {
console.log('Symbol: ');
console.log(_symbol);
return _symbol;
}
function name() public view returns (string memory) {
console.log('Name: ');
console.log(_name);
return _name;
}
function getVersion() pure external returns(uint256) {
return 1;
}
}
Refer TransparentUpgradableProxy: https://docs.openzeppelin.com/contracts/4.x/api/proxy#TransparentUpgradeableProxy
// The test file in JS using ethers.js and Hardhat environment
async function main() {
let t0, t1, t2, t3, t4, v1, v2, v3, v4;
const [owner, proxyAdmin, user, other] = await ethers.getSigners();
// --- 0. Deploy MyConV0
let input0 = [ 'TotoName', 'Toto' ];
let con0 = await deploy_verify_contract("MyConV0", input0);
t0 = await con0.symbol() ;
console.log(t0); // worked -> Toto
// --- 1. Deploy MyConV1
let input1 = [ ];
let con1 = await deploy_verify_contract("MyConV1", input1);
// --- 2. get data
let abi = [ `function initialize( string name_,
string symbol_,
)` ];
let iface = new ethers.utils.Interface(abi);
let data = iface.encodeFunctionData("initialize", [ 'TotoName', 'Toto' ]);
// --- 3. deploy trans proxy
let input2 = [ con1.address, owner.address, data ];
let con2 = await deploy_verify_contract("TransparentUpgradeableProxy1", input2);
// --- 4. call proxy method
t2 = await con2.implementation();
console.log(t2); // DO NOT WORK, t2 is object tx, and do not contains the results like step 0
// --- 5. call MyConV1 contact via proxy -> ERROR: "TypeError: con2.symbol is not a function"
t3 = await con2.symbol();
console.log(t3);
}
async function deploy_verify_contract(contractName, input, lib = {}){
const _contract = await hre.ethers.getContractFactory(contractName, lib);
const contract = await _contract.deploy(...input);
await contract.deployed();
console.log( contractName + " deployed to:", contract.address );
return contract;
}
I used Hardhat's console.log function and it seems to have successfully deployed the Proxy, and sent the correct data to the MyConV1.initialize function, but don't know how to call the proxy properly. Specifically with the above code, I don't understand a few points:
Have I deployed the proxy correctly?
Why can't I get the correct return data of the proxy's implementation() function?
Why can't I call MyConV1's function through the proxy?
Hope you guys can help me how to correct the code, I have not been able to solve this problem for a few days.
You are not calling UpgradeTo() on proxy contract to set the implementation contract's address. After you have set the implementation contract in proxy you have to initialise the implementation contract via proxy contract only! not any other way or directly calling it on implementation contract (it wont work!!)
I got this error from metamask.
It was working fine couple of hours ago. I have tried reinstalling/ disable and re-enabling again but nothing worked.
Also,
My smart contract is fully functional (Tested in Remix Browser based IDE) and no other errors or logs are present anywhere. I also restarted Ganache and re-compiled and re-migrated my contracts with no luck.
Here is my solidity code:
pragma solidity ^0.4.18;
contract Voting {
address mainAddress;
bytes32[] candidateNames;
mapping(bytes32 => uint) candidateVotes;
mapping(bytes32 => bytes32) candidatesDetails;
function Voting() public {
mainAddress = msg.sender;
}
modifier isMainAddress {
if (msg.sender == mainAddress) {
_;
}
}
function getAllCandidates() public view returns (bytes32[]) {
return candidateNames;
}
function setCandidate(bytes32 newCandidate) isMainAddress public {
candidateNames.push(newCandidate);
}
function setVote(bytes32 candidate) public {
candidateVotes[candidate] = candidateVotes[candidate] + 1;
}
function getVote(bytes32 candidate) public view returns (uint) {
return candidateVotes[candidate];
}
function setDescrption(bytes32 candidateName, bytes32 candidatesDesc) isMainAddress public {
candidatesDetails[candidateName] = candidatesDesc;
}
function getDescription(bytes32 candidateName) public view returns (bytes32){
return candidatesDetails[candidateName];
}
}
And I am calling these functions like :
let votingContractInstance;
const contract = require('truffle-contract')
const votingContract = contract(VotingContract)
votingContract.setProvider(this.state.web3.currentProvider)
this.state.web3.eth.getAccounts((error, accounts) => {
votingContract.deployed().then((instance) => {
votingContractInstance = instance
return votingContractInstance.setVote(this.state.candidateName);
}).then((result) => {
this.setState(() => ({
allCandidates: result
}));
})
})
All of the calls are made by this way only.
I am using one of the truffle boxes (REACT box) and no logs/errors are present in console either.
Did you figure this out? Can you just call this.setState({ allcandidates: result })
Also, the result from setVote isn't anything because you don't have it return anything in the solc contract.