How to store IPFS hash on Ethereum blockchain using smart contracts? - ethereum

I'm working on a react + IPFS DAPP and making a simple Pet shop app. for now I have just create smart contract on remix and trying it's functions to work properly but I'm facing a problem.
transact to PetShop.generatePet errored: Error encoding arguments: Error: invalid arrayify value (argument="value", value="QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz", code=INVALID_ARGUMENT, version=bytes/5.0.5)
whenever I'm calling generatePet function and passing IPFS hash it returns above mentioned error.
contract petshop
pragma solidity ^0.6.6;
import "https://github.com/MuhammadSajid404/ERC721-Token/blob/master/ERC721.sol";
contract PetShop is ERC721 {
uint256 public tokenId;
uint256 public prevOwnerTokenID;
mapping(uint256 => uint256) public priceMapping;
mapping(uint256 => bytes32) tokenIdToOffchainContentHash;
event PetGenerated(address, uint256, uint256, bytes32);
event BuyPet(uint256, address, address);
event SuccessfulEtherWithdrawal(uint256, address, bool);
constructor() public
ERC721("ShanBuilders", "SBRS")
{}
function generatePet(uint256 _petPrice, bytes32 contentHash) public returns(bool) {
require(msg.sender != address(0), "Please! Check back! Registeration should not be from zero address");
require(msg.sender == ownerA, "Only contract owner can generate more pets");
tokenId++;
require(tokenId <= 16, "More than 16 pets is not allowed");
priceMapping[tokenId] = _petPrice;
tokenIdToOffchainContentHash[tokenId] = contentHash;
_mint(ownerA, tokenId);
emit PetGenerated(ownerA, tokenId, _petPrice, contentHash);
return true;
}
function checkPrice(uint256 _tokenId) public view returns(uint256) {
return priceMapping[_tokenId];
}
function checkHashForAToken(uint256 _tokenId) public view returns(bytes32) {
return tokenIdToOffchainContentHash[_tokenId];
}
function buyPet(uint256 _tokenId) public payable returns(bool, string memory) {
prevOwnerTokenID = _tokenId;
address buyer = msg.sender;
address _owner = ownerOf(prevOwnerTokenID);
require(buyer != address(0), "Should not be zero address");
require(_exists(prevOwnerTokenID), "Invalid property Id, not registered");
require(msg.value == checkPrice(prevOwnerTokenID), "Please Send The Required Value");
withDraw(msg.value);
_transfer(_owner, buyer, prevOwnerTokenID);
emit BuyPet(_tokenId, _owner, buyer);
return (true, "Succesful");
}
function withDraw(uint256 _amount) public returns(bool) {
address _owner = ownerOf(prevOwnerTokenID);
require(_amount > 0, "Amount must be valid");
payable(_owner).transfer(_amount);
emit SuccessfulEtherWithdrawal(_amount, _owner, true);
return true;
}
}
Note: I would prefer using bytes over string because they consume less gas than strings.. how to solve this problem, I have seen a lot of answers on stackoverflow but actually i'm not getting understand any of them.

bytes32 is just enough for SHA-256 hashes, and may not be enough for other hash functions or variable length multihash format for that matter.
See https://ethereum.stackexchange.com/questions/6861/what-datatype-should-i-use-for-an-ipfs-address-hash

