Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SealedBidTokenSale #338

Merged
merged 28 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7798d4a
Add SealedBidTokenSale contract and specification for a sealed-bid, m…
ylv-io Jan 29, 2025
c07793d
Remove outdated ownership and treasury details from SealedBidTokenSal…
ylv-io Jan 29, 2025
7564bfc
Remove maximum cap check and associated error handling from SealedBid…
ylv-io Jan 29, 2025
a6c2da7
Refactor SealedBidTokenSale contract: update OpenZeppelin imports, us…
ylv-io Jan 29, 2025
57a28de
Refactor SealedBidTokenSale constructor to use custom errors for inva…
ylv-io Jan 29, 2025
019d61b
Refactor SealedBidTokenSale contract: enhance error handling, update …
ylv-io Jan 29, 2025
32dbb38
Remove unnecessary whitespace in SealedBidTokenSale.sol for improved …
ylv-io Jan 29, 2025
f4ef095
Add sale token address validation and initialization in SealedBidToke…
ylv-io Jan 29, 2025
d10c5e6
Refactor SealedBidTokenSale contract to enhance error handling with d…
ylv-io Jan 31, 2025
bd49189
Refactor SealedBidTokenSale test contract: rename file, update Merkle…
ylv-io Feb 3, 2025
f7d4689
Refactor SealedBidTokenSale contract: remove endTime, rename function…
ylv-io Feb 3, 2025
9acb3df
Remove maximumCap from SealedBidTokenSale contract and update related…
ylv-io Feb 3, 2025
bb7e110
Refactor SealedBidTokenSale contract and test to include USDC allocat…
ylv-io Feb 3, 2025
2621d28
Refactor BridgerL2.t.sol by moving 'deal' function call for gasFee af…
ylv-io Feb 3, 2025
18ba6f6
Refactor SealedBidTokenSale contract to replace 'successful' with 'ca…
ylv-io Feb 3, 2025
5e239a7
Refactor SealedBidTokenSale contract to include user address in claim…
ylv-io Feb 3, 2025
f3f1a4c
Refactor error handling in SealedBidTokenSale contract and enhance un…
ylv-io Feb 3, 2025
25e2059
Refactor SealedBidTokenSale contract to enhance documentation, improv…
ylv-io Feb 3, 2025
9107906
Refactor error handling and add comments for clarity in SealedBidToke…
ylv-io Feb 3, 2025
2c16f05
Refactor SealedBidTokenSale: simplify timing, remove max cap, add dua…
ylv-io Feb 3, 2025
3e6417d
Removed the "Key Design Changes" section from SealedBidTokenSale.md, …
ylv-io Feb 3, 2025
2d2c705
Add SaleInfo struct and saleStatus function to SealedBidTokenSale con…
ylv-io Feb 4, 2025
c94a695
Add maxPrice functionality to SealedBidTokenSale contract and update …
ylv-io Feb 4, 2025
b266e46
Refactor deposit logic to enforce a minimum deposit amount and update…
ylv-io Feb 6, 2025
aef2a44
Add max price range check in SealedBidTokenSale and update unit tests…
ylv-io Feb 6, 2025
bb32eed
Add early participation window for emissaries in SealedBidTokenSale c…
ylv-io Feb 6, 2025
6bfa48e
Refactor SealedBidTokenSale contract to support UUPS upgradeability, …
ylv-io Feb 8, 2025
f135245
Add SealedBidTokenSale deployment script, update artifacts with new c…
ylv-io Feb 8, 2025
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
302 changes: 302 additions & 0 deletions broadcast/155-deploy-sale.s.sol/7887/run-1739030918.json

Large diffs are not rendered by default.

302 changes: 302 additions & 0 deletions broadcast/155-deploy-sale.s.sol/7887/run-latest.json

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions script/migrations/155-deploy-sale.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {SealedBidTokenSale} from "@kinto-core/apps/SealedBidTokenSale.sol";

