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
);
}
@0xStrobe
Copy link

0xStrobe commented Jan 14, 2025

Answering some of the questions from the markdown:

  1. Does the contracts on https://github.com/CrocSwap/CrocSwap-protocol match the deployed contracts at https://scrollscan.com/address/0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106

Yes, some of the components may have been updated, please check the branches like scrollDeploy-swapRouter. We sometimes have very smol changes specific to a chain

  1. Does the documentation https://docs.ambient.finance/developers/dex-contract-interface match the deployed contracts?

Yes but we do need to update it for some new chains as it was primarily written for the Ethereum deployment

  1. We need to integrate an exchange with our current protocol. We aim to have different from to exchange with out stablecoin (neither target is a stablecoin). This will enable a feature that will only be deployed at a later stage

Sorry not entirely sure what you mean for this one, could you rephrase?

  1. What project do you recommend that has a mature and secure integration with ambient, where we can take inspiration from the source code?

Tempest has a good integration with ambient but I'm not sure if the source code is set to public on GitHub, might need to look up the contracts from Etherscan. Example - https://scrollscan.com/address/0x965e23ea362f4b27a57645262f06f0e88b17a97c#readProxyContract or https://vscode.blockscan.com/534352/0x965e23ea362f4b27a57645262f06f0e88b17a97c

  1. What's the best practice / correct process for pool initialization and to provide liquidity to it?

Make sure the init price is set to the current market price, to avoid the first liquidity additions getting screwed over by arb bots

  1. Best way to implement a uniV3Quoter.quoteExactOutputSingle like function?

Exact output is a bit more complex and unfortunately currently there's no direct way to query it from our contracts, you could work backwards from the pool state, or estimate it.

  1. Differences from documentation and current implementation

Thank you for pointing those discrepancies out, some of the callpaths on later chains are indeed different and not directly documented in the docs, but they are essentially immutable on each individual chain. We will update the docs. For now, the SDK has the most up-to-date and complete configuration, and for the exact CrocSwapDex implementations, right now you can use the in-browser VSCode feature on Etherscan (that "IDE" button) to navigate - sorry for the inconvenience.

@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