feat(wallet)_: handle paraswap price impact error (#5622)
This commit is contained in:
parent
01288227d3
commit
d7fcbd3444
1
Makefile
1
Makefile
|
@ -357,6 +357,7 @@ mock: ##@other Regenerate mocks
|
|||
mockgen -package=mock_collectibles -destination=services/wallet/collectibles/mock/collection_data_db.go -source=services/wallet/collectibles/collection_data_db.go
|
||||
mockgen -package=mock_collectibles -destination=services/wallet/collectibles/mock/collectible_data_db.go -source=services/wallet/collectibles/collectible_data_db.go
|
||||
mockgen -package=mock_thirdparty -destination=services/wallet/thirdparty/mock/collectible_types.go -source=services/wallet/thirdparty/collectible_types.go
|
||||
mockgen -package=mock_paraswap -destination=services/wallet/thirdparty/paraswap/mock/types.go -source=services/wallet/thirdparty/paraswap/types.go
|
||||
|
||||
docker-test: ##@tests Run tests in a docker container with golang.
|
||||
docker run --privileged --rm -it -v "$(PWD):$(DOCKER_TEST_WORKDIR)" -w "$(DOCKER_TEST_WORKDIR)" $(DOCKER_TEST_IMAGE) go test ${ARGS}
|
||||
|
|
|
@ -46,6 +46,7 @@ var (
|
|||
ErrContextDeadlineExceeded = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-036"), Details: "context deadline exceeded"}
|
||||
ErrPriceTimeout = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-037"), Details: "price timeout"}
|
||||
ErrNotEnoughLiquidity = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-038"), Details: "not enough liquidity"}
|
||||
ErrPriceImpactTooHigh = &errors.ErrorResponse{Code: errors.ErrorCode("WPP-039"), Details: "price impact too high"}
|
||||
)
|
||||
|
||||
func createErrorResponse(processorName string, err error) error {
|
||||
|
|
|
@ -28,7 +28,7 @@ type SwapParaswapTxArgs struct {
|
|||
}
|
||||
|
||||
type SwapParaswapProcessor struct {
|
||||
paraswapClient *paraswap.ClientV5
|
||||
paraswapClient paraswap.ClientInterface
|
||||
transactor transactions.TransactorIface
|
||||
priceRoute sync.Map // [fromChainName-toChainName-fromTokenSymbol-toTokenSymbol, paraswap.Route]
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ func createSwapParaswapErrorResponse(err error) error {
|
|||
return ErrPriceTimeout
|
||||
case "No routes found with enough liquidity":
|
||||
return ErrNotEnoughLiquidity
|
||||
case "ESTIMATED_LOSS_GREATER_THAN_MAX_IMPACT":
|
||||
return ErrPriceImpactTooHigh
|
||||
}
|
||||
return createErrorResponse(ProcessorSwapParaswapName, err)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/paraswap"
|
||||
mock_paraswap "github.com/status-im/status-go/services/wallet/thirdparty/paraswap/mock"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -69,3 +74,60 @@ func TestParaswapWithPartnerFee(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectClientFetchPriceRoute(clientMock *mock_paraswap.MockClientInterface, route paraswap.Route, err error) {
|
||||
clientMock.EXPECT().FetchPriceRoute(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(route, err)
|
||||
}
|
||||
|
||||
func TestParaswapErrors(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
client := mock_paraswap.NewMockClientInterface(ctrl)
|
||||
|
||||
processor := NewSwapParaswapProcessor(nil, nil, nil)
|
||||
processor.paraswapClient = client
|
||||
|
||||
fromToken := token.Token{
|
||||
Symbol: EthSymbol,
|
||||
}
|
||||
toToken := token.Token{
|
||||
Symbol: UsdcSymbol,
|
||||
}
|
||||
chainID := walletCommon.EthereumMainnet
|
||||
|
||||
testInputParams := ProcessorInputParams{
|
||||
FromChain: ¶ms.Network{ChainID: chainID},
|
||||
ToChain: ¶ms.Network{ChainID: chainID},
|
||||
FromToken: &fromToken,
|
||||
ToToken: &toToken,
|
||||
}
|
||||
|
||||
// Test Errors
|
||||
type testCase struct {
|
||||
clientError string
|
||||
processorError error
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{"Price Timeout", ErrPriceTimeout},
|
||||
{"No routes found with enough liquidity", ErrNotEnoughLiquidity},
|
||||
{"ESTIMATED_LOSS_GREATER_THAN_MAX_IMPACT", ErrPriceImpactTooHigh},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
expectClientFetchPriceRoute(client, paraswap.Route{}, errors.New(tc.clientError))
|
||||
_, err := processor.EstimateGas(testInputParams)
|
||||
require.Equal(t, tc.processorError.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: services/wallet/thirdparty/paraswap/types.go
|
||||
|
||||
// Package mock_paraswap is a generated GoMock package.
|
||||
package mock_paraswap
|
||||
|
||||
import (
|
||||
context "context"
|
||||
json "encoding/json"
|
||||
big "math/big"
|
||||
reflect "reflect"
|
||||
|
||||
common "github.com/ethereum/go-ethereum/common"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
paraswap "github.com/status-im/status-go/services/wallet/thirdparty/paraswap"
|
||||
)
|
||||
|
||||
// MockClientInterface is a mock of ClientInterface interface.
|
||||
type MockClientInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockClientInterfaceMockRecorder
|
||||
}
|
||||
|
||||
// MockClientInterfaceMockRecorder is the mock recorder for MockClientInterface.
|
||||
type MockClientInterfaceMockRecorder struct {
|
||||
mock *MockClientInterface
|
||||
}
|
||||
|
||||
// NewMockClientInterface creates a new mock instance.
|
||||
func NewMockClientInterface(ctrl *gomock.Controller) *MockClientInterface {
|
||||
mock := &MockClientInterface{ctrl: ctrl}
|
||||
mock.recorder = &MockClientInterfaceMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockClientInterface) EXPECT() *MockClientInterfaceMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// BuildTransaction mocks base method.
|
||||
func (m *MockClientInterface) BuildTransaction(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint, srcAmountWei *big.Int, destTokenAddress common.Address, destTokenDecimals uint, destAmountWei *big.Int, slippageBasisPoints uint, addressFrom, addressTo common.Address, priceRoute json.RawMessage, side paraswap.SwapSide) (paraswap.Transaction, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "BuildTransaction", ctx, srcTokenAddress, srcTokenDecimals, srcAmountWei, destTokenAddress, destTokenDecimals, destAmountWei, slippageBasisPoints, addressFrom, addressTo, priceRoute, side)
|
||||
ret0, _ := ret[0].(paraswap.Transaction)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// BuildTransaction indicates an expected call of BuildTransaction.
|
||||
func (mr *MockClientInterfaceMockRecorder) BuildTransaction(ctx, srcTokenAddress, srcTokenDecimals, srcAmountWei, destTokenAddress, destTokenDecimals, destAmountWei, slippageBasisPoints, addressFrom, addressTo, priceRoute, side interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTransaction", reflect.TypeOf((*MockClientInterface)(nil).BuildTransaction), ctx, srcTokenAddress, srcTokenDecimals, srcAmountWei, destTokenAddress, destTokenDecimals, destAmountWei, slippageBasisPoints, addressFrom, addressTo, priceRoute, side)
|
||||
}
|
||||
|
||||
// FetchPriceRoute mocks base method.
|
||||
func (m *MockClientInterface) FetchPriceRoute(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint, destTokenAddress common.Address, destTokenDecimals uint, amountWei *big.Int, addressFrom, addressTo common.Address, side paraswap.SwapSide) (paraswap.Route, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FetchPriceRoute", ctx, srcTokenAddress, srcTokenDecimals, destTokenAddress, destTokenDecimals, amountWei, addressFrom, addressTo, side)
|
||||
ret0, _ := ret[0].(paraswap.Route)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchPriceRoute indicates an expected call of FetchPriceRoute.
|
||||
func (mr *MockClientInterfaceMockRecorder) FetchPriceRoute(ctx, srcTokenAddress, srcTokenDecimals, destTokenAddress, destTokenDecimals, amountWei, addressFrom, addressTo, side interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchPriceRoute", reflect.TypeOf((*MockClientInterface)(nil).FetchPriceRoute), ctx, srcTokenAddress, srcTokenDecimals, destTokenAddress, destTokenDecimals, amountWei, addressFrom, addressTo, side)
|
||||
}
|
||||
|
||||
// FetchTokensList mocks base method.
|
||||
func (m *MockClientInterface) FetchTokensList(ctx context.Context) ([]paraswap.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FetchTokensList", ctx)
|
||||
ret0, _ := ret[0].([]paraswap.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchTokensList indicates an expected call of FetchTokensList.
|
||||
func (mr *MockClientInterfaceMockRecorder) FetchTokensList(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchTokensList", reflect.TypeOf((*MockClientInterface)(nil).FetchTokensList), ctx)
|
||||
}
|
||||
|
||||
// SetChainID mocks base method.
|
||||
func (m *MockClientInterface) SetChainID(chainID uint64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetChainID", chainID)
|
||||
}
|
||||
|
||||
// SetChainID indicates an expected call of SetChainID.
|
||||
func (mr *MockClientInterfaceMockRecorder) SetChainID(chainID interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetChainID", reflect.TypeOf((*MockClientInterface)(nil).SetChainID), chainID)
|
||||
}
|
||||
|
||||
// SetPartnerAddress mocks base method.
|
||||
func (m *MockClientInterface) SetPartnerAddress(partnerAddress common.Address) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetPartnerAddress", partnerAddress)
|
||||
}
|
||||
|
||||
// SetPartnerAddress indicates an expected call of SetPartnerAddress.
|
||||
func (mr *MockClientInterfaceMockRecorder) SetPartnerAddress(partnerAddress interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPartnerAddress", reflect.TypeOf((*MockClientInterface)(nil).SetPartnerAddress), partnerAddress)
|
||||
}
|
||||
|
||||
// SetPartnerFeePcnt mocks base method.
|
||||
func (m *MockClientInterface) SetPartnerFeePcnt(partnerFeePcnt float64) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetPartnerFeePcnt", partnerFeePcnt)
|
||||
}
|
||||
|
||||
// SetPartnerFeePcnt indicates an expected call of SetPartnerFeePcnt.
|
||||
func (mr *MockClientInterfaceMockRecorder) SetPartnerFeePcnt(partnerFeePcnt interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPartnerFeePcnt", reflect.TypeOf((*MockClientInterface)(nil).SetPartnerFeePcnt), partnerFeePcnt)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package paraswap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type ClientInterface interface {
|
||||
SetChainID(chainID uint64)
|
||||
SetPartnerAddress(partnerAddress common.Address)
|
||||
SetPartnerFeePcnt(partnerFeePcnt float64)
|
||||
BuildTransaction(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint, srcAmountWei *big.Int,
|
||||
destTokenAddress common.Address, destTokenDecimals uint, destAmountWei *big.Int, slippageBasisPoints uint,
|
||||
addressFrom common.Address, addressTo common.Address, priceRoute json.RawMessage, side SwapSide) (Transaction, error)
|
||||
FetchPriceRoute(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint,
|
||||
destTokenAddress common.Address, destTokenDecimals uint, amountWei *big.Int, addressFrom common.Address,
|
||||
addressTo common.Address, side SwapSide) (Route, error)
|
||||
FetchTokensList(ctx context.Context) ([]Token, error)
|
||||
}
|
Loading…
Reference in New Issue