Reading Attributes or Properties from Contracts - solidity

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++;
}
}

Related

How to run multiples contracts functionalities in a single contract Solidity

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!

Get array from struct

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;
}

Upgreadable ERC20 TypeError: Cannot create property 'kind' on string

I created 2 smart contracts.
contract A is ERC20 token and B is a contract that accepts ERC20 A as payment method. When deploying, contract A got deployed but when I passed its proxy address to the initialize function in contract B for deployment it shows this error:
TypeError: Cannot create property 'kind' on string '0x814bd9012bD1e51b86890343b9731501875502e8'
what could be the problem?
hardhat deploy script
const token = await ethers.getContractFactory("Token");
console.log("Deploying token...");
const tokeninit= await upgrades.deployProxy(token , {
initializer: "initialize",
});
await tokeninit.deployed();
const tokenaddr = tokeninit.address;
console.log("token deployed at :", tokenaddr );
const SmartB= await ethers.getContractFactory("SmartB");
const SmartBInit= await upgrades.deployProxy(SmartB,tokenaddr, {
initializer: "initialize",
});
Smart contract B shorten code:
contract SamrtB is Initializable, UUPSUpgradeable, OwnableUpgradeable {
using CountersUpgradeable for CountersUpgradeable.Counter;
CountersUpgradeable.Counter private _tokenIdCounter;
IERC20Upgradeable public tokenAccepted;
constructor() initializer {}
function initialize(address _token) initializer public {
__Ownable_init();
__UUPSUpgradeable_init();
tokenAccepted =IERC20Upgradeable(_token);
}
I was passing the parameters in the next smart contract the wrong way, it should be
const SmartBInit= await upgrades.deployProxy(SmartB,[tokenaddr],{}
Instead of
const SmartBInit= await upgrades.deployProxy(SmartB,tokenaddr, {}

Solidity struct arrar

I'm running into a problem with solidity with structures containing arrays, can you help me see what I'm missing?Any help would be greatly appreciated!
struct Info {
uint a;
uint256 b;
uint[] data;
}
mapping(address => Info) infos;
function set() public {
infos[msg.sender].a = 1;
infos[msg.sender].b = 2;
infos[msg.sender].data.push(3);
}
function get() public {
infos[msg.sender].a; //yes It is equal to 1
infos[msg.sender].b; //yes It is equal to 2
infos[msg.sender].data[0]; //The problem here is that anyone calling this function can read data[0]=3
}
I am a little confused as to what you require, but first solution I have provided modifies your Smart Contract such that mapping object infos and the getter function is both private (available only in the Contract defined).
contract test{
struct Info {
uint a;
uint b;
uint[] data;
}
mapping(address => Info) private infos;
function set() public {
infos[msg.sender].a = 1;
infos[msg.sender].b = 2;
infos[msg.sender].data.push(3);
}
function get() private view{
infos[msg.sender].a; //yes It is equal to 1
infos[msg.sender].b; //yes It is equal to 2
infos[msg.sender].data[0]; //The problem here is that anyone calling this function can read data[0]=3
} }
Second solution is to add something called 'require' in the getter function so that only the person who deploys the Smart Contract can view the array index. The constructor function assigns the person who deploys the contract as the 'owner'.
contract test{
struct Info {
uint a;
uint b;
uint[] data;
}
address owner;
constructor() {
owner = msg.sender;
}
mapping(address => Info) infos;
function set() public {
infos[msg.sender].a = 1;
infos[msg.sender].b = 2;
infos[msg.sender].data.push(3);
}
function get() public view{
infos[msg.sender].a; //yes It is equal to 1
infos[msg.sender].b; //yes It is equal to 2
require(owner == msg.sender, 'you cannot read this data, you are not the owner!');
infos[msg.sender].data[0]; //The problem here is that anyone calling this function can read data[0]=3
}
}
Let me know if I have misunderstood your question.

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");
}
}