struct
mappings + structs are a powerful combination
Picking up from the mapping example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.1;
contract MappingStructExmaple {
struct Payment {
uint amount;
uint timestamp;
}
struct Balance {
uint totalBalance;
uint numPayments;
mapping (uint => Payment) payments; //map numPayments to struct Payment - called payments
}
mapping(address => Balance) public balanceReceived;
function getBalance() public view returns(uint){
return address(this).balance;
}
function sendMoney() public payable {
balanceReceived[msg.sender].totalBalance += msg.value; // balance init @0. incremented from there.
Payment memory payment = Payment(msg.value, block.timestamp); //payment = Payment(amt, time)
balanceReceived[msg.sender].payments[balanceReceived[msg.sender].numPayments] = payment;
balanceReceived[msg.sender].numPayments++;
}
function partialWithdraw(uint _withdraw_amt, address payable _to) public {
require(_withdraw_amt <= balanceReceived[msg.sender].totalBalance, "Balance exceeded"); //do you hav enuff to withdraw
balanceReceived[msg.sender].totalBalance -= _withdraw_amt; //update balances
_to.transfer(_withdraw_amt); // whr u want to send to?
}
function withdrawAllMoney(address payable _to) public { //i can withdraw to addr of choice
uint user_balance = balanceReceived[msg.sender].totalBalance;
balanceReceived[msg.sender].totalBalance = 0;
// checks-effect interaction pattern
_to.transfer(user_balance);
}
}Create object: Payment,
Payment,Payment.amount
Payment.time
to reflect the value and datetime per transaction.
Create object: Balance,
Balance, Balance.totalBalance
numPayments (initialized @ 0)
mapping(uint => Payment) payments
Create : mapping(address => Balance) public balanceReceived;
mapping(address => Balance) public balanceReceived;Because mappings have no length, we can't do something like balanceReceived.length or payments.length. It's technically not possible. In order to store the length of the payments mapping, we have an additional helper variable numPayments.
So, if you want to the first payment for address 0x123... you could address it like this: balanceReceived[0x123...].payments[0].amount = .... But that would mean we have static keys for the payments mapping inside the Balance struct. We actually store the keys in numPayments, that would mean, the current payment is in balanceReceived[0x123...].numPayments. If we put this together, we can do balanceReceived[0x123...].payments[balanceReceived[0x123...].numPayments].amount = ....
numPayments is not number of payments made.
no. of payments made = numPayments -1 (since we start frm 0)
numPayments serves as an incrementing counter, to load the next "index" into the mapping to create a sequence starting with 0.
balanceReceived
//mapping(address => uint) public balanceReceived;
mapping(address => Balance) public balanceReceived;In this new mapping, balanceReceived[addr] returns a struct:
balanceReceived[addr].totalbalancebalanceReceived[addr].numPayments
sendmoney()
Updating total balance
balanceReceived[msg.sender].totalBalance += msg.value;
Balance.totalBalance += msg.value
The first simplifies to the second line, where the Balance object is respective to the address fed in the first.
Updating individual payments as per ledger
Balance object, contains a mapping as a member, mapping (uint => Payment) payments; it maps an integer ("index") to an object Payment.
First we will create the individual transaction.
// create the payment: insit + assign
// payment = Payment(amt, time)
Payment memory payment = Payment(msg.value, now); //memory cos ref. typeNow we need to book the transaction into our "ledger", which is the mapping payments
// booking .numPayment
// x= 0, on init
balanceReceived[msg.sender].payments[x] = payment //(Payment(msg.value, now))
the key x is:
balanceReceived[msg.sender].numPayments (= 0, on init.)
nesting it:
balanceReceived[msg.sender].payments[balanceReceived[msg.sender].numPayments] = paymentsLoading the transaction is easy enough, from the first line. But how to create a running sequence?
We would need an index/counter -> numPayments -> key value of mapping payments.
uint numPaymentsinit as 0, so our counter starts at 0 for the first payment.
After booking a transaction, increment numPayments in preparation for the next transaction
balanceReceived[msg.sender].numPayments++;
Last updated