Reusable smart contract - ethereum

I'm currently trying to develop a simple smart contract
Being relatively new to this, can multiple pairs of users interact with the contract at the same time and benefit from the escrow (will it instantiate a new version for every pair) ?
It's essentially an escrow contract that will hold the funds from a transaction until both the buyer and the seller have accepted the release. Additionally, if the either one of them does not accept the funds will be withheld by the smart contract for a duration of 30 days.
Also, how can I add the address that initially deployed the smart and transfer 3% of the total deposit to that address if the transaction was successful ?
This is what I have tried for now:
pragma solidity 0.7.0;
contract NewEscrow {
enum State {AWAITING_FUNDS,AWAITING_CLAIM,CLAIM,COMPLETE}
State public currentState;
address payable public buyer;
address payable public seller;
address payable public owner;
uint256 public agreementDay;
mapping (address => uint256) deposits;
// checks if msg.sender is equal to buyer
modifier onlyBuyer (){
require(msg.sender == buyer);
_;
}
// checks if msg.sender is equal to seller
modifier onlySeller(){
require(msg.sender == seller);
_;
}
constructor (){
owner = msg.sender;
}
function setVariables (address payable _buyer, address payable _seller, uint256 _agreementDay) public {
buyer = _buyer;
seller = _seller;
agreementDay = _agreementDay + 30 days;
currentState = State.AWAITING_FUNDS;
}
function deposit() public onlyBuyer payable {
require(currentState == State.AWAITING_FUNDS);
uint256 amount = msg.value;
deposits[seller] = deposits[seller] + amount;
currentState = State.AWAITING_CLAIM;
}
function claim () public onlySeller {
require(currentState == State.AWAITING_CLAIM);
currentState = State.CLAIM;
}
function confirm () public onlyBuyer {
uint256 payment = deposits[seller];
deposits[seller] = 0;
seller.transfer(payment);
currentState = State.COMPLETE;
}
function cancelPayement () public onlySeller {
uint256 payment = deposits[seller];
deposits[seller] = 0;
buyer.transfer(payment);
currentState = State.COMPLETE;
}
function release() public{
// funds cannot be retrieved before release day
require (agreementDay < block.timestamp);
uint256 payment = deposits[seller];
deposits[seller] = 0;
buyer.transfer(payment);
revert('funds returned');
}
}

can multiple pairs of users interact with the contract at the same time and benefit from the escrow
Currently not. Only the first pair of users would be able to use it, as currently buyer and seller variables can only hold one value each.
If you want to scale for multiple pairs, you need to make an array of structs representing the buyer and seller connection. From the top of my head, it would look like:
struct Pair {
address buyer;
address seller;
}
Pair[] pairs;
Or another approach, where a common index of the arrays shows that the buyer and seller are connected.
address[] buyers;
address[] sellers;
This scaling would also mean expanding most of the current logic to validate whether the input buyer and seller are connected.
if the either one of them does not accept the funds will be withheld by the smart contract for a duration of 30 days
You'll need to create a new function that checks whether the deposit has been withdrawn and whether it's (deposit date + 30 days) and some validation who can actually withdraw this money.
address constant contractOwner = '0x123'
function withdrawOwner() external {
require(msg.sender == contractOwner); // validate who can withdraw
require(agreementDay + 30 days <= block.timestamp); // check if 30 days has passed since the deposit date
require(deposits[seller] > 0); // check that it hasn't been withdrawn
uint256 amount = deposits[seller]; // make sure the contract is not vulnerable to reentrancy
deposits[seller] = 0;
payable(contractOwner).transfer(amount); // withdraw the money
}
how can I add the address that initially deployed the smart and transfer 3% of the total deposit
Let's expand the 2nd point and use the contractOwner. You'll need to update the deposit function:
function deposit() public onlyBuyer payable {
require(currentState == State.AWAITING_FUNDS);
uint256 amount = msg.value;
// these lines calculate the fee, update the amount and send the fee to the contract owner
// make sure you're not vulnerable to overflow
uint256 fee = (amount / 100) * 3;
payable(contractOwner).transfer(fee); // transfer 3% to the contract owner
amount -= fee; // substract the fee from the amount that is going to be saved
deposits[seller] = deposits[seller] + amount;
currentState = State.AWAITING_CLAIM;
}
Make sure you're not vulnerable to integer overflow. Imagine a scenario:
Buyer deposits 1 wei
Fee is calculated as 3 wei
The contract has enough ETH so it sends the 3 wei to the owner
But the 3 wei is substracted from the 1 in amount so that results in 2^256 - 3 instead of -2
Solidity 0.8+ reverts the transaction automatically if underflow/overflow would happen. If you're using older version of Solidity, you can use for example OpenZeppelin's SafeMath library.

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

