Skip to content
Merged
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
561 changes: 561 additions & 0 deletions contracts/PegInContract.sol

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions contracts/PegOutContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ contract PegOutContract is
/// @param newTime the new Bitcoin block time
event BtcBlockTimeSet(uint256 indexed oldTime, uint256 indexed newTime);

// solhint-disable-next-line comprehensive-interface
receive() external payable {
revert Flyover.PaymentNotAllowed();
}

/// @inheritdoc IPegOut
function depositPegOut(
Quotes.PegOutQuote calldata quote,
Expand All @@ -66,7 +71,7 @@ contract PegOutContract is
}
uint256 requiredAmount = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee;
if (msg.value < requiredAmount) {
revert InsufficientAmount(msg.value, requiredAmount);
revert Flyover.InsufficientAmount(msg.value, requiredAmount);
}
if (quote.depositDateLimit < block.timestamp || quote.expireDate < block.timestamp) {
revert QuoteExpiredByTime(quote.depositDateLimit, quote.expireDate);
Expand Down Expand Up @@ -179,7 +184,7 @@ contract PegOutContract is

Quotes.PegOutQuote memory quote = _pegOutQuotes[quoteHash];
if (quote.lbcAddress == address(0)) revert Flyover.QuoteNotFound(quoteHash);
if (quote.lpRskAddress != msg.sender) revert InvalidSender(quote.lpRskAddress, msg.sender);
if (quote.lpRskAddress != msg.sender) revert Flyover.InvalidSender(quote.lpRskAddress, msg.sender);

BtcUtils.TxRawOutput[] memory outputs = BtcUtils.getOutputs(btcTx);
_validateBtcTxNullData(outputs, quoteHash);
Expand All @@ -194,7 +199,7 @@ contract PegOutContract is
_addDaoContribution(quote.lpRskAddress, quote.productFeeAmount);

if (_shouldPenalize(quote, quoteHash, btcBlockHeaderHash)) {
_collateralManagement.slashPegOutCollateral(quote, quoteHash);
_collateralManagement.slashPegOutCollateral(msg.sender, quote, quoteHash);
}

uint256 refundAmount = quote.value + quote.callFee + quote.gasFee;
Expand All @@ -219,7 +224,7 @@ contract PegOutContract is
_pegOutRegistry[quoteHash].completed = true;

emit PegOutUserRefunded(quoteHash, addressToTransfer, valueToTransfer);
_collateralManagement.slashPegOutCollateral(quote, quoteHash);
_collateralManagement.slashPegOutCollateral(msg.sender, quote, quoteHash);

(bool sent, bytes memory reason) = addressToTransfer.call{value: valueToTransfer}("");
if (!sent) {
Expand Down Expand Up @@ -348,7 +353,7 @@ contract PegOutContract is
requiredAmount = quote.value - (quote.value % _SAT_TO_WEI_CONVERSION);
}
uint256 paidAmount = outputs[_PAY_TO_ADDRESS_OUTPUT].value * _SAT_TO_WEI_CONVERSION;
if (paidAmount < requiredAmount) revert InsufficientAmount(paidAmount, requiredAmount);
if (paidAmount < requiredAmount) revert Flyover.InsufficientAmount(paidAmount, requiredAmount);
}

/// @notice This function is used to validate the null data of the Bitcoin transaction. The null data
Expand Down
15 changes: 12 additions & 3 deletions contracts/interfaces/ICollateralManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ interface ICollateralManagement {
address indexed liquidityProvider,
bytes32 indexed quoteHash,
Flyover.ProviderType indexed collateralType,
uint penalty
uint256 penalty,
uint256 reward
);

