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
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ interface IClearingHouseCustomErrors is IClearingHouseStructures {

/// @notice error to denote an invalid setting for parameters
error InvalidSetting(uint256 errorCode);

error TimelockBreached();

error InvalidAtomicSwap();
}
14 changes: 14 additions & 0 deletions contracts/interfaces/clearinghouse/IClearingHouseEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,18 @@ interface IClearingHouseEvents is IClearingHouseStructures {
);

event PausedUpdated(bool paused);

event AtomicSwapInitiated(
uint256 atomicSwapId,
uint256 senderAccountId,
uint256 receiverAccountId,
int256 vTokenAmount,
int256 vQuoteAmount,
uint32 poolId,
uint64 timelock
);

event AtomicSwapExecuted(uint256 atomicSwapId);

event AtomicSwapAllowanceUpdated(uint256 accountId, bool isAllowed);
}
31 changes: 31 additions & 0 deletions contracts/libraries/Account.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ library Account {
address owner;
VTokenPosition.Set tokenPositions;
CollateralDeposit.Set collateralDeposits;
bool isAtomicSwapAllowed;
uint256[100] _emptySlots; // reserved for adding variables when upgrading logic
}

Expand Down Expand Up @@ -389,6 +390,36 @@ library Account {
account._updateVQuoteBalance(-int256(limitOrderFee));
}

/// @notice swaps 'vToken' of token amount equal to 'swapParams.amount'
/// @notice if vTokenAmount>0 then the swap is a long or close short and if vTokenAmount<0 then swap is a short or close long
/// @notice isNotional specifies whether the amount represents token amount (false) or vQuote amount(true)
/// @notice isPartialAllowed specifies whether to revert (false) or to execute a partial swap (true)
/// @notice sqrtPriceLimit threshold sqrt price which if crossed then revert or execute partial swap
function atomicSwapToken(
Account.Info storage senderAccount,
Account.Info storage receiverAccount,
uint32 poolId,
int256 vTokenAmount,
int256 vQuoteAmount,
Protocol.Info storage protocol
) external {
// make a swap. vQuoteIn and vTokenAmountOut (in and out wrt uniswap).
// mints erc20 tokens in callback and send to the pool
IClearingHouseStructures.BalanceAdjustments memory senderBalanceAdjustments = IClearingHouseStructures
.BalanceAdjustments(vQuoteAmount, vTokenAmount, vTokenAmount);
IClearingHouseStructures.BalanceAdjustments memory receiverBalanceAdjustments = IClearingHouseStructures
.BalanceAdjustments(-vQuoteAmount, -vTokenAmount, -vTokenAmount);

senderAccount.tokenPositions.update(senderAccount.id, senderBalanceAdjustments, poolId, protocol);
receiverAccount.tokenPositions.update(receiverAccount.id, receiverBalanceAdjustments, poolId, protocol);

//TODO: check if there needs to be settleProfit

// after all the stuff, account should be above water
senderAccount._checkIfMarginAvailable(true, protocol);
receiverAccount._checkIfMarginAvailable(true, protocol);
}

/**
* External view methods
*/
Expand Down
17 changes: 17 additions & 0 deletions contracts/libraries/AtomicVTokenSwap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.4;

/// @title Protocol storage functions
/// @dev This is used as main storage interface containing protocol info
library AtomicVTokenSwap {
struct Info {
uint256 senderAccountId;
uint256 receiverAccountId;
int256 vTokenAmount;
int256 vQuoteAmount;
uint32 poolId;
uint64 timelock;
bool complete;
}
}
74 changes: 74 additions & 0 deletions contracts/protocol/clearinghouse/ClearingHouse.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AddressHelper } from '../../libraries/AddressHelper.sol';
import { BatchedLoop } from '../../libraries/BatchedLoop.sol';
import { Protocol } from '../../libraries/Protocol.sol';
import { SignedMath } from '../../libraries/SignedMath.sol';
import { AtomicVTokenSwap } from '../../libraries/AtomicVTokenSwap.sol';

