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

Base version #1

Merged
merged 17 commits into from
May 15, 2024
Merged
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ runs:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: "16.18.x"
node-version: "18.16.x"
cache: npm
- name: Install packages
run: npm install
Expand Down
108 changes: 108 additions & 0 deletions contracts/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

import {IFactory} from "./interfaces/IFactory.sol";

abstract contract Factory is IFactory, OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add method to precalculate address. It should take deployer as a parameter.

mapping(uint8 => address) internal _implementations;
mapping(bytes32 => bool) private _usedSalts;

/**
* @dev It is used exclusively for storing information about the detached proxies.
*
* `_msgSender()` -> `poolName` -> `poolType` -> `proxy`
*/
mapping(address => mapping(string => mapping(uint8 => address))) public deployedProxies;

function __Factory_init() internal onlyInitializing {}

Check warning on line 23 in contracts/Factory.sol

View workflow job for this annotation

GitHub Actions / test

Function name must be in mixedCase

/**
* @notice Returns contract to normal state.
*/
function pause() external onlyOwner {
_pause();
}

/**
* @notice Triggers stopped state.
*/
function unpause() external onlyOwner {
_unpause();
}

/**
* @notice The function to set implementation for the specific pool.
*
* @param poolType_ the type of the pool.
* @param implementation_ the implementation the pool will point to.
*/
function setImplementation(uint8 poolType_, address implementation_) public onlyOwner {
Copy link
Collaborator

@dovgopoly dovgopoly May 14, 2024

Choose a reason for hiding this comment

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

Should implementation_ be a beacon?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We decided to do it with ERC1967Proxy to have the chance to remove the upgradability

_implementations[poolType_] = implementation_;
}

/**
* @notice The function to get implementation of the specific pools.
*
* @param poolType_ the type of the pools.
* @return implementation the implementation which the pool points to.
*/
function getImplementation(uint8 poolType_) public view returns (address) {
return _implementations[poolType_];
}

/**
* @notice The function to deploy new `ERC1967Proxy`.
*
* @param poolType_ the type of the pool.
* @param poolName_ the name of the pool.
* @return proxy the proxy address for the `poolType_`.
*/
function _deploy2(uint8 poolType_, string calldata poolName_) internal returns (address) {
require(bytes(poolName_).length != 0, "F: poolName_ is empty");

Check warning on line 67 in contracts/Factory.sol

View workflow job for this annotation

GitHub Actions / test

GC: Use Custom Errors instead of require statements
bytes32 salt_ = _calculatePoolSalt(_msgSender(), poolName_, poolType_);

address implementation_ = getImplementation(poolType_);
require(implementation_ != address(0), "F: implementation not found");

Check warning on line 71 in contracts/Factory.sol

View workflow job for this annotation

GitHub Actions / test

GC: Use Custom Errors instead of require statements

require(!_usedSalts[salt_], "F: salt used");

Check warning on line 73 in contracts/Factory.sol

View workflow job for this annotation

GitHub Actions / test

GC: Use Custom Errors instead of require statements
_usedSalts[salt_] = true;

address proxy_ = address(new ERC1967Proxy{salt: salt_}(implementation_, bytes("")));

deployedProxies[_msgSender()][poolName_][poolType_] = proxy_;

emit ProxyDeployed(proxy_, implementation_, poolType_, poolName_);

return proxy_;
}

function _predictPoolAddress(
uint8 poolType_,
string calldata poolName_,
address sender_
) internal view returns (address) {
bytes32 salt_ = _calculatePoolSalt(sender_, poolName_, uint8(poolType_));

bytes32 bytecodeHash_ = keccak256(
abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(getImplementation(poolType_), bytes("")))
);

return Create2.computeAddress(salt_, bytecodeHash_);
}

function _calculatePoolSalt(
address sender_,
string calldata poolName_,
uint8 poolType_
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(sender_, poolName_, poolType_));
}

function _authorizeUpgrade(address) internal view override onlyOwner {}
}
32 changes: 25 additions & 7 deletions contracts/L1/Distribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {PRECISION} from "@solarity/solidity-lib/utils/Globals.sol";

import {LinearDistributionIntervalDecrease} from "../libs/LinearDistributionIntervalDecrease.sol";

import {IDistribution} from "../interfaces/IDistribution.sol";

