Skip to content

Commit e55b69d

Browse files
Merge pull request #13 from soramitsu/develop
Draft version of smart contracts
2 parents 3b44c49 + 55d6919 commit e55b69d

38 files changed

+10118
-0
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
AMOY_API_KEY="00002N111EF000004101000051113J0000"
2+
PRIVATE_KEY="0x0000000000000000000000000000000000000000000000000000000000000000"

.github/workflows/codespell.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Codespell checkup
2+
3+
on:
4+
push:
5+
branches: [ develop ]
6+
pull_request:
7+
branches: [ master, develop ]
8+
9+
jobs:
10+
lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
- name: Run CodeSpell
16+
uses: codespell-project/[email protected]
17+
with:
18+
check_filenames: true
19+
skip: package-lock.json,*.pdf

.github/workflows/coverage.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Solidity Coverage
2+
3+
on:
4+
push:
5+
branches: [ develop ]
6+
pull_request:
7+
branches: [ master, develop ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
node-version: [ 20.x ]
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
- name: Set .env
20+
run: cp .env.example .env
21+
22+
- name: Run tests
23+
uses: actions/setup-node@v3
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
- run: npm ci
27+
- run: npm run coverage
28+
- uses: codecov/codecov-action@v4
29+
with:
30+
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/slither.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Slither Analysis
2+
3+
on:
4+
push:
5+
branches: [ develop ]
6+
pull_request:
7+
branches: [ master, develop ]
8+
9+
jobs:
10+
analyze:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
actions: read
15+
security-events: write
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
- name: Set .env
20+
run: cp .env.example .env
21+
22+
- name: Run Slither
23+
uses: crytic/[email protected]
24+
id: slither
25+
with:
26+
fail-on: high
27+
sarif: results.sarif
28+
slither-config: slither.config.json
29+
slither-args: --checklist --markdown-root ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/
30+
31+
- name: Upload SARIF file
32+
uses: github/codeql-action/upload-sarif@v3
33+
with:
34+
sarif_file: ${{ steps.slither.outputs.sarif }}

.github/workflows/tests.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Hardhat Testing
2+
3+
on:
4+
push:
5+
branches: [ develop ]
6+
pull_request:
7+
branches: [ master, develop ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
node-version: [ 20.x ]
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
- name: Set .env
20+
run: cp .env.example .env
21+
22+
- name: Run tests
23+
uses: actions/setup-node@v3
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
- run: npm ci
27+
- run: npm run test
28+
- name: Markdown report
29+
run: cat "./test/gasReport.md" > $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
node_modules
2+
.env
3+
4+
# Hardhat files
5+
/cache
6+
/artifacts
7+
/ignition/deployments
8+
/test/gasReport.md
9+
10+
# TypeChain files
11+
/typechain
12+
/typechain-types
13+
14+
# solidity-coverage files
15+
/coverage
16+
/coverage.json
17+
yarn.lock
18+
19+
20+
settings.json
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
ERC20LockUpFactory
3+
SPDX-License-Identifier: MIT
4+
*/
5+
6+
pragma solidity 0.8.25;
7+
import {ERC20LockUpPool} from "../pools/ERC20LockUpStakingPool.sol";
8+
import {ILockUpFactory} from "../interfaces/IFactories/ILockUpFactory.sol";
9+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
10+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
11+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
12+
13+
/// @title ERC20LockUpStakingFactory
14+
/// @notice A smart contract for deploying ERC20 LockUp staking pools.
15+
/// @author Ayooluwa Akindeko, Soramitsu team
16+
contract ERC20LockUpStakingFactory is Ownable, ILockUpFactory {
17+
using SafeERC20 for IERC20;
18+
19+
address[] public stakingPools;
20+
Request[] public requests;
21+
mapping(uint256 id => address pool) public poolById;
22+
23+
constructor() Ownable(msg.sender) {}
24+
25+
/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
26+
function deploy(uint256 id) public returns (address newPoolAddress) {
27+
if (requests.length < id) revert InvalidId();
28+
Request memory req = requests[id];
29+
if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus();
30+
if (msg.sender != req.deployer) revert InvalidCaller();
31+
newPoolAddress = address(
32+
new ERC20LockUpPool{
33+
salt: keccak256(
34+
abi.encode(
35+
req.data.stakeToken,
36+
req.data.rewardToken,
37+
req.data.rewardPerSecond,
38+
req.data.poolStartTime,
39+
req.data.poolEndTime
40+
)
41+
)
42+
}(
43+
req.data.stakeToken,
44+
req.data.rewardToken,
45+
req.data.poolStartTime,
46+
req.data.poolEndTime,
47+
req.data.unstakeLockUpTime,
48+
req.data.claimLockUpTime,
49+
req.data.rewardPerSecond
50+
)
51+
);
52+
stakingPools.push(newPoolAddress);
53+
requests[id].requestStatus = Status.DEPLOYED;
54+
poolById[id] = newPoolAddress;
55+
uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) *
56+
req.data.rewardPerSecond;
57+
ERC20LockUpPool(newPoolAddress).transferOwnership(msg.sender);
58+
// Transfer reward tokens from the owner to the contract
59+
// slither-disable-next-line arbitrary-send-erc20
60+
IERC20(req.data.rewardToken).safeTransferFrom(
61+
msg.sender,
62+
newPoolAddress,
63+
rewardAmount
64+
);
65+
emit StakingPoolDeployed(newPoolAddress, id);
66+
}
67+
68+
function requestDeployment(DeploymentData calldata data) external {
69+
if (data.stakeToken == address(0) || data.rewardToken == address(0))
70+
revert InvalidTokenAddress();
71+
if (data.rewardPerSecond == 0) revert InvalidRewardRate();
72+
requests.push(
73+
Request({
74+
deployer: msg.sender,
75+
requestStatus: Status.CREATED,
76+
data: data
77+
})
78+
);
79+
emit RequestSubmitted(
80+
requests.length - 1,
81+
msg.sender,
82+
Status.CREATED,
83+
data
84+
);
85+
}
86+
87+
function approveRequest(uint256 id) external onlyOwner {
88+
if (requests.length < id) revert InvalidId();
89+
Request storage req = requests[id];
90+
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
91+
req.requestStatus = Status.APPROVED;
92+
emit RequestStatusChanged(id, req.requestStatus);
93+
}
94+
95+
function denyRequest(uint256 id) external onlyOwner {
96+
if (requests.length < id) revert InvalidId();
97+
Request storage req = requests[id];
98+
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
99+
req.requestStatus = Status.DENIED;
100+
emit RequestStatusChanged(id, req.requestStatus);
101+
}
102+
103+
function cancelRequest(uint256 id) external {
104+
if (requests.length < id) revert InvalidId();
105+
Request storage req = requests[id];
106+
if (msg.sender != req.deployer) revert InvalidCaller();
107+
if (
108+
req.requestStatus != Status.CREATED &&
109+
req.requestStatus != Status.APPROVED
110+
) revert InvalidRequestStatus();
111+
req.requestStatus = Status.CANCELED;
112+
emit RequestStatusChanged(id, req.requestStatus);
113+
}
114+
115+
function getRequests() external view returns (Request[] memory reqs) {
116+
reqs = requests;
117+
}
118+
119+
function getPools() external view returns (address[] memory pools) {
120+
pools = stakingPools;
121+
}
122+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
ERC20LockUpFactory
3+
SPDX-License-Identifier: MIT
4+
*/
5+
6+
pragma solidity 0.8.25;
7+
import {ERC20NoLockUpPool} from "../pools/ERC20NoLockUpStakingPool.sol";
8+
import {INoLockUpFactory} from "../interfaces/IFactories/INoLockUpFactory.sol";
9+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
10+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
11+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
12+
13+
/// @title ERC20LockUpStakingFactory
14+
/// @notice A smart contract for deploying ERC20 regular staking pools.
15+
/// @author Ayooluwa Akindeko, Soramitsu team
16+
contract ERC20NoLockUpStakingFactory is Ownable, INoLockUpFactory {
17+
using SafeERC20 for IERC20;
18+
19+
address[] public stakingPools;
20+
Request[] public requests;
21+
mapping(uint256 id => address pool) public poolById;
22+
23+
constructor() Ownable(msg.sender) {}
24+
25+
/// @notice Function allows users to deploy the LockUp staking pool with specified parameters
26+
function deploy(uint256 id) public returns (address newPoolAddress) {
27+
if (requests.length < id) revert InvalidId();
28+
Request memory req = requests[id];
29+
if (req.requestStatus != Status.APPROVED) revert InvalidRequestStatus();
30+
if (msg.sender != req.deployer) revert InvalidCaller();
31+
newPoolAddress = address(
32+
new ERC20NoLockUpPool{
33+
salt: keccak256(
34+
abi.encode(
35+
req.data.stakeToken,
36+
req.data.rewardToken,
37+
req.data.rewardPerSecond,
38+
req.data.poolStartTime,
39+
req.data.poolEndTime
40+
)
41+
)
42+
}(
43+
req.data.stakeToken,
44+
req.data.rewardToken,
45+
req.data.rewardPerSecond,
46+
req.data.poolStartTime,
47+
req.data.poolEndTime
48+
)
49+
);
50+
stakingPools.push(newPoolAddress);
51+
requests[id].requestStatus = Status.DEPLOYED;
52+
poolById[id] = newPoolAddress;
53+
uint256 rewardAmount = (req.data.poolEndTime - req.data.poolStartTime) *
54+
req.data.rewardPerSecond;
55+
ERC20NoLockUpPool(newPoolAddress).transferOwnership(msg.sender);
56+
// Transfer reward tokens from the owner to the contract
57+
// slither-disable-next-line arbitrary-send-erc20
58+
IERC20(req.data.rewardToken).safeTransferFrom(
59+
msg.sender,
60+
newPoolAddress,
61+
rewardAmount
62+
);
63+
emit StakingPoolDeployed(newPoolAddress, id);
64+
}
65+
66+
function requestDeployment(DeploymentData calldata data) external {
67+
if (data.stakeToken == address(0) || data.rewardToken == address(0))
68+
revert InvalidTokenAddress();
69+
if (data.rewardPerSecond == 0) revert InvalidRewardRate();
70+
requests.push(
71+
Request({
72+
deployer: msg.sender,
73+
requestStatus: Status.CREATED,
74+
data: data
75+
})
76+
);
77+
emit RequestSubmitted(
78+
requests.length - 1,
79+
msg.sender,
80+
Status.CREATED,
81+
data
82+
);
83+
}
84+
85+
function approveRequest(uint256 id) external onlyOwner {
86+
if (requests.length < id) revert InvalidId();
87+
Request storage req = requests[id];
88+
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
89+
req.requestStatus = Status.APPROVED;
90+
emit RequestStatusChanged(id, req.requestStatus);
91+
}
92+
93+
function denyRequest(uint256 id) external onlyOwner {
94+
if (requests.length < id) revert InvalidId();
95+
Request storage req = requests[id];
96+
if (req.requestStatus != Status.CREATED) revert InvalidRequestStatus();
97+
req.requestStatus = Status.DENIED;
98+
emit RequestStatusChanged(id, req.requestStatus);
99+
}
100+
101+
function cancelRequest(uint256 id) external {
102+
if (requests.length < id) revert InvalidId();
103+
Request storage req = requests[id];
104+
if (msg.sender != req.deployer) revert InvalidCaller();
105+
if (
106+
req.requestStatus != Status.CREATED ||
107+
req.requestStatus != Status.APPROVED
108+
) revert InvalidRequestStatus();
109+
req.requestStatus = Status.CANCELED;
110+
emit RequestStatusChanged(id, req.requestStatus);
111+
}
112+
113+
function getRequests() external view returns (Request[] memory reqs) {
114+
reqs = requests;
115+
}
116+
117+
function getPools() external view returns (address[] memory pools) {
118+
pools = stakingPools;
119+
}
120+
}

0 commit comments

Comments
 (0)