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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "lib/allo-v2.1"]
path = lib/allo-v2.1
url = https://github.com/allo-protocol/allo-v2.1.git
36 changes: 36 additions & 0 deletions compiler_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"language": "Solidity",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
},
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"strategies/=lib/allo-v2.1/contracts/strategies/",
"libraries/=lib/allo-v2.1/contracts/core/libraries/",
"solady/=lib/allo-v2.1/lib/solady/src/"
]
}
}
7 changes: 5 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ optimizer_runs = 200
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

remappings = [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"strategies/=lib/allo-v2.1/contracts/strategies/",
"libraries/=lib/allo-v2.1/contracts/core/libraries/",
"solady/=lib/allo-v2.1/lib/solady/src/",
]
1 change: 1 addition & 0 deletions lib/allo-v2.1
Submodule allo-v2.1 added at 258f3c
112 changes: 112 additions & 0 deletions src/HyperStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {AccessControl} from "lib/allo-v2.1/lib/openzeppelin-contracts/contracts/access/AccessControl.sol";
import {BaseStrategy} from "strategies/BaseStrategy.sol";
import {IAllo} from "lib/allo-v2.1/contracts/core/interfaces/IAllo.sol";
import {IHypercertToken} from "./interfaces/IHypercertToken.sol";
import {IHyperfund} from "./interfaces/IHyperfund.sol";

contract HyperStrategy is AccessControl, BaseStrategy {
// Roles
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

// Interfaces
IHyperfund public hyperfund;
IHypercertToken public hypercertMinter;

// Events
event Donated(address indexed donor, address token, uint256 amount, uint256 indexed hypercertUnits);

// Errors
error NOOP();

function initialize(uint256 _poolId, bytes memory _data) external virtual override {
(address _manager, address _hyperfund) = abi.decode(_data, (address, address));

hyperfund = IHyperfund(_hyperfund);
hypercertMinter = IHypercertToken(hyperfund.hypercertMinter());

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MANAGER_ROLE, _manager);
__BaseStrategy_init(_poolId);
emit Initialized(_poolId, _data);
}
Comment on lines +24 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent multiple calls to initialize(...)
You’re using a standalone initialize function, but there’s no safeguard against being called more than once. To avoid re-initialization issues and maintain security, consider using initializer modifiers (e.g., from OpenZeppelin) or adding a guard to ensure it’s invoked only once.


/// ===============================
/// ========= Constructor =========
/// ===============================
constructor(address _allo, string memory _name) BaseStrategy(_allo, _name) {}

/// @notice Allocate funds hyperfund and hyperstaker pools
/// @param _data The data to decode
/// @param _sender The sender
function _allocate(bytes memory _data, address _sender) internal virtual override onlyRole(MANAGER_ROLE) {
(address[] memory _recipients, uint256[] memory _amounts) = abi.decode(_data, (address[], uint256[]));

// Assert recipient and amounts length are equal
if (_recipients.length != _amounts.length) {
revert ARRAY_MISMATCH();
}

IAllo.Pool memory pool = allo.getPool(poolId);
for (uint256 i; i < _recipients.length; ++i) {
uint256 _amount = _amounts[i];
address _recipientAddress = _recipients[i];

_transferAmount(pool.token, _recipientAddress, _amount);

emit Allocated(_recipientAddress, _amount, pool.token, _sender);
}
}

function _afterIncreasePoolAmount(uint256 _amount) internal virtual override {
IAllo.Pool memory pool = allo.getPool(poolId);
uint256 hypercertFraction = hyperfund.hypercertId();
int256 multiplier = hyperfund.tokenMultipliers(pool.token);
uint256 units;
if (multiplier > 0) {
units = _amount * uint256(multiplier);
} else {
units = _amount / uint256(-multiplier);
}

uint256 availableSupply = hypercertMinter.unitsOf(hypercertFraction);
require(availableSupply >= units);
_mintFraction(tx.origin, units, hypercertFraction);
emit Donated(tx.origin, pool.token, _amount, units);
}

function _distribute(address[] memory _recipientIds, bytes memory _recipientAmounts, address _sender)
internal
virtual
override
{
revert NOOP();
}

function _getRecipientStatus(address) internal view virtual override returns (Status) {
revert NOOP();
}

function _isValidAllocator(address _allocator) internal view virtual override returns (bool) {}

function _registerRecipient(bytes memory _data, address _sender) internal virtual override returns (address) {}

function _getPayout(address _recipientId, bytes memory _data)
internal
view
virtual
override
returns (PayoutSummary memory)
{}

function _mintFraction(address account, uint256 units, uint256 hypercertId) internal {
uint256[] memory newallocations = new uint256[](2);
newallocations[0] = hypercertMinter.unitsOf(hypercertId) - units;
newallocations[1] = units;
hypercertMinter.splitFraction(account, hypercertId, newallocations);
}

receive() external payable {}
}
29 changes: 29 additions & 0 deletions src/HyperstrategyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Creating a seperate factory for compatibility issues
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./HyperStrategy.sol"; // Import Hyperstrategy contract

contract HyperStrategyFactory {
address hypercertMinter;

// Event to emit when a new HyperStrategy is created
event HyperstrategyCreated(address indexed hyperstrategyAddress, address allo);

constructor(address _hypercertMinter) {
require(_hypercertMinter != address(0));
hypercertMinter = _hypercertMinter;
}

// Function to create a new Hyperstrategy
function createHyperstrategy(address _allo, string memory _name) external returns (address) {
require(_allo != address(0));

address newHyperStrategy = address(new HyperStrategy(_allo, _name));
require(newHyperStrategy != address(0));

IHypercertToken(hypercertMinter).setApprovalForAll(newHyperStrategy, true);
emit HyperstrategyCreated(newHyperStrategy, _allo);
return newHyperStrategy;
}
}
2 changes: 1 addition & 1 deletion src/interfaces/IHypercertToken.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
pragma solidity ^0.8.18;

interface IHypercertToken {
/**
Expand Down
8 changes: 8 additions & 0 deletions src/interfaces/IHyperfund.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

interface IHyperfund {
function hypercertId() external view returns (uint256);
function tokenMultipliers(address token) external view returns (int256);
function hypercertMinter() external view returns (address);
}
Comment on lines +4 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add documentation to explain interface functions

The interface lacks documentation explaining the purpose and expected behavior of each function. Consider adding NatSpec comments to improve code readability and maintainability.

 interface IHyperfund {
+    /**
+     * @notice Returns the ID of the hypercert associated with this fund
+     * @return The hypercert ID
+     */
     function hypercertId() external view returns (uint256);
+    
+    /**
+     * @notice Returns the multiplier for a given token
+     * @param token The address of the token
+     * @return The multiplier value for the token (can be negative)
+     */
     function tokenMultipliers(address token) external view returns (int256);
+    
+    /**
+     * @notice Returns the address of the hypercert minter contract
+     * @return The address of the hypercert minter
+     */
     function hypercertMinter() external view returns (address);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface IHyperfund {
function hypercertId() external view returns (uint256);
function tokenMultipliers(address token) external view returns (int256);
function hypercertMinter() external view returns (address);
}
interface IHyperfund {
/**
* @notice Returns the ID of the hypercert associated with this fund
* @return The hypercert ID
*/
function hypercertId() external view returns (uint256);
/**
* @notice Returns the multiplier for a given token
* @param token The address of the token
* @return The multiplier value for the token (can be negative)
*/
function tokenMultipliers(address token) external view returns (int256);
/**
* @notice Returns the address of the hypercert minter contract
* @return The address of the hypercert minter
*/
function hypercertMinter() external view returns (address);
}

Loading