feat(wallet)_: incorporate partner fees for L1 paraswap swaps

This commit is contained in:
Dario Gabriel Lipicar 2024-07-18 17:14:42 -03:00 committed by dlipicar
parent 30fee0cfd3
commit 7c4b43b4d9
6 changed files with 167 additions and 11 deletions

View File

@ -3,6 +3,8 @@ package common
import ( import (
"strconv" "strconv"
"time" "time"
ethCommon "github.com/ethereum/go-ethereum/common"
) )
type MultiTransactionIDType int64 type MultiTransactionIDType int64
@ -28,6 +30,10 @@ const (
BinanceTestChainID uint64 = 97 // obsolete? BinanceTestChainID uint64 = 97 // obsolete?
) )
var (
ZeroAddress = ethCommon.HexToAddress("0x0000000000000000000000000000000000000000")
)
type ContractType byte type ContractType byte
const ( const (

View File

@ -33,11 +33,37 @@ type SwapParaswapProcessor struct {
priceRoute sync.Map // [fromChainName-toChainName-fromTokenSymbol-toTokenSymbol, paraswap.Route] priceRoute sync.Map // [fromChainName-toChainName-fromTokenSymbol-toTokenSymbol, paraswap.Route]
} }
const (
partnerID = "status.app"
)
func getPartnerAddressAndFeePcnt(chainID uint64) (common.Address, float64) {
const partnerFeePcnt = 0.7
switch chainID {
case walletCommon.EthereumMainnet:
return common.HexToAddress("0xd9abc564bfabefa88a6C2723d78124579600F568"), partnerFeePcnt
case walletCommon.OptimismMainnet:
return common.HexToAddress("0xE9B59dC0b30cd4646430c25de0111D651c395775"), partnerFeePcnt
case walletCommon.ArbitrumMainnet:
return common.HexToAddress("0x9a8278e856C0B191B9daa2d7DD1f7B28268E4DA2"), partnerFeePcnt
}
return common.Address{}, 0
}
func NewSwapParaswapProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *walletToken.Manager) *SwapParaswapProcessor { func NewSwapParaswapProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *walletToken.Manager) *SwapParaswapProcessor {
defaultChainID := walletCommon.EthereumMainnet
partnerAddress, partnerFeePcnt := getPartnerAddressAndFeePcnt(defaultChainID)
return &SwapParaswapProcessor{ return &SwapParaswapProcessor{
paraswapClient: paraswap.NewClientV5(walletCommon.EthereumMainnet), paraswapClient: paraswap.NewClientV5(
transactor: transactor, defaultChainID,
priceRoute: sync.Map{}, partnerID,
partnerAddress,
partnerFeePcnt,
),
transactor: transactor,
priceRoute: sync.Map{},
} }
} }
@ -75,7 +101,11 @@ func (s *SwapParaswapProcessor) AvailableFor(params ProcessorInputParams) (bool,
return false, ErrFromAndToTokensMustBeDifferent return false, ErrFromAndToTokensMustBeDifferent
} }
s.paraswapClient.SetChainID(params.FromChain.ChainID) chainID := params.FromChain.ChainID
partnerAddress, partnerFeePcnt := getPartnerAddressAndFeePcnt(chainID)
s.paraswapClient.SetChainID(chainID)
s.paraswapClient.SetPartnerAddress(partnerAddress)
s.paraswapClient.SetPartnerFeePcnt(partnerFeePcnt)
searchForToken := params.FromToken.Address == ZeroAddress searchForToken := params.FromToken.Address == ZeroAddress
searchForToToken := params.ToToken.Address == ZeroAddress searchForToToken := params.ToToken.Address == ZeroAddress
@ -111,6 +141,23 @@ func (s *SwapParaswapProcessor) AvailableFor(params ProcessorInputParams) (bool,
return true, nil return true, nil
} }
func calcReceivedAmountAndFee(baseDestAmount *big.Int, feePcnt float64) (destAmount *big.Int, destFee *big.Int) {
destAmount = new(big.Int).Set(baseDestAmount)
destFee = new(big.Int).SetUint64(0)
if feePcnt > 0 {
baseDestAmountFloat := new(big.Float).SetInt(baseDestAmount)
feePcntFloat := big.NewFloat(feePcnt / 100.0)
destFeeFloat := new(big.Float).Set(baseDestAmountFloat)
destFeeFloat = destFeeFloat.Mul(destFeeFloat, feePcntFloat)
destFeeFloat.Int(destFee)
destAmount = destAmount.Sub(destAmount, destFee)
}
return
}
func (s *SwapParaswapProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { func (s *SwapParaswapProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) {
return ZeroBigIntValue, ZeroBigIntValue, nil return ZeroBigIntValue, ZeroBigIntValue, nil
} }
@ -252,5 +299,8 @@ func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams)
} }
priceRoute := priceRouteIns.(*paraswap.Route) priceRoute := priceRouteIns.(*paraswap.Route)
return priceRoute.DestAmount.Int, nil _, partnerFeePcnt := getPartnerAddressAndFeePcnt(params.FromChain.ChainID)
destAmount, _ := calcReceivedAmountAndFee(priceRoute.DestAmount.Int, partnerFeePcnt)
return destAmount, nil
} }

View File

