How to track transactions with mapping without overwriting previous data in Solidity? - ethereum

I am trying to record all the transaction details done by any address like so:
contract GetPayment {
struct Payment {
uint amount;
uint timestamp;
}
mapping(address => Payment) public mainMap;
function pay() public payable {
mainMap[msg.sender].amount = msg.value;
mainMap[msg.sender].timestamp = now;
}
}
The problem with this code, is that every time the msg.sender makes a new transaction, it overwrites the previous data.
How to save the data without overwriting the previous data?

Maybe you want to use a mapping to a dynamic array of Payments and push a new payment each time it is made?
mapping(address => Payment[]) public mainMap;
function pay() public payable {
Payment[] storage payment = mainMap[msg.sender];
payment.push(Payment({amount: msg.value, timestamp: block.timestamp}));
mainMap[msg.sender] = payment;
}
That is if you want it to be accessed in the contract.
Otherwise, if you want to utilize it in the front-end, you can use an event with an inerrable element of the address to parse it.

Related

enable 2 users to use my contract at the same time with different states in Solidity

I'm new in solidity, and I was trying to create a simple purchase contract between 2 users, with different states as below:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract purchase {
uint public value;
struct Users {
address payable seller;
address payable buyer;
}
Users public users;
uint256 public contractID;
enum State{Created, Locked, Release, Inactive}
State public state;
constructor() payable {
users.seller = payable(msg.sender);
value = msg.value / 2;
}
///The function can't be called at the current state.
error InvalidState();
///Only buyer/buyer can call this function
error Onlybuyer();
///Only Seller can call this function
error OnlySeller();
modifier inState(State state_){
if (state != state_) {
revert InvalidState();
}
_;
}
modifier onlybuyer(){
if (msg.sender != users.buyer) {
revert Onlybuyer();
}
_;
}
modifier onlySeller(){
if (msg.sender != users.seller) {
revert OnlySeller();
}
_;
}
mapping(uint => Users) public contractUsers;
mapping(uint => State) public contractState;
function confirmPurchase() external inState(State.Created) payable {
contractID ++;
require(msg.value == (2*value), "Please send in 2X the purchase amount");
users.buyer = payable(msg.sender);
state = State.Locked;
}
function confirmRecieved() external onlybuyer inState(State.Locked) {
state = State.Release;
users.buyer.transfer(value);
}
function paySeller() external onlySeller inState(State.Release){
state = State.Inactive;
users.seller.transfer(3 * value);
}
function abort() external onlySeller inState(State.Created){
state = State.Inactive;
users.seller.transfer(address(this).balance);
}
}
I would like to ask, how I can enable 2 sellers to use the contract at the same time with different states? or how I enable many users to use my contract at the same time?
for example:let's say we have seller 1, buyer 1 with price 2 ETH at state 1.
at the same time seller 2, buyer 2 with price 3 ETH at state 0.
each one of them is using the contract at the same time and they can view their contract details using the contract ID. how I can do that?
I thought about creating a function to set the state to initial state 0,then a new user can use the contract, and retrieve their data from the mapping using contract ID. but I'm not sure if this is the best practice. Can anyone advice please!
Thank you in advance.

How to transfer a contract balance to an external address from an internal function?

In this contract, I call payWinner() once the third participant has bought a ticket. payWinner() is internal to avoid any external call but when it executes winnerAddress.transfer(winnableBalance);, the contract balance is sent to an internal address rather than to the external address winnerAddress. Same thing goes for burnLeftBalance() obviously.
I guess either it's impossible to transfer part of the contract holdings to an external address from an internal function and I need to create a public function for that (but I don't see how I can be sure that it's the contract itself calling the function), or there is something I'm missing.
pragma solidity ^0.8.14;
contract Lottery {
address payable public burnAddress = payable(0x0000000000000000000000000000000000000000);
uint256 public ticketsPerRound = 3;
uint256 public ticketPrice = 0.001 * 10e8 * 10e8;
address[] public participantAddresses;
error WrongTicketPrice(uint256 expectedRole, uint256 actualRole);
function buyTicket() external payable {
if (msg.value != ticketPrice) {
revert WrongTicketPrice(ticketPrice, msg.value);
}
participantAddresses.push(msg.sender);
if (participantAddresses.length >= ticketsPerRound) {
payWinner();
}
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
function getParticipantAddresses() external view returns (address[] memory) {
return participantAddresses;
}
function getWinnableBalance() public view returns (uint256) {
return address(this).balance / 2;
}
function getWinner() internal view returns (address payable winnerAddress) {
uint256 unsafeRandomNumber = uint256(
keccak256(abi.encodePacked(block.difficulty, block.timestamp, participantAddresses))
);
uint256 winnerIndex = unsafeRandomNumber % participantAddresses.length;
winnerAddress = payable(participantAddresses[winnerIndex]);
}
function burnLeftBalance() internal {
uint256 leftBalance = address(this).balance;
burnAddress.transfer(leftBalance);
reset();
}
function payWinner() internal {
uint256 winnableBalance = getWinnableBalance();
address payable winnerAddress = getWinner();
winnerAddress.transfer(winnableBalance);
burnLeftBalance();
}
function reset() internal {
delete participantAddresses;
}
}
There's no such thing as "internal and external addresses". The closest to this term I can think of is Externally Owned Account, which is a term representing an address derived from a private key, without a contract bytecode, mostly used by end user wallets. But that does not seem to be related to your question.
Etherscan is using "internal transactions" as a more user-friedly term for "message calls within the virtual machine". These "internal transaction" details (such as from what sender, to what recipient, what data was passed, what ETH value, ...) are not part of the public blockchain data - only the combined state changes of the "parent" transaction are stored in the blockchain. So Etherscan retrieves these details by emulating the transaction, records its message calls and stores them in Etherscan's own database, and then displays them in a separate tab.
So, the transaction that you sent is from your address, to the contract address, with value of 0.01 ETH, its data field contains selector of the buyTicket() function that you want to execute... These are all params of the transaction that you sent. And the "internal transactions" are just a way of displaying state changes that your transaction produced, and their trace. But as long as the state changes were produced (e.g. increased balance of the winnerAddress), there's no difference if they were produced by a "parent" transaction or an "internal transaction".
TLDR: It's not a bug, it's a feature. :)

