Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions evm/contracts/tge/UDrop.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
pragma solidity ^0.8.27;

import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import
"@openzeppelin-upgradeable/contracts/access/manager/AccessManagedUpgradeable.sol";
import "@openzeppelin-upgradeable/contracts/utils/PausableUpgradeable.sol";

import "solady/tokens/ERC20.sol";
import "solady/utils/MerkleProofLib.sol";
import "solady/utils/EfficientHashLib.sol";

import "../internal/Versioned.sol";

contract UDrop is
Initializable,
UUPSUpgradeable,
PausableUpgradeable,
AccessManagedUpgradeable,
Versioned
{
error UDrop_ZeroAddress();
error UDrop_AlreadyClaimed();
error UDrop_InvalidProof();
error UDrop_NotActive();
error UDrop_ClaimIsActive();

bytes32 public immutable ROOT;
address public immutable TOKEN;

bool public active;
mapping(address => bool) public claimed;

constructor(bytes32 _root, address _token) {
_disableInitializers();
ROOT = _root;
TOKEN = _token;
}

function initialize(
address _authority,
bool _active
) external initializer {
__UUPSUpgradeable_init();
__Pausable_init();
__AccessManaged_init(_authority);
active = _active;
}

function claim(
address beneficiary,
uint256 amount,
bytes32[] calldata proof
) external whenNotPaused {
if (!active) revert UDrop_NotActive();
if (claimed[beneficiary]) revert UDrop_AlreadyClaimed();
bytes32 leaf =
EfficientHashLib.hash(abi.encodePacked(beneficiary, amount));
if (!MerkleProofLib.verifyCalldata(proof, ROOT, leaf)) {
revert UDrop_InvalidProof();
}
claimed[beneficiary] = true;
ERC20(TOKEN).transfer(beneficiary, amount);
}

function setActive(
bool _active
) external restricted whenNotPaused {
active = _active;
}

function withdraw(
address destination
) external restricted whenNotPaused {
if (active) revert UDrop_ClaimIsActive();
ERC20(TOKEN).transfer(
destination, ERC20(TOKEN).balanceOf(address(this))
);
}

function _authorizeUpgrade(
address
) internal override restricted {}

function pause() public restricted {
_pause();
}

function unpause() public restricted {
_unpause();
}
}
9 changes: 7 additions & 2 deletions evm/evm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ _: {
solady = pkgs.fetchFromGitHub {
owner = "vectorized";
repo = "solady";
rev = "v0.1.12";
hash = "sha256-XsIXs3lj5gddBzswNFY1DhnlhUQx+ITf6lvBPSkMY7c=";
rev = "v0.1.26";
hash = "sha256-ycYSZnpJBJiJTGpJCnt1R/vKP7pTQY6dd8e35HIP0Co=";
};
forge-std = pkgs.fetchFromGitHub {
owner = "foundry-rs";
Expand Down Expand Up @@ -1083,6 +1083,9 @@ _: {
${contracts}/out/Zkgm.sol/AbiExport.json \
${contracts}/out/Zkgm.sol/UCS03Zkgm.json > app.ucs03.json

jq --compact-output --slurp 'map(.abi) | add' \
${contracts}/out/UDrop.sol/UDrop.json > udrop.json

jq --compact-output --slurp 'map(.abi) | add' \
${contracts}/out/CometblsClient.sol/CometblsClient.json \
${contracts}/out/CometblsClient.sol/CometblsClientLib.json > lightclient.cometbls.json
Expand Down Expand Up @@ -1219,6 +1222,7 @@ _: {
multicall = "Multicall";
erc20 = "ZkgmERC20";
u = "U";
udrop = "UDrop";
}
)
# other various deployment scripts
Expand Down Expand Up @@ -1261,6 +1265,7 @@ _: {
state-lens-ics23-smt-client = "StateLensIcs23SmtClient";
core = "IBCHandler";
u = "U";
udrop = "UDrop";
}
)
))
Expand Down
193 changes: 192 additions & 1 deletion evm/scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import "forge-std/Script.sol";
import "solady/utils/CREATE3.sol";
import "solady/utils/LibString.sol";
import "solady/utils/LibBytes.sol";
import "solady/utils/LibSort.sol";
import "solady/utils/MerkleTreeLib.sol";
import "solady/utils/EfficientHashLib.sol";