@ -0,0 +1,71 @@
package pathprocessor
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"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"
"github.com/status-im/status-go/services/wallet/token"
"github.com/stretchr/testify/require"
)
func TestParaswapWithPartnerFee(t *testing.T) {
testPriceRoute := &paraswap.Route{
GasCost: &bigint.BigInt{Int: big.NewInt(500)},
SrcAmount: &bigint.BigInt{Int: big.NewInt(1000)},
SrcTokenAddress: common.HexToAddress("0x123"),
SrcTokenDecimals: 18,
DestAmount: &bigint.BigInt{Int: big.NewInt(2000)},
DestTokenAddress: common.HexToAddress("0x465"),
DestTokenDecimals: 6,
Side: paraswap.SellSide,
}
processor := NewSwapParaswapProcessor(nil, nil, nil)
fromToken := token.Token{
Symbol: EthSymbol,
}
toToken := token.Token{
Symbol: UsdcSymbol,
}
chainIDs := []uint64{walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet, walletCommon.OptimismMainnet, walletCommon.UnknownChainID}
for _, chainID := range chainIDs {
key := makeKey(chainID, chainID, fromToken.Symbol, toToken.Symbol)
processor.priceRoute.Store(key, testPriceRoute)
testInputParams := ProcessorInputParams{
FromChain: &params.Network{ChainID: chainID},
ToChain: &params.Network{ChainID: chainID},
FromToken: &fromToken,
ToToken: &toToken,
}
partnerAddress, partnerFeePcnt := getPartnerAddressAndFeePcnt(chainID)
if partnerAddress != walletCommon.ZeroAddress {
require.Greater(t, partnerFeePcnt, 0.0)
expectedFee := uint64(float64(testPriceRoute.DestAmount.Uint64()) * partnerFeePcnt / 100.0)
expectedDestAmount := testPriceRoute.DestAmount.Uint64() - expectedFee
amountOut, err := processor.CalculateAmountOut(testInputParams)
require.NoError(t, err)
require.NotNil(t, amountOut)
require.InEpsilon(t, expectedDestAmount, amountOut.Uint64(), 2.0)
} else {
require.Equal(t, 0.0, partnerFeePcnt)
amountOut, err := processor.CalculateAmountOut(testInputParams)
require.NoError(t, err)
require.NotNil(t, amountOut)
require.Equal(t, testPriceRoute.DestAmount.Uint64(), amountOut.Uint64())
}
}
}

View File

@ -1,6 +1,9 @@
package paraswap package paraswap
import "github.com/status-im/status-go/services/wallet/thirdparty" import (
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
)
type SwapSide string type SwapSide string
@ -10,17 +13,35 @@ const (
) )
type ClientV5 struct { type ClientV5 struct {
httpClient *thirdparty.HTTPClient httpClient *thirdparty.HTTPClient
chainID uint64 chainID uint64
partnerID string
partnerAddress common.Address
partnerFeePcnt float64
} }
func NewClientV5(chainID uint64) *ClientV5 { func NewClientV5(
chainID uint64,
partnerID string,
partnerAddress common.Address,
partnerFeePcnt float64) *ClientV5 {
return &ClientV5{ return &ClientV5{
httpClient: thirdparty.NewHTTPClient(), httpClient: thirdparty.NewHTTPClient(),
chainID: chainID, chainID: chainID,
partnerID: partnerID,
partnerAddress: partnerAddress,
partnerFeePcnt: partnerFeePcnt,
} }
} }
func (c *ClientV5) SetChainID(chainID uint64) { func (c *ClientV5) SetChainID(chainID uint64) {
c.chainID = chainID c.chainID = chainID
} }
func (c *ClientV5) SetPartnerAddress(partnerAddress common.Address) {
c.partnerAddress = partnerAddress
}
func (c *ClientV5) SetPartnerFeePcnt(partnerFeePcnt float64) {
c.partnerFeePcnt = partnerFeePcnt
}

View File

@ -8,6 +8,8 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
walletCommon "github.com/status-im/status-go/services/wallet/common"
) )
const transactionsURL = "https://apiv5.paraswap.io/transactions/%d" const transactionsURL = "https://apiv5.paraswap.io/transactions/%d"
@ -47,6 +49,11 @@ func (c *ClientV5) BuildTransaction(ctx context.Context, srcTokenAddress common.
params["srcAmount"] = srcAmountWei.String() params["srcAmount"] = srcAmountWei.String()
params["destAmount"] = destAmountWei.String() params["destAmount"] = destAmountWei.String()
} }
params["partner"] = c.partnerID
if c.partnerAddress != walletCommon.ZeroAddress && c.partnerFeePcnt > 0 {
params["partnerAddress"] = c.partnerAddress.Hex()
params["partnerFeeBps"] = uint(c.partnerFeePcnt * 100)
}
url := fmt.Sprintf(transactionsURL, c.chainID) url := fmt.Sprintf(transactionsURL, c.chainID)
response, err := c.httpClient.DoPostRequest(ctx, url, params) response, err := c.httpClient.DoPostRequest(ctx, url, params)

View File

@ -46,6 +46,7 @@ func (c *ClientV5) FetchPriceRoute(ctx context.Context, srcTokenAddress common.A
params.Add("network", strconv.FormatUint(c.chainID, 10)) params.Add("network", strconv.FormatUint(c.chainID, 10))
params.Add("amount", amountWei.String()) params.Add("amount", amountWei.String())
params.Add("side", string(side)) params.Add("side", string(side))
params.Add("partner", c.partnerID)
url := pricesURL url := pricesURL
response, err := c.httpClient.DoGetRequest(ctx, url, params) response, err := c.httpClient.DoGetRequest(ctx, url, params)