Using sha3-224 would make the IPFS CID short enough to store it in bytes32. Example:
Add a file to IPFS and use sha3-224:
$ ipfs add --cid-version=1 --hash=sha3-224 <file>
bafybohbdfanrni6fre2bmjeivvwqhhpwq3wkqbxsd25az26qgsda
Convert CID to base16:
$ ipfs cid format -b base16 bafybohbdfanrni6fre2bmjeivvwqhhpwq3wkqbxsd25az26qgsda
f0170171c23281b16a3c58934162488ad6d039df686eca806f21eba0cebd03486
Remove the multibase prefix. It's the first character of the CID (in version 1) and it's f for base16 encodings. See multibase table on GitHub.
0170171c23281b16a3c58934162488ad6d039df686eca806f21eba0cebd03486
In Solidity
bytes32 ipfs_cid = 0x0170171c23281b16a3c58934162488ad6d039df686eca806f21eba0cebd03486
Alternativly, if you have a CIDv0 (Qm...), you can convert it to CIDv1 and split into a 32-Bit long prefix (excluding multibase prefix) and a 256-Bit long hash and store them seperately.
$ ipfs cid base32 QmccqhJg5wm5kNjAP4k4HrYxoqaXUGNuotDUqfvYBx8jrR # Convert CIDv0 to CIDv1
bafybeiguffkqavstb6lvfkay5ebplabvc73smdwqiwwxouv4zaupv3vbei
$ ipfs cid format -b base16 bafybeiguffkqavstb6lvfkay5ebplabvc73smdwqiwwxouv4zaupv3vbei # Convert from base32 (default) to base16
f01701220d429550056530f9752a818e902f5803517f7260ed045ad7752bcc828faeea122
bytes4 ipfs_cid_prefix = 0x01701220
bytes32 ipfs_cid_hash = 0xd429550056530f9752a818e902f5803517f7260ed045ad7752bcc828faeea122
Note: When encoding the CID from the contract in your application, don't forget to prepend the multibase prefix back according to the table on GitHub.

Related

"Execution reverted" error when I call createCollectible function on goerli testnet

I'm new to smart contract and Solidity programming and I've been trying to deploy an NFT contract on goerli testnet. The contract deploys without any problem but when I try to call the "createCollectible" function, I get the following error:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
{
"originalError": {
"code": 3,
"data": "0xf0019fe60000000000000000000000000000000000000000000000000000000000000c6a000000000000000000000000b54644506388a04187d943dbbbd3edbb3ee53094",
"message": "execution reverted"
}
}
Here is my contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
/**
* #title The AdvandedCollectible contract
* #notice A contract that mints an NFT
*/
contract AdvancedCollectible is ERC721, VRFConsumerBaseV2 {
VRFCoordinatorV2Interface immutable COORDINATOR;
LinkTokenInterface immutable LINKTOKEN;
uint256 public tokenCounter;
uint64 immutable s_subscriptionId;
// The gas lane to use, which specifies the maximum gas price to bump to.
// For a list of available gas lanes on each network,
// see https://docs.chain.link/docs/vrf-contracts/#configurations
bytes32 immutable s_keyhash;
uint32 immutable s_numWords = 1;
uint16 immutable s_requestConfirmations = 3;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
// so 100,000 is a safe default for this example contract. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 immutable s_callbackGasLimit = 100000;
uint256 public s_requestId;
uint256 public s_randomWords;
address s_owner;
enum Breed {
PUG,
SHIBA_INU,
ST_BERNARD
}
mapping(uint256 => Breed) public tokenIdToBreed;
mapping(uint256 => address) public requestIdToSender;
event requestCollectible(uint256 indexed requestId, address requester);
event breedAssigned(uint256 tokenId, Breed breed);
constructor(
address vrfCoordinator,
address link,
bytes32 keyhash,
uint64 subscriptionId
) VRFConsumerBaseV2(vrfCoordinator) ERC721("Dogie", "DOG") {
tokenCounter = 0;
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
LINKTOKEN = LinkTokenInterface(link);
s_keyhash = keyhash;
s_owner = msg.sender;
s_subscriptionId = subscriptionId;
}
modifier onlyOwner() {
require(msg.sender == s_owner, "You are not the owner");
_;
}
function createCollectible() public returns (uint256) {
s_requestId = COORDINATOR.requestRandomWords(
s_keyhash,
s_subscriptionId,
s_requestConfirmations,
s_callbackGasLimit,
s_numWords
);
requestIdToSender[s_requestId] = msg.sender;
emit requestCollectible(s_requestId, msg.sender);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)
internal
override
{
s_randomWords = randomWords[0];
Breed breed = Breed(s_randomWords % 3);
uint256 newTokenId = tokenCounter;
tokenIdToBreed[newTokenId] = breed;
emit breedAssigned(newTokenId, breed);
address owner = requestIdToSender[requestId];
_safeMint(owner, newTokenId);
tokenCounter++;
}
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
require(
_isApprovedOrOwner(_msgSender(), tokenId),
"ERC721: Caller is not owner nor approved."
);
setTokenURI(tokenId, _tokenURI);
}
}
Does anybody have an idea what I'm doing wrong here?
So I realized that I'm missing one small yet important step, and that is adding a consumer to my subscription.
Using the Chainlink VRF V2, you need to create a subscription, add funds to that subscription and add the address where your contract is deployed at as a consumer to the subscription. I wasn't doing that last step and that's why my transaction's execution kept reverting.