enable 2 users to use my contract at the same time with different states in Solidity

I'm new in solidity, and I was trying to create a simple purchase contract between 2 users, with different states as below:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract purchase {
uint public value;
struct Users {
address payable seller;
address payable buyer;
}
Users public users;
uint256 public contractID;
enum State{Created, Locked, Release, Inactive}
State public state;
constructor() payable {
users.seller = payable(msg.sender);
value = msg.value / 2;
}
///The function can't be called at the current state.
error InvalidState();
///Only buyer/buyer can call this function
error Onlybuyer();
///Only Seller can call this function
error OnlySeller();
modifier inState(State state_){
if (state != state_) {
revert InvalidState();
}
_;
}
modifier onlybuyer(){
if (msg.sender != users.buyer) {
revert Onlybuyer();
}
_;
}
modifier onlySeller(){
if (msg.sender != users.seller) {
revert OnlySeller();
}
_;
}
mapping(uint => Users) public contractUsers;
mapping(uint => State) public contractState;
function confirmPurchase() external inState(State.Created) payable {
contractID ++;
require(msg.value == (2*value), "Please send in 2X the purchase amount");
users.buyer = payable(msg.sender);
state = State.Locked;
}
function confirmRecieved() external onlybuyer inState(State.Locked) {
state = State.Release;
users.buyer.transfer(value);
}
function paySeller() external onlySeller inState(State.Release){
state = State.Inactive;
users.seller.transfer(3 * value);
}
function abort() external onlySeller inState(State.Created){
state = State.Inactive;
users.seller.transfer(address(this).balance);
}
}
I would like to ask, how I can enable 2 sellers to use the contract at the same time with different states? or how I enable many users to use my contract at the same time?
for example:let's say we have seller 1, buyer 1 with price 2 ETH at state 1.
at the same time seller 2, buyer 2 with price 3 ETH at state 0.
each one of them is using the contract at the same time and they can view their contract details using the contract ID. how I can do that?
I thought about creating a function to set the state to initial state 0,then a new user can use the contract, and retrieve their data from the mapping using contract ID. but I'm not sure if this is the best practice. Can anyone advice please!
Thank you in advance.

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.

How to transfer a NFT from one account to another using ERC721?

