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
16 changes: 8 additions & 8 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ out = "out"
solc_version = "0.8.22"
optimizer = true
optimizer_runs = 200
fs_permissions = [{ access = "read", path = "./"}]
fs_permissions = [{ access = "read", path = "./" }]

libs = ['lib']

Expand All @@ -23,14 +23,14 @@ remappings = [
]

[fmt] # See https://book.getfoundry.sh/reference/config/formatter
int_types = "long"
line_length = 120
bracket_spacing = true
int_types = "long"
line_length = 120
bracket_spacing = true
multiline_func_header = "all"
number_underscore = "thousands"
quote_style = "double"
sort_imports = true
ignore = ['./lib/**/*']
number_underscore = "thousands"
quote_style = "double"
sort_imports = true
ignore = ['./lib/**/*']

[profile.default.rpc_endpoints]
holesky = "${RPC_URL_HOLESKY}"
Expand Down
29 changes: 29 additions & 0 deletions script/DeploySatoshiPeriphery.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { Script, console } from "forge-std/Script.sol";

import { SatoshiPeriphery } from "../src/core/helpers/SatoshiPeriphery.sol";
import { ISatoshiPeriphery } from "../src/core/helpers/interfaces/ISatoshiPeriphery.sol";
import { IDebtToken } from "../src/core/interfaces/IDebtToken.sol";

contract DeploySatoshiPeriphery is Script {
function run() external {
uint256 deployerKey = vm.envUint("DEPLOYMENT_PRIVATE_KEY");
address debtToken = vm.envAddress("DEBT_TOKEN");
address xApp = vm.envAddress("X_APP");
address owner = vm.envAddress("OWNER");

vm.startBroadcast(deployerKey);

address peripheryImpl = address(new SatoshiPeriphery());
bytes memory data = abi.encodeCall(ISatoshiPeriphery.initialize, (IDebtToken(debtToken), xApp, owner));
ISatoshiPeriphery periphery = ISatoshiPeriphery(address(new ERC1967Proxy(peripheryImpl, data)));

console.log("Implementation:", peripheryImpl);
console.log("Proxy:", address(periphery));

vm.stopBroadcast();
}
}
3 changes: 0 additions & 3 deletions script/strategyVault/cygnus/SetCygnusVaultConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
pragma solidity ^0.8.20;

import { CygnusVault } from "../../../src/vault/CygnusVault.sol";
import { IVault } from "../../../src/vault/interfaces/IVault.sol";
import { IVaultManager } from "../../../src/vault/interfaces/IVaultManager.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Script, console2 } from "forge-std/Script.sol";

address constant CYGNUS_VAULT_ADDRESS = 0xE8c5b4517610006C1fb0eD5467E01e4bAd43558D;
Expand Down
4 changes: 1 addition & 3 deletions script/upgrade/UpgradeNYM.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ contract UpgradeNYMScript is Script {

IERC2535DiamondCutInternal.FacetCut[] memory facetCuts = new IERC2535DiamondCutInternal.FacetCut[](1);
facetCuts[0] = IERC2535DiamondCutInternal.FacetCut({
target: newNYMImpl,
action: IERC2535DiamondCutInternal.FacetCutAction.ADD,
selectors: selectors
target: newNYMImpl, action: IERC2535DiamondCutInternal.FacetCutAction.ADD, selectors: selectors
});

ISatoshiXApp XAPP = ISatoshiXApp(SATOSHI_X_APP_ADDRESS);
Expand Down
2 changes: 2 additions & 0 deletions src/OSHI/RewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -423,4 +423,6 @@ contract RewardManager is IRewardManager, UUPSUpgradeable, OwnableUpgradeable {
) isRegistered = true;
require(isRegistered, "RewardManager: Caller is not Valid");
}

receive() external payable { }
}
8 changes: 1 addition & 7 deletions src/core/facets/FactoryFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,7 @@ contract FactoryFacet is IFactoryFacet, AccessControlInternal {
return ITroveManager(address(new BeaconProxy(address(s.troveManagerBeacon), data)));
}

