Skip to content

Commit 04ea681

Browse files
author
ilitteri
committed
Add openzeppelin audit changes
1 parent c6ef331 commit 04ea681

File tree

7 files changed

+90
-52
lines changed

7 files changed

+90
-52
lines changed

claim_contracts/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ To deploy the contracts, set the following environment variables:
1717
- `MERKLE_ROOT`: The merkle root of all valid token claims.
1818

1919
Example:
20+
2021
```
2122
export DEPLOYER_PRIVATE_KEY=<deployer_private_key>
2223
export SAFE_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
@@ -42,6 +43,7 @@ This is a series of steps to deploy the token to production and upgrade it if ne
4243
### Safe wallet creation
4344

4445
First we create a wallet in [Safe](https://app.safe.global/) to represent the foundation. We assume this is done by the user. This safe will:
46+
4547
- Receive part of the deployed tokens.
4648
- Own the proxy admin contract. This is the contract that can upgrade the token contract.
4749
- Own the proxy contract. This will point to the current implementation of the contract. All users and the safe itself will interact with this contract to mint, transfer, burn, and other erc20 operations.
@@ -51,7 +53,7 @@ First we create a wallet in [Safe](https://app.safe.global/) to represent the fo
5153
There is an example configuration file in `script-config/config.example.json`. Before deploying to production, we need to create a `config.mainnet.json` file in the same folder with the same contents as the example, but we need to change a couple of fields:
5254

5355
- `foundation`: this is the address of the safe that was created in the previous step.
54-
- `claimSupplier`: this is the address of a different safe that will provide the funds for the claim contract when it is deployed.
56+
- `tokenDistributor`: this is the address of a different safe that will provide the funds for the claim contract when it is deployed.
5557
- `deployer`: The address of the deterministic create2 deployer as specified in the [official repo](https://github.com/Arachnid/deterministic-deployment-proxy). The address should be `0x4e59b44847b379578588920ca78fbf26c0b4956c`.
5658
- `salt`: An arbitrary value provided by the sender. This is a 32-bytes hex string. We default to 0.
5759

@@ -69,6 +71,7 @@ This make target internally executes a forge script that:
6971
The private key does NOT correspond to the safe, it needs to represent an account with sufficient funds to deploy the token.
7072

7173
Arguments (env variables):
74+
7275
- `PRIVATE_KEY`: the private key of the deployer account. This is NOT the foundation safe, just any account with enough eth for the deployment. This operation consumes approximately `3935470` gas units. As of Dec 16 2024, the gas price for a high priority is 16 gwei, which means around `0.063` eth.
7376
- `RPC_URL`: a gateway or node that allows for rpc calls.
7477
- `CONFIG`: the name of the configuration file. For `config.example.json` the name would be `example`. For `config.mainnet.json`, this would be `mainnet`.

claim_contracts/script-config/config.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
33
"deployer": "0x4e59b44847b379578588920cA78FbF26c0B4956C",
44
"foundation": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
5-
"claimSupplier": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
5+
"tokenDistributor": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
66
"limitTimestampToClaim": 2733427549,
77
"claimMerkleRoot": "0x90076b5fb9a6c81d9fce83dfd51760987b8c49e7c861ea25b328e6e63d2cd3df",
88
"airdropProxy": "0x882b82f4E9014164Af87ecdAB64955cf7C252C4f",

claim_contracts/script/DeployAlignedToken.s.sol

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,28 @@ import {Utils} from "./Utils.sol";
1212
contract DeployAlignedToken is Script {
1313
function run(string memory config) public {
1414
string memory root = vm.projectRoot();
15-
string memory path = string.concat(root, "/script-config/config.", config, ".json");
15+
string memory path = string.concat(
16+
root,
17+
"/script-config/config.",
18+
config,
19+
".json"
20+
);
1621
string memory config_json = vm.readFile(path);
1722

1823
bytes32 _salt = stdJson.readBytes32(config_json, ".salt");
1924
address _deployer = stdJson.readAddress(config_json, ".deployer");
2025
address _foundation = stdJson.readAddress(config_json, ".foundation");
21-
address _claimSupplier = stdJson.readAddress(config_json, ".claimSupplier");
26+
address _tokenDistributor = stdJson.readAddress(
27+
config_json,
28+
".tokenDistributor"
29+
);
2230

2331
TransparentUpgradeableProxy _tokenProxy = deployAlignedTokenProxy(
2432
_foundation,
2533
_salt,
2634
_deployer,
2735
_foundation,
28-
_claimSupplier
36+
_tokenDistributor
2937
);
3038

3139
console.log(
@@ -40,12 +48,21 @@ contract DeployAlignedToken is Script {
4048
);
4149

4250
string memory deployedAddressesJson = "deployedAddressesJson";
43-
string memory finalJson = vm.serializeAddress(deployedAddressesJson, "tokenProxy", address(_tokenProxy));
51+
string memory finalJson = vm.serializeAddress(
52+
deployedAddressesJson,
53+
"tokenProxy",
54+
address(_tokenProxy)
55+
);
4456

45-
vm.writeJson(finalJson, _getOutputPath("deployed_token_addresses.json"));
57+
vm.writeJson(
58+
finalJson,
59+
_getOutputPath("deployed_token_addresses.json")
60+
);
4661
}
4762

48-
function _getOutputPath(string memory fileName) internal returns (string memory) {
63+
function _getOutputPath(
64+
string memory fileName
65+
) internal returns (string memory) {
4966
string memory outputDir = "script-out/";
5067

5168
// Create output directory if it doesn't exist

claim_contracts/script/DeployAll.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ contract DeployAll is Script {
2323
address _deployer = stdJson.readAddress(config_json, ".deployer");
2424
address _foundation = stdJson.readAddress(config_json, ".foundation");
2525
address _safe = _foundation;
26-
address _claim = stdJson.readAddress(config_json, ".claimSupplier");
26+
address _claim = stdJson.readAddress(config_json, ".tokenDistributor");
2727
uint256 _claimPrivateKey = vm.envUint("CLAIM_SUPPLIER_PRIVATE_KEY");
2828
uint256 _limitTimestampToClaim = stdJson.readUint(
2929
config_json,

claim_contracts/src/AlignedToken.sol

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUp
77
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
88
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
99

10+
/// @title Aligned Token
11+
/// @notice This contract is the implementation of the Aligned Token
12+
/// @dev This contract is upgradeable and should be used only through the proxy contract
1013
contract AlignedToken is
1114
Initializable,
1215
ERC20Upgradeable,
@@ -20,6 +23,12 @@ contract AlignedToken is
2023
/// @notice Symbol of the token.
2124
string public constant SYMBOL = "ALIGN";
2225

26+
/// @notice Supply of the token for the foundation.
27+
uint256 public constant FOUNDATION_SUPPLY = 7_400_000_000e18; // 7.4 billion
28+
29+
/// @notice Supply of the token for the token distributor.
30+
uint256 public constant TOKEN_DISTRIBUTOR_SUPPLY = 2_600_000_000e18; // 2.6 billion
31+
2332
/// @custom:oz-upgrades-unsafe-allow constructor
2433
constructor() {
2534
_disableInitializers();
@@ -28,23 +37,22 @@ contract AlignedToken is
2837
/// @notice Initializes the contract.
2938
/// @dev This initializer should be called only once.
3039
/// @param _foundation address of the foundation.
31-
/// @param _claimSupplier address of the claim supplier. This is the address
40+
/// @param _tokenDistributor address of the token distributor. This is the address
3241
/// that will give the tokens to the users that claim them.
3342
function initialize(
3443
address _foundation,
35-
address _claimSupplier
36-
) public initializer {
44+
address _tokenDistributor
45+
) external initializer {
3746
require(
38-
_foundation != address(0) && _claimSupplier != address(0),
39-
"Invalid _foundation or _claimSupplier"
47+
_foundation != address(0) && _tokenDistributor != address(0),
48+
"Invalid _foundation or _tokenDistributor"
4049
);
4150
__ERC20_init(NAME, SYMBOL);
4251
__ERC20Permit_init(NAME);
4352
__ERC20Burnable_init();
44-
__Ownable2Step_init(); // default is msg.sender
45-
_transferOwnership(_foundation);
46-
_mint(_foundation, 7_400_000_000e18); // 7.4 billion
47-
_mint(_claimSupplier, 2_600_000_000e18); // 2.6 billion
53+
__Ownable_init(_foundation);
54+
_mint(_foundation, FOUNDATION_SUPPLY);
55+
_mint(_tokenDistributor, TOKEN_DISTRIBUTOR_SUPPLY);
4856
}
4957

5058
/// @notice Mints `amount` of tokens.
Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.28;
33

4-
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5-
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
6-
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
7-
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
8-
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
9-
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
10-
4+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
6+
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
7+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
8+
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
9+
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
10+
11+
/// @title Claimable Airdrop
12+
/// @notice This contract is the implementation of the Claimable Airdrop
13+
/// @dev This contract is upgradeable and should be used only through the proxy contract
1114
contract ClaimableAirdrop is
12-
ReentrancyGuard,
1315
Initializable,
16+
ReentrancyGuardUpgradeable,
1417
PausableUpgradeable,
1518
Ownable2StepUpgradeable
1619
{
1720
/// @notice Address of the token contract to claim.
1821
address public tokenProxy;
1922

2023
/// @notice Address of the wallet that has the tokens to distribute to the claimants.
21-
address public claimSupplier;
24+
address public tokenDistributor;
2225

2326
/// @notice Timestamp until which the claimants can claim the tokens.
2427
uint256 public limitTimestampToClaim;
@@ -28,10 +31,20 @@ contract ClaimableAirdrop is
2831

2932
/// @notice Mapping of the claimants that have claimed the tokens.
3033
/// @dev true if the claimant has claimed the tokens.
31-
mapping(address => bool) public hasClaimed;
34+
mapping(address claimer => bool claimed) public hasClaimed;
3235

3336
/// @notice Event emitted when a claimant claims the tokens.
34-
event TokenClaimed(address indexed to, uint256 indexed amount);
37+
/// @param to address of the claimant.
38+
/// @param amount amount of tokens claimed.
39+
event TokensClaimed(address indexed to, uint256 indexed amount);
40+
41+
/// @notice Event emitted when the Merkle root is updated.
42+
/// @param newRoot new Merkle root.
43+
event MerkleRootUpdated(bytes32 indexed newRoot);
44+
45+
/// @notice Event emitted when the claim period is extended.
46+
/// @param newTimestamp new timestamp until which the claimants can claim the tokens.
47+
event ClaimPeriodExtended(uint256 indexed newTimestamp);
3548

3649
/// @custom:oz-upgrades-unsafe-allow constructor
3750
constructor() {
@@ -42,34 +55,35 @@ contract ClaimableAirdrop is
4255
/// @dev This initializer should be called only once.
4356
/// @param _owner address of the owner of the token.
4457
/// @param _tokenProxy address of the token contract.
45-
/// @param _claimSupplier address of the wallet that has the tokens to distribute to the claimants.
58+
/// @param _tokenDistributor address of the wallet that has the tokens to distribute to the claimants.
4659
/// @param _limitTimestampToClaim timestamp until which the claimants can claim the tokens.
4760
/// @param _claimMerkleRoot Merkle root of the claimants.
4861
function initialize(
4962
address _owner,
5063
address _tokenProxy,
51-
address _claimSupplier,
64+
address _tokenDistributor,
5265
uint256 _limitTimestampToClaim,
5366
bytes32 _claimMerkleRoot
54-
) public initializer {
67+
) external initializer {
5568
require(_owner != address(0), "Invalid owner address");
5669
require(
5770
_tokenProxy != address(0) && _tokenProxy != address(this),
5871
"Invalid token contract address"
5972
);
6073
require(
61-
_claimSupplier != address(0) && _claimSupplier != address(this),
74+
_tokenDistributor != address(0) &&
75+
_tokenDistributor != address(this),
6276
"Invalid token owner address"
6377
);
6478
require(_limitTimestampToClaim > block.timestamp, "Invalid timestamp");
6579
require(_claimMerkleRoot != 0, "Invalid Merkle root");
6680

67-
__Ownable2Step_init(); // default is msg.sender
68-
_transferOwnership(_owner);
81+
__Ownable_init(_owner);
6982
__Pausable_init();
83+
__ReentrancyGuard_init();
7084

7185
tokenProxy = _tokenProxy;
72-
claimSupplier = _claimSupplier;
86+
tokenDistributor = _tokenDistributor;
7387
limitTimestampToClaim = _limitTimestampToClaim;
7488
claimMerkleRoot = _claimMerkleRoot;
7589
}
@@ -80,7 +94,7 @@ contract ClaimableAirdrop is
8094
function claim(
8195
uint256 amount,
8296
bytes32[] calldata merkleProof
83-
) public nonReentrant whenNotPaused {
97+
) external nonReentrant whenNotPaused {
8498
require(
8599
!hasClaimed[msg.sender],
86100
"Account has already claimed the drop"
@@ -97,14 +111,8 @@ contract ClaimableAirdrop is
97111

98112
require(verifies, "Invalid Merkle proof");
99113

100-
require(
101-
IERC20(tokenProxy).allowance(claimSupplier, address(this)) >=
102-
amount,
103-
"Insufficient token allowance"
104-
);
105-
106114
bool success = IERC20(tokenProxy).transferFrom(
107-
claimSupplier,
115+
tokenDistributor,
108116
msg.sender,
109117
amount
110118
);
@@ -113,14 +121,15 @@ contract ClaimableAirdrop is
113121

114122
hasClaimed[msg.sender] = true;
115123

116-
emit TokenClaimed(msg.sender, amount);
124+
emit TokensClaimed(msg.sender, amount);
117125
}
118126

119127
/// @notice Update the Merkle root.
120128
/// @param newRoot new Merkle root.
121129
function updateMerkleRoot(bytes32 newRoot) external whenPaused onlyOwner {
122130
require(newRoot != 0 && newRoot != claimMerkleRoot, "Invalid root");
123131
claimMerkleRoot = newRoot;
132+
emit MerkleRootUpdated(newRoot);
124133
}
125134

126135
/// @notice Extend the claim period.
@@ -134,15 +143,16 @@ contract ClaimableAirdrop is
134143
"Can only extend from current timestamp"
135144
);
136145
limitTimestampToClaim = newTimestamp;
146+
emit ClaimPeriodExtended(newTimestamp);
137147
}
138148

139149
/// @notice Pause the contract.
140-
function pause() public whenNotPaused onlyOwner {
150+
function pause() external onlyOwner {
141151
_pause();
142152
}
143153

144154
/// @notice Unpause the contract.
145-
function unpause() public whenPaused onlyOwner {
155+
function unpause() external onlyOwner {
146156
_unpause();
147157
}
148158
}

claim_contracts/src/ExampleAlignedTokenV3.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,23 @@ contract ExampleAlignedTokenV3 is
2828
/// @notice Initializes the contract.
2929
/// @dev This initializer should be called only once.
3030
/// @param _foundation address of the foundation.
31-
/// @param _claimSupplier address of the claim supplier. This is the address
31+
/// @param _tokenDistributor address of the claim supplier. This is the address
3232
/// that will give the tokens to the users that claim them.
3333
function initialize(
3434
address _foundation,
35-
address _claimSupplier
35+
address _tokenDistributor
3636
) public initializer {
3737
require(
38-
_foundation != address(0) && _claimSupplier != address(0),
39-
"Invalid _foundation or _claimSupplier"
38+
_foundation != address(0) && _tokenDistributor != address(0),
39+
"Invalid _foundation or _tokenDistributor"
4040
);
4141
__ERC20_init(NAME, SYMBOL);
4242
__ERC20Permit_init(NAME);
4343
__ERC20Burnable_init();
4444
__Ownable2Step_init(); // default is msg.sender
4545
_transferOwnership(_foundation);
4646
_mint(_foundation, 7_300_000_000e18); // 7.3 billion
47-
_mint(_claimSupplier, 2_700_000_000e18); // 2.7 billion
47+
_mint(_tokenDistributor, 2_700_000_000e18); // 2.7 billion
4848
}
4949

5050
function reinitialize() public reinitializer(3) {}

0 commit comments

Comments
 (0)