Skip to content

Instantly share code, notes, and snippets.

@naps62
Last active January 15, 2025 11:30
Show Gist options
  • Save naps62/8872612ddb3a043c3341e1ae6a7287fd to your computer and use it in GitHub Desktop.
Save naps62/8872612ddb3a043c3341e1ae6a7287fd to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import "openzeppelin-contracts/contracts/utils/math/Math.sol";
import "src/Interfaces/IBorrowerOperations.sol";
import {DECIMAL_PRECISION} from "src/Dependencies/Constants.sol";
import "src/Zappers/Modules/Exchanges/UniswapV3/IUniswapV3Pool.sol";
import "src/Zappers/Modules/Exchanges/UniswapV3/INonfungiblePositionManager.sol";
import {ICrocSwapDex, CrocSwapDexHelper} from "src/Exchanges/CrocSwap/ICrocSwapDex.sol";
INonfungiblePositionManager constant scrollUniV3PositionManager =
INonfungiblePositionManager(0xB39002E4033b162fAc607fc3471E205FA2aE5967);
ICrocSwapDex crocSwapDex = ICrocSwapDex(0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106);
// Initial liquidity values (these values need to be changed)
uint256 constant initialLiquidityTroveUsdqAmount = 500e18;
uint256 constant initialLiquidityTroveWethAmount = 500e18;
uint256 constant initialLiquidityTroveInterestRate = _1pct / 2;
uint256 constant initialLiquiditySpAmount = 100e18;
uint256 constant initialLiquidityUniV3Amount = 200e18;
uint256 constant initialLiquidityCrocSwapAmount = 200e18;
uint256 constant initialLiquidityExchangePrice = DECIMAL_PRECISION; // 10e18; // 1:1 ratio, same amount for both tokens (?)
uint24 constant UNIV3_FEE = 0.3e4;
function openInitialLiquidityTrove(
address deployer,
IBorrowerOperations borrowerOperations,
uint256 boldAmount,
uint256 collAmount,
uint256 annualInterestRate,
uint256 maxUpfrontFee
) {
borrowerOperations.openTrove(
deployer, // _owner
0, // _ownerIndex
collAmount, // _collAmount
boldAmount, // _boldAmount
0, // _upperHint
0, // _lowerHint
annualInterestRate, // _annualInterestRate
// type(uint256).max, // _maxUpfrontFee
maxUpfrontFee, // _maxUpfrontFee
address(0), // _addManager
address(0), // _removeManager
address(0) // _receiver
);
}
function depositInStabilityPool(IStabilityPool stabilityPool, uint256 boldAmount) {
stabilityPool.provideToSP(
boldAmount, // _topUp
false // _doClaim
);
}
struct ProvideUniV3LiquidityVars {
uint256 token2Amount;
uint256 price;
int24 TICK_SPACING;
int24 tick;
int24 tickLower;
int24 tickUpper;
address[2] tokens;
uint256[2] amounts;
}
// _price should be _token1 / _token2
function provideUniV3Liquidity(
address deployer,
INonfungiblePositionManager uniV3PositionManager,
IERC20 _token1,
IERC20 _token2,
uint256 _token1Amount,
uint256 _price,
uint24 _fee
) returns (address uniV3PoolAddress, uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) {
ProvideUniV3LiquidityVars memory vars;
// tokens and amounts
vars.token2Amount = _token1Amount * DECIMAL_PRECISION / _price;
if (address(_token1) < address(_token2)) {
vars.tokens[0] = address(_token1);
vars.tokens[1] = address(_token2);
vars.amounts[0] = _token1Amount;
vars.amounts[1] = vars.token2Amount;
// inverse price if token1 goes first
vars.price = DECIMAL_PRECISION * DECIMAL_PRECISION / _price;
} else {
vars.tokens[0] = address(_token2);
vars.tokens[1] = address(_token1);
vars.amounts[0] = vars.token2Amount;
vars.amounts[1] = _token1Amount;
vars.price = _price;
}
uniV3PoolAddress = uniV3PositionManager.createAndInitializePoolIfNecessary(
vars.tokens[0],
vars.tokens[1],
_fee,
priceToSqrtPriceX96(vars.price) // sqrtPriceX96
);
_token1.approve(address(uniV3PositionManager), _token1Amount);
_token2.approve(address(uniV3PositionManager), vars.token2Amount);
vars.TICK_SPACING = IUniswapV3Pool(uniV3PoolAddress).tickSpacing();
(, vars.tick,,,,,) = IUniswapV3Pool(uniV3PoolAddress).slot0();
vars.tickLower = (vars.tick - 6000) / vars.TICK_SPACING * vars.TICK_SPACING;
vars.tickUpper = (vars.tick + 6000) / vars.TICK_SPACING * vars.TICK_SPACING;
INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({
token0: vars.tokens[0],
token1: vars.tokens[1],
fee: _fee,
tickLower: vars.tickLower,
tickUpper: vars.tickUpper,
amount0Desired: vars.amounts[0],
amount1Desired: vars.amounts[1],
amount0Min: 0,
amount1Min: 0,
recipient: deployer,
deadline: block.timestamp + 600 minutes
});
(tokenId, liquidity, amount0, amount1) = uniV3PositionManager.mint(params);
}
function priceToSqrtPriceX96(uint256 _price) pure returns (uint160 sqrtPriceX96) {
// overflow vs precision
if (_price > (1 << 64)) {
// ~18.4e18
sqrtPriceX96 = uint160(Math.sqrt(_price / DECIMAL_PRECISION) << 96);
} else {
sqrtPriceX96 = uint160(Math.sqrt((_price << 192) / DECIMAL_PRECISION));
}
}
struct ProvideCrocSwapLiquidityVars {
uint256 token2Amount;
uint256 price;
address[2] tokens;
uint256[2] amounts;
uint128 liq;
uint128 limitLower;
uint128 limitHigher;
uint256 priceConvertedToQ64;
address base;
address quote;
}
function _initAndFundCrocSwapPools(
ICrocSwapDex crocSwapDex,
address _base,
address _quote,
uint256, /* _poolIdx */
uint128 _price, // Q64.64
uint128 liq,
uint128 limitLower,
uint128 limitHigher
) {
crocSwapDex.userCmd(
CrocSwapDexHelper.COLD_PROXY,
abi.encode(CrocSwapDexHelper.FIXED_INITPOOL_SUBCODE, _base, _quote, CrocSwapDexHelper.POOL_TYPE_INDEX, _price)
);
crocSwapDex.userCmd(
CrocSwapDexHelper.WARM_PATH,
abi.encode(
CrocSwapDexHelper.MINT_AMBIENT_LIQ_LP, // uint8 code
_base, // address base
_quote, // address quote
CrocSwapDexHelper.POOL_TYPE_INDEX, // uint256 poolIdx
0, // int24 bidTick -- ignored for ambient mint
0, // int24 askTick -- ignored for ambient mint
liq, // uint128 liq -- The total amount of liquidity being minted. Represented as sqrt(X*Y)
limitLower, // uint128 limitLower
limitHigher, // uint128 limitHigher
3, // uint8 reserveFlags -- ignored for ambient mint
address(0x0) // lpConduit -- all examples use 0x0
)
);
}
function provideCrocSwapLiquidity(
ICrocSwapDex crocSwapDex,
IERC20 _token1, // collateral
IERC20 _token2, // quill
uint256 _token1Amount,
uint256 _price
) {
uint256 priceConvertedToQ64 = CrocSwapDexHelper.convertDecimalToQ64(_price, 18, address(_token1) < address(_token2));
uint256 collAmount = _token1Amount;
uint256 boldAmount = collAmount * _price / DECIMAL_PRECISION;
_token1.approve(address(crocSwapDex), collAmount);
_token2.approve(address(crocSwapDex), boldAmount);
// If the values aren't decreased, the transfer fails. Still haven't figured out how to calculate this value the correct way.
uint128 liq = CrocSwapDexHelper.calcLiq(collAmount - DECIMAL_PRECISION, boldAmount - DECIMAL_PRECISION);
(uint128 limitLower, uint128 limitHigher) = CrocSwapDexHelper.calcLimits(priceConvertedToQ64, 50); // 50 = 0.5%
(address base, address quote) = CrocSwapDexHelper.getQuoteBaseOrder(address(_token1), address(_token2));
_initAndFundCrocSwapPools(
crocSwapDex,
base,
quote,
CrocSwapDexHelper.POOL_TYPE_INDEX,
uint128(priceConvertedToQ64),
liq,
limitLower,
limitHigher
);
}
@pvgo80
Copy link

