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

Feature/refactor #47

Merged
merged 5 commits into from
Jul 10, 2024
Merged
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
86 changes: 84 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,84 @@
# stakepad-contracts
Designing a Better World Through Decentralized Technologies

# StakePad Project

This repository contains the smart contracts and tests for the Stakepad project.

## Setup

### Installation

Install the required dependencies by running:

```
npm install
```

### Environment Variables

Before running the project, you need to set up the environment variables. Create a `.env` file in the root directory of the project and add the following variables:

```
AMOY_API_KEY="Your_Amoy_API_Key"
PRIVATE_KEY="0xYour_Wallet_Private_Key"
```

To get your AMOY API key, please refer to the [AMOY Documentation](https://docs.polygonscan.com/getting-started/viewing-api-usage-statistics#creating-an-api-key).

## Running Tests

To run all unit tests, use the following command:

```
npx hardhat test
```

Make sure you have provided the necessary environment variables in the `.env` file before running the tests.

## Deployment

Before deploying the smart contracts, ensure you have set up all required parameters in your `.env` file as described above.

### Deploying the Contracts

To deploy the main Stakepad smart contracts, run the following command:

```
npx hardhat ignition deploy ignition/modules/stakepad.ts --network `network`
```

Replace `network` with the desired network name. The network should be configured in your `hardhat.config.ts` file under the `networks` section. Please refer to the [Hardhat Networks Configuration](https://hardhat.org/hardhat-runner/docs/config#networks-configuration) guide for more information.

Currently, the following networks are already configured:

- hardhat (default localhost)
- amoy (also known as polygon-amoy, Polygon PoS testnet)


Example for deploying to the amoy network:

```bash
npx hardhat ignition deploy ignition/modules/stakepad.ts -network 'amoy'
```

Example for deploying to the default hardhat network:

In the first terminal run the hardhat node:

```bash
npx hardhat node
```

In the second terminal run the deployment module:

```bash
npx hardhat ignition deploy ignition/modules/stakepad.ts -network 'localhost'
```

## Documentation

This repository contains the specifications and audit reports for the ERC20 Lockup Staking Pool smart contracts. For more detailed information, please refer to the [Documentation](./docs/README.md).

## Support

If you have any questions or need further assistance, feel free to open an issue on the repository or contact the maintainers.

48 changes: 29 additions & 19 deletions contracts/RequestManager.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
ERC20LockUpFactory
RequestManager
SPDX-License-Identifier: MIT
*/

pragma solidity 0.8.25;

import {IRequestManager} from "./interfaces/IRequestManager.sol";
import {IBaseFactory} from "./interfaces/IFactories/IBaseFactory.sol";
import {IGenericFactory} from "./interfaces/IFactories/IGenericFactory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand All @@ -22,48 +22,50 @@

constructor() Ownable(msg.sender) {}

function addFactory(address factory) onlyOwner external {
if (factory == address(0)) revert InvalidAddress();
function addFactory(address factory) external onlyOwner {
if (factory == address(0) || factory == address(this)) revert InvalidAddress();
if (whitelistFactory[factory]) revert AlreadyRegisteredFactory();
whitelistFactory[factory] = true;
emit FactoryRegistered(factory);
}

function removeFactory(address factory) onlyOwner external {
if (whitelistFactory[factory] != true) revert UnregisteredFactory();
function removeFactory(address factory) external onlyOwner {
if (!whitelistFactory[factory]) revert UnregisteredFactory();
whitelistFactory[factory] = false;
emit FactoryUnregistered(factory);
}

/// @notice Function allows users to deploy the staking pool with specified parameters
function deploy(uint256 id) external returns (address newPoolAddress) {
/// @notice Deploys the staking pool with parameters from an approved request.
/// @param id ID of the request to be deployed.
function deploy(uint256 id) external {
if (requests.length <= id) revert InvalidId();
Request memory req = requests[id];
if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus();
if (msg.sender != req.data.deployer) revert InvalidCaller();
if (msg.sender != req.data.deployer) revert InvalidDeployer();
requests[id].requestStatus = Status.DEPLOYED;
newPoolAddress = IBaseFactory(req.data.factory).deploy(
address newPoolAddress = IGenericFactory(req.data.factory).deploy(
req.data.deployer,
req.data.stakingData
);
stakingPools.push(newPoolAddress);
poolById[id] = newPoolAddress;
emit RequestFullfilled(id, newPoolAddress);
}

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low


/// @notice Requests deployment of a new staking pool.
/// @param data Request data for the staking pool.
function requestDeployment(RequestPayload calldata data) external {
if (data.deployer == address(0) || data.factory == address(0))
revert InvalidAddress();
if (data.ipfsHash == bytes32(0)) revert InvalidIpfsHash();
if (data.stakingData.length == 0) revert InvalidPayload();
if (whitelistFactory[data.factory] != true)
revert UnregisteredFactory();
if (data.ipfsHash == bytes32(0)) revert IpfsZeroHash();
if (data.stakingData.length == 0) revert EmptyPayload();
if (!whitelistFactory[data.factory]) revert UnregisteredFactory();
requests.push(Request({requestStatus: Status.CREATED, data: data}));
emit RequestSubmitted(
requests.length - 1,
data
);
emit RequestSubmitted(requests.length - 1, data);
}

/// @notice Approves a deployment request.
/// @param id ID of the request to be approved.
function approveRequest(uint256 id) external onlyOwner {
if (requests.length <= id) revert InvalidId();
Request storage req = requests[id];
Expand All @@ -72,6 +74,8 @@
emit RequestStatusChanged(id, req.requestStatus);
}

/// @notice Denies a deployment request.
/// @param id ID of the request to be denied.
function denyRequest(uint256 id) external onlyOwner {
if (requests.length <= id) revert InvalidId();
Request storage req = requests[id];
Expand All @@ -80,10 +84,12 @@
emit RequestStatusChanged(id, req.requestStatus);
}

/// @notice Cancels a deployment request.
/// @param id ID of the request to be canceled.
function cancelRequest(uint256 id) external {
if (requests.length <= id) revert InvalidId();
Request storage req = requests[id];
if (msg.sender != req.data.deployer) revert InvalidCaller();
if (msg.sender != req.data.deployer) revert InvalidDeployer();
if (
req.requestStatus != Status.CREATED &&
req.requestStatus != Status.APPROVED
Expand All @@ -92,10 +98,14 @@
emit RequestStatusChanged(id, req.requestStatus);
}

/// @notice Returns all deployment requests.
/// @return reqs Array of all PenaltyFee requests.
function getRequests() external view returns (Request[] memory reqs) {
reqs = requests;
}

/// @notice Returns all deployed staking pools.
/// @return pools Array of all staking pool addresses.
function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
Expand Down
24 changes: 10 additions & 14 deletions contracts/factories/ERC20LockUpFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {ERC20LockUpPool} from "../pools/ERC20/ERC20LockUpStakingPool.sol";
import {ILockUpFactory} from "../interfaces/IFactories/ILockUpFactory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {GenericFactory} from "./GenericFactory.sol";

/// @title ERC20LockUpStakingFactory
/// @notice A smart contract for deploying ERC20 LockUp staking pools.
contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory {
contract ERC20LockUpStakingFactory is GenericFactory, ILockUpFactory {
using SafeERC20 for IERC20;
address public requestManager;
address[] public stakingPools;

constructor(address managerContract) Ownable(msg.sender) {
if (managerContract == address(0)) revert InvalidManagerAddress();
requestManager = managerContract;
}
constructor(address managerContract) GenericFactory(managerContract) {}

/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
function deploy(address deployer, bytes calldata payload) external returns (address newPoolAddress) {
if (payload.length != 224) revert InvalidPayloadLength();
/// @param deployer Address of the deployer
/// @param payload Encoded staking pool deployment parameters
function deploy(
address deployer,
bytes calldata payload
) external returns (address newPoolAddress) {
if (msg.sender != requestManager) revert InvalidCaller();
if (payload.length != 224) revert InvalidPayloadLength();
DeploymentData memory data = abi.decode(payload, (DeploymentData));
newPoolAddress = address(
new ERC20LockUpPool{
Expand Down Expand Up @@ -61,8 +61,4 @@ contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory {
);
emit StakingPoolDeployed(newPoolAddress);
}

function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
}
25 changes: 10 additions & 15 deletions contracts/factories/ERC20PenaltyFeeFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@ SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {ERC20PenaltyFeePool} from "../pools/ERC20/ERC20PenaltyFeePool.sol";
import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {GenericFactory} from "./GenericFactory.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title ERC20PenaltyFeeStakingFactory
/// @notice A smart contract for deploying ERC20 staking pools with penalty fees.
/// @author Ayooluwa Akindeko, Soramitsu team
contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory {
contract ERC20PenaltyFeeStakingFactory is GenericFactory, IPenaltyFeeFactory {
using SafeERC20 for IERC20;
address public requestManager;
address[] public stakingPools;

constructor(address managerContract) Ownable(msg.sender) {
if (managerContract == address(0)) revert InvalidManagerAddress();
requestManager = managerContract;
}
constructor(address managerContract) GenericFactory(managerContract) {}

/// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters
function deploy(address deployer, bytes calldata payload) external returns (address newPoolAddress) {
if (payload.length != 192) revert InvalidPayloadLength();
/// @param deployer Address of the deployer
/// @param payload Encoded staking pool deployment parameters
function deploy(
address deployer,
bytes calldata payload
) external returns (address newPoolAddress) {
if (msg.sender != requestManager) revert InvalidCaller();
if (payload.length != 192) revert InvalidPayloadLength();
DeploymentData memory data = abi.decode(payload, (DeploymentData));
newPoolAddress = address(
new ERC20PenaltyFeePool{
Expand Down Expand Up @@ -62,8 +61,4 @@ contract ERC20PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory {
);
emit StakingPoolDeployed(newPoolAddress);
}

function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
}
27 changes: 11 additions & 16 deletions contracts/factories/ERC721LockUpFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,27 @@ SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {ERC721LockUpPool} from "../pools/ERC721/ERC721LockUpStakingPool.sol";
import {ILockUpFactory} from "../interfaces/IFactories/ILockUpFactory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {GenericFactory} from "./GenericFactory.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title ERC721LockUpStakingFactory
/// @notice A smart contract for deploying ERC721 LockUp staking pools.
/// @author Ayooluwa Akindeko, Soramitsu team
contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory {
contract ERC721LockUpStakingFactory is GenericFactory, ILockUpFactory {
using SafeERC20 for IERC20;

address public requestManager;
address[] public stakingPools;
constructor(address managerContract) GenericFactory(managerContract) {}

constructor(address managerContract) Ownable(msg.sender) {
if (managerContract == address(0)) revert InvalidManagerAddress();
requestManager = managerContract;
}

/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
function deploy(address deployer, bytes calldata payload) public returns (address newPoolAddress) {
if (payload.length != 224) revert InvalidPayloadLength();
/// @notice Function allows users to deploy the ERC721 LockUp staking pool with specified parameters
/// @param deployer Address of the deployer
/// @param payload Encoded staking pool deployment parameters
function deploy(
address deployer,
bytes calldata payload
) public returns (address newPoolAddress) {
if (msg.sender != requestManager) revert InvalidCaller();
if (payload.length != 224) revert InvalidPayloadLength();
DeploymentData memory data = abi.decode(payload, (DeploymentData));
newPoolAddress = address(
new ERC721LockUpPool{
Expand Down Expand Up @@ -63,8 +62,4 @@ contract ERC721LockUpStakingFactory is Ownable, ILockUpFactory {
);
emit StakingPoolDeployed(newPoolAddress);
}

function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
}
21 changes: 7 additions & 14 deletions contracts/factories/ERC721PenaltyFeeFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,23 @@ SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {ERC721PenaltyFeePool} from "../pools/ERC721/ERC721PenaltyFeePool.sol";
import {IPenaltyFeeFactory} from "../interfaces/IFactories/IPenaltyFeeFactory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {GenericFactory} from "./GenericFactory.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title ERC721PenaltyFeeStakingFactory
/// @notice A smart contract for deploying ERC721 staking pools with penalty fees.
contract ERC721PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory {
contract ERC721PenaltyFeeStakingFactory is GenericFactory, IPenaltyFeeFactory {
using SafeERC20 for IERC20;
address public requestManager;
address[] public stakingPools;

constructor(address managerContract) Ownable(msg.sender) {
if (managerContract == address(0)) revert InvalidManagerAddress();
requestManager = managerContract;
}
constructor(address managerContract) GenericFactory(managerContract) {}

/// @notice Function allows users to deploy the penaltyFee staking pool with specified parameters
/// @notice Function allows users to deploy the ERC721 penaltyFee staking pool with specified parameters
/// @param deployer Address of the deployer
/// @param payload Encoded staking pool deployment parameters
function deploy(address deployer, bytes calldata payload) public returns (address newPoolAddress) {
if (payload.length != 192) revert InvalidPayloadLength();
if (msg.sender != requestManager) revert InvalidCaller();
if (payload.length != 192) revert InvalidPayloadLength();
DeploymentData memory data = abi.decode(payload, (DeploymentData));
newPoolAddress = address(
new ERC721PenaltyFeePool{
Expand Down Expand Up @@ -62,8 +59,4 @@ contract ERC721PenaltyFeeStakingFactory is Ownable, IPenaltyFeeFactory {

emit StakingPoolDeployed(newPoolAddress);
}

function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
}
Loading
Loading