-
Notifications
You must be signed in to change notification settings - Fork 11
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
HUBS-224 crowdsale with individual locking staking periods #170
base: main
Are you sure you want to change the base?
Changes from all commits
4780b1a
e080796
8d83cfc
f4dd2ad
d9a4102
a806488
0a8cb52
a6b7a19
ded1b66
33cab65
c816611
48afc39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.18; | ||
|
||
import "forge-std/Script.sol"; | ||
import "forge-std/console.sol"; | ||
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import { IPNFT } from "../../src/IPNFT.sol"; | ||
import { IPermissioner, TermsAcceptedPermissioner } from "../../src/Permissioner.sol"; | ||
import { StakedLockingCrowdSale } from "../../src/crowdsale/StakedLockingCrowdSale.sol"; | ||
import { LockingCrowdSale } from "../../src/crowdsale/LockingCrowdSale.sol"; | ||
import { TimelockedToken } from "../../src/TimelockedToken.sol"; | ||
import { TokenVesting } from "@moleculeprotocol/token-vesting/TokenVesting.sol"; | ||
|
||
TimelockedToken constant timelockedTokenImplementation = TimelockedToken(0xF8F79c1E02387b0Fc9DE0945cD9A2c06F127D851); | ||
address constant moleculeDevMultisig = 0x9d5a6ae551f1117946FF6e0e86ef9A1B20C90Cb0; | ||
|
||
//mainnet 0xCfA0F84660fB33bFd07C369E5491Ab02C449f71B; | ||
|
||
contract RolloutV25Sale is Script { | ||
function run() public { | ||
|
||
TokenVesting vesting = TokenVesting(0x8f80d1183CD983B01B0C9AC6777cC732Ec9800de); //Moldao | ||
|
||
vm.startBroadcast(); | ||
StakedLockingCrowdSale stakedLockingCrowdSale = new StakedLockingCrowdSale(timelockedTokenImplementation); | ||
vesting.grantRole(vesting.ROLE_CREATE_SCHEDULE(), address(stakedLockingCrowdSale)); | ||
//stakedLockingCrowdSale.trustLockingContract(IERC20()); | ||
stakedLockingCrowdSale.trustVestingContract(vesting); | ||
// stakedLockingCrowdSale.transferOwnership(moleculeDevMultisig); | ||
Comment on lines
+27
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Based on the search results, I can now provide a final response: Critical: Trust relationships and ownership transfer must be properly configured The commented lines in RolloutV25Sale.sol require attention:
Required actions:
🔗 Analysis chainCritical: Implement contract trust relationships The commented lines indicate missing trust relationships between contracts: //stakedLockingCrowdSale.trustLockingContract(IERC20());
stakedLockingCrowdSale.trustVestingContract(vesting);
//stakedLockingCrowdSale.transferOwnership(moleculeDevMultisig); These relationships are crucial for the crowdsale functionality. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify vesting contract configuration
# Check if vesting contract exists and has correct roles
echo "Checking vesting contract at 0x8f80d1183CD983B01B0C9AC6777cC732Ec9800de..."
cast call 0x8f80d1183CD983B01B0C9AC6777cC732Ec9800de "hasRole(bytes32,address)" \
$(cast --format-bytes32-string "ROLE_CREATE_SCHEDULE") \
0x2d309CF13dC3872f9c9B1B06Ebf6F60caDe08d55
Length of output: 526 Script: #!/bin/bash
# Let's analyze the contract relationships and dependencies
# Find all references to trustLockingContract and trustVestingContract
echo "Searching for trust relationships..."
rg -A 3 "trustLockingContract|trustVestingContract" --type solidity
# Find ownership transfer patterns
echo -e "\nSearching for ownership transfers..."
rg -A 3 "transferOwnership" --type solidity
# Find contract interfaces and implementations
echo -e "\nSearching for related contract definitions..."
rg -B 2 "contract StakedLockingCrowdSale|contract TokenVesting" --type solidity
Length of output: 573 Script: #!/bin/bash
# Let's analyze the contract relationships and dependencies without file type filter
# Find all references to trustLockingContract and trustVestingContract
echo "Searching for trust relationships..."
rg -A 3 "trustLockingContract|trustVestingContract"
# Find ownership transfer patterns
echo -e "\nSearching for ownership transfers..."
rg -A 3 "transferOwnership"
# Find contract interfaces and implementations
echo -e "\nSearching for related contract definitions..."
rg -B 2 "contract StakedLockingCrowdSale|contract TokenVesting"
# List all Solidity files to understand the contract structure
echo -e "\nListing Solidity files..."
fd -e sol
Length of output: 12463 |
||
vm.stopBroadcast(); | ||
|
||
console.log("STAKED_LOCKING_CROWDSALE_ADDRESS=%s", address(stakedLockingCrowdSale)); | ||
console.log("timelocked token implementation=%s", address(timelockedTokenImplementation)); | ||
// 0x7c36c64DA1c3a2065074caa9C48e7648FB733aAB | ||
// vestedDaoToken.grantRole(vestedDaoToken.ROLE_CREATE_SCHEDULE(), address(stakedLockingCrowdSale)); | ||
// stakedLockingCrowdSale.trustVestingContract(vestedDaoToken); | ||
} | ||
Comment on lines
+34
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Uncomment and implement role management The commented code appears to handle critical role assignments: // vestedDaoToken.grantRole(vestedDaoToken.ROLE_CREATE_SCHEDULE(), address(stakedLockingCrowdSale));
// stakedLockingCrowdSale.trustVestingContract(vestedDaoToken); This should either be:
Also applies to: 47-49 |
||
} | ||
|
||
contract RolloutV25LockingSale is Script { | ||
function run() public { | ||
|
||
vm.startBroadcast(); | ||
LockingCrowdSale lockingCrowdsale = new LockingCrowdSale(timelockedTokenImplementation); | ||
//lockingCrowdsale.transferOwnership(moleculeDevMultisig); | ||
vm.stopBroadcast(); | ||
Comment on lines
+44
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Implement ownership transfer The ownership transfer to the multisig is commented out: //lockingCrowdsale.transferOwnership(moleculeDevMultisig); This is a security risk as the deployer will retain control. |
||
|
||
console.log("LOCKING_CROWDSALE_ADDRESS=%s", address(lockingCrowdsale)); | ||
console.log("timelocked token implementation=%s", address(timelockedTokenImplementation)); | ||
// 0x7c36c64DA1c3a2065074caa9C48e7648FB733aAB | ||
// vestedDaoToken.grantRole(vestedDaoToken.ROLE_CREATE_SCHEDULE(), address(stakedLockingCrowdSale)); | ||
// stakedLockingCrowdSale.trustVestingContract(vestedDaoToken); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||
// SPDX-License-Identifier: MIT | ||||||||||||||||||||||||||||
pragma solidity 0.8.18; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||||||||||||||||||||||||||||
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||||||||||||||||||||||||||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||||||||||||||||||||||||||||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||||||||||||||||||||||||||||
|
@@ -12,6 +13,7 @@ import { CrowdSale, Sale } from "./CrowdSale.sol"; | |||||||||||||||||||||||||||
error UnsupportedInitializer(); | ||||||||||||||||||||||||||||
error InvalidDuration(); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||
* @title LockingCrowdSale | ||||||||||||||||||||||||||||
* @author molecule.to | ||||||||||||||||||||||||||||
|
@@ -25,16 +27,30 @@ contract LockingCrowdSale is CrowdSale { | |||||||||||||||||||||||||||
/// @notice map from token address to reusable TimelockedToken contracts | ||||||||||||||||||||||||||||
mapping(address => TimelockedToken) public lockingContracts; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
address immutable lockingTokenImplementation = address(new TimelockedToken()); | ||||||||||||||||||||||||||||
address immutable public TIMELOCKED_TOKEN_IMPLEMENTATION; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
event Started(uint256 indexed saleId, address indexed issuer, Sale sale, TimelockedToken lockingToken, uint256 lockingDuration, uint16 feeBp); | ||||||||||||||||||||||||||||
event LockingContractCreated(TimelockedToken indexed lockingContract, IERC20Metadata indexed underlyingToken); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
constructor(TimelockedToken _timelockedTokenImplementation) { | ||||||||||||||||||||||||||||
TIMELOCKED_TOKEN_IMPLEMENTATION = address(_timelockedTokenImplementation); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add address validation in constructor. The constructor should validate that the provided TimelockedToken implementation address is not zero to prevent potential deployment issues. constructor(TimelockedToken _timelockedTokenImplementation) {
+ require(address(_timelockedTokenImplementation) != address(0), "Invalid implementation address");
TIMELOCKED_TOKEN_IMPLEMENTATION = address(_timelockedTokenImplementation);
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/// @dev disable parent sale starting functions | ||||||||||||||||||||||||||||
function startSale(Sale calldata) public pure override returns (uint256) { | ||||||||||||||||||||||||||||
revert UnsupportedInitializer(); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||
* @notice allows the owner to trust a timelocked token contract for a specific underlying token so it's not registered again. | ||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||
* @param token the underlying token | ||||||||||||||||||||||||||||
* @param _timelockedToken the timelocked token contract to trust | ||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||
function trustLockingContract(IERC20 token, TimelockedToken _timelockedToken) public onlyOwner { | ||||||||||||||||||||||||||||
lockingContracts[address(token)] = _timelockedToken; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||
* @notice allows anyone to create a timelocked token that's controlled by this sale contract | ||||||||||||||||||||||||||||
* helpful if you want to reuse the timelocked token for your own custom schedules | ||||||||||||||||||||||||||||
|
@@ -114,7 +130,7 @@ contract LockingCrowdSale is CrowdSale { | |||||||||||||||||||||||||||
* @return lockedTokenContract address of the new timelocked token contract | ||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||
function _makeNewLockedTokenContract(IERC20Metadata auctionToken) private returns (TimelockedToken lockedTokenContract) { | ||||||||||||||||||||||||||||
lockedTokenContract = TimelockedToken(Clones.clone(lockingTokenImplementation)); | ||||||||||||||||||||||||||||
lockedTokenContract = TimelockedToken(Clones.clone(TIMELOCKED_TOKEN_IMPLEMENTATION)); | ||||||||||||||||||||||||||||
lockedTokenContract.initialize(auctionToken); | ||||||||||||||||||||||||||||
emit LockingContractCreated(lockedTokenContract, auctionToken); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+133
to
136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for external calls in _makeNewLockedTokenContract. The initialization call could fail but there's no error handling. Consider adding try-catch to handle potential initialization failures gracefully. function _makeNewLockedTokenContract(IERC20Metadata auctionToken) private returns (TimelockedToken lockedTokenContract) {
lockedTokenContract = TimelockedToken(Clones.clone(TIMELOCKED_TOKEN_IMPLEMENTATION));
- lockedTokenContract.initialize(auctionToken);
+ try lockedTokenContract.initialize(auctionToken) {
+ emit LockingContractCreated(lockedTokenContract, auctionToken);
+ } catch Error(string memory reason) {
+ revert(string.concat("Initialization failed: ", reason));
+ } catch {
+ revert("Initialization failed");
+ }
- emit LockingContractCreated(lockedTokenContract, auctionToken);
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Hardcoded addresses need to be updated for mainnet deployment
The hardcoded addresses in
RolloutV25Sale.sol
are currently set for the Sepolia testnet environment, as evidenced by:Please update these addresses with the appropriate mainnet values before deployment:
🔗 Analysis chain
Validate hardcoded contract addresses
The TimelockedToken implementation and multisig addresses should be validated before deployment.
Let me try to verify the addresses by searching for their usage in the codebase to understand the context better.
Let me search for more context about the TimelockedToken contract and its implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 1187
Script:
Length of output: 1750
Script:
Length of output: 7524