I'm writing an NFT smart contract using the OpenZeppelin ERC721Full contract. I'm able to mint NFTs, but I want to have a button that enables them to be bought. I'm trying writing this function:
function buyNFT(uint _id) public payable{
//Get NFT owner address
address payable _seller = ownerOf(_id);
// aprove nft sell
approve(_seller, _id);
setApprovalForAll(msg.sender, true);
//transfer NFT
transferFrom(_seller, msg.sender, _id);
// transfer price in ETH
address(_seller).transfer(msg.value);
emit NftBought(_seller, msg.sender, msg.value);
}
This does not work because function approve must be called by the owner or an already approved address. I have no clue on how a buy function should be built. I know that I must use some requirements but first I want the function to work on tests and then I'll write the requirements.
How should a buy function be coded? Because the only solution I have found is to overwrite the approve function and omit the require of who can call this function. But it looks like it isn't the way it should be done.
Thank you!
You can use just the _transfer() function, see my buy() function for an example of implementation.
The approvals for sale can be done using a custom mapping - in my example tokenIdToPrice. If the value is non-zero, the token ID (mapping key) is for sale.
This is a basic code that allows selling an NTF. Feel free to expand on my code to allow "give away for free", "whitelist buyers" or any other feature.
pragma solidity ^0.8.4;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol';
contract MyToken is ERC721 {
event NftBought(address _seller, address _buyer, uint256 _price);
mapping (uint256 => uint256) public tokenIdToPrice;
constructor() ERC721('MyToken', 'MyT') {
_mint(msg.sender, 1);
}
function allowBuy(uint256 _tokenId, uint256 _price) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
require(_price > 0, 'Price zero');
tokenIdToPrice[_tokenId] = _price;
}
function disallowBuy(uint256 _tokenId) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = 0;
}
function buy(uint256 _tokenId) external payable {
uint256 price = tokenIdToPrice[_tokenId];
require(price > 0, 'This token is not for sale');
require(msg.value == price, 'Incorrect value');
address seller = ownerOf(_tokenId);
_transfer(seller, msg.sender, _tokenId);
tokenIdToPrice[_tokenId] = 0; // not for sale anymore
payable(seller).transfer(msg.value); // send the ETH to the seller
emit NftBought(seller, msg.sender, msg.value);
}
}
How to simulate the sale:
The contract deployer (msg.sender) gets token ID 1.
Execute allowBuy(1, 2) that will allow anyone to buy token ID 1 for 2 wei.
From a second address, execute buy(1) sending along 2 wei, to buy the token ID 1.
Call (the parent ERC721) function ownerOf(1) to validate that the owner is now the second address.
If you let anyone call the approve function, it would allow anyone to approve themselves to take NFTs! The purpose of approve is to give the owner of an asset the ability to give someone else permission to transfer that asset as if it was theirs.
The basic premise of any sale is that you want to make sure that you get paid, and that the buyer receives the goods in return for the sale. Petr Hedja's solution takes care of this by having the buy function not only transfer the NFT, but also include the logic for sending the price of the token. I'd like to recommend a similar structure with a few changes. One is so that the function will also work with ERC20 tokens, the other is to prevent an edge case where if gas runs out during execution, the buyer could end up with their NFT for free. This is building on his answer, though, and freely uses some of the code in that answer for architecture.
Ether can still be set as the accepted currency by inputting the zero address (address(0)) as the contract address of the token.
If the sale is in an ERC20 token, the buyer will need to approve the NFT contract to spend the amount of the sale since the contract will be pulling the funds from the buyer's account directly.
pragma solidity ^0.8.4;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';
contract MyToken is ERC721 {
event NftBought(address _seller, address _buyer, uint256 _price);
mapping (uint256 => uint256) public tokenIdToPrice;
mapping (uint256 => address) public tokenIdToTokenAddress;
constructor() ERC721('MyToken', 'MyT') {
_mint(msg.sender, 1);
}
function setPrice(uint256 _tokenId, uint256 _price, address _tokenAddress) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = _price;
tokenIdToTokenAddress[_tokenId] = _tokenAddress;
}
function allowBuy(uint256 _tokenId, uint256 _price) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
require(_price > 0, 'Price zero');
tokenIdToPrice[_tokenId] = _price;
}
function disallowBuy(uint256 _tokenId) external {
require(msg.sender == ownerOf(_tokenId), 'Not owner of this token');
tokenIdToPrice[_tokenId] = 0;
}
function buy(uint256 _tokenId) external payable {
uint256 price = tokenIdToPrice[_tokenId];
require(price > 0, 'This token is not for sale');
require(msg.value == price, 'Incorrect value');
address seller = ownerOf(_tokenId);
address tokenAddress = tokenIdToTokenAddress[_tokenId];
if(address != address(0){
IERC20 tokenContract = IERC20(tokenAddress);
require(tokenContract.transferFrom(msg.sender, address(this), price),
"buy: payment failed");
} else {
payable(seller).transfer(msg.value);
}
_transfer(seller, msg.sender, _tokenId);
tokenIdToPrice[_tokenId] = 0;
emit NftBought(seller, msg.sender, msg.value);
}
}
// mapping is for fast lookup. the longer operation, the more gas
mapping(uint => NftItem) private _idToNftItem;
function buyNft(uint tokenId) public payable{
uint price=_idToNftItem[tokenId].price;
// this is set in erc721 contract
// Since contracts are inheriting, I want to make sure I use this method in ERC721
address owner=ERC721.ownerOf(tokenId);
require(msg.sender!=owner,"You already own this nft");
require(msg.value==price,"Please submit the asking price");
// since this is purchased, it is not for sale anymore
_idToNftItem[tokenId].isListed=false;
_listedItems.decrement();
// this is defined in ERC721
// this already sets owner _owners[tokenId] = msg.sender;
_transfer(owner,msg.sender,tokenId);
payable(owner).transfer(msg.value);
}
this is Nft struct
struct NftItem{
uint tokenId;
uint price;
// creator and owner are not same. creator someone who minted. creator does not change
address creator;
bool isListed;
}

