Error: cannot estimate gas; transaction may fail or may require manual gas limit - testing

im trying to test a constructor method of a exchange developed in solidity but i given the following error:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="VM Exception while processing transaction: reverted with reason string 'Invalid address _tokenVault'", method="estimateGas", transaction={"from":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","data":"0x60806040523480156200001157600080fd5b5060405162001dba38038062001dba833981810160405281019062000037919062000239565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603620000a9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000a090620002f6565b60405180910390fd5b33600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060058190555060126000819055506003600181905550600060068190555050505062000318565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620001c68262000199565b9050919050565b620001d881620001b9565b8114620001e457600080fd5b50565b600081519050620001f881620001cd565b92915050565b6000819050919050565b6200021381620001fe565b81146200021f57600080fd5b50565b600081519050620002338162000208565b92915050565b60008060006060848603121562000255576200025462000194565b5b60006200026586828701620001e7565b93505060206200027886828701620001e7565b92505060406200028b8682870162000222565b9150509250925092565b600082825260208201905092915050565b7f496e76616c69642061646472657373205f746f6b656e5661756c740000000000600082015250565b6000620002de601b8362000295565b9150620002eb82620002a6565b602082019050919050565b600060208201905081810360008301526200031181620002cf565b9050919050565b611a9280620003286000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063a8f6c91311610097578063d0e30db011610066578063d0e30db01461028c578063e4828367146102aa578063e6aa216c146102da578063f071db5a146102f8576100f5565b8063a8f6c913146101f0578063ae06c1b71461020e578063b03a9a051461023e578063bda6606c1461025c576100f5565b80636497a8a0116100d35780636497a8a0146101665780638da5cb5b146101965780639902084b146101b4578063a001ecdd146101d2576100f5565b80632d296bf1146100fa578063313ce5671461012a5780635bc789d914610148575b600080fd5b610114600480360381019061010f9190611254565b610316565b6040516101219190611290565b60405180910390f35b6101326105ec565b60405161013f9190611290565b60405180910390f35b6101506105f2565b60405161015d91906112ec565b60405180910390f35b610180600480360381019061017b9190611333565b610618565b60405161018d9190611290565b60405180910390f35b61019e61089a565b6040516101ab91906112ec565b60405180910390f35b6101bc6108c0565b6040516101c99190611290565b60405180910390f35b6101da6109f8565b6040516101e79190611290565b60405180910390f35b6101f86109fe565b60405161020591906112ec565b60405180910390f35b61022860048036038101906102239190611254565b610a24565b6040516102359190611290565b60405180910390f35b610246610b09565b6040516102539190611290565b60405180910390f35b61027660048036038101906102719190611254565b610b0f565b6040516102839190611290565b60405180910390f35b610294610b81565b6040516102a19190611290565b60405180910390f35b6102c460048036038101906102bf9190611254565b610e46565b6040516102d19190611290565b60405180910390f35b6102e26111d6565b6040516102ef9190611290565b60405180910390f35b61030061120b565b60405161030d9190611290565b60405180910390f35b600080821161035a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610351906113bd565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c836040518263ffffffff1660e01b81526004016103939190611290565b6020604051808303816000875af11580156103b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d691906113f2565b6103de611211565b101561041f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104169061146b565b60405180910390fd5b600060646001548461043191906114ba565b61043b919061152b565b90506000818461044b919061155c565b9050816006600082825461045f9190611590565b9250508190555080600560008282546104789190611590565b92505081905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b81526004016104dc9291906115c4565b600060405180830381600087803b1580156104f657600080fd5b505af115801561050a573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c876040518263ffffffff1660e01b81526004016105619190611290565b6020604051808303816000875af1158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a491906113f2565b6105ac611211565b6105b6919061155c565b9081150290604051600060405180830381858888f193505050501580156105e1573d6000803e3d6000fd5b508092505050919050565b60005481565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610688576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067f90611639565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff163b146106e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106d9906116a5565b60405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610772576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161076990611711565b60405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f846040518263ffffffff1660e01b81526004016107cf91906112ec565b602060405180830381865afa1580156107ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081091906113f2565b11610850576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108479061177d565b60405180910390fd5b81600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060019050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610952576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094990611711565b60405180910390fd5b6706f05b59d3b20000600654101561099f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610996906117e9565b60405180910390fd5b60006006819055503373ffffffffffffffffffffffffffffffffffffffff166108fc6006549081150290604051600060405180830381858888f193505050501580156109ef573d6000803e3d6000fd5b50600654905090565b60015481565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000808211610a68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a5f90611855565b60405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610af8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aef90611711565b60405180910390fd5b816001819055506001549050919050565b60055481565b6000808211610b53576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4a906118c1565b60405180910390fd5b6000600554670de0b6b3a764000084610b6c91906114ba565b610b76919061152b565b905080915050919050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610c13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c0a90611711565b60405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f336040518263ffffffff1660e01b8152600401610c7091906112ec565b602060405180830381865afa158015610c8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb191906113f2565b03610cf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ce89061192d565b60405180910390fd5b60003073ffffffffffffffffffffffffffffffffffffffff1663bda6606c610d17611211565b6040518263ffffffff1660e01b8152600401610d339190611290565b6020604051808303816000875af1158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7691906113f2565b90508060056000828254610d8a9190611590565b92505081905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401610e109291906115c4565b600060405180830381600087803b158015610e2a57600080fd5b505af1158015610e3e573d6000803e3d6000fd5b505050505090565b6000808211610e8a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e8190611999565b60405180910390fd5b81600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f336040518263ffffffff1660e01b8152600401610ee691906112ec565b602060405180830381865afa158015610f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2791906113f2565b1015610f68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5f90611a05565b60405180910390fd5b6000606460015484610f7a91906114ba565b610f84919061152b565b905060008184610f94919061155c565b9050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b333836040518363ffffffff1660e01b8152600401610ff39291906115c4565b600060405180830381600087803b15801561100d57600080fd5b505af1158015611021573d6000803e3d6000fd5b50505050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518463ffffffff1660e01b81526004016110a693929190611a25565b600060405180830381600087803b1580156110c057600080fd5b505af11580156110d4573d6000803e3d6000fd5b5050505081600660008282546110ea9190611590565b925050819055508060056000828254611103919061155c565b925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c846040518263ffffffff1660e01b815260040161115d9190611290565b6020604051808303816000875af115801561117c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a091906113f2565b9081150290604051600060405180830381858888f193505050501580156111cb573d6000803e3d6000fd5b508092505050919050565b600080670de0b6b3a7640000600554670de0b6b3a76400006111f891906114ba565b611202919061152b565b90508091505090565b60065481565b600034905090565b600080fd5b6000819050919050565b6112318161121e565b811461123c57600080fd5b50565b60008135905061124e81611228565b92915050565b60006020828403121561126a57611269611219565b5b60006112788482850161123f565b91505092915050565b61128a8161121e565b82525050565b60006020820190506112a56000830184611281565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006112d6826112ab565b9050919050565b6112e6816112cb565b82525050565b600060208201905061130160008301846112dd565b92915050565b611310816112cb565b811461131b57600080fd5b50565b60008135905061132d81611307565b92915050565b60006020828403121561134957611348611219565b5b60006113578482850161131e565b91505092915050565b600082825260208201905092915050565b7f496e76616c6964205f616d6f756e74546f4275792076616c7565000000000000600082015250565b60006113a7601a83611360565b91506113b282611371565b602082019050919050565b600060208201905081810360008301526113d68161139a565b9050919050565b6000815190506113ec81611228565b92915050565b60006020828403121561140857611407611219565b5b6000611416848285016113dd565b91505092915050565b7f496e73756666696369656e742065746865727300000000000000000000000000600082015250565b6000611455601383611360565b91506114608261141f565b602082019050919050565b6000602082019050818103600083015261148481611448565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006114c58261121e565b91506114d08361121e565b92508282026114de8161121e565b915082820484148315176114f5576114f461148b565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006115368261121e565b91506115418361121e565b925082611551576115506114fc565b5b828204905092915050565b60006115678261121e565b91506115728361121e565b925082820390508181111561158a5761158961148b565b5b92915050565b600061159b8261121e565b91506115a68361121e565b92508282019050808211156115be576115bd61148b565b5b92915050565b60006040820190506115d960008301856112dd565b6115e66020830184611281565b9392505050565b7f496e76616c69642061646472657373205f746f6b656e5661756c740000000000600082015250565b6000611623601b83611360565b915061162e826115ed565b602082019050919050565b6000602082019050818103600083015261165281611616565b9050919050565b7f5f746f6b656e5661756c742063616e6e6f74206265206120636f6e7472616374600082015250565b600061168f602083611360565b915061169a82611659565b602082019050919050565b600060208201905081810360008301526116be81611682565b9050919050565b7f4e6f74206175746f72697a656400000000000000000000000000000000000000600082015250565b60006116fb600d83611360565b9150611706826116c5565b602082019050919050565b6000602082019050818103600083015261172a816116ee565b9050919050565b7f5f746f6b656e5661756c7420686173206e6f2062616c616e6365000000000000600082015250565b6000611767601a83611360565b915061177282611731565b602082019050919050565b600060208201905081810360008301526117968161175a565b9050919050565b7f496e73756666696369656e7420616d6f756e74206f6620666565730000000000600082015250565b60006117d3601b83611360565b91506117de8261179d565b602082019050919050565b60006020820190508181036000830152611802816117c6565b9050919050565b7f496e76616c6964205f70657263656e746167652076616c756500000000000000600082015250565b600061183f601983611360565b915061184a82611809565b602082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f496e76616c6964205f746f6b656e416d6f756e742076616c7565000000000000600082015250565b60006118ab601a83611360565b91506118b682611875565b602082019050919050565b600060208201905081810360008301526118da8161189e565b9050919050565b7f4e6f20657468657273206465706f736974656400000000000000000000000000600082015250565b6000611917601383611360565b9150611922826118e1565b602082019050919050565b600060208201905081810360008301526119468161190a565b9050919050565b7f496e76616c6964205f616d6f756e74546f457863686167652076616c75650000600082015250565b6000611983601e83611360565b915061198e8261194d565b602082019050919050565b600060208201905081810360008301526119b281611976565b9050919050565b7f496e73756666696369656e742062616c616e6365000000000000000000000000600082015250565b60006119ef601483611360565b91506119fa826119b9565b602082019050919050565b60006020820190508181036000830152611a1e816119e2565b9050919050565b6000606082019050611a3a60008301866112dd565b611a4760208301856112dd565b611a546040830184611281565b94935050505056fea2646970667358221220ffad1a9439d9834abe467db3f1382023c0dc5f0f1296ff9a6bd076af0f22edd464736f6c6343000811003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a","accessList":null}, error={"stackTrace":[{"type":4,"sourceReference":{"function":"constructor","contract":"Exchange","sourceName":"contracts/Exchange.sol","sourceContent":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\nimport "./interface/IExchange.sol";\nimport "./interface/IERC20.sol";\n\ncontract Exchange is IExchange {\n\n uint256 public decimals;\n uint256 public feePercentage;\n address public owner;\n address public tokenVault;\n address public erc20Contract;\n uint256 public invariant;\n uint256 public feesCollected;\n\n/\n* #notice Constructor\n* #dev Throw if '_tokenVault' is zero address\n* #dev Throw if '_tokenVault' is a contract\n* #dev Throw if '_erc20Contract' is zero address\n* #dev Throw if '_erc20Contract' is not a contract\n* #dev Throw if '_tokenAmount' is zero\n* #dev Throw if '_tokenVault' doesn't have enough balance\n* #param _tokenVault Address of the token vault\n* #param _erc20Contract Address of the ERC20 contract\n* #param _tokenAmount Amount of tokens to be deposited in the vault\n */\nconstructor(address _tokenVault, address _erc20Contract,uint256 _tokenAmount ){\n require(_tokenVault != address(0), "Invalid address _tokenVault");\n // require(_tokenVault.code.length==0, "_tokenVault cannot be a contract");\n //require(_erc20Contract != address(0), "_erc20Contract cannot be zero address");\n // require(_erc20Contract.code.length>0, "_erc20Contract is not a contract");\n // require(_tokenAmount > 0, "Invalid _tokenAmount value");\n // require(IERC20(_erc20Contract).getBalance(_tokenVault) >= _tokenAmount, "Insufficient tokens in the vault");\n owner = msg.sender;\n tokenVault = _tokenVault;\n erc20Contract = _erc20Contract;\n invariant = _tokenAmount;\n decimals = 18;\n feePercentage = 3;\n feesCollected = 0;\n}\n\n\n //auxiliaries\n function getValue() internal view returns (uint256) {\n return msg.value;\n }\n\n \n /\n * #notice calculate Ether Amount \n * #dev Throw if '_tokenAmount' is zero\n * #param _tokenAmount Amount of tokens to be exchanged\n */\n function calculateEtherAmount(uint256 _tokenAmount) external override returns (uint256)\n { \n require(_tokenAmount > 0, "Invalid _tokenAmount value");\n uint256 etherAmount = (_tokenAmount * 1 ether) / invariant;\n return etherAmount; \n }\n\n /\n * #notice returns the quantity of tokens that can be obtained for an ether at the moment of the query\n */\n function getExchangeRate() external override returns (uint256) {\n uint256 tokenAmount = (1 ether * invariant) / 1 ether;\n return tokenAmount;\n }\n\n /\n * #notice Buy tokens\n * #dev Throw if '_amountToBuy' is zero \n * #dev Thorw if 'msg.value' is less than '_amountToBuy'\n * #param _amountToBuy Amount of tokens to be bought\n */\n function buyToken(uint256 _amountToBuy) external override returns (uint256)\n {\n require(_amountToBuy > 0, "Invalid _amountToBuy value");\n require(getValue()>=this.calculateEtherAmount(_amountToBuy),"Insufficient ethers");\n uint256 fee = (_amountToBuy * feePercentage) / 100;\n uint256 amountToTransfer = _amountToBuy - fee;\n feesCollected += fee;\n invariant += amountToTransfer;\n IERC20(erc20Contract).transfer(msg.sender, amountToTransfer);\n payable(msg.sender).transfer(getValue()-this.calculateEtherAmount(_amountToBuy));\n return amountToTransfer;\n }\n\n /\n * #notice Buy ethers\n * #dev Throw if '_amountToExchange' is zero\n * #param _amountToExchange Amount of tokens to be exchanged\n */\n function buyEther(uint256 _amountToExchange) external override returns (uint256)\n {\n require(_amountToExchange > 0, "Invalid _amountToExchage value");\n require(IERC20(erc20Contract).getBalance(msg.sender) >=_amountToExchange,"Insufficient balance");\n uint256 fee = (_amountToExchange * feePercentage) / 100;\n uint256 amountToTransfer = _amountToExchange - fee;\n IERC20(erc20Contract).approve(msg.sender, amountToTransfer);\n IERC20(erc20Contract).transferFrom(msg.sender,tokenVault,amountToTransfer);\n feesCollected += fee;\n invariant -= amountToTransfer;\n\n payable(msg.sender).transfer(this.calculateEtherAmount(amountToTransfer));\n return amountToTransfer;\n }\n\n /\n * #notice Set fee percentage\n * #dev Throw if '_percentage' is zero\n * #dev Throw if 'msg.sender' is not the owner\n * #param _percentage Percentage of fee\n */\n function setFeePercentage(uint256 _percentage) external override returns (uint256)\n {\n require(_percentage > 0, "Invalid _percentage value");\n require(msg.sender == owner, "Not autorized");\n feePercentage = _percentage;\n return feePercentage;\n }\n\n /\n * #notice Deposit ethers\n * #dev Throw if 'msg.value' is zero\n * #dev Throw if 'msg.sender' is not the owner\n */\n\n function deposit() external override returns (uint256) {\n require(msg.sender == owner, "Not autorized");\n require (IERC20(erc20Contract).getBalance(msg.sender)!=0, "No ethers deposited");\n uint256 amountToTransfer = this.calculateEtherAmount(getValue());\n invariant += amountToTransfer;\n IERC20(erc20Contract).transfer(tokenVault, amountToTransfer);\n }\n\n\n /\n * #notice Set token vault\n * #dev Throw if '_tokenVault' is zero\n * #dev Throw if '_tokenVault' is a contract\n * #dev Throw if 'msg.sender' is not the owner\n * #dev Throw if '_tokenVault' has no balance\n * #dev Throw if '_tokenVault' has no authorization\n * #param _tokenVault Address of the token vault\n */\n function setTokenVault(address _tokenVault) external override returns (uint256)\n {\n require(_tokenVault != address(0), "Invalid address _tokenVault");\n require (_tokenVault.code.length==0, "_tokenVault cannot be a contract");\n require(msg.sender == owner, "Not autorized");\n require(IERC20(erc20Contract).getBalance(_tokenVault) > 0, "_tokenVault has no balance");\n tokenVault = _tokenVault;\n return 1;\n }\n\n /**\n * #notice Withdraw fees\n * #dev Throw if 'msg.sender' is not the owner\n * #dev Throw if 'feesCollected' is less than 0.5 ethers\n */\n function withdrawFeesAmount() external override returns (uint256) {\n require(msg.sender == owner, "Not autorized");\n require(feesCollected >= 0.5 ether, "Insufficient amount of fees");\n feesCollected = 0;\n payable(msg.sender).transfer(feesCollected);\n return feesCollected;\n }\n}\n","line":29,"range":[950,1015]},"message":{"value":{"type":"Buffer","data":[8,195,121,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,73,110,118,97,108,105,100,32,97,100,100,114,101,115,115,32,95,116,111,107,101,110,86,97,117,108,116,0,0,0,0,0]},"_selector":"08c379a0"},"isInvalidOpcodeError":false}],"data":"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b496e76616c69642061646472657373205f746f6b656e5661756c740000000000"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.6.8)
at Logger.makeError (node_modules/#ethersproject/logger/src.ts/index.ts:261:28)
at Logger.throwError (node_modules/#ethersproject/logger/src.ts/index.ts:273:20)
at checkError (node_modules/#ethersproject/providers/src.ts/json-rpc-provider.ts:78:20)
at EthersProviderWrapper. (node_modules/#ethersproject/providers/src.ts/json-rpc-provider.ts:603:20)
at step (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:48:23)
at Object.throw (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:29:53)
at rejected (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:21:65)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at runNextTicks (internal/process/task_queues.js:64:3)
at listOnTimeout (internal/timers.js:526:9)
the test code:
const { ethers } = require("hardhat");
const chai = require("chai");
const { solidity } = require( "ethereum-waffle");
const { ConstructorFragment } = require("ethers/lib/utils");
chai.use(solidity);
const { expect } = chai;
const EchangePath = "contracts/Exchange.sol:Exchange";
const confirmations_number = 1;
const zeroAddress = '0x0000000000000000000000000000000000000000';
let contractInstance;
// Constructor parameters
const decimals = 18 ;
const feePercentage = 1 ;
const owner = '0x0000000000000000000000000000000000000000';
const tokenVault = '0x0000000000000000000000000000000000000000';
const erc20Contract = '0x0000000000000000000000000000000000000000';
const invariant = 1 ;
const feesCollected = 0 ;
const tokenAmount = 10;
describe("Echange tests", () => {
beforeEach(async () => {
console.log("-----------------------------------------------------------------------------------");
console.log(" -- Exchange tests start");
console.log("-----------------------------------------------------------------------------------");
[signer, account1, account2, account3] = await ethers.getSigners();
provider = ethers.provider;
// Deploy Exchange contract
const Exchange = await ethers.getContractFactory(EchangePath, signer);
contractInstance = await Exchange.deploy(
tokenVault,
erc20Contract,
tokenAmount
);
});
describe("Constructor tests", () => {
it("Try send invalid tokenValue address", async () => {
const Exchange = await ethers.getContractFactory(EchangePath, signer);
await expect(
Exchange.deploy(
zeroAddress,
erc20Contract,
tokenAmount,
{ gas: 1000000, gasPrice: 1000000000,gasLimit:1000000 , from: owner}
)
).to.be.revertedWith("Invalid token address");
});
});
the exchange code:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./interface/IExchange.sol";
import "./interface/IERC20.sol";
contract Exchange is IExchange {
uint256 public decimals;
uint256 public feePercentage;
address public owner;
address public tokenVault;
address public erc20Contract;
uint256 public invariant;
uint256 public feesCollected;
constructor(address _tokenVault, address _erc20Contract,uint256 _tokenAmount ){
require(_tokenVault != address(0), "Invalid address _tokenVault");
require(_tokenVault.code.length==0, "_tokenVault cannot be a contract");
require(_erc20Contract != address(0), "_erc20Contract cannot be zero address");
require(_erc20Contract.code.length>0, "_erc20Contract is not a contract");
require(_tokenAmount > 0, "Invalid _tokenAmount value");
require(IERC20(_erc20Contract).getBalance(_tokenVault) >= _tokenAmount, "Insufficient tokens in the vault");
owner = msg.sender;
tokenVault = _tokenVault;
erc20Contract = _erc20Contract;
invariant = _tokenAmount;
decimals = 18;
feePercentage = 3;
feesCollected = 0;
}
I tried commenting the requires and it did not return the error.Also i tried deploy without the line :
{ gas: 1000000, gasPrice: 1000000000,gasLimit:1000000 , from: owner}

If you look closely the error is more like "Invalid address _tokenVault".
If you look into your tests, the error is coming from the beforeEach() under the comment
// Deploy Exchange contract
There you are trying to invoke the deploy method which is outside the test. Here is the updated test method.
describe("Echange tests", () => {
beforeEach(async () => {
console.log("-----------------------------------------------------------------------------------");
console.log(" -- Exchange tests start");
console.log("-----------------------------------------------------------------------------------");
[signer, account1, account2, account3] = await ethers.getSigners();
provider = ethers.provider;
});
describe("Constructor tests", () => {
it("Try send invalid tokenValue address", async () => {
const Exchange = await ethers.getContractFactory(EchangePath, signer);
const transactionPromise = Exchange.deploy(
zeroAddress,
erc20Contract,
tokenAmount
)
await expect(
transactionPromise
).to.be.revertedWith("Invalid address _tokenVault");
});
})
});

Related

Error: execution reverted: SafeERC20: low-level call failed with AAVE

Don't know if here is the best place but i'm in despair. I have this flashloan code:
pragma solidity ^0.6.6;
import "#aave/protocol-v2/contracts/flashloan/base/FlashLoanReceiverBase.sol";
import "#aave/protocol-v2/contracts/interfaces/ILendingPoolAddressesProvider.sol";
import "#aave/protocol-v2/contracts/interfaces/ILendingPool.sol";
import "#uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {SafeERC20} from "#aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol";
//import "#openzeppelin/contracts/interfaces/IERC20.sol";
contract FlashloanV2 is FlashLoanReceiverBase {
using SafeERC20 for IERC20;
constructor(
ILendingPoolAddressesProvider _addressProvider,
address _routerA,
address _routerB,
address _token,
address _WETH
) public FlashLoanReceiverBase(_addressProvider)
{
owner = msg.sender;
routerA = _routerA;
routerB = _routerB;
token = _token;
WETH = _WETH;
}
address owner;
address routerA;
address routerB;
address token;
address WETH;
modifier onlyOwner{
require(msg.sender == owner, "Hey hey hey you can't use this function");
_;
}
/**
* #dev This function must be called only be the LENDING_POOL and takes care of repaying
* active debt positions, migrating collateral and incurring new V2 debt token debt.
*
* #param assets The array of flash loaned assets used to repay debts.
* #param amounts The array of flash loaned asset amounts used to repay debts.
* #param premiums The array of premiums incurred as additional debts.
* #param initiator The address that initiated the flash loan, unused.
* #param params The byte array containing, in this case, the arrays of aTokens and aTokenAmounts.
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external override returns (bool) {
//
// This contract now has the funds requested.
// Your logic goes here.
//
address[] memory path = new address[](2);
path[0] = token;
path[1] = WETH;
address[] memory path2 = new address[](2);
path2[0] = WETH;
path2[1] = token;
uint balance = address(this).balance;
IERC20(WETH).approve(routerA, balance);
IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
balance,
0,
path2,
address(this),
block.timestamp + 1200
);
uint tokenBalance = IERC20(token).balanceOf(address(this));
IERC20(token).approve(routerB, tokenBalance);
IUniswapV2Router02(routerB).swapExactTokensForTokensSupportingFeeOnTransferTokens(
tokenBalance,
0,
path,
address(this),
block.timestamp + 1200
);
//payable(owner).transfer(address(this).balance - (amounts[0] + premiums[0]));
// At the end of your logic above, this contract owes
// the flashloaned amounts + premiums.
// Therefore ensure your contract has enough to repay
// these amounts.
// Approve the LendingPool contract allowance to *pull* the owed amount
for (uint256 i = 0; i < assets.length; i++) {
uint256 amountOwing = amounts[i].add(premiums[i]);
IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing);
}
return true;
}
function _flashloan(address[] memory assets, uint256[] memory amounts)
internal
{
address receiverAddress = address(this);
address onBehalfOf = address(this);
bytes memory params = "";
uint16 referralCode = 0;
uint256[] memory modes = new uint256[](assets.length);
// 0 = no debt (flash), 1 = stable, 2 = variable
for (uint256 i = 0; i < assets.length; i++) {
modes[i] = 0;
}
LENDING_POOL.flashLoan(
receiverAddress,
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
}
/*
* Flash multiple assets
*/
function flashloan(address[] memory assets, uint256[] memory amounts)
public
onlyOwner
{
_flashloan(assets, amounts);
}
/*
* Flash loan 100000000000000000 wei (0.1 ether) worth of `_asset`
*/
function flashloan(address _asset) public onlyOwner {
bytes memory data = "";
uint256 amount = 50 ether;
address[] memory assets = new address[](1);
assets[0] = _asset;
uint256[] memory amounts = new uint256[](1);
amounts[0] = amount;
_flashloan(assets, amounts);
}
event LogWithdraw(
address indexed _from,
address indexed _assetAddress,
uint amount
);
/**
* #dev Withdraw asset.
* #param _assetAddress Asset to be withdrawn.
*/
function withdraw(address _assetAddress) public onlyOwner {
uint assetBalance;
if (_assetAddress == WETH) {
address self = address(this); // workaround for a possible solidity bug
assetBalance = self.balance;
payable(msg.sender).transfer(assetBalance);
} else {
assetBalance = IERC20(_assetAddress).balanceOf(address(this));
IERC20(_assetAddress).safeTransfer(msg.sender, assetBalance);
}
emit LogWithdraw(msg.sender, _assetAddress, assetBalance);
}
function setter(address _routerA, address _routerB, address _token) external onlyOwner returns(bool){
routerA = _routerA;
routerB = _routerB;
token = _token;
return true;
}
function returnOwner() external view returns(address){
return owner;
}
function returnToken() external view returns(address){
return token;
}
function returnWETH() external view returns(address){
return WETH;
}
fallback() external payable {}
}
I'm receiving SafeERC20: low-level call failed when calling flashloan(). There's something wrong with my approving logic or something else?
The contract is funded with enough to pay the fees. Could someone give a hint? Thank you!

Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'

Many thanks in advance for your time and help
I want to run flashloan between uniswap and sushiswap. But, I get this error and it persists, despite trying several potential solutions mentioned by answers to similar problem.
I wrote it with hardhat via vscode.
here is the error:
Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'
Here is the solidity code:
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
// Uniswap interface and library imports
import "./libraries/UniswapV2Library.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IUniswapV2Router02.sol";
import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IUniswapV2Factory.sol";
import "./interfaces/IERC20.sol";
contract FlashloanSwap {
using SafeERC20 for IERC20;
// Define the factory and router addresses of the DEXs
address private constant UNISWAP_FACTORY =
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant UNISWAP_ROUTER =
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address private constant SUSHISWAP_FACTORY =
0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
address private constant SUSHISWAP_ROUTER =
0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
// Trade variables
uint256 private deadline = block.timestamp + 1 days;
// Get the contract balance of token
function getBalanceOfToken(address _address) public view returns (uint256) {
return IERC20(_address).balanceOf(address(this));
}
function checkProfitability(uint256 _input, uint256 _outPut)
private
pure
returns (bool)
{
return _outPut > _input;
}
// There must be a function to receive ETH
receive() external payable {}
fallback() external payable {}
// Eth balance needs to be checked sometimes
function getBalance() public view returns (uint256) {
return address(this).balance;
}
// Define a function to trade a given token in exchange for another token on a given DEX
function executeTrade(
address _fromToken,
address _toToken,
uint256 _amountIn,
address factory,
address router
) private returns (uint256) {
address pair = IUniswapV2Factory(factory).getPair(_fromToken, _toToken);
require(pair != address(0), "Pool does not exist");
// Calculate amount out
address[] memory path = new address[](2);
path[0] = _fromToken;
path[1] = _toToken;
uint256 amountRequired = IUniswapV2Router01(router).getAmountsOut(
_amountIn,
path
)[1];
console.log("Amount Required: ", amountRequired);
// Perform arbitrage - Swap for another token
IUniswapV2Router02(router)
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
amountRequired,
path,
address(this),
deadline
);
uint256 balanceA = getBalanceOfToken(_fromToken);
uint256 balanceB = getBalanceOfToken(_toToken);
uint256 amountReceived = balanceA == 0 ? balanceB : balanceA;
console.log("amountReceived: ", amountReceived);
require(amountReceived > 0, "Aborted Transaction");
return amountReceived;
}
/*
I) It will be run whenever an arbitrage opportunity is detected
*/
function runTheArbitrage(
address tokenA,
address tokenB,
uint256 amountA,
uint256 amountB
) external {
// Get the pair address on uniswap
address pairAddress = IUniswapV2Factory(UNISWAP_FACTORY).getPair(
tokenA,
tokenB
);
// Check whether the pair exists
require(
pairAddress != address(0),
"The pair does not exist on uniswap"
);
// Save the borrowed token's specifications in _data to be passed to uniswapV2Call
address borrowedTokenAddress = amountA == 0 ? tokenB : tokenA;
uint256 borrowedTokenAmount = amountA == 0 ? amountB : amountA;
bytes memory data = abi.encode(
borrowedTokenAddress,
borrowedTokenAmount
);
// Create the flashloan with the swap function
IUniswapV2Pair(pairAddress).swap(amountA, amountB, address(this), data);
}
/*
II) With executing the previous function, uniswap will call this function in order to complete the flashloan cycle
*/
function uniswapV2Call(
address _sender,
uint256 _amountA,
uint256 _amountB,
bytes calldata _data
) external {
// get the specifications of the borrowed token
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
(address borrowedTokenAddress, uint256 borrowedTokenAmount) = abi
.decode(_data, (address, uint256));
token0 = token0 == borrowedTokenAddress ? token0 : token1;
token1 = token0 == borrowedTokenAddress ? token1 : token0;
// Check whether this function is called only by the pair contracts of uniswap
require(
msg.sender ==
UniswapV2Library.pairFor(UNISWAP_FACTORY, token0, token1),
"Only requests from uniswap pair contracts are accepted"
);
// Check whether this contract is the sender
require(_sender == address(this), "Sender should match this contract");
// Check one of the amounts to be zero
require(
_amountA == 0 || _amountB == 0,
"One of the amounts must be zero"
);
// Execute the first swap on source DEX
IERC20(token0).safeIncreaseAllowance(
UNISWAP_ROUTER,
borrowedTokenAmount
);
uint256 firstAmountOut = executeTrade(
token0,
token1,
borrowedTokenAmount,
UNISWAP_FACTORY,
UNISWAP_ROUTER
);
// Aprove the second DEX to spend the swapped token, then execute the trade on it
IERC20(token1).safeIncreaseAllowance(SUSHISWAP_ROUTER, firstAmountOut);
uint256 secondAmountOut = executeTrade(
token1,
token0,
firstAmountOut,
SUSHISWAP_FACTORY,
SUSHISWAP_ROUTER
);
uint256 fee = ((borrowedTokenAmount * 3) / 997) + 1;
uint256 amountToBePaidBack = borrowedTokenAmount + fee;
// Check profitability
bool profCheck = checkProfitability(
amountToBePaidBack,
secondAmountOut
);
require(profCheck, "Arbitrage not profitable");
// Pay back the loan
bool success1 = IERC20(token0).transfer(msg.sender, amountToBePaidBack);
// Send the profit to the initiator of the transaction
bool success2 = IERC20(token0).transfer(
tx.origin,
secondAmountOut - amountToBePaidBack
);
console.log(secondAmountOut - amountToBePaidBack, success2);
}
}
Also here is the hardhat.config.js file for the hardhat test
const { version } = require("chai");
require("#nomiclabs/hardhat-waffle");
require('dotenv').config();
module.exports = {
solidity: {
compilers: [
{version: '0.5.5'},
{version: '0.6.6'},
{version: '0.8.8'},
],
},
networks: {
hardhat: {
forking: {
url: process.env.alchemy_mainnet_key,
},
},
testnet: {
url: process.env.alchemy_renkiby_api,
chainId: 97,
accounts: [
process.enc.test_private_key
],
},
mainnet: {
url: process.env.alchemy_mainnet_key,
chainId: 56,
accounts: [
process.env.private_key
],
},
},
};
And finally, here is my test code
const { expect } = require("chai");
const { ethers, waffle } = require("hardhat");
const { deployContract } = require("ethereum-waffle");
const provider = waffle.provider;
const { abi } = require('../artifacts/contracts/interfaces/IERC20.sol/IERC20.json');
describe("Checking the whole arbitrage process", function () {
// Get the factory and router addresses
const UNISWAP_FACTORY = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
const UNISWAP_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D";
const SUSHI_FACTORY = "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac";
const SUSHI_ROUTER = "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F";
// Token addresses
const tokenA = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const tokenB = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942";
// Get the decimals
const decimals = 18;
beforeEach(async function () {
// Get owner as signer
[owner] = await ethers.getSigners();
// Deploy smart contract
const FlashloanSwap = await ethers.getContractFactory("FlashloanSwap");
flashloanSwap = await FlashloanSwap.deploy();
await flashloanSwap.deployed();
console.log('\n', "Contract is deployed by: ", owner.address);
console.log("contract is deployed to: ", flashloanSwap.address, '\n');
const transactionHash = await owner.sendTransaction({
to: flashloanSwap.address,
value: ethers.utils.parseEther("1.0"),
});
console.log("transactionHash : ", transactionHash);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatUnits(balanceOfEth, 18);
console.log('\n', "Balance of ETH before transaction : ", balanceOfEth.toString(), '\n');
});
it("Check Whether Swap Occurs or Not", async () => {
await flashloanSwap.runTheArbitrage(tokenA, tokenB, 0, 100);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatEther(balanceOfEth, 18);
console.log('\n', "Balance of ETH after transaction : ", balanceOfEth, '\n');
});
When you're trying to pay back the loan + fees, you transfer the amount for each asset from your FlashloanSwap to Uniswap. Could the exception you're getting be thrown because you have to allow Uniswap to pull the loan + fees from your account, i.e. you call approve on uniswap and uniswap calls transferFrom on your account?
Try replacing:
IERC20(token0).transfer(msg.sender, amountToBePaidBack);`
with:
IERC20(token0).approve(msg.sender, amountToBePaidBack);
100% sure that it's an allowance issue, you didn't approved the tokens like:
IERC20(WETH).approve(routerA, amount);
IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
amount,
0,
path2,
cttAddress,
block.timestamp + 1200
);

'Gas Estimation Failed' Error when trying to deploy a contract on remix to the Ganache test net

This is what the error reads:
Gas estimation errored with the following message (see below). The
transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "message": "Returned error: project ID does
not have access to archive state", "code": -32000, "data": { "stack":
"Error: Returned error: project ID does not have access to archive
state\n at Object.ErrorResponse
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2110625)\n
at a
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2108932)\n
at
/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2093154\n
at runMicrotasks ()\n at processTicksAndRejections
(internal/process/task_queues.js:95:5)", "name": "Error" } }
I have made the settings for deployment on remix:
Injected Web 3
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
interface Structs {
struct Val {
uint256 value;
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination {
Wei // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
contract DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) public view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) public;
}
/**
* #dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
mapping(address => uint256) public currencies;
constructor() public {
currencies[WETH] = 1;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(address token, uint256 amount, bytes memory data)
internal
{
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
contract Flashloan is DyDxFlashLoan {
uint256 public loan;
constructor() public payable {
(bool success, ) = WETH.call.value(msg.value)("");
require(success, "fail to get weth");
}
function getFlashloan(address flashToken, uint256 flashAmount) external {
uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore);
flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`
}
function callFunction(
address, /* sender */
Info calldata, /* accountInfo */
bytes calldata data
) external onlyPool {
(address flashToken, uint256 flashAmount, uint256 balanceBefore) = abi
.decode(data, (address, uint256, uint256));
uint256 balanceAfter = IERC20(flashToken).balanceOf(address(this));
require(
balanceAfter - balanceBefore == flashAmount,
"contract did not get the loan"
);
loan = balanceAfter;
// Use the money here!
}
}

Swap ETH to DAI using ISwapRouter from Uniswap

I am trying to get started with Uniswap V3. As an example, I took the most basic use case: Given X amount of ETH do a swap to DAI. Unfortunately, I am not being able to make it work.
There is already a very similar question (with no answer) but slightly different as the code doesn't look like mine.
I am using Hardhat to fork the main-net and then I connect Remix to localhost:8545
npx hardhat node --fork https://mainnet.infura.io/v3/{MY_API_KEY}
Hardhat config down below:
solidity: {
compilers: [
{
version: "0.8.7",
settings: {
optimizer: {
enabled: true,
runs: 1000,
}
}
}
]
}
As you can notice (full contract at the very bottom), the contract offers 3 payable functions:
function convertExactEthToDai() external payable;
function convertEthToExactDai(uint256 daiAmount) external payable;
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256);
All of them fail, even getEstimatedETHforDAI, which is fairly simple and readonly (almost). There is no given reason, so I am blind. When I execute the function from Remix, I just get a generic error "Returned error: Error: Transaction reverted without a reason string".
When I look at hardhat console, I see this error:
eth_sendTransaction
Contract call: <UnrecognizedContract>
Transaction: 0xe17fd5a07ca4a0d5a0f91525a77d21152c41f4dc2a9e59feaac4fec7452ba3a1
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xc351628eb244ec633d5f21fbd6621e1a683b1181
Value: 0 ETH
Gas used: 52351 of 3000000
Block #13238807: 0x7471674d8b76462230d687644e20970137640a7b2fe005c264a04c2af35d4985
Error: Transaction reverted without a reason string
at <UnrecognizedContract>.<unknown> (0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6)
at <UnrecognizedContract>.<unknown> (0xc351628eb244ec633d5f21fbd6621e1a683b1181)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at HardhatNode._mineBlockWithPendingTxs (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1582:23)
at HardhatNode.mineBlock (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:435:16)
at EthModule._sendTransactionAndReturnHash (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1494:18)
at HardhatNetworkProvider._sendWithLogging (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:129:22)
at HardhatNetworkProvider.request (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:106:18)
Looks like the contract is invalid, however I can in EtherScan the Quoter and the Router
Any idea? I really appreciate it.
Here is the full contract
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.0;
pragma abicoder v2;
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol";
interface IUniswapRouter is ISwapRouter {
function refundETH() external payable;
}
contract Uniswap3 {
IUniswapRouter public constant uniswapRouter = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IQuoter public constant quoter = IQuoter(0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6);
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant WETH9 = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
function convertExactEthToDai() external payable {
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15;
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountIn = msg.value;
uint256 amountOutMinimum = 1;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountIn,
amountOutMinimum,
sqrtPriceLimitX96
);
uniswapRouter.exactInputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
function convertEthToExactDai(uint256 daiAmount) external payable {
require(daiAmount > 0, "Must pass non 0 DAI amount");
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15; // using 'now' for convenience, for mainnet pass deadline from frontend!
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountOut = daiAmount;
uint256 amountInMaximum = msg.value;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountOut,
amountInMaximum,
sqrtPriceLimitX96
);
uniswapRouter.exactOutputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
// do not used on-chain, gas inefficient!
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256) {
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 10000;
uint160 sqrtPriceLimitX96 = 0;
return quoter.quoteExactOutputSingle(
tokenIn,
tokenOut,
fee,
daiAmount,
sqrtPriceLimitX96
);
}
// important to receive ETH
receive() payable external {}
}

Type error: Member not found or visible in unit256

I have this crowdfunding platform that was initially using SafeMath library. And since is deprecated I'm looking for a workaround to make it compile and I'm getting a compilation error with remix in line 131:
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
Here you have the whole contract code, it's a platform where you can create projects and raise funds:
// We will be using Solidity version 0.6.0
pragma solidity 0.6.0;
// Importing OpenZeppelin's SafeMath Implementation
//import 'https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/math/SafeMath.sol';
//import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/mocks/SafeMathMock.sol";
//import "#openzeppelin/contracts/utils/math/SafeMath.sol";
contract Crowdfunding {
// using SafeMath for uint256;
// List of existing projects
Project[] private projects;
// Event that will be emitted whenever a new project is started
event ProjectStarted(
address contractAddress,
address projectStarter,
string projectTitle,
string projectDesc,
uint deadline,
uint goalAmount
);
/** #dev Function to start a new project.
* #param title Title of the project to be created
* #param description Brief description about the project
* #param durationInDays Project deadline in days
* #param amountToRaise Project goal in wei
*/
function startProject(
string calldata title,
string calldata description,
uint durationInDays,
uint amountToRaise
) external {
// uint raiseUntil = now.add(durationInDays.mul(1 days));
uint durationInSeconds = durationInDays * 1 days;
// Check that the result of division (inverse operation to multiplication) is the original number.
// If it's not, throw an exception, because the multiplication overflowed.
require(durationInSeconds / durationInDays == 1 days, 'Multiplication overflow');
uint raiseUntil = block.timestamp + durationInSeconds;
// Check that the result of subtraction (inverse operation to addition) is the original number.
// If it's not, throw an exception, because the addition overflowed.
require(raiseUntil - block.timestamp == durationInSeconds, 'Addition overflow');
Project newProject = new Project(msg.sender, title, description, raiseUntil, amountToRaise);
projects.push(newProject);
emit ProjectStarted(
address(newProject),
msg.sender,
title,
description,
raiseUntil,
amountToRaise
);
}
/** #dev Function to get all projects' contract addresses.
* #return A list of all projects' contract addreses
*/
function returnAllProjects() external view returns(Project[] memory){
return projects;
}
}
contract Project {
// not using SafeMath for uint256;
// Data structures
enum State {
Fundraising,
Expired,
Successful
}
// State variables
address payable public creator;
uint public amountGoal; // required to reach at least this much, else everyone gets refund
uint public completeAt;
uint256 public currentBalance;
uint public raiseBy;
string public title;
string public description;
State public state = State.Fundraising; // initialize on create
mapping (address => uint) public contributions;
// Event that will be emitted whenever funding will be received
event FundingReceived(address contributor, uint amount, uint currentTotal);
// Event that will be emitted whenever the project starter has received the funds
event CreatorPaid(address recipient);
// Modifier to check current state
modifier inState(State _state) {
require(state == _state);
_;
}
// Modifier to check if the function caller is the project creator
modifier isCreator() {
require(msg.sender == creator);
_;
}
constructor
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint fundRaisingDeadline,
uint goalAmount
) public {
creator = projectStarter;
title = projectTitle;
description = projectDesc;
amountGoal = goalAmount;
raiseBy = fundRaisingDeadline;
currentBalance = 0;
}
/** #dev Function to fund a certain project.
*/
function contribute() external inState(State.Fundraising) payable {
require(msg.sender != creator);
contributions[msg.sender] = contributions[msg.sender].add(msg.value);
currentBalance = currentBalance.add(msg.value);
emit FundingReceived(msg.sender, msg.value, currentBalance);
checkIfFundingCompleteOrExpired();
}
/** #dev Function to change the project state depending on conditions.
*/
function checkIfFundingCompleteOrExpired() public {
if (currentBalance >= amountGoal) {
state = State.Successful;
payOut();
} else if (now > raiseBy) {
state = State.Expired;
}
completeAt = now;
}
/** #dev Function to give the received funds to project starter.
*/
function payOut() internal inState(State.Successful) returns (bool) {
uint256 totalRaised = currentBalance;
currentBalance = 0;
if (creator.send(totalRaised)) {
emit CreatorPaid(creator);
return true;
} else {
currentBalance = totalRaised;
state = State.Successful;
}
return false;
}
/** #dev Function to retrieve donated amount when a project expires.
*/
function getRefund() public inState(State.Expired) returns (bool) {
require(contributions[msg.sender] > 0);
uint amountToRefund = contributions[msg.sender];
contributions[msg.sender] = 0;
if (!msg.sender.send(amountToRefund)) {
contributions[msg.sender] = amountToRefund;
return false;
} else {
currentBalance = currentBalance.sub(amountToRefund);
}
return true;
}
/** #dev Function to get specific information about the project.
* #return Returns all the project's details
*/
function getDetails() public view returns
(
address payable projectStarter,
string memory projectTitle,
string memory projectDesc,
uint256 deadline,
State currentState,
uint256 currentAmount,
uint256 goalAmount
) {
projectStarter = creator;
projectTitle = title;
projectDesc = description;
deadline = raiseBy;
currentState = state;
currentAmount = currentBalance;
goalAmount = amountGoal;
}
}
The Using X for Y expression extends the Y datatype with functions of the X library.
In case of using SafeMath for uint256, it allows to use the functions defined in SafeMath (such as add()) on uint256 variables.
Specifically, function add() in SafeMath checks whether the addition would overflow the 256bit unsigned integer. If it did overflow, it throws an exception. If it didn't overflow, it returns the result of the addition.
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
You can achieve the same result with this snippet:
uint256 contribution = contributions[msg.sender] + msg.value; // c = a + b
require(contribution >= contributions[msg.sender]); // require(c >= a)
contributions[msg.sender] = contribution; // return c
If you switch to Solidity 0.8+, this is not needed, because since version 0.8.0, Solidity performs the overflow check automatically and reverts if an overflow/underflow would occur. So it would be sufficient to use just this snippet:
// safe in Solidity 0.8+, unsafe in older versions
contributions[msg.sender] += msg.value;
Arithmetic operations revert on underflow and overflow. You can use unchecked { ... } to use the previous wrapping behaviour.
Source: docs