Skip to content
Open
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
5 changes: 5 additions & 0 deletions deployments/.base-sepolia-1742317640.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"deployments.callbacks.BatchCappedMerkleAllowlist": "0x98e489De04f8Ea4A93e5AD6862D8e8847781158d",
"deployments.callbacks.BatchMerkleAllowlist": "0x985942dEC200DD6dBFf2D60d3425299fbB0a07fE",
"deployments.callbacks.BatchAllocatedMerkleAllowlist": "0x985a5C530dd14F469b9032d5ca1B8B7CB6878109"
}
56 changes: 40 additions & 16 deletions script/deploy/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address atomicAuctionHouse = _getAddressNotZero("deployments.AtomicAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -451,10 +453,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(CappedMerkleAllowlist).creationCode,
abi.encode(atomicAuctionHouse, permissions)
abi.encode(atomicAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -482,6 +485,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -494,10 +499,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(CappedMerkleAllowlist).creationCode,
abi.encode(batchAuctionHouse, permissions)
abi.encode(batchAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -525,6 +531,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address atomicAuctionHouse = _getAddressNotZero("deployments.AtomicAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -537,10 +545,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(MerkleAllowlist).creationCode,
abi.encode(atomicAuctionHouse, permissions)
abi.encode(atomicAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -568,6 +577,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -580,10 +591,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(MerkleAllowlist).creationCode,
abi.encode(batchAuctionHouse, permissions)
abi.encode(batchAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -611,6 +623,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address atomicAuctionHouse = _getAddressNotZero("deployments.AtomicAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -623,10 +637,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(TokenAllowlist).creationCode,
abi.encode(atomicAuctionHouse, permissions)
abi.encode(atomicAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -654,6 +669,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -666,10 +683,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(TokenAllowlist).creationCode,
abi.encode(batchAuctionHouse, permissions)
abi.encode(batchAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -697,6 +715,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address atomicAuctionHouse = _getAddressNotZero("deployments.AtomicAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -709,10 +729,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(AllocatedMerkleAllowlist).creationCode,
abi.encode(atomicAuctionHouse, permissions)
abi.encode(atomicAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down Expand Up @@ -740,6 +761,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse");
string memory deploymentKey = _getDeploymentKey(sequenceName_);
console2.log(" deploymentKey:", deploymentKey);

// 10011000 = 0x98
Callbacks.Permissions memory permissions = Callbacks.Permissions({
onCreate: true,
onCancel: false,
Expand All @@ -752,10 +775,11 @@ contract Deploy is Script, WithDeploySequence, WithSalts {
});

// Get the salt
bytes32 salt_ = _getSalt(
bytes32 salt_ = _generateSalt(
deploymentKey,
type(AllocatedMerkleAllowlist).creationCode,
abi.encode(batchAuctionHouse, permissions)
abi.encode(batchAuctionHouse, permissions),
"98"
);

// Revert if the salt is not set
Expand Down
6 changes: 3 additions & 3 deletions script/env.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@
},
"deployments": {
"callbacks": {
"BatchAllocatedMerkleAllowlist": "0x98C8ffFf24bcfC3A5B0b463c43F10932Cedb7B8F",
"BatchCappedMerkleAllowlist": "0x9859AcCA8a9afEbb9b3986036d4E0efc0246cEeA",
"BatchMerkleAllowlist": "0x98d64E00D9d6550913E73C940Ff476Cf1723d834",
"BatchAllocatedMerkleAllowlist": "0x985a5C530dd14F469b9032d5ca1B8B7CB6878109",
"BatchCappedMerkleAllowlist": "0x98e489De04f8Ea4A93e5AD6862D8e8847781158d",
"BatchMerkleAllowlist": "0x985942dEC200DD6dBFf2D60d3425299fbB0a07fE",
"BatchTokenAllowlist": "0x9801e45362a2bb7C9F22486CC3F5cA9224e9CC55",
"BatchUniswapV2DirectToLiquidity": "0xE6546c03B1b9DFC4238f0A2923FdefD5E4af7659",
"BatchUniswapV3DirectToLiquidity": "0xE68b21C071534781BC4c40E6BF1bCFC23638fF4B"
Expand Down
8 changes: 8 additions & 0 deletions src/callbacks/allowlists/AllocatedMerkleAllowlist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ contract AllocatedMerkleAllowlist is MerkleAllowlist {
) internal {
// Validate that the buyer is allowed to participate

// If the merkle root is zero, anyone can participate
// Given anyone can spin up a new wallet, it also doesn't make sense to have a buyer limit
if (lotMerkleRoot[lotId_] == bytes32(0)) {
// Update the buyer's spent amount
lotBuyerSpent[lotId_][buyer_] += amount_;
return;
}

// Decode the merkle proof and allocated amount from buyer submitted callback data
(bytes32[] memory proof, uint256 allocatedAmount) =
abi.decode(callbackData_, (bytes32[], uint256));
Expand Down
14 changes: 13 additions & 1 deletion src/callbacks/allowlists/CappedMerkleAllowlist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract CappedMerkleAllowlist is MerkleAllowlist {
/// @param callbackData_ abi-encoded data: (bytes32, uint256) representing the merkle root and buyer limit
function _onCreate(
uint96 lotId_,
address,
address seller_,
address,
address,
uint256,
Expand All @@ -67,6 +67,10 @@ contract CappedMerkleAllowlist is MerkleAllowlist {
lotMerkleRoot[lotId_] = merkleRoot;
lotBuyerLimit[lotId_] = buyerLimit;
emit MerkleRootSet(lotId_, merkleRoot);

// Set the lot admin to the seller address
lotAdmin[lotId_] = seller_;
emit LotAdminSet(lotId_, seller_);
}

/// @inheritdoc MerkleAllowlist
Expand Down Expand Up @@ -95,6 +99,14 @@ contract CappedMerkleAllowlist is MerkleAllowlist {
// ========== INTERNAL FUNCTIONS ========== //

function _canBuy(uint96 lotId_, address buyer_, uint256 amount_) internal {
// If the merkle root is zero, anyone can participate
if (lotMerkleRoot[lotId_] == bytes32(0)) {
// Update the buyer spent amount
// Given anyone can spin up a new wallet, it also doesn't make sense to have a buyer limit
lotBuyerSpent[lotId_][buyer_] += amount_;
return;
}

// Check if the buyer has already spent their limit
if (lotBuyerSpent[lotId_][buyer_] + amount_ > lotBuyerLimit[lotId_]) {
revert Callback_ExceedsLimit();
Expand Down
58 changes: 43 additions & 15 deletions src/callbacks/allowlists/MerkleAllowlist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@ import {MerkleProof} from "@openzeppelin-contracts-4.9.2/utils/cryptography/Merk
import {BaseCallback} from "@axis-core-1.0.4/bases/BaseCallback.sol";
import {Callbacks} from "@axis-core-1.0.4/lib/Callbacks.sol";

import {IAuctionHouse} from "@axis-core-1.0.4/interfaces/IAuctionHouse.sol";
import {IMerkleAllowlist} from "./interfaces/IMerkleAllowlist.sol";

/// @title MerkleAllowlist
/// @notice This contract implements a merkle tree-based allowlist for buyers to participate in an auction.
/// In this implementation, buyers do not have a limit on the amount they can purchase/bid.
contract MerkleAllowlist is BaseCallback {
// ========== EVENTS ========== //

/// @notice Emitted when the merkle root is set
event MerkleRootSet(uint96 lotId, bytes32 merkleRoot);

contract MerkleAllowlist is BaseCallback, IMerkleAllowlist {
// ========== STATE VARIABLES ========== //

/// @notice The root of the merkle tree that represents the allowlist for a lot
/// @dev The merkle tree should adhere to the format specified in the OpenZeppelin MerkleProof library at https://github.com/OpenZeppelin/merkle-tree
/// In particular, leaf values (such as `(address)` or `(address,uint256)`) should be double-hashed.
mapping(uint96 lotId => bytes32 merkleRoot) public lotMerkleRoot;

/// @notice The admin for the given lot id
/// @dev The admin is permitted to set the merkle root
mapping(uint96 lotId => address admin) public lotAdmin;

// ========== CONSTRUCTOR ========== //

// PERMISSIONS
Expand Down Expand Up @@ -54,10 +53,11 @@ contract MerkleAllowlist is BaseCallback {
/// - The callback data is of an invalid length
///
/// @param lotId_ The id of the lot
/// @param seller_ The address of the seller
/// @param callbackData_ abi-encoded data: (bytes32) representing the merkle root
function _onCreate(
uint96 lotId_,
address,
address seller_,
address,
address,
uint256,
Expand All @@ -75,6 +75,10 @@ contract MerkleAllowlist is BaseCallback {
// Set the merkle root
lotMerkleRoot[lotId_] = merkleRoot;
emit MerkleRootSet(lotId_, merkleRoot);

// Set the lot admin to the seller address
lotAdmin[lotId_] = seller_;
emit LotAdminSet(lotId_, seller_);
}

/// @inheritdoc BaseCallback
Expand Down Expand Up @@ -180,6 +184,11 @@ contract MerkleAllowlist is BaseCallback {
address buyer_,
bytes calldata callbackData_
) internal view virtual {
// If the merkle root is zero, anyone can participate
if (lotMerkleRoot[lotId_] == bytes32(0)) {
return;
}

// Decode the merkle proof from the callback data
bytes32[] memory proof = abi.decode(callbackData_, (bytes32[]));

Expand All @@ -206,18 +215,37 @@ contract MerkleAllowlist is BaseCallback {
/// - The auction has not been registered
///
/// @param merkleRoot_ The new merkle root
function setMerkleRoot(uint96 lotId_, bytes32 merkleRoot_) external onlyRegisteredLot(lotId_) {
// We check that the lot is registered on this callback

// Check that the caller is the lot's seller
(address seller,,,,,,,,) = IAuctionHouse(AUCTION_HOUSE).lotRouting(lotId_);
if (msg.sender != seller) {
function setMerkleRoot(
uint96 lotId_,
bytes32 merkleRoot_
) external override onlyRegisteredLot(lotId_) {
// Check that the caller is the lot's admin
if (lotAdmin[lotId_] != msg.sender) {
revert Callback_NotAuthorized();
}

// Set the new merkle root and emit an event
lotMerkleRoot[lotId_] = merkleRoot_;

emit MerkleRootSet(lotId_, merkleRoot_);
}

/// @inheritdoc IMerkleAllowlist
function setLotAdmin(
uint96 lotId_,
address admin_
) external override onlyRegisteredLot(lotId_) {
// Validate that the caller is the lot's current admin
if (lotAdmin[lotId_] != msg.sender) {
revert Callback_NotAuthorized();
}

// Validate that the address is not the zero address
if (admin_ == address(0)) {
revert Callback_InvalidParams();
}

// Set the new admin
lotAdmin[lotId_] = admin_;
emit LotAdminSet(lotId_, admin_);
}
}
Loading
Loading