How to swap array elements using destructuring in Solidity? - solidity

I want to delete data from an array. But deleting data from a particular array index creates a gap in the array. Hence, I am swapping the array[i] with array[array.length-1] and popping out the array. I tried to do it with a destructuring technique that is supported in Solidity yet it turns out the values do not tend to swap. While swapping using the "temp" variable seems to work perfectly fine but it consumes more gas.
There's a warning that solidity throws which I am not able to understand.
Warning: This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
Can anyone help?
Please find the code below:
struct Details{
uint256 id;
address walletAddress;
string fullName;
uint256 phoneNumber;
string residentialAddress;
}
Details [] public userDetails;
function deleteData(uint256 _id) public onlyOwner returns(string memory){
for(uint256 i=0;i<userDetails.length;i++)
{
if (userDetails[i].id==_id){
(userDetails[i],userDetails[userDetails.length-1])=(userDetails[userDetails.length-1],userDetails[i]);
userDetails.pop();
}
}
}

You can just assign the last item to the place of the deleted one (effectively duplicating the last item for the moment), and then delete the last item.
if (userDetails[i].id == _id) {
userDetails[i] = userDetails[userDetails.length-1];
userDetails.pop();
break;
}

Related

Solidity Address Book Smart Contract returns error when trying to create new mapping entry

I'm learning Solidity and I am trying to make an Address Book smart contract. Right now the code allows the user to create an account of sorts using the createBook function.
contract AddressBook {
struct ownerInfo{
string ownerAlias;
}
string[] Alias;
mapping(address => ownerInfo) public ownerToAlias;
function createBook(string memory _ownerAlias) public{
for(uint i = 0; i <= Alias.length; i++) {
require(keccak256(abi.encodePacked(_ownerAlias)) != keccak256(abi.encodePacked(Alias[i])));
ownerToAlias[msg.sender] = ownerInfo(_ownerAlias);
Alias.push(_ownerAlias);
}
}
}
There are no errors when compiling or deploying, but the createBook function returns an error. The problem seems to come from the require() function which I'm using to check whether the input'd name is already in use.
I expected the code to work when I pass in "Address Book Name" for the first time, and return an error when I try to pass in the same string again.
The condition of the for loop needs to be: for(uint i = 0; i < Alias.length; i++)
because as it is right now the loop will continue forever, because you push a new element to the Alias array at the end, the condition will always be true. The length of the Array will always be equal to i as they both increment by one after every loop

Solidity: Dedupping string keys inside a pure function

I am trying to define a mapping to use inside a function as a means of quickly determining whether a key exists. I realize I could also use an array and loop thorugh it but that seems like it might get expensive. But that you can't create mappings dynamically, and if I create a state variable for the purpose, then I have the problem of clearing state between calls to this function which makes it pricey.
Here is what I was tyring to do, any suggestions for the most efficient method to dedupe a set of strings inside a pure function?
function dedupe(string[] memory keys) public pure returns(string[] memory) {
string[] memory deduped;
mapping(string=>bool) map;
string memory ikey;
for(uint i=0; i<keys.length; i++) {
ikey = keys[i];
if (!map[ikey]) {
map[ikey]=true;
deduped.push(ikey);
}
}
return deduped;
}

Solidity: Nested mapping access cannot be at return