pvgo80 commented Jan 15, 2025

In the matter regarding the usage of CrocImpact to have a similar behaviour of uniV3Quoter.quoteExactOutputSingle, we tried the following (fully expecting to have some errors along the way) with unsuccessful results (921$ vs 1000$ expected):

function testCrocSwapImpact() public {
        CrocSwapDexHelper.SwapParams memory params;
        // Give B 1M scroll
        deal(address(SCR_Token), B, 1_000_000 * DECIMAL_PRECISION);
        uint256 startingBalance = IERC20(USDC_Token).balanceOf(B);
        uint128 goalBalance = 1000 * (10**6); //1000$ USDC -- the amount we want after the trade, 10**6 is usdc decimal precision

        (params.base, params.quote) = CrocSwapDexHelper.getQuoteBaseOrder(SCR_Token, USDC_Token);
        params.poolIdx = CrocSwapDexHelper.POOL_TYPE_INDEX;
        params.qty = goalBalance; 
        bool isUSDCBase = params.base == USDC_Token;
        params.inBaseQty = isUSDCBase;
        params.isBuy = isUSDCBase;
        params.limitPrice = isUSDCBase ? 21267430153580247136652501917186561137 : 65538; // pure guess work
        params.tip = 0; // I have no idea how to calculate this value, but from source code doesn't go beyond a percentage
        params.settleFlags = 0;
        params.minOut = 0; // we don't protect in a simulation

        (int128 baseFlow, int128 quoteFlow, uint128 finalPrice) = crocImpact.calcImpact(
            params.base, 
            params.quote, 
            params.poolIdx, 
            params.isBuy, 
            params.inBaseQty, 
            params.qty, 
            params.tip,
            params.limitPrice
        );
        uint128 positiveQuoteFlow = uint128(quoteFlow < 0 ? -quoteFlow : quoteFlow);

        //lets make the trade using the quoteFlow and validate the final usdc received

        params.inBaseQty = !isUSDCBase;
        params.isBuy = !isUSDCBase;
        params.limitPrice = !isUSDCBase ? 21267430153580247136652501917186561137 : 65538; 
        params.qty = uint128(positiveQuoteFlow);

        vm.prank(B);
        IERC20(SCR_Token).approve(address(crocSwapDex), params.qty);

        _makeSwap(B, params);

        uint256 endBalance = IERC20(USDC_Token).balanceOf(B);
        assertEq(endBalance, startingBalance + goalBalance, "Should have swapped requested amount");
    }
  [767460] CrocSwapDexTest::testCrocSwapImpact()
    ├─ [7564] SCROLL_Token::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [staticcall]
    │   ├─ [2640] 0x7600174E2a730a05da046fFA8Fc32DEC27FfdDC8::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [delegatecall]
    │   │   └─ ← [Return] 10000000000000000000000 [1e22]
    │   └─ ← [Return] 10000000000000000000000 [1e22]
    ├─ [0] VM::load(SCROLL_Token: [0xd29687c813D741E2F938F4aC377128810E217b1b], 0x224944ccfb924ee8b8902e9a49912a4619b803e9c6556b1c0d9cf29dab4d6bbf) [staticcall]
    │   └─ ← [Return] 0x00000000000000000000000000000000000000000000021e19e0c9bab2400000
    ├─ [0] VM::store(SCROLL_Token: [0xd29687c813D741E2F938F4aC377128810E217b1b], 0x224944ccfb924ee8b8902e9a49912a4619b803e9c6556b1c0d9cf29dab4d6bbf, 0x00000000000000000000000000000000000000000000d3c21bcecceda1000000)
    │   └─ ← [Return] 
    ├─ [1064] SCROLL_Token::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [staticcall]
    │   ├─ [640] 0x7600174E2a730a05da046fFA8Fc32DEC27FfdDC8::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [delegatecall]
    │   │   └─ ← [Return] 1000000000000000000000000 [1e24]
    │   └─ ← [Return] 1000000000000000000000000 [1e24]
    ├─ [9726] 0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [staticcall]
    │   ├─ [2529] 0x72e2451a2da1535DBf0E7CB1e8C69F56E00A7B7b::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [delegatecall]
    │   │   └─ ← [Return] 0
    │   └─ ← [Return] 0
    ├─ [142877] croc-impact::calcImpact(0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4, SCROLL_Token: [0xd29687c813D741E2F938F4aC377128810E217b1b], 420, true, true, 1000000000 [1e9], 0, 21267430153580247136652501917186561137 [2.126e37]) [staticcall]
    │   ├─ [2359] crocswapdex::readSlot(65045675089258841282994332466172103527827742192530481468407093350446163516931 [6.504e76]) [staticcall]
    │   │   └─ ← [Return] 2450239689446769665 [2.45e18]
    │   ├─ [2359] crocswapdex::readSlot(65006889788353940852689926802351332735102890341882464401732428711872028765721 [6.5e76]) [staticcall]
    │   │   └─ ← [Return] 115915590301724596222916627725442542969048562989658926 [1.159e53]
    │   ├─ [2359] crocswapdex::readSlot(65006889788353940852689926802351332735102890341882464401732428711872028765722 [6.5e76]) [staticcall]
    │   │   └─ ← [Return] 47149863050986826133900635475216288887282883183700653560429249206747136 [4.714e70]
    │   ├─ [2359] crocswapdex::readSlot(66866552608990375605202052641522377046085742402946055868805720340955794758882 [6.686e76]) [staticcall]
    │   │   └─ ← [Return] 356811923176489970264571492366877383723057152 [3.568e44]
    │   ├─ [2359] crocswapdex::readSlot(20153743646870408477948354023889050143355904149972425598230418071012053525713 [2.015e76]) [staticcall]
    │   │   └─ ← [Return] 431359146674410236714672241392314090778194310760649159697657780764672 [4.313e68]
    │   ├─ [2359] crocswapdex::readSlot(32128763096103394929436351677355610673920633202447988756069013005903164036045 [3.212e76]) [staticcall]
    │   │   └─ ← [Return] 46190794918041564291974713252088386299427536731386242342219948396380160 [4.619e70]
    │   ├─ [359] crocswapdex::readSlot(20153743646870408477948354023889050143355904149972425598230418071012053525713 [2.015e76]) [staticcall]
    │   │   └─ ← [Return] 431359146674410236714672241392314090778194310760649159697657780764672 [4.313e68]
    │   ├─ [2359] crocswapdex::readSlot(14250542383800123457919502695374030191199068465377793824974215286162652751352 [1.425e76]) [staticcall]
    │   │   └─ ← [Return] 30004676772158823823198739836488888885663217747909494069179197966448770 [3e70]
    │   ├─ [359] crocswapdex::readSlot(20153743646870408477948354023889050143355904149972425598230418071012053525713 [2.015e76]) [staticcall]
    │   │   └─ ← [Return] 431359146674410236714672241392314090778194310760649159697657780764672 [4.313e68]
    │   ├─ [2359] crocswapdex::readSlot(83537090791684539960054826961347336137087932342510657830515061306442206102083 [8.353e76]) [staticcall]
    │   │   └─ ← [Return] 1393796574908163946345982392040522594123776 [1.393e42]
    │   ├─ [2359] crocswapdex::readSlot(52205914590282670903256600491672532795729353470765552465906913532172711425882 [5.22e76]) [staticcall]
    │   │   └─ ← [Return] 46785001607690179280724252944740303628192785222684035316507359634784256 [4.678e70]
    │   ├─ [359] crocswapdex::readSlot(83537090791684539960054826961347336137087932342510657830515061306442206102083 [8.353e76]) [staticcall]
    │   │   └─ ← [Return] 1393796574908163946345982392040522594123776 [1.393e42]
    │   ├─ [2359] crocswapdex::readSlot(66182594575269931767542812509651178709174552897375869543104101992825555619338 [6.618e76]) [staticcall]
    │   │   └─ ← [Return] 110859300695323430835670766037824721329995938174971843863643114069557248 [1.108e71]
    │   └─ ← [Return] 1000000000 [1e9], -1246761801763041213301 [-1.246e21], 16762899212379 [1.676e13]
    ├─ [0] VM::prank(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67])
    │   └─ ← [Return] 
    ├─ [25137] SCROLL_Token::approve(crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], 1246761801763041213301 [1.246e21])
    │   ├─ [24710] 0x7600174E2a730a05da046fFA8Fc32DEC27FfdDC8::approve(crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], 1246761801763041213301 [1.246e21]) [delegatecall]
    │   │   ├─ emit Approval(owner: userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], spender: crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], value: 1246761801763041213301 [1.246e21])
    │   │   └─ ← [Return] true
    │   └─ ← [Return] true
    ├─ [0] VM::startPrank(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67])
    │   └─ ← [Return] 
    ├─ [429071] crocswapdex::userCmd(1, 0x00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4000000000000000000000000d29687c813d741e2f938f4ac377128810e217b1b00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043964bb5138c0e7b750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
    │   ├─ [398894] croc-hotproxy-2::userCmd(0x00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4000000000000000000000000d29687c813d741e2f938f4ac377128810e217b1b00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043964bb5138c0e7b750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
    │   │   ├─ [35428] 0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4::transfer(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], 921381319 [9.213e8])
    │   │   │   ├─ [34728] 0x72e2451a2da1535DBf0E7CB1e8C69F56E00A7B7b::transfer(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], 921381319 [9.213e8]) [delegatecall]
    │   │   │   │   ├─ emit Transfer(from: crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], to: userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], value: 921381319 [9.213e8])
    │   │   │   │   └─ ← [Return] true
    │   │   │   └─ ← [Return] true
    │   │   ├─ [14170] SCROLL_Token::transferFrom(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], 1246761801763041213301 [1.246e21])
    │   │   │   ├─ [13737] 0x7600174E2a730a05da046fFA8Fc32DEC27FfdDC8::transferFrom(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], 1246761801763041213301 [1.246e21]) [delegatecall]
    │   │   │   │   ├─ emit Transfer(from: userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67], to: crocswapdex: [0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106], value: 1246761801763041213301 [1.246e21])
    │   │   │   │   └─ ← [Return] true
    │   │   │   └─ ← [Return] true
    │   │   ├─  emit topic 0: 0x1f5359759208315a45fc3fa86af1948560d8b87afdcaf1702a110ce0fbc305f3
    │   │   │           data: 0x0000000000000000000000000000000000000000000000000000000000000060ffffffffffffffffffffffffffffffffffffffffffffffffffffffffc914d639000000000000000000000000000000000000000000000043964bb5138c0e7b75000000000000000000000000000000000000000000000000000000000000014000000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4000000000000000000000000d29687c813d741e2f938f4ac377128810e217b1b00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043964bb5138c0e7b750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    │   │   └─ ← [Return] 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffc914d639000000000000000000000000000000000000000000000043964bb5138c0e7b75
    │   └─ ← [Return] 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffc914d639000000000000000000000000000000000000000000000043964bb5138c0e7b75
    ├─ [0] VM::stopPrank()
    │   └─ ← [Return] 
    ├─ [1226] 0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [staticcall]
    │   ├─ [529] 0x72e2451a2da1535DBf0E7CB1e8C69F56E00A7B7b::balanceOf(userB: [0x1b1E98f4912aE9014064a70537025EF338e6aD67]) [delegatecall]
    │   │   └─ ← [Return] 921381319 [9.213e8]
    │   └─ ← [Return] 921381319 [9.213e8]
    ├─ [0] VM::assertEq(921381319 [9.213e8], 1000000000 [1e9], "Should have swapped requested amount") [staticcall]
    │   └─ ← [Revert] Should have swapped requested amount: 921381319 != 1000000000
    └─ ← [Revert] Should have swapped requested amount: 921381319 != 1000000000

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment