When I tried to update string value inside smart contract, it is done successfully like this:
string private test;
function edit(string memory account) public {
test = account;
}
But after I used object and tried to edit string inside the object, I can not do that:
Hospital [] hospital_list;
struct Hospital {
int id;
string name;
string account;
}
function addHospital(int hosid, string memory name) public { // worked correctly
Hospital memory hospital = Hospital(hosid, name, 'null');
hospital_list.push(hospital);
}
function editHospitalAccount(int hosid, string memory account) public {
Hospital memory hospital = getHospital(hosid); // worked correctly
hospital.account = account; // this is not worked
}
How I can update the value?
Thanks,
Related
Issue
There seems to be some issue with this line here family.People.push(people[x]);
I keep getting Member "push" not found or not visible after argument-dependent lookup when I try to compile with brownie.
What have I tried
I saw a few SO posts with similar exceptions but it was related to type casting. I did try to cast my incoming array to its type but it just resulted in more exceptions.
Code
pragma solidity ^0.8.9;
contract Person{
string public FirstName;
string public LastName;
}
contract Family{
Person[] public People;
}
contract FamilyManager{
Family[] Families;
function AddFamily(Person[] memory people) public {
Family family = new Family();
for(uint x; x < people.length; x++){
family.People.push(people[x]);
}
Families.push(family);
}
function GetFamilies() public view returns (Family[] memory){
return Families;
}
}
Can anyone spot what I'm doing wrong here or link to an article that can lead to an answer?
I think it is related to access modifiers. Using the public modifier for your array only generates the getter function for it and not the setter.
As a result, you cannot directly push to an array from another contract. I created a public function to add elements to the array as follows:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Person{
string public FirstName;
string public LastName;
}
contract Family{
Person[] public People;
function addPerson(Person person) public {
People.push(person);
}
}
contract FamilyManager{
Family[] Families;
function AddFamily(Person[] memory people) public {
Family family = new Family();
for(uint x; x < people.length; x++) {
family.addPerson(people[x]);
}
Families.push(family);
}
function GetFamilies() public view returns (Family[] memory){
return Families;
}
}
I'm trying to understand inheritance, interfaces and the call of functions from other smart contracts in Solidity. So I did this example. It has two interfaces, one for struct type definition and other for functions definition. It also has two contracts for implementing logic of functions defined in the interface. Finally, there is a main contract that interacts with functions contracts via the interface.
My question is how to share data written and read through the contract, since when interacting with functions contracts on the main smart contract, the struct data type that is written in registerPerson function is not read on getPerson function. It returns me a blank struct data type.
I think maybe is something that's missing in the code, but I don't know what it is.
Thanks in advance.
StructInterface interface Smart Contract
interface StructInterface {
struct Person {
uint personId;
string name;
string surname;
uint age;
}
}
FunctionsInterface interface Smart Contract
interface FunctionsInterface {
function addPerson (uint, string memory, string memory,uint ) external;
function askPerson(uint) external view returns (uint, string memory, string memory, uint);
}
RegisterPerson Smart Contract
import "./StructInterface.sol";
contract RegisterPerson is StructInterface {
mapping (uint => Person) persons;
function addPerson (
uint _personId,
string memory _name,
string memory _surname,
uint _age
) external {
persons[_personId].personId = _personId;
persons[_personId].name = _name;
persons[_personId].surname =_surname;
persons[_personId].age = _age;
}
}
GetPerson Smart Contract
import "./StructInterface.sol";
contract GetPerson is StructInterface {
mapping (uint => Person) persons;
function askPerson(uint _personId) external view
returns (uint personId, string memory name, string memory surname, uint age) {
personId = persons[_personId].personId;
name = persons[_personId].name;
surname = persons[_personId].surname;
age = persons[_personId].age;
return (personId, name, surname, age);
}
}
Main Smart Contract
import "./StructInterface.sol";
import "./RegisterPerson.sol";
import "./GetPerson.sol";
import "./FunctionsInterface.sol";
contract Main is StructInterface{
address regPersonA;
address getPersonA;
Person myPersons;
constructor (address _regPersonA, address _getPersonA) {
regPersonA = _regPersonA;
getPersonA = _getPersonA;
}
function registerPerson(
uint _personId,
string memory _name,
string memory _surname,
uint _age
) external {
FunctionsInterface interf = FunctionsInterface(regPersonA);
interf.addPerson(_personId, _name, _surname, _age);
}
function getPerson(
uint _personId
) external view
returns (uint personId, string memory name, string memory surname, uint age)
{
FunctionsInterface interf = FunctionsInterface(getPersonA);
return interf.askPerson(_personId);
}
}
Goal
I am trying to push some data to a mongo db using mongojack.
I expect the result to be something like this in the db:
{
"_id": "840617013772681266",
"messageCount": 69420,
"seedCount": 18,
"prefix": "f!",
"language": "en"
}
Problem
Instead, I get this error in my console.
Caused by: java.lang.IllegalArgumentException: invalid hexadecimal representation of an ObjectId: [840617013772681266]
at org.bson.types.ObjectId.parseHexString(ObjectId.java:390)
at org.bson.types.ObjectId.<init>(ObjectId.java:193)
at org.mongojack.internal.ObjectIdSerializer.serialiseObject(ObjectIdSerializer.java:66)
at org.mongojack.internal.ObjectIdSerializer.serialize(ObjectIdSerializer.java:49)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
... 59 more
Code
This is the code that gets called when I try to create a new Guild in the db:
public static Guild getGuild(String id) throws ExecutionException {
return cache.get(id);
}
cache is the following (load get executed):
private static LoadingCache<String, Guild> cache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<>() {
#Override
public Guild load(#NotNull String id) {
return findGuild(id).orElseGet(() -> new Guild(id, "f!"));
}
});
The findGuild method that gets called first:
public static Optional<Guild> findGuild(String id) {
return Optional.ofNullable(guildCollection.find()
.filter(Filters.eq("_id", id)).first());
}
And finally the Guild document.
#Getter
#Setter
public class Guild implements Model {
public Guild(String id, String prefix) {
this.id = id;
this.prefix = prefix;
}
public Guild() {
}
private String id;
/*
If a Discord guild sent 1,000,000,000 messages per second,
it would take roughly 292471 years to reach the long primitive limit.
*/
private long messageCount;
private long seedCount;
// The default language is specified in BotValues.java's bot.yaml.
private String language;
private String prefix;
#ObjectId
#JsonProperty("_id")
public String getId() {
return id;
}
#ObjectId
#JsonProperty("_id")
public void setId(String id) {
this.id = id;
}
}
What I've tried
I've tried multiple things, such as doing Long.toHexString(Long.parseLong(id)) truth is I don't understand the error completely and after seeing documentation I'm left with more questions than answers.
ObjectId is a 12-byte value that is commonly represented as a sequence of 24 hex digits. It is not an integer.
You can either create ObjectId values using the appropriate ObjectId constructor or parse a 24-hex-digit string. You appear to be trying to perform an integer conversion to ObjectId which generally isn't a supported operation.
You can technically convert the integer 840617013772681266 to an ObjectId by zero-padding it to 12 bytes, but standard MongoDB driver tooling doesn't do that for you and considers this invalid input (either as an integer or as a string) for conversion to ObjectId.
Example in Ruby:
irb(main):011:0> (v = '%x' % 840617013772681266) + '0' * (24 - v.length)
=> "baa78b862120032000000000"
Note that while the resulting value would be parseable as an ObjectId, it isn't constructed following the ObjectId rules and thus the value cannot be sensibly decomposed into the ObjectId components (machine id, counter and a random value).
I'm using MySQL DB.
My entity for the table is Account with the following fields:
id(long), balance (double), created_on(Date), currency(Enum).
When I'm doing a PUT request to update the account, I pass in the request body JSON.
I want to update, for example, only the balance, but the other columns' values to be saved.
In that case (I'm not passing the currency type) the balance is updated, but the currency has value NULL. Is that because it's enum?
I've tried using #DynamicUpdate annotation, but still, it doesn't have any change.
#RestController
public class AccountController {
#PutMapping("/accounts/{id}")
public void updateAccount(\#PathVariable long id, #RequestBody AccountDto accountDto) {
accountService.updateAccount(id, accountDto);
}
}
I'm using AccountDto (which I pass in the request body) and I'm calling the accountService
public void updateAccount(long id, AccountDto accountDto) {
Account account = accountRepository.getOne(id);
account.fromDto(accountDto);
this.accountRepository.save(account); }),
which calls the AccountRepository
public void fromDto(AccountDto accountDto) {
this.balance = accountDto.getBalance();
this.currency = accountDto.getCurrency();
}
Here is the AccountDto class:
public class AccountDto {
private long id;
#NotNull #PositiveOrZero
private double balance;
#NotNull #Enumerated(EnumType.STRING)
private Currency currency;
}
You need to perform a select query on Account entity and then update only the desired fields.
(Eg - making assumptions of my own of underlying method being used for accessing DB)
public updateAccount(AccountModel jsonBody) {
Account entity = accountRepository.findById(jsonBody.getAccountId());
entity.setBalance(jsonBody.getBalance());
accountRepository.save(entity);
}
If you get null as currency in the JSON you shouldn't update it:
So fromDto must look like:
public void fromDto(AccountDto accountDto) {
this.balance = accountDto.getBalance();
if (accountDto.getCurrency() != null) {
this.currency = accountDto.getCurrency();
}
}
I am attempting to call a solidity function that looks something like the following:
function fillOrder(
Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
Using web3j I would create the function similar to below, however I'm not quite sure how to represent the order which is represented as a struct in Solidity.
List<Type> inputParams = Arrays.asList(???, new
Uint256(takerAssetFillAmount), new Bytes32(signture));
new Function("fillOrder", inputParams, Collections.emptyList());
Any pointers on how I should represent the struct?
Thanks.
You can wrap parameters with square brackets.
For example, let's say I have a contract:
contract Test {
struct Foo {
uint a;
string b;
address c;
}
function bar (Foo memory foo) public {
c = foo.c;
}
}
I can call bar function with web3.js like this:
contract.methods.foo([123, "123", "0xABC...."]).send({ from: '0x...' })
here is the contract address https://goerli.etherscan.io/address/0xd5999bf0ce31a1d9d6a6de2bf03feaff1913cee5#writeContract
in the write function , createSwapOrder is asking nested Tuple . here is the solidity code to show the structure of tuple :
struct Side {
address user;
bytes signedRequiredOutput;
ERC20Component[] erc20s;
ERC721Component[] erc721s;
ERC1155Component[] erc1155s;
}
struct ERC20Component {
uint256 amount;
address underlying;
// A signed approval transaction giving `amount` transfer rights
// of token `underlying` to address(this).
// bytes signedApproval;
}
struct ERC721Component {
uint256 tokenId;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct ERC1155Component {
uint256 tokenId;
uint256 amount;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct Order {
Side side0;
Side side1;
uint256 expiry;
bytes32 hashlock;
bytes32 preimage;
bool completed;
}
event OrderCreated(address indexed user, bytes32 orderId);
uint256 public totalOrders;
mapping(bytes32 => Order) public orders;
function createSwapOrder(
Side calldata side0,
bytes32 hashlock,
uint256 timelock
) public {
...
}
and in first args side0 is asking a nested tuple and this tuple formet should be like this
["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0x00",[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]]],
i hope you can understand the structure how its provided !! and sure it working
Web3j offers such classes as StaticStruct and DynamicStruct where you define your struct object via primitives. Here is the sample from my project:
class Value: DynamicStruct {
private lateinit var offer: String
private lateinit var availableSince: BigInteger
private lateinit var availabilityEnd: BigInteger
private var isConsumed: Boolean = false
private lateinit var lockedUntil: BigInteger
constructor(
offer: String,
availableSince: BigInteger,
availabilityEnd: BigInteger,
isConsumed: Boolean,
lockedUntil: BigInteger
) : super(
Utf8String(offer), Uint256(availableSince),
Uint256(availabilityEnd), Bool(isConsumed),
Uint256(lockedUntil)
) {
this.offer = offer
this.availableSince = availableSince
this.availabilityEnd = availabilityEnd
this.isConsumed = isConsumed
this.lockedUntil = lockedUntil
}
constructor(
offer: Utf8String,
availableSince: Uint256,
availabilityEnd: Uint256,
isConsumed: Bool,
lockedUntil: Uint256
) : super(offer, availableSince, availabilityEnd, isConsumed, lockedUntil) {
this.offer = offer.value
this.availableSince = availableSince.value
this.availabilityEnd = availabilityEnd.value
this.isConsumed = isConsumed.value
this.lockedUntil = lockedUntil.value
}
}
Ideally you just need to pass this struct instance to you contract method as a parameter where contract is autogenerated over $web3j solidity generate -b /path/to/<smart-contract>.bin -a /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name command.
However personally I faced with issue to make this console command working and have to implement required logic by my own. This is in the progress now.