Skip to content

Commit

Permalink
Merge pull request #13 from soramitsu/develop
Browse files Browse the repository at this point in the history
Draft version of smart contracts
  • Loading branch information
SergeyPoslavskiy authored May 14, 2024
2 parents 3b44c49 + 55d6919 commit e55b69d
Show file tree
Hide file tree
Showing 38 changed files with 10,118 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
AMOY_API_KEY="00002N111EF000004101000051113J0000"
PRIVATE_KEY="0x0000000000000000000000000000000000000000000000000000000000000000"
19 changes: 19 additions & 0 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Codespell checkup

on:
push:
branches: [ develop ]
pull_request:
branches: [ master, develop ]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run CodeSpell
uses: codespell-project/[email protected]
with:
check_filenames: true
skip: package-lock.json,*.pdf
30 changes: 30 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Solidity Coverage

on:
push:
branches: [ develop ]
pull_request:
branches: [ master, develop ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 20.x ]

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set .env
run: cp .env.example .env

- name: Run tests
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run coverage
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
34 changes: 34 additions & 0 deletions .github/workflows/slither.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Slither Analysis

on:
push:
branches: [ develop ]
pull_request:
branches: [ master, develop ]

jobs:
analyze:
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set .env
run: cp .env.example .env

- name: Run Slither
uses: crytic/[email protected]
id: slither
with:
fail-on: high
sarif: results.sarif
slither-config: slither.config.json
slither-args: --checklist --markdown-root ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/

- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.slither.outputs.sarif }}
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Hardhat Testing

on:
push:
branches: [ develop ]
pull_request:
branches: [ master, develop ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 20.x ]

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set .env
run: cp .env.example .env

- name: Run tests
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run test
- name: Markdown report
run: cat "./test/gasReport.md" > $GITHUB_STEP_SUMMARY
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
node_modules
.env

# Hardhat files
/cache
/artifacts
/ignition/deployments
/test/gasReport.md

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json
yarn.lock


settings.json
122 changes: 122 additions & 0 deletions contracts/factories/ERC20LockUpFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
ERC20LockUpFactory
SPDX-License-Identifier: MIT
*/

pragma solidity 0.8.25;
import {ERC20LockUpPool} from "../pools/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";

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

address[] public stakingPools;
Request[] public requests;
mapping(uint256 id => address pool) public poolById;

constructor() Ownable(msg.sender) {}

/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
function deploy(uint256 id) public returns (address newPoolAddress) {
if (requests.length < id) revert InvalidId();
Request memory req = requests[id];
if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus();
if (msg.sender != req.deployer) revert InvalidCaller();
newPoolAddress = address(
new ERC20LockUpPool{
salt: keccak256(
abi.encode(
req.data.stakeToken,
req.data.rewardToken,
req.data.rewardPerSecond,
req.data.poolStartTime,
req.data.poolEndTime
)
)
}(
req.data.stakeToken,
req.data.rewardToken,
req.data.poolStartTime,
req.data.poolEndTime,
req.data.unstakeLockUpTime,
req.data.claimLockUpTime,
req.data.rewardPerSecond
)
);
stakingPools.push(newPoolAddress);
requests[id].requestStatus = Status.DEPLOYED;
poolById[id] = newPoolAddress;
uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) *
req.data.rewardPerSecond;
ERC20LockUpPool(newPoolAddress).transferOwnership(msg.sender);
// Transfer reward tokens from the owner to the contract
// slither-disable-next-line arbitrary-send-erc20
IERC20(req.data.rewardToken).safeTransferFrom(
msg.sender,
newPoolAddress,
rewardAmount
);
emit StakingPoolDeployed(newPoolAddress, id);
}

function requestDeployment(DeploymentData calldata data) external {
if (data.stakeToken == address(0) || data.rewardToken == address(0))
revert InvalidTokenAddress();
if (data.rewardPerSecond == 0) revert InvalidRewardRate();
requests.push(
Request({
deployer: msg.sender,
requestStatus: Status.CREATED,
data: data
})
);
emit RequestSubmitted(
requests.length - 1,
msg.sender,
Status.CREATED,
data
);
}