import { IClearingHouse } from '../../interfaces/IClearingHouse.sol';
import { IInsuranceFund } from '../../interfaces/IInsuranceFund.sol';
Expand Down Expand Up @@ -179,6 +180,18 @@ contract ClearingHouse is
if (completed) _unpause();
}

function allowAccountForAtomicSwap(uint256 accountId, bool isAllowed)
external
onlyGovernanceOrTeamMultisig
whenNotPaused
{
bool isAtomicSwapAllowed = accounts[accountId].isAtomicSwapAllowed;
if (isAtomicSwapAllowed != isAllowed) {
accounts[accountId].isAtomicSwapAllowed = isAllowed;
emit AtomicSwapAllowanceUpdated(accountId, isAllowed);
} else revert InvalidSetting(0x40);
}

/// @inheritdoc IClearingHouseOwnerActions
function withdrawProtocolFee(uint256 numberOfPoolsToUpdateInThisTx) external {
withdrawProtocolFeeLoop.iterate({
Expand Down Expand Up @@ -247,6 +260,67 @@ contract ClearingHouse is
return _swapToken(account, poolId, swapParams, true);
}

function initiateAtomicSwapToken(
uint256 accountId,
uint256 receiverAccountId,
int256 vTokenAmount,
int256 vQuoteAmount,
uint32 poolId,
uint64 timelock
) external whenNotPaused returns (uint256 atomicSwapId) {
Account.Info storage account = _getAccountAndCheckOwner(accountId);

atomicSwapId = numAtomicSwaps++;

//check if both accounts (sender and receiver) are whitelisted for atomic swaps
if (!account.isAtomicSwapAllowed || !accounts[receiverAccountId].isAtomicSwapAllowed)
revert InvalidAtomicSwap();

atomicSwaps[atomicSwapId] = AtomicVTokenSwap.Info(
accountId,
receiverAccountId,
vTokenAmount,
vQuoteAmount,
poolId,
timelock,
false
);

emit AtomicSwapInitiated(
atomicSwapId,
accountId,
receiverAccountId,
vTokenAmount,
vQuoteAmount,
poolId,
timelock
);
}

function executeAtomicSwapToken(uint256 atomicSwapId) external whenNotPaused {
AtomicVTokenSwap.Info memory swapInfo = atomicSwaps[atomicSwapId];
Account.Info storage receiverAccount = _getAccountAndCheckOwner(swapInfo.receiverAccountId);
Account.Info storage senderAccount = accounts[swapInfo.senderAccountId];
_updateAccountPoolPrices(senderAccount);
_updateAccountPoolPrices(receiverAccount);

// check timelock
if (block.timestamp > swapInfo.timelock) revert TimelockBreached();

Account.atomicSwapToken(
senderAccount,
receiverAccount,
swapInfo.poolId,
swapInfo.vTokenAmount,
swapInfo.vQuoteAmount,
protocol
);

swapInfo.completed = true;

emit AtomicSwapExecuted(atomicSwapId);
}

/// @inheritdoc IClearingHouseActions
function updateRangeOrder(
uint256 accountId,
Expand Down
7 changes: 6 additions & 1 deletion contracts/protocol/clearinghouse/ClearingHouseStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity =0.8.14;
import { Account } from '../../libraries/Account.sol';
import { BatchedLoop } from '../../libraries/BatchedLoop.sol';
import { Protocol } from '../../libraries/Protocol.sol';
import { AtomicVTokenSwap } from '../../libraries/AtomicVTokenSwap.sol';

import { IInsuranceFund } from '../../interfaces/IInsuranceFund.sol';
import { IOracle } from '../../interfaces/IOracle.sol';
Expand All @@ -28,6 +29,10 @@ abstract contract ClearingHouseStorage {
BatchedLoop.Info internal unpauseLoop;
BatchedLoop.Info internal withdrawProtocolFeeLoop;

// storage for atomic token swap
mapping(uint256 => AtomicVTokenSwap.Info) atomicSwaps;
uint256 public numAtomicSwaps;

// reserved for adding slots in future
uint256[100] private _emptySlots2;
uint256[98] private _emptySlots2;
}