import {L1Sender} from "./L1Sender.sol";
import {IDistribution} from "../interfaces/L1/IDistribution.sol";
import {IFeeConfig} from "../interfaces/L1/IFeeConfig.sol";
import {IL1Sender} from "../interfaces/L1/IL1Sender.sol";

contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {
using SafeERC20 for IERC20;
Expand All @@ -20,6 +20,7 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {

address public depositToken;
address public l1Sender;
address public feeConfig;

// Pool storage
Pool[] public pools;
Expand Down Expand Up @@ -55,6 +56,7 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {
function Distribution_init(
address depositToken_,
address l1Sender_,
address feeConfig_,
Pool[] calldata poolsInfo_
) external initializer {
__Ownable_init();
Expand All @@ -66,6 +68,7 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {

depositToken = depositToken_;
l1Sender = l1Sender_;
feeConfig = feeConfig_;
}

/**********************************************************************************************/
Expand All @@ -82,7 +85,14 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {

function editPool(uint256 poolId_, Pool calldata pool_) external onlyOwner poolExists(poolId_) {
_validatePool(pool_);
require(pools[poolId_].isPublic == pool_.isPublic, "DS: invalid pool type");

Pool storage pool = pools[poolId_];
require(pool.isPublic == pool_.isPublic, "DS: invalid pool type");
if (pool_.payoutStart > block.timestamp) {
FedokDL marked this conversation as resolved.
Show resolved Hide resolved
require(pool.payoutStart == pool_.payoutStart, "DS: invalid payout start value");
require(pool.withdrawLockPeriod == pool_.withdrawLockPeriod, "DS: invalid WLP value");
require(pool.withdrawLockPeriodAfterStake == pool_.withdrawLockPeriodAfterStake, "DS: invalid WLPAS value");
}

PoolData storage poolData = poolsData[poolId_];
uint256 currentPoolRate_ = _getCurrentPoolRate(poolId_);
Expand Down Expand Up @@ -174,7 +184,7 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {
userData.pendingRewards = 0;

// Transfer rewards
L1Sender(l1Sender).sendMintMessage{value: msg.value}(receiver_, pendingRewards_, user_);
IL1Sender(l1Sender).sendMintMessage{value: msg.value}(receiver_, pendingRewards_, user_);

emit UserClaimed(poolId_, user_, receiver_, pendingRewards_);
}
Expand Down Expand Up @@ -327,9 +337,18 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {
uint256 overplus_ = overplus();
require(overplus_ > 0, "DS: overplus is zero");

(uint256 feePercent_, address treasuryAddress_) = IFeeConfig(feeConfig).getFeeAndTreasury(address(this));

uint256 fee_ = (overplus_ * feePercent_) / PRECISION;
if (fee_ != 0) {
IERC20(depositToken).safeTransfer(treasuryAddress_, fee_);

overplus_ -= fee_;
}

IERC20(depositToken).safeTransfer(l1Sender, overplus_);

bytes memory bridgeMessageId_ = L1Sender(l1Sender).sendDepositToken{value: msg.value}(
bytes memory bridgeMessageId_ = IL1Sender(l1Sender).sendDepositToken{value: msg.value}(
gasLimit_,
maxFeePerGas_,
maxSubmissionCost_
Expand All @@ -343,7 +362,6 @@ contract Distribution is IDistribution, OwnableUpgradeable, UUPSUpgradeable {
/**********************************************************************************************/
/*** UUPS ***/
/**********************************************************************************************/

function removeUpgradeability() external onlyOwner {
isNotUpgradeable = true;
}
Expand Down
53 changes: 53 additions & 0 deletions contracts/L1/FeeConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

import {PRECISION} from "@solarity/solidity-lib/utils/Globals.sol";

import {IFeeConfig} from "../interfaces/L1/IFeeConfig.sol";

contract FeeConfig is IFeeConfig, OwnableUpgradeable, UUPSUpgradeable {
address public treasury;
uint256 public baseFee;

mapping(address => uint256) public fees;

function __FeeConfig_init(address treasury_, uint256 baseFee_) external initializer {
__Ownable_init();
__UUPSUpgradeable_init();

treasury = treasury_;
baseFee = baseFee_;
}

function setFee(address sender_, uint256 fee_) external onlyOwner {
require(fee_ <= PRECISION, "FC: invalid fee");

fees[sender_] = fee_;
}

function setTreasury(address treasury_) external onlyOwner {
require(treasury_ != address(0), "FC: invalid treasury");

treasury = treasury_;
}

function setBaseFee(uint256 baseFee_) external onlyOwner {
require(baseFee_ < PRECISION, "FC: invalid base fee");

baseFee = baseFee_;
}

function getFeeAndTreasury(address sender_) external view returns (uint256, address) {
uint256 fee_ = fees[sender_];
if (fee_ == 0) {
fee_ = baseFee;
}

return (fee_, treasury);
}

function _authorizeUpgrade(address) internal override onlyOwner {}
}
100 changes: 100 additions & 0 deletions contracts/L1/L1Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IDistribution} from "../interfaces/L1/IDistribution.sol";
import {IL1Factory} from "../interfaces/L1/IL1Factory.sol";
import {IL1Sender} from "../interfaces/L1/IL1Sender.sol";
import {IOwnable} from "../interfaces/IOwnable.sol";

import {Factory} from "../Factory.sol";

contract L1Factory is IL1Factory, Factory {
address public feeConfig;

DepositTokenExternalDeps public depositTokenExternalDeps;
ArbExternalDeps public arbExternalDeps;
LzExternalDeps public lzExternalDeps;

constructor() {
_disableInitializers();
}

function L1Factory_init() external initializer {
__Pausable_init();
__Ownable_init();
__UUPSUpgradeable_init();
__Factory_init();
}

function setDepositTokenExternalDeps(
DepositTokenExternalDeps calldata depositTokenExternalDeps_
) external onlyOwner {
require(depositTokenExternalDeps_.token != address(0), "L1F: invalid token");
require(depositTokenExternalDeps_.wToken != address(0), "L1F: invalid wtoken");

depositTokenExternalDeps = depositTokenExternalDeps_;
}

function setLzExternalDeps(LzExternalDeps calldata lzExternalDeps_) external onlyOwner {
require(lzExternalDeps_.endpoint != address(0), "L1F: invalid LZ endpoint");
require(lzExternalDeps_.destinationChainId != 0, "L1F: invalid chain ID");

lzExternalDeps = lzExternalDeps_;
}

function setArbExternalDeps(ArbExternalDeps calldata arbExternalDeps_) external onlyOwner {
require(arbExternalDeps_.endpoint != address(0), "L1F: invalid ARB endpoint");

arbExternalDeps = arbExternalDeps_;
}

function setFeeConfig(address feeConfig_) external onlyOwner {
require(feeConfig_ != address(0), "L1F: invalid fee config");

feeConfig = feeConfig_;
}

function deploy(L1Params calldata l1Params_) external whenNotPaused {
address distributionProxy_ = _deploy2(uint8(PoolType.DISTRIBUTION), l1Params_.protocolName);
address l1SenderProxy_ = _deploy2(uint8(PoolType.L1_SENDER), l1Params_.protocolName);

IDistribution(distributionProxy_).Distribution_init(
depositTokenExternalDeps.token,
l1SenderProxy_,
feeConfig,
l1Params_.poolsInfo
);

IL1Sender.RewardTokenConfig memory lzConfig_ = IL1Sender.RewardTokenConfig(
lzExternalDeps.endpoint,
l1Params_.l2MessageReceiver,
lzExternalDeps.destinationChainId,
lzExternalDeps.zroPaymentAddress,
lzExternalDeps.adapterParams
);

IL1Sender.DepositTokenConfig memory arbConfig_ = IL1Sender.DepositTokenConfig(
depositTokenExternalDeps.wToken,
arbExternalDeps.endpoint,
l1Params_.l2TokenReceiver
);

IL1Sender(l1SenderProxy_).L1Sender__init(distributionProxy_, lzConfig_, arbConfig_);

if (l1Params_.isNotUpgradeable) {
IDistribution(distributionProxy_).removeUpgradeability();
}

IOwnable(distributionProxy_).transferOwnership(_msgSender());
IOwnable(l1SenderProxy_).transferOwnership(_msgSender());
}

function predictAddresses(
string calldata poolName_,
address sender_
) external view returns (address distribution_, address l1Sender_) {
distribution_ = _predictPoolAddress(uint8(PoolType.DISTRIBUTION), poolName_, sender_);

l1Sender_ = _predictPoolAddress(uint8(PoolType.L1_SENDER), poolName_, sender_);
}
}
Loading
Loading