Hello I'm new in Solidity, I want to make a contact that can create ships, show all ships (I use a "pagination"), show all ships of a user, and destroy a ship.
I want to make a correct code with good practices. That's why I'm posting my "solution" to "delete" a value from an array without going over the array.
What I do is, create the array of ships, a mapping of array of index of all the ships an user have, and a mapping of the array of the user to find easy where is the position in the index of user array of index.
My question is if the "deleteShip" function, could broken because multiple transactions (should it be atomic)? How? What's the correct way to do this?
struct StructureShip {
address owner;
string name;
uint256 price;
bool sale;
}
StructureShip[] private ships;
mapping(address => uint256[]) private ships_by_user;
mapping(uint256 => uint256) private ships_by_user_index;
uint256[] private ships_sale;
mapping(uint256 => uint256) private ships_sale_index;
function createShip(string memory _ship_name) public {
StructureShip memory _ship = StructureShip({
name: _ship_name,
owner: msg.sender,
price: 0,
sale: false
});
ships.push(_ship);
ships_by_user[msg.sender].push(ships.length - 1);
ships_by_user_index[ships.length - 1] =
ships_by_user[msg.sender].length -
1;
}
function getShips(uint _page)
external
view
returns (StructureShip[10] memory)
{
StructureShip[10] memory _ships;
for (uint index = (_page - 1) * 10; index < _page * 10; index++ ) {
if (ships.length == index) {
break;
}
_ships[index - (_page - 1) * 10] = ships[index];
}
return _ships;
}
function getShipsByUser(address _user, uint256 _page)
external
view
returns (StructureShip[10] memory)
{
StructureShip[10] memory _ships;
for (uint index = (_page - 1) * 10; index < _page * 10; index++ ) {
if (ships_by_user[_user].length == index) {
break;
}
_ships[index - (_page - 1) * 10] = ships[ships_by_user[_user][index]];
}
return _ships;
}
function deleteShip(uint256 _id) public onlyCaptain(_id) {
//borrar index de usuario
ships_by_user[ships[_id].owner][
ships_by_user_index[_id]
] = ships_by_user[ships[_id].owner][
ships_by_user[ships[_id].owner].length - 1
];
ships_by_user_index[_id] = ships_by_user[ships[_id].owner].length - 1;
ships_by_user[ships[_id].owner].pop();
//reubicar ultimo barco
ships_by_user[ships[ships.length - 1].owner][
ships_by_user_index[ships.length - 1]
] = _id;
ships_by_user_index[ships.length - 1] = ships_by_user_index[_id];
//borrar publicación de venta
if (ships[_id].sale) {
ships_sale_index[ships_sale[ships_sale.length - 1]] = _id;
ships_sale[ships_sale_index[_id]] = ships_sale[
ships_sale.length - 1
];
ships_sale.pop();
}
//reubicar publicación de venta ultimo barco
if (ships[ships.length - 1].sale) {
ships_sale_index[_id] = ships_sale_index[ships.length - 1];
ships_sale[ships_sale_index[ships.length - 1]] = _id;
}
//borrar ultimo barco vacío
ships[_id] = ships[ships.length - 1];
ships.pop();
}
}
Each transaction is mined (i.e., processed and added to a block) chronologically and sequentially, in an order decided by the winning miner.
Thus, when the first transaction destroying a ship is executed and mined, the contract state shifts. Then the next transaction is processed and mined, and the state shifts once again, ready for the next state change through the subsequent transaction.
See the following for more discussion around this topic:
https://ethereum.stackexchange.com/questions/43895/update-the-same-variable-in-parallel https://ethereum.stackexchange.com/questions/1405/what-is-the-order-and-concurrency-behavior-of-multiple-calls-to-a-contract-in-a