i'm writing simple smart contract for learning.
I wrote a contract as
contract A {
mapping(address => mapping(uint32 => uint32)) data;
..
function getData(address addr, index id) public view {
return data[addr][id];
}
}
And it returns compile error
TypeError: Different number of arguments in return statement than in returns declaration
In right that line.
Maybe there is miswriting in my code data[addr][index] , which one should be fixed,
or just a bug of Solidity?
Weirdly, i found answer right after posting..
Simply added returns keywords like
function getData(address addr, index id) public view returns(uint32) {
...
then works fine.
For others, i'll leave this question as is.

eth.call on web3 interface returns null value for contract function returning array of bytes32/strings

I am trying to run a bidding smart contract on a private blockchain and my smart contract is working on the Remix IDE and the same works on my private chain except for one function [dataOwnedBy()] which is suposed to return an array of bytes32 but returns all zero values in geth console.
I have compiled and deployed my smart contract using truffle.
The function which is not working is : (along with data declaration snippet and other function prototypes)
struct data{
bytes32 data_id;
address bidder;
uint bid;
}
mapping(bytes32=>data) bidInfo;
mapping(address=>data[]) dataOwned; //data owned by each address
address[] dataOwners; //list of address who own data
function Bid(bytes32 data_id) public payable { ... }
function closeBid(bytes32 data_id) public { ... }
function whoOwns(bytes32 _data_id) constant public returns (address){ ... }
function dataOwnedBy(address _addr) constant public returns (bytes32[10]){
uint length = dataOwned[_addr].length;
bytes32[10] memory _idArray;
for (uint i=0;i<length;i++){
_idArray[i] = (dataOwned[_addr][i].data_id);
}
return _idArray;
}
After closing the bid, when I query the above function with the winner's address, it returns array of size 10 bytes32 values, all equal to zero, where it should be returning the data_ids owned by the address.!
Version Information from console
> web3.version.api
"0.20.1"
truffle(development)> version
Truffle v3.4.11 (core: 3.4.11)
Solidity v0.4.15 (solc-js)
This is the console output :
playbid.whoOwns("data_id1")
"0x7d8eb703bd863313325b784ac35017614484f2e7"
playbid.dataOwnedBy("0x7d8eb703bd863313325b784ac35017614484f2e7")
["0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"]
Instead the first value of the array should be the hex of "data_id1".
Don't know what is going wrong here, but it works perfectly fine on Remix IDE.
Thanks in advance !
As your code is working OK in remix, there is no problem with the smart contract code. I experienced same issue when I wanted to return some arrays back to my web3j powered java app. I also tested web3js and encountered the same problem. The returned array was broken the same way.
I ended up in serializing and deserializing arrays to strings with a delimiter, both in inputs and outputs.

Is it possible to cast a managed bytes-array to native struct without pin_ptr, so not to bug the GC too much?

It is possible to cast a managed array<Byte>^ to some non-managed struct only using pin_ptr, AFAIK, like:
void Example(array<Byte>^ bfr) {
pin_ptr<Byte> ptr = &bfr[0];
auto data = reinterpret_cast<NonManagedStruct*>(ptr);
data->Header = 7;
data->Length = sizeof(data);
data->CRC = CalculateCRC(data);
}
However, is with interior_ptr in any way?
I'd rather work on managed data the low-level-way (using unions, struct-bit-fields, and so on), without pinning data - I could be holding this data for quite a long time and don't want to harass the GC.
Clarification:
I do not want to copy managed-data to native and back (so the Marshaling way is not an option here...)
You likely won't harass the GC with pin_ptr - it's pretty lightweight unlike GCHandle.
GCHandle::Alloc(someObject, GCHandleType::Pinned) will actually register the object as being pinned in the GC. This lets you pin an object for extended periods of time and across function calls, but the GC has to track that object.
On the other hand, pin_ptr gets translated to a pinned local in IL code. The GC isn't notified about it, but it will get to see that the object is pinned only during a collection. That is, it will notice it's pinned status when looking for object references on the stack.
If you really want to, you can access stack memory in the following way:
[StructLayout(LayoutKind::Explicit, Size = 256)]
public value struct ManagedStruct
{
};
struct NativeStruct
{
char data[256];
};
static void DoSomething()
{
ManagedStruct managed;
auto nativePtr = reinterpret_cast<NativeStruct*>(&managed);
nativePtr->data[42] = 42;
}
There's no pinning at all here, but this is only due to the fact that the managed struct is stored on the stack, and therefore is not relocatable in the first place.
It's a convoluted example, because you could just write:
static void DoSomething()
{
NativeStruct native;
native.data[42] = 42;
}
...and the compiler would perform a similar trick under the covers for you.