Created
December 28, 2021 14:00
-
-
Save sunnyRK/11f8179974c16d2f23f54bcaaa6d9331 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { ethers } = require("hardhat"); | |
const { expect, use } = require("chai"); | |
const {solidity} = require('ethereum-waffle'); | |
const { utils } = require('ethers'); | |
const { parseEther, parseUnits } = require("ethers/lib/utils") | |
const { BigNumber } = require("@ethersproject/bignumber") | |
const { loadPerpLushanInfo, snapshot, revertToSnapshot, fromBigNumber } = require("./utils"); | |
const bn = require("bignumber.js"); | |
bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 }) | |
const ClearingHouseAbi = require('../perp-lushan/artifacts/contracts/test/TestClearingHouse.sol/TestClearingHouse.json') | |
const OrderBookAbi = require('../perp-lushan/artifacts/contracts/OrderBook.sol/OrderBook.json') | |
const ClearingHouseConfigAbi = require('../perp-lushan/artifacts/contracts/ClearingHouseConfig.sol/ClearingHouseConfig.json') | |
const VaultAbi = require('../perp-lushan/artifacts/contracts/Vault.sol/Vault.json') | |
const ExchangeAbi = require('../perp-lushan/artifacts/contracts/Exchange.sol/Exchange.json') | |
const MarketRegistryAbi = require('../perp-lushan/artifacts/contracts/MarketRegistry.sol/MarketRegistry.json') | |
const TestERC20Abi = require('../perp-lushan/artifacts/contracts/test/TestERC20.sol/TestERC20.json') | |
const BaseTokenAbi = require('../perp-lushan/artifacts/contracts/BaseToken.sol/BaseToken.json') | |
const BaseToken2Abi = require('../perp-lushan/artifacts/contracts/BaseToken.sol/BaseToken.json') | |
const QuoteTokenAbi = require('../perp-lushan/artifacts/contracts/QuoteToken.sol/QuoteToken.json') | |
const AccountBalanceAbi = require('../perp-lushan/artifacts/contracts/AccountBalance.sol/AccountBalance.json') | |
const MockTestAggregatorV3Abi = require('../perp-lushan/artifacts/contracts/mock/MockTestAggregatorV3.sol/MockTestAggregatorV3.json') | |
const UniswapV3PoolAbi = require('../perp-lushan/artifacts/@uniswap/v3-core/contracts/UniswapV3Pool.sol/UniswapV3Pool.json') | |
const UniswapV3Pool2Abi = require('../perp-lushan/artifacts/@uniswap/v3-core/contracts/UniswapV3Pool.sol/UniswapV3Pool.json'); | |
const QuoterAbi = require('../perp-lushan/artifacts/@uniswap/v3-periphery/contracts/lens/Quoter.sol/Quoter.json') | |
const UniswapV3FactoryAbi = require('../perp-lushan/artifacts/@uniswap/v3-core/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'); | |
use(solidity); | |
function encodePriceSqrt(reserve1, reserve0) { | |
return BigNumber.from( | |
new bn(reserve1.toString()) | |
.div(reserve0.toString()) | |
.sqrt() | |
.multipliedBy(new bn(2).pow(96)) | |
.integerValue(3) | |
.toString(), | |
) | |
} | |
function getMinTick(tickSpacing) { | |
Math.ceil(-887272 / tickSpacing) * tickSpacing | |
} | |
function getMaxTick(tickSpacing) { | |
Math.floor(887272 / tickSpacing) * tickSpacing | |
} | |
describe("perpLemma2", async function () { | |
let defaultSigner, usdLemma, reBalancer, hasWETH, keeperGasReward, signer1, signer2, usdl2; | |
let perpAddresses; | |
const ZERO = BigNumber.from("0"); | |
let snapshotId; | |
let clearingHouse | |
let marketRegistry | |
let clearingHouseConfig | |
let exchange | |
let orderBook | |
let accountBalance | |
let vault | |
let collateral | |
let baseToken | |
let baseToken2 | |
let quoteToken | |
let univ3factory | |
let pool | |
let pool2 | |
let mockedBaseAggregator | |
let mockedBaseAggregator2 | |
let quoter | |
let perpLemma | |
let collateralDecimals | |
// const lowerTick = 0 | |
// const upperTick = 100000 | |
before(async function () { | |
[defaultSigner, usdLemma, reBalancer, hasWETH, signer1, signer2, usdl2] = await ethers.getSigners(); | |
perpAddresses = await loadPerpLushanInfo(); | |
clearingHouse = new ethers.Contract(perpAddresses.clearingHouse.address, ClearingHouseAbi.abi, defaultSigner) | |
orderBook = new ethers.Contract(perpAddresses.orderBook.address, OrderBookAbi.abi, defaultSigner); | |
clearingHouseConfig = new ethers.Contract(perpAddresses.clearingHouseConfig.address, ClearingHouseConfigAbi.abi, defaultSigner); | |
vault = new ethers.Contract(perpAddresses.vault.address, VaultAbi.abi, defaultSigner); | |
exchange = new ethers.Contract(perpAddresses.exchange.address, ExchangeAbi.abi, defaultSigner); | |
marketRegistry = new ethers.Contract(perpAddresses.marketRegistry.address, MarketRegistryAbi.abi, defaultSigner); | |
collateral = new ethers.Contract(perpAddresses.collateral.address, TestERC20Abi.abi, defaultSigner); | |
baseToken = new ethers.Contract(perpAddresses.baseToken.address, BaseTokenAbi.abi, defaultSigner); | |
baseToken2 = new ethers.Contract(perpAddresses.baseToken2.address, BaseToken2Abi.abi, defaultSigner); | |
quoteToken = new ethers.Contract(perpAddresses.quoteToken.address, QuoteTokenAbi.abi, defaultSigner); | |
univ3factory = new ethers.Contract(perpAddresses.univ3factory.address, UniswapV3FactoryAbi.abi, defaultSigner) | |
accountBalance = new ethers.Contract(perpAddresses.accountBalance.address, AccountBalanceAbi.abi, defaultSigner); | |
mockedBaseAggregator = new ethers.Contract(perpAddresses.mockedBaseAggregator.address, MockTestAggregatorV3Abi.abi, defaultSigner); | |
mockedBaseAggregator2 = new ethers.Contract(perpAddresses.mockedBaseAggregator2.address, MockTestAggregatorV3Abi.abi, defaultSigner); | |
pool = new ethers.Contract(perpAddresses.pool.address, UniswapV3PoolAbi.abi, defaultSigner); | |
pool2 = new ethers.Contract(perpAddresses.pool2.address, UniswapV3Pool2Abi.abi, defaultSigner); | |
quoter = new ethers.Contract(perpAddresses.quoter.address, QuoterAbi.abi, defaultSigner) | |
collateralDecimals = await collateral.decimals() | |
const maxPosition = ethers.constants.MaxUint256; | |
const perpLemmaFactory = await ethers.getContractFactory("PerpLemma") | |
perpLemma = await upgrades.deployProxy(perpLemmaFactory, | |
[ | |
collateral.address, | |
quoteToken.address, // vUSD | |
baseToken.address, | |
quoteToken.address, | |
clearingHouse.address, | |
vault.address, | |
accountBalance.address, | |
quoter.address, | |
usdLemma.address, | |
maxPosition | |
], { initializer: 'initialize' }); | |
await perpLemma.connect(signer1).resetApprovals() | |
await mockedBaseAggregator.setLatestRoundData(0, parseUnits("100", collateralDecimals), 0, 0, 0) | |
await pool.initialize(encodePriceSqrt("100", "1")) | |
// the initial number of oracle can be recorded is 1; thus, have to expand it | |
await pool.increaseObservationCardinalityNext((2 ^ 16) - 1) | |
// await pool2.initialize(encodePriceSqrt("100", "1")) // tick = 50200 (1.0001^50200 = 151.373306858723226652) | |
await marketRegistry.addPool(baseToken.address, 10000) | |
// await marketRegistry.addPool(baseToken2.address, 10000) | |
await marketRegistry.setFeeRatio(baseToken.address, 10000) | |
// await marketRegistry.setFeeRatio(baseToken2.address, 10000) | |
// prepare collateral for maker | |
const makerCollateralAmount = parseUnits("1000000", collateralDecimals) | |
await collateral.mint(signer1.address, makerCollateralAmount) | |
await collateral.mint(signer2.address, makerCollateralAmount) | |
const parsedAmount = parseUnits("100000", collateralDecimals) | |
await collateral.connect(signer1).approve(vault.address, ethers.constants.MaxUint256) | |
await collateral.connect(signer2).approve(vault.address, ethers.constants.MaxUint256) | |
// Deposit into vault | |
// await vault.connect(signer1).deposit(collateral.address, parsedAmount) | |
await vault.connect(signer2).deposit(collateral.address, parsedAmount) | |
await clearingHouse.connect(signer2).addLiquidity({ | |
baseToken: baseToken.address, | |
base: parseEther('100'), | |
quote: parseEther('10000'), | |
lowerTick: -887200, //50000, | |
upperTick: 887200, //50400, | |
minBase: 0, | |
minQuote: 0, | |
useTakerBalance: false, | |
deadline: ethers.constants.MaxUint256, | |
}) | |
}) | |
beforeEach(async function () { | |
snapshotId = await snapshot(); | |
}); | |
afterEach(async function () { | |
await revertToSnapshot(snapshotId); | |
}); | |
describe("OpenPosition with getCollateralAmountGivenUnderlyingAssetAmount", () => { | |
let collateralmintAmount, collateralAmount, parsedAmount, leveragedAmount, collateralToGetBack_1e18, collateralToGetBack_1e6 | |
beforeEach(async function () { | |
collateralmintAmount = parseUnits("1000", collateralDecimals) // 6 decimal | |
collateralToGetBack_1e18 = await perpLemma.callStatic.getCollateralAmountGivenUnderlyingAssetAmount(parseEther('1'), true) | |
collateralToGetBack_1e6 = collateralToGetBack_1e18.mul(parseUnits('1', collateralDecimals)).div(parseEther('1')) | |
console.log('collateralToGetBack_1e18 & collateralToGetBack_1e6: ', collateralToGetBack_1e18.toString(), collateralToGetBack_1e6.toString()) | |
await collateral.mint(usdLemma.address, collateralmintAmount) | |
const balIntial = await collateral.balanceOf(usdLemma.address) | |
console.log('\nbalIntial: ', balIntial.toString()) | |
}); | |
it("openPosition => emit event PositionChanged", async () => { | |
const bal0 = await collateral.balanceOf(usdLemma.address) | |
console.log('\nbal0: ', bal0.toString()) | |
await collateral.connect(usdLemma).transfer( | |
perpLemma.address, | |
collateralToGetBack_1e6 | |
) | |
const accountValue_Before = await clearingHouse.getAccountValue(perpLemma.address) | |
console.log('accountValue-Before: ', accountValue_Before.toString()) | |
await perpLemma.connect(usdLemma).open( | |
parseEther('1'), | |
collateralToGetBack_1e6 | |
) | |
const accountValue = await clearingHouse.getAccountValue(perpLemma.address) | |
const marginRequirementForLiquidation = await accountBalance.getMarginRequirementForLiquidation(perpLemma.address) | |
console.log('accountValue: ', accountValue.toString()) | |
console.log('marginRequirementForLiquidation: ', marginRequirementForLiquidation.toString()) | |
// const mmRatio = await clearingHouseConfig.getMmRatio() | |
// const imRatio = await clearingHouseConfig.getImRatio() | |
// const ratioforMm0 = await vault.getFreeCollateralByRatio(perpLemma.address, 0) | |
// const ratioforMm = await vault.getFreeCollateralByRatio(perpLemma.address, mmRatio) | |
// const ratioforIm = await vault.getFreeCollateralByRatio(perpLemma.address, imRatio) | |
// console.log('\nmmRatio & ratioforMm', mmRatio.toString(), ratioforMm.toString()) | |
// console.log('imRatio & ratioforIm', imRatio.toString(), ratioforIm.toString()) | |
// console.log('ratioforMm0', ratioforMm0.toString()) | |
let getBalance = await vault.getBalance(perpLemma.address) | |
console.log('\ngetBalance: ', getBalance.toString()) | |
const getPnlAndPendingFee = await accountBalance.getPnlAndPendingFee(perpLemma.address) | |
console.log('getPnlAndPendingFee: ', getPnlAndPendingFee.toString()) | |
const getBase = await accountBalance.getBase(perpLemma.address, baseToken.address) | |
console.log('getBase: ', getBase.toString()) | |
const getQuote = await accountBalance.getQuote(perpLemma.address, baseToken.address) | |
console.log('getQuote: ', getQuote.toString()) | |
const bal1 = await collateral.balanceOf(usdLemma.address) | |
console.log('\nbal1: ', bal1.toString()) | |
let positionValue = await accountBalance.getTotalPositionValue(perpLemma.address, baseToken.address) | |
console.log('\npositionValue: ', positionValue.toString()) | |
let positionSize = await accountBalance.getTotalPositionSize(perpLemma.address, baseToken.address) | |
console.log('\positionSize: ', positionSize.toString()) | |
let collateralToGetBackToClose_1e18 = await perpLemma.callStatic | |
.getCollateralAmountGivenUnderlyingAssetAmount(getQuote,false) | |
console.log('\ncollateralToGetBackToClose_1e18: ', collateralToGetBackToClose_1e18.toString()) // get eth by long | |
const collateralToGetBackToClose_1e6 = collateralToGetBackToClose_1e18.mul(parseUnits('1', collateralDecimals)).div(parseUnits('1', 16)) | |
console.log('collateralToGetBackToClose_1e6: ', collateralToGetBackToClose_1e6.toString()) | |
let collateralToGetBackToClose_1e18_1 = await perpLemma.callStatic | |
.getCollateralAmountGivenUnderlyingAssetAmount(getBase.mul(-1), true) | |
console.log('\collateralToGetBackToClose_1e18_1: ', collateralToGetBackToClose_1e18_1.toString()) // get eth by long | |
let collateralToGetBackToClose_1e6_1 = collateralToGetBackToClose_1e18_1.mul(parseUnits('1', collateralDecimals)).div(parseEther('1')) | |
console.log('collateralToGetBackToClose_1e6_1: ', collateralToGetBackToClose_1e6_1.toString()) | |
let collateralToGetBackToClose_1e18_getBlance = await perpLemma.callStatic | |
.getCollateralAmountGivenUnderlyingAssetAmount( | |
getBalance.mul(parseEther('1')).div(parseUnits('1', collateralDecimals)), | |
false | |
) | |
console.log('\collateralToGetBackToClose_1e18_getBlance: ', collateralToGetBackToClose_1e18_getBlance.toString()) // get eth by long | |
const collateralToGetBackToClose_1e6_getBlance = collateralToGetBackToClose_1e18_getBlance.mul(parseUnits('1', collateralDecimals)).div(parseEther('1')) | |
console.log('collateralToGetBackToClose_1e6_getBlance: ', collateralToGetBackToClose_1e6_getBlance.toString()) | |
const [baseBalanceAfter, quoteBalanceAfter] = await clearingHouse.getTokenBalance( | |
perpLemma.address, | |
baseToken.address, | |
) | |
console.log('baseBalanceAfter: ', baseBalanceAfter.toString()) | |
console.log('quoteBalanceAfter: ', quoteBalanceAfter.toString()) | |
const acc = accountValue.mul(parseUnits('1', 6)).div(parseEther('1')) | |
console.log('acc: ', acc.toString()) | |
const getPnlAndPendingFee1 = await accountBalance.getPnlAndPendingFee(perpLemma.address) | |
console.log('getPnlAndPendingFee1: ', getPnlAndPendingFee1.toString()) | |
await expect(perpLemma.connect(usdLemma).close( | |
getQuote, | |
collateralToGetBackToClose_1e6 | |
)).to.emit(clearingHouse, 'PositionChanged') | |
const getPnlAndPendingFee2 = await accountBalance.getPnlAndPendingFee(perpLemma.address) | |
console.log('getPnlAndPendingFee2: ', getPnlAndPendingFee2.toString()) | |
positionSize = await accountBalance.getTotalPositionSize(perpLemma.address, baseToken.address) | |
console.log('\positionSize: ', positionSize.toString()) | |
let ratioforMm1 = await vault.getFreeCollateralByRatio(perpLemma.address, 0) | |
console.log('ratioforMm1', ratioforMm1.toString()) | |
let bal2 = await collateral.balanceOf(usdLemma.address) | |
console.log('\nbal2: ', bal2.toString()) | |
let positionValue2 = await accountBalance.getTotalPositionValue(perpLemma.address, baseToken.address) | |
console.log('last positionValue: ', positionValue2.toString()) | |
getBalance = await vault.getBalance(perpLemma.address) | |
console.log('\ngetBalance: ', getBalance.toString()) | |
collateralToGetBackToClose_1e18_1 = await perpLemma.callStatic | |
.getCollateralAmountGivenUnderlyingAssetAmount(getBase.mul(-1), true) | |
console.log('\collateralToGetBackToClose_1e18_1: ', collateralToGetBackToClose_1e18_1.toString()) // get eth by long | |
collateralToGetBackToClose_1e6_1 = collateralToGetBackToClose_1e18_1.mul(parseUnits('1', collateralDecimals)).div(parseEther('1')) | |
console.log('collateralToGetBackToClose_1e6_1: ', collateralToGetBackToClose_1e6_1.toString()) | |
// await expect(perpLemma.connect(usdLemma).close( | |
// parseEther('0.1'), | |
// BigNumber.from('0') | |
// )).to.emit(clearingHouse, 'PositionChanged') | |
// positionSize = await accountBalance.getTotalPositionSize(perpLemma.address, baseToken.address) | |
// console.log('\positionSize: ', positionSize.toString()) | |
// ratioforMm1 = await vault.getFreeCollateralByRatio(perpLemma.address, 0) | |
// console.log('ratioforMm1', ratioforMm1.toString()) | |
// bal2 = await collateral.balanceOf(usdLemma.address) | |
// console.log('\nbal2: ', bal2.toString()) | |
// positionValue2 = await accountBalance.getTotalPositionValue(perpLemma.address, baseToken.address) | |
// console.log('last positionValue: ', positionValue2.toString()) | |
// getBalance = await vault.getBalance(perpLemma.address) | |
// console.log('\ngetBalance: ', getBalance.toString()) | |
}); | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-or-later | |
pragma solidity =0.8.3; | |
// pragma abicoder v2; | |
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; | |
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; | |
import { ERC2771ContextUpgradeable } from "@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol"; | |
import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | |
import { IPerpetualDEXWrapper } from "../interfaces/IPerpetualDEXWrapper.sol"; | |
import { Utils } from "../libraries/Utils.sol"; | |
import { SafeMathExt } from "../libraries/SafeMathExt.sol"; | |
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | |
import "../libraries/TransferHelper.sol"; | |
import "../interfaces/Perpetual/IClearingHouse.sol"; | |
import "../interfaces/Perpetual/IAccountBalance.sol"; | |
import "../interfaces/UniswapV3/IQuoter.sol"; | |
import "hardhat/console.sol"; | |
interface IPerpVault { | |
function deposit(address token, uint256 amount) external; | |
function withdraw(address token, uint256 amountX10_D) external; | |
function _getBalance(address trader, address token) external view returns (int256); | |
function decimals() external view returns (uint8); | |
} | |
interface IUSDLemma { | |
function lemmaTreasury() external view returns (address); | |
} | |
contract PerpLemma is OwnableUpgradeable, ERC2771ContextUpgradeable, IPerpetualDEXWrapper { | |
using SafeCastUpgradeable for uint256; | |
using SafeCastUpgradeable for int256; | |
using Utils for int256; | |
using SafeMathExt for int256; | |
bytes32 public HashZero; | |
uint256 public constant MAX_UINT256 = type(uint256).max; | |
int256 public constant MAX_INT256 = type(int256).max; | |
address public usdLemma; | |
address public reBalancer; | |
address public baseTokenAddress; | |
address public quoteTokenAddress; | |
bytes32 public referrerCode; | |
IERC20Upgradeable public usd; // ETH | |
IERC20Upgradeable public collateral; // ETH | |
uint256 public collateralDecimals; | |
IClearingHouse public iClearingHouse; | |
IPerpVault public iPerpVault; | |
IAccountBalance public iAccountBalance; | |
IQuoter public iUniV3Router; | |
uint256 public maxPosition; | |
//events | |
event USDLemmaUpdated(address usdlAddress); | |
event ReferrerUpdated(bytes32 referrerCode); | |
event RebalancerUpdated(address rebalancerAddress); | |
event MaxPositionUpdated(uint256 maxPos); | |
function initialize( | |
address _collateral, | |
address _usd, | |
address _baseToken, | |
address _quoteToken, | |
address _iClearingHouse, | |
address _iPerpVault, | |
address _iAccountBalance, | |
address _iUniV3Router, | |
address _usdLemma, | |
uint256 _maxPosition | |
) public initializer { | |
__Ownable_init(); | |
usdLemma = _usdLemma; | |
maxPosition = _maxPosition; | |
baseTokenAddress = _baseToken; | |
quoteTokenAddress = _quoteToken; | |
collateral = IERC20Upgradeable(_collateral); | |
usd = IERC20Upgradeable(_usd); | |
iClearingHouse = IClearingHouse(_iClearingHouse); | |
iPerpVault = IPerpVault(_iPerpVault); | |
iUniV3Router = IQuoter(_iUniV3Router); | |
iAccountBalance = IAccountBalance(_iAccountBalance); | |
collateralDecimals = iPerpVault.decimals(); // need to verify | |
collateral.approve(_iClearingHouse, MAX_UINT256); | |
} | |
///@notice sets USDLemma address - only owner can set | |
///@param _usdlemma USDLemma address to set | |
function setUSDLemma(address _usdlemma) public onlyOwner { | |
usdLemma = _usdlemma; | |
emit USDLemmaUpdated(usdLemma); | |
} | |
///@notice sets refferer address - only owner can set | |
///@param _referrerCode referrerCode of address to set | |
function setReferrerCode(bytes32 _referrerCode) external onlyOwner { | |
referrerCode = _referrerCode; | |
emit ReferrerUpdated(referrerCode); | |
} | |
///@notice sets reBalncer address - only owner can set | |
///@param _reBalancer reBalancer address to set | |
function setReBalancer(address _reBalancer) public onlyOwner { | |
reBalancer = _reBalancer; | |
emit RebalancerUpdated(reBalancer); | |
} | |
///@param _maxPosition reBalancer address to set | |
function setMaxPosition(uint256 _maxPosition) public onlyOwner { | |
maxPosition = _maxPosition; | |
emit MaxPositionUpdated(maxPosition); | |
} | |
/// @notice reset approvals | |
function resetApprovals() external { | |
SafeERC20Upgradeable.safeApprove(collateral, address(iPerpVault), 0); | |
SafeERC20Upgradeable.safeApprove(collateral, address(iPerpVault), MAX_UINT256); | |
} | |
//this needs to be done before the first withdrawal happens | |
//Keeper gas reward needs to be handled seperately which owner can get back when perpetual has settled | |
/// @notice Deposit Keeper gas reward for the perpetual - only owner can call | |
function depositKeeperGasReward() external onlyOwner { | |
} | |
//go short to open | |
/// @notice Open short position on dex and deposit collateral | |
/// @param amount worth in USD short position which is to be opened | |
/// @param collateralAmountRequired collateral amount required to open the position | |
function open(uint256 amount, uint256 collateralAmountRequired) external override { | |
require(_msgSender() == usdLemma, "only usdLemma is allowed"); | |
// require( | |
// collateral.balanceOf(address(this)) >= getAmountInCollateralDecimals(collateralAmountRequired, true), | |
// "not enough collateral" | |
// ); | |
iPerpVault.deposit(address(collateral), collateralAmountRequired); | |
int256 positionSize = iAccountBalance.getTotalPositionValue(address(this), baseTokenAddress); | |
require(positionSize.abs().toUint256() + amount <= maxPosition, "max position reached"); | |
// create short position by giving isBaseToQuote=true | |
// and amount in eth(baseToken) by giving isExactInput=true | |
IClearingHouse.OpenPositionParams memory params = IClearingHouse.OpenPositionParams({ | |
baseToken: baseTokenAddress, | |
isBaseToQuote: true, | |
isExactInput: true, | |
amount: amount, | |
oppositeAmountBound: 0, | |
deadline: MAX_UINT256, | |
sqrtPriceLimitX96: 0, | |
referralCode: bytes32(0) | |
}); | |
(uint256 base, uint256 quote) = iClearingHouse.openPosition(params); | |
console.log('base: ', base); | |
console.log('quote: ', quote); | |
// needs to updateEntryFunding() call (need to implement) | |
} | |
function close(uint256 amount, uint256 collateralAmountToGetBack) external override { | |
require(_msgSender() == usdLemma, "only usdLemma is allowed"); | |
// int256 positionSize = IAccountBalance(address(iAccountBalance)).getPositionSize(address(this), baseTokenAddress); | |
// require (positionSize != 0); | |
int256 accountValue = iClearingHouse.getAccountValue(address(this)); // get Total account value | |
console.log('accountValue-close-first: ', uint256(accountValue)); | |
// create long position by giving isBaseToQuote=false | |
// and amount in eth(baseToken) by giving isExactInput=false | |
IClearingHouse.OpenPositionParams memory params = IClearingHouse.OpenPositionParams({ | |
baseToken: baseTokenAddress, | |
isBaseToQuote: false, | |
isExactInput: true, | |
amount: amount, | |
oppositeAmountBound: 0, | |
deadline: MAX_UINT256, | |
sqrtPriceLimitX96: 0, | |
referralCode: bytes32(0) | |
}); | |
(uint256 base, uint256 quote) = iClearingHouse.openPosition(params); | |
console.log('base-close: ', base); | |
console.log('quote-close: ', quote); | |
accountValue = iClearingHouse.getAccountValue(address(this)); // get Total account value | |
console.log('accountValue-close-before: ', uint256(accountValue)); | |
int256 getTotalPositionValue = iAccountBalance.getTotalPositionValue(address(this), baseTokenAddress); // get current position value | |
console.log('getTotalPositionValue-close: ', uint256(getTotalPositionValue.abs())); | |
// get closed account value and convert from 1e18 to 1e6(bcuz collateral in 1e6) | |
int256 closedAccontValue = ((accountValue - getTotalPositionValue.abs()) * 1e6) / 1e18; | |
console.log('closedAccontValue-close-after: ', uint256(closedAccontValue)); | |
(int256 a,int256 b,uint256 c) = iAccountBalance.getPnlAndPendingFee(address(this)); // get current position value | |
console.log('a-close: ', uint256(a)); | |
console.log('b-close: ', uint256(b)); | |
console.log('c-close: ', uint256(c)); | |
console.log('collateralAmountToGetBack-close-before: ', collateralAmountToGetBack); | |
if (a > 0) { | |
console.log('Positive'); | |
a = (a*1e6)/1e18; | |
collateralAmountToGetBack = collateralAmountToGetBack + uint256(a); | |
} else { | |
console.log('Negative'); | |
a = a.abs(); | |
a = (a*1e6)/1e18; | |
console.log('a-abs', uint256(a)); | |
collateralAmountToGetBack = collateralAmountToGetBack - uint256(a); | |
} | |
console.log('collateralAmountToGetBack-close-after: ', collateralAmountToGetBack); | |
iPerpVault.withdraw( | |
address(collateral), | |
collateralAmountToGetBack | |
// uint256(closedAccontValue) | |
); // withdraw closed position fund | |
// // needs to updateEntryFunding() call | |
SafeERC20Upgradeable.safeTransfer( | |
collateral, | |
usdLemma, | |
collateralAmountToGetBack | |
// uint256(closedAccontValue) | |
); | |
// // *** when getCollateralAmountGivenUnderlyingAssetAmount ready we will use collateralAmountToGetBack instead amount i guess | |
// -> iPerpVault.withdraw(address(collateral), collateralAmountToGetBack); | |
// // *** needs to updateEntryFunding() call (need to implement) | |
// -> SafeERC20Upgradeable.safeTransfer( | |
// collateral, | |
// usdLemma, | |
// getAmountInCollateralDecimals(collateralAmountToGetBack, false) | |
// ); | |
} | |
function getCollateralAmountGivenUnderlyingAssetAmount(uint256 amount, bool isShorting) | |
external | |
override | |
returns (uint256 collateralAmountRequired) | |
{ | |
// TODO: K-Aizen Implement | |
address tokenIn; | |
address tokenOut; | |
uint24 fee = 10000; | |
uint160 sqrtPriceLimitX96 = 0; | |
if (isShorting) { | |
tokenIn = address(baseTokenAddress); | |
tokenOut = address(quoteTokenAddress); | |
// Need to deposit `collateralAmountRequired` of collateral to mint `amount` USD | |
collateralAmountRequired = iUniV3Router.quoteExactInputSingle( | |
tokenIn, // token in | |
tokenOut, // token out | |
fee, | |
amount, | |
sqrtPriceLimitX96 | |
); | |
} else { | |
tokenIn = address(quoteTokenAddress); | |
tokenOut = address(baseTokenAddress); | |
// Burning `amount` USD we get `collateralAmountRequired` collateral | |
collateralAmountRequired = iUniV3Router.quoteExactInputSingle( | |
tokenIn, | |
tokenOut, | |
fee, | |
amount, | |
sqrtPriceLimitX96 | |
); | |
} | |
} | |
/// @notice Rebalance position of dex based on accumulated funding, since last rebalancing | |
/// @param _reBalancer Address of rebalancer who called function on USDL contract | |
/// @param amount Amount of accumulated funding fees used to rebalance by opening or closing a short position | |
/// @param data Abi encoded data to call respective perpetual function, contains limitPrice and deadline | |
/// @return True if successful, False if unsuccessful | |
function reBalance( | |
address _reBalancer, | |
int256 amount, | |
bytes calldata data | |
) external override returns (bool) { | |
require(_msgSender() == usdLemma, "only usdLemma is allowed"); | |
require(_reBalancer == reBalancer, "only rebalancer is allowed"); | |
(uint160 _sqrtPriceLimitX96, uint256 _deadline) = abi.decode(data, (uint160, uint256)); | |
bool _isBaseToQuote; | |
bool _isExactInput; | |
if (amount > 0) { | |
// open long position and amount in usd | |
_isBaseToQuote = false; | |
_isExactInput = true; | |
} else { | |
// open short position and amount in usd | |
_isBaseToQuote = true; | |
_isExactInput = false; | |
} | |
IClearingHouse.OpenPositionParams memory params = IClearingHouse.OpenPositionParams({ | |
baseToken: baseTokenAddress, | |
isBaseToQuote: _isBaseToQuote, | |
isExactInput: _isExactInput, | |
amount: uint256(amount.abs()), | |
oppositeAmountBound: 0, | |
deadline: _deadline, | |
sqrtPriceLimitX96: _sqrtPriceLimitX96, | |
referralCode: referrerCode | |
}); | |
iClearingHouse.openPosition(params); | |
return true; | |
} | |
/// @notice Get Amount in collateral decimals, provided amount is in 18 decimals | |
/// @param amount Amount in 18 decimals | |
/// @param roundUp If needs to round up | |
/// @return decimal adjusted value | |
function getAmountInCollateralDecimals(uint256 amount, bool roundUp) public view override returns (uint256) { | |
if (roundUp && (amount % (uint256(10**(18 - collateralDecimals))) != 0)) { | |
return amount / uint256(10**(18 - collateralDecimals)) + 1; // need to verify | |
} | |
return amount / uint256(10**(18 - collateralDecimals)); | |
} | |
function _msgSender() | |
internal | |
view | |
virtual | |
override(ContextUpgradeable, ERC2771ContextUpgradeable) | |
returns (address sender) | |
{ | |
//ERC2771ContextUpgradeable._msgSender(); | |
return super._msgSender(); | |
} | |
function _msgData() | |
internal | |
view | |
virtual | |
override(ContextUpgradeable, ERC2771ContextUpgradeable) | |
returns (bytes calldata) | |
{ | |
//ERC2771ContextUpgradeable._msgData(); | |
return super._msgData(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment