-
Notifications
You must be signed in to change notification settings - Fork 351
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
431 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Docs | ||
docs/ | ||
|
||
# Dotenv file | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## AlignedToken | ||
|
||
## Requirements | ||
|
||
- Foundry | ||
|
||
## Local deploying | ||
|
||
To deploy the contracts, set the following environment variables: | ||
|
||
- `DEPLOYER_PRIVATE_KEY`: The private key of the account that's going to deploy the contracts. | ||
- `SAFE_ADDRESS`: The address of the safe that's going to own the Proxy admin that in turn owns the token and airdrop contracts. | ||
- `OWNER1_ADDRESS`, `OWNER2_ADDRESS`, and `OWNER3_ADDRESS`: The three owners of the token. | ||
- `MINT_AMOUNT`: The amount to mint to each account (the contract actually supports minting different amounts of the token to each owner, but in the deploy script we simplified it). | ||
- `RPC_URL`: The url of the network to deploy to. | ||
- `CLAIM_TIME_LIMIT`: The claim time limit timestamp. | ||
- `MERKLE_ROOT`: The merkle root of all valid token claims. | ||
|
||
Example: | ||
``` | ||
export DEPLOYER_PRIVATE_KEY=<deployer_private_key> | ||
export SAFE_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | ||
export OWNER1_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 | ||
export OWNER2_ADDRESS=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC | ||
export OWNER3_ADDRESS=0x90F79bf6EB2c4f870365E785982E1f101E93b906 | ||
export MINT_AMOUNT=100 | ||
export RPC_URL=http://localhost:8545 | ||
export CLAIM_TIME_LIMIT=2733247661 | ||
export MERKLE_ROOT=0x90076b5fb9a6c81d9fce83dfd51760987b8c49e7c861ea25b328e6e63d2cd3df | ||
``` | ||
|
||
Then run the following script: | ||
|
||
``` | ||
./deployClaim.sh | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#!/bin/bash | ||
|
||
forge --version >/dev/null 2>&1 | ||
if [ $? != 0 ]; then | ||
echo "Error: Please make sure you have forge installed and in your PATH" | ||
exit 2 | ||
fi | ||
|
||
safe=${SAFE_ADDRESS:-$1} | ||
owner1=${OWNER1_ADDRESS:-$2} | ||
owner2=${OWNER2_ADDRESS:-$3} | ||
owner3=${OWNER3_ADDRESS:-$4} | ||
mint_amount=${MINT_AMOUNT:-$5} | ||
rpc_url=${RPC_URL:-$6} | ||
claim_time_limit=${CLAIM_TIME_LIMIT:-2733247661} | ||
merkle_root=${MERKLE_ROOT:-$7} | ||
|
||
cd script && forge script DeployScript $safe $owner1 $owner2 $owner3 $mint_amount $claim_time_limit $merkle_root --sig "run(address,address,address,address,uint256,uint256,bytes32)" --fork-url $rpc_url --broadcast |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "out" | ||
libs = ["lib"] | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options |
Submodule openzeppelin-contracts
added at
69c8de
Submodule openzeppelin-contracts-upgradeable
added at
fa5253
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ | ||
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {Vm} from "forge-std/Vm.sol"; | ||
|
||
library Utils { | ||
// Cheatcodes address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. | ||
address internal constant VM_ADDRESS = | ||
address(uint160(uint256(keccak256("hevm cheat code")))); | ||
Vm internal constant vm = Vm(VM_ADDRESS); | ||
|
||
/// @notice Address of the deterministic create2 factory. | ||
/// @dev This address corresponds to a contracts that is set in the storage | ||
/// in the genesis file. The same contract with the same address is deployed | ||
/// in every testnet, so if this script is run in a testnet instead of in a | ||
/// local environment, it should work. | ||
address constant DETERMINISTIC_CREATE2_ADDRESS = | ||
0x4e59b44847b379578588920cA78FbF26c0B4956C; | ||
|
||
function deployWithCreate2( | ||
bytes memory bytecode, | ||
bytes32 salt, | ||
address create2Factory, | ||
uint256 signerPrivateKey | ||
) internal returns (address) { | ||
if (bytecode.length == 0) { | ||
revert("Bytecode is not set"); | ||
} | ||
address contractAddress = vm.computeCreate2Address( | ||
salt, | ||
keccak256(bytecode), | ||
create2Factory | ||
); | ||
if (contractAddress.code.length != 0) { | ||
revert("Contract already deployed"); | ||
} | ||
|
||
vm.broadcast(signerPrivateKey); | ||
(bool success, bytes memory data) = create2Factory.call( | ||
abi.encodePacked(salt, bytecode) | ||
); | ||
contractAddress = bytesToAddress(data); | ||
|
||
if (!success) { | ||
revert( | ||
"Failed to deploy contract via create2: create2Factory call failed" | ||
); | ||
} | ||
|
||
if (contractAddress == address(0)) { | ||
revert( | ||
"Failed to deploy contract via create2: contract address is zero" | ||
); | ||
} | ||
|
||
if (contractAddress.code.length == 0) { | ||
revert( | ||
"Failed to deploy contract via create2: contract code is empty" | ||
); | ||
} | ||
|
||
return contractAddress; | ||
} | ||
|
||
function bytesToAddress( | ||
bytes memory addressOffset | ||
) internal pure returns (address addr) { | ||
assembly { | ||
addr := mload(add(addressOffset, 20)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {Script, console} from "forge-std/Script.sol"; | ||
import {AlignedToken} from "../src/AlignedToken.sol"; | ||
import {ClaimableAirdrop} from "../src/ClaimableAirdrop.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; | ||
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; | ||
import {Utils} from "./Utils.sol"; | ||
|
||
contract DeployScript is Script { | ||
struct Supply { | ||
address beneficiary; | ||
uint256 amount; | ||
} | ||
|
||
function setUp() public {} | ||
|
||
function run( | ||
address safe, | ||
address owner1, | ||
address owner2, | ||
address owner3, | ||
uint256 mintAmount, | ||
uint256 limitTimestamp, | ||
bytes32 merkleRoot | ||
) public { | ||
console.log("Deploying contracts"); | ||
uint256 deployer = vm.envUint("DEPLOYER_PRIVATE_KEY"); | ||
|
||
ProxyAdmin contractsProxyAdmin = deployProxyAdmin(safe, deployer); | ||
|
||
TransparentUpgradeableProxy tokenContractProxy = deployTokenContractProxy( | ||
address(contractsProxyAdmin), | ||
owner1, | ||
owner2, | ||
owner3, | ||
mintAmount, | ||
deployer | ||
); | ||
|
||
TransparentUpgradeableProxy airdropContractProxy = deployAirdropContractProxy( | ||
address(contractsProxyAdmin), | ||
address(tokenContractProxy), | ||
owner3, | ||
limitTimestamp, | ||
merkleRoot, | ||
deployer | ||
); | ||
|
||
vm.startBroadcast(); | ||
(deployer); | ||
(bool success, bytes memory data) = address(tokenContractProxy).call( | ||
abi.encodeCall( | ||
IERC20.approve, | ||
(address(airdropContractProxy), mintAmount) | ||
) | ||
); | ||
bool approved; | ||
assembly { | ||
approved := mload(add(data, 0x20)) | ||
} | ||
|
||
if (!success || !approved) { | ||
revert("Failed to give approval to airdrop contract"); | ||
} | ||
vm.stopBroadcast(); | ||
|
||
console.log("Succesfully gave approval to airdrop contract"); | ||
} | ||
|
||
function deployProxyAdmin( | ||
address tokenContractProxyAdminOwner, | ||
uint256 signerPrivateKey | ||
) internal returns (ProxyAdmin) { | ||
bytes memory bytecode = abi.encodePacked( | ||
type(ProxyAdmin).creationCode, | ||
abi.encode(tokenContractProxyAdminOwner) | ||
); | ||
bytes32 salt = bytes32(0); | ||
address proxyAdminAddress = Utils.deployWithCreate2( | ||
bytecode, | ||
salt, | ||
Utils.DETERMINISTIC_CREATE2_ADDRESS, | ||
signerPrivateKey | ||
); | ||
console.log( | ||
"Aligned Proxy Admin deployed at:", | ||
proxyAdminAddress, | ||
"with owner:", | ||
tokenContractProxyAdminOwner | ||
); | ||
return ProxyAdmin(proxyAdminAddress); | ||
} | ||
|
||
function deployTokenContractProxy( | ||
address tokenContractProxyAdmin, | ||
address owner1, | ||
address owner2, | ||
address owner3, | ||
uint256 mintAmount, | ||
uint256 signerPrivateKey | ||
) internal returns (TransparentUpgradeableProxy) { | ||
vm.broadcast(signerPrivateKey); | ||
AlignedToken tokenContract = new AlignedToken(); | ||
bytes memory bytecode = abi.encodePacked( | ||
type(TransparentUpgradeableProxy).creationCode, | ||
abi.encode( | ||
address(tokenContract), | ||
tokenContractProxyAdmin, | ||
abi.encodeCall( | ||
tokenContract.initialize, | ||
(owner1, mintAmount, owner2, mintAmount, owner3, mintAmount) | ||
) | ||
) | ||
); | ||
bytes32 salt = bytes32(0); | ||
address tokenContractProxyAddress = Utils.deployWithCreate2( | ||
bytecode, | ||
salt, | ||
Utils.DETERMINISTIC_CREATE2_ADDRESS, | ||
signerPrivateKey | ||
); | ||
console.log( | ||
"Aligned Token Proxy deployed at:", | ||
tokenContractProxyAddress, | ||
"with admin:", | ||
tokenContractProxyAdmin | ||
); | ||
return TransparentUpgradeableProxy(payable(tokenContractProxyAddress)); | ||
} | ||
|
||
function deployAirdropContractProxy( | ||
address airdropContractProxyAdmin, | ||
address tokenContractProxyAddress, | ||
address tokenOwnerAddress, | ||
uint256 limitTimestamp, | ||
bytes32 merkleRoot, | ||
uint256 signerPrivateKey | ||
) internal returns (TransparentUpgradeableProxy) { | ||
vm.broadcast(signerPrivateKey); | ||
ClaimableAirdrop airdropContract = new ClaimableAirdrop(); | ||
bytes memory bytecode = abi.encodePacked( | ||
type(TransparentUpgradeableProxy).creationCode, | ||
abi.encode( | ||
address(airdropContract), | ||
airdropContractProxyAdmin, | ||
abi.encodeCall( | ||
airdropContract.initialize, | ||
( | ||
tokenContractProxyAddress, | ||
tokenOwnerAddress, | ||
limitTimestamp, | ||
merkleRoot | ||
) | ||
) | ||
) | ||
); | ||
bytes32 salt = bytes32(0); | ||
address airdropContractProxy = Utils.deployWithCreate2( | ||
bytecode, | ||
salt, | ||
Utils.DETERMINISTIC_CREATE2_ADDRESS, | ||
signerPrivateKey | ||
); | ||
console.log( | ||
"Airdrop Proxy deployed at:", | ||
airdropContractProxy, | ||
"with admin:", | ||
airdropContractProxyAdmin | ||
); | ||
return TransparentUpgradeableProxy(payable(airdropContractProxy)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; | ||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | ||
|
||
contract AlignedToken is Initializable, ERC20Upgradeable, ReentrancyGuard { | ||
struct Supply { | ||
address beneficiary; | ||
uint256 amount; | ||
} | ||
|
||
constructor() { | ||
// Ensure that initialization methods are run only once. | ||
// This is a safeguard against accidental reinitialization. | ||
_disableInitializers(); | ||
} | ||
|
||
function initialize( | ||
address beneficiary1, | ||
uint256 amount1, | ||
address beneficiary2, | ||
uint256 amount2, | ||
address beneficiary3, | ||
uint256 amount3 | ||
) public initializer nonReentrant { | ||
__ERC20_init("AlignedToken", "ALI"); | ||
_mint(beneficiary1, amount1); | ||
_mint(beneficiary2, amount2); | ||
_mint(beneficiary3, amount3); | ||
} | ||
} |
Oops, something went wrong.