import {SafeBeaconProxy} from "@kinto-core/proxy/SafeBeaconProxy.sol";
import {KintoAppRegistry} from "@kinto-core/apps/KintoAppRegistry.sol";

import {UUPSProxy} from "@kinto-core-test/helpers/UUPSProxy.sol";
import {MigrationHelper} from "@kinto-core-script/utils/MigrationHelper.sol";

import "@kinto-core-test/helpers/ArrayHelpers.sol";
import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";

contract DeployScript is Script, MigrationHelper {
using ArrayHelpers for *;

address public constant SOCKET_APP = 0x3e9727470C66B1e77034590926CDe0242B5A3dCc;
address public constant KINTO = 0x010700808D59d2bb92257fCafACfe8e5bFF7aB87;
address public constant TREASURY = 0x793500709506652Fcc61F0d2D0fDa605638D4293;
address public constant USDC = 0x05DC0010C9902EcF6CBc921c6A4bd971c69E5A2E;
//Tuesday, Feb 11, 2025, 10:00:00 AM PT.
uint256 public constant PRE_START_TIME = 1739296800;
//Tuesday, Feb 18, 2025, 10:00:00 AM PT.
uint256 public constant START_TIME = 1739901600;
uint256 public constant MINIMUM_CAP = 250_000 * 1e6;

function run() public override {
super.run();

if (_getChainDeployment("SealedBidTokenSale") != address(0)) {
console2.log("SealedBidTokenSale is deployed");
return;
}

vm.broadcast(deployerPrivateKey);
SealedBidTokenSale impl = new SealedBidTokenSale(KINTO, TREASURY, USDC, PRE_START_TIME, START_TIME, MINIMUM_CAP);

(bytes32 salt, address expectedAddress) =
mineSalt(keccak256(abi.encodePacked(type(UUPSProxy).creationCode, abi.encode(address(impl), ""))), "5A1E00");

vm.broadcast(deployerPrivateKey);
UUPSProxy proxy = new UUPSProxy{salt: salt}(address(impl), "");
SealedBidTokenSale sale = SealedBidTokenSale(address(proxy));

_handleOps(
abi.encodeWithSelector(
KintoAppRegistry.addAppContracts.selector, SOCKET_APP, [address(sale)].toMemoryArray()
),
address(_getChainDeployment("KintoAppRegistry"))
);

uint256[] memory privateKeys = new uint256[](1);
privateKeys[0] = deployerPrivateKey;
_handleOps(
abi.encodeWithSelector(SealedBidTokenSale.initialize.selector),
payable(kintoAdminWallet),
address(proxy),
0,
address(0),
privateKeys
);

assertEq(address(sale), address(expectedAddress));
assertEq(address(sale.USDC()), USDC);

saveContractAddress("SealedBidTokenSale", address(sale));
saveContractAddress("SealedBidTokenSale-impl", address(impl));
}
}
164 changes: 164 additions & 0 deletions src/apps/SealedBidTokenSale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# SealedBidTokenSale Technical Specification

Below is the **technical specification** for a Solidity **sealed‐bid** token sale contract.

---

## 1. **Contract Overview**

- **Name**: `SealedBidTokenSale`
- **Purpose**: Accept USDC deposits for a token sale, enforce timing and minimum cap requirements, enable refunds if the sale is unsuccessful, and distribute tokens and USDC allocations via a Merkle proof if successful.
- **Inheritance**: `Ownable`, `ReentrancyGuard`

---

## 2. **Key Roles**

- **Owner**:
- Inherits from OpenZeppelin `Ownable`.
- Sets crucial parameters during contract deployment.
- Controls sale finalization, Merkle root setting, and proceeds withdrawal.

- **Participants**:
- Deposit USDC during the sale window.
- Withdraw their deposit if the sale fails.
- Claim tokens and USDC allocations after sale success using a Merkle proof.

- **Treasury**:
- Immutable address specified at deployment.
- Receives USDC proceeds upon successful sale completion.