error AlreadyResigned(address from);
Expand All @@ -25,10 +26,18 @@ interface ICollateralManagement {

function addPegInCollateralTo(address addr) external payable;
function addPegInCollateral() external payable;
function slashPegInCollateral(Quotes.PegInQuote calldata quote, bytes32 quoteHash) external;
function slashPegInCollateral(
address punisher,
Quotes.PegInQuote calldata quote,
bytes32 quoteHash
) external;
function addPegOutCollateralTo(address addr) external payable;
function addPegOutCollateral() external payable;
function slashPegOutCollateral(Quotes.PegOutQuote calldata quote, bytes32 quoteHash) external;
function slashPegOutCollateral(
address punisher,
Quotes.PegOutQuote calldata quote,
bytes32 quoteHash
) external;

function getPegInCollateral(address addr) external view returns (uint256);
function getPegOutCollateral(address addr) external view returns (uint256);
Expand Down
165 changes: 165 additions & 0 deletions contracts/interfaces/IPegIn.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {Quotes} from "../libraries/Quotes.sol";

/// @title PegIn interface
/// @notice This interface is used to expose the required functions to provide the Flyover peg in service
interface IPegIn {

/// @notice The states of a peg in quote
/// @dev The quote set to CALL_DONE when the callForUser function is called
/// @dev The quote set to PROCESSED_QUOTE when the registerPegIn function is called
/// @dev The quote set to UNPROCESSED_QUOTE when the the contract is not aware of the quote
enum PegInStates { UNPROCESSED_QUOTE, CALL_DONE, PROCESSED_QUOTE }

/// @notice Emitted when the balance of a liquidity provider increases
/// @param dest The address of the liquidity provider
/// @param amount The amount of the increase
event BalanceIncrease(address indexed dest, uint256 indexed amount);

/// @notice Emitted when the balance of a liquidity provider decreases
/// @param dest The address of the liquidity provider
/// @param amount The amount of the decrease
event BalanceDecrease(address indexed dest, uint256 indexed amount);

/// @notice Emitted when a liquidity provider withdraws funds
/// @param from The address of the liquidity provider
/// @param amount The amount of the withdrawal
event Withdrawal(address indexed from, uint256 indexed amount);

/// @notice Emitted when the bridge capacity is exceeded
/// @param quoteHash The hash of the quote
/// @param errorCode The error code returned by the bridge
event BridgeCapExceeded(bytes32 indexed quoteHash, int256 indexed errorCode);

/// @notice Emitted when a peg in is registered successfully
/// @param quoteHash The hash of the quote
/// @param transferredAmount The amount of the peg in
event PegInRegistered(bytes32 indexed quoteHash, uint256 indexed transferredAmount);

/// @notice Emitted when a user is refunded. This can happen if the liquidity provider
/// fails to provide the service, if the user's payment was invalid, if the user requires
/// to receive the change of their payment
/// @param dest The address of the user
/// @param quoteHash The hash of the quote
/// @param amount The amount of the refund
/// @param success Whether the refund was successful or not
event Refund(address indexed dest, bytes32 indexed quoteHash, uint indexed amount, bool success);

/// @notice Emitted when a call is made on behalf of the user
/// @param from The address of the caller
/// @param dest The address of the destination
/// @param quoteHash The hash of the quote
/// @param gasLimit The gas limit of the call
/// @param value The value of the call
/// @param data The data of the call
/// @param success Whether the call was successful or not
event CallForUser(
address indexed from,
address indexed dest,
bytes32 indexed quoteHash,
uint gasLimit,
uint value,
bytes data,
bool success
);

/// @notice This error is emitted when the refund address is invalid
/// @param refundAddress The refund address that is invalid
error InvalidRefundAddress(bytes refundAddress);

/// @notice This error is emitted when the amount is under the bridge's minimum amount
/// @param amount The amount that is under the bridge's minimum
error AmountUnderMinimum(uint256 amount);

/// @notice This error is emitted when the quote has already been processed.
/// This can happen if the callForUser or the registerPegIn functions are being
/// called twice with the same quote
/// @param quoteHash The hash of the quote
error QuoteAlreadyProcessed(bytes32 quoteHash);

/// @notice This error is emitted when the gas limit is insufficient to make the call
/// on behalf of the user
/// @param gasLeft The amount of gas left
/// @param gasRequired The amount of gas required
error InsufficientGas(uint256 gasLeft, uint256 gasRequired);

/// @notice This error is emitted when the bridge needs more confirmations in order to
/// be capable of registering the peg in
error NotEnoughConfirmations();

/// @notice This error is emitted when the bridge returns an unexpected error code
/// @param errorCode The error code returned by the bridge
error UnexpectedBridgeError(int256 errorCode);

/// @notice This function is used to deposit funds into the contract to provide the
/// peg in service
/// @dev This function is only callable by a liquidity provider
function deposit() external payable;

/// @notice This function is used to make a peg in call on behalf of the user
/// @dev This function is only callable by a liquidity provider. The value of the call
/// will be added to the liquidity provider's balance.
/// @param quote The quote of the peg in
/// @return success Whether the call was successful or not
function callForUser(Quotes.PegInQuote calldata quote) external payable returns (bool);

/// @notice This function is used to withdraw funds from the contract
/// @dev This function is only callable by a liquidity provider. They can partially
/// withdraw their balance, this includes the profit made with the peg in service.
/// @param amount The amount of the withdrawal
function withdraw(uint256 amount) external;

/// @notice This function is used to register a peg in in the bridge.
/// It refunds the proper parties and penalizes the liquidity provider
/// if applicable.
/// @dev This function can be called by anyone
/// @param quote The quote of the peg in
/// @param signature The signature of the quoteHash by the liquidity provider
/// @param btcRawTransaction The raw transaction of the peg in in the Bitcoin network
/// @param partialMerkleTree The partial merkle tree proving the inclusion of the peg
/// in transaction
/// @param height The height of the peg in transaction
/// @return registerResult The result of the registration. It can be:
/// - A negative value: An error code returned by the bridge
/// - A positive value: The amount of the peg in transaction
function registerPegIn(
Quotes.PegInQuote calldata quote,
bytes calldata signature,
bytes calldata btcRawTransaction,
bytes calldata partialMerkleTree,
uint256 height
) external returns (int256);

/// @notice This function is used to get the balance of a liquidity provider
/// @param addr The address of the liquidity provider
/// @return balance The balance of the liquidity provider
function getBalance(address addr) external view returns (uint256);

/// @notice This function is used to get the status of a peg in quote
/// @param quoteHash The hash of the quote
/// @return status The status of the quote. Any value of the PegInStates enum
function getQuoteStatus(bytes32 quoteHash) external view returns (PegInStates);

/// @notice This function is used to validate the deposit address of a peg in quote
/// @dev This function is used to validate the derivation address returned by the
/// liquidity provider.
/// @param quote The quote of the peg in
/// @param depositAddress The deposit address to validate
/// @return isValid Whether the deposit address is valid or not
function validatePegInDepositAddress(
Quotes.PegInQuote calldata quote,
bytes calldata depositAddress
) external view returns (bool);

/// @notice This view is used to get the hash of a peg in quote, this should be used as the
/// single source of truth so all the involved parties can compute the quote hash in the same way
/// @param quote The quote of the peg in
/// @return quoteHash The hash of the quote
function hashPegInQuote(Quotes.PegInQuote calldata quote) external view returns (bytes32);

/// @notice This function is used to get the minimum peg in amount allowed by the protocol
/// @return minPegIn The minimum peg in amount
function getMinPegIn() external view returns (uint256);
}
10 changes: 0 additions & 10 deletions contracts/interfaces/IPegOut.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ interface IPegOut {
uint256 amount
);

/// @notice This error is emitted when the amount sent is less than the amount required to pay for the quote
/// @param amount the amount sent
/// @param target the amount required to pay for the quote
error InsufficientAmount(uint256 amount, uint256 target);

/// @notice This error is emitted when the quote has expired by the number of blocks
/// @param expireBlock the number of blocks the quote has expired
error QuoteExpiredByBlocks(uint32 expireBlock);
Expand Down Expand Up @@ -83,11 +78,6 @@ interface IPegOut {
/// @param actual the actual quote hash
error InvalidQuoteHash(bytes32 expected, bytes32 actual);

/// @notice This error is emitted when the sender is not allowed to perform a specific operation
/// @param expected the expected sender
/// @param actual the actual sender
error InvalidSender(address expected, address actual);

/// @notice This error is emitted when the get confirmations from the rootstock bridge fails
/// @param errorCode The error code returned by the rootstock bridge
error UnableToGetConfirmations(int errorCode);
Expand Down
12 changes: 11 additions & 1 deletion contracts/libraries/Flyover.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ library Flyover {
error IncorrectContract(address expected, address actual);
error QuoteNotFound(bytes32 quoteHash);
error PaymentFailed(address addr, uint amount, bytes reason);
error EmptyBlockHeader(bytes32 blockHash);
error EmptyBlockHeader(bytes32 heightOrHash);
error NoBalance(uint256 wanted, uint256 actual);
error NoContract(address addr);
error PaymentNotAllowed();
/// @notice This error is emitted when the sender is not allowed to perform a specific operation
/// @param expected the expected sender
/// @param actual the actual sender
error InvalidSender(address expected, address actual);
/// @notice This error is emitted when the amount sent is less than the amount required to pay for the quote
/// @param amount the amount sent
/// @param target the amount required to pay for the quote
error InsufficientAmount(uint256 amount, uint256 target);
error Overflow(uint256 passedAmount);
}
29 changes: 23 additions & 6 deletions contracts/split/CollateralManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ contract CollateralManagementContract is

event MinCollateralSet(uint256 oldMinCollateral, uint256 newMinCollateral);
event ResignDelayInBlocksSet(uint oldResignDelayInBlocks, uint newResignDelayInBlocks);
event RewardPercentageSet(uint256 indexed oldReward, uint256 indexed newReward);

uint private _minCollateral;
uint private _resignDelayInBlocks;
mapping(address => uint) private _pegInCollateral;
mapping(address => uint) private _pegOutCollateral;
mapping(address => uint) private _resignationBlockNum;
mapping(address => uint256) private _pegInCollateral;
mapping(address => uint256) private _pegOutCollateral;
mapping(address => uint256) private _resignationBlockNum;
mapping(address => uint256) private _rewards;
uint256 public rewardPercentage;

modifier onlyRegisteredForPegIn() {
if (!_isRegistered(Flyover.ProviderType.PegIn, msg.sender))
Expand All @@ -42,12 +45,14 @@ contract CollateralManagementContract is
address owner,
uint48 initialDelay,
uint minCollateral,
uint resignDelayInBlocks
uint resignDelayInBlocks,
uint256 rewardPercentage_
) public initializer {
__AccessControlDefaultAdminRules_init(initialDelay, owner);
__ReentrancyGuard_init();
_minCollateral = minCollateral;
_resignDelayInBlocks = resignDelayInBlocks;
rewardPercentage = rewardPercentage_;
}

function setMinCollateral(uint minCollateral) external onlyRole(DEFAULT_ADMIN_ROLE) {
Expand All @@ -60,6 +65,12 @@ contract CollateralManagementContract is
_resignDelayInBlocks = resignDelayInBlocks;
}

// solhint-disable-next-line comprehensive-interface
function setRewardPercentage(uint256 rewardPercentage_) external onlyRole(DEFAULT_ADMIN_ROLE) {
emit RewardPercentageSet(rewardPercentage, rewardPercentage_);
rewardPercentage = rewardPercentage_;
}

function getPegInCollateral(address addr) external view returns (uint) {
return _pegInCollateral[addr];
}
Expand All @@ -81,6 +92,7 @@ contract CollateralManagementContract is
}

function slashPegInCollateral(
address punisher,
Quotes.PegInQuote calldata quote,
bytes32 quoteHash
) external onlyRole(COLLATERAL_SLASHER) {
Expand All @@ -89,7 +101,9 @@ contract CollateralManagementContract is
_pegInCollateral[quote.liquidityProviderRskAddress]
);
_pegInCollateral[quote.liquidityProviderRskAddress] -= penalty;
emit Penalized(quote.liquidityProviderRskAddress, quoteHash, Flyover.ProviderType.PegIn, penalty);
uint256 punisherReward = (penalty * rewardPercentage) / 100;
_rewards[punisher] += punisherReward;
emit Penalized(quote.liquidityProviderRskAddress, quoteHash, Flyover.ProviderType.PegIn, penalty, punisherReward);
}

function addPegOutCollateralTo(address addr) external onlyRole(COLLATERAL_ADDER) payable {
Expand All @@ -101,6 +115,7 @@ contract CollateralManagementContract is
}

function slashPegOutCollateral(
address punisher,
Quotes.PegOutQuote calldata quote,
bytes32 quoteHash
) external onlyRole(COLLATERAL_SLASHER) {
Expand All @@ -109,7 +124,9 @@ contract CollateralManagementContract is
_pegOutCollateral[quote.lpRskAddress]
);
_pegOutCollateral[quote.lpRskAddress] -= penalty;
emit Penalized(quote.lpRskAddress, quoteHash, Flyover.ProviderType.PegOut, penalty);
uint256 punisherReward = (penalty * rewardPercentage) / 100;
_rewards[punisher] += punisherReward;
emit Penalized(quote.lpRskAddress, quoteHash, Flyover.ProviderType.PegOut, penalty, punisherReward);
}

function getMinCollateral() external view returns (uint) {
Expand Down
Loading