How to store ETH in the Smart Contract?

I'm writing a LibraryPortal Smart Contract in which multiple users can rent their books to each other. So, in this contract the msg.value contain the total amount which is a combination of the Security Deposit & the Renting Rate.
What I have to do is transfer the Renting amount to the owner of the book instantly & store the remaining amount in the contract i.e., the Security Deposit.
If the Renter will not return the book with in the time specified then the security amount will be transfered to the Owner of the book, otherwise get returned to the Renter.
Here is my snippet:
function borrowBook(string _bName) payable returns (string){
if(msg.sender != books[_bName].owner){
if(books[_bName].available == true){
if(getBalance()>=(books[_bName].amtSecurity + books[_bName].rate) ){
books[_bName].borrower = msg.sender;
books[_bName].available = false;
books[_bName].owner.transfer(msg.value - books[_bName].amtSecurity);
// Code missing
// For storing
// ETH into the Contact
return "Borrowed Succesful";
}else{
return "Insufficient Fund";
}
}else{
return "Currently this Book is Not Available!";
}
}else{
return "You cannot Borrow your own Book";
}
}
You can achieve the result from something called as Escrow contract.
Following is the implementation of Escrow contract by open-zeppelin:
contract Escrow is Secondary {
using SafeMath for uint256;
event Deposited(address indexed payee, uint256 weiAmount);
event Withdrawn(address indexed payee, uint256 weiAmount);
mapping(address => uint256) private _deposits;
function depositsOf(address payee) public view returns (uint256) {
return _deposits[payee];
}
/**
* #dev Stores the sent amount as credit to be withdrawn.
* #param payee The destination address of the funds.
*/
function deposit(address payee) public onlyPrimary payable {
uint256 amount = msg.value;
_deposits[payee] = _deposits[payee].add(amount);
emit Deposited(payee, amount);
}
/**
* #dev Withdraw accumulated balance for a payee.
* #param payee The address whose funds will be withdrawn and transferred to.
*/
function withdraw(address payee) public onlyPrimary {
uint256 payment = _deposits[payee];
_deposits[payee] = 0;
payee.transfer(payment);
emit Withdrawn(payee, payment);
}
}
You can just instantiate the contract in your contract and forward the funds to the contract.
For full implementation of similar functionality have a look at the refundable crowdsale contract
Thank you guys for answers but, later I came to know that the VALUE sent along with the transaction to the contract is stored in the Contract itself, & you can access that by using the address(this).balance which will always gives you the Balance available in that instance of the contract. Due to this you don't require any variable or other stuff to store ETHER in your contract.