From 18079e00a5892f420211da6e64aa12e917e113c9 Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Sat, 8 Mar 2025 23:41:07 +0530 Subject: [PATCH 1/8] Add: strategy --- .gitmodules | 3 ++ foundry.toml | 7 +++- lib/allo-v2.1 | 1 + src/HyperStrategy.sol | 89 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 160000 lib/allo-v2.1 create mode 100644 src/HyperStrategy.sol diff --git a/.gitmodules b/.gitmodules index 9296efd..b1a15eb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/foundry.toml b/foundry.toml index f28298c..b1247ee 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,6 +6,9 @@ libs = ["lib"] # 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/", ] diff --git a/lib/allo-v2.1 b/lib/allo-v2.1 new file mode 160000 index 0000000..258f3c6 --- /dev/null +++ b/lib/allo-v2.1 @@ -0,0 +1 @@ +Subproject commit 258f3c6aa2823577e3e1f95b1dbed8b6d9482a1d diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol new file mode 100644 index 0000000..d5ded75 --- /dev/null +++ b/src/HyperStrategy.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {AccessControlUpgradeable} from "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol"; +import {UUPSUpgradeable} from "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; +import {BaseStrategy} from "strategies/BaseStrategy.sol"; +import {IAllo} from "lib/allo-v2.1/contracts/core/interfaces/IAllo.sol"; + + +contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrategy { + + // Roles + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + + error NOOP(); + + function initialize(uint256 _poolId, bytes memory _data) public initializer { + __AccessControl_init(); + __UUPSUpgradeable_init(); + + address _manager = abi.decode(_data, (address)); + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(MANAGER_ROLE, _manager); + __BaseStrategy_init(_poolId); + emit Initialized(_poolId, _data); + } + + /// =============================== + /// ========= Constructor ========= + /// =============================== + constructor(address _allo, string memory _name) BaseStrategy(_allo, _name) {} + + function _authorizeUpgrade(address newImplementation) internal override onlyRole(MANAGER_ROLE) {} + + /// @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 _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) + {} + + receive() external payable {} +} \ No newline at end of file From f5032250ee717a06138a63c4b5ffccbdffaa1136 Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Sun, 9 Mar 2025 00:10:02 +0530 Subject: [PATCH 2/8] fmt --- src/HyperStrategy.sol | 46 +++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index d5ded75..fcd3bc3 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {AccessControlUpgradeable} from "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol"; -import {UUPSUpgradeable} from "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; +import {AccessControlUpgradeable} from + "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol"; +import {UUPSUpgradeable} from + "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; import {BaseStrategy} from "strategies/BaseStrategy.sol"; import {IAllo} from "lib/allo-v2.1/contracts/core/interfaces/IAllo.sol"; - contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrategy { - // Roles bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); @@ -25,7 +25,7 @@ contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrateg __BaseStrategy_init(_poolId); emit Initialized(_poolId, _data); } - + /// =============================== /// ========= Constructor ========= /// =============================== @@ -36,29 +36,23 @@ contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrateg /// @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[])); + 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(); - } + // 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]; + 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); - } + _transferAmount(pool.token, _recipientAddress, _amount); + emit Allocated(_recipientAddress, _amount, pool.token, _sender); + } } function _distribute(address[] memory _recipientIds, bytes memory _recipientAmounts, address _sender) @@ -66,7 +60,7 @@ contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrateg virtual override { - revert NOOP(); + revert NOOP(); } function _getRecipientStatus(address) internal view virtual override returns (Status) { @@ -86,4 +80,4 @@ contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrateg {} receive() external payable {} -} \ No newline at end of file +} From a9f9710cb1740e1fbe6bcdccdd7542eea831fc1a Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Sun, 9 Mar 2025 01:46:22 +0530 Subject: [PATCH 3/8] Degrade to non-upgradeable --- compiler_config.json | 36 ++++++++++++++++++++++++++++++++++++ src/HyperStrategy.sol | 15 ++++----------- 2 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 compiler_config.json diff --git a/compiler_config.json b/compiler_config.json new file mode 100644 index 0000000..1e4845d --- /dev/null +++ b/compiler_config.json @@ -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/" + ] + } +} \ No newline at end of file diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index fcd3bc3..cac6a5b 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -1,23 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {AccessControlUpgradeable} from - "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol"; -import {UUPSUpgradeable} from - "lib/allo-v2.1/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; +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"; -contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrategy { +contract HyperStrategy is AccessControl, BaseStrategy { // Roles bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); error NOOP(); - function initialize(uint256 _poolId, bytes memory _data) public initializer { - __AccessControl_init(); - __UUPSUpgradeable_init(); - + function initialize(uint256 _poolId, bytes memory _data) external virtual override { address _manager = abi.decode(_data, (address)); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); @@ -31,8 +26,6 @@ contract HyperStrategy is AccessControlUpgradeable, UUPSUpgradeable, BaseStrateg /// =============================== constructor(address _allo, string memory _name) BaseStrategy(_allo, _name) {} - function _authorizeUpgrade(address newImplementation) internal override onlyRole(MANAGER_ROLE) {} - /// @notice Allocate funds hyperfund and hyperstaker pools /// @param _data The data to decode /// @param _sender The sender From d9ec3f55cf197514cc157b862824d258195139f6 Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Sun, 9 Mar 2025 02:19:53 +0530 Subject: [PATCH 4/8] fmt --- src/HyperStrategy.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index cac6a5b..1299aca 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {AccessControl} from - "lib/allo-v2.1/lib/openzeppelin-contracts/contracts/access/AccessControl.sol"; +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"; From ea7b4210c0c999656da2f7ecd39752e4da986fdd Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Thu, 20 Mar 2025 12:07:53 +0530 Subject: [PATCH 5/8] Add: _afterIncreasePoolAmount --- src/HyperStrategy.sol | 34 +++++++++++++++++++++++++++++- src/interfaces/IHypercertToken.sol | 2 +- src/interfaces/IHyperfund.sol | 8 +++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/interfaces/IHyperfund.sol diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index 1299aca..3cc38b8 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -4,15 +4,24 @@ 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"); + IHyperfund public hyperfund; + + IHypercertToken public hypercertMinter; + error NOOP(); function initialize(uint256 _poolId, bytes memory _data) external virtual override { - address _manager = abi.decode(_data, (address)); + (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); @@ -47,6 +56,22 @@ contract HyperStrategy is AccessControl, BaseStrategy { } } + 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); + } + function _distribute(address[] memory _recipientIds, bytes memory _recipientAmounts, address _sender) internal virtual @@ -71,5 +96,12 @@ contract HyperStrategy is AccessControl, BaseStrategy { 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 {} } diff --git a/src/interfaces/IHypercertToken.sol b/src/interfaces/IHypercertToken.sol index 4a7cf2d..6ad3f5c 100644 --- a/src/interfaces/IHypercertToken.sol +++ b/src/interfaces/IHypercertToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; +pragma solidity ^0.8.18; interface IHypercertToken { /** diff --git a/src/interfaces/IHyperfund.sol b/src/interfaces/IHyperfund.sol new file mode 100644 index 0000000..262e6ca --- /dev/null +++ b/src/interfaces/IHyperfund.sol @@ -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); +} From 4282bf7c0d99d79860af797279557b6bc92a6657 Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Thu, 20 Mar 2025 12:40:11 +0530 Subject: [PATCH 6/8] Add: event --- src/HyperStrategy.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index 3cc38b8..f34dc3e 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -11,10 +11,14 @@ 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 donor, address token, uint256 amount, uint256 hypercertUnits); + + // Errors error NOOP(); function initialize(uint256 _poolId, bytes memory _data) external virtual override { @@ -70,6 +74,7 @@ contract HyperStrategy is AccessControl, BaseStrategy { 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) From 9379e82ee0f4309e97a835930bb81f5e87297834 Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Thu, 20 Mar 2025 18:12:39 +0530 Subject: [PATCH 7/8] Add: HyperstrategyFactory.sol --- src/HyperStrategy.sol | 2 +- src/HyperstrategyFactory.sol | 29 +++++++++++++++++++++++++++++ src/interfaces/IHyperfund.sol | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/HyperstrategyFactory.sol diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index f34dc3e..bce5a6e 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.19; +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"; diff --git a/src/HyperstrategyFactory.sol b/src/HyperstrategyFactory.sol new file mode 100644 index 0000000..15df89d --- /dev/null +++ b/src/HyperstrategyFactory.sol @@ -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; + } +} diff --git a/src/interfaces/IHyperfund.sol b/src/interfaces/IHyperfund.sol index 262e6ca..1d2e48d 100644 --- a/src/interfaces/IHyperfund.sol +++ b/src/interfaces/IHyperfund.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; +pragma solidity ^0.8.19; interface IHyperfund { function hypercertId() external view returns (uint256); From 5665e72e6d36bc142c3261ef067ce2a63175327a Mon Sep 17 00:00:00 2001 From: Prajjawalk Date: Tue, 25 Mar 2025 21:55:58 +0530 Subject: [PATCH 8/8] make event fields indexed --- src/HyperStrategy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HyperStrategy.sol b/src/HyperStrategy.sol index bce5a6e..4bf85d9 100644 --- a/src/HyperStrategy.sol +++ b/src/HyperStrategy.sol @@ -16,7 +16,7 @@ contract HyperStrategy is AccessControl, BaseStrategy { IHypercertToken public hypercertMinter; // Events - event Donated(address donor, address token, uint256 amount, uint256 hypercertUnits); + event Donated(address indexed donor, address token, uint256 amount, uint256 indexed hypercertUnits); // Errors error NOOP();