import "@openzeppelin-foundry-upgradeable/Upgrades.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
Expand All @@ -25,6 +29,7 @@ import "../contracts/apps/ucs/00-pingpong/PingPong.sol";
import "../contracts/apps/ucs/03-zkgm/Zkgm.sol";
import "../contracts/apps/ucs/06-funded-dispatch/FundedDispatch.sol";
import "../contracts/tge/Vesting.sol";
import "../contracts/tge/UDrop.sol";

import "./Deployer.sol";

Expand Down Expand Up @@ -53,6 +58,8 @@ struct UCS03Parameters {
library INSTANCE_SALT {
bytes constant U =
hex"12c206e42a6e7773c97d1f1b855d7848492f9e4e396b33fcf0172d6758e9b047";
bytes constant UDROP =
hex"96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17177504";
}

library LIB_SALT {
Expand Down Expand Up @@ -200,6 +207,25 @@ abstract contract UnionScript is UnionBase {
);
}

function deployUDrop(
Manager authority,
bytes32 root,
address token,
bool active
) internal returns (UDrop) {
return UDrop(
deploy(
string(INSTANCE_SALT.UDROP),
abi.encode(
address(new UDrop(root, token)),
abi.encodeCall(
UDrop.initialize, (address(authority), active)
)
)
)
);
}

function deployIBCHandler(
Manager manager
) internal returns (IBCHandler) {
Expand Down Expand Up @@ -910,6 +936,7 @@ contract GetDeployed is VersionedScript {
address ucs03 = getDeployed(Protocols.UCS03);

address u = getDeployed(string(INSTANCE_SALT.U));
address udrop = getDeployed(string(INSTANCE_SALT.UDROP));

console.log(
string(abi.encodePacked("Manager: ", manager.toHexString()))
Expand Down Expand Up @@ -953,6 +980,8 @@ contract GetDeployed is VersionedScript {
);
console.log(string(abi.encodePacked("UCS00: ", ucs00.toHexString())));
console.log(string(abi.encodePacked("UCS03: ", ucs03.toHexString())));
console.log(string(abi.encodePacked("U: ", u.toHexString())));
console.log(string(abi.encodePacked("UDrop: ", udrop.toHexString())));

string memory impls = "base";

Expand Down Expand Up @@ -987,7 +1016,27 @@ contract GetDeployed is VersionedScript {
)
)
);
impls.serialize(manager.toHexString(), proxyU);
impls.serialize(u.toHexString(), proxyU);

if (udrop.code.length > 0) {
string memory proxyUDrop = "proxyUDrop";
proxyUDrop.serialize(
"contract",
string(
"libs/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy"
)
);
proxyUDrop = proxyUDrop.serialize(
"args",
abi.encode(
implOf(udrop),
abi.encodeCall(
UDrop.initialize, (manager, UDrop(udrop).active())
)
)
);
impls.serialize(udrop.toHexString(), proxyUDrop);
}

string memory proxyMulticall = "proxyMulticall";
proxyMulticall.serialize(
Expand Down Expand Up @@ -1136,6 +1185,17 @@ contract GetDeployed is VersionedScript {
implU = implU.serialize("args", bytes(hex""));
impls.serialize(implOf(u).toHexString(), implU);

if (udrop.code.length > 0) {
string memory implUDrop = "implUDrop";
implUDrop.serialize(
"contract", string("contracts/tge/UDrop.sol:UDrop")
);
implUDrop = implUDrop.serialize(
"args", abi.encode(UDrop(udrop).ROOT(), UDrop(udrop).TOKEN())
);
impls.serialize(implOf(udrop).toHexString(), implUDrop);
}

string memory implHandler = "implHandler";
implHandler.serialize(
"contract",
Expand Down Expand Up @@ -2179,6 +2239,85 @@ contract DryDeployU is UnionScript, VersionedScript {
}
}

contract DeployUDrop is UnionScript, VersionedScript {
using LibString for *;

address immutable deployer;
address immutable sender;
bytes32 immutable root;
bool immutable active;

constructor() {
deployer = vm.envAddress("DEPLOYER");
sender = vm.envAddress("SENDER");
root = vm.envBytes32("MERKLE_ROOT");
active = vm.envBool("ACTIVE");
}

function getDeployer() internal view override returns (Deployer) {
return Deployer(deployer);
}

function getDeployed(
string memory salt
) internal view returns (address) {
return CREATE3.predictDeterministicAddress(
keccak256(abi.encodePacked(sender.toHexString(), "/", salt)),
deployer
);
}

function run() public {
uint256 privateKey = vm.envUint("PRIVATE_KEY");

Manager manager = Manager(getDeployed(IBC_SALT.MANAGER));
address token = getDeployed(string(INSTANCE_SALT.U));

vm.startBroadcast(privateKey);
UDrop udrop = deployUDrop(manager, root, token, active);
vm.stopBroadcast();

console.log("UDrop: ", address(udrop));
}
}

contract UpgradeUDrop is VersionedScript {
using LibString for *;

address immutable deployer;
address immutable sender;
uint256 immutable privateKey;

constructor() {
deployer = vm.envAddress("DEPLOYER");
sender = vm.envAddress("SENDER");
privateKey = vm.envUint("PRIVATE_KEY");
}

function getDeployed(
string memory salt
) internal view returns (address) {
return CREATE3.predictDeterministicAddress(
keccak256(abi.encodePacked(sender.toHexString(), "/", salt)),
deployer
);
}

function run() public {
UDrop udrop = UDrop(getDeployed(string(INSTANCE_SALT.UDROP)));

console.log(
string(abi.encodePacked("UDrop: ", address(udrop).toHexString()))
);

vm.startBroadcast(privateKey);
address newImplementation =
address(new UDrop(udrop.ROOT(), udrop.TOKEN()));
udrop.upgradeToAndCall(newImplementation, new bytes(0));
vm.stopBroadcast();
}
}

contract MintedAddress is VersionedScript {
using LibString for *;
using LibBytes for *;
Expand All @@ -2205,3 +2344,55 @@ contract MintedAddress is VersionedScript {
console.log(mintedAddress);
}
}

struct AirdropEntry {
address beneficiary;
uint256 amount;
}

contract SimpleMerkleTree is Script {
mapping(bytes32 => AirdropEntry) preimages;

function pushEntry(
address beneficiary,
uint256 amount
) internal returns (bytes32) {
bytes32 image =
EfficientHashLib.hash(abi.encodePacked(beneficiary, amount));
preimages[image] =
AirdropEntry({beneficiary: beneficiary, amount: amount});
return image;
}

function run() public {
bytes32[] memory leaves = new bytes32[](8);
leaves[0] = pushEntry(address(1), 1);
leaves[1] = pushEntry(address(2), 2);
leaves[2] =
pushEntry(address(0x50A22f95bcB21E7bFb63c7A8544AC0683dCeA302), 3);
leaves[3] = pushEntry(address(4), 4);
leaves[4] = pushEntry(address(5), 5);
leaves[5] =
pushEntry(address(0x2FB055fC77D751e2E6B7c88A1B404505154521c3), 6);
leaves[6] = pushEntry(address(7), 7);
leaves[7] = pushEntry(address(8), 8);
LibSort.sort(leaves);

bytes32[] memory tree = MerkleTreeLib.build(leaves);
bytes32 root = MerkleTreeLib.root(tree);
console.log("Root: ");
console.logBytes32(root);
for (uint256 i = 0; i < leaves.length; i++) {
bytes32[] memory proof = MerkleTreeLib.leafProof(tree, i);
console.log("==================================");
console.log("index: ");
console.log(i);
console.log("beneficiary: ");
console.log(preimages[leaves[i]].beneficiary);
console.log("amount: ");
console.log(preimages[leaves[i]].amount);
console.log("proof: ");
console.logBytes(abi.encode(proof));
}
}
}