function approveRequest(uint256 id) external onlyOwner {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
req.requestStatus = Status.APPROVED;
emit RequestStatusChanged(id, req.requestStatus);
}

function denyRequest(uint256 id) external onlyOwner {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
req.requestStatus = Status.DENIED;
emit RequestStatusChanged(id, req.requestStatus);
}

function cancelRequest(uint256 id) external {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (msg.sender != req.deployer) revert InvalidCaller();
if (
req.requestStatus != Status.CREATED &&
req.requestStatus != Status.APPROVED
) revert InvalidRequestStatus();
req.requestStatus = Status.CANCELED;
emit RequestStatusChanged(id, req.requestStatus);
}

function getRequests() external view returns (Request[] memory reqs) {
reqs = requests;
}

function getPools() external view returns (address[] memory pools) {
pools = stakingPools;
}
}
120 changes: 120 additions & 0 deletions contracts/factories/ERC20NoLockUpFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
ERC20LockUpFactory
SPDX-License-Identifier: MIT
*/

pragma solidity 0.8.25;
import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol";
import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.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";

/// @title ERC20LockUpStakingFactory
/// @notice A smart contract for deploying ERC20 regular staking pools.
/// @author Ayooluwa Akindeko, Soramitsu team
contract ERC20NoLockUpStakingFactory is Ownable, INoLockUpFactory {
using SafeERC20 for IERC20;

address[] public stakingPools;
Request[] public requests;
mapping(uint256 id => address pool) public poolById;

constructor() Ownable(msg.sender) {}

/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
function deploy(uint256 id) public returns (address newPoolAddress) {
if (requests.length < id) revert InvalidId();
Request memory req = requests[id];
if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus();
if (msg.sender != req.deployer) revert InvalidCaller();
newPoolAddress = address(
new ERC20NoLockUpPool{
salt: keccak256(
abi.encode(
req.data.stakeToken,
req.data.rewardToken,
req.data.rewardPerSecond,
req.data.poolStartTime,
req.data.poolEndTime
)
)
}(
req.data.stakeToken,
req.data.rewardToken,
req.data.rewardPerSecond,
req.data.poolStartTime,
req.data.poolEndTime
)
);
stakingPools.push(newPoolAddress);
requests[id].requestStatus = Status.DEPLOYED;
poolById[id] = newPoolAddress;
uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) *
req.data.rewardPerSecond;
ERC20NoLockUpPool(newPoolAddress).transferOwnership(msg.sender);
// Transfer reward tokens from the owner to the contract
// slither-disable-next-line arbitrary-send-erc20
IERC20(req.data.rewardToken).safeTransferFrom(
msg.sender,
newPoolAddress,
rewardAmount
);
emit StakingPoolDeployed(newPoolAddress, id);
}

function requestDeployment(DeploymentData calldata data) external {
if (data.stakeToken == address(0) || data.rewardToken == address(0))
revert InvalidTokenAddress();
if (data.rewardPerSecond == 0) revert InvalidRewardRate();
requests.push(
Request({
deployer: msg.sender,
requestStatus: Status.CREATED,
data: data
})
);
emit RequestSubmitted(
requests.length - 1,
msg.sender,
Status.CREATED,
data
);
}

function approveRequest(uint256 id) external onlyOwner {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
req.requestStatus = Status.APPROVED;
emit RequestStatusChanged(id, req.requestStatus);
}

function denyRequest(uint256 id) external onlyOwner {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
req.requestStatus = Status.DENIED;
emit RequestStatusChanged(id, req.requestStatus);
}

function cancelRequest(uint256 id) external {
if (requests.length < id) revert InvalidId();
Request storage req = requests[id];
if (msg.sender != req.deployer) revert InvalidCaller();
if (
req.requestStatus != Status.CREATED ||
req.requestStatus != Status.APPROVED
) revert InvalidRequestStatus();
req.requestStatus = Status.CANCELED;
emit RequestStatusChanged(id, req.requestStatus);
}

function getRequests() external view returns (Request[] memory reqs) {
reqs = requests;
}

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

0 comments on commit e55b69d

Please sign in to comment.