chore_: swap via paraswap improvements
This commit is contained in:
parent
9c3b49b866
commit
462013520f
|
@ -37,6 +37,7 @@ type ProcessorInputParams struct {
|
|||
FromToken *token.Token
|
||||
ToToken *token.Token
|
||||
AmountIn *big.Int
|
||||
AmountOut *big.Int
|
||||
|
||||
// extra params
|
||||
BonderFee *big.Int
|
||||
|
|
|
@ -20,7 +20,8 @@ import (
|
|||
|
||||
type SwapParaswapTxArgs struct {
|
||||
transactions.SendTxArgs
|
||||
ChainID uint64 `json:"chainId"`
|
||||
ChainID uint64 `json:"chainId"`
|
||||
SlippagePercentage float32 `json:"slippagePercentage"`
|
||||
}
|
||||
|
||||
type SwapParaswapProcessor struct {
|
||||
|
@ -95,8 +96,13 @@ func (s *SwapParaswapProcessor) PackTxInputData(params ProcessorInputParams) ([]
|
|||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) {
|
||||
swapSide := paraswap.SellSide
|
||||
if params.AmountOut != nil && params.AmountOut.Cmp(ZeroBigIntValue) > 0 {
|
||||
swapSide = paraswap.BuySide
|
||||
}
|
||||
|
||||
priceRoute, err := s.paraswapClient.FetchPriceRoute(context.Background(), params.FromToken.Address, params.FromToken.Decimals,
|
||||
params.ToToken.Address, params.ToToken.Decimals, params.AmountIn, params.FromAddr, params.ToAddr)
|
||||
params.ToToken.Address, params.ToToken.Decimals, params.AmountIn, params.FromAddr, params.ToAddr, swapSide)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -138,8 +144,12 @@ func (s *SwapParaswapProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.
|
|||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorTxArgs) error {
|
||||
slippageBP := uint(sendArgs.SwapTx.SlippagePercentage * 100) // convert to basis points
|
||||
|
||||
tx, err := s.paraswapClient.BuildTransaction(context.Background(), s.priceRoute.SrcTokenAddress, s.priceRoute.SrcTokenDecimals, s.priceRoute.SrcAmount.Int,
|
||||
s.priceRoute.DestTokenAddress, s.priceRoute.DestTokenDecimals, s.priceRoute.DestAmount.Int, common.Address(sendArgs.SwapTx.From), common.Address(*sendArgs.SwapTx.To), s.priceRoute.RawPriceRoute)
|
||||
s.priceRoute.DestTokenAddress, s.priceRoute.DestTokenDecimals, s.priceRoute.DestAmount.Int, slippageBP,
|
||||
common.Address(sendArgs.SwapTx.From), common.Address(*sendArgs.SwapTx.To),
|
||||
s.priceRoute.RawPriceRoute, s.priceRoute.Side)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -589,7 +589,7 @@ func (r *Router) SuggestedRoutes(
|
|||
continue
|
||||
}
|
||||
|
||||
ProcessorInputParams := pathprocessor.ProcessorInputParams{
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: network,
|
||||
ToChain: dest,
|
||||
FromToken: token,
|
||||
|
@ -599,7 +599,7 @@ func (r *Router) SuggestedRoutes(
|
|||
AmountIn: amountIn,
|
||||
}
|
||||
|
||||
can, err := pProcessor.AvailableFor(ProcessorInputParams)
|
||||
can, err := pProcessor.AvailableFor(processorInputParams)
|
||||
if err != nil || !can {
|
||||
continue
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ func (r *Router) SuggestedRoutes(
|
|||
continue
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(ProcessorInputParams)
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ func (r *Router) SuggestedRoutes(
|
|||
}
|
||||
gasLimit := uint64(0)
|
||||
if sendType.isTransfer(false) {
|
||||
gasLimit, err = pProcessor.EstimateGas(ProcessorInputParams)
|
||||
gasLimit, err = pProcessor.EstimateGas(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ func (r *Router) SuggestedRoutes(
|
|||
gasLimit = sendType.EstimateGas(r.ensService, r.stickersService, network, addrFrom, tokenID)
|
||||
}
|
||||
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(ProcessorInputParams)
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -643,7 +643,7 @@ func (r *Router) SuggestedRoutes(
|
|||
|
||||
var l1GasFeeWei uint64
|
||||
if sendType.needL1Fee() {
|
||||
txInputData, err := pProcessor.PackTxInputData(ProcessorInputParams)
|
||||
txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ func (r *Router) SuggestedRoutes(
|
|||
suggestedRoutes.TokenPrice = prices[tokenID]
|
||||
suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
|
||||
for _, path := range suggestedRoutes.Best {
|
||||
ProcessorInputParams := pathprocessor.ProcessorInputParams{
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: path.From,
|
||||
ToChain: path.To,
|
||||
AmountIn: path.AmountIn.ToInt(),
|
||||
|
@ -732,7 +732,7 @@ func (r *Router) SuggestedRoutes(
|
|||
},
|
||||
}
|
||||
|
||||
amountOut, err := r.pathProcessors[path.BridgeName].CalculateAmountOut(ProcessorInputParams)
|
||||
amountOut, err := r.pathProcessors[path.BridgeName].CalculateAmountOut(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -103,9 +103,15 @@ func (s SendType) needL1Fee() bool {
|
|||
return !s.IsEnsTransfer() && !s.IsStickersTransfer()
|
||||
}
|
||||
|
||||
// canUseProcessor is used to check if certain SendType can be used with a given path processor
|
||||
func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
|
||||
pathProcessorName := p.Name()
|
||||
switch s {
|
||||
case Bridge:
|
||||
return pathProcessorName == pathprocessor.ProcessorBridgeHopName ||
|
||||
pathProcessorName == pathprocessor.ProcessorBridgeCelerName
|
||||
case Swap:
|
||||
return pathProcessorName == pathprocessor.ProcessorSwapParaswapName
|
||||
case ERC721Transfer:
|
||||
return pathProcessorName == pathprocessor.ProcessorERC721Name
|
||||
case ERC1155Transfer:
|
||||
|
|
|
@ -38,6 +38,7 @@ type RouteInputParams struct {
|
|||
AddrFrom common.Address `json:"addrFrom" validate:"required"`
|
||||
AddrTo common.Address `json:"addrTo" validate:"required"`
|
||||
AmountIn *hexutil.Big `json:"amountIn" validate:"required"`
|
||||
AmountOut *hexutil.Big `json:"amountOut"`
|
||||
TokenID string `json:"tokenID" validate:"required"`
|
||||
ToTokenID string `json:"toTokenID"`
|
||||
DisabledFromChainIDs []uint64 `json:"disabledFromChainIDs"`
|
||||
|
@ -326,6 +327,30 @@ func validateInputData(input *RouteInputParams) error {
|
|||
}
|
||||
}
|
||||
|
||||
if input.SendType == Swap {
|
||||
if input.ToTokenID == "" {
|
||||
return errors.New("toTokenID is required for Swap")
|
||||
}
|
||||
if input.TokenID == input.ToTokenID {
|
||||
return errors.New("tokenID and toTokenID must be different")
|
||||
}
|
||||
|
||||
// we can do this check, cause AmountIn is required in `RouteInputParams`
|
||||
if input.AmountIn.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 &&
|
||||
input.AmountOut != nil &&
|
||||
input.AmountOut.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
return errors.New("only one of amountIn or amountOut can be set")
|
||||
}
|
||||
|
||||
if input.AmountIn.ToInt().Sign() < 0 {
|
||||
return errors.New("amountIn must be positive")
|
||||
}
|
||||
|
||||
if input.AmountOut != nil && input.AmountOut.ToInt().Sign() < 0 {
|
||||
return errors.New("amountOut must be positive")
|
||||
}
|
||||
}
|
||||
|
||||
if input.FromLockedAmount != nil && len(input.FromLockedAmount) > 0 {
|
||||
for chainID, amount := range input.FromLockedAmount {
|
||||
if input.TestnetMode {
|
||||
|
@ -414,6 +439,22 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
}
|
||||
|
||||
for _, pProcessor := range r.pathProcessors {
|
||||
// With the condition below we're eliminating `Swap` as potential path that can participate in calculating the best route
|
||||
// once we decide to inlcude `Swap` in the calculation we need to update `canUseProcessor` function.
|
||||
// This also applies to including another (Celer) bridge in the calculation.
|
||||
// TODO:
|
||||
// this algorithm, includeing finding the best route, has to be updated to include more bridges and one (for now) or more swap options
|
||||
// it means that candidates should not be treated linearly, but improve the logic to have multiple routes with different processors of the same type.
|
||||
// Example:
|
||||
// Routes for sending SNT from Ethereum to Optimism can be:
|
||||
// 1. Swap SNT(mainnet) to ETH(mainnet); then bridge via Hop ETH(mainnet) to ETH(opt); then Swap ETH(opt) to SNT(opt); then send SNT (opt) to the destination
|
||||
// 2. Swap SNT(mainnet) to ETH(mainnet); then bridge via Celer ETH(mainnet) to ETH(opt); then Swap ETH(opt) to SNT(opt); then send SNT (opt) to the destination
|
||||
// 3. Swap SNT(mainnet) to USDC(mainnet); then bridge via Hop USDC(mainnet) to USDC(opt); then Swap USDC(opt) to SNT(opt); then send SNT (opt) to the destination
|
||||
// 4. Swap SNT(mainnet) to USDC(mainnet); then bridge via Celer USDC(mainnet) to USDC(opt); then Swap USDC(opt) to SNT(opt); then send SNT (opt) to the destination
|
||||
// 5. ...
|
||||
// 6. ...
|
||||
//
|
||||
// With the current routing algorithm atm we're not able to generate all possible routes.
|
||||
if !input.SendType.canUseProcessor(pProcessor) {
|
||||
continue
|
||||
}
|
||||
|
@ -439,7 +480,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
continue
|
||||
}
|
||||
|
||||
ProcessorInputParams := pathprocessor.ProcessorInputParams{
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromChain: network,
|
||||
ToChain: dest,
|
||||
FromToken: token,
|
||||
|
@ -447,28 +488,29 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
ToAddr: input.AddrTo,
|
||||
FromAddr: input.AddrFrom,
|
||||
AmountIn: amountToSend,
|
||||
AmountOut: input.AmountOut.ToInt(),
|
||||
|
||||
Username: input.Username,
|
||||
PublicKey: input.PublicKey,
|
||||
PackID: input.PackID.Int,
|
||||
}
|
||||
|
||||
can, err := pProcessor.AvailableFor(ProcessorInputParams)
|
||||
can, err := pProcessor.AvailableFor(processorInputParams)
|
||||
if err != nil || !can {
|
||||
continue
|
||||
}
|
||||
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(ProcessorInputParams)
|
||||
bonderFees, tokenFees, err := pProcessor.CalculateFees(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
gasLimit, err := pProcessor.EstimateGas(ProcessorInputParams)
|
||||
gasLimit, err := pProcessor.EstimateGas(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(ProcessorInputParams)
|
||||
approvalContractAddress, err := pProcessor.GetContractAddress(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -480,7 +522,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
var l1FeeWei uint64
|
||||
if input.SendType.needL1Fee() {
|
||||
|
||||
txInputData, err := pProcessor.PackTxInputData(ProcessorInputParams)
|
||||
txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -504,7 +546,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
selctedPriorityFee = priorityFees.Low
|
||||
}
|
||||
|
||||
amountOut, err := pProcessor.CalculateAmountOut(ProcessorInputParams)
|
||||
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -2,6 +2,13 @@ package paraswap
|
|||
|
||||
import "github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
||||
type SwapSide string
|
||||
|
||||
const (
|
||||
SellSide = SwapSide("SELL")
|
||||
BuySide = SwapSide("BUY")
|
||||
)
|
||||
|
||||
type ClientV5 struct {
|
||||
httpClient *thirdparty.HTTPClient
|
||||
chainID uint64
|
||||
|
|
|
@ -24,21 +24,30 @@ type Transaction struct {
|
|||
}
|
||||
|
||||
func (c *ClientV5) BuildTransaction(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint, srcAmountWei *big.Int,
|
||||
destTokenAddress common.Address, destTokenDecimals uint, destAmountWei *big.Int,
|
||||
addressFrom common.Address, addressTo common.Address, priceRoute json.RawMessage) (Transaction, error) {
|
||||
destTokenAddress common.Address, destTokenDecimals uint, destAmountWei *big.Int, slippageBasisPoints uint,
|
||||
addressFrom common.Address, addressTo common.Address, priceRoute json.RawMessage, side SwapSide) (Transaction, error) {
|
||||
|
||||
params := map[string]interface{}{}
|
||||
params["srcToken"] = srcTokenAddress.Hex()
|
||||
params["srcDecimals"] = srcTokenDecimals
|
||||
params["srcAmount"] = srcAmountWei.String()
|
||||
params["destToken"] = destTokenAddress.Hex()
|
||||
params["destDecimals"] = destTokenDecimals
|
||||
// params["destAmount"] = destAmountWei.String()
|
||||
params["userAddress"] = addressFrom.Hex()
|
||||
// params["receiver"] = addressTo.Hex() // at this point paraswap doesn't allow swap and transfer transaction
|
||||
params["slippage"] = "500"
|
||||
params["priceRoute"] = priceRoute
|
||||
|
||||
if slippageBasisPoints > 0 {
|
||||
params["slippage"] = slippageBasisPoints
|
||||
if side == SellSide {
|
||||
params["srcAmount"] = srcAmountWei.String()
|
||||
} else {
|
||||
params["destAmount"] = destAmountWei.String()
|
||||
}
|
||||
} else {
|
||||
params["srcAmount"] = srcAmountWei.String()
|
||||
params["destAmount"] = destAmountWei.String()
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(transactionsURL, c.chainID)
|
||||
response, err := c.httpClient.DoPostRequest(ctx, url, params)
|
||||
if err != nil {
|
||||
|
|
|
@ -24,6 +24,7 @@ type Route struct {
|
|||
DestTokenAddress common.Address `json:"destToken"`
|
||||
DestTokenDecimals uint `json:"destDecimals"`
|
||||
RawPriceRoute json.RawMessage `json:"rawPriceRoute"`
|
||||
Side SwapSide `json:"side"`
|
||||
}
|
||||
|
||||
type PriceRouteResponse struct {
|
||||
|
@ -33,7 +34,7 @@ type PriceRouteResponse struct {
|
|||
|
||||
func (c *ClientV5) FetchPriceRoute(ctx context.Context, srcTokenAddress common.Address, srcTokenDecimals uint,
|
||||
destTokenAddress common.Address, destTokenDecimals uint, amountWei *big.Int, addressFrom common.Address,
|
||||
addressTo common.Address) (Route, error) {
|
||||
addressTo common.Address, side SwapSide) (Route, error) {
|
||||
|
||||
params := netUrl.Values{}
|
||||
params.Add("srcToken", srcTokenAddress.Hex())
|
||||
|
@ -44,7 +45,7 @@ func (c *ClientV5) FetchPriceRoute(ctx context.Context, srcTokenAddress common.A
|
|||
// params.Add("receiver", addressTo.Hex()) // at this point paraswap doesn't allow swap and transfer transaction
|
||||
params.Add("network", strconv.FormatUint(c.chainID, 10))
|
||||
params.Add("amount", amountWei.String())
|
||||
params.Add("side", "SELL")
|
||||
params.Add("side", string(side))
|
||||
|
||||
url := pricesURL
|
||||
response, err := c.httpClient.DoGetRequest(ctx, url, params)
|
||||
|
|
|
@ -108,6 +108,7 @@ func TestUnmarshallPriceRoute(t *testing.T) {
|
|||
DestAmount: &bigint.BigInt{Int: big.NewInt(1000000000000000000)},
|
||||
DestTokenAddress: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
||||
DestTokenDecimals: 18,
|
||||
Side: SellSide,
|
||||
RawPriceRoute: data,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue