Get array from struct - solidity

so I have this contract
pragma solidity >=0.7.0 <0.9.0;
struct Universe{
uint256 year;
uint64[3] space;
}
contract myContract {
mapping(address => Universe) public myUni;
function setSomething(uint256 _year, uint64[3] memory _space) public {
Universe memory testUni = Universe({
year: _year,
space: _space
});
myUni[msg.sender] = testUni;
}
}
And I am currently trying to test if the array is being passed correctly with truffle:
pragma solidity >=0.7.0 <0.9.0;
const myContract = artifacts.require("./1_Storage.sol");
contract('myContract', (accounts) => {
it('checks if setSomething works', async () => {
const myContractInstance = await myContract.new();
const spaceData = [6000, 6000, 6000];
await myContractInstance.setSomething(2543,spaceData,{from: accounts[1]});
const myPassedData = (await myContractInstance.myUni(accounts[1]));
console.log(myPassedData);
console.log(spaceData);
});
});
The issue is, that I am not able to get spaceData passed into the function and I don't know why. The console.log(myPassedData); shows me only the year and when I try something like console.log(myPassedData.spaceData); it says undefined.

Thanks #sms for the link. As stated in the solidity Documentation:
If you have a public state variable of array type, then you can only
retrieve single elements of the array via the generated getter
function. This mechanism exists to avoid high gas costs when returning
an entire array.
So I had to write a Getter-Function inside of the Contract to be able to access the array:
function getUniverseArray(address _address) public view returns (uint64[3] memory) {
return myUni[_address].space;
}

Related

solidity array mapping with array inside struct

Unable to execture createSchema function. It gives following error
revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
Debug the transaction to get more information.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Testing {
struct Schema {
mapping(string => string) entity;
}
struct SchemaMapping {
// mapping(string => string) key;
// mapping(string => string) value;
string[] key;
string[] value;
}
mapping(uint256 => Schema) schemas;
mapping(uint256 => SchemaMapping[]) schemaMappings;
function createSchema(uint256 id, string memory key, string memory value) public {
SchemaMapping[] storage schemamapping = schemaMappings[id];
schemamapping[id].key.push(key);
schemamapping[id].value.push(value);
schemas[id].entity[key] = value;
}
function getSchemaElemet(uint256 id) public view returns (SchemaMapping[] memory) {
return schemaMappings[id];
}
}
I adjusted your smart contract. The issue in your original contract is that you were trying to add values into schemaMapping without create SchemaMapping at specific index.
Smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Testing {
struct Schema {
mapping(string => string) entity;
}
struct SchemaMapping {
string[] key;
string[] value;
}
mapping(uint256 => Schema) schemas;
mapping(uint256 => SchemaMapping[]) schemaMappings;
function createSchema(uint256 id, string memory key, string memory value) public {
SchemaMapping[] storage schemamapping = schemaMappings[id];
// NOTE: I created an empty space in storage for create object and after bind it with values.
SchemaMapping storage singleSchemaItem = schemamapping.push();
// NOTE: I put values inside keys
singleSchemaItem.key.push(key);
singleSchemaItem.value.push(value);
schemas[id].entity[key] = value;
}
function getSchemaElemet(uint256 id) public view returns (SchemaMapping[] memory) {
return schemaMappings[id];
}
}

CONTRACT_REVERT_EXECUTED Hedera Smart Contract

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.

Reading Attributes or Properties from Contracts