Where is this Solidity mapping variable initialised?

There is sample code in "Modular Contracts" that uses a mapping, but I cannot see where that values of each mapping index get assigned.
The mapping is defined as:
mapping(address => uint256) balances;
and it is passed into the library function "Balances":
library Balances {
function move(mapping(address => uint256) storage balances,
address from,
address to,
uint amount) internal {
require(balances[from] >= amount);
require(balances[to] + amount >= balances[to]);
balances[from] -= amount;
balances[to] += amount;
...
But I don't see where any value is actually initially added to balances in any part of the full code example.
The move function first checks if balances[from] >= amount. At the end of that function you see it adds and subtracts from the identified balances, but that code will never be reached, right? Since require(balances[from] >= amount); will always return false.
What am I missing?
It's just an example of Modular Contracts you can add any balance in your constructor like
constructor() public {
balances[yourAddress] = amount
}

Do I need to always use a setter since contracts are essentially permanent?

So, from what I understand, it's a good idea to use temporary contracts and redeploy when a change is necessary. Let's assume that I controlled a smart lightbulb with the following code.
pragma solidity 0.4.24;
contract Lightbulb {
enum LightState { off, on }
address public owner;
LightState light;
LightState constant defaultChoice = LightState.off;
uint public cost = 1 ether;
constructor () public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function turnOn() public payable {
require(msg.value == cost);
light = LightState.on;
}
function turnOff() public {
light = LightState.off;
}
function getState() public view returns (LightState) {
return light;
}
}
Should I just redeploy the contract if I wanted to change the cost? Also, I could put the cost in a constructor, but if I wanted to change the cost I would still have to redeploy so does that really matter?
Contract deployment is very costly and you typically want to avoid it whenever possible. You would deploy a new contract when business rules change. For a simple state change operation like the one you described, you would want to provider a setter for the cost that uses the onlyOwner modifier to restrict access.

How to get return values when function with argument is called

I am writing below simple contract which stores all results of questionnaires of each ID.
contract answer{
mapping(address => mapping(string => bool)) voters;
struct qList {
uint count; //The number of respondents
mapping(address => mapping(uint => uint)) answer;
}
mapping(string => qList) questionnaires;
function vote(string ID, uint qNum, uint ans) returns (bool) {
if(voters[msg.sender][ID]) throw;
voters[msg.sender][ID] = true;
questionnaires[ID].count += 1;
questionnaires[ID].answer[msg.sender][qNum] = ans;
return true;
}
function getNumResult(string ID) constant returns (uint res) {
return questionnaires[ID].count;
}
}
The function "vote" including arguments can be called and mined successfully, howerver I cannot get the return value with the status message "Waiting for transaction to be mined..." in solidity-browser screen when I call "getNumResult" with ID which has already registered via the function "vote".
Appreciate it if someone would advise the cause of this and solution to get the return value of function with arguments.
The author also asked on the Ethereum Stack Exchange and here's one answer.
With non-constant function vote, you can only get the transaction hash back immediately because the transaction may never get mined. Or it could take several blocks as indicated by "Waiting for transaction to be mined..."
Recommend to check: https://ethereum.stackexchange.com/questions/765/what-is-the-difference-between-a-transaction-and-a-call
Events
Events are needed to get the "return value" of vote.
Example of how to add and trigger an event:
contract answer{
// ...
event VoteEvent(string ID, bool returnValue);
function vote(string ID, uint qNum, uint ans) returns (bool) {
// ...
VoteEvent(ID, true);
return true;
}
}
See Contract Events for the different ways to watch for and get event data using web3.js.