function setTMRewardRate(
uint128[] calldata _numerator,
uint128 _denominator
)
external
onlyRole(Config.OWNER_ROLE)
{
function setTMRewardRate(uint128[] calldata _numerator, uint128 _denominator) external onlyRole(Config.OWNER_ROLE) {
AppStorage.Layout storage s = AppStorage.layout();
// console.log("setTMRewardRate", _numerator.length, s.troveManagers.length);
require(_numerator.length == s.troveManagers.length, "Factory: invalid length");
Expand Down
2 changes: 1 addition & 1 deletion src/core/facets/LiquidationFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ contract LiquidationFacet is ILiquidationFacet, AccessControlInternal, OwnableIn
if (singleLiquidation.debtToOffset == 0) continue;
debtInStabPool -= singleLiquidation.debtToOffset;
entireSystemColl -= (singleLiquidation.collToSendToSP + singleLiquidation.collSurplus)
* troveManagerValues.price;
* troveManagerValues.price;
entireSystemDebt -= singleLiquidation.debtToOffset;
_applyLiquidationValuesToTotals(totals, singleLiquidation);
unchecked {
Expand Down
1 change: 0 additions & 1 deletion src/core/facets/StabilityPoolFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { SatoshiMath } from "../../library/SatoshiMath.sol";

import { AppStorage } from "../AppStorage.sol";
import { Config } from "../Config.sol";
import { IDebtToken } from "../interfaces/IDebtToken.sol";
import {
AccountDeposit,
IStabilityPoolFacet,
Expand Down
88 changes: 79 additions & 9 deletions src/core/helpers/SatoshiPeriphery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ILiquidationFacet } from "../interfaces/ILiquidationFacet.sol";
import { ITroveManager } from "../interfaces/ITroveManager.sol";

import { IDebtToken } from "../interfaces/IDebtToken.sol";
import { INexusYieldManagerFacet } from "../interfaces/INexusYieldManagerFacet.sol";
import { ISatoshiPeriphery, LzSendParam } from "./interfaces/ISatoshiPeriphery.sol";
import { IWETH } from "./interfaces/IWETH.sol";
import {
Expand All @@ -25,18 +26,21 @@ import {
} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
* @title Satoshi Borrower Operations Router
* Handle the native token and ERC20 for the borrower operations
*/
contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradeable {
contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20;
using SafeERC20 for DebtTokenWithLz;

DebtTokenWithLz public debtToken;
address public xApp;
address public okxRouter;
address public okxApprove;

function initialize(IDebtToken _debtToken, address _xApp, address _owner) external initializer {
if (address(_debtToken) == address(0)) revert InvalidZeroAddress();
Expand All @@ -47,6 +51,7 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea

__Ownable_init(_owner);
__UUPSUpgradeable_init_unchained();
__ReentrancyGuard_init_unchained();
}

receive() external payable {
Expand Down Expand Up @@ -97,14 +102,7 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea
/// @param _collAmount The amount of additional collateral
/// @param _upperHint The upper hint (for querying the position of the sorted trove)
/// @param _lowerHint The lower hint (for querying the position of the sorted trove)
function addColl(
ITroveManager troveManager,
uint256 _collAmount,
address _upperHint,
address _lowerHint
)
external
{
function addColl(ITroveManager troveManager, uint256 _collAmount, address _upperHint, address _lowerHint) external {
IERC20 collateralToken = troveManager.collateralToken();

_beforeAddColl(collateralToken, _collAmount);
Expand Down Expand Up @@ -346,4 +344,76 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea
function _authorizeUpgrade(address newImplementation) internal view override onlyOwner {
// No additional authorization logic is needed for this contract
}

/// @notice Set the OKX DEX router address (routerContractAddress from OKX /swap API)
function setOkxRouter(address _okxRouter) external onlyOwner {
if (_okxRouter == address(0)) revert InvalidZeroAddress();
okxRouter = _okxRouter;
emit OkxRouterSet(_okxRouter);
}

/// @notice Set the OKX token-approve proxy address (tokenApproveAddress from OKX /approve-transaction API)
function setOkxApprove(address _okxApprove) external onlyOwner {
if (_okxApprove == address(0)) revert InvalidZeroAddress();
okxApprove = _okxApprove;
emit OkxApproveSet(_okxApprove);
}

/// @notice Swap any ERC20 → stable token via OKX DEX → DebtToken via NYM.swapIn
/// @dev ERC20 only — native token input is not supported
/// @dev fromToken must be pre-approved to this contract by msg.sender
/// @dev okxCalldata must be generated with this contract address as userWalletAddress
/// @dev DebtToken is always delivered to msg.sender on the same chain
function swapInWithOkx(
address fromToken,
uint256 fromAmount,
bytes calldata okxCalldata,
address stableAsset,
uint256 minDebtAmount
)
public
nonReentrant
{
address _okxRouter = okxRouter;
address _okxApprove = okxApprove;
require(_okxRouter != address(0), "SatoshiPeriphery: okxRouter not set");
require(_okxApprove != address(0), "SatoshiPeriphery: okxApprove not set");

IERC20(fromToken).safeTransferFrom(msg.sender, address(this), fromAmount);

uint256 fromTokenBefore = IERC20(fromToken).balanceOf(address(this));

// Must approve the token approve proxy, NOT the router — OKX uses a separate spender contract
IERC20(fromToken).forceApprove(_okxApprove, fromAmount);

uint256 stableBalanceBefore = IERC20(stableAsset).balanceOf(address(this));

(bool success,) = _okxRouter.call(okxCalldata);
require(success, "SatoshiPeriphery: OKX swap failed");

IERC20(fromToken).forceApprove(_okxApprove, 0);

uint256 fromTokenAfter = IERC20(fromToken).balanceOf(address(this));
if (fromTokenAfter > fromTokenBefore - fromAmount) {
uint256 refund = fromTokenAfter - (fromTokenBefore - fromAmount);
IERC20(fromToken).safeTransfer(msg.sender, refund);
}

uint256 stableReceived = IERC20(stableAsset).balanceOf(address(this)) - stableBalanceBefore;
require(stableReceived > 0, "SatoshiPeriphery: No stable tokens received from OKX");

IERC20(stableAsset).forceApprove(xApp, stableReceived);

uint256 debtTokenBalanceBefore = debtToken.balanceOf(address(this));

INexusYieldManagerFacet(xApp).swapIn(stableAsset, address(this), stableReceived);

uint256 debtTokenReceived = debtToken.balanceOf(address(this)) - debtTokenBalanceBefore;

if (debtTokenReceived < minDebtAmount) {
revert SlippageTooHigh(debtTokenReceived, minDebtAmount);
}

debtToken.safeTransfer(msg.sender, debtTokenReceived);
}
}
35 changes: 28 additions & 7 deletions src/core/helpers/interfaces/ISatoshiPeriphery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,25 @@ interface ISatoshiPeriphery {
error InvalidZeroAddress();
error RefundFailed();
error InsufficientMsgValue(uint256 msgValue, uint256 requiredValue);
error SlippageTooHigh(uint256 actual, uint256 minimum);

event OkxRouterSet(address indexed okxRouter);
event OkxApproveSet(address indexed okxApprove);

function debtToken() external view returns (DebtTokenWithLz);

function xApp() external view returns (address);

function okxRouter() external view returns (address);

function okxApprove() external view returns (address);

function initialize(IDebtToken _debtToken, address _xApp, address _owner) external;

function setOkxRouter(address _okxRouter) external;

function setOkxApprove(address _okxApprove) external;

function openTrove(
ITroveManager troveManager,
uint256 _maxFeePercentage,
Expand All @@ -41,13 +53,7 @@ interface ISatoshiPeriphery {
external
payable;

function addColl(
ITroveManager troveManager,
uint256 _collAmount,
address _upperHint,
address _lowerHint
)
external;
function addColl(ITroveManager troveManager, uint256 _collAmount, address _upperHint, address _lowerHint) external;

function withdrawColl(
ITroveManager troveManager,
Expand Down Expand Up @@ -100,4 +106,19 @@ interface ISatoshiPeriphery {
)
external
payable;

/// @notice Swap any ERC20 → stable token via OKX DEX → DebtToken via NYM.swapIn (ERC20 only)
/// @param fromToken Input ERC20 token (must be approved to this contract)
/// @param fromAmount Raw input amount
/// @param okxCalldata OKX swap calldata from backend /okx/nym-swap response
/// @param stableAsset Stable token address from backend /okx/nym-swap response
/// @param minDebtAmount Minimum DebtToken to receive; revert if below this (slippage guard)
function swapInWithOkx(
address fromToken,
uint256 fromAmount,
bytes calldata okxCalldata,
address stableAsset,
uint256 minDebtAmount
)
external;
}
9 changes: 1 addition & 8 deletions src/core/helpers/interfaces/ITroveHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@ pragma solidity ^0.8.20;
import { ITroveManager } from "../../interfaces/ITroveManager.sol";

interface ITroveHelper {
function getNicrByTime(
ITroveManager troveManager,
address _borrower,
uint256 time
)
external
view
returns (uint256);
function getNicrByTime(ITroveManager troveManager, address _borrower, uint256 time) external view returns (uint256);

function getNicrListByTime(
ITroveManager troveManager,
Expand Down
5 changes: 1 addition & 4 deletions src/core/helpers/interfaces/ITroveManagerGetter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,5 @@ interface ITroveManagerGetter {

function getAllCollateralsAndTroveManagers() external view returns (Collateral[] memory);

function getActiveTroveManagersForAccount(address account)
external
view
returns (ITroveManager[] memory, uint256);
function getActiveTroveManagersForAccount(address account) external view returns (ITroveManager[] memory, uint256);
}
6 changes: 3 additions & 3 deletions src/core/libs/BorrowerOperationsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ library BorrowerOperationsLib {
uint256 loopEnd = balances.collaterals.length;
for (uint256 i; i < loopEnd;) {
totalPricedCollateral += (SatoshiMath._getScaledCollateralAmount(
balances.collaterals[i], balances.decimals[i]
)
* balances.prices[i]);
balances.collaterals[i], balances.decimals[i]
)
* balances.prices[i]);
totalDebt += balances.debts[i];
unchecked {
++i;
Expand Down
5 changes: 1 addition & 4 deletions src/core/libs/StabilityPoolLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,7 @@ library StabilityPoolLib {
return releasedToken;
}

function _getCompoundedDebtDeposit(
AppStorage.Layout storage s,
address _depositor
)
function _getCompoundedDebtDeposit(AppStorage.Layout storage s, address _depositor)
internal
view
returns (uint256)
Expand Down
8 changes: 1 addition & 7 deletions src/library/interfaces/vault/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,7 @@ interface IDelegationManager {
/**
* @notice Given array of strategies, returns array of shares for the operator
*/
function getOperatorShares(
address operator,
IStrategy[] memory strategies
)
external
view
returns (uint256[] memory);
function getOperatorShares(address operator, IStrategy[] memory strategies) external view returns (uint256[] memory);

/**
* @notice Given a list of strategies, return the minimum cooldown that must pass to withdraw
Expand Down
8 changes: 1 addition & 7 deletions src/library/interfaces/vault/ISlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,7 @@ interface ISlasher {
returns (uint32);

/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
function getMiddlewareTimesIndexServeUntilTimestamp(
address operator,
uint32 index
)
external
view
returns (uint32);
function getMiddlewareTimesIndexServeUntilTimestamp(address operator, uint32 index) external view returns (uint32);

/// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256);
Expand Down
Loading