-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBlockbase.sol
More file actions
268 lines (229 loc) · 9.63 KB
/
Copy pathBlockbase.sol
File metadata and controls
268 lines (229 loc) · 9.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// SPDX-License-Identifier: MIT
// This line specifies the license for this code - MIT is an open-source license
pragma solidity ^0.8.0;
/**
* these type of comments are called NatSpec comments
* NatSpec comments are used to provide detailed documentation for smart contracts
*/
/**
* @title ExpenseTracker
* @dev A decentralized application (DApp) for college students to track and split expenses
* All data is stored on-chain, making it transparent and immutable
*/
// This contract allows users to register, add expenses, and settle debts
contract ExpenseTracker {
/**
* @dev Person struct represents a user in the system
* @param name The user's name
* @param walletAddress The Ethereum address of the user's wallet
*/
struct Person {
string name;
address walletAddress;
}
/**
* @dev Expense struct represents a shared expense
* @param id Unique identifier for the expense
* @param label Description of what the expense was for
* @param timestamp When the expense was recorded (in Unix time)
* @param amountPaid Mapping of addresses to amounts each person paid
* @param amountOwed Mapping of addresses to amounts each person owes
* @param participants Array of addresses of people involved in this expense
*/
struct Expense {
uint256 id;
string label;
uint256 timestamp;
mapping(address => uint256) amountPaid; // What each person actually paid
mapping(address => uint256) amountOwed; // What each person should have paid (their fair share)
address[] participants; // List of people involved in this expense
}
// Storage for all expenses, mapped by their unique ID
mapping(uint256 => Expense) private expenses;
// Storage for all registered people, mapped by their wallet address
mapping(address => Person) private people;
// Array to keep track of all registered wallet addresses
address[] private registeredPeople;
// Counter for expense IDs, also gives us the total number of expenses
uint256 public expenseCount;
// Events - these help front-end applications react to changes in the contract
// Emitted when a new person registers
event PersonRegistered(address indexed walletAddress, string name);
// Emitted when a new expense is added
event ExpenseAdded(uint256 indexed expenseId, string label);
// Emitted when someone settles a debt
event DebtSettled(address indexed from, address indexed to, uint256 amount);
/**
* @dev Register a new person in the expense tracker
* @param _name The name of the person
*/
function registerPerson(string memory _name) public {
// Validate that the name isn't empty
require(bytes(_name).length > 0, "Name cannot be empty");
// Check if this address has already been registered
require(
people[msg.sender].walletAddress == address(0),
"Person already registered"
);
// Create and store the new person
people[msg.sender] = Person(_name, msg.sender);
// Add to the list of registered people
registeredPeople.push(msg.sender);
// Emit event for front-end apps
emit PersonRegistered(msg.sender, _name);
}
/**
* @dev Add a new shared expense to the tracker
* @param _label Description of the expense
* @param _participants Array of addresses of people involved in this expense
* @param _amountsPaid Array of amounts each participant paid
* @param _amountsOwed Array of amounts each participant owes (their fair share)
*/
function addExpense(
string memory _label,
address[] memory _participants,
uint256[] memory _amountsPaid,
uint256[] memory _amountsOwed
) public {
// Validate inputs
require(bytes(_label).length > 0, "Label cannot be empty");
require(_participants.length > 0, "No participants");
require(
_participants.length == _amountsPaid.length,
"Participants and amounts paid must have the same length"
);
require(
_participants.length == _amountsOwed.length,
"Participants and amounts owed must have the same length"
);
// Create new expense with the next ID
uint256 expenseId = expenseCount++;
Expense storage newExpense = expenses[expenseId];
// Set the basic expense information
newExpense.id = expenseId;
newExpense.label = _label;
newExpense.timestamp = block.timestamp; // Current block time in Unix timestamp format
// Add each participant's data to the expense
for (uint256 i = 0; i < _participants.length; i++) {
require(
_participants[i] != address(0),
"Invalid participant address"
);
// Store the participant, what they paid, and what they owe
newExpense.participants.push(_participants[i]);
newExpense.amountPaid[_participants[i]] = _amountsPaid[i];
newExpense.amountOwed[_participants[i]] = _amountsOwed[i];
}
// Emit event for front-end apps
emit ExpenseAdded(expenseId, _label);
}
/**
* @dev Get information about a person
* @param _addr Address of the person to look up
* @return name The person's name
* @return walletAddress The person's wallet address
*/
function getPerson(
address _addr
) public view returns (string memory name, address walletAddress) {
Person storage p = people[_addr];
return (p.name, p.walletAddress);
}
/**
* @dev Get the list of participants in a specific expense
* @param _expenseId ID of the expense
* @return Array of participant addresses
*/
function getExpenseParticipants(
uint256 _expenseId
) public view returns (address[] memory) {
require(_expenseId < expenseCount, "Expense does not exist");
return expenses[_expenseId].participants;
}
/**
* @dev Get basic information about an expense
* @param _expenseId ID of the expense
* @return id The expense ID
* @return label The expense description
* @return timestamp When the expense was created
*/
function getExpenseBasicInfo(
uint256 _expenseId
) public view returns (uint256, string memory, uint256) {
require(_expenseId < expenseCount, "Expense does not exist");
Expense storage expense = expenses[_expenseId];
return (expense.id, expense.label, expense.timestamp);
}
/**
* @dev Get the amount a participant paid for a specific expense
* @param _expenseId ID of the expense
* @param _participant Address of the participant
* @return Amount paid by the participant
*/
function getAmountPaid(
uint256 _expenseId,
address _participant
) public view returns (uint256) {
require(_expenseId < expenseCount, "Expense does not exist");
return expenses[_expenseId].amountPaid[_participant];
}
/**
* @dev Get the amount a participant owes for a specific expense
* @param _expenseId ID of the expense
* @param _participant Address of the participant
* @return Amount owed by the participant
*/
function getAmountOwed(
uint256 _expenseId,
address _participant
) public view returns (uint256) {
require(_expenseId < expenseCount, "Expense does not exist");
return expenses[_expenseId].amountOwed[_participant];
}
/**
* @dev Calculate the net balance for a person across all expenses
* Positive balance means they are owed money
* Negative balance means they owe money
* @param _person Address of the person
* @return netBalance The calculated net balance
*/
function getNetBalance(address _person) public view returns (int256) {
int256 netBalance = 0;
// Loop through all expenses
for (uint256 i = 0; i < expenseCount; i++) {
// Add what they paid (increases their balance)
netBalance += int256(expenses[i].amountPaid[_person]);
// Subtract what they owed (decreases their balance)
netBalance -= int256(expenses[i].amountOwed[_person]);
}
return netBalance;
}
/**
* @dev Settle a debt by sending ETH to another person
* @param _to Address of the person to pay
* Note: The actual ETH amount is sent with the transaction (msg.value)
*/
function settleDebt(address _to) public payable {
// Validate recipient address
require(_to != address(0), "Invalid recipient address");
// Prevent sending to yourself
require(_to != msg.sender, "Cannot settle debt with yourself");
// Record the settlement as an event
// The actual ETH transfer happens automatically with the transaction
emit DebtSettled(msg.sender, _to, msg.value);
}
/**
* @dev Get a list of all registered users' addresses
* @return Array of all registered wallet addresses
*/
function getAllRegisteredPeople() public view returns (address[] memory) {
return registeredPeople;
}
/**
* @dev Get the total number of registered users
* @return count The number of registered users
*/
function getTotalUsers() public view returns (uint256 count) {
return registeredPeople.length;
}
}