---

## 3. **Immutable Parameters**

1. **`saleToken`** (`IERC20`)
- Token being sold through the contract.
- Set at construction.

2. **`USDC`** (`IERC20`)
- USDC token contract reference for deposits.
- Set at construction.

3. **`treasury`** (`address`)
- Fixed address that receives proceeds.
- Set at construction.

4. **`startTime`** (`uint256`)
- Sale start timestamp.
- Set at construction.

5. **`minimumCap`** (`uint256`)
- Minimum USDC required for success.
- Set at construction.

---

## 4. **State Variables**

1. **`saleEnded`** (`bool`)
- Indicates if owner has ended the sale.

2. **`capReached`** (`bool`)
- Set to `true` if `totalDeposited >= minimumCap` when sale ends.

3. **`totalDeposited`** (`uint256`)
- Sum of all USDC deposits.

4. **`merkleRoot`** (`bytes32`)
- Root hash for token and USDC allocation proofs.

5. **`deposits`** (`mapping(address => uint256)`)
- Tracks each user's USDC deposit amount.

6. **`hasClaimed`** (`mapping(address => bool)`)
- Records whether an address has claimed their allocation.

---

## 5. **Core Functions**

### 5.1 **`deposit(uint256 amount)`**
- **Purpose**: Accepts USDC deposits from participants.
- **Constraints**:
1. Must be after `startTime`.
2. Sale must not be ended.
3. Amount must be non-zero.
- **Effects**:
- Updates `deposits[msg.sender]` and `totalDeposited`.
- Transfers USDC from sender to contract.
- Emits `Deposited` event.

### 5.2 **`withdraw()`**
- **Purpose**: Returns USDC to depositors if sale fails.
- **Constraints**:
1. Sale must be ended.
2. Cap must not be reached.
3. Caller must have non-zero deposit.
- **Effects**:
- Returns user's entire USDC deposit.
- Zeroes their deposit balance.
- Emits `Withdrawn` event.

### 5.3 **`endSale()`** (Owner-only)
- **Purpose**: Finalizes sale and determines success.
- **Constraints**:
1. Only callable by owner.
2. Sale must not already be ended.
- **Effects**:
- Sets `saleEnded = true`.
- Sets `capReached` based on minimum cap check.
- Emits `SaleEnded` event.

### 5.4 **`claimTokens(uint256 saleTokenAllocation, uint256 usdcAllocation, bytes32[] calldata proof, address user)`**
- **Purpose**: Processes token and USDC claims using Merkle proofs.
- **Constraints**:
1. Sale must be ended and successful.
2. Merkle root must be set.
3. User must not have claimed.
4. Valid Merkle proof required.
- **Effects**:
- Marks user as claimed.
- Transfers allocated sale tokens.
- Returns allocated USDC.
- Emits `Claimed` event.

### 5.5 **`setMerkleRoot(bytes32 newRoot)`** (Owner-only)
- **Purpose**: Sets allocation Merkle root.
- **Constraints**:
1. Sale must be ended and successful.
2. Only callable by owner.
- **Effects**:
- Sets `merkleRoot`.
- Emits `MerkleRootSet` event.

### 5.6 **`withdrawProceeds()`** (Owner-only)
- **Purpose**: Sends USDC to treasury.
- **Constraints**:
1. Sale must be ended and successful.
2. Only callable by owner.
- **Effects**:
- Transfers all USDC to treasury address.

---

## 6. **Custom Errors**

1. **Parameter Validation**:
- `InvalidSaleTokenAddress`
- `InvalidTreasuryAddress`
- `ZeroDeposit`

2. **State Checks**:
- `SaleNotStarted`
- `SaleAlreadyEnded`
- `SaleNotEnded`
- `CapNotReached`
- `SaleWasSuccessful`

3. **Claim Validation**:
- `NothingToWithdraw`
- `AlreadyClaimed`
- `InvalidProof`
- `MerkleRootNotSet`

Loading
Loading