I'm trying to call execute the diamondCut function using another contract, but it keeps getting reverted, even though all the facet addresses and respective function selectors exist and are included in the call. I'm trying to do the following:
Remove 3 functions, each function is in a separate facet, using the cutSomeDiamonds function in this contract
The interfaces used are in https://github.com/mudgen/diamond-1-hardhat/tree/main/contracts/interfaces
contract ExecuteDiamondCuts {
address CONTRACT_ADDRESS = 0xblahblah; // dummy address
string[] functionsToRemove;
constructor() {
functionsToRemove.push("exampleFunctionA(address)");
functionsToRemove.push("exampleFunctionB(uint)");
functionsToRemove.push("exampleFunctionC()");
}
function cutSomeDiamonds() external {
IDiamondLoupe _loupe = IDiamondLoupe(CONTRACT_ADDRESS);
IDiamondCut.FacetCut[] memory _facetCuts = new IDiamondCut.FacetCut[](functionsToRemove.length);
for (uint i=0; i<functionsToRemove.length; i++) {
address facetWithFn = _loupe.facetAddress(bytes4(keccak256(bytes((functionsToRemove[i])))));
bytes4[] memory _fnSelectors = new bytes4[](1);
_fnSelectors[0] = bytes4(keccak256(bytes((functionsToRemove[i]))));
IDiamondCut.FacetCut memory _facetCut = IDiamondCut.FacetCut({
facetAddress: facetWithFn, // facet address
action: IDiamondCut.FacetCutAction.Remove, // action
functionSelectors: _fnSelectors // array of fn selectors
});
_facetCuts[i] = _facetCut;
}
(bool success,) = CONTRACT_ADDRESS.delegatecall(
abi.encodeWithSignature(
"diamondCut(FacetCut[],address,bytes)",
_facetCuts,
address(0),
"0x"
)
);
require(success);
}
}
Everything seems to work until require(success); which got reverted.
Related
I'm new to Solidity but I can't find much information about my problem.
For example, I want to make different contracts for different functionalities (I see them as classes)
For example
Main contract
// SPDX-License-Identifier: None
pragma solidity >=0.8.6;
import "./AuthContract.sol";
contract Contract {
string public message;
constructor() {
message = "test";
}
function getMessage() public view returns(string memory) {
return message;
}
}
and second contract
contract Auth {
struct UserDetail {
address addr;
string name;
string password;
string CNIC;
bool isUserLoggedIn;
}
mapping(address => UserDetail) user;
// user registration function
function register(
address _address,
string memory _name,
string memory _password,
string memory _cnic
) public returns (bool) {
require(user[_address].addr != msg.sender);
user[_address].addr = _address;
user[_address].name = _name;
user[_address].password = _password;
user[_address].CNIC = _cnic;
user[_address].isUserLoggedIn = false;
return true;
}
// user login function
function login(address _address, string memory _password)
public
returns (bool)
{
if (
keccak256(abi.encodePacked(user[_address].password)) ==
keccak256(abi.encodePacked(_password))
) {
user[_address].isUserLoggedIn = true;
return user[_address].isUserLoggedIn;
} else {
return false;
}
}
// check the user logged In or not
function checkIsUserLogged(address _address) public view returns (bool) {
return (user[_address].isUserLoggedIn);
}
// logout the user
function logout(address _address) public {
user[_address].isUserLoggedIn = false;
}
}
How could I use the functionalities from that contract in the main contract?
Is such a thing possible in the blockchain?
I am quite new here also but i can solve your problem, if not solve i can lead you to a good path .
so firstly you said you want to create multiple contract for different functionalities, this is good but keep in my you are going to exhaust a lot of gas.
so the answer to your problem is easy you can just read it and implement it.
if you want to use a contract in the Another contract (main in your case) you can do it in two ways(according to my knowledge there might be other).
using new keyword
using address of your previously deployed contract
we will be using the First case as i suppose you havenot deployed the second contract yet
In Order to do it you can use
Auth myObj=new Auth();
This will create a new instance of the contract Auth in your main contract and now you can use the Auth contract's function in your Main contract.you can create a function copy the above line and you can use the Functions using dot operator.
myObj.register(_address,_name,moreandmore);
I believe this will solve your problem if not you can ask it.
Thank You!
CONTRACT_REVERT_EXECUTED
Not sure what I'm doing wrong but I'm trying to call a function and it takes in one parameter and I made sure it was correct but it still reverts. This is hedera-hashgraph using HederaTokenService.
Smart Contract:
pragma solidity ^0.8.11;
import "./hip-206/HederaTokenService.sol";
import "./hip-206/HederaResponseCodes.sol";
contract Minting is HederaTokenService {
address tokenAddress;
bytes metadata;
string baseURI = "abc";
uint64 mintPrice;
function mintNonFungibleToken(uint64 _amount) external payable {
bytes[] memory nftMetadatas = generateBytesArrayForHTS(
baseURI,
_amount
);
(
int256 response,
uint64 newTotalSupply,
) = HederaTokenService.mintToken(tokenAddress, _amount, metadata);
if (response != HederaResponseCodes.SUCCESS) {
revert("Mint Failed");
}
}
// #dev Helper function which generates array of addresses required for HTSPrecompiled
function generateAddressArrayForHTS(address _address, uint256 _items)
internal
pure
returns (address[] memory _addresses)
{
_addresses = new address[](_items);
for (uint256 i = 0; i < _items; i++) {
_addresses[i] = _address;
}
}
// #dev Helper function which generates array required for metadata by HTSPrecompiled
function generateBytesArrayForHTS(bytes memory _bytes, uint256 _items)
internal
pure
returns (bytes[] memory _bytesArray)
{
_bytesArray = new bytes[](_items);
for (uint256 i = 0; i < _items; i++) {
_bytesArray[i] = _bytes;
}
}
Calling the transaction in js:
const contractMint = await new ContractExecuteTransaction()
.setContractId(contractId)
.setGas(3000000)
.setFunction(
"mintFungibleToken",
new ContractFunctionParameters().addUint64(1)
)
.setMaxTransactionFee(new Hbar(2));
Also note that a REVERT does usually contain useful information, you can navigate to hashscan.io to look up the response from your smart contract, for example
https://hashscan.io/testnet/transaction/1675427464.278782297?tid=0.0.1189-1675427451-309271560
shows a contract that reverted, the error message is 0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b52656d697420746f6b656e20616c726561647920637265617465640000000000
with the error message starting with 0x08c379a, we know it's a string, so we can decode it
Hacky way:
Navigate to: https://www.rapidtables.com/convert/number/hex-to-ascii.html
Paste the above (remove the 0x)
Choose ASCII for character encoding
Press convert
Output: Ãy Remit token already created
In Javascript
const {ethers} = require("ethers");
async function main() {
const error = "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b52656d697420746f6b656e20616c726561647920637265617465640000000000";
const reason = ethers.utils.defaultAbiCoder.decode(
['string'],
ethers.utils.hexDataSlice(error, 4)
)
console.log(reason);
};
void main();
It looks like you're trying to call mintFungibleToken from JS, but the smart contract doesn't have a mintFungibleToken function; the smart contract function name is mintNonFungibleToken.
How can I push a value into an dynamic sized array within a function?
I have a function which builds an array dynamically to use this as a function argument afterwards:
function autoMintBatch(...) {
uint32[] xList;
uint32[] yList;
// ... y is declared properly ...
yList.push(y);
for (uint256 i = 0; i < idList.length; i++) {
xList.push(x++);
}
}
I get the following compilation error:
TypeError: Member "push" is not available in uint32[] memory outside
of storage.
But when I change the variable declaration to storage like:
uint32[] storage xList;
another error appears:
TypeError: This variable is of storage pointer type and can be
accessed without prior assignment, which would lead to undefined
behaviour.
Is there any way to build an array dynamically within a function?
Assign the result of "x++" in a variable inside the function and then try to push the variable inside the array. Remember to create a state variable for the array. This worked for me
Item[] public items;
event MarketItem(
bytes32 title,
uint8 price,
bool sold,
bool published,
address owner
);
mapping(address => Item) public itemToOwner;
function addItem(bytes32 _title, uint8 _price) public {
Item memory newItem = Item({
title: _title,
price: _price,
sold: false,
published: true,
owner: msg.sender
});
items.push(newItem);
}
Only dynamic storage arrays can be resized (push/pop).
In your case better implementation could rely on static memory array with fixed size, such as:
function autoMintBatch(...) public virtual returns (uint256[] memory)
{
...
uint256[] memory xList = new uint[](idList.length);
for(uint256 i = 0; i< amount ; i++)
{
xList[i] = y++;
}
return xList;
}
im new to web3 and solidity in general but I've been following the web documentation on calling contract methods from web3
my solidity smart-contract:
pragma solidity ^ 0.5.0;
contract Royalties {
string public name = "RoyaltiesContract";
uint public nextArtistId = 1;
uint public nextSongId = 0;
uint public nextCollabId = 0;
mapping(uint => Artist) public Artists;
struct Artist {
uint id;
string ArtistName;
address ArtistAddress;
}
function createArtist(string memory ArtistName,address Artistaddress) public returns(uint id, string memory Name, address Address) {
Artists[nextArtistId] = Artist(nextArtistId,ArtistName, Artistaddress);
nextArtistId++;
return(nextArtistId,ArtistName, Artistaddress);
}
function getArtist(uint id) view public returns(uint, string memory , address){
return(Artists[id].id,Artists[id].ArtistName, Artists[id].ArtistAddress);
}
}
my web3 code after doing all the configs :
shoot(royalties) {
const createArtist = royalties.methods.createArtist("DMX","0x518251583591f3DE330Eb539AB64b6E95C1EE5c5").call().then(
function(result){
console.log(result)
},
royalties.methods.getArtist(1).call({defaultBlock :'latest'}).then(console.log)
)
}
keep in mind an artist with id:1 already exist the above shoot() function is trigged on-click now the first console.log which is createArtist method returns the result fine same as smart-contract like this:
Result {0: "2", 1: "DMX", 2: "0x518251583591f3DE330Eb539AB64b6E95C1EE5c5", id: "2", Name: "DMX", Address: "0x518251583591f3DE330Eb539AB64b6E95C1EE5c5"}
but the second console.log which is getArtist(1) method returns keep in mind an artist with id:1 already:
Result {0: "0", 1: "", 2: "0x0000000000000000000000000000000000000000"}
thank you for taking the time to read my question.
You need to interact with the contract function createArtist() using send(), not call().
send() sends a transaction, which effectively allows for writing into the contract storage.
If you haven't configured your web3 defaultSender, you'll also need to pass it an options object containing at least {from: <address>}, so that web3 knows from which address you want to send (and sign - so you need to pass its private key to web3 as well) the transaction.
call() doesn't send a transaction, just reads data. So you can safely use it for the getArtist() view function call.
Let's start with my solidity code :
pragma solidity ^0.4.18;
contract Voting {
address mainAddress;
bytes32[] candidateNames;
mapping(bytes32 => uint) candidateVotes;
mapping(bytes32 => bytes32) candidatesDetails;
address[] voters;
function Voting() public {
mainAddress = msg.sender;
}
modifier isMainAddress {
if (msg.sender == mainAddress) {
_;
}
}
function getAllCandidates() public view returns (bytes32[]) {
return candidateNames;
}
function setCandidate(bytes32 newCandidate) isMainAddress public {
candidateNames.push(newCandidate);
}
function setVote(bytes32 candidate) public {
require(validVoters());
candidateVotes[candidate] = candidateVotes[candidate] + 1;
voters.push(msg.sender);
}
function getVote(bytes32 candidate) public view returns (uint) {
return candidateVotes[candidate];
}
function setDescrption(bytes32 candidateName, bytes32 candidatesDesc) isMainAddress public {
candidatesDetails[candidateName] = candidatesDesc;
}
function getDescription(bytes32 candidateName) public view returns (bytes32){
return candidatesDetails[candidateName];
}
function getCurrentAddress() public view returns (address) {
return msg.sender;
}
function validVoters() public view returns (bool) {
for(uint i = 0; i < voters.length ; i++){
if (voters[i] == msg.sender) {
return false;
}
}
return true;
}
}
The functions : Voting(), getAllCandidates(), setCandidate(), getVote(), setDescription(), getDescription(), getCurrentAddress() works fine when called multiple times. So, I guess we can ignore them for now.
The function setVote() runs fine the first time it executes ie. when a person votes for once. The problem arises when the same person tries to vote the second time. It gives the following error :
This might be a beginners mistake but I have been trying to fix this for 2 days straight and now I really need help.
Also,
I use Remix - browser based IDE to run/check my solidity code.
I use Ganache for test accounts.
Thanks.
The function in question:
function setVote(bytes32 candidate) public {
require(validVoters());
candidateVotes[candidate] = candidateVotes[candidate] + 1;
voters.push(msg.sender);
}
Note that validVoters() must return true for this function to succeed. If it returns false, the require will fail and revert the transaction. Also note that at the end of the function, msg.sender is added to the array voters.
Let's take a look at validVoters():
function validVoters() public view returns (bool) {
for(uint i = 0; i < voters.length ; i++){
if (voters[i] == msg.sender) {
return false;
}
}
return true;
}
This function returns false if msg.sender is in voters, which we know will be the case after the account has voted once.
So the second time through, validVoters() returns false, which causes require(validVoters()) in setVote() to revert the transaction.