-
Notifications
You must be signed in to change notification settings - Fork 230
/
Copy pathInvitationManager.sol
125 lines (96 loc) · 4.61 KB
/
InvitationManager.sol
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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract InvitationManager {
/* An ECDSA signature. */
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
// Indicate if invitation is still available. The default value is true.
mapping(address => bool) public invitationEnabled;
// Records the timestamp when a particular user gets verified.
mapping(address => uint256) public userVerificationTimestamps;
// Records the timestamp when a particular user accepted an invitation from an inviter.
mapping(address => mapping(address => uint256)) public acceptedInvitationsTimestamp;
// Store invitees for each inviter
mapping(address => address[]) public inviteeLists;
// Total invites accepted by day (using the start timestamp of each day as key)
mapping(uint256 => uint256) public totalInvitesByDay;
// Total invites accepted by inviter by day (using the start timestamp of each day as key)
mapping(address => mapping(uint256 => uint256)) public totalInvitesByInviterByDay;
error UserAlreadyVerified();
error UserNotVerified();
error UnrecognizedInvitation();
error IndexOutOfBounds();
error CanNotInviteYourself();
event UserVerified(address indexed userAddress, uint256 verifiedAt, uint256 unix_timestamp);
event InvitationAccepted(
address indexed inviter,
address indexed invitee,
uint256 index,
uint256 expiration,
uint256 acceptedAt,
uint256 unix_timestamp
);
function _markAsVerified(address user) internal {
// Check if the user is already verified
if (userVerificationTimestamps[user] > 0) revert UserAlreadyVerified();
userVerificationTimestamps[user] = block.timestamp;
emit UserVerified(user, block.timestamp, block.timestamp);
}
function markAsVerified() external {
_markAsVerified(msg.sender);
invitationEnabled[msg.sender] = true;
}
function updateInvitationStatus(bool value) external {
if (userVerificationTimestamps[msg.sender] == 0) revert UserNotVerified();
invitationEnabled[msg.sender] = value;
}
function hasBeenVerified(address userAddress) external view returns (bool) {
return userVerificationTimestamps[userAddress] > 0;
}
function getVerifiedTimestamp(address userAddress) external view returns (uint256) {
return userVerificationTimestamps[userAddress];
}
function _verifySignature(address inviter, uint256 expiration, Signature calldata signature) internal pure {
bytes32 payloadHash = keccak256(abi.encode(inviter, expiration));
bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
address messageSigner = ecrecover(messageHash, signature.v, signature.r, signature.s);
if (inviter != messageSigner) revert UnrecognizedInvitation();
}
function confirmAndAcceptInvitation(address inviter, uint256 expiration, Signature calldata signature) external {
if (inviter == msg.sender) revert CanNotInviteYourself();
if (!invitationEnabled[inviter]) revert UnrecognizedInvitation();
_verifySignature(inviter, expiration, signature);
if (expiration < block.timestamp) revert UnrecognizedInvitation();
acceptedInvitationsTimestamp[inviter][msg.sender] = block.timestamp;
_markAsVerified(msg.sender);
// Add the invitee to the inviter's list
inviteeLists[inviter].push(msg.sender);
uint256 dayStartTimestamp = (block.timestamp / 86400) * 86400; // Normalize to the start of the day
totalInvitesByDay[dayStartTimestamp]++;
totalInvitesByInviterByDay[inviter][dayStartTimestamp]++;
emit InvitationAccepted(
inviter,
msg.sender,
inviteeLists[inviter].length - 1,
expiration,
block.timestamp,
block.timestamp
);
}
function getInviteeCount(address inviter) external view returns (uint256) {
return inviteeLists[inviter].length;
}
function getInviteeAtIndex(address inviter, uint256 index) external view returns (address) {
if (index >= inviteeLists[inviter].length) revert IndexOutOfBounds();
return inviteeLists[inviter][index];
}
function getTotalInvitesOnDay(uint256 dayStartTimestamp) external view returns (uint256) {
return totalInvitesByDay[dayStartTimestamp];
}
function getInvitesByInviterOnDay(address inviter, uint256 dayStartTimestamp) external view returns (uint256) {
return totalInvitesByInviterByDay[inviter][dayStartTimestamp];
}
}