diff --git a/proto/neutron/dex/tx.proto b/proto/neutron/dex/tx.proto index 4b5bb18be..fa48c2863 100644 --- a/proto/neutron/dex/tx.proto +++ b/proto/neutron/dex/tx.proto @@ -30,6 +30,8 @@ service Msg { message DepositOptions { bool disable_autoswap = 1; bool fail_tx_on_bel = 2; + bool swap_on_deposit = 3; + uint64 swap_on_deposit_slop_tolerance_bps = 4; } message MsgDeposit { diff --git a/x/dex/client/cli/flags.go b/x/dex/client/cli/flags.go index e7b7c1915..5d3affb8b 100644 --- a/x/dex/client/cli/flags.go +++ b/x/dex/client/cli/flags.go @@ -7,6 +7,7 @@ const ( FlagIncludePoolData = "include-pool-data" FlagCalcWithdraw = "calc-withdraw" FlagPrice = "price" + FlagSwapOnDeposit = "swap-on-deposit" ) func FlagSetMaxAmountOut() *flag.FlagSet { @@ -32,3 +33,9 @@ func FlagSetCalcWithdrawableAmount() *flag.FlagSet { fs.Bool(FlagCalcWithdraw, false, "Calculate withdrawable amount") return fs } + +func FlagSetSwapOnDeposit() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Bool(FlagSwapOnDeposit, false, "Before BEL swap for deposits") + return fs +} diff --git a/x/dex/client/cli/tx_deposit.go b/x/dex/client/cli/tx_deposit.go index dc0f3aeae..58f7eeae9 100644 --- a/x/dex/client/cli/tx_deposit.go +++ b/x/dex/client/cli/tx_deposit.go @@ -43,6 +43,11 @@ func CmdDeposit() *cobra.Command { argAutoswapOptions := strings.Split(args[7], ",") argFailTxOptions := strings.Split(args[8], ",") + swapOnDeposit, err := cmd.Flags().GetBool(FlagSwapOnDeposit) + if err != nil { + return err + } + var AmountsA []math.Int var AmountsB []math.Int var TicksIndexesInt []int64 @@ -98,6 +103,7 @@ func CmdDeposit() *cobra.Command { DepositOptions = append(DepositOptions, &types.DepositOptions{ DisableAutoswap: disableAutoswap, FailTxOnBel: failTx, + SwapOnDeposit: swapOnDeposit, }) } @@ -123,6 +129,7 @@ func CmdDeposit() *cobra.Command { } flags.AddTxFlagsToCmd(cmd) + cmd.Flags().AddFlagSet(FlagSetSwapOnDeposit()) return cmd } diff --git a/x/dex/keeper/deposit.go b/x/dex/keeper/deposit.go index 15351f67f..a7c86880e 100644 --- a/x/dex/keeper/deposit.go +++ b/x/dex/keeper/deposit.go @@ -8,7 +8,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/neutron-org/neutron/v5/utils" + math_utils "github.com/neutron-org/neutron/v5/utils/math" "github.com/neutron-org/neutron/v5/x/dex/types" + dexutils "github.com/neutron-org/neutron/v5/x/dex/utils" ) // DepositCore handles core logic for MsgDeposit including bank operations and event emissions @@ -27,8 +29,8 @@ func (k Keeper) DepositCore( amounts0Deposited, amounts1Deposited, - totalAmountReserve0, - totalAmountReserve1, + totalInAmount0, + totalInAmount1, sharesIssued, events, failedDeposits, @@ -39,15 +41,15 @@ func (k Keeper) DepositCore( ctx.EventManager().EmitEvents(events) - if totalAmountReserve0.IsPositive() { - coin0 := sdk.NewCoin(pairID.Token0, totalAmountReserve0) + if totalInAmount0.IsPositive() { + coin0 := sdk.NewCoin(pairID.Token0, totalInAmount0) if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, callerAddr, types.ModuleName, sdk.Coins{coin0}); err != nil { return nil, nil, nil, nil, err } } - if totalAmountReserve1.IsPositive() { - coin1 := sdk.NewCoin(pairID.Token1, totalAmountReserve1) + if totalInAmount1.IsPositive() { + coin1 := sdk.NewCoin(pairID.Token1, totalInAmount1) if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, callerAddr, types.ModuleName, sdk.Coins{coin1}); err != nil { return nil, nil, nil, nil, err } @@ -73,14 +75,14 @@ func (k Keeper) ExecuteDeposit( fees []uint64, options []*types.DepositOptions) ( amounts0Deposited, amounts1Deposited []math.Int, - totalAmountReserve0, totalAmountReserve1 math.Int, + totalInAmount0, totalInAmount1 math.Int, sharesIssued sdk.Coins, events sdk.Events, failedDeposits []*types.FailedDeposit, err error, ) { - totalAmountReserve0 = math.ZeroInt() - totalAmountReserve1 = math.ZeroInt() + totalInAmount0 = math.ZeroInt() + totalInAmount1 = math.ZeroInt() amounts0Deposited = make([]math.Int, len(amounts0)) amounts1Deposited = make([]math.Int, len(amounts1)) sharesIssued = sdk.Coins{} @@ -91,11 +93,12 @@ func (k Keeper) ExecuteDeposit( } isWhitelistedLP := k.IsWhitelistedLP(ctx, callerAddr) - for i, amount0 := range amounts0 { - amount1 := amounts1[i] + for i, depositAmount0 := range amounts0 { + depositAmount1 := amounts1[i] tickIndex := tickIndices[i] fee := fees[i] option := options[i] + inAmount0, inAmount1 := depositAmount0, depositAmount1 if option == nil { option = &types.DepositOptions{} } @@ -107,8 +110,15 @@ func (k Keeper) ExecuteDeposit( return nil, nil, math.ZeroInt(), math.ZeroInt(), nil, nil, nil, err } } + if option.SwapOnDeposit { + inAmount0, inAmount1, depositAmount0, depositAmount1, err = k.SwapOnDeposit(ctx, pairID, tickIndex, fee, depositAmount0, depositAmount1, option.SwapOnDepositSlopToleranceBps) + if err != nil { + return nil, nil, math.ZeroInt(), math.ZeroInt(), nil, nil, nil, err + } + } - if k.IsPoolBehindEnemyLines(ctx, pairID, tickIndex, fee, amount0, amount1) { + // This check is redundant when using SwapOnDepsit. But we leave it as an extra check. + if k.IsPoolBehindEnemyLines(ctx, pairID, tickIndex, fee, depositAmount0, depositAmount1) { err = sdkerrors.Wrapf(types.ErrDepositBehindEnemyLines, "deposit failed at tick %d fee %d", tickIndex, fee) if option.FailTxOnBel { @@ -130,7 +140,12 @@ func (k Keeper) ExecuteDeposit( existingShares := k.bankKeeper.GetSupply(ctx, pool.GetPoolDenom()).Amount - inAmount0, inAmount1, outShares := pool.Deposit(amount0, amount1, existingShares, autoswap) + depositAmount0, depositAmount1, outShares := pool.Deposit(depositAmount0, depositAmount1, existingShares, autoswap) + if option.DisableAutoswap { + // If autoswap is disabled inAmount might change. + // SwapOnDeposit cannot be used without autoswap, so nothing is affected here. + inAmount0, inAmount1 = depositAmount0, depositAmount1 + } // Save updates to both sides of the pool k.UpdatePool(ctx, pool) @@ -145,10 +160,10 @@ func (k Keeper) ExecuteDeposit( sharesIssued = append(sharesIssued, outShares) - amounts0Deposited[i] = inAmount0 - amounts1Deposited[i] = inAmount1 - totalAmountReserve0 = totalAmountReserve0.Add(inAmount0) - totalAmountReserve1 = totalAmountReserve1.Add(inAmount1) + amounts0Deposited[i] = depositAmount0 + amounts1Deposited[i] = depositAmount1 + totalInAmount0 = totalInAmount0.Add(inAmount0) + totalInAmount1 = totalInAmount1.Add(inAmount1) depositEvent := types.CreateDepositEvent( callerAddr, @@ -157,6 +172,8 @@ func (k Keeper) ExecuteDeposit( pairID.Token1, tickIndex, fee, + depositAmount0, + depositAmount1, inAmount0, inAmount1, outShares.Amount, @@ -167,5 +184,101 @@ func (k Keeper) ExecuteDeposit( // At this point shares issued is not sorted and may have duplicates // we must sanitize to convert it to a valid set of coins sharesIssued = utils.SanitizeCoins(sharesIssued) - return amounts0Deposited, amounts1Deposited, totalAmountReserve0, totalAmountReserve1, sharesIssued, events, failedDeposits, nil + return amounts0Deposited, amounts1Deposited, totalInAmount0, totalInAmount1, sharesIssued, events, failedDeposits, nil +} + +func (k Keeper) SwapOnDeposit( + ctx sdk.Context, + pairID *types.PairID, + tickIndex int64, + fee uint64, + amount0, amount1 math.Int, + slopToleranceBPs uint64, +) (inAmount0, inAmount1, depositAmount0, depositAmount1 math.Int, err error) { + feeInt64 := dexutils.MustSafeUint64ToInt64(fee) + inAmount0, inAmount1 = amount0, amount1 + depositAmount0, depositAmount1 = inAmount0, inAmount1 + swappedToken0 := false + if amount0.IsPositive() { + // Use Amount0 to swap any Token1 ticks < (-depositTick0 -1 ) + depositTickToken0 := -tickIndex + feeInt64 + // subtract 1 from limit price because we can have double-sided liquidity at the deposit tick + // we don't want to swap through this liquidity + limitPrice0 := types.MustCalcPrice(-depositTickToken0 - 1) + tradePairID := types.MustNewTradePairID(pairID.Token0, pairID.Token1) + + swapToken0In, swapToken1Out, orderFilled, err := k.Swap(ctx, tradePairID, amount0, nil, &limitPrice0) + if err != nil { + return math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), err + } + + if err := CheckSwapOnDepositSlopTolerance(swapToken0In.Amount, swapToken1Out.Amount, limitPrice0, slopToleranceBPs); err != nil { + return math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), err + } + + if swapToken0In.IsPositive() { + if orderFilled { + // due to monotonic rounding we may not be able to swap all of Token0. + // but we can't deposit the remainder because it's still behind enemy lines so we don't use it + inAmount0 = swapToken0In.Amount + } // else inAmount0 = amount0 we have swapped through all opposing BEL liquidity, + // so we can safely deposit the full amount + + depositAmount0 = inAmount0.Sub(swapToken0In.Amount) + inAmount1 = amount1 + depositAmount1 = amount1.Add(swapToken1Out.Amount) + swappedToken0 = true + } + + } + + if amount1.IsPositive() { + // Use amount1 to swap any Token0 ticks < -depositTick1 + depositTickToken1 := tickIndex + feeInt64 + limitPrice1 := types.MustCalcPrice(-depositTickToken1 - 1) + tradePairID := types.MustNewTradePairID(pairID.Token1, pairID.Token0) + + swapToken1In, swapToken0Out, orderFilled, err := k.Swap(ctx, tradePairID, amount1, nil, &limitPrice1) + if err != nil { + return math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), err + } + + if err := CheckSwapOnDepositSlopTolerance(swapToken1In.Amount, swapToken0Out.Amount, limitPrice1, slopToleranceBPs); err != nil { + return math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), err + } + + if swapToken1In.IsPositive() { + + if swappedToken0 { + // This should be impossible, but leaving this as an extra precaution + return math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), types.ErrDoubleSidedSwapOnDeposit + } + + inAmount0 = amount0 + depositAmount0 = amount0.Add(swapToken0Out.Amount) + + // see note above on monotonic rounding logic + if orderFilled { + inAmount1 = swapToken1In.Amount + } // else inAmount1 = amount1 + depositAmount1 = inAmount1.Sub(swapToken1In.Amount) + } + + } + + return inAmount0, inAmount1, depositAmount0, depositAmount1, nil +} + +func CheckSwapOnDepositSlopTolerance(swapAmountIn, swapAmountOut math.Int, limitPrice math_utils.PrecDec, slopToleranceBPs uint64) error { + if swapAmountIn.IsPositive() { + trueTakerPrice := math_utils.NewPrecDecFromInt(swapAmountIn).QuoInt(swapAmountOut) + // slopToleranceBPs has already been validated so no risk of overflow + slopToleranceInt64 := dexutils.MustSafeUint64ToInt64(slopToleranceBPs) + slopToleranceDec := math_utils.NewPrecDec(slopToleranceInt64).Quo(math_utils.NewPrecDecFromInt(math.NewInt(10000))) + maxAllowedTakerPrice := limitPrice.Mul(math_utils.OnePrecDec().Add(slopToleranceDec)) + if trueTakerPrice.GTE(maxAllowedTakerPrice) { + return types.ErrSwapOnDepositSlopToleranceNotSatisfied + } + } + return nil } diff --git a/x/dex/keeper/integration_deposit_doublesided_test.go b/x/dex/keeper/integration_deposit_doublesided_test.go index 57f9a2e0c..d04015296 100644 --- a/x/dex/keeper/integration_deposit_doublesided_test.go +++ b/x/dex/keeper/integration_deposit_doublesided_test.go @@ -205,3 +205,110 @@ func (s *DexTestSuite) TestDepositValueAccural() { s.carolWithdraws(NewWithdrawalInt(math.NewInt(90484150), 0, 10)) s.assertCarolBalances(100, 1) } + +func (s *DexTestSuite) TestDepositToken0BELWithSwapPartial() { + s.fundAliceBalances(50, 10) + s.fundBobBalances(0, 30) + + // GIVEN TokenA liquidity at tick 2002-2004 + s.bobDeposits(NewDeposit(0, 10, 2001, 1), + NewDeposit(0, 10, 2002, 1), + NewDeposit(0, 10, 2003, 1), + ) + // WHEN alice deposits TokenA at tick -2005 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(50, 10, 2006, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN some of alice's TokenA is swapped and she deposits ~13TokenA & 40TokenB + // A = 50 - 30 * 1.0001^~2002 = 13.3 + // B = 30(from swap) + 10 (initial deposit) = 40 + // SharesIssued = 13.3 + 40 * 1.0001^2006 = 62 + + s.Equal(math.NewInt(13347289), resp.Reserve0Deposited[0]) + s.Equal(math.NewInt(40000000), resp.Reserve1Deposited[0]) + s.Equal(math.NewInt(62232231), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(math.NewInt(13347289), math.NewInt(40000000), 2006, 1) +} + +func (s *DexTestSuite) TestDepositToken0BELWithSwapAll() { + s.fundAliceBalances(20, 10) + s.fundBobBalances(0, 30) + + // GIVEN TokenB liquidity at tick 10,001 + s.bobDeposits(NewDeposit(0, 10, 10000, 1)) + // WHEN alice deposits TokenA at tick -10,002 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(20, 10, 10003, 1, + types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10}, + ), + ) + + // THEN all of alice's token0 is swapped with 2 coins not swapped due to rounding + // and she deposits 0TokenA & ~17.3TokenB + // B = 10 + 20 / 1.0001^100001 = 17.3 + // SharesIssued = 17.3 * 1.0001^10003 = 47 + + s.True(resp.Reserve0Deposited[0].IsZero()) + s.Equal(math.NewInt(17357220), resp.Reserve1Deposited[0]) + s.Equal(math.NewInt(47193612), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(math.NewInt(2), math.ZeroInt()) + + s.assertLiquidityAtTickInt(math.ZeroInt(), math.NewInt(17357220), 10003, 1) +} + +func (s *DexTestSuite) TestDepositToken1BELWithSwapPartial() { + s.fundAliceBalances(10, 50) + s.fundBobBalances(30, 30) + + // GIVEN TokenA liquidity at tick -2000 to -2002 + s.bobDeposits(NewDeposit(10, 0, 2001, 1), + NewDeposit(10, 0, 2002, 1), + NewDeposit(10, 0, 2003, 1), + ) + // WHEN alice deposits TokenB at tick 1998 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(10, 50, 1998, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN some of alice's tokenB is swapped and she deposits 40TokenA & ~25.4TokenB + // + // A = 30(from swap) + 10 (initial deposit) = 40 + // B = 50 - 30 * 1.0001^~-2001 = 25.4 + // SharesIssued = 40 + 25.4 * 1.0001^1998 = ~71 + + s.Equal(math.NewInt(40000000), resp.Reserve0Deposited[0]) + s.Equal(math.NewInt(25440286), resp.Reserve1Deposited[0]) + s.Equal(math.NewInt(71066311), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(math.NewInt(40000000), math.NewInt(25440286), 1998, 1) +} + +func (s *DexTestSuite) TestDepositToken1BELWithSwapAll() { + s.fundAliceBalances(10, 20) + s.fundBobBalances(30, 0) + + // GIVEN TokenA liquidity at tick 10,000 + s.bobDeposits(NewDeposit(10, 0, -10001, 1)) + // WHEN alice deposits TokenB at tick -10,003 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(10, 20, -10004, 1, + types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10}, + ), + ) + + // THEN all of alice's tokenB is swapped + // and she deposits ~17.3TokenA and 0TokenB + // A = 10 + 20 / 1.0001^10000 = 17.3 + // SharesIssued = 17.3 + + s.Equal(math.NewInt(17356485), resp.Reserve0Deposited[0]) + s.True(resp.Reserve1Deposited[0].IsZero()) + s.Equal(math.NewInt(17356485), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(math.NewInt(0), math.ZeroInt()) + + s.assertLiquidityAtTickInt(math.NewInt(17356485), math.ZeroInt(), -10004, 1) +} diff --git a/x/dex/keeper/integration_deposit_singlesided_test.go b/x/dex/keeper/integration_deposit_singlesided_test.go index 4f7181f0c..59d74a8cd 100644 --- a/x/dex/keeper/integration_deposit_singlesided_test.go +++ b/x/dex/keeper/integration_deposit_singlesided_test.go @@ -458,3 +458,366 @@ func (s *DexTestSuite) TestDepositSingleToken1BELFails() { NewDepositWithOptions(0, 50, -2004, 1, types.DepositOptions{FailTxOnBel: true}), ) } + +func (s *DexTestSuite) TestDepositSingleToken0BELWithSwapPartial() { + s.fundAliceBalances(50, 0) + s.fundBobBalances(0, 30) + + // GIVEN TokenB liquidity at tick 2002-2004 + s.bobDeposits(NewDeposit(0, 10, 2001, 1), + NewDeposit(0, 10, 2002, 1), + NewDeposit(0, 10, 2003, 1), + ) + // WHEN alice deposits TokenA at tick -2005 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(50, 0, 2006, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN some of alice's tokenA is swapped and she deposits ~13TokenA & ~30TokenB + // A = 50 - 30 * 1.0001^~2003 = 13.3 + // SharesIssued = 13.3 + 30 * 1.0001^2006 = ~50 + + s.Equal(sdkmath.NewInt(13347289), resp.Reserve0Deposited[0]) + s.Equal(sdkmath.NewInt(30000000), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(50010995), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(13347289), sdkmath.NewInt(30000000), 2006, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken0BELWithSwapAll() { + s.fundAliceBalances(25, 0) + s.fundBobBalances(0, 30) + + // GIVEN TokenB liquidity at tick 2002-2004 + s.bobDeposits(NewDeposit(0, 10, 2001, 1), + NewDeposit(0, 10, 2002, 1), + NewDeposit(0, 10, 2003, 1), + ) + // WHEN alice deposits TokenA at tick -2005 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(25, 0, 2006, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN all of alice's TokenA is swapped and she deposits 0TokenA & ~20TokenB + // B = 25 / 1.0001^~2003 = 20.4 + // SharesIssued = 20.4 * 1.0001^2006 = 25 + + s.True(resp.Reserve0Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(20463287), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(25008665), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(sdkmath.ZeroInt(), sdkmath.NewInt(20463287), 2006, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken0BELWithSwapAll2() { + s.fundAliceBalances(20, 0) + s.fundBobBalances(0, 30) + + // GIVEN TokenB liquidity at tick 10,001 + s.bobDeposits(NewDeposit(0, 10, 10000, 1)) + // WHEN alice deposits TokenA at tick -10,002 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(20, 0, 10003, 1, + types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10}, + ), + ) + + // THEN (almost) all of alice's TokenA is swapped with 2 coins not swapped due to monotonic rounding + // and she deposits 0TokenA & ~7.3TokenB + // B = 20 / 1.0001^10001 = 7.3 + // SharesIssued = 7.3 * 1.0001^10003 = 20 + + s.True(resp.Reserve0Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(7357220), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(20003997), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.NewInt(2), sdkmath.ZeroInt()) + + s.assertLiquidityAtTickInt(sdkmath.ZeroInt(), sdkmath.NewInt(7357220), 10003, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken1BELWithSwapPartial() { + s.fundAliceBalances(0, 50) + s.fundBobBalances(20, 0) + + // GIVEN TokenA liquidity at tick 5002 & 5003 + s.bobDeposits( + NewDeposit(10, 0, -5001, 1), + NewDeposit(10, 0, -5002, 1), + ) + // WHEN alice deposits TokenB at tick -5004 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(0, 50, -5005, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN some of alice's tokenB is swapped and she deposits 20TokenA & ~17TokenB + // A = 20 (from swap) + // B = 50 - 20 * 1.0001^~5002 = ~17 + // SharesIssued = 20 + 17 * 1.0001^-5005 = 30.3 + + s.Equal(sdkmath.NewInt(20000000), resp.Reserve0Deposited[0]) + s.Equal(sdkmath.NewInt(17018153), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(30317130), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(20000000), sdkmath.NewInt(17018153), -5005, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken1BELWithSwapAll() { + s.fundAliceBalances(0, 5) + s.fundBobBalances(20, 0) + + // GIVEN TokenA liquidity at tick -5000 & -5001 + s.bobDeposits( + NewDeposit(10, 0, 5001, 1), + NewDeposit(10, 0, 5002, 1), + ) + // WHEN alice deposits TokenB at tick 5000 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(0, 5, 4999, 1, + types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10}, + ), + ) + + // THEN all of alice's TokenB is swapped and she deposits ~15TokenA & 0TokenB + // A = 5 / 1.0001^-5001 = 8.2 + // SharesIssued = 8.2 + + s.Equal(sdkmath.NewInt(8244224), resp.Reserve0Deposited[0]) + s.True(resp.Reserve1Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(8244224), resp.SharesIssued[0].Amount) + s.assertAliceBalances(0, 0) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(8244224), sdkmath.ZeroInt(), 4999, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken1BELWithSwapAll2() { + s.fundAliceBalances(0, 20) + s.fundBobBalances(10, 0) + + // GIVEN TokenA liquidity at tick 10,003 + s.bobDeposits(NewDeposit(10, 0, -10002, 1)) + // WHEN alice deposits TokenB at tick -10,004 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(0, 20, -10005, 1, + types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10}), + ) + + // THEN (almost) all of alice's TokenB is swapped with 2 coins not swapped due to monotonic rounding + // and she deposits ~7.3TokenA & 0TokenB + // A = 20 / 1.0001^10003 = 7.3 + // SharesIssued = 7.3 + + s.Equal(sdkmath.NewInt(7355749), resp.Reserve0Deposited[0]) + s.True(resp.Reserve1Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(7355749), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.NewInt(2)) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(7355749), sdkmath.ZeroInt(), -10005, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken1SameTickWithSwap() { + s.fundAliceBalances(0, 20) + s.fundBobBalances(10, 0) + + // GIVEN TokenA liquidity at tick 10,004 + s.bobDeposits(NewDeposit(10, 0, -10003, 1)) + // WHEN alice deposits TokenB at tick -10,004 (double sided liquidity; but not BEL ) + resp := s.aliceDeposits( + NewDepositWithOptions(0, 20, -10005, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN no swap happens all of alice's TokenB is deposited at -10004 + s.True(resp.Reserve0Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(20000000), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(7354278), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.ZeroInt()) + + s.assertLiquidityAtTickInt(sdkmath.ZeroInt(), sdkmath.NewInt(20000000), -10005, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken0SameTickWithSwap() { + s.fundAliceBalances(10, 0) + s.fundBobBalances(0, 10) + + // GIVEN TokenB liquidity at tick 1000 + s.bobDeposits(NewDeposit(0, 10, 999, 1)) + // WHEN alice deposits TokenA at tick -1000 (BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(10, 0, 1001, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN no swap happens all of alice's TokenA is deposited at tick -1000 + s.Equal(sdkmath.NewInt(10000000), resp.Reserve0Deposited[0]) + s.True(resp.Reserve1Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(10000000), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.ZeroInt()) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(10000000), sdkmath.ZeroInt(), 1001, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken0NotBELWithSwap() { + s.fundAliceBalances(20, 0) + s.fundBobBalances(0, 30) + + // GIVEN TokenB liquidity at tick 10,001 + s.bobDeposits(NewDeposit(0, 10, 10000, 1)) + // WHEN alice deposits TokenA at tick -49 (NOT BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(20, 0, 50, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN there is no swap and the deposit goes through as specified + s.Equal(sdkmath.NewInt(20000000), resp.Reserve0Deposited[0]) + s.True(resp.Reserve1Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(20000000), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.ZeroInt()) + + s.assertLiquidityAtTickInt(sdkmath.NewInt(20000000), sdkmath.ZeroInt(), 50, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken1NotBELWithSwap() { + s.fundAliceBalances(0, 20) + s.fundBobBalances(10, 0) + + // GIVEN TokenA liquidity at tick 10,003 + s.bobDeposits(NewDeposit(10, 0, -10002, 1)) + // WHEN alice deposits TokenB at tick -10,002 (NOT BEL) + resp := s.aliceDeposits( + NewDepositWithOptions(0, 20, -10003, 1, types.DepositOptions{FailTxOnBel: true, SwapOnDeposit: true}), + ) + + // THEN there is no swap and the deposit goes through as specified + s.True(resp.Reserve0Deposited[0].IsZero()) + s.Equal(sdkmath.NewInt(20000000), resp.Reserve1Deposited[0]) + s.Equal(sdkmath.NewInt(7355749), resp.SharesIssued[0].Amount) + s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.ZeroInt()) + + s.assertLiquidityAtTickInt(sdkmath.ZeroInt(), sdkmath.NewInt(20000000), -10003, 1) +} + +func (s *DexTestSuite) TestDepositSingleToken0WithSwapSlopToleranceFails() { + s.fundAliceBalances(10, 0) + s.fundBobBalances(0, 1) + + // GIVEN TokenB dust + s.limitSellsIntSuccess(s.bob, "TokenB", 999, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 998, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 997, sdkmath.NewInt(400)) + + // WHEN alice deposits TokenA at tick -1000 (BEL) with 0 slop tolerance + + // THEN deposit fails + s.assertAliceDepositFails( + types.ErrSwapOnDepositSlopToleranceNotSatisfied, + NewDepositWithOptions(10, 0, 1001, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 0, // 0% slop tolerance + }), + ) +} + +func (s *DexTestSuite) TestDepositSingleToken0WithSwapSlopToleranceFails2() { + s.fundAliceBalances(10, 0) + s.fundBobBalances(0, 1) + + // GIVEN TokenB dust + s.limitSellsIntSuccess(s.bob, "TokenB", 999, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 998, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 997, sdkmath.NewInt(400)) + + // WHEN alice deposits TokenA at tick -1000 (BEL) with 0.25% slop tolerance + // THEN deposit fails + s.assertAliceDepositFails( + types.ErrSwapOnDepositSlopToleranceNotSatisfied, + NewDepositWithOptions(10, 0, 1001, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 25, // 0.25% slop tolerance + }), + ) +} + +func (s *DexTestSuite) TestDepositSingleToken0WithSwapSlopTolerance() { + s.fundAliceBalances(10, 0) + s.fundBobBalances(0, 1) + + // GIVEN TokenB dust + s.limitSellsIntSuccess(s.bob, "TokenB", 999, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 998, sdkmath.NewInt(10)) + s.limitSellsIntSuccess(s.bob, "TokenB", 997, sdkmath.NewInt(400)) + + // WHEN alice deposits TokenA at tick -1000 (BEL) with 0.5% slop tolerance + // THEN deposit succeeds + s.aliceDeposits( + NewDepositWithOptions(10, 0, 1001, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 50, // 0.5% slop tolerance + }), + ) +} + +func (s *DexTestSuite) TestDepositSingleToken1WithSwapSlopToleranceFails() { + s.fundAliceBalances(0, 10) + s.fundBobBalances(1, 0) + + // GIVEN TokenA dust + s.limitSellsIntSuccess(s.bob, "TokenA", 999, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 998, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 997, sdkmath.NewInt(300)) + + // WHEN alice deposits TokenB at tick 996 (BEL) with 0 slop tolerance + // THEN deposit fails + s.assertAliceDepositFails( + types.ErrSwapOnDepositSlopToleranceNotSatisfied, + NewDepositWithOptions(0, 10, 995, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 0, // 0% slop tolerance + }), + ) +} + +func (s *DexTestSuite) TestDepositSingleToken1WithSwapSlopToleranceFails2() { + s.fundAliceBalances(0, 10) + s.fundBobBalances(1, 0) + + // GIVEN TokenA dust + s.limitSellsIntSuccess(s.bob, "TokenA", 999, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 998, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 997, sdkmath.NewInt(300)) + + // WHEN alice deposits TokenB at tick 996 (BEL) with 0.5% slop tolerance + // THEN deposit fails + s.assertAliceDepositFails( + types.ErrSwapOnDepositSlopToleranceNotSatisfied, + NewDepositWithOptions(0, 10, 995, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 50, // 0.5% slop tolerance + }), + ) +} + +func (s *DexTestSuite) TestDepositSingleToken1WithSwapSlopTolerance() { + s.fundAliceBalances(0, 10) + s.fundBobBalances(1, 0) + + // GIVEN TokenA dust + s.limitSellsIntSuccess(s.bob, "TokenA", 999, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 998, sdkmath.NewInt(20)) + s.limitSellsIntSuccess(s.bob, "TokenA", 997, sdkmath.NewInt(300)) + + // WHEN alice deposits TokenB at tick 996 (BEL) with 0.75% slop tolerance + // THEN deposit succeeds + s.aliceDeposits( + NewDepositWithOptions(0, 10, 995, 1, types.DepositOptions{ + FailTxOnBel: true, + SwapOnDeposit: true, + SwapOnDepositSlopToleranceBps: 75, // 0.75% slop tolerance + }), + ) +} diff --git a/x/dex/keeper/liquidity.go b/x/dex/keeper/liquidity.go index a0d74497d..8078066fe 100644 --- a/x/dex/keeper/liquidity.go +++ b/x/dex/keeper/liquidity.go @@ -57,11 +57,9 @@ func (k Keeper) Swap( // this avoids unnecessary iteration since outAmount will always be 0 going forward // this also catches the normal exit case where remainingTakerDenom == 0 - // NOTE: In theory this check should be: remainingTakerDenom / price < 1 - // but due to rounding and inaccuracy of fixed decimal math, it is possible - // for liq.swap to use the full the amount of taker liquidity and have a leftover - // amount of the taker Denom > than 1 token worth of maker denom - if math_utils.NewPrecDecFromInt(remainingTakerDenom).Quo(liq.Price()).LT(math_utils.NewPrecDec(2)) { + // This also allows us to handle a corner case where totalTakerCoin < maxAmountAmountTakerDenom + // and there is still valid tradeable liquidity but the order cannot be filled any further due to monotonic rounding. + if math_utils.NewPrecDecFromInt(remainingTakerDenom).Quo(liq.Price()).LT(math_utils.OnePrecDec()) { orderFilled = true break } diff --git a/x/dex/keeper/msg_server_test.go b/x/dex/keeper/msg_server_test.go index 5598c0945..22db135f7 100644 --- a/x/dex/keeper/msg_server_test.go +++ b/x/dex/keeper/msg_server_test.go @@ -485,6 +485,19 @@ func (s *DexTestSuite) limitSellsInt( return msg.TrancheKey, err } +func (s *DexTestSuite) limitSellsIntSuccess( + account sdk.AccAddress, + tokenIn string, + tickIndexNormalized int, + amountIn sdkmath.Int, + orderTypeOpt ...types.LimitOrderType, +) string { + trancheKey, err := s.limitSellsInt(account, tokenIn, tickIndexNormalized, amountIn, orderTypeOpt...) + s.NoError(err) + + return trancheKey +} + func (s *DexTestSuite) limitSells( account sdk.AccAddress, tokenIn string, @@ -1773,6 +1786,36 @@ func TestMsgDepositValidate(t *testing.T) { }, types.ErrInvalidFee, }, + { + "SwapOnDeposit without autoswap", + types.MsgDeposit{ + Creator: sample.AccAddress(), + Receiver: sample.AccAddress(), + TokenA: "TokenA", + TokenB: "TokenB", + Fees: []uint64{1}, + TickIndexesAToB: []int64{0}, + AmountsA: []sdkmath.Int{sdkmath.OneInt()}, + AmountsB: []sdkmath.Int{sdkmath.OneInt()}, + Options: []*types.DepositOptions{{DisableAutoswap: true, SwapOnDeposit: true}}, + }, + types.ErrSwapOnDepositWithoutAutoswap, + }, + { + "invalid slop tolerance", + types.MsgDeposit{ + Creator: sample.AccAddress(), + Receiver: sample.AccAddress(), + TokenA: "TokenA", + TokenB: "TokenB", + Fees: []uint64{1}, + TickIndexesAToB: []int64{0}, + AmountsA: []sdkmath.Int{sdkmath.OneInt()}, + AmountsB: []sdkmath.Int{sdkmath.OneInt()}, + Options: []*types.DepositOptions{{DisableAutoswap: false, SwapOnDeposit: true, SwapOnDepositSlopToleranceBps: 10001}}, + }, + types.ErrInvalidSlopTolerance, + }, } for _, tt := range tests { diff --git a/x/dex/types/errors.go b/x/dex/types/errors.go index ff0b765b2..a0cd179b6 100644 --- a/x/dex/types/errors.go +++ b/x/dex/types/errors.go @@ -227,6 +227,26 @@ var ( ErrZeroMinAverageSellPrice = sdkerrors.Register( ModuleName, 1165, - "MinAverageSellPrice must be nil or > 0.", + "MinAverageSellPrice must be nil or > 0", + ) + ErrDoubleSidedSwapOnDeposit = sdkerrors.Register( + ModuleName, + 1166, + "Swap on deposit cannot be performed for Token0 and Token1", + ) + ErrSwapOnDepositWithoutAutoswap = sdkerrors.Register( + ModuleName, + 1167, + "Cannot disable autoswap when using swap_on_deposit", + ) + ErrSwapOnDepositSlopToleranceNotSatisfied = sdkerrors.Register( + ModuleName, + 1168, + "Swap on deposit true price is less than minimum allowed price", + ) + ErrInvalidSlopTolerance = sdkerrors.Register( + ModuleName, + 1169, + "Slop tolerance must be between 0 and 10000", ) ) diff --git a/x/dex/types/events.go b/x/dex/types/events.go index ce8c1d4bd..d74f6b93f 100644 --- a/x/dex/types/events.go +++ b/x/dex/types/events.go @@ -20,6 +20,8 @@ const ( AttributeTokenIn = "TokenIn" AttributeTokenOut = "TokenOut" AttributeAmountIn = "AmountIn" + AttributeAmountIn0 = "AmountInTokenZero" + AttributeAmountIn1 = "AmountInTokenOne" AttributeAmountOut = "AmountOut" AttributeSwapAmountIn = "SwapAmountIn" AttributeSwapAmountOut = "SwapAmountOut" @@ -85,6 +87,8 @@ func CreateDepositEvent( fee uint64, depositAmountReserve0 math.Int, depositAmountReserve1 math.Int, + amountIn0 math.Int, + amountIn1 math.Int, sharesMinted math.Int, ) sdk.Event { attrs := []sdk.Attribute{ @@ -99,6 +103,8 @@ func CreateDepositEvent( sdk.NewAttribute(AttributeReserves0Deposited, depositAmountReserve0.String()), sdk.NewAttribute(AttributeReserves1Deposited, depositAmountReserve1.String()), sdk.NewAttribute(AttributeSharesMinted, sharesMinted.String()), + sdk.NewAttribute(AttributeAmountIn0, amountIn0.String()), + sdk.NewAttribute(AttributeAmountIn1, amountIn1.String()), } return sdk.NewEvent(sdk.EventTypeMessage, attrs...) diff --git a/x/dex/types/message_deposit.go b/x/dex/types/message_deposit.go index 668b48d30..abac5e2c8 100644 --- a/x/dex/types/message_deposit.go +++ b/x/dex/types/message_deposit.go @@ -112,6 +112,14 @@ func (msg *MsgDeposit) Validate() error { if err := ValidateTickFee(msg.TickIndexesAToB[i], msg.Fees[i]); err != nil { return err } + + if msg.Options[i] != nil && msg.Options[i].DisableAutoswap && msg.Options[i].SwapOnDeposit { + return ErrSwapOnDepositWithoutAutoswap + } + + if msg.Options[i] != nil && msg.Options[i].SwapOnDepositSlopToleranceBps > 10000 { + return ErrInvalidSlopTolerance + } } return nil diff --git a/x/dex/types/tx.pb.go b/x/dex/types/tx.pb.go index 93681d614..9abcfc04a 100644 --- a/x/dex/types/tx.pb.go +++ b/x/dex/types/tx.pb.go @@ -74,8 +74,10 @@ func (LimitOrderType) EnumDescriptor() ([]byte, []int) { } type DepositOptions struct { - DisableAutoswap bool `protobuf:"varint,1,opt,name=disable_autoswap,json=disableAutoswap,proto3" json:"disable_autoswap,omitempty"` - FailTxOnBel bool `protobuf:"varint,2,opt,name=fail_tx_on_bel,json=failTxOnBel,proto3" json:"fail_tx_on_bel,omitempty"` + DisableAutoswap bool `protobuf:"varint,1,opt,name=disable_autoswap,json=disableAutoswap,proto3" json:"disable_autoswap,omitempty"` + FailTxOnBel bool `protobuf:"varint,2,opt,name=fail_tx_on_bel,json=failTxOnBel,proto3" json:"fail_tx_on_bel,omitempty"` + SwapOnDeposit bool `protobuf:"varint,3,opt,name=swap_on_deposit,json=swapOnDeposit,proto3" json:"swap_on_deposit,omitempty"` + SwapOnDepositSlopToleranceBps uint64 `protobuf:"varint,4,opt,name=swap_on_deposit_slop_tolerance_bps,json=swapOnDepositSlopToleranceBps,proto3" json:"swap_on_deposit_slop_tolerance_bps,omitempty"` } func (m *DepositOptions) Reset() { *m = DepositOptions{} } @@ -125,6 +127,20 @@ func (m *DepositOptions) GetFailTxOnBel() bool { return false } +func (m *DepositOptions) GetSwapOnDeposit() bool { + if m != nil { + return m.SwapOnDeposit + } + return false +} + +func (m *DepositOptions) GetSwapOnDepositSlopToleranceBps() uint64 { + if m != nil { + return m.SwapOnDepositSlopToleranceBps + } + return 0 +} + type MsgDeposit struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` @@ -1061,129 +1077,132 @@ func init() { func init() { proto.RegisterFile("neutron/dex/tx.proto", fileDescriptor_a489f6e187d5e074) } var fileDescriptor_a489f6e187d5e074 = []byte{ - // 1940 bytes of a gzipped FileDescriptorProto + // 1992 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcb, 0x6f, 0x23, 0x49, - 0x19, 0x4f, 0xdb, 0x79, 0xd8, 0x95, 0xc4, 0xf1, 0x74, 0x32, 0x93, 0x1e, 0x0f, 0xa4, 0xad, 0x9e, - 0xd5, 0x8e, 0x19, 0x31, 0xf6, 0x78, 0x60, 0xf7, 0x90, 0x03, 0x92, 0x9d, 0xc7, 0xae, 0x59, 0x7b, - 0x12, 0xf5, 0x78, 0x05, 0xda, 0x95, 0x68, 0xda, 0xee, 0x8a, 0xd3, 0x4a, 0x77, 0x97, 0xd5, 0x55, - 0x76, 0x1c, 0x2e, 0xac, 0x10, 0xa7, 0x3d, 0xed, 0x05, 0x81, 0xc4, 0x3f, 0x00, 0x12, 0x87, 0x39, - 0xec, 0x99, 0xf3, 0x70, 0x5b, 0x21, 0x21, 0x01, 0x07, 0x03, 0x33, 0x87, 0x91, 0xf6, 0x98, 0x03, - 0x5c, 0x10, 0x42, 0xf5, 0x70, 0xbf, 0xec, 0x24, 0x93, 0xdd, 0x59, 0xc4, 0x61, 0x2f, 0x71, 0xd7, - 0xef, 0xfb, 0xea, 0xab, 0x5f, 0xd5, 0xf7, 0xa8, 0xaf, 0x3b, 0x60, 0xc3, 0x83, 0x03, 0xe2, 0x23, - 0xaf, 0x62, 0xc1, 0x51, 0x85, 0x8c, 0xca, 0x7d, 0x1f, 0x11, 0x24, 0x2f, 0x0b, 0xb4, 0x6c, 0xc1, - 0x51, 0xe1, 0x86, 0xe9, 0xda, 0x1e, 0xaa, 0xb0, 0xbf, 0x5c, 0x5e, 0xd8, 0xea, 0x22, 0xec, 0x22, - 0x5c, 0xe9, 0x98, 0x18, 0x56, 0x86, 0xd5, 0x0e, 0x24, 0x66, 0xb5, 0xd2, 0x45, 0xb6, 0x27, 0xe4, - 0x9b, 0x42, 0xee, 0xe2, 0x5e, 0x65, 0x58, 0xa5, 0x3f, 0x42, 0x70, 0x9b, 0x0b, 0x0c, 0x36, 0xaa, - 0xf0, 0x81, 0x10, 0x6d, 0xf4, 0x50, 0x0f, 0x71, 0x9c, 0x3e, 0x09, 0x54, 0xed, 0x21, 0xd4, 0x73, - 0x60, 0x85, 0x8d, 0x3a, 0x83, 0xa3, 0x0a, 0xb1, 0x5d, 0x88, 0x89, 0xe9, 0xf6, 0x85, 0x82, 0x12, - 0xdd, 0x40, 0xdf, 0xf4, 0x4d, 0x57, 0x18, 0xd4, 0x7e, 0x0c, 0x72, 0xbb, 0xb0, 0x8f, 0xb0, 0x4d, - 0x0e, 0xfa, 0xc4, 0x46, 0x1e, 0x96, 0xbf, 0x05, 0xf2, 0x96, 0x8d, 0xcd, 0x8e, 0x03, 0x0d, 0x73, - 0x40, 0x10, 0x3e, 0x35, 0xfb, 0x8a, 0x54, 0x94, 0x4a, 0x19, 0x7d, 0x4d, 0xe0, 0x35, 0x01, 0xcb, - 0x77, 0x41, 0xee, 0xc8, 0xb4, 0x1d, 0x83, 0x8c, 0x0c, 0xe4, 0x19, 0x1d, 0xe8, 0x28, 0x29, 0xa6, - 0xb8, 0x4c, 0xd1, 0xf6, 0xe8, 0xc0, 0xab, 0x43, 0x47, 0x7b, 0x96, 0x06, 0xa0, 0x85, 0x7b, 0x62, - 0x15, 0x59, 0x01, 0x4b, 0x5d, 0x1f, 0x9a, 0x04, 0xf9, 0xcc, 0x6a, 0x56, 0x9f, 0x0c, 0xe5, 0x02, - 0xc8, 0xf8, 0xb0, 0x0b, 0xed, 0x21, 0xf4, 0x99, 0x9d, 0xac, 0x1e, 0x8c, 0xe5, 0x4d, 0xb0, 0x44, - 0xd0, 0x09, 0xf4, 0x0c, 0x53, 0x49, 0x33, 0xd1, 0x22, 0x1b, 0xd6, 0x42, 0x41, 0x47, 0x99, 0x8f, - 0x08, 0xea, 0xf2, 0x87, 0x20, 0x6b, 0xba, 0x68, 0xe0, 0x11, 0x6c, 0x98, 0xca, 0x42, 0x31, 0x5d, - 0xca, 0xd6, 0xbf, 0xf7, 0x6c, 0xac, 0xce, 0xfd, 0x75, 0xac, 0xde, 0xe4, 0x47, 0x8a, 0xad, 0x93, - 0xb2, 0x8d, 0x2a, 0xae, 0x49, 0x8e, 0xcb, 0x0d, 0x8f, 0x7c, 0x3e, 0x56, 0xc3, 0x19, 0xe7, 0x63, - 0x35, 0x7f, 0x66, 0xba, 0xce, 0xb6, 0x16, 0x40, 0x9a, 0x9e, 0x11, 0xcf, 0xb5, 0xa8, 0xf1, 0x8e, - 0xb2, 0x78, 0x4d, 0xe3, 0x9d, 0x69, 0xe3, 0x9d, 0xd0, 0x78, 0x5d, 0xfe, 0x36, 0x58, 0x27, 0x76, - 0xf7, 0xc4, 0xb0, 0x3d, 0x0b, 0x8e, 0x20, 0x36, 0x4c, 0x83, 0x20, 0xa3, 0xa3, 0x2c, 0x15, 0xd3, - 0xa5, 0xb4, 0xbe, 0x46, 0x45, 0x0d, 0x2e, 0xa9, 0xb5, 0x51, 0x5d, 0x96, 0xc1, 0xfc, 0x11, 0x84, - 0x58, 0xc9, 0x14, 0xd3, 0xa5, 0x79, 0x9d, 0x3d, 0xcb, 0x6f, 0x81, 0x25, 0xc4, 0xbd, 0xa9, 0x64, - 0x8b, 0xe9, 0xd2, 0xf2, 0xa3, 0x3b, 0xe5, 0x48, 0xac, 0x96, 0xe3, 0x0e, 0xd7, 0x27, 0xba, 0xdb, - 0xea, 0xcf, 0x5e, 0x3e, 0xbd, 0x3f, 0x71, 0xc7, 0xc7, 0x2f, 0x9f, 0xde, 0xcf, 0xd1, 0x70, 0x09, - 0x7d, 0xa7, 0xed, 0x83, 0xd5, 0x7d, 0xd3, 0x76, 0xa0, 0x35, 0x71, 0xa6, 0x0a, 0x96, 0x2d, 0xfe, - 0x68, 0xd8, 0xd6, 0x88, 0x39, 0x74, 0x5e, 0x07, 0x02, 0x6a, 0x58, 0x23, 0x79, 0x03, 0x2c, 0x40, - 0xdf, 0x47, 0x13, 0x87, 0xf2, 0x81, 0xf6, 0xcf, 0x34, 0x90, 0x43, 0xb3, 0x3a, 0xc4, 0x7d, 0xe4, - 0x61, 0x28, 0xff, 0x14, 0xc8, 0x3e, 0xc4, 0xd0, 0x1f, 0xc2, 0x87, 0x86, 0xb0, 0x01, 0x2d, 0x45, - 0x62, 0xc7, 0x7b, 0x78, 0xd5, 0xf1, 0xce, 0x98, 0x7a, 0x3e, 0x56, 0x6f, 0xf3, 0x73, 0x9e, 0x96, - 0x69, 0xfa, 0x8d, 0x09, 0xb8, 0x3b, 0xc1, 0x22, 0x04, 0xaa, 0x11, 0x02, 0xa9, 0xeb, 0x11, 0xa8, - 0x5e, 0x42, 0xa0, 0x3a, 0x8b, 0x40, 0x35, 0x24, 0xb0, 0x03, 0xd6, 0x8e, 0xd8, 0x01, 0x4f, 0xf4, - 0xb0, 0x92, 0x66, 0x0e, 0x2c, 0xc4, 0x1c, 0x18, 0x73, 0x82, 0x9e, 0x3b, 0x8a, 0x0e, 0xb1, 0xfc, - 0x2b, 0x09, 0xac, 0xe2, 0x63, 0xd3, 0x87, 0xd8, 0xb0, 0x31, 0x1e, 0x40, 0x4b, 0x99, 0x67, 0x36, - 0x6e, 0x97, 0x45, 0x29, 0xa1, 0x05, 0xa9, 0x2c, 0x0a, 0x52, 0x79, 0x07, 0xd9, 0x5e, 0xfd, 0x87, - 0x62, 0x73, 0xf7, 0x7a, 0x36, 0x39, 0x1e, 0x74, 0xca, 0x5d, 0xe4, 0x8a, 0xba, 0x23, 0x7e, 0x1e, - 0x60, 0xeb, 0xa4, 0x42, 0xce, 0xfa, 0x10, 0xb3, 0x09, 0x9f, 0x8f, 0xd5, 0xf8, 0x12, 0xe7, 0x63, - 0x75, 0x83, 0xef, 0x34, 0x06, 0x6b, 0xfa, 0x0a, 0x1f, 0x37, 0xf8, 0xf0, 0x4f, 0x29, 0xb0, 0xda, - 0xc2, 0xbd, 0x1f, 0xd8, 0xe4, 0xd8, 0xf2, 0xcd, 0x53, 0xd3, 0xf9, 0x9f, 0x95, 0x83, 0x21, 0xc8, - 0x0b, 0x66, 0x04, 0x19, 0x3e, 0x74, 0xd1, 0x10, 0x8a, 0xaa, 0xd0, 0xbc, 0xca, 0xb1, 0x53, 0x13, - 0xcf, 0xc7, 0xea, 0x66, 0x6c, 0xb3, 0x81, 0x44, 0xd3, 0x73, 0x1c, 0x6a, 0x23, 0x9d, 0x01, 0x17, - 0x25, 0xf3, 0xe2, 0xe5, 0xc9, 0xbc, 0x14, 0x26, 0xf3, 0xb6, 0x96, 0xcc, 0xca, 0x1b, 0x22, 0x2b, - 0xc3, 0x53, 0xd4, 0x3e, 0x4d, 0x83, 0x9b, 0x31, 0x64, 0x66, 0x4e, 0x9d, 0x0a, 0xb1, 0xc7, 0x8f, - 0xfa, 0x3a, 0x39, 0x15, 0x4c, 0x9d, 0x91, 0x53, 0x81, 0x2c, 0x92, 0x53, 0x13, 0x26, 0x5e, 0x2c, - 0xa7, 0x42, 0x02, 0xa9, 0xeb, 0x11, 0xa8, 0x5e, 0x42, 0xa0, 0x3a, 0x8b, 0x40, 0x35, 0x24, 0x10, - 0x49, 0x87, 0xce, 0xc0, 0xf7, 0xa0, 0x25, 0x52, 0xea, 0xab, 0x49, 0x07, 0xbe, 0xc4, 0x54, 0x3a, - 0x70, 0x38, 0x48, 0x87, 0x3a, 0x1f, 0xfe, 0x67, 0x91, 0xd5, 0xc1, 0x43, 0xc7, 0xec, 0xc2, 0xa6, - 0xed, 0xda, 0xe4, 0xc0, 0xb7, 0xa0, 0xff, 0x05, 0x73, 0xe2, 0x36, 0xc8, 0xf0, 0xd0, 0xb7, 0x3d, - 0x91, 0x14, 0x3c, 0x15, 0x1a, 0x9e, 0x7c, 0x07, 0x64, 0xb9, 0x08, 0x0d, 0x88, 0xc8, 0x0b, 0xae, - 0x7b, 0x30, 0x20, 0xf2, 0x23, 0xb0, 0x11, 0x46, 0xa8, 0x61, 0x7b, 0x34, 0x40, 0xa9, 0xde, 0x42, - 0x51, 0x2a, 0xa5, 0xeb, 0x29, 0x45, 0xd2, 0xf3, 0x41, 0x98, 0x36, 0xbc, 0x36, 0xa2, 0x73, 0x82, - 0xfb, 0x8f, 0x2e, 0xb6, 0xc4, 0x7c, 0xf9, 0xaa, 0xf7, 0x9f, 0x61, 0x7b, 0xc9, 0xfb, 0xcf, 0xb0, - 0xbd, 0xe0, 0xfe, 0x6b, 0x78, 0xf2, 0x36, 0x00, 0x88, 0x9e, 0x83, 0x41, 0x0f, 0x58, 0xc9, 0x14, - 0xa5, 0x52, 0x2e, 0x71, 0x81, 0x85, 0x67, 0xd5, 0x3e, 0xeb, 0x43, 0x3d, 0x8b, 0x26, 0x8f, 0x72, - 0x0b, 0xac, 0xc1, 0x51, 0xdf, 0xf6, 0x4d, 0x7a, 0xa3, 0x19, 0xb4, 0x0d, 0x52, 0xb2, 0x45, 0x89, - 0x15, 0x50, 0xde, 0x23, 0x95, 0x27, 0x3d, 0x52, 0xb9, 0x3d, 0xe9, 0x91, 0xea, 0x99, 0x67, 0x63, - 0x55, 0xfa, 0xe4, 0x6f, 0xaa, 0xa4, 0xe7, 0xc2, 0xc9, 0x54, 0x2c, 0x7b, 0x20, 0xe7, 0x9a, 0x23, - 0x43, 0xd0, 0xa4, 0xa7, 0x02, 0xd8, 0x66, 0xdf, 0xa5, 0x33, 0x2e, 0xdb, 0x6c, 0x62, 0xda, 0xf9, - 0x58, 0xbd, 0xc9, 0x77, 0x1c, 0xc7, 0x35, 0x7d, 0xc5, 0x35, 0x47, 0x35, 0x36, 0xa6, 0xe7, 0xfa, - 0x0b, 0x09, 0xe4, 0x1d, 0xba, 0x39, 0x03, 0x43, 0xc7, 0x31, 0xfa, 0xbe, 0xdd, 0x85, 0xca, 0x32, - 0x5b, 0xf2, 0x44, 0x2c, 0xf9, 0xdd, 0x48, 0x4c, 0x8a, 0x33, 0x79, 0x80, 0xfc, 0xde, 0xe4, 0xb9, - 0x32, 0x7c, 0xab, 0x32, 0x20, 0xb6, 0x83, 0x39, 0x9b, 0x43, 0x1f, 0x76, 0x77, 0x61, 0x97, 0x56, - 0xb1, 0xa4, 0xdd, 0xb0, 0x8a, 0x25, 0x25, 0x9a, 0x9e, 0x63, 0xd0, 0x13, 0xe8, 0x38, 0x87, 0x14, - 0x90, 0x7f, 0x27, 0x81, 0x5b, 0xae, 0xed, 0x19, 0xe6, 0x10, 0xfa, 0x66, 0x0f, 0x46, 0xd9, 0xad, - 0x30, 0x76, 0xa7, 0x5f, 0x92, 0xdd, 0x05, 0xd6, 0xcf, 0xc7, 0xea, 0x37, 0xc5, 0xb9, 0xcd, 0x94, - 0x6b, 0xfa, 0xba, 0x6b, 0x7b, 0x35, 0x8e, 0x07, 0x74, 0xb7, 0xef, 0x25, 0x4b, 0xe6, 0x2d, 0x51, - 0x32, 0x13, 0x99, 0xa6, 0xfd, 0x2b, 0x0d, 0x0a, 0xd3, 0x70, 0x50, 0x3c, 0xb7, 0x00, 0x20, 0xbe, - 0xe9, 0x75, 0x8f, 0xe1, 0x7b, 0xf0, 0x4c, 0xe4, 0x62, 0x04, 0x91, 0x3f, 0x92, 0xc0, 0x12, 0x6d, - 0xe8, 0x69, 0x16, 0xa4, 0x58, 0x98, 0x5d, 0x52, 0x54, 0x9a, 0xd7, 0x2f, 0x2a, 0x13, 0xe3, 0xe7, - 0x63, 0x35, 0xc7, 0x8f, 0x41, 0x00, 0x9a, 0xbe, 0x48, 0x9f, 0x1a, 0x9e, 0xfc, 0x6b, 0x09, 0xe4, - 0x88, 0x79, 0x02, 0x7d, 0x83, 0x89, 0x68, 0x88, 0xa6, 0xaf, 0x62, 0xf2, 0xc1, 0xf5, 0x99, 0x24, - 0xd6, 0x08, 0xe3, 0x39, 0x8e, 0x6b, 0xfa, 0x0a, 0x03, 0xe8, 0x2c, 0x1a, 0xcf, 0xbf, 0x94, 0xc0, - 0x6a, 0x44, 0xc3, 0xf6, 0x58, 0xf5, 0x79, 0xed, 0xb5, 0x37, 0xb6, 0x44, 0x58, 0x7b, 0x63, 0xb0, - 0xa6, 0x2f, 0x07, 0xd4, 0x1a, 0x9e, 0xf6, 0xb1, 0x04, 0xee, 0x44, 0x6e, 0xcc, 0x7d, 0xdb, 0x71, - 0xa0, 0xf5, 0x4a, 0x35, 0x58, 0x05, 0xcb, 0x22, 0x04, 0x8c, 0x13, 0x78, 0x26, 0xca, 0x70, 0x24, - 0x2a, 0xb6, 0x1f, 0x26, 0xa3, 0x4f, 0x4d, 0x5c, 0xd8, 0xc9, 0xc5, 0xb4, 0x7f, 0xa4, 0xc0, 0xdd, - 0x4b, 0xe4, 0x41, 0x3c, 0xce, 0x70, 0xb6, 0xf4, 0xff, 0xe3, 0x6c, 0xca, 0xce, 0x8d, 0xb3, 0x4b, - 0x7d, 0x15, 0xec, 0xdc, 0x0b, 0xd8, 0xb9, 0x49, 0x76, 0x6e, 0x84, 0x9d, 0xf6, 0x13, 0xb0, 0xde, - 0xc2, 0xbd, 0x1d, 0xd3, 0xeb, 0x42, 0xe7, 0xf5, 0xf8, 0xb9, 0x94, 0xf4, 0xf3, 0xa6, 0xf0, 0x73, - 0x72, 0x11, 0xed, 0x2f, 0x29, 0x16, 0x6c, 0x49, 0xfc, 0x6b, 0xbf, 0xbe, 0x06, 0xbf, 0xde, 0x05, - 0xab, 0xad, 0x81, 0x43, 0xec, 0x77, 0x51, 0x5f, 0x47, 0x03, 0x02, 0x69, 0x0f, 0x7d, 0x8c, 0xfa, - 0x98, 0xbf, 0x37, 0xea, 0xec, 0x59, 0xfb, 0x7d, 0x1a, 0xac, 0xb5, 0x70, 0x6f, 0xa2, 0xf8, 0xe4, - 0xd4, 0xec, 0x7f, 0xc1, 0x2e, 0xeb, 0x11, 0x58, 0xf4, 0xe9, 0x32, 0xb3, 0x5f, 0xcc, 0x62, 0x4c, - 0x74, 0xa1, 0x19, 0xef, 0x96, 0xe6, 0x5f, 0x73, 0xb7, 0x44, 0x5b, 0x06, 0x38, 0xb2, 0x89, 0xc1, - 0x6f, 0x71, 0x7e, 0x29, 0x2f, 0x04, 0x2d, 0xc3, 0xdc, 0x97, 0x69, 0x19, 0x92, 0x76, 0xc3, 0x96, - 0x21, 0x29, 0xd1, 0x68, 0xeb, 0x64, 0x13, 0x16, 0xdb, 0xbc, 0x65, 0x78, 0x13, 0xac, 0xf5, 0x69, - 0x5b, 0xd9, 0x81, 0x98, 0x18, 0xec, 0x20, 0x94, 0x45, 0xf6, 0x71, 0x68, 0x95, 0xc2, 0x75, 0x88, - 0x09, 0x3b, 0xa4, 0xed, 0x37, 0x92, 0x59, 0xb4, 0x2e, 0xb2, 0x28, 0xea, 0x2c, 0xed, 0x0f, 0x29, - 0xb0, 0x99, 0xc0, 0x82, 0xec, 0xf9, 0xb9, 0x04, 0x32, 0xaf, 0x9e, 0x37, 0x8f, 0xaf, 0x1f, 0x99, - 0x99, 0x48, 0x4c, 0xae, 0x45, 0xee, 0x61, 0x16, 0x8d, 0xec, 0x8e, 0xa6, 0x69, 0xf2, 0x10, 0x2c, - 0xf0, 0x6d, 0xa6, 0x44, 0xc3, 0x79, 0x71, 0x60, 0x70, 0x45, 0x79, 0x00, 0xe6, 0xad, 0x01, 0x26, - 0x57, 0xbf, 0x8f, 0xec, 0x5f, 0x9f, 0x33, 0xb3, 0x7c, 0x3e, 0x56, 0x97, 0x39, 0x5f, 0x3a, 0xd2, - 0x74, 0x06, 0x6a, 0xbf, 0x95, 0x58, 0x32, 0xbc, 0xdf, 0xb7, 0x4c, 0x02, 0x0f, 0xd9, 0xc7, 0x40, - 0xf9, 0x6d, 0x90, 0x35, 0x07, 0xe4, 0x18, 0xf9, 0x36, 0x11, 0x8d, 0x4e, 0x5d, 0xf9, 0xe3, 0xa7, - 0x0f, 0x36, 0x04, 0xa5, 0x9a, 0x65, 0xf9, 0x10, 0xe3, 0x27, 0xc4, 0xb7, 0xbd, 0x9e, 0x1e, 0xaa, - 0xca, 0x6f, 0x83, 0x45, 0xfe, 0x39, 0x51, 0xec, 0x7a, 0x3d, 0xb6, 0x6b, 0x6e, 0xbc, 0x9e, 0xa5, - 0xf4, 0x7f, 0xf3, 0xf2, 0xe9, 0x7d, 0x49, 0x17, 0xda, 0xdb, 0x6f, 0x52, 0xaf, 0x87, 0x76, 0xa2, - 0x7e, 0x8f, 0xf2, 0xd2, 0x6e, 0x33, 0xb7, 0x47, 0xa1, 0x89, 0xdb, 0xef, 0x8f, 0x40, 0x2e, 0xfe, - 0x1e, 0x20, 0xdf, 0x02, 0xf2, 0x3b, 0x07, 0x07, 0xbb, 0x46, 0xbb, 0xd1, 0x34, 0x76, 0x6a, 0x8f, - 0x77, 0xf6, 0x9a, 0xcd, 0xbd, 0xdd, 0xfc, 0x9c, 0x9c, 0x07, 0x2b, 0xfb, 0x8d, 0x66, 0xd3, 0x38, - 0xd0, 0x8d, 0xf7, 0x1a, 0xcd, 0x66, 0x5e, 0x92, 0x37, 0xc1, 0x7a, 0xa3, 0xd5, 0xda, 0xdb, 0x6d, - 0xd4, 0xda, 0x7b, 0x14, 0xe6, 0xda, 0xf9, 0x14, 0x55, 0xfd, 0xfe, 0xfb, 0x4f, 0xda, 0x46, 0xe3, - 0xb1, 0xd1, 0x6e, 0xb4, 0xf6, 0xf2, 0x69, 0xf9, 0x06, 0x58, 0x0d, 0x8c, 0x32, 0x68, 0xfe, 0xd1, - 0xbf, 0xe7, 0x41, 0xba, 0x85, 0x7b, 0xf2, 0x0e, 0x58, 0x9a, 0x7c, 0x08, 0xdb, 0x8c, 0x7b, 0x3b, - 0xf8, 0xb6, 0x55, 0x50, 0x2f, 0x10, 0x04, 0xd1, 0xdb, 0x04, 0x20, 0xf2, 0x39, 0xa4, 0x90, 0x54, - 0x0f, 0x65, 0x05, 0xed, 0x62, 0x59, 0x60, 0xed, 0x43, 0xb0, 0x96, 0x7c, 0x9b, 0x9c, 0x62, 0x90, - 0x50, 0x28, 0xdc, 0xbb, 0x42, 0x21, 0x30, 0x3e, 0x04, 0xca, 0x85, 0xfd, 0x52, 0xe9, 0x22, 0x72, - 0x49, 0xcd, 0xc2, 0xc3, 0x57, 0xd5, 0x0c, 0xd6, 0xfd, 0x11, 0xc8, 0x4f, 0xdd, 0xdb, 0xc5, 0xa4, - 0x95, 0xa4, 0x46, 0xa1, 0x74, 0x95, 0x46, 0x60, 0x5f, 0x07, 0x2b, 0xb1, 0x9b, 0xe1, 0x1b, 0xc9, - 0x99, 0x51, 0x69, 0xe1, 0x8d, 0xcb, 0xa4, 0x51, 0x9b, 0xb1, 0x04, 0x9b, 0xb2, 0x19, 0x95, 0x4e, - 0xdb, 0x9c, 0x15, 0xf1, 0x85, 0x85, 0x8f, 0x68, 0x0e, 0xd5, 0xdf, 0x79, 0xf6, 0x7c, 0x4b, 0xfa, - 0xec, 0xf9, 0x96, 0xf4, 0xf7, 0xe7, 0x5b, 0xd2, 0x27, 0x2f, 0xb6, 0xe6, 0x3e, 0x7b, 0xb1, 0x35, - 0xf7, 0xe7, 0x17, 0x5b, 0x73, 0x1f, 0x3c, 0xb8, 0xba, 0xd0, 0x8f, 0xf8, 0xff, 0x30, 0x68, 0xa9, - 0xe8, 0x2c, 0xb2, 0x77, 0xe1, 0xef, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x95, 0x04, 0x7c, 0xe0, - 0xdf, 0x18, 0x00, 0x00, + 0x19, 0x4f, 0xdb, 0xce, 0xc3, 0x95, 0xc4, 0xf1, 0x74, 0xb2, 0x93, 0x1e, 0x0f, 0x9b, 0xb6, 0x7a, + 0x56, 0x33, 0x66, 0xc4, 0xd8, 0xe3, 0x81, 0xdd, 0x43, 0x0e, 0x48, 0x71, 0x32, 0xd9, 0x35, 0x6b, + 0x4f, 0xa2, 0x8e, 0x57, 0xa0, 0x5d, 0x89, 0x56, 0xdb, 0x5d, 0x71, 0x5a, 0xe9, 0xee, 0x6a, 0x75, + 0x95, 0x13, 0x87, 0x0b, 0x2b, 0xc4, 0x69, 0x4f, 0x7b, 0x41, 0x20, 0xf1, 0x0f, 0x80, 0xc4, 0x61, + 0x0e, 0x7b, 0xe6, 0x3c, 0xdc, 0x56, 0x20, 0x24, 0xe0, 0x60, 0x60, 0xe6, 0x30, 0xd2, 0x1e, 0x73, + 0x80, 0x0b, 0x42, 0xa8, 0x1e, 0xfd, 0xcc, 0x6b, 0xb2, 0x3b, 0x8b, 0x38, 0x70, 0x89, 0xbb, 0x7e, + 0xdf, 0x57, 0x5f, 0xfd, 0xaa, 0xbe, 0x47, 0x7d, 0xdd, 0x01, 0x2b, 0x1e, 0x1c, 0x91, 0x00, 0x79, + 0x0d, 0x0b, 0x8e, 0x1b, 0x64, 0x5c, 0xf7, 0x03, 0x44, 0x90, 0x3c, 0x2f, 0xd0, 0xba, 0x05, 0xc7, + 0x95, 0x1b, 0xa6, 0x6b, 0x7b, 0xa8, 0xc1, 0xfe, 0x72, 0x79, 0x65, 0x6d, 0x80, 0xb0, 0x8b, 0x70, + 0xa3, 0x6f, 0x62, 0xd8, 0x38, 0x6a, 0xf6, 0x21, 0x31, 0x9b, 0x8d, 0x01, 0xb2, 0x3d, 0x21, 0x5f, + 0x15, 0x72, 0x17, 0x0f, 0x1b, 0x47, 0x4d, 0xfa, 0x23, 0x04, 0xb7, 0xb8, 0xc0, 0x60, 0xa3, 0x06, + 0x1f, 0x08, 0xd1, 0xca, 0x10, 0x0d, 0x11, 0xc7, 0xe9, 0x93, 0x40, 0xd5, 0x21, 0x42, 0x43, 0x07, + 0x36, 0xd8, 0xa8, 0x3f, 0xda, 0x6f, 0x10, 0xdb, 0x85, 0x98, 0x98, 0xae, 0x2f, 0x14, 0x94, 0xe4, + 0x06, 0x7c, 0x33, 0x30, 0x5d, 0x61, 0x50, 0xfb, 0x83, 0x04, 0x4a, 0x5b, 0xd0, 0x47, 0xd8, 0x26, + 0x3b, 0x3e, 0xb1, 0x91, 0x87, 0xe5, 0x6f, 0x82, 0xb2, 0x65, 0x63, 0xb3, 0xef, 0x40, 0xc3, 0x1c, + 0x11, 0x84, 0x8f, 0x4d, 0x5f, 0x91, 0xaa, 0x52, 0x6d, 0x4e, 0x5f, 0x12, 0xf8, 0x86, 0x80, 0xe5, + 0x3b, 0xa0, 0xb4, 0x6f, 0xda, 0x8e, 0x41, 0xc6, 0x06, 0xf2, 0x8c, 0x3e, 0x74, 0x94, 0x1c, 0x53, + 0x9c, 0xa7, 0x68, 0x6f, 0xbc, 0xe3, 0xb5, 0xa0, 0x23, 0xdf, 0x05, 0x4b, 0x54, 0x99, 0x6a, 0x58, + 0x7c, 0x25, 0x25, 0xcf, 0xb4, 0x16, 0x29, 0xbc, 0xe3, 0x89, 0xe5, 0xe5, 0x36, 0xd0, 0x32, 0x7a, + 0x06, 0x76, 0x90, 0x6f, 0x10, 0xe4, 0xc0, 0xc0, 0xf4, 0x06, 0xd0, 0xe8, 0xfb, 0x58, 0x29, 0x54, + 0xa5, 0x5a, 0x41, 0x7f, 0x33, 0x35, 0x75, 0xcf, 0x41, 0x7e, 0x2f, 0xd4, 0x6a, 0xf9, 0x58, 0x7b, + 0x96, 0x07, 0xa0, 0x8b, 0x87, 0xa1, 0x65, 0x05, 0xcc, 0x0e, 0x02, 0x68, 0x12, 0x14, 0xb0, 0x8d, + 0x14, 0xf5, 0x70, 0x28, 0x57, 0xc0, 0x5c, 0x00, 0x07, 0xd0, 0x3e, 0x82, 0x01, 0xa3, 0x5e, 0xd4, + 0xa3, 0xb1, 0xbc, 0x0a, 0x66, 0x09, 0x3a, 0x84, 0x9e, 0x61, 0x32, 0xbe, 0x45, 0x7d, 0x86, 0x0d, + 0x37, 0x62, 0x41, 0x9f, 0xb1, 0x09, 0x05, 0x2d, 0xf9, 0x23, 0x50, 0x34, 0x5d, 0x34, 0xf2, 0x08, + 0x36, 0x4c, 0x65, 0xba, 0x9a, 0xaf, 0x15, 0x5b, 0xdf, 0x7d, 0x36, 0x51, 0xa7, 0xfe, 0x32, 0x51, + 0xdf, 0xe0, 0x6e, 0xc4, 0xd6, 0x61, 0xdd, 0x46, 0x0d, 0xd7, 0x24, 0x07, 0xf5, 0xb6, 0x47, 0xbe, + 0x98, 0xa8, 0xf1, 0x8c, 0xd3, 0x89, 0x5a, 0x3e, 0x31, 0x5d, 0x67, 0x5d, 0x8b, 0x20, 0x4d, 0x9f, + 0x13, 0xcf, 0x1b, 0x49, 0xe3, 0x7d, 0x65, 0xe6, 0x9a, 0xc6, 0xfb, 0x67, 0x8d, 0xf7, 0x63, 0xe3, + 0x2d, 0xf9, 0x5b, 0x60, 0x99, 0xd8, 0x83, 0x43, 0xc3, 0xf6, 0x2c, 0x38, 0x86, 0xd8, 0x30, 0x0d, + 0x82, 0x8c, 0xbe, 0x32, 0x5b, 0xcd, 0xd7, 0xf2, 0xfa, 0x12, 0x15, 0xb5, 0xb9, 0x64, 0xa3, 0x87, + 0x5a, 0xb2, 0x0c, 0x0a, 0xfb, 0x10, 0x62, 0x65, 0xae, 0x9a, 0xaf, 0x15, 0x74, 0xf6, 0x2c, 0xbf, + 0x0d, 0x66, 0x11, 0x0f, 0x20, 0xa5, 0x58, 0xcd, 0xd7, 0xe6, 0x1f, 0xdd, 0xae, 0x27, 0xf2, 0xa3, + 0x9e, 0x8e, 0x31, 0x3d, 0xd4, 0x5d, 0x57, 0x7f, 0xf2, 0xf2, 0xe9, 0xfd, 0xd0, 0x1d, 0x9f, 0xbc, + 0x7c, 0x7a, 0xbf, 0x44, 0x43, 0x34, 0xf6, 0x9d, 0xb6, 0x0d, 0x16, 0xb7, 0x4d, 0xdb, 0x81, 0x56, + 0xe8, 0x4c, 0x15, 0xcc, 0x87, 0xe1, 0x61, 0x5b, 0x63, 0xe6, 0xd0, 0x82, 0x0e, 0x04, 0xd4, 0xb6, + 0xc6, 0xf2, 0x0a, 0x98, 0x86, 0x41, 0x80, 0x42, 0x87, 0xf2, 0x81, 0xf6, 0x8f, 0x3c, 0x90, 0x63, + 0xb3, 0x3a, 0xc4, 0x3e, 0xf2, 0x30, 0x94, 0x7f, 0x0c, 0xe4, 0x00, 0x62, 0x18, 0x1c, 0xc1, 0x87, + 0x61, 0xd4, 0x41, 0x4b, 0x91, 0xd8, 0xf1, 0xee, 0x5e, 0x75, 0xbc, 0xe7, 0x4c, 0x3d, 0x9d, 0xa8, + 0xb7, 0xf8, 0x39, 0x9f, 0x95, 0x69, 0xfa, 0x8d, 0x10, 0xdc, 0x0a, 0xb1, 0x04, 0x81, 0x66, 0x82, + 0x40, 0xee, 0x7a, 0x04, 0x9a, 0x97, 0x10, 0x68, 0x9e, 0x47, 0xa0, 0x19, 0x13, 0xd8, 0x04, 0x4b, + 0xfb, 0xec, 0x80, 0x43, 0x3d, 0xac, 0xe4, 0x99, 0x03, 0x2b, 0x29, 0x07, 0xa6, 0x9c, 0xa0, 0x97, + 0xf6, 0x93, 0x43, 0x2c, 0xff, 0x42, 0x02, 0x8b, 0xf8, 0xc0, 0x0c, 0x20, 0x36, 0x6c, 0x8c, 0x47, + 0xd0, 0x52, 0x0a, 0xcc, 0xc6, 0xad, 0xba, 0x28, 0x5f, 0xb4, 0x08, 0xd6, 0x45, 0x11, 0xac, 0x6f, + 0x22, 0xdb, 0x6b, 0xfd, 0x40, 0x6c, 0xee, 0xde, 0xd0, 0x26, 0x07, 0xa3, 0x7e, 0x7d, 0x80, 0x5c, + 0x51, 0xeb, 0xc4, 0xcf, 0x03, 0x6c, 0x1d, 0x36, 0xc8, 0x89, 0x0f, 0x31, 0x9b, 0xf0, 0xc5, 0x44, + 0x4d, 0x2f, 0x71, 0x3a, 0x51, 0x57, 0xf8, 0x4e, 0x53, 0xb0, 0xa6, 0x2f, 0xf0, 0x71, 0x9b, 0x0f, + 0xff, 0x98, 0x03, 0x8b, 0x5d, 0x3c, 0xfc, 0xbe, 0x4d, 0x0e, 0xac, 0xc0, 0x3c, 0x36, 0x9d, 0xff, + 0x5a, 0x39, 0x38, 0x02, 0x65, 0xc1, 0x8c, 0x20, 0x23, 0x80, 0x2e, 0x3a, 0x82, 0xa2, 0x2a, 0x74, + 0xae, 0x72, 0xec, 0x99, 0x89, 0xa7, 0x13, 0x75, 0x35, 0xb5, 0xd9, 0x48, 0xa2, 0xe9, 0x25, 0x0e, + 0xf5, 0x90, 0xce, 0x80, 0x8b, 0x92, 0x79, 0xe6, 0xf2, 0x64, 0x9e, 0x8d, 0x93, 0x79, 0x5d, 0xcb, + 0x66, 0xe5, 0x0d, 0x91, 0x95, 0xf1, 0x29, 0x6a, 0x9f, 0xe5, 0xc1, 0x1b, 0x29, 0xe4, 0xdc, 0x9c, + 0x3a, 0x16, 0x62, 0x8f, 0x1f, 0xf5, 0x75, 0x72, 0x2a, 0x9a, 0x7a, 0x4e, 0x4e, 0x45, 0xb2, 0x44, + 0x4e, 0x85, 0x4c, 0xbc, 0x54, 0x4e, 0xc5, 0x04, 0x72, 0xd7, 0x23, 0xd0, 0xbc, 0x84, 0x40, 0xf3, + 0x3c, 0x02, 0xcd, 0x98, 0x40, 0x22, 0x1d, 0xfa, 0xa3, 0xc0, 0x83, 0x96, 0x48, 0xa9, 0xaf, 0x27, + 0x1d, 0xf8, 0x12, 0x67, 0xd2, 0x81, 0xc3, 0x51, 0x3a, 0xb4, 0xf8, 0xf0, 0xdf, 0x33, 0xac, 0x0e, + 0xee, 0x3a, 0xe6, 0x00, 0x76, 0x6c, 0xd7, 0x26, 0x3b, 0x81, 0x05, 0x83, 0x2f, 0x99, 0x13, 0xb7, + 0xc0, 0x1c, 0x0f, 0x7d, 0xdb, 0x13, 0x49, 0xc1, 0x53, 0xa1, 0xed, 0xc9, 0xb7, 0x41, 0x91, 0x8b, + 0xd0, 0x88, 0x88, 0xbc, 0xe0, 0xba, 0x3b, 0x23, 0x22, 0x3f, 0x02, 0x2b, 0x71, 0x84, 0x1a, 0xb6, + 0x47, 0x03, 0x94, 0xea, 0x4d, 0x57, 0xa5, 0x5a, 0xbe, 0x95, 0x53, 0x24, 0xbd, 0x1c, 0x85, 0x69, + 0xdb, 0xeb, 0x21, 0x3a, 0x27, 0xba, 0xff, 0xe8, 0x62, 0xb3, 0xcc, 0x97, 0xaf, 0x7a, 0xff, 0x19, + 0xb6, 0x97, 0xbd, 0xff, 0x0c, 0xdb, 0x8b, 0xee, 0xbf, 0xb6, 0x27, 0xaf, 0x03, 0x80, 0xe8, 0x39, + 0x18, 0xf4, 0x80, 0x95, 0xb9, 0xaa, 0x54, 0x2b, 0x65, 0x2e, 0xb0, 0xf8, 0xac, 0x7a, 0x27, 0x3e, + 0xd4, 0x8b, 0x28, 0x7c, 0x94, 0xbb, 0x60, 0x09, 0x8e, 0x7d, 0x3b, 0x30, 0xe9, 0x8d, 0x66, 0xd0, + 0xd6, 0x4b, 0x29, 0x56, 0x25, 0x56, 0x40, 0x79, 0x5f, 0x56, 0x0f, 0xfb, 0xb2, 0x7a, 0x2f, 0xec, + 0xcb, 0x5a, 0x73, 0xcf, 0x26, 0xaa, 0xf4, 0xe9, 0x5f, 0x55, 0x49, 0x2f, 0xc5, 0x93, 0xa9, 0x58, + 0xf6, 0x40, 0xc9, 0x35, 0xc7, 0x86, 0xa0, 0x49, 0x4f, 0x05, 0xb0, 0xcd, 0xbe, 0x47, 0x67, 0x5c, + 0xb6, 0xd9, 0xcc, 0xb4, 0xd3, 0x89, 0xfa, 0x06, 0xdf, 0x71, 0x1a, 0xd7, 0xf4, 0x05, 0xd7, 0x1c, + 0x6f, 0xb0, 0x31, 0x3d, 0xd7, 0x9f, 0x49, 0xa0, 0xec, 0xd0, 0xcd, 0x19, 0x18, 0x3a, 0x8e, 0xe1, + 0x07, 0xf6, 0x00, 0x2a, 0xf3, 0x6c, 0xc9, 0x43, 0xb1, 0xe4, 0x77, 0x12, 0x31, 0x29, 0xce, 0xe4, + 0x01, 0x0a, 0x86, 0xe1, 0x73, 0xe3, 0xe8, 0xed, 0xc6, 0x88, 0xd8, 0x0e, 0xe6, 0x6c, 0x76, 0x03, + 0x38, 0xd8, 0x82, 0x03, 0x5a, 0xc5, 0xb2, 0x76, 0xe3, 0x2a, 0x96, 0x95, 0x68, 0x7a, 0x89, 0x41, + 0x7b, 0xd0, 0x71, 0x76, 0x29, 0x20, 0xff, 0x46, 0x02, 0x37, 0x5d, 0xdb, 0x33, 0xcc, 0x23, 0x18, + 0x98, 0x43, 0x98, 0x64, 0xb7, 0xc0, 0xd8, 0x1d, 0x7f, 0x45, 0x76, 0x17, 0x58, 0x3f, 0x9d, 0xa8, + 0x6f, 0x8a, 0x73, 0x3b, 0x57, 0xae, 0xe9, 0xcb, 0xae, 0xed, 0x6d, 0x70, 0x3c, 0xa2, 0xbb, 0x7e, + 0x2f, 0x5b, 0x32, 0x6f, 0x8a, 0x92, 0x99, 0xc9, 0x34, 0xed, 0x9f, 0x79, 0x50, 0x39, 0x0b, 0x47, + 0xc5, 0x73, 0x0d, 0x00, 0x42, 0xfb, 0xd8, 0x03, 0xf8, 0x3e, 0x3c, 0x11, 0xb9, 0x98, 0x40, 0xe4, + 0x8f, 0x25, 0x30, 0x4b, 0x5f, 0x22, 0x68, 0x16, 0xe4, 0x58, 0x98, 0x5d, 0x52, 0x54, 0x3a, 0xd7, + 0x2f, 0x2a, 0xa1, 0xf1, 0xd3, 0x89, 0x5a, 0xe2, 0xc7, 0x20, 0x00, 0x4d, 0x9f, 0xa1, 0x4f, 0x6d, + 0x4f, 0xfe, 0xa5, 0x04, 0x4a, 0xc4, 0x3c, 0x84, 0x81, 0xc1, 0x44, 0x34, 0x44, 0xf3, 0x57, 0x31, + 0xf9, 0xf0, 0xfa, 0x4c, 0x32, 0x6b, 0xc4, 0xf1, 0x9c, 0xc6, 0x35, 0x7d, 0x81, 0x01, 0x74, 0x16, + 0x8d, 0xe7, 0x9f, 0x4b, 0x60, 0x31, 0xa1, 0x61, 0x7b, 0xac, 0xfa, 0xbc, 0xf6, 0xda, 0x9b, 0x5a, + 0x22, 0xae, 0xbd, 0x29, 0x58, 0xd3, 0xe7, 0x23, 0x6a, 0x6d, 0x4f, 0xfb, 0x44, 0x02, 0xb7, 0x13, + 0x37, 0xe6, 0xb6, 0xed, 0x38, 0xd0, 0x7a, 0xa5, 0x1a, 0xac, 0x82, 0x79, 0x11, 0x02, 0xc6, 0x21, + 0x3c, 0x11, 0x65, 0x38, 0x11, 0x15, 0xeb, 0x0f, 0xb3, 0xd1, 0xa7, 0x66, 0x2e, 0xec, 0xec, 0x62, + 0xda, 0xdf, 0x73, 0xe0, 0xce, 0x25, 0xf2, 0x28, 0x1e, 0xcf, 0x71, 0xb6, 0xf4, 0xbf, 0xe3, 0x6c, + 0xca, 0xce, 0x4d, 0xb3, 0xcb, 0x7d, 0x1d, 0xec, 0xdc, 0x0b, 0xd8, 0xb9, 0x59, 0x76, 0x6e, 0x82, + 0x9d, 0xf6, 0x23, 0xb0, 0xdc, 0xc5, 0xc3, 0x4d, 0xfa, 0x56, 0xea, 0xbc, 0x1e, 0x3f, 0xd7, 0xb2, + 0x7e, 0x5e, 0x15, 0x7e, 0xce, 0x2e, 0xa2, 0xfd, 0x39, 0xc7, 0x82, 0x2d, 0x8b, 0xff, 0xdf, 0xaf, + 0xaf, 0xc1, 0xaf, 0x77, 0xc0, 0x62, 0x77, 0xe4, 0x10, 0xfb, 0x3d, 0xe4, 0xeb, 0x68, 0x44, 0x20, + 0xed, 0xa1, 0x0f, 0x90, 0x8f, 0xf9, 0x7b, 0xa3, 0xce, 0x9e, 0xb5, 0xdf, 0xe6, 0xc1, 0x52, 0x17, + 0x0f, 0x43, 0xc5, 0xbd, 0x63, 0xd3, 0xff, 0x92, 0x5d, 0xd6, 0x23, 0x30, 0x13, 0xd0, 0x65, 0xce, + 0x7f, 0x31, 0x4b, 0x31, 0xd1, 0x85, 0x66, 0xba, 0x5b, 0x2a, 0xbc, 0xe6, 0x6e, 0x89, 0xb6, 0x0c, + 0x70, 0x6c, 0x13, 0x83, 0xdf, 0xe2, 0xfc, 0x52, 0x9e, 0x8e, 0x5a, 0x86, 0xa9, 0xaf, 0xd2, 0x32, + 0x64, 0xed, 0xc6, 0x2d, 0x43, 0x56, 0xa2, 0xd1, 0xd6, 0xc9, 0x26, 0x2c, 0xb6, 0x79, 0xcb, 0x70, + 0x17, 0x2c, 0xf9, 0xb4, 0xad, 0xec, 0x43, 0x4c, 0x0c, 0x76, 0x10, 0xca, 0x0c, 0xff, 0xd2, 0x44, + 0xe1, 0x16, 0xc4, 0x84, 0x1d, 0xd2, 0xfa, 0x5b, 0xd9, 0x2c, 0x5a, 0x16, 0x59, 0x94, 0x74, 0x96, + 0xf6, 0xbb, 0x1c, 0x58, 0xcd, 0x60, 0x51, 0xf6, 0xfc, 0x54, 0x02, 0x73, 0xaf, 0x9e, 0x37, 0x4f, + 0xae, 0x1f, 0x99, 0x73, 0x89, 0x98, 0x5c, 0x4a, 0xdc, 0xc3, 0x2c, 0x1a, 0xd9, 0x1d, 0x4d, 0xd3, + 0xe4, 0x21, 0x98, 0xe6, 0xdb, 0xcc, 0x89, 0x86, 0xf3, 0xe2, 0xc0, 0xe0, 0x8a, 0xf2, 0x08, 0x14, + 0xac, 0x11, 0x26, 0x57, 0xbf, 0x8f, 0x6c, 0x5f, 0x9f, 0x33, 0xb3, 0x7c, 0x3a, 0x51, 0xe7, 0x39, + 0x5f, 0x3a, 0xd2, 0x74, 0x06, 0x6a, 0xbf, 0x96, 0x58, 0x32, 0x7c, 0xe0, 0x5b, 0x26, 0x81, 0xbb, + 0xec, 0x03, 0xa4, 0xfc, 0x0e, 0x28, 0x9a, 0x23, 0x72, 0x80, 0x02, 0x9b, 0x88, 0x46, 0xa7, 0xa5, + 0xfc, 0xfe, 0xb3, 0x07, 0x2b, 0x82, 0xd2, 0x86, 0x65, 0x05, 0x10, 0xe3, 0x3d, 0x12, 0xd8, 0xde, + 0x50, 0x8f, 0x55, 0xe5, 0x77, 0xc0, 0x0c, 0xff, 0x84, 0x29, 0x76, 0xbd, 0x9c, 0xda, 0x35, 0x37, + 0xde, 0x2a, 0x52, 0xfa, 0xbf, 0x7a, 0xf9, 0xf4, 0xbe, 0xa4, 0x0b, 0xed, 0xf5, 0xbb, 0xd4, 0xeb, + 0xb1, 0x9d, 0xa4, 0xdf, 0x93, 0xbc, 0xb4, 0x5b, 0xcc, 0xed, 0x49, 0x28, 0x74, 0xfb, 0xfd, 0x31, + 0x28, 0xa5, 0xdf, 0x03, 0xe4, 0x9b, 0x40, 0x7e, 0x77, 0x67, 0x67, 0xcb, 0xe8, 0xb5, 0x3b, 0xc6, + 0xe6, 0xc6, 0x93, 0xcd, 0xc7, 0x9d, 0xce, 0xe3, 0xad, 0xf2, 0x94, 0x5c, 0x06, 0x0b, 0xdb, 0xed, + 0x4e, 0xc7, 0xd8, 0xd1, 0x8d, 0xf7, 0xdb, 0x9d, 0x4e, 0x59, 0x92, 0x57, 0xc1, 0x72, 0xbb, 0xdb, + 0x7d, 0xbc, 0xd5, 0xde, 0xe8, 0x3d, 0xa6, 0x30, 0xd7, 0x2e, 0xe7, 0xa8, 0xea, 0xf7, 0x3e, 0xd8, + 0xeb, 0x19, 0xed, 0x27, 0x46, 0xaf, 0xdd, 0x7d, 0x5c, 0xce, 0xcb, 0x37, 0xc0, 0x62, 0x64, 0x94, + 0x41, 0x85, 0x47, 0xff, 0x2a, 0x80, 0x7c, 0x17, 0x0f, 0xe5, 0x4d, 0x30, 0x1b, 0x7e, 0x08, 0x5b, + 0x4d, 0x7b, 0x3b, 0xfa, 0xb6, 0x55, 0x51, 0x2f, 0x10, 0x44, 0xd1, 0xdb, 0x01, 0x20, 0xf1, 0x39, + 0xa4, 0x92, 0x55, 0x8f, 0x65, 0x15, 0xed, 0x62, 0x59, 0x64, 0xed, 0x23, 0xb0, 0x94, 0x7d, 0x9b, + 0x3c, 0xc3, 0x20, 0xa3, 0x50, 0xb9, 0x77, 0x85, 0x42, 0x64, 0xfc, 0x08, 0x28, 0x17, 0xf6, 0x4b, + 0xb5, 0x8b, 0xc8, 0x65, 0x35, 0x2b, 0x0f, 0x5f, 0x55, 0x33, 0x5a, 0xf7, 0x87, 0xa0, 0x7c, 0xe6, + 0xde, 0xae, 0x66, 0xad, 0x64, 0x35, 0x2a, 0xb5, 0xab, 0x34, 0x22, 0xfb, 0x3a, 0x58, 0x48, 0xdd, + 0x0c, 0xdf, 0xc8, 0xce, 0x4c, 0x4a, 0x2b, 0x6f, 0x5d, 0x26, 0x4d, 0xda, 0x4c, 0x25, 0xd8, 0x19, + 0x9b, 0x49, 0xe9, 0x59, 0x9b, 0xe7, 0x45, 0x7c, 0x65, 0xfa, 0x63, 0x9a, 0x43, 0xad, 0x77, 0x9f, + 0x3d, 0x5f, 0x93, 0x3e, 0x7f, 0xbe, 0x26, 0xfd, 0xed, 0xf9, 0x9a, 0xf4, 0xe9, 0x8b, 0xb5, 0xa9, + 0xcf, 0x5f, 0xac, 0x4d, 0xfd, 0xe9, 0xc5, 0xda, 0xd4, 0x87, 0x0f, 0xae, 0x2e, 0xf4, 0x63, 0xfe, + 0x7f, 0x13, 0x5a, 0x2a, 0xfa, 0x33, 0xec, 0x5d, 0xf8, 0xdb, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, + 0xbb, 0xd9, 0xb1, 0x88, 0x53, 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1502,6 +1521,21 @@ func (m *DepositOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.SwapOnDepositSlopToleranceBps != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.SwapOnDepositSlopToleranceBps)) + i-- + dAtA[i] = 0x20 + } + if m.SwapOnDeposit { + i-- + if m.SwapOnDeposit { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } if m.FailTxOnBel { i-- if m.FailTxOnBel { @@ -2523,6 +2557,12 @@ func (m *DepositOptions) Size() (n int) { if m.FailTxOnBel { n += 2 } + if m.SwapOnDeposit { + n += 2 + } + if m.SwapOnDepositSlopToleranceBps != 0 { + n += 1 + sovTx(uint64(m.SwapOnDepositSlopToleranceBps)) + } return n } @@ -2989,6 +3029,45 @@ func (m *DepositOptions) Unmarshal(dAtA []byte) error { } } m.FailTxOnBel = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapOnDeposit", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SwapOnDeposit = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapOnDepositSlopToleranceBps", wireType) + } + m.SwapOnDepositSlopToleranceBps = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SwapOnDepositSlopToleranceBps |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:])