1
// SPDX-License-Identifier: MIT
2
pragma solidity ^0.8.20;
3
4
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
6
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
7
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";
8
9
/**
10
* @title LOCKIT Token
11
* @author LockItIn Protocol DAO
12
* @notice Governance token for the LockItIn Protocol
13
* @dev ERC20 with EIP-2612 permit and EIP-5805 votes (timestamp mode)
14
*
15
* Key Properties:
16
* - Fixed supply: 1,000,000,000 (1 billion) tokens, 18 decimals
17
* - No mint/burn functions after deployment (immutable supply)
18
* - Timestamp-based voting snapshots (L2/Base compatible)
19
* - Auto-self-delegation on first token receipt (UX optimization)
20
*
21
* Auto-Delegation Logic:
22
* When tokens are transferred to an address that has never held voting
23
* power, that address is automatically self-delegated. This ensures
24
* recipients have immediate voting power without requiring a separate
25
* delegation transaction.
26
*
27
* Security Considerations:
28
* - Zero-value transfers do not trigger auto-delegation (prevents griefing)
29
* - Only first-time recipients are auto-delegated (respects user agency)
30
* - No admin functions, no pause, no upgrade mechanism
31
*/
32
contract LOCKITToken is ERC20, ERC20Permit, ERC20Votes {
33
34
/// @notice Total supply: 1 billion tokens with 18 decimals
35
uint256 public constant TOTAL_SUPPLY = 1_000_000_000e18;
36
37
/**
38
* @notice Deploy token with entire supply minted to deployer
39
* @dev Deployer is responsible for distributing to vault/vesting contracts
40
*/
41
constructor() ERC20("LockItIn", "LOCKIT") ERC20Permit("LockItIn") {
42
_mint(msg.sender, TOTAL_SUPPLY);
43
}
44
45
/**
46
* @notice Clock used for voting snapshots (EIP-6372)
47
* @dev Uses timestamp instead of block number for L2 compatibility.
48
* Block numbers on L2s like Base don't map predictably to time,
49
* making timestamp-based voting periods more reliable.
50
* @return Current block timestamp as uint48
51
*/
52
function clock() public view override returns (uint48) {
53
return uint48(block.timestamp);
54
}
55
56
/**
57
* @notice Machine-readable clock mode descriptor (EIP-6372)
58
* @return Mode string indicating timestamp-based snapshots
59
*/
60
function CLOCK_MODE() public pure override returns (string memory) {
61
return "mode=timestamp";
62
}
63
64
/**
65
* @dev Handle voting power transfers with auto-self-delegation
66
*
67
* Auto-delegation triggers only when ALL conditions are met:
68
* 1. value > 0 - Prevents zero-transfer griefing attacks
69
* 2. to != address(0) - Not a burn operation
70
* 3. delegates(to) == address(0) - Recipient hasn't chosen a delegate
71
* 4. numCheckpoints(to) == 0 - Recipient has never held voting power
72
*
73
* This "first meaningful receipt" pattern:
74
* - Gives new holders immediate voting power (no extra tx needed)
75
* - Respects user agency (doesn't override existing delegation choices)
76
* - Prevents state-bloat attacks via zero-value transfers
77
*
78
* Note: Contracts receiving tokens will be self-delegated but cannot
79
* vote unless they implement voting logic. This affects quorum math
80
* for governance - plan Governor parameters accordingly.
81
*/
82
function _update(
83
address from,
84
address to,
85
uint256 value
86
) internal virtual override(ERC20, ERC20Votes) {
87
super._update(from, to, value);
88
89
// Auto-self-delegate on first meaningful receipt only
90
if (
91
value > 0 &&
92
to != address(0) &&
93
delegates(to) == address(0) &&
94
numCheckpoints(to) == 0
95
) {
96
_delegate(to, to);
97
}
98
}
99
100
/**
101
* @dev Required override for ERC20Permit + Nonces diamond inheritance
102
* @param owner Address to query nonce for
103
* @return Current permit nonce for owner
104
*/
105
function nonces(
106
address owner
107
) public view virtual override(ERC20Permit, Nonces) returns (uint256) {
108
return super.nonces(owner);
109
}
110
}