From 45ba0eb5c40992e4c2f120a1147ae4fd94d2fc94 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Sat, 10 Aug 2019 13:20:32 +0800 Subject: [PATCH 1/8] Optimization code --- app/v2/coinswap/handler.go | 6 +- app/v2/coinswap/internal/keeper/keeper.go | 212 +++++------- .../coinswap/internal/keeper/keeper_test.go | 47 +-- app/v2/coinswap/internal/keeper/querier.go | 10 +- .../coinswap/internal/keeper/querier_test.go | 7 - app/v2/coinswap/internal/keeper/swap.go | 149 ++++---- app/v2/coinswap/internal/keeper/swap_test.go | 70 ++-- .../internal/types/expected_keepers.go | 17 + app/v2/coinswap/internal/types/msgs.go | 126 +++---- app/v2/coinswap/internal/types/querier.go | 2 - client/coinswap/lcd/rest.go | 12 + client/coinswap/lcd/sendtx.go | 325 ++++++++++++++++++ types/coin.go | 2 +- types/int.go | 4 + 14 files changed, 609 insertions(+), 380 deletions(-) create mode 100644 client/coinswap/lcd/rest.go create mode 100644 client/coinswap/lcd/sendtx.go diff --git a/app/v2/coinswap/handler.go b/app/v2/coinswap/handler.go index c27ea5945..02d33855c 100644 --- a/app/v2/coinswap/handler.go +++ b/app/v2/coinswap/handler.go @@ -32,7 +32,7 @@ func HandleMsgSwapOrder(ctx sdk.Context, msg MsgSwapOrder, k Keeper) sdk.Result if ctx.BlockHeader().Time.After(msg.Deadline) { return ErrInvalidDeadline("deadline has passed for MsgSwapOrder").Result() } - tag, err := k.Swap(ctx, msg) + tag, err := k.HandleSwap(ctx, msg) if err != nil { return err.Result() } @@ -48,7 +48,7 @@ func HandleMsgAddLiquidity(ctx sdk.Context, msg MsgAddLiquidity, k Keeper) sdk.R return ErrInvalidDeadline("deadline has passed for MsgAddLiquidity").Result() } - err := k.AddLiquidity(ctx, msg) + err := k.HandleAddLiquidity(ctx, msg) if err != nil { return err.Result() } @@ -63,7 +63,7 @@ func HandleMsgRemoveLiquidity(ctx sdk.Context, msg MsgRemoveLiquidity, k Keeper) return ErrInvalidDeadline("deadline has passed for MsgRemoveLiquidity").Result() } - err := k.RemoveLiquidity(ctx, msg) + err := k.HandleRemoveLiquidity(ctx, msg) if err != nil { return err.Result() } diff --git a/app/v2/coinswap/internal/keeper/keeper.go b/app/v2/coinswap/internal/keeper/keeper.go index 8947c7080..dbda0425d 100644 --- a/app/v2/coinswap/internal/keeper/keeper.go +++ b/app/v2/coinswap/internal/keeper/keeper.go @@ -3,8 +3,6 @@ package keeper import ( "fmt" - "github.com/irisnet/irishub/app/v1/auth" - "github.com/irisnet/irishub/app/v1/bank" "github.com/irisnet/irishub/app/v1/params" "github.com/irisnet/irishub/app/v2/coinswap/internal/types" "github.com/irisnet/irishub/codec" @@ -16,8 +14,8 @@ import ( type Keeper struct { cdc *codec.Codec storeKey sdk.StoreKey - bk bank.Keeper - ak auth.AccountKeeper + bk types.BankKeeper + ak types.AuthKeeper paramSpace params.Subspace } @@ -25,7 +23,7 @@ type Keeper struct { // - creating new ModuleAccounts for each trading pair // - burning minting liquidity coins // - sending to and from ModuleAccounts -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, bk bank.Keeper, ak auth.AccountKeeper, paramSpace params.Subspace) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, bk types.BankKeeper, ak types.AuthKeeper, paramSpace params.Subspace) Keeper { return Keeper{ storeKey: key, bk: bk, @@ -35,20 +33,32 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, bk bank.Keeper, ak auth.Accou } } -// CreateReservePool initializes a new reserve pool by creating a special account. -func (k Keeper) CreateReservePool(ctx sdk.Context, reservePoolName string) { - reservePoolAccAddr := getReservePoolAddr(reservePoolName) - moduleAcc := k.ak.GetAccount(ctx, reservePoolAccAddr) - if moduleAcc != nil { - panic(fmt.Sprintf("reserve pool for %s already exists", reservePoolName)) - } - k.bk.AddCoins(ctx, reservePoolAccAddr, sdk.Coins{}) -} - -func (k Keeper) Swap(ctx sdk.Context, msg types.MsgSwapOrder) (sdk.Tags, sdk.Error) { +func (k Keeper) HandleSwap(ctx sdk.Context, msg types.MsgSwapOrder) (sdk.Tags, sdk.Error) { tags := sdk.EmptyTags() - handler := k.GetHandler(msg) - amount, err := handler(ctx, msg.Input, msg.Output, msg.Sender, msg.Recipient) + var amount sdk.Int + var err sdk.Error + var isDoubleSwap = msg.Input.Coin.Denom != sdk.IrisAtto && msg.Output.Coin.Denom != sdk.IrisAtto + + switch msg.IsBuyOrder { + case true: + switch isDoubleSwap { + case true: + amount, err = k.doubleTradeInputForExactOutput(ctx, msg.Input, msg.Output) + break + case false: + amount, err = k.tradeInputForExactOutput(ctx, msg.Input, msg.Output) + break + } + case false: + switch isDoubleSwap { + case true: + amount, err = k.doubleTradeExactInputForOutput(ctx, msg.Input, msg.Output) + break + case false: + amount, err = k.tradeExactInputForOutput(ctx, msg.Input, msg.Output) + break + } + } if err != nil { return nil, err } @@ -56,168 +66,124 @@ func (k Keeper) Swap(ctx sdk.Context, msg types.MsgSwapOrder) (sdk.Tags, sdk.Err return tags, nil } -func (k Keeper) AddLiquidity(ctx sdk.Context, msg types.MsgAddLiquidity) sdk.Error { - reservePoolName, err := k.GetReservePoolName(sdk.IrisAtto, msg.Deposit.Denom) - +func (k Keeper) HandleAddLiquidity(ctx sdk.Context, msg types.MsgAddLiquidity) sdk.Error { + reservePoolName, err := k.GetReservePoolName(sdk.IrisAtto, msg.MaxToken.Denom) if err != nil { return err } - - // create reserve pool if it does not exist - reservePool, found := k.GetReservePool(ctx, reservePoolName) - if !found { - k.CreateReservePool(ctx, reservePoolName) - } - - nativeReserveAmt := reservePool.AmountOf(sdk.IrisAtto) - depositedReserveAmt := reservePool.AmountOf(msg.Deposit.Denom) + reservePool := k.GetReservePool(ctx, reservePoolName) + irisReserveAmt := reservePool.AmountOf(sdk.IrisAtto) + tokenReserveAmt := reservePool.AmountOf(msg.MaxToken.Denom) liquidity := reservePool.AmountOf(reservePoolName) var mintLiquidityAmt sdk.Int - var depositedCoin sdk.Coin - var nativeCoin = sdk.NewCoin(sdk.IrisAtto, msg.DepositAmount) + var depositToken sdk.Coin + var irisCoin = sdk.NewCoin(sdk.IrisAtto, msg.ExactIrisAmt) // calculate amount of UNI to be minted for sender // and coin amount to be deposited if liquidity.IsZero() { - mintLiquidityAmt = msg.DepositAmount - depositedCoin = sdk.NewCoin(msg.Deposit.Denom, msg.Deposit.Amount) + mintLiquidityAmt = msg.ExactIrisAmt + depositToken = sdk.NewCoin(msg.MaxToken.Denom, msg.MaxToken.Amount) } else { - mintLiquidityAmt = (liquidity.Mul(msg.DepositAmount)).Div(nativeReserveAmt) - if mintLiquidityAmt.LT(msg.MinReward) { - return types.ErrConstraintNotMet(fmt.Sprintf("liquidity[%s] is less than user 's min reward[%s]", mintLiquidityAmt.String(), msg.MinReward.String())) - } - - mod := depositedReserveAmt.Mul(msg.DepositAmount).Mod(nativeReserveAmt) - depositAmt := (depositedReserveAmt.Mul(msg.DepositAmount)).Div(nativeReserveAmt) - if !mod.IsZero() { - depositAmt = depositAmt.AddRaw(1) + mintLiquidityAmt = (liquidity.Mul(msg.ExactIrisAmt)).Div(irisReserveAmt) + if mintLiquidityAmt.LT(msg.MinLiquidity) { + return types.ErrConstraintNotMet(fmt.Sprintf("liquidity[%s] is less than user 's min reward[%s]", mintLiquidityAmt.String(), msg.MinLiquidity.String())) } + depositAmt := (tokenReserveAmt.Mul(msg.ExactIrisAmt)).Div(irisReserveAmt) + depositToken = sdk.NewCoin(msg.MaxToken.Denom, depositAmt) - depositedCoin = sdk.NewCoin(msg.Deposit.Denom, depositAmt) - - if depositAmt.GT(msg.Deposit.Amount) { - return types.ErrConstraintNotMet(fmt.Sprintf("amount[%s] of token depositd is greater than user 's max deposited amount[%s]", depositedCoin.String(), msg.Deposit.String())) + if depositAmt.GT(msg.MaxToken.Amount) { + return types.ErrConstraintNotMet(fmt.Sprintf("amount[%s] of depositToken depositd is greater than user 's max deposited amount[%s]", depositToken.String(), msg.MaxToken.String())) } } - if !k.bk.HasCoins(ctx, msg.Sender, sdk.NewCoins(nativeCoin, depositedCoin)) { - return sdk.ErrInsufficientCoins("sender does not have sufficient funds to add liquidity") - } - - // transfer deposited liquidity into coinswaps ModuleAccount - k.SendCoins(ctx, msg.Sender, reservePoolName, sdk.NewCoins(nativeCoin, depositedCoin)) + return k.addLiquidity(ctx, msg.Sender, irisCoin, depositToken, reservePoolName, mintLiquidityAmt) +} - // mint liquidity vouchers for sender - coins := k.MintCoins(ctx, reservePoolName, mintLiquidityAmt) - _, _, err = k.bk.AddCoins(ctx, msg.Sender, coins) +func (k Keeper) addLiquidity(ctx sdk.Context, sender sdk.AccAddress, irisCoin, token sdk.Coin, reservePoolName string, mintLiquidityAmt sdk.Int) sdk.Error { + depositedTokens := sdk.NewCoins(irisCoin, token) + poolAddr := getReservePoolAddr(reservePoolName) + // transfer deposited token into coinswaps Account + _, err := k.bk.SendCoins(ctx, sender, poolAddr, depositedTokens) if err != nil { return err } + // mint liquidity vouchers for reserve Pool + mintToken := sdk.NewCoins(sdk.NewCoin(reservePoolName, mintLiquidityAmt)) + k.bk.AddCoins(ctx, poolAddr, mintToken) + // mint liquidity vouchers for sender + k.bk.AddCoins(ctx, sender, mintToken) return nil } -func (k Keeper) RemoveLiquidity(ctx sdk.Context, msg types.MsgRemoveLiquidity) sdk.Error { - reservePoolName, err := k.GetReservePoolName(sdk.IrisAtto, msg.Withdraw.Denom) - +func (k Keeper) HandleRemoveLiquidity(ctx sdk.Context, msg types.MsgRemoveLiquidity) sdk.Error { + reservePoolName, err := k.GetReservePoolName(sdk.IrisAtto, msg.MinToken.Denom) if err != nil { return err } // check if reserve pool exists - reservePool, found := k.GetReservePool(ctx, reservePoolName) - if !found { + reservePool := k.GetReservePool(ctx, reservePoolName) + if reservePool == nil { return types.ErrReservePoolNotExists("") } - nativeReserveAmt := reservePool.AmountOf(sdk.IrisAtto) - depositedReserveAmt := reservePool.AmountOf(msg.Withdraw.Denom) + irisReserveAmt := reservePool.AmountOf(sdk.IrisAtto) + tokenReserveAmt := reservePool.AmountOf(msg.MinToken.Denom) liquidityReserve := reservePool.AmountOf(reservePoolName) + if irisReserveAmt.LT(msg.MinIrisAmt) { + return types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s,expect:%s", irisReserveAmt.String(), msg.MinIrisAmt.String())) + } + if tokenReserveAmt.LT(msg.MinToken.Amount) { + return types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s,expect:%s", tokenReserveAmt.String(), msg.MinToken.Amount.String())) + } // calculate amount of UNI to be burned for sender // and coin amount to be returned - nativeWithdrawnAmt := msg.WithdrawAmount.Mul(nativeReserveAmt).Div(liquidityReserve) - depositedWithdrawnAmt := msg.WithdrawAmount.Mul(depositedReserveAmt).Div(liquidityReserve) + irisWithdrawnAmt := msg.WithdrawLiquidity.Mul(irisReserveAmt).Div(liquidityReserve) + tokenWithdrawnAmt := msg.WithdrawLiquidity.Mul(tokenReserveAmt).Div(liquidityReserve) - nativeWithdrawCoin := sdk.NewCoin(sdk.IrisAtto, nativeWithdrawnAmt) - depositedWithdrawCoin := sdk.NewCoin(msg.Withdraw.Denom, depositedWithdrawnAmt) - deltaLiquidityCoin := sdk.NewCoin(reservePoolName, msg.WithdrawAmount) + irisWithdrawCoin := sdk.NewCoin(sdk.IrisAtto, irisWithdrawnAmt) + tokenWithdrawCoin := sdk.NewCoin(msg.MinToken.Denom, tokenWithdrawnAmt) + deductUniCoin := sdk.NewCoin(reservePoolName, msg.WithdrawLiquidity) - if nativeWithdrawCoin.Amount.LT(msg.MinNative) { - return types.ErrConstraintNotMet(fmt.Sprintf("The amount of cash available [%s] is less than the minimum amount specified [%s] by the user.", nativeWithdrawCoin.String(), sdk.NewCoin(sdk.IrisAtto, msg.MinNative).String())) + if irisWithdrawCoin.Amount.LT(msg.MinIrisAmt) { + return types.ErrConstraintNotMet(fmt.Sprintf("The amount of iris available [%s] is less than the minimum amount specified [%s] by the user.", irisWithdrawCoin.String(), sdk.NewCoin(sdk.IrisAtto, msg.MinIrisAmt).String())) } - if depositedWithdrawCoin.Amount.LT(msg.Withdraw.Amount) { - return types.ErrConstraintNotMet(fmt.Sprintf("The amount of cash available [%s] is less than the minimum amount specified [%s] by the user.", depositedWithdrawCoin.String(), msg.Withdraw.String())) + if tokenWithdrawCoin.Amount.LT(msg.MinToken.Amount) { + return types.ErrConstraintNotMet(fmt.Sprintf("The amount of token available [%s] is less than the minimum amount specified [%s] by the user.", tokenWithdrawCoin.String(), msg.MinToken.String())) } + poolAddr := getReservePoolAddr(reservePoolName) + return k.removeLiquidity(ctx, poolAddr, msg.Sender, deductUniCoin, irisWithdrawCoin, tokenWithdrawCoin) +} +func (k Keeper) removeLiquidity(ctx sdk.Context, poolAddr, sender sdk.AccAddress, deductUniCoin, irisWithdrawCoin, tokenWithdrawCoin sdk.Coin) sdk.Error { // burn liquidity from reserve Pool - err = k.BurnLiquidity(ctx, reservePoolName, deltaLiquidityCoin) + deltaCoins := sdk.NewCoins(deductUniCoin) + _, _, err := k.bk.SubtractCoins(ctx, poolAddr, deltaCoins) if err != nil { return err } // burn liquidity from account - _, err = k.bk.BurnCoins(ctx, msg.Sender, sdk.Coins{deltaLiquidityCoin}) - + _, _, err = k.bk.SubtractCoins(ctx, sender, deltaCoins) if err != nil { return err } - // transfer withdrawn liquidity from coinswaps ModuleAccount to sender's account - coins := sdk.NewCoins(nativeWithdrawCoin, depositedWithdrawCoin) - k.ReceiveCoins(ctx, msg.Sender, reservePoolName, coins) - return nil -} - -// BurnCoins burns liquidity coins from the ModuleAccount at reservePoolName. The -// reservePoolName and denomination of the liquidity coins are the same. -func (k Keeper) BurnLiquidity(ctx sdk.Context, reservePoolName string, deltaCoin sdk.Coin) sdk.Error { - swapPoolAccAddr := getReservePoolAddr(reservePoolName) - if !k.bk.HasCoins(ctx, swapPoolAccAddr, sdk.NewCoins(deltaCoin)) { - return sdk.ErrInsufficientCoins("sender does not have sufficient funds to remove liquidity") - } - coins := sdk.NewCoins(deltaCoin) - _, err := k.bk.BurnCoins(ctx, swapPoolAccAddr, coins) - if err != nil { - return err - } - return nil -} - -// MintCoins mints liquidity coins to the ModuleAccount at reservePoolName. The -// reservePoolName and denomination of the liquidity coins are the same. -func (k Keeper) MintCoins(ctx sdk.Context, reservePoolName string, amt sdk.Int) sdk.Coins { - reservePoolAccAddr := getReservePoolAddr(reservePoolName) - coins := sdk.NewCoins(sdk.NewCoin(reservePoolName, amt)) - _, _, err := k.bk.AddCoins(ctx, reservePoolAccAddr, coins) - if err != nil { - panic(err) - } - return coins -} - -// SendCoin sends coins from the address to the ModuleAccount at reservePoolName. -func (k Keeper) SendCoins(ctx sdk.Context, addr sdk.AccAddress, reservePoolName string, coins sdk.Coins) sdk.Error { - swapPoolAccAddr := getReservePoolAddr(reservePoolName) - _, err := k.bk.SendCoins(ctx, addr, swapPoolAccAddr, coins) - return err -} - -// RecieveCoin sends coins from the ModuleAccount at reservePoolName to the -// address provided. -func (k Keeper) ReceiveCoins(ctx sdk.Context, addr sdk.AccAddress, reservePoolName string, coins sdk.Coins) sdk.Error { - swapPoolAccAddr := getReservePoolAddr(reservePoolName) - _, err := k.bk.SendCoins(ctx, swapPoolAccAddr, addr, coins) + coins := sdk.NewCoins(irisWithdrawCoin, tokenWithdrawCoin) + _, err = k.bk.SendCoins(ctx, poolAddr, sender, coins) return err } // GetReservePool returns the total balance of an reserve pool at the // provided denomination. -func (k Keeper) GetReservePool(ctx sdk.Context, reservePoolName string) (coins sdk.Coins, found bool) { +func (k Keeper) GetReservePool(ctx sdk.Context, reservePoolName string) (coins sdk.Coins) { swapPoolAccAddr := getReservePoolAddr(reservePoolName) acc := k.ak.GetAccount(ctx, swapPoolAccAddr) if acc == nil { - return nil, false + return nil } - return acc.GetCoins(), true + return acc.GetCoins() } // GetParams gets the parameters for the coinswap module. diff --git a/app/v2/coinswap/internal/keeper/keeper_test.go b/app/v2/coinswap/internal/keeper/keeper_test.go index 916d80bdc..7878466b1 100644 --- a/app/v2/coinswap/internal/keeper/keeper_test.go +++ b/app/v2/coinswap/internal/keeper/keeper_test.go @@ -12,24 +12,6 @@ const ( reservePoolName = "swap:iris:btc" ) -// test that the module account gets created with an initial -// balance of zero coins. -func TestCreateReservePool(t *testing.T) { - ctx, keeper, _ := createTestInput(t, sdk.NewInt(0), 0) - - poolAcc := getReservePoolAddr(reservePoolName) - moduleAcc := keeper.ak.GetAccount(ctx, poolAcc) - require.Nil(t, moduleAcc) - - keeper.CreateReservePool(ctx, reservePoolName) - moduleAcc = keeper.ak.GetAccount(ctx, poolAcc) - require.NotNil(t, moduleAcc) - require.Equal(t, true, moduleAcc.GetCoins().Empty(), "module account has non zero balance after creation") - - // attempt to recreate existing ModuleAccount - require.Panics(t, func() { keeper.CreateReservePool(ctx, reservePoolName) }) -} - // test that the params can be properly set and retrieved func TestParams(t *testing.T) { ctx, keeper, _ := createTestInput(t, sdk.NewInt(0), 0) @@ -49,27 +31,6 @@ func TestParams(t *testing.T) { } } -// test that non existent reserve pool returns false and -// that balance is updated. -func TestGetReservePool(t *testing.T) { - amt := sdk.NewInt(100) - ctx, keeper, accs := createTestInput(t, amt, 1) - - poolAcc := getReservePoolAddr(reservePoolName) - reservePool, found := keeper.GetReservePool(ctx, reservePoolName) - require.False(t, found) - - keeper.CreateReservePool(ctx, reservePoolName) - reservePool, found = keeper.GetReservePool(ctx, reservePoolName) - require.True(t, found) - - keeper.bk.SendCoins(ctx, accs[0].GetAddress(), poolAcc, sdk.Coins{sdk.NewCoin(sdk.IrisAtto, amt)}) - reservePool, found = keeper.GetReservePool(ctx, reservePoolName) - reservePool, found = keeper.GetReservePool(ctx, reservePoolName) - require.True(t, found) - require.Equal(t, amt, reservePool.AmountOf(sdk.IrisAtto)) -} - //func TestKeeper_UpdateLiquidity(t *testing.T) { // ctx, keeper, accs := createTestInput(t, sdk.NewInt(1000), 1) // @@ -81,7 +42,7 @@ func TestGetReservePool(t *testing.T) { // sdk.NewInt(10), sdk.NewInt(10), ctx.BlockHeader().Time, // accs[0].GetAddress()) // -// require.Nil(t, keeper.AddLiquidity(ctx, msgAdd)) +// require.Nil(t, keeper.HandleAddLiquidity(ctx, msgAdd)) // // poolAccout := keeper.ak.GetAccount(ctx, poolAddr) // acc := keeper.ak.GetAccount(ctx, accs[0].GetAddress()) @@ -91,7 +52,7 @@ func TestGetReservePool(t *testing.T) { // msgAdd1 := types.NewMsgAddLiquidity(sdk.Coin{Denom: "btc", Amount: sdk.NewInt(1)}, // sdk.NewInt(3), sdk.NewInt(3), ctx.BlockHeader().Time, // accs[0].GetAddress()) -// require.Nil(t, keeper.AddLiquidity(ctx, msgAdd1)) +// require.Nil(t, keeper.HandleAddLiquidity(ctx, msgAdd1)) // // poolAccout = keeper.ak.GetAccount(ctx, poolAddr) // acc = keeper.ak.GetAccount(ctx, accs[0].GetAddress()) @@ -101,7 +62,7 @@ func TestGetReservePool(t *testing.T) { // require.Equal(t, "100btc,10iris-atto,10swap:btc:iris-atto", poolAccout.GetCoins().String()) // require.Equal(t, "900btc,990iris-atto,10swap:btc:iris-atto", acc.GetCoins().String()) // -// require.Nil(t, keeper.AddLiquidity(ctx, msgAdd)) +// require.Nil(t, keeper.HandleAddLiquidity(ctx, msgAdd)) // // poolAccout = keeper.ak.GetAccount(ctx, poolAddr) // acc = keeper.ak.GetAccount(ctx, accs[0].GetAddress()) @@ -111,7 +72,7 @@ func TestGetReservePool(t *testing.T) { // msgRemove := types.NewMsgRemoveLiquidity(sdk.Coin{Denom: "btc", Amount: sdk.NewInt(1)}, // sdk.NewInt(3), sdk.NewInt(3), ctx.BlockHeader().Time, // accs[0].GetAddress()) -// require.Nil(t, keeper.RemoveLiquidity(ctx, msgRemove)) +// require.Nil(t, keeper.HandleRemoveLiquidity(ctx, msgRemove)) // // poolAccout = keeper.ak.GetAccount(ctx, poolAddr) // acc = keeper.ak.GetAccount(ctx, accs[0].GetAddress()) diff --git a/app/v2/coinswap/internal/keeper/querier.go b/app/v2/coinswap/internal/keeper/querier.go index 7a5e7725b..703cc95ce 100644 --- a/app/v2/coinswap/internal/keeper/querier.go +++ b/app/v2/coinswap/internal/keeper/querier.go @@ -37,8 +37,8 @@ func queryLiquidity(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, s if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not retrieve module name", err.Error())) } - reservePool, found := k.GetReservePool(ctx, reservePoolName) - if !found { + reservePool := k.GetReservePool(ctx, reservePoolName) + if reservePool == nil { return nil, sdk.ErrInternal("reserve pool does not exist") } @@ -59,12 +59,6 @@ func queryParameters(ctx sdk.Context, path []string, req abci.RequestQuery, k Ke return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil - case types.ParamNativeDenom: - bz, err := k.cdc.MarshalJSONIndent(sdk.IrisAtto, "", " ") - if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) - } - return bz, nil default: return nil, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path)) } diff --git a/app/v2/coinswap/internal/keeper/querier_test.go b/app/v2/coinswap/internal/keeper/querier_test.go index 04789f814..bd638ea30 100644 --- a/app/v2/coinswap/internal/keeper/querier_test.go +++ b/app/v2/coinswap/internal/keeper/querier_test.go @@ -41,13 +41,6 @@ func TestNewQuerier(t *testing.T) { keeper.cdc.UnmarshalJSON(res, &fee) require.Nil(t, err) require.Equal(t, fee, types.DefaultParams().Fee) - - // query for native denom param - var nativeDenom string - req.Path = fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, types.QueryParameters, types.ParamNativeDenom) - res, err = querier(ctx, []string{types.QueryParameters, types.ParamNativeDenom}, req) - keeper.cdc.UnmarshalJSON(res, &nativeDenom) - require.Nil(t, err) } // TODO: Add tests for valid liquidity queries diff --git a/app/v2/coinswap/internal/keeper/swap.go b/app/v2/coinswap/internal/keeper/swap.go index 4f92e5320..923bfe3c1 100644 --- a/app/v2/coinswap/internal/keeper/swap.go +++ b/app/v2/coinswap/internal/keeper/swap.go @@ -6,19 +6,16 @@ import ( sdk "github.com/irisnet/irishub/types" ) -type HandlerFun func(ctx sdk.Context, input, output sdk.Coin, sender, receipt sdk.AccAddress) (sdk.Int, sdk.Error) +const PrefixReservePool = "u-%s" func (k Keeper) SwapCoins(ctx sdk.Context, sender, recipient sdk.AccAddress, coinSold, coinBought sdk.Coin) sdk.Error { - if !k.bk.HasCoins(ctx, sender, sdk.NewCoins(coinSold)) { - return sdk.ErrInsufficientCoins(fmt.Sprintf("sender account does not have sufficient amount of %s to fulfill the swap order", coinSold.Denom)) - } - reservePoolName, err := k.GetReservePoolName(coinSold.Denom, coinBought.Denom) if err != nil { return err } - err = k.SendCoins(ctx, sender, reservePoolName, sdk.NewCoins(coinSold)) + poolAddr := getReservePoolAddr(reservePoolName) + _, err = k.bk.SendCoins(ctx, sender, poolAddr, sdk.NewCoins(coinSold)) if err != nil { return err } @@ -26,44 +23,23 @@ func (k Keeper) SwapCoins(ctx sdk.Context, sender, recipient sdk.AccAddress, coi if recipient.Empty() { recipient = sender } - err = k.ReceiveCoins(ctx, recipient, reservePoolName, sdk.NewCoins(coinBought)) + _, err = k.bk.SendCoins(ctx, poolAddr, recipient, sdk.NewCoins(coinBought)) return err } -func (k Keeper) GetHandler(msg types.MsgSwapOrder) HandlerFun { - var handlerMap = map[bool]map[bool]HandlerFun{ - // BuyOrder - true: map[bool]HandlerFun{ - // Double swap - true: k.SwapDoubleByOutput, - // Single swap - false: k.SwapByOutput, - }, - // SellOrder - false: map[bool]HandlerFun{ - // Double swap - true: k.SwapDoubleByInput, - // Single swap - false: k.SwapByInput, - }, - } - hMap := handlerMap[msg.IsBuyOrder] - return hMap[k.IsDoubleSwap(msg.Input.Denom, msg.Output.Denom)] -} - /** Calculate the amount of another token to be received based on the exact amount of tokens sold @param exactSoldCoin : sold coin @param soldTokenDenom : received token's denom @return : token amount that will to be received */ -func (k Keeper) GetPriceByInput(ctx sdk.Context, exactSoldCoin sdk.Coin, boughtTokenDenom string) (sdk.Int, sdk.Error) { +func (k Keeper) calculateWithExactInput(ctx sdk.Context, exactSoldCoin sdk.Coin, boughtTokenDenom string) (sdk.Int, sdk.Error) { reservePoolName, err := k.GetReservePoolName(exactSoldCoin.Denom, boughtTokenDenom) if err != nil { return sdk.ZeroInt(), err } - reservePool, found := k.GetReservePool(ctx, reservePoolName) - if !found { + reservePool := k.GetReservePool(ctx, reservePoolName) + if reservePool == nil { return sdk.ZeroInt(), types.ErrReservePoolNotExists(fmt.Sprintf("reserve pool for %s not found", reservePoolName)) } inputReserve := reservePool.AmountOf(exactSoldCoin.Denom) @@ -77,30 +53,30 @@ func (k Keeper) GetPriceByInput(ctx sdk.Context, exactSoldCoin sdk.Coin, boughtT } param := k.GetParams(ctx) - boughtTokenAmt := GetInputPrice(exactSoldCoin.Amount, inputReserve, outputReserve, param.Fee) + boughtTokenAmt := getInputPrice(exactSoldCoin.Amount, inputReserve, outputReserve, param.Fee) return boughtTokenAmt, nil } /** sell a exact amount of another token with a token,one of token denom is iris -@param exactSoldCoin : sold Token -@param minExpect : another token received,user specified minimum amount +@param input : sold MaxToken +@param output : another token received,user specified minimum amount @param sender : address of transaction sender -@param receipt : address of receiver bought Token +@param receipt : address of receiver bought MaxToken @return : token amount received */ -func (k Keeper) SwapByInput(ctx sdk.Context, exactSoldCoin, minExpect sdk.Coin, sender, receipt sdk.AccAddress) (sdk.Int, sdk.Error) { - boughtTokenAmt, err := k.GetPriceByInput(ctx, exactSoldCoin, minExpect.Denom) +func (k Keeper) tradeExactInputForOutput(ctx sdk.Context, input types.Input, output types.Output) (sdk.Int, sdk.Error) { + boughtTokenAmt, err := k.calculateWithExactInput(ctx, input.Coin, output.Coin.Denom) if err != nil { return sdk.ZeroInt(), err } // assert that the calculated amount is less than the // minimum amount the buyer is willing to buy. - if boughtTokenAmt.LT(minExpect.Amount) { - return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", boughtTokenAmt, minExpect.Amount)) + if boughtTokenAmt.LT(output.Coin.Amount) { + return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", boughtTokenAmt, output.Coin.Amount)) } - boughtToken := sdk.NewCoin(minExpect.Denom, boughtTokenAmt) - err = k.SwapCoins(ctx, sender, receipt, exactSoldCoin, boughtToken) + boughtToken := sdk.NewCoin(output.Coin.Denom, boughtTokenAmt) + err = k.SwapCoins(ctx, input.Address, output.Address, input.Coin, boughtToken) if err != nil { return sdk.ZeroInt(), err } @@ -109,35 +85,35 @@ func (k Keeper) SwapByInput(ctx sdk.Context, exactSoldCoin, minExpect sdk.Coin, /** sell a exact amount of another non-iris token with a non-iris token -@param exactSoldCoin : sold Token -@param minExpect : another token received,user specified minimum amount +@param input : sold MaxToken +@param output : another token received,user specified minimum amount @param sender : address of transaction sender -@param receipt : address of receiver bought Token +@param receipt : address of receiver bought MaxToken @return : token amount received */ -func (k Keeper) SwapDoubleByInput(ctx sdk.Context, exactSoldCoin, minExpect sdk.Coin, sender, receipt sdk.AccAddress) (sdk.Int, sdk.Error) { - nativeAmount, err := k.GetPriceByInput(ctx, exactSoldCoin, sdk.IrisAtto) +func (k Keeper) doubleTradeExactInputForOutput(ctx sdk.Context, input types.Input, output types.Output) (sdk.Int, sdk.Error) { + nativeAmount, err := k.calculateWithExactInput(ctx, input.Coin, sdk.IrisAtto) if err != nil { return sdk.ZeroInt(), err } nativeCoin := sdk.NewCoin(sdk.IrisAtto, nativeAmount) - err = k.SwapCoins(ctx, sender, receipt, exactSoldCoin, nativeCoin) + err = k.SwapCoins(ctx, input.Address, output.Address, input.Coin, nativeCoin) if err != nil { return sdk.ZeroInt(), err } - boughtAmt, err := k.GetPriceByInput(ctx, nativeCoin, minExpect.Denom) + boughtAmt, err := k.calculateWithExactInput(ctx, nativeCoin, output.Coin.Denom) if err != nil { return sdk.ZeroInt(), err } - boughtToken := sdk.NewCoin(minExpect.Denom, boughtAmt) + boughtToken := sdk.NewCoin(output.Coin.Denom, boughtAmt) // assert that the calculated amount is less than the // minimum amount the buyer is willing to buy. - if boughtAmt.LT(minExpect.Amount) { - return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", boughtAmt, minExpect.Amount)) + if boughtAmt.LT(output.Coin.Amount) { + return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", boughtAmt, output.Coin.Amount)) } - err = k.SwapCoins(ctx, sender, receipt, nativeCoin, boughtToken) + err = k.SwapCoins(ctx, input.Address, output.Address, nativeCoin, boughtToken) if err != nil { return sdk.ZeroInt(), err } @@ -150,14 +126,14 @@ Calculate the amount of another token to be spent based on the exact amount of t @param soldTokenDenom : sold token @return : token amount that needs to be sold */ -func (k Keeper) GetPriceByOutput(ctx sdk.Context, exactBoughtCoin sdk.Coin, soldTokenDenom string) (sdk.Int, sdk.Error) { +func (k Keeper) calculateWithExactOutput(ctx sdk.Context, exactBoughtCoin sdk.Coin, soldTokenDenom string) (sdk.Int, sdk.Error) { reservePoolName, err := k.GetReservePoolName(exactBoughtCoin.Denom, soldTokenDenom) if err != nil { - panic(err) + return sdk.ZeroInt(), types.ErrReservePoolNotExists(fmt.Sprintf("reserve pool for %s not found", reservePoolName)) } - reservePool, found := k.GetReservePool(ctx, reservePoolName) - if !found { - panic(fmt.Sprintf("reserve pool for %s not found", reservePoolName)) + reservePool := k.GetReservePool(ctx, reservePoolName) + if reservePool == nil { + return sdk.ZeroInt(), types.ErrReservePoolNotExists(fmt.Sprintf("reserve pool for %s not found", reservePoolName)) } inputReserve := reservePool.AmountOf(exactBoughtCoin.Denom) outputReserve := reservePool.AmountOf(soldTokenDenom) @@ -173,30 +149,30 @@ func (k Keeper) GetPriceByOutput(ctx sdk.Context, exactBoughtCoin sdk.Coin, sold } param := k.GetParams(ctx) - soldTokenAmt := GetOutputPrice(exactBoughtCoin.Amount, inputReserve, outputReserve, param.Fee) + soldTokenAmt := getOutputPrice(exactBoughtCoin.Amount, inputReserve, outputReserve, param.Fee) return soldTokenAmt, nil } /** Purchase a exact amount of another token with a token,one of token denom is iris -@param exactBoughtCoin : bought Token -@param maxExpect : another token that needs to be spent,user specified maximum +@param input : bought MaxToken +@param output : another token that needs to be spent,user specified maximum @param sender : address of transaction sender -@param receipt : address of receiver bought Token +@param receipt : address of receiver bought MaxToken @return : token amount that needs to be spent */ -func (k Keeper) SwapByOutput(ctx sdk.Context, maxExpect, exactBoughtCoin sdk.Coin, sender, receipt sdk.AccAddress) (sdk.Int, sdk.Error) { - soldTokenAmt, err := k.GetPriceByOutput(ctx, exactBoughtCoin, maxExpect.Denom) +func (k Keeper) tradeInputForExactOutput(ctx sdk.Context, input types.Input, output types.Output) (sdk.Int, sdk.Error) { + soldTokenAmt, err := k.calculateWithExactOutput(ctx, output.Coin, input.Coin.Denom) if err != nil { return sdk.ZeroInt(), err } // assert that the calculated amount is less than the // minimum amount the buyer is willing to buy. - if soldTokenAmt.GT(maxExpect.Amount) { - return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", soldTokenAmt, maxExpect.Amount)) + if soldTokenAmt.GT(input.Coin.Amount) { + return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", soldTokenAmt, input.Coin.Amount)) } - soldToken := sdk.NewCoin(maxExpect.Denom, soldTokenAmt) - err = k.SwapCoins(ctx, sender, receipt, soldToken, exactBoughtCoin) + soldToken := sdk.NewCoin(input.Coin.Denom, soldTokenAmt) + err = k.SwapCoins(ctx, input.Address, output.Address, soldToken, output.Coin) if err != nil { return sdk.ZeroInt(), err } @@ -205,49 +181,44 @@ func (k Keeper) SwapByOutput(ctx sdk.Context, maxExpect, exactBoughtCoin sdk.Coi /** Purchase a exact amount of another non-iris token with a non-iris token -@param exactBoughtCoin : bought Token -@param maxExpect : another token that needs to be spent,user specified maximum +@param input : bought MaxToken +@param output : another token that needs to be spent,user specified maximum @param sender : address of transaction sender -@param receipt : address of receiver bought Token +@param receipt : address of receiver bought MaxToken @return : token amount that needs to be spent */ -func (k Keeper) SwapDoubleByOutput(ctx sdk.Context, maxExpect, exactBoughtCoin sdk.Coin, sender, receipt sdk.AccAddress) (sdk.Int, sdk.Error) { - soldIrisAmount, err := k.GetPriceByOutput(ctx, exactBoughtCoin, sdk.IrisAtto) +func (k Keeper) doubleTradeInputForExactOutput(ctx sdk.Context, input types.Input, output types.Output) (sdk.Int, sdk.Error) { + soldIrisAmount, err := k.calculateWithExactOutput(ctx, output.Coin, sdk.IrisAtto) if err != nil { return sdk.ZeroInt(), err } soldIrisCoin := sdk.NewCoin(sdk.IrisAtto, soldIrisAmount) - soldTokenAmt, err := k.GetPriceByOutput(ctx, soldIrisCoin, maxExpect.Denom) + soldTokenAmt, err := k.calculateWithExactOutput(ctx, soldIrisCoin, input.Coin.Denom) if err != nil { return sdk.ZeroInt(), err } - soldTokenCoin := sdk.NewCoin(maxExpect.Denom, soldTokenAmt) + soldTokenCoin := sdk.NewCoin(input.Coin.Denom, soldTokenAmt) // assert that the calculated amount is less than the // minimum amount the buyer is willing to buy. - if soldTokenAmt.GT(maxExpect.Amount) { - return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", soldTokenAmt, maxExpect.Amount)) + if soldTokenAmt.GT(input.Coin.Amount) { + return sdk.ZeroInt(), types.ErrConstraintNotMet(fmt.Sprintf("token amount (%s) to be bought was less than the minimum amount (%s)", soldTokenAmt, input.Coin.Amount)) } - err = k.SwapCoins(ctx, sender, receipt, soldTokenCoin, soldIrisCoin) + err = k.SwapCoins(ctx, input.Address, output.Address, soldTokenCoin, soldIrisCoin) if err != nil { return sdk.ZeroInt(), err } - err = k.SwapCoins(ctx, sender, receipt, soldIrisCoin, exactBoughtCoin) + err = k.SwapCoins(ctx, input.Address, output.Address, soldIrisCoin, output.Coin) if err != nil { return sdk.ZeroInt(), err } return soldTokenAmt, nil } -// IsDoubleSwap returns true if the trade requires a double swap. -func (k Keeper) IsDoubleSwap(denom1, denom2 string) bool { - return denom1 != sdk.IrisAtto && denom2 != sdk.IrisAtto -} - // GetReservePoolName returns the reserve pool name for the provided denominations. -// The reserve pool name is in the format of 's-denom' which the denomination +// The reserve pool name is in the format of 'u-denom' which the denomination // is not iris-atto. func (k Keeper) GetReservePoolName(denom1, denom2 string) (string, sdk.Error) { if denom1 == denom2 { @@ -262,13 +233,13 @@ func (k Keeper) GetReservePoolName(denom1, denom2 string) (string, sdk.Error) { if denom1 != sdk.IrisAtto { denom = denom1 } - return fmt.Sprintf("s-%s", denom), nil + return fmt.Sprintf(PrefixReservePool, denom), nil } -// GetInputPrice returns the amount of coins bought (calculated) given the input amount being sold (exact) +// getInputPrice returns the amount of coins bought (calculated) given the input amount being sold (exact) // The fee is included in the input coins being bought // https://github.com/runtimeverification/verified-smart-contracts/blob/uniswap/uniswap/x-y-k.pdf -func GetInputPrice(inputAmt, inputReserve, outputReserve sdk.Int, fee sdk.Rat) sdk.Int { +func getInputPrice(inputAmt, inputReserve, outputReserve sdk.Int, fee sdk.Rat) sdk.Int { deltaFee := sdk.OneRat().Sub(fee) inputAmtWithFee := inputAmt.Mul(deltaFee.Num()) numerator := inputAmtWithFee.Mul(outputReserve) @@ -276,9 +247,9 @@ func GetInputPrice(inputAmt, inputReserve, outputReserve sdk.Int, fee sdk.Rat) s return numerator.Div(denominator) } -// GetOutputPrice returns the amount of coins sold (calculated) given the output amount being bought (exact) +// getOutputPrice returns the amount of coins sold (calculated) given the output amount being bought (exact) // The fee is included in the output coins being bought -func GetOutputPrice(outputAmt, inputReserve, outputReserve sdk.Int, fee sdk.Rat) sdk.Int { +func getOutputPrice(outputAmt, inputReserve, outputReserve sdk.Int, fee sdk.Rat) sdk.Int { deltaFee := sdk.OneRat().Sub(fee) numerator := inputReserve.Mul(outputAmt).Mul(deltaFee.Denom()) denominator := (outputReserve.Sub(outputAmt)).Mul(deltaFee.Num()) diff --git a/app/v2/coinswap/internal/keeper/swap_test.go b/app/v2/coinswap/internal/keeper/swap_test.go index 7e9612aa9..2c72778a9 100644 --- a/app/v2/coinswap/internal/keeper/swap_test.go +++ b/app/v2/coinswap/internal/keeper/swap_test.go @@ -14,28 +14,6 @@ var ( native = sdk.IrisAtto ) -func TestIsDoubleSwap(t *testing.T) { - _, keeper, _ := createTestInput(t, sdk.NewInt(0), 0) - - cases := []struct { - name string - denom1 string - denom2 string - isDoubleSwap bool - }{ - {"denom1 is native", native, "btc", false}, - {"denom2 is native", "btc", native, false}, - {"neither denom is native", "eth", "btc", true}, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - doubleSwap := keeper.IsDoubleSwap(tc.denom1, tc.denom2) - require.Equal(t, tc.isDoubleSwap, doubleSwap) - }) - } -} - func TestGetReservePoolName(t *testing.T) { _, keeper, _ := createTestInput(t, sdk.NewInt(0), 0) @@ -46,10 +24,10 @@ func TestGetReservePoolName(t *testing.T) { expectResult string expectPass bool }{ - {"denom1 is native", native, "btc", "s-btc", true}, - {"denom2 is native", "btc", native, "s-btc", true}, - {"denom1 equals denom2", "btc", "btc", "s-btc", false}, - {"neither denom is native", "eth", "btc", "s-btc", false}, + {"denom1 is native", native, "btc", "u-btc", true}, + {"denom2 is native", "btc", native, "u-btc", true}, + {"denom1 equals denom2", "btc", "btc", "u-btc", false}, + {"neither denom is native", "eth", "btc", "u-btc", false}, } for _, tc := range cases { @@ -100,7 +78,7 @@ func TestGetInputPrice(t *testing.T) { } for _, tcase := range datas { data := tcase.data - actual := GetInputPrice(data.delta, data.x, data.y, data.fee) + actual := getInputPrice(data.delta, data.x, data.y, data.fee) fmt.Println(fmt.Sprintf("expect:%s,actual:%s", tcase.expect.String(), actual.String())) require.Equal(t, tcase.expect, actual) } @@ -127,7 +105,7 @@ func TestGetOutputPrice(t *testing.T) { } for _, tcase := range datas { data := tcase.data - actual := GetOutputPrice(data.delta, data.x, data.y, data.fee) + actual := getOutputPrice(data.delta, data.x, data.y, data.fee) fmt.Println(fmt.Sprintf("expect:%s,actual:%s", tcase.expect.String(), actual.String())) require.Equal(t, tcase.expect, actual) } @@ -146,24 +124,34 @@ func TestKeeperSwap(t *testing.T) { minReward := sdk.NewInt(1) deadline := time.Now().Add(1 * time.Minute) msg := types.NewMsgAddLiquidity(depositCoin, depositAmount, minReward, deadline, sender) - err := keeper.AddLiquidity(ctx, msg) + err := keeper.HandleAddLiquidity(ctx, msg) //assert require.Nil(t, err) - reservePoolBalances := keeper.bk.GetCoins(ctx, reservePoolAddr) - require.Equal(t, "1000btc-min,1000iris-atto,1000s-btc-min", reservePoolBalances.String()) - senderBlances := keeper.bk.GetCoins(ctx, sender) - require.Equal(t, "99999000btc-min,99999000iris-atto,1000s-btc-min", senderBlances.String()) + reservePoolBalances := keeper.ak.GetAccount(ctx, reservePoolAddr).GetCoins() + require.Equal(t, "1000btc-min,1000iris-atto,1000u-btc-min", reservePoolBalances.String()) + senderBlances := keeper.ak.GetAccount(ctx, sender).GetCoins() + require.Equal(t, "99999000btc-min,99999000iris-atto,1000u-btc-min", senderBlances.String()) + + inputCoin := sdk.NewCoin("btc-min", sdk.NewInt(100)) + outputCoin := sdk.NewCoin(sdk.IrisAtto, sdk.NewInt(1)) + + input := types.Input{ + Address: sender, + Coin: inputCoin, + } + + output := types.Output{ + Coin: outputCoin, + } - input := sdk.NewCoin("btc-min", sdk.NewInt(100)) - output := sdk.NewCoin(sdk.IrisAtto, sdk.NewInt(1)) deadline1 := time.Now().Add(1 * time.Minute) - msg1 := types.NewMsgSwapOrder(input, output, deadline1, sender, nil, false) - _, err = keeper.Swap(ctx, msg1) + msg1 := types.NewMsgSwapOrder(input, output, deadline1, false) + _, err = keeper.HandleSwap(ctx, msg1) require.Nil(t, err) - reservePoolBalances = keeper.bk.GetCoins(ctx, reservePoolAddr) - require.Equal(t, "1100btc-min,910iris-atto,1000s-btc-min", reservePoolBalances.String()) - senderBlances = keeper.bk.GetCoins(ctx, sender) - require.Equal(t, "99998900btc-min,99999090iris-atto,1000s-btc-min", senderBlances.String()) + reservePoolBalances = keeper.ak.GetAccount(ctx, reservePoolAddr).GetCoins() + require.Equal(t, "1100btc-min,910iris-atto,1000u-btc-min", reservePoolBalances.String()) + senderBlances = keeper.ak.GetAccount(ctx, sender).GetCoins() + require.Equal(t, "99998900btc-min,99999090iris-atto,1000u-btc-min", senderBlances.String()) } diff --git a/app/v2/coinswap/internal/types/expected_keepers.go b/app/v2/coinswap/internal/types/expected_keepers.go index ab1254f4c..e73891a1f 100644 --- a/app/v2/coinswap/internal/types/expected_keepers.go +++ b/app/v2/coinswap/internal/types/expected_keepers.go @@ -1 +1,18 @@ package types + +import ( + "github.com/irisnet/irishub/app/v1/auth" + sdk "github.com/irisnet/irishub/types" +) + +type BankKeeper interface { + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) + + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) + + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) +} + +type AuthKeeper interface { + GetAccount(ctx sdk.Context, addr sdk.AccAddress) auth.Account +} diff --git a/app/v2/coinswap/internal/types/msgs.go b/app/v2/coinswap/internal/types/msgs.go index 05e8a1fc2..a8a4126d5 100644 --- a/app/v2/coinswap/internal/types/msgs.go +++ b/app/v2/coinswap/internal/types/msgs.go @@ -21,27 +21,32 @@ var ( // An exact coin has the senders desired buy or sell amount. // A calculated coin has the desired denomination and bounded amount // the sender is willing to buy or sell in this order. +type Input struct { + Address sdk.AccAddress `json:"address"` + Coin sdk.Coin `json:"coin"` +} + +type Output struct { + Address sdk.AccAddress `json:"address"` + Coin sdk.Coin `json:"coin"` +} + type MsgSwapOrder struct { - Input sdk.Coin `json:"input"` // the amount the sender is trading - Output sdk.Coin `json:"output"` // the amount the sender is recieivng - Deadline time.Time `json:"deadline"` // deadline for the transaction to still be considered valid - Sender sdk.AccAddress `json:"sender"` - Recipient sdk.AccAddress `json:"recipient"` - IsBuyOrder bool `json:"is_buy_order"` // boolean indicating whether the order should be treated as a buy or sell + Input Input `json:"input"` // the amount the sender is trading + Output Output `json:"output"` // the amount the sender is recieivng + Deadline time.Time `json:"deadline"` // deadline for the transaction to still be considered valid + IsBuyOrder bool `json:"is_buy_order"` // boolean indicating whether the order should be treated as a buy or sell } // NewMsgSwapOrder creates a new MsgSwapOrder object. func NewMsgSwapOrder( - input, output sdk.Coin, deadline time.Time, - sender, recipient sdk.AccAddress, isBuyOrder bool, + input Input, output Output, deadline time.Time, isBuyOrder bool, ) MsgSwapOrder { return MsgSwapOrder{ Input: input, Output: output, Deadline: deadline, - Sender: sender, - Recipient: recipient, IsBuyOrder: isBuyOrder, } } @@ -54,30 +59,25 @@ func (msg MsgSwapOrder) Type() string { return "swap_order" } // ValidateBasic Implements Msg. func (msg MsgSwapOrder) ValidateBasic() sdk.Error { - if !msg.Input.IsPositive() { - return sdk.ErrInvalidCoins("input coin is invalid: " + msg.Input.String()) + if !(msg.Input.Coin.IsValid() && msg.Input.Coin.IsPositive()) { + return sdk.ErrInvalidCoins("input coin is invalid: " + msg.Input.Coin.String()) } - if msg.Input.IsZero() { - return sdk.ErrInvalidCoins("input coin is zero: " + msg.Input.String()) - } - if !msg.Output.IsPositive() { - return sdk.ErrInvalidCoins("output coin is invalid: " + msg.Output.String()) - } - if msg.Output.IsZero() { - return sdk.ErrInvalidCoins("output coin is zero: " + msg.Output.String()) + + if !(msg.Output.Coin.IsValid() && msg.Output.Coin.IsPositive()) { + return sdk.ErrInvalidCoins("output coin is invalid: " + msg.Output.Coin.String()) } - if msg.Input.Denom == msg.Output.Denom { + + if msg.Input.Coin.Denom == msg.Output.Coin.Denom { return ErrEqualDenom("") } + if msg.Deadline.IsZero() { return ErrInvalidDeadline("deadline for MsgSwapOrder not initialized") } - if msg.Sender.Empty() { + + if msg.Input.Address.Empty() { return sdk.ErrInvalidAddress("invalid sender address") } - if msg.Recipient.Empty() { - return sdk.ErrInvalidAddress("invalid recipient address") - } return nil } @@ -88,7 +88,7 @@ func (msg MsgSwapOrder) GetSignBytes() []byte { // GetSigners Implements Msg. func (msg MsgSwapOrder) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Sender} + return []sdk.AccAddress{msg.Input.Address} } /* --------------------------------------------------------------------------- */ @@ -97,25 +97,25 @@ func (msg MsgSwapOrder) GetSigners() []sdk.AccAddress { // MsgAddLiquidity - struct for adding liquidity to a reserve pool type MsgAddLiquidity struct { - Deposit sdk.Coin `json:"deposit"` // coin to be deposited as liquidity with an upper bound for its amount - DepositAmount sdk.Int `json:"deposit_amount"` // exact amount of native asset being add to the liquidity pool - MinReward sdk.Int `json:"min_reward"` // lower bound UNI sender is willing to accept for deposited coins - Deadline time.Time `json:"deadline"` - Sender sdk.AccAddress `json:"sender"` + MaxToken sdk.Coin `json:"max_token"` // coin to be deposited as liquidity with an upper bound for its amount + ExactIrisAmt sdk.Int `json:"exact_iris_amt"` // exact amount of native asset being add to the liquidity pool + MinLiquidity sdk.Int `json:"min_liquidity"` // lower bound UNI sender is willing to accept for deposited coins + Deadline time.Time `json:"deadline"` + Sender sdk.AccAddress `json:"sender"` } // NewMsgAddLiquidity creates a new MsgAddLiquidity object. func NewMsgAddLiquidity( - deposit sdk.Coin, depositAmount, minReward sdk.Int, + maxToken sdk.Coin, exactIrisAmt, minLiquidity sdk.Int, deadline time.Time, sender sdk.AccAddress, ) MsgAddLiquidity { return MsgAddLiquidity{ - Deposit: deposit, - DepositAmount: depositAmount, - MinReward: minReward, - Deadline: deadline, - Sender: sender, + MaxToken: maxToken, + ExactIrisAmt: exactIrisAmt, + MinLiquidity: minLiquidity, + Deadline: deadline, + Sender: sender, } } @@ -127,17 +127,17 @@ func (msg MsgAddLiquidity) Type() string { return "add_liquidity" } // ValidateBasic Implements Msg. func (msg MsgAddLiquidity) ValidateBasic() sdk.Error { - if !msg.Deposit.IsPositive() { - return sdk.ErrInvalidCoins("coin is invalid: " + msg.Deposit.String()) + if !(msg.MaxToken.IsValid() && msg.MaxToken.IsPositive()) { + return sdk.ErrInvalidCoins("maxToken is invalid: " + msg.MaxToken.String()) } - if msg.Deposit.IsZero() { - return sdk.ErrInvalidCoins("coin is zero: " + msg.Deposit.String()) + if msg.MaxToken.Denom == sdk.IrisAtto { + return sdk.ErrInvalidCoins("max token should be non-iris token: " + msg.MaxToken.String()) } - if !msg.DepositAmount.IsPositive() { + if msg.ExactIrisAmt.IsNil() || !msg.ExactIrisAmt.IsPositive() { return ErrNotPositive("deposit amount provided is not positive") } - if !msg.MinReward.IsPositive() { - return ErrNotPositive("minimum liquidity is not positive") + if msg.ExactIrisAmt.IsNil() || msg.MinLiquidity.IsNegative() { + return ErrNotPositive("minimum liquidity is negative") } if msg.Deadline.IsZero() { return ErrInvalidDeadline("deadline for MsgAddLiquidity not initialized") @@ -164,25 +164,25 @@ func (msg MsgAddLiquidity) GetSigners() []sdk.AccAddress { // MsgRemoveLiquidity - struct for removing liquidity from a reserve pool type MsgRemoveLiquidity struct { - Withdraw sdk.Coin `json:"withdraw"` // coin to be withdrawn with a lower bound for its amount - WithdrawAmount sdk.Int `json:"withdraw_amount"` // amount of UNI to be burned to withdraw liquidity from a reserve pool - MinNative sdk.Int `json:"min_native"` // minimum amount of the native asset the sender is willing to accept - Deadline time.Time `json:"deadline"` - Sender sdk.AccAddress `json:"sender"` + MinToken sdk.Coin `json:"min_token"` // coin to be withdrawn with a lower bound for its amount + WithdrawLiquidity sdk.Int `json:"withdraw_liquidity"` // amount of UNI to be burned to withdraw liquidity from a reserve pool + MinIrisAmt sdk.Int `json:"min_iris_amt"` // minimum amount of the native asset the sender is willing to accept + Deadline time.Time `json:"deadline"` + Sender sdk.AccAddress `json:"sender"` } // NewMsgRemoveLiquidity creates a new MsgRemoveLiquidity object func NewMsgRemoveLiquidity( - withdraw sdk.Coin, withdrawAmount, minNative sdk.Int, + minToken sdk.Coin, withdrawLiquidity, minIrisAmt sdk.Int, deadline time.Time, sender sdk.AccAddress, ) MsgRemoveLiquidity { return MsgRemoveLiquidity{ - Withdraw: withdraw, - WithdrawAmount: withdrawAmount, - MinNative: minNative, - Deadline: deadline, - Sender: sender, + MinToken: minToken, + WithdrawLiquidity: withdrawLiquidity, + MinIrisAmt: minIrisAmt, + Deadline: deadline, + Sender: sender, } } @@ -194,17 +194,17 @@ func (msg MsgRemoveLiquidity) Type() string { return "remove_liquidity" } // ValidateBasic Implements Msg. func (msg MsgRemoveLiquidity) ValidateBasic() sdk.Error { - if !msg.WithdrawAmount.IsPositive() { - return ErrNotPositive("withdraw amount is not positive") + if !(msg.MinToken.IsValid() && msg.MinToken.IsPositive()) { + return sdk.ErrInvalidCoins("minToken is invalid: " + msg.MinToken.String()) } - if !msg.Withdraw.IsPositive() { - return sdk.ErrInvalidCoins("coin is invalid: " + msg.Withdraw.String()) + if msg.MinToken.Denom == sdk.IrisAtto { + return sdk.ErrInvalidCoins("min token should be non-iris token: " + msg.MinToken.String()) } - if msg.Withdraw.IsZero() { - return sdk.ErrInvalidCoins("coin is zero: " + msg.Withdraw.String()) + if msg.WithdrawLiquidity.IsNil() || !msg.WithdrawLiquidity.IsPositive() { + return ErrNotPositive("withdraw liquidity is not positive") } - if !msg.MinNative.IsPositive() { - return ErrNotPositive("minimum native amount is not positive") + if msg.MinIrisAmt.IsNil() || !msg.MinIrisAmt.IsPositive() { + return ErrNotPositive("minimum iris amount is not positive") } if msg.Deadline.IsZero() { return ErrInvalidDeadline("deadline for MsgRemoveLiquidity not initialized") diff --git a/app/v2/coinswap/internal/types/querier.go b/app/v2/coinswap/internal/types/querier.go index 100aea539..85f5e949c 100644 --- a/app/v2/coinswap/internal/types/querier.go +++ b/app/v2/coinswap/internal/types/querier.go @@ -7,6 +7,4 @@ const ( QueryParameters = "parameters" // ParamFee fee query endpoint supported by the coinswap querier ParamFee = "fee" - // ParamNativeDenom native denom query endpoint supported by the coinswap querier - ParamNativeDenom = "nativeDenom" ) diff --git a/client/coinswap/lcd/rest.go b/client/coinswap/lcd/rest.go new file mode 100644 index 000000000..8cd3d0c8a --- /dev/null +++ b/client/coinswap/lcd/rest.go @@ -0,0 +1,12 @@ +package lcd + +import ( + "github.com/gorilla/mux" + "github.com/irisnet/irishub/client/context" + "github.com/irisnet/irishub/codec" +) + +// RegisterRoutes registers asset-related REST handlers to a router +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { + registerTxRoutes(cliCtx, r, cdc) +} diff --git a/client/coinswap/lcd/sendtx.go b/client/coinswap/lcd/sendtx.go new file mode 100644 index 000000000..bd2e05e34 --- /dev/null +++ b/client/coinswap/lcd/sendtx.go @@ -0,0 +1,325 @@ +package lcd + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/irisnet/irishub/app/v1/asset" + "github.com/irisnet/irishub/client/context" + "github.com/irisnet/irishub/client/utils" + "github.com/irisnet/irishub/codec" + sdk "github.com/irisnet/irishub/types" +) + +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { + // create a gateway + r.HandleFunc( + "/asset/gateways", + createGatewayHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // edit a gateway + r.HandleFunc( + "/asset/gateways/{moniker}", + editGatewayHandlerFn(cdc, cliCtx), + ).Methods("PUT") + + // transfer the ownership of a gateway + r.HandleFunc( + "/asset/gateways/{moniker}/transfer", + transferGatewayOwnerHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // issue a token + r.HandleFunc( + "/asset/tokens", + issueTokenHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // edit a token + r.HandleFunc( + "/asset/tokens/{token-id}", + editTokenHandlerFn(cdc, cliCtx), + ).Methods("PUT") + + // transfer owner + r.HandleFunc( + "/asset/tokens/{token-id}/transfer-owner", + transferOwnerHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // mint token + r.HandleFunc( + "/asset/tokens/{token-id}/mint", + mintTokenHandlerFn(cdc, cliCtx), + ).Methods("POST") +} + +type issueTokenReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // Owner of the token + Family asset.AssetFamily `json:"family"` + Source asset.AssetSource `json:"source"` + Gateway string `json:"gateway"` + Symbol string `json:"symbol"` + CanonicalSymbol string `json:"canonical_symbol"` + Name string `json:"name"` + Decimal uint8 `json:"decimal"` + MinUnitAlias string `json:"min_unit_alias"` + InitialSupply uint64 `json:"initial_supply"` + MaxSupply uint64 `json:"max_supply"` + Mintable bool `json:"mintable"` +} + +type createGatewayReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // Owner of the gateway + Moniker string `json:"moniker"` // Name of the gateway + Identity string `json:"identity"` // Identity of the gateway + Details string `json:"details"` // Description of the gateway + Website string `json:"website"` // Website of the gateway +} + +type editGatewayReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // Owner of the gateway + Identity string `json:"identity"` // Identity of the gateway + Details string `json:"details"` // Description of the gateway + Website string `json:"website"` // Website of the gateway +} + +type transferGatewayOwnerReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // Current Owner of the gateway + To sdk.AccAddress `json:"to"` // New owner of the gateway +} + +type editTokenReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // owner of asset + CanonicalSymbol string `json:"canonical_symbol"` // canonical_symbol of asset + MinUnitAlias string `json:"min_unit_alias"` // min_unit_alias of asset + MaxSupply uint64 `json:"max_supply"` + Mintable string `json:"mintable"` // mintable of asset + Name string `json:"name"` +} + +type transferTokenOwnerReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + SrcOwner sdk.AccAddress `json:"src_owner"` // the current owner address of the token + DstOwner sdk.AccAddress `json:"dst_owner"` // the new owner +} + +type mintTokenReq struct { + BaseTx utils.BaseTx `json:"base_tx"` + Owner sdk.AccAddress `json:"owner"` // the current owner address of the token + To sdk.AccAddress `json:"to"` // address of mint token to + Amount uint64 `json:"amount"` // amount of mint token +} + +func createGatewayHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req createGatewayReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgCreateGateway message + msg := asset.NewMsgCreateGateway(req.Owner, req.Moniker, req.Identity, req.Details, req.Website) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func editGatewayHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + moniker := vars["moniker"] + + var req editGatewayReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgEditGateway message + msg := asset.NewMsgEditGateway(req.Owner, moniker, req.Identity, req.Details, req.Website) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func transferGatewayOwnerHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + moniker := vars["moniker"] + + var req transferGatewayOwnerReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgTransferGatewayOwner message + msg := asset.NewMsgTransferGatewayOwner(req.Owner, moniker, req.To) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func issueTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req issueTokenReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgEditGateway message + msg := asset.NewMsgIssueToken(req.Family, req.Source, req.Gateway, req.Symbol, req.CanonicalSymbol, req.Name, req.Decimal, req.MinUnitAlias, req.InitialSupply, req.MaxSupply, req.Mintable, req.Owner) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func editTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + tokenId := vars["token-id"] + + var req editTokenReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgEditToken message + mintable, err := asset.ParseBool(req.Mintable) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + msg := asset.NewMsgEditToken(req.Name, req.CanonicalSymbol, req.MinUnitAlias, tokenId, req.MaxSupply, mintable, req.Owner) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func transferOwnerHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + tokenId := vars["token-id"] + var req transferTokenOwnerReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgTransferTokenOwner message + msg := asset.NewMsgTransferTokenOwner(req.SrcOwner, req.DstOwner, tokenId) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} + +func mintTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + tokenId := vars["token-id"] + var req mintTokenReq + err := utils.ReadPostBody(w, r, cdc, &req) + if err != nil { + return + } + + baseReq := req.BaseTx.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + // create the MsgMintToken message + msg := asset.NewMsgMintToken(tokenId, req.Owner, req.To, req.Amount) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) + + utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) + } +} diff --git a/types/coin.go b/types/coin.go index dfa181e26..c8dee91a0 100644 --- a/types/coin.go +++ b/types/coin.go @@ -570,7 +570,7 @@ func (coins Coins) Sort() Coins { var ( // Denominations can be 3 ~ 21 characters long. - reDenom = `(s\-)?(([a-z][a-z0-9]{2,7}|x)\.)?([a-z][a-z0-9]{2,7})(-[a-z]{3,5})?` + reDenom = `(u\-)?(([a-z][a-z0-9]{2,7}|x)\.)?([a-z][a-z0-9]{2,7})(-[a-z]{3,5})?` reAmount = `[0-9]+(\.[0-9]+)?` reSpace = `[[:space:]]*` reDenomCompiled = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDenom)) diff --git a/types/int.go b/types/int.go index 07bf7ad97..87bbe5d27 100644 --- a/types/int.go +++ b/types/int.go @@ -410,6 +410,10 @@ func (i Int) IsPositive() bool { return i.i.Sign() == 1 } +func (i Int) IsNil() bool { + return i.i == nil +} + // Sign returns sign of Uint func (i Uint) Sign() int { return i.i.Sign() From f9d5646aa62a6b826f0f543a35cf945a790a9b8d Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Sat, 10 Aug 2019 13:40:22 +0800 Subject: [PATCH 2/8] remove code --- client/coinswap/lcd/rest.go | 12 -- client/coinswap/lcd/sendtx.go | 325 ---------------------------------- 2 files changed, 337 deletions(-) delete mode 100644 client/coinswap/lcd/rest.go delete mode 100644 client/coinswap/lcd/sendtx.go diff --git a/client/coinswap/lcd/rest.go b/client/coinswap/lcd/rest.go deleted file mode 100644 index 8cd3d0c8a..000000000 --- a/client/coinswap/lcd/rest.go +++ /dev/null @@ -1,12 +0,0 @@ -package lcd - -import ( - "github.com/gorilla/mux" - "github.com/irisnet/irishub/client/context" - "github.com/irisnet/irishub/codec" -) - -// RegisterRoutes registers asset-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - registerTxRoutes(cliCtx, r, cdc) -} diff --git a/client/coinswap/lcd/sendtx.go b/client/coinswap/lcd/sendtx.go deleted file mode 100644 index bd2e05e34..000000000 --- a/client/coinswap/lcd/sendtx.go +++ /dev/null @@ -1,325 +0,0 @@ -package lcd - -import ( - "net/http" - - "github.com/gorilla/mux" - "github.com/irisnet/irishub/app/v1/asset" - "github.com/irisnet/irishub/client/context" - "github.com/irisnet/irishub/client/utils" - "github.com/irisnet/irishub/codec" - sdk "github.com/irisnet/irishub/types" -) - -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - // create a gateway - r.HandleFunc( - "/asset/gateways", - createGatewayHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // edit a gateway - r.HandleFunc( - "/asset/gateways/{moniker}", - editGatewayHandlerFn(cdc, cliCtx), - ).Methods("PUT") - - // transfer the ownership of a gateway - r.HandleFunc( - "/asset/gateways/{moniker}/transfer", - transferGatewayOwnerHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // issue a token - r.HandleFunc( - "/asset/tokens", - issueTokenHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // edit a token - r.HandleFunc( - "/asset/tokens/{token-id}", - editTokenHandlerFn(cdc, cliCtx), - ).Methods("PUT") - - // transfer owner - r.HandleFunc( - "/asset/tokens/{token-id}/transfer-owner", - transferOwnerHandlerFn(cdc, cliCtx), - ).Methods("POST") - - // mint token - r.HandleFunc( - "/asset/tokens/{token-id}/mint", - mintTokenHandlerFn(cdc, cliCtx), - ).Methods("POST") -} - -type issueTokenReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // Owner of the token - Family asset.AssetFamily `json:"family"` - Source asset.AssetSource `json:"source"` - Gateway string `json:"gateway"` - Symbol string `json:"symbol"` - CanonicalSymbol string `json:"canonical_symbol"` - Name string `json:"name"` - Decimal uint8 `json:"decimal"` - MinUnitAlias string `json:"min_unit_alias"` - InitialSupply uint64 `json:"initial_supply"` - MaxSupply uint64 `json:"max_supply"` - Mintable bool `json:"mintable"` -} - -type createGatewayReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // Owner of the gateway - Moniker string `json:"moniker"` // Name of the gateway - Identity string `json:"identity"` // Identity of the gateway - Details string `json:"details"` // Description of the gateway - Website string `json:"website"` // Website of the gateway -} - -type editGatewayReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // Owner of the gateway - Identity string `json:"identity"` // Identity of the gateway - Details string `json:"details"` // Description of the gateway - Website string `json:"website"` // Website of the gateway -} - -type transferGatewayOwnerReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // Current Owner of the gateway - To sdk.AccAddress `json:"to"` // New owner of the gateway -} - -type editTokenReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // owner of asset - CanonicalSymbol string `json:"canonical_symbol"` // canonical_symbol of asset - MinUnitAlias string `json:"min_unit_alias"` // min_unit_alias of asset - MaxSupply uint64 `json:"max_supply"` - Mintable string `json:"mintable"` // mintable of asset - Name string `json:"name"` -} - -type transferTokenOwnerReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - SrcOwner sdk.AccAddress `json:"src_owner"` // the current owner address of the token - DstOwner sdk.AccAddress `json:"dst_owner"` // the new owner -} - -type mintTokenReq struct { - BaseTx utils.BaseTx `json:"base_tx"` - Owner sdk.AccAddress `json:"owner"` // the current owner address of the token - To sdk.AccAddress `json:"to"` // address of mint token to - Amount uint64 `json:"amount"` // amount of mint token -} - -func createGatewayHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req createGatewayReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgCreateGateway message - msg := asset.NewMsgCreateGateway(req.Owner, req.Moniker, req.Identity, req.Details, req.Website) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func editGatewayHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - moniker := vars["moniker"] - - var req editGatewayReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgEditGateway message - msg := asset.NewMsgEditGateway(req.Owner, moniker, req.Identity, req.Details, req.Website) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func transferGatewayOwnerHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - moniker := vars["moniker"] - - var req transferGatewayOwnerReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgTransferGatewayOwner message - msg := asset.NewMsgTransferGatewayOwner(req.Owner, moniker, req.To) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func issueTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req issueTokenReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgEditGateway message - msg := asset.NewMsgIssueToken(req.Family, req.Source, req.Gateway, req.Symbol, req.CanonicalSymbol, req.Name, req.Decimal, req.MinUnitAlias, req.InitialSupply, req.MaxSupply, req.Mintable, req.Owner) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func editTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - tokenId := vars["token-id"] - - var req editTokenReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgEditToken message - mintable, err := asset.ParseBool(req.Mintable) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - msg := asset.NewMsgEditToken(req.Name, req.CanonicalSymbol, req.MinUnitAlias, tokenId, req.MaxSupply, mintable, req.Owner) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func transferOwnerHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - tokenId := vars["token-id"] - var req transferTokenOwnerReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgTransferTokenOwner message - msg := asset.NewMsgTransferTokenOwner(req.SrcOwner, req.DstOwner, tokenId) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} - -func mintTokenHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - tokenId := vars["token-id"] - var req mintTokenReq - err := utils.ReadPostBody(w, r, cdc, &req) - if err != nil { - return - } - - baseReq := req.BaseTx.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - // create the MsgMintToken message - msg := asset.NewMsgMintToken(tokenId, req.Owner, req.To, req.Amount) - err = msg.ValidateBasic() - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txCtx := utils.BuildReqTxCtx(cliCtx, baseReq, w) - - utils.WriteGenerateStdTxResponse(w, txCtx, []sdk.Msg{msg}) - } -} From 8576d6d3786009c9abcbe86a0635958e6fc72e17 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 09:36:36 +0800 Subject: [PATCH 3/8] fix unit test error --- app/v2/coinswap/internal/keeper/keeper.go | 27 ++----- app/v2/coinswap/internal/types/msgs_test.go | 74 ++++++++++++++++--- app/v2/coinswap/internal/types/test_common.go | 4 +- 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/app/v2/coinswap/internal/keeper/keeper.go b/app/v2/coinswap/internal/keeper/keeper.go index dbda0425d..a88b02e66 100644 --- a/app/v2/coinswap/internal/keeper/keeper.go +++ b/app/v2/coinswap/internal/keeper/keeper.go @@ -39,25 +39,14 @@ func (k Keeper) HandleSwap(ctx sdk.Context, msg types.MsgSwapOrder) (sdk.Tags, s var err sdk.Error var isDoubleSwap = msg.Input.Coin.Denom != sdk.IrisAtto && msg.Output.Coin.Denom != sdk.IrisAtto - switch msg.IsBuyOrder { - case true: - switch isDoubleSwap { - case true: - amount, err = k.doubleTradeInputForExactOutput(ctx, msg.Input, msg.Output) - break - case false: - amount, err = k.tradeInputForExactOutput(ctx, msg.Input, msg.Output) - break - } - case false: - switch isDoubleSwap { - case true: - amount, err = k.doubleTradeExactInputForOutput(ctx, msg.Input, msg.Output) - break - case false: - amount, err = k.tradeExactInputForOutput(ctx, msg.Input, msg.Output) - break - } + if isDoubleSwap && msg.IsBuyOrder { + amount, err = k.doubleTradeInputForExactOutput(ctx, msg.Input, msg.Output) + } else if isDoubleSwap && !msg.IsBuyOrder { + amount, err = k.doubleTradeExactInputForOutput(ctx, msg.Input, msg.Output) + } else if !isDoubleSwap && msg.IsBuyOrder { + amount, err = k.tradeInputForExactOutput(ctx, msg.Input, msg.Output) + } else if !isDoubleSwap && !msg.IsBuyOrder { + amount, err = k.tradeExactInputForOutput(ctx, msg.Input, msg.Output) } if err != nil { return nil, err diff --git a/app/v2/coinswap/internal/types/msgs_test.go b/app/v2/coinswap/internal/types/msgs_test.go index 50d95bd4b..980ee9257 100644 --- a/app/v2/coinswap/internal/types/msgs_test.go +++ b/app/v2/coinswap/internal/types/msgs_test.go @@ -15,16 +15,70 @@ func TestMsgSwapOrder(t *testing.T) { msg MsgSwapOrder expectPass bool }{ - {"no input coin", NewMsgSwapOrder(sdk.Coin{}, output, deadline, sender, recipient, true), false}, - {"zero input coin", NewMsgSwapOrder(sdk.NewCoin(denom0, sdk.ZeroInt()), output, deadline, sender, recipient, true), false}, - {"no output coin", NewMsgSwapOrder(input, sdk.Coin{}, deadline, sender, recipient, false), false}, - {"zero output coin", NewMsgSwapOrder(input, sdk.NewCoin(denom1, sdk.ZeroInt()), deadline, sender, recipient, true), false}, - {"swap and coin denomination are equal", NewMsgSwapOrder(input, sdk.NewCoin(denom0, amt), deadline, sender, recipient, true), false}, - {"deadline not initialized", NewMsgSwapOrder(input, output, emptyTime, sender, recipient, true), false}, - {"no sender", NewMsgSwapOrder(input, output, deadline, emptyAddr, recipient, true), false}, - {"no recipient", NewMsgSwapOrder(input, output, deadline, sender, emptyAddr, true), false}, - {"valid MsgSwapOrder", NewMsgSwapOrder(input, output, deadline, sender, recipient, true), true}, - {"sender and recipient are same", NewMsgSwapOrder(input, output, deadline, sender, sender, true), true}, + {"no input coin", NewMsgSwapOrder(Input{Address: sender}, Output{recipient, output}, deadline, true), false}, + {"zero input coin", NewMsgSwapOrder(Input{ + Address: sender, + Coin: sdk.NewCoin(denom0, sdk.ZeroInt()), + }, Output{ + Address: recipient, + Coin: output, + }, deadline, true), false}, + {"no output coin", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: recipient, + Coin: sdk.Coin{}, + }, deadline, false), false}, + {"zero output coin", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: recipient, + Coin: sdk.NewCoin(denom1, sdk.ZeroInt()), + }, deadline, true), false}, + {"swap and coin denomination are equal", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: recipient, + Coin: sdk.NewCoin(denom0, amt), + }, deadline, true), false}, + {"deadline not initialized", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: recipient, + Coin: output, + }, emptyTime, true), false}, + {"no sender", NewMsgSwapOrder(Input{ + Address: emptyAddr, + Coin: input, + }, Output{ + Address: recipient, + Coin: output, + }, deadline, true), false}, + {"no recipient", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: emptyAddr, + Coin: output, + }, deadline, true), true}, + {"valid MsgSwapOrder", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: recipient, + Coin: output, + }, deadline, true), true}, + {"sender and recipient are same", NewMsgSwapOrder(Input{ + Address: sender, + Coin: input, + }, Output{ + Address: sender, + Coin: output, + }, deadline, true), true}, } for _, tc := range tests { diff --git a/app/v2/coinswap/internal/types/test_common.go b/app/v2/coinswap/internal/types/test_common.go index a481f54e2..0a7369258 100644 --- a/app/v2/coinswap/internal/types/test_common.go +++ b/app/v2/coinswap/internal/types/test_common.go @@ -16,8 +16,8 @@ var ( sender = sdk.AccAddress(senderPk.Address()) recipient = sdk.AccAddress(recipientPk.Address()) - denom0 = "atom" - denom1 = "btc" + denom0 = "atom-min" + denom1 = "btc-min" input = sdk.NewCoin(denom0, sdk.NewInt(1000)) output = sdk.NewCoin(denom1, sdk.NewInt(500)) From 2775da510a27d5f6652635a402c17e1588df09e4 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 09:42:37 +0800 Subject: [PATCH 4/8] remove cli command --- client/coinswap/cli/flags.go | 51 -------- client/coinswap/cli/sendTx.go | 219 ---------------------------------- 2 files changed, 270 deletions(-) delete mode 100644 client/coinswap/cli/flags.go delete mode 100644 client/coinswap/cli/sendTx.go diff --git a/client/coinswap/cli/flags.go b/client/coinswap/cli/flags.go deleted file mode 100644 index 9810a3646..000000000 --- a/client/coinswap/cli/flags.go +++ /dev/null @@ -1,51 +0,0 @@ -package cli - -import ( - flag "github.com/spf13/pflag" -) - -const ( - //AddLiquidity - flagDeposit = "deposit" - flagAmount = "amount" - flagMinReward = "min-reward" - flagPeriod = "period" - - //RemoveLiquidity - flagMinToken = "min-token" - flagMinNative = "min-native" - - //SwapTokens - flagInput = "input" - flagOutput = "output" - flagDeadline = "deadline" - FlagRecipient = "recipient" - FlagIsBuyOrder = "is-buy-order" -) - -var ( - FsSwapTokens = flag.NewFlagSet("", flag.ContinueOnError) - FsAddLiquidity = flag.NewFlagSet("", flag.ContinueOnError) - FsRemoveLiquidity = flag.NewFlagSet("", flag.ContinueOnError) -) - -func init() { - //AddLiquidity - FsAddLiquidity.String(flagDeposit, "", "Token to be deposit") - FsAddLiquidity.String(flagAmount, "", "Amount of iris token") - FsAddLiquidity.String(flagMinReward, "", "lower bound UNI sender is willing to accept for deposited coins") - FsAddLiquidity.String(flagPeriod, "", "deadline of transaction") - - //RemoveLiquidity - FsRemoveLiquidity.String(flagMinToken, "", "coin to be withdrawn with a lower bound for its amount") - FsRemoveLiquidity.String(flagMinNative, "", "minimum amount of the native asset the sender is willing to accept") - FsRemoveLiquidity.String(flagAmount, "", "amount of UNI to be burned to withdraw liquidity from a reserve pool") - FsRemoveLiquidity.String(flagPeriod, "", "deadline of transaction") - - //SwapTokens - FsSwapTokens.String(flagInput, "", "Amount of coins to swap, if --is-buy-order=true,it means this is the exact amount of coins sold,otherwise it means this is the max amount of coins sold") - FsSwapTokens.String(flagOutput, "", "Amount of coins to swap, if --is-buy-order=true,it means this is the min amount of coins bought,otherwise it means this is the exact amount of coins bought") - FsSwapTokens.String(flagDeadline, "", "deadline for the transaction to still be considered valid,such as 6m") - FsSwapTokens.String(FlagRecipient, "", "address of coins bought") - FsSwapTokens.Bool(FlagIsBuyOrder, false, "boolean indicating whether the order should be treated as a buy or sell") -} diff --git a/client/coinswap/cli/sendTx.go b/client/coinswap/cli/sendTx.go deleted file mode 100644 index 5b8087d43..000000000 --- a/client/coinswap/cli/sendTx.go +++ /dev/null @@ -1,219 +0,0 @@ -package cli - -import ( - "fmt" - "github.com/irisnet/irishub/app/v2/coinswap" - "github.com/irisnet/irishub/client/context" - "github.com/irisnet/irishub/client/utils" - "github.com/irisnet/irishub/codec" - sdk "github.com/irisnet/irishub/types" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "os" - "time" -) - -// SendTxCmd will create a send tx and sign it with the given key. -func GetCmdAddLiquidity(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "add-liquidity", - Short: "Create and sign a send tx", - Example: "iriscli swap add-liquidity --deposit=1eth --amount=1500iris --period=10s --from= --fee=0.4iris --chain-id=", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithLogger(os.Stdout). - WithAccountDecoder(utils.GetAccountDecoder(cdc)) - txCtx := utils.NewTxContextFromCLI().WithCodec(cdc).WithCliCtx(cliCtx) - - // parse coins trying to be sent - depositTokenStr := viper.GetString(flagDeposit) - depositToken, err := cliCtx.ParseCoin(depositTokenStr) - if err != nil { - return err - } - - nativeTokenStr := viper.GetString(flagAmount) - nativeToken, err := cliCtx.ParseCoin(nativeTokenStr) - if err != nil { - return err - } - - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - - periodStr := viper.GetString(flagPeriod) - period, err := time.ParseDuration(periodStr) - if err != nil { - return err - } - deadline := time.Now().Add(period) - - minReward := sdk.ZeroInt() - minRewardStr := viper.GetString(flagMinReward) - if len(minRewardStr) > 0 { - reward, ok := sdk.NewIntFromString(minRewardStr) - if !ok { - return fmt.Errorf("invalid min reward:%s", minRewardStr) - } - minReward = reward - } - - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - - // ensure account has enough coins - coins := sdk.NewCoins(depositToken, nativeToken) - if !account.GetCoins().IsAllGTE(coins) { - return fmt.Errorf("Address %s doesn't have enough coins to pay for this transaction", from.String()) - } - - msg := coinswap.NewMsgAddLiquidity(depositToken, nativeToken.Amount, minReward, deadline, from) - return utils.SendOrPrintTx(txCtx, cliCtx, []sdk.Msg{msg}) - }, - } - - cmd.Flags().AddFlagSet(FsAddLiquidity) - cmd.MarkFlagRequired(flagDeposit) - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagPeriod) - - return cmd -} - -func GetCmdRemoveLiquidity(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "remove-liquidity", - Short: "Create and sign a send tx", - Example: "iriscli swap remove-liquidity --min-token=1eth --min-native=1000 --amount=1000uni --from --fee=0.4iris --chain-id= --amount=10iris", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithLogger(os.Stdout). - WithAccountDecoder(utils.GetAccountDecoder(cdc)) - txCtx := utils.NewTxContextFromCLI().WithCodec(cdc).WithCliCtx(cliCtx) - - var minToken, liquidity sdk.Coin - var minNative = sdk.ZeroInt() - // parse coins trying to be sent - minTokenStr := viper.GetString(flagMinToken) - if len(minTokenStr) > 0 { - coin, err := cliCtx.ParseCoin(minTokenStr) - if err != nil { - return err - } else { - minToken = coin - } - } - - minNativeStr := viper.GetString(flagMinNative) - if len(minNativeStr) > 0 { - amt, ok := sdk.NewIntFromString(minNativeStr) - if !ok { - return fmt.Errorf("invalid amount:%s", minNativeStr) - } else { - minNative = amt - } - } - - liquidityStr := viper.GetString(flagAmount) - if len(liquidityStr) > 0 { - coin, err := cliCtx.ParseCoin(liquidityStr) - if err != nil { - return err - } else { - liquidity = coin - } - } - - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - - account, err := cliCtx.GetAccount(from) - if err != nil { - return err - } - // ensure account has enough coins - coins := sdk.NewCoins(liquidity) - if !account.GetCoins().IsAllGTE(coins) { - return fmt.Errorf("Address %s doesn't have enough coins to pay for this transaction", from.String()) - } - - periodStr := viper.GetString(flagPeriod) - period, err := time.ParseDuration(periodStr) - if err != nil { - return err - } - deadline := time.Now().Add(period) - - msg := coinswap.NewMsgRemoveLiquidity(minToken, liquidity.Amount, minNative, deadline, from) - return utils.SendOrPrintTx(txCtx, cliCtx, []sdk.Msg{msg}) - }, - } - - cmd.Flags().AddFlagSet(FsRemoveLiquidity) - cmd.MarkFlagRequired(flagAmount) - cmd.MarkFlagRequired(flagPeriod) - return cmd -} - -func GetCmdPlaceOrder(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "place-order", - Short: "swap tokens with other token", - Example: "iriscli swap place-order --input-token= --output-token= --deadline= --recipient= --is-buy-order= --from --fee=0.4iris --chain-id= --amount=10iris", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext(). - WithCodec(cdc). - WithLogger(os.Stdout). - WithAccountDecoder(utils.GetAccountDecoder(cdc)) - txCtx := utils.NewTxContextFromCLI().WithCodec(cdc).WithCliCtx(cliCtx) - - // parse coins trying to be sent - inputStr := viper.GetString(flagInput) - input, err := cliCtx.ParseCoin(inputStr) - if err != nil { - return err - } - outputStr := viper.GetString(flagOutput) - output, err := cliCtx.ParseCoin(outputStr) - if err != nil { - return err - } - periodStr := viper.GetString(flagDeadline) - period, err := time.ParseDuration(periodStr) - if err != nil { - return err - } - deadline := time.Now().Add(period) - - recipientStr := viper.GetString(FlagRecipient) - recipient, err := sdk.AccAddressFromBech32(recipientStr) - if err != nil { - return err - } - - isBuyOrder := viper.GetBool(FlagIsBuyOrder) - - sender, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - - msg := coinswap.NewMsgSwapOrder(input, output, deadline, sender, recipient, isBuyOrder) - - if err := msg.ValidateBasic(); err != nil { - return err - } - return utils.SendOrPrintTx(txCtx, cliCtx, []sdk.Msg{msg}) - }, - } - cmd.Flags().AddFlagSet(FsSwapTokens) - return cmd -} From d120db28c6b3bc4d00568e010f5948e51e456357 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 09:46:19 +0800 Subject: [PATCH 5/8] remove cli command --- cmd/iriscli/main.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/cmd/iriscli/main.go b/cmd/iriscli/main.go index 9ce7ef54c..b3962ca93 100644 --- a/cmd/iriscli/main.go +++ b/cmd/iriscli/main.go @@ -10,7 +10,6 @@ import ( "github.com/irisnet/irishub/client" assetcmd "github.com/irisnet/irishub/client/asset/cli" bankcmd "github.com/irisnet/irishub/client/bank/cli" - swap "github.com/irisnet/irishub/client/coinswap/cli" distributioncmd "github.com/irisnet/irishub/client/distribution/cli" govcmd "github.com/irisnet/irishub/client/gov/cli" guardiancmd "github.com/irisnet/irishub/client/guardian/cli" @@ -283,21 +282,6 @@ func main() { assetCmd, ) - //Add bank commands - swapCmd := &cobra.Command{ - Use: "swap", - Short: "coin swap subcommands", - } - swapCmd.AddCommand( - client.PostCommands( - swap.GetCmdAddLiquidity(cdc), - swap.GetCmdRemoveLiquidity(cdc), - swap.GetCmdPlaceOrder(cdc), - )...) - rootCmd.AddCommand( - swapCmd, - ) - // add rand commands randCmd := &cobra.Command{ Use: "rand", From 0af93ffa6274a8abfdcc7726c5f4fb0123594fdc Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 09:57:01 +0800 Subject: [PATCH 6/8] fix unit test error --- app/v2/coinswap/internal/types/msgs.go | 2 +- app/v2/coinswap/internal/types/msgs_test.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/v2/coinswap/internal/types/msgs.go b/app/v2/coinswap/internal/types/msgs.go index a8a4126d5..c85a00728 100644 --- a/app/v2/coinswap/internal/types/msgs.go +++ b/app/v2/coinswap/internal/types/msgs.go @@ -136,7 +136,7 @@ func (msg MsgAddLiquidity) ValidateBasic() sdk.Error { if msg.ExactIrisAmt.IsNil() || !msg.ExactIrisAmt.IsPositive() { return ErrNotPositive("deposit amount provided is not positive") } - if msg.ExactIrisAmt.IsNil() || msg.MinLiquidity.IsNegative() { + if msg.MinLiquidity.IsNil() || msg.MinLiquidity.IsNegative() { return ErrNotPositive("minimum liquidity is negative") } if msg.Deadline.IsZero() { diff --git a/app/v2/coinswap/internal/types/msgs_test.go b/app/v2/coinswap/internal/types/msgs_test.go index 980ee9257..68fc8f10d 100644 --- a/app/v2/coinswap/internal/types/msgs_test.go +++ b/app/v2/coinswap/internal/types/msgs_test.go @@ -103,7 +103,6 @@ func TestMsgAddLiquidity(t *testing.T) { {"no deposit coin", NewMsgAddLiquidity(sdk.Coin{}, amt, sdk.OneInt(), deadline, sender), false}, {"zero deposit coin", NewMsgAddLiquidity(sdk.NewCoin(denom1, sdk.ZeroInt()), amt, sdk.OneInt(), deadline, sender), false}, {"invalid withdraw amount", NewMsgAddLiquidity(input, sdk.ZeroInt(), sdk.OneInt(), deadline, sender), false}, - {"invalid minumum reward bound", NewMsgAddLiquidity(input, amt, sdk.ZeroInt(), deadline, sender), false}, {"deadline not initialized", NewMsgAddLiquidity(input, amt, sdk.OneInt(), emptyTime, sender), false}, {"empty sender", NewMsgAddLiquidity(input, amt, sdk.OneInt(), deadline, emptyAddr), false}, {"valid MsgAddLiquidity", NewMsgAddLiquidity(input, amt, sdk.OneInt(), deadline, sender), true}, From 3a42e65eba3fee2340bb656aa582b25297a9d935 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 11:18:44 +0800 Subject: [PATCH 7/8] fix unit test error --- app/v2/coinswap/internal/keeper/keeper.go | 3 +++ app/v2/coinswap/internal/keeper/swap.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/v2/coinswap/internal/keeper/keeper.go b/app/v2/coinswap/internal/keeper/keeper.go index a88b02e66..d57e2701b 100644 --- a/app/v2/coinswap/internal/keeper/keeper.go +++ b/app/v2/coinswap/internal/keeper/keeper.go @@ -126,6 +126,9 @@ func (k Keeper) HandleRemoveLiquidity(ctx sdk.Context, msg types.MsgRemoveLiquid if tokenReserveAmt.LT(msg.MinToken.Amount) { return types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s,expect:%s", tokenReserveAmt.String(), msg.MinToken.Amount.String())) } + if liquidityReserve.LT(msg.WithdrawLiquidity) { + return types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s,expect:%s", liquidityReserve.String(), msg.WithdrawLiquidity.String())) + } // calculate amount of UNI to be burned for sender // and coin amount to be returned diff --git a/app/v2/coinswap/internal/keeper/swap.go b/app/v2/coinswap/internal/keeper/swap.go index 923bfe3c1..dda5f44bc 100644 --- a/app/v2/coinswap/internal/keeper/swap.go +++ b/app/v2/coinswap/internal/keeper/swap.go @@ -6,7 +6,7 @@ import ( sdk "github.com/irisnet/irishub/types" ) -const PrefixReservePool = "u-%s" +const FormatReservePool = "u-%s" func (k Keeper) SwapCoins(ctx sdk.Context, sender, recipient sdk.AccAddress, coinSold, coinBought sdk.Coin) sdk.Error { reservePoolName, err := k.GetReservePoolName(coinSold.Denom, coinBought.Denom) @@ -233,7 +233,7 @@ func (k Keeper) GetReservePoolName(denom1, denom2 string) (string, sdk.Error) { if denom1 != sdk.IrisAtto { denom = denom1 } - return fmt.Sprintf(PrefixReservePool, denom), nil + return fmt.Sprintf(FormatReservePool, denom), nil } // getInputPrice returns the amount of coins bought (calculated) given the input amount being sold (exact) From 8da509c79e93d8efe61b885595f5d450a436ee06 Mon Sep 17 00:00:00 2001 From: zhangzhiqiang <745124335@qq.com> Date: Mon, 12 Aug 2019 11:27:21 +0800 Subject: [PATCH 8/8] format --- app/v2/coinswap/internal/keeper/swap.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/v2/coinswap/internal/keeper/swap.go b/app/v2/coinswap/internal/keeper/swap.go index dda5f44bc..2aae458e9 100644 --- a/app/v2/coinswap/internal/keeper/swap.go +++ b/app/v2/coinswap/internal/keeper/swap.go @@ -46,10 +46,10 @@ func (k Keeper) calculateWithExactInput(ctx sdk.Context, exactSoldCoin sdk.Coin, outputReserve := reservePool.AmountOf(boughtTokenDenom) if !inputReserve.IsPositive() { - return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s", inputReserve.String())) + return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds, actual:%s", inputReserve.String())) } if !outputReserve.IsPositive() { - return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s", outputReserve.String())) + return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds, actual:%s", outputReserve.String())) } param := k.GetParams(ctx) @@ -139,13 +139,13 @@ func (k Keeper) calculateWithExactOutput(ctx sdk.Context, exactBoughtCoin sdk.Co outputReserve := reservePool.AmountOf(soldTokenDenom) if !inputReserve.IsPositive() { - return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s", inputReserve.String())) + return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds, actual:%s", inputReserve.String())) } if !outputReserve.IsPositive() { - return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,actual:%s", outputReserve.String())) + return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds, actual:%s", outputReserve.String())) } if exactBoughtCoin.Amount.GTE(outputReserve) { - return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds,want:%s,actual:%s", exactBoughtCoin.String(), outputReserve.String())) + return sdk.ZeroInt(), types.ErrInsufficientFunds(fmt.Sprintf("insufficient funds, want:%s,actual:%s", exactBoughtCoin.String(), outputReserve.String())) } param := k.GetParams(ctx)