Let's say in an example i have a simple contract:
contract CryptoCharacters is ERC721Enumerable, Ownable {
uint number;
function setNumber(uint256 n) public {
number = n;
}
}
To test this i wrote the following test:
before(async function () {
this.Chars = await ethers.getContractFactory("CryptoCharacters");
});
beforeEach(async function () {
this._chars = await this.Chars.deploy()
await this._chars.deployed()
this.provider = ethers.getDefaultProvider();
});
it('test setting number', async function () {
await this._chars.setNumber.call(OWNER_ADDRESS, 123);
number = await this._chars.number
expect(number ).to.equal(123);
});
The main problem is that i have not found out a way to read the property number from the contrat without a getter function. Is there a way to read that property without a getter function ?
What if this property is another contract ? Could i read it like myInstance.subcontract.property ?
Thanks for any help !
If you make the property public, it will be read-only available for other contracts and off-chain apps through an auto-generated getter function.
uint public number;
number = await this._chars.number()
Unless the property is private, child contracts can access it as read-write.
pragma solidity ^0.8;
contract Parent {
// read-only for external contracts and off-chain apps
// read-write for child contracts
uint public number;
}
contract Child is Parent {
function foo() public {
number++;
}
}

Truffle test in solidity with sending value

duplicating my question from SA:
I have a simple contract with public function, that can receive value and do something based on that value:
pragma solidity >= 0.8.0 < 0.9.0;
contract ContractA {
uint public boughtItems = 0;
uint price = 10;
address [] buyers;
function buySomething() public payable {
require(msg.value >= price, "Sent value is lower");
boughtItems++;
buyers.push(msg.sender);
}
}
and in test folder of my Truffle project I have test contract:
pragma solidity >=0.8.0 <0.9.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/TicketsRoutes.sol";
contract TestTicketsRoutes {
ContractA instance;
address account1 = 0xD8Ce37FA3A1A61623705dac5dCb708Bb5eb9a125;
function beforeAll() public {
instance = new ContractA();
}
function testBuying() public {
//Here I need to invoke buySomething with specific value from specific address
instance.buySomething();
Assert.equal(instance.boughtItems, 1, "Routes amount is not equal");
}
}
How do I invoke function of ContractA in my TestContractA with passing value and sender?
You can use the low-level call() Solidity function to pass a value.
(bool success, bytes memory returnedData) = address(instance).call{value: 1 ether}(
abi.encode(instance.buySomething.selector)
);
But, in order to execute the buySomething() function from a different sender, you need to send it from a different address than the TestTicketsRoutes deployed address.
So you'll need to change your approach and perform the test from an off-chain script (instead of the on-chain test contract) that allows you to sign the transaction from a different sender. Since you tagged the question truffle, here's an example of executing a contract function using the Truffle JS suite (docs).
const instance = await MyContract.at(contractAddress);
const tx = await instance.buySomething({
from: senderAddress,
value: web3.toWei(1, "ether")
});

How to access Solidity mapping which has value of array type?

I define a state variable of mapping type, e.g. mapping(uint256 => uint256[]). I thought to make it public so that I can access it from outside of the contract. However, the compiler reports error TypeError: Wrong argument count for function call: 1 arguments given but expected 2.. It looks like the automatic getter of the mapping doesn't return an array.
For example, ContractB is the contract to be built,
pragma solidity >=0.5.0 <0.6.0;
contract ContractB {
mapping(uint256 => uint256[]) public data;
function getData(uint256 index) public view returns(uint256[] memory) {
return data[index];
}
function add(uint256 index, uint256 value) public {
data[index].push(value);
}
}
Creating a test contract to test ContractB,
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./ContractB.sol";
contract TestContractB {
function testGetData () public {
ContractB c = new ContractB();
c.add(0, 1);
c.add(0, 2);
Assert.equal(c.data(0).length, 2, "should have 2 elements"); // There is error in this line
}
}
I could create a function in ContractB which returns array, though.
Unfortunately, Solidity can't return dynamic arrays yet.
But you can get elements one by one. For this, you need to pass an index to getter:
contract TestContractB {
function testGetData () public {
ContractB c = new ContractB();
c.add(0, 1);
c.add(0, 2);
// Assert.equal(c.data(0).length, 2, "should have 2 elements"); // Don't use this
Assert.equal(c.data(0,0), 1, "First element should be 1");
Assert.equal(c.data(0,1), 2, "Second element should be 2");
}
}