Skip to content
Open
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
21 changes: 19 additions & 2 deletions script/DeploySetup.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {IPriceFeed} from "../src/interfaces/dependencies/IPriceFeed.sol";
import {IMultiCollateralHintHelpers} from "../src/helpers/interfaces/IMultiCollateralHintHelpers.sol";
import {IMultiTroveGetter} from "../src/helpers/interfaces/IMultiTroveGetter.sol";
import {ISatoshiBORouter} from "../src/helpers/interfaces/ISatoshiBORouter.sol";
import {IReferralManager} from "../src/helpers/interfaces/IReferralManager.sol";
import {IWETH} from "../src/helpers/interfaces/IWETH.sol";
import {SortedTroves} from "../src/core/SortedTroves.sol";
import {SatoshiCore} from "../src/core/SatoshiCore.sol";
Expand All @@ -33,6 +34,7 @@ import {Factory} from "../src/core/Factory.sol";
import {MultiCollateralHintHelpers} from "../src/helpers/MultiCollateralHintHelpers.sol";
import {MultiTroveGetter} from "../src/helpers/MultiTroveGetter.sol";
import {SatoshiBORouter} from "../src/helpers/SatoshiBORouter.sol";
import {ReferralManager} from "../src/helpers/ReferralManager.sol";
import {
SATOSHI_CORE_OWNER,
SATOSHI_CORE_GUARDIAN,
Expand All @@ -42,7 +44,9 @@ import {
DEBT_TOKEN_SYMBOL,
BO_MIN_NET_DEBT,
GAS_COMPENSATION,
WETH_ADDRESS
WETH_ADDRESS,
REFERRAL_START_TIMESTAMP,
REFERRAL_END_TIMESTAMP
} from "./DeploySetupConfig.sol";

contract DeploySetupScript is Script {
Expand Down Expand Up @@ -74,6 +78,7 @@ contract DeploySetupScript is Script {
IMultiCollateralHintHelpers hintHelpers;
IMultiTroveGetter multiTroveGetter;
ISatoshiBORouter satoshiBORouter;
IReferralManager referralManager;

/* computed contracts for deployment */
// implementation contracts
Expand Down Expand Up @@ -245,7 +250,18 @@ contract DeploySetupScript is Script {
multiTroveGetter = new MultiTroveGetter();

// SatoshiBORouter
satoshiBORouter = new SatoshiBORouter(debtToken, borrowerOperationsProxy, IWETH(WETH_ADDRESS));
address cpSatoshiBORouterAddr = vm.computeCreateAddress(deployer, ++nonce);
address cpReferralManagerAddr = vm.computeCreateAddress(deployer, ++nonce);
satoshiBORouter = new SatoshiBORouter(
debtToken, borrowerOperationsProxy, IReferralManager(cpReferralManagerAddr), IWETH(WETH_ADDRESS)
);
assert(cpSatoshiBORouterAddr == address(satoshiBORouter));

// ReferralManager
referralManager = new ReferralManager(
ISatoshiBORouter(cpSatoshiBORouterAddr), REFERRAL_START_TIMESTAMP, REFERRAL_END_TIMESTAMP
);
assert(cpReferralManagerAddr == address(referralManager));

console.log("Deployed contracts:");
console.log("priceFeedAggregatorImpl:", address(priceFeedAggregatorImpl));
Expand All @@ -267,6 +283,7 @@ contract DeploySetupScript is Script {
console.log("hintHelpers:", address(hintHelpers));
console.log("multiTroveGetter:", address(multiTroveGetter));
console.log("satoshiBORouter:", address(satoshiBORouter));
console.log("referralManager:", address(referralManager));

vm.stopBroadcast();
}
Expand Down
4 changes: 4 additions & 0 deletions script/DeploySetupConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ string constant DEBT_TOKEN_NAME = "Statoshi Stablecoin";
string constant DEBT_TOKEN_SYMBOL = "SAT";

address constant WETH_ADDRESS = 0x51abb19F1ebc7B64040aFd0ef3C789d75C8707e0;

//TODO: Replace with the actual timestamp
uint256 constant REFERRAL_START_TIMESTAMP = 1709251200; // 2024-03-01 00:00:00 UTC
uint256 constant REFERRAL_END_TIMESTAMP = 1711929600; // 2024-04-01 00:00:00 UTC
123 changes: 123 additions & 0 deletions src/helpers/ReferralManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IReferralManager} from "./interfaces/IReferralManager.sol";
import {ISatoshiBORouter} from "./interfaces/ISatoshiBORouter.sol";

contract ReferralManager is IReferralManager, Ownable {
ISatoshiBORouter public immutable satoshiBORouter;

uint256 public startTimestamp;
uint256 public endTimestamp;

uint256 internal totalPoints;
// borrower => referrer
mapping(address => address) internal referrers;
// referrer => points
mapping(address => uint256) internal points;

event SetStartTimestamp(uint256 _startTimestamp);
event SetEndTimestamp(uint256 _endTimestamp);
event ExecuteReferral(address indexed borrower, address indexed referrer, uint256 points);

error InvalidTimestamp(uint256 timestamp);
error InvalidZeroAddress();
error InvalidSelfReferral();
error Unauthorized(address _caller);

modifier onlySatoshiBORouter() {
if (msg.sender != address(satoshiBORouter)) revert Unauthorized(msg.sender);
_;
}

constructor(ISatoshiBORouter _satoshiBORouter, uint256 _startTimestamp, uint256 _endTimestamp) {
if (address(_satoshiBORouter) == address(0)) revert InvalidZeroAddress();

satoshiBORouter = _satoshiBORouter;
startTimestamp = _startTimestamp;
endTimestamp = _endTimestamp;
}

function executeReferral(address _borrower, address _referrer, uint256 _points) external onlySatoshiBORouter {
// only execute referral if it's active
if (!_isReferralActive()) return;
// no referrer
if (_referrer == address(0)) return;
if (_borrower == _referrer) revert InvalidSelfReferral();

address currentReferrer = referrers[_borrower];
if (currentReferrer == address(0)) {
_setReferrer(_borrower, _referrer);
} else {
// use existing referrer
_referrer = currentReferrer;
}

_addPoint(_referrer, _points);
_addTotalPoints(_points);

emit ExecuteReferral(_borrower, _referrer, _points);
}

function isReferralActive() external view returns (bool) {
return _isReferralActive();
}

function _addPoint(address _account, uint256 _points) internal {
points[_account] += _points;
}

function _addTotalPoints(uint256 _points) internal {
totalPoints += _points;
}

function _setReferrer(address _account, address _referrer) internal {
referrers[_account] = _referrer;
}

function _isReferralActive() internal view returns (bool) {
uint256 _timestamp = block.timestamp;
return _timestamp >= startTimestamp && _timestamp <= endTimestamp;
}

function getTotalPoints() external view returns (uint256) {
return totalPoints;
}

function getBatchPoints(address[] calldata _accounts) external view returns (uint256[] memory) {
uint256[] memory _points = new uint256[](_accounts.length);
for (uint256 i = 0; i < _accounts.length; i++) {
_points[i] = points[_accounts[i]];
}
return _points;
}

function getPoints(address _account) external view returns (uint256) {
return points[_account];
}

function getBatchReferrers(address[] calldata _accounts) external view returns (address[] memory) {
address[] memory _referrers = new address[](_accounts.length);
for (uint256 i = 0; i < _accounts.length; i++) {
_referrers[i] = referrers[_accounts[i]];
}
return _referrers;
}

function getReferrer(address _account) external view returns (address) {
return referrers[_account];
}

function setStartTimestamp(uint256 _startTimestamp) external onlyOwner {
if (_startTimestamp > endTimestamp) revert InvalidTimestamp(_startTimestamp);
startTimestamp = _startTimestamp;
emit SetStartTimestamp(_startTimestamp);
}

function setEndTimestamp(uint256 _endTimestamp) external onlyOwner {
if (_endTimestamp < startTimestamp) revert InvalidTimestamp(_endTimestamp);
endTimestamp = _endTimestamp;
emit SetEndTimestamp(_endTimestamp);
}
}
29 changes: 23 additions & 6 deletions src/helpers/SatoshiBORouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {IBorrowerOperations} from "../interfaces/core/IBorrowerOperations.sol";
import {ITroveManager} from "../interfaces/core/ITroveManager.sol";
import {IDebtToken} from "../interfaces/core/IDebtToken.sol";
import {ISatoshiBORouter} from "./interfaces/ISatoshiBORouter.sol";
import {IReferralManager} from "./interfaces/IReferralManager.sol";
import {SatoshiMath} from "../dependencies/SatoshiMath.sol";

/**
Expand All @@ -16,15 +17,23 @@ import {SatoshiMath} from "../dependencies/SatoshiMath.sol";
contract SatoshiBORouter is ISatoshiBORouter {
IDebtToken public immutable debtToken;
IBorrowerOperations public immutable borrowerOperationsProxy;
IReferralManager public immutable referralManager;
IWETH public immutable weth;

constructor(IDebtToken _debtToken, IBorrowerOperations _borrowerOperationsProxy, IWETH _weth) {
constructor(
IDebtToken _debtToken,
IBorrowerOperations _borrowerOperationsProxy,
IReferralManager _referralManager,
IWETH _weth
) {
if (address(_debtToken) == address(0)) revert InvalidZeroAddress();
if (address(_borrowerOperationsProxy) == address(0)) revert InvalidZeroAddress();
if (address(_referralManager) == address(0)) revert InvalidZeroAddress();
if (address(_weth) == address(0)) revert InvalidZeroAddress();

debtToken = _debtToken;
borrowerOperationsProxy = _borrowerOperationsProxy;
referralManager = _referralManager;
weth = _weth;
}

Expand All @@ -37,7 +46,8 @@ contract SatoshiBORouter is ISatoshiBORouter {
uint256 _collAmount,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
address _lowerHint,
address _referrer
) external payable {
IERC20 collateralToken = troveManager.collateralToken();

Expand All @@ -52,7 +62,7 @@ contract SatoshiBORouter is ISatoshiBORouter {
uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this));
uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore;
require(userDebtAmount == _debtAmount, "SatoshiBORouter: Debt amount mismatch");
_afterWithdrawDebt(userDebtAmount);
_afterWithdrawDebt(account, _referrer, userDebtAmount);
}

function addColl(
Expand Down Expand Up @@ -102,7 +112,9 @@ contract SatoshiBORouter is ISatoshiBORouter {
uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this));
uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore;
require(userDebtAmount == _debtAmount, "SatoshiBORouter: Debt amount mismatch");
_afterWithdrawDebt(userDebtAmount);

address _referrer = referralManager.getReferrer(account);
_afterWithdrawDebt(account, _referrer, userDebtAmount);
}

function repayDebt(
Expand Down Expand Up @@ -162,7 +174,9 @@ contract SatoshiBORouter is ISatoshiBORouter {
if (_isDebtIncrease) {
uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore;
require(userDebtAmount == _debtChange, "SatoshiBORouter: Debt amount mismatch");
_afterWithdrawDebt(userDebtAmount);

address _referrer = referralManager.getReferrer(account);
_afterWithdrawDebt(account, _referrer, userDebtAmount);
}
}

Expand Down Expand Up @@ -215,10 +229,13 @@ contract SatoshiBORouter is ISatoshiBORouter {
debtToken.transferFrom(msg.sender, address(this), debtAmount);
}

function _afterWithdrawDebt(uint256 debtAmount) private {
function _afterWithdrawDebt(address _borrower, address _referrer, uint256 debtAmount) private {
if (debtAmount == 0) return;

debtToken.transfer(msg.sender, debtAmount);

// execute referral
referralManager.executeReferral(_borrower, _referrer, debtAmount);
}

receive() external payable {
Expand Down
30 changes: 30 additions & 0 deletions src/helpers/interfaces/IReferralManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

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

interface IReferralManager {
function satoshiBORouter() external view returns (ISatoshiBORouter);

function startTimestamp() external view returns (uint256);

function endTimestamp() external view returns (uint256);

function executeReferral(address _borrower, address _referrer, uint256 _points) external;

function isReferralActive() external view returns (bool);

function getTotalPoints() external view returns (uint256);

function getBatchPoints(address[] calldata _accounts) external view returns (uint256[] memory);

function getPoints(address _account) external view returns (uint256);

function getBatchReferrers(address[] calldata _accounts) external view returns (address[] memory);

function getReferrer(address _account) external view returns (address);

function setStartTimestamp(uint256 _startTimestamp) external;

function setEndTimestamp(uint256 _endTimestamp) external;
}
6 changes: 5 additions & 1 deletion src/helpers/interfaces/ISatoshiBORouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.13;
import {IDebtToken} from "../../interfaces/core/IDebtToken.sol";
import {IBorrowerOperations} from "../../interfaces/core/IBorrowerOperations.sol";
import {ITroveManager} from "../../interfaces/core/ITroveManager.sol";
import {IReferralManager} from "./IReferralManager.sol";
import {IWETH} from "./IWETH.sol";

interface ISatoshiBORouter {
Expand All @@ -17,6 +18,8 @@ interface ISatoshiBORouter {

function borrowerOperationsProxy() external view returns (IBorrowerOperations);

function referralManager() external view returns (IReferralManager);

function weth() external view returns (IWETH);

function openTrove(
Expand All @@ -26,7 +29,8 @@ interface ISatoshiBORouter {
uint256 _collAmount,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
address _lowerHint,
address _referrer
) external payable;

function addColl(
Expand Down
Loading