ERC721 contract deployed with create2 stops working after a particular function is called

So I'm working on an NFT marketplace smart contract and one of the features in this marketplace allows users to deploy their own collection (this is basically a smart contract that extends the ERC721 specification). For this, I'm leveraging the create2 opcode. It deploys well while testing on the testnet (except for the insanely huge gas requirement) but the problem is, I can only call a function once in this contract. This is the contract that extends ERC721:
pragma solidity ^0.8.0;
import '#openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol';
import '#openzeppelin/contracts/token/ERC721/IERC721.sol';
import '#openzeppelin/contracts/security/ReentrancyGuard.sol';
import '#openzeppelin/contracts/utils/Counters.sol';
import './interfaces/IDeployableCollection.sol';
contract DeployableCollection is IDeployableCollection, ERC721URIStorage, ReentrancyGuard {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
address public _collectionOwner;
bytes32 public _category;
address payable public _paymentReceiver;
string public _collectionURI;
mapping(address => uint256) public lastMintedForIDs;
constructor(
string memory name_,
string memory symbol_,
address collectionOwner_,
string memory category_,
address paymentReceiver_,
string memory collectionURI_
) ERC721(name_, symbol_) {
_collectionOwner = collectionOwner_;
_category = keccak256(abi.encode(category_));
_paymentReceiver = payable(paymentReceiver_);
_collectionURI = collectionURI_;
}
function mintFor(string memory _tokenURI, address to) external nonReentrant returns (uint256 _tokenId) {
_tokenIds.increment();
_tokenId = _tokenIds.current();
_mint(to, _tokenId);
_setTokenURI(_tokenId, _tokenURI);
lastMintedForIDs[to] = _tokenId;
}
}
This is the line that deploys the contract:
function deployCollection(
string memory name_,
string memory symbol_,
string memory category_,
address paymentReceiver_,
string memory _collectionURI
) external payable nonReentrant {
uint256 _fee = _utilityToken != address(0) && IERC20(_utilityToken).balanceOf(_msgSender()) >= _requiredHold
? _collectionDeployFeeInEther.sub((uint256(_percentageDiscount).mul(_collectionDeployFeeInEther)).div(100))
: _collectionDeployFeeInEther;
require(msg.value >= _fee, 'FEE_TOO_LOW');
bytes memory _byteCode = abi.encodePacked(
type(DeployableCollection).creationCode,
abi.encode(name_, symbol_, _msgSender(), category_, paymentReceiver_, _collectionURI)
);
bytes32 _salt = keccak256(abi.encode(name_, _msgSender()));
address _collection;
assembly {
_collection := create2(0, add(_byteCode, 32), mload(_byteCode), _salt)
}
emit CollectionDeployed(_collection, _msgSender(), block.timestamp, name_, category_, symbol_);
}
Now this works fine, except once I call mintFor and an NFT gets minted, the contract stops working. It shows a total supply of zero (0) on the block explorer even though an NFT has been created (I'm only able to create an NFT with an ID of 1). I also am not able to call any function again as it raises an exception (the cause of which is unknown). This is the line in the factory contract that does the actual minting:
function mintNFT(
address collection,
string memory tokenURI_,
address _for
) external payable nonReentrant returns (bool) {
uint256 _fee = _utilityToken != address(0) && IERC20(_utilityToken).balanceOf(_msgSender()) >= _requiredHold
? _mintFeeInEther.sub((uint256(_percentageDiscount).mul(_mintFeeInEther)).div(100))
: _mintFeeInEther;
require(msg.value >= _fee, 'FEE_TOO_LOW');
address _paymentReceiver = IDeployableCollection(collection)._paymentReceiver();
uint256 _feeForOwner = (uint256(_percentageForCollectionOwners).mul(_fee)).div(100);
_safeMintFor(collection, tokenURI_, _for);
_safeTransferETH(_paymentReceiver, _feeForOwner);
uint256 _tokenId = IDeployableCollection(collection).lastMintedForIDs(_msgSender());
emit Mint(collection, _tokenId, block.timestamp, tokenURI_, _msgSender());
return true;
}
I'm guessing an optimization with a runs of 1 is applied upon deployment using create2 (forgive me if this is stupid) but I also think it isn't likely as I'm also watching for events on the smart contract with a Nodejs back-end and I'm able to call '_collectionURI()' when the event data is propagated to my back-end app and this is before calling mintFor. I'm confused! Please help!
This is a link to the contract info on the explorer: https://testnet.bscscan.com/token/0x6dd5bd0072cdc5e8c24f262a9631c175bc2356a0

How to fix 'TransferHelper: ETH_TRANSFER_FAILED' when interacting with Uni V2

I'm dealing with a strange issue with the safeTransferETH helper function in Uniswap V2's router contract.
I'm trying to swap tokens held by the contract to Uniswap for Ether, using the swapExactTokensForETH function provided by the Uniswap V2 router. (The function code is present on Uniswap's github in router1). The function being called is:
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
override
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
_swap(amounts, path, address(this));
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
The only part of this code that is throwing an error is the TransferHelper.safeTransferETH function, which is:
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call{value:value}(new bytes(0));
require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
}
My code is:
function uniV2ReceiveETH(address _token0, uint _amount0) public payable returns (uint[] memory amountsReceived) {
require(_amount0 > 0, "Must provide tokens if we want tokens in return!");
address[] memory path = new address[](2);
path[0] = _token0;
path[1] = WETH;
IERC20 token;
token = IERC20(_token0);
if (token.balanceOf(address(this)) > 0) {
_amount0 = token.balanceOf(address(this));
}
require(token.approve(address(uniV2Router), _amount0 + 10000), "approval failed");
// Swap logic
uint amountOutMin = UniswapV2Library.getAmountsOut(address(uniV2Factory), _amount0, path)[1];
amountsReceived = uniV2Router.swapExactTokensForETH(_amount0, amountOutMin, path, address(this), deadline);
uint endBalance = address(this).balance;
// Let everyone know we're done!
emit Swap(msg.sender, _amount0, endBalance);
}
A few other notes are:
The contract does receive ETH from other addresses without issue.
I am using hardhat, and a forked version of the mainnet to test.
The contract also works with the Uniswap router's other swap functions, including SwapExactETHForTokens and SwapExactTokensForTokens.
I solved this issue by having a payable fallback function by defining it as:
fallback() external payable { }
inside my smart contract.
Add to your contract.
receive() external payable {}
fallback() external payable {}

Return value from a deployed smart contract, via a smart contract, to a smart contract

I am trying to return a value using a function of a deployed smart contract on the blockchain.
pragma solidity 0.6.2;
contract Caller {
address cont;
function changeAdd(address _change) public {
cont = _change;
}
function caller (bytes memory test) public returns(bool, bytes memory) {
bytes memory payload = abi.encodeWithSignature("callMe(bytes)", test);
(bool success, bytes memory result)= address(cont).call(payload);
return (success, (result));
}
function viewCont() public view returns(address) {
return cont;
}
}
And the deployed test contract is this:
pragma solidity 0.6.2;
contract Store {
uint counter;
function callMe(bytes calldata test) external returns(bytes memory) {
counter++;
return abi.encode(test);
}
function viewCounter () public view returns(uint256) {
return(counter);
}
function clearCounter() public {
counter = 0 ;
}
}
In this example, I am deploying contract "Store" on the blockchain where it is assigned a random address, which can be pointed at via the Caller.changeAdd function in order to use Caller.caller function. Thus, I am trying to send data in the form of bytes to the Store contract, which is supposed to send back the same data to the Caller contract. I am trying to isolate -only- the data that is originally sent, so I can use it to test interaction between smart contracts on the blockchain. I tried in the beginning to send an integer, but I couldn't find a way to do it. So I used bytes and it worked, but still the data I receive isn't the same that I send in the first place(e.g. I send 0x0 and I receive a big bytes number, which isn't the same as 0x0).
I could appreciate any help on how to receive and handle data between two different, unlinked smart contracts, thank you in advance.
Do you try to use abi.decode function of Solidity.
In the below example, contract B calls setName of contract A, then decodes the result by using abi.decode function.
contract A {
string public name;
constructor(string memory tokenName) public {
name = tokenName;
}
function setName(string memory newName) public returns ( string memory){
name = newName;
return newName;
}
}
contract B {
event Log(string msg);
string public myName;
function call(address addr, string memory newName) public {
bytes memory payload = abi.encodeWithSignature("setName(string)", newName);
(bool success, bytes memory result)= addr.call(payload);
// Decode data
string memory name = abi.decode(result, (string));
myName = name;
emit Log(name);
}
}

How to get keccak256 hash in Solidity

I just got started with solidity, I have used truffle to compile and deploy the code to ganache, everything works as I expect, I can call the other functions in the code, but there are certain functions that only the owner can access, the code appears to use keccak256 to get back the address calling the function and determine if the caller address is allowed, I have tried to hash my eth address using this website:
https://emn178.github.io/online-tools/keccak_256.html
and then add the hash to the code before recompiling again, but calling the owner function still throws this error:
"Error: VM Exception while processing transaction: revert"
What am i doing wrong ?
Here's the code with the original hash.
modifier onlyOwner(){
address _customerAddress = msg.sender;
require(owners[keccak256(_customerAddress)]);
_;
}
// owners list
mapping(bytes32 => bool) public owners;
function PetShop()
public
{
// add owners here
owners[0x66e62cf7a807daaf3e42f7af3befe7b2416a79ba5348820245a69fe701f80eb4] = true;
}
/*---------- Owner ONLY FUNCTIONS ----------*/
function disableDogs()
onlyOwner()
public
{
onlyDogs = false;
}
/*-----replace owner ------*/
function setOwner(bytes32 _identifier, bool _status)
onlyOwner()
public
{
owners[_identifier] = _status;
}
/*-----set price for pet adoption----*/
function setAdoptionRequirement(uint256 _amountOfTokens)
onlyOwner()
public
{
AdoptionRequirement = _amountOfTokens;
}
The keccak256 implementation in Solidity stores data differently.
keccak256(...) returns (bytes32):
compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
Just use the function yourself when creating the contract:
function PetShop() public {
// add owners here
owners[keccak256(msg.sender)] = true;
}
as of now
// .encodePacked merges inputs together
owners[keccak256(abi.encodePacked(_text, _num, _addr))}=true
abi.encodePacked() , Solidity supports a non-standard packed mode where:
types shorter than 32 bytes are neither zero padded nor sign extended
dynamic types are encoded in-place and without the length.
array elements are padded, but still encoded in-place