feat(wallet)_: incorporate partner fees for L1 paraswap swaps
This commit is contained in:
parent
30fee0cfd3
commit
7c4b43b4d9
|
@ -3,6 +3,8 @@ package common
|
|||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type MultiTransactionIDType int64
|
||||
|
@ -28,6 +30,10 @@ const (
|
|||
BinanceTestChainID uint64 = 97 // obsolete?
|
||||
)
|
||||
|
||||
var (
|
||||
ZeroAddress = ethCommon.HexToAddress("0x0000000000000000000000000000000000000000")
|
||||
)
|
||||
|
||||
type ContractType byte
|
||||
|
||||
const (
|
||||
|
|
|
@ -33,11 +33,37 @@ type SwapParaswapProcessor struct {
|
|||
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 {
|
||||
defaultChainID := walletCommon.EthereumMainnet
|
||||
partnerAddress, partnerFeePcnt := getPartnerAddressAndFeePcnt(defaultChainID)
|
||||
|
||||
return &SwapParaswapProcessor{
|
||||
paraswapClient: paraswap.NewClientV5(walletCommon.EthereumMainnet),
|
||||
transactor: transactor,
|
||||
priceRoute: sync.Map{},
|
||||
paraswapClient: paraswap.NewClientV5(
|
||||
defaultChainID,
|
||||
partnerID,
|
||||
partnerAddress,
|
||||
partnerFeePcnt,
|
||||
),
|
||||
transactor: transactor,
|
||||
priceRoute: sync.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +101,11 @@ func (s *SwapParaswapProcessor) AvailableFor(params ProcessorInputParams) (bool,
|
|||
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
|
||||
searchForToToken := params.ToToken.Address == ZeroAddress
|
||||
|
@ -111,6 +141,23 @@ func (s *SwapParaswapProcessor) AvailableFor(params ProcessorInputParams) (bool,
|
|||
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) {
|
||||
return ZeroBigIntValue, ZeroBigIntValue, nil
|
||||
}
|
||||
|
@ -252,5 +299,8 @@ func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams)
|
|||
}
|
||||
priceRoute := priceRouteIns.(*paraswap.Route)
|
||||
|
||||
return priceRoute.DestAmount.Int, nil
|
||||
_, partnerFeePcnt := getPartnerAddressAndFeePcnt(params.FromChain.ChainID)
|
||||
destAmount, _ := calcReceivedAmountAndFee(priceRoute.DestAmount.Int, partnerFeePcnt)
|
||||
|
||||
return destAmount, nil
|
||||
}
|
||||
|
|
|
@ -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 := ¶swap.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: ¶ms.Network{ChainID: chainID},
|
||||
ToChain: ¶ms.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())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
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
|
||||
|
||||
|
@ -10,17 +13,35 @@ const (
|
|||
)
|
||||
|
||||
type ClientV5 struct {
|
||||
httpClient *thirdparty.HTTPClient
|
||||
chainID uint64
|
||||
httpClient *thirdparty.HTTPClient
|
||||
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{
|
||||
httpClient: thirdparty.NewHTTPClient(),
|
||||
chainID: chainID,
|
||||
httpClient: thirdparty.NewHTTPClient(),
|
||||
chainID: chainID,
|
||||
partnerID: partnerID,
|
||||
partnerAddress: partnerAddress,
|
||||
partnerFeePcnt: partnerFeePcnt,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientV5) SetChainID(chainID uint64) {
|
||||
c.chainID = chainID
|
||||
}
|
||||
|
||||
func (c *ClientV5) SetPartnerAddress(partnerAddress common.Address) {
|
||||
c.partnerAddress = partnerAddress
|
||||
}
|
||||
|
||||
func (c *ClientV5) SetPartnerFeePcnt(partnerFeePcnt float64) {
|
||||
c.partnerFeePcnt = partnerFeePcnt
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||
)
|
||||
|
||||
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["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)
|
||||
response, err := c.httpClient.DoPostRequest(ctx, url, params)
|
||||
|
|
|
@ -46,6 +46,7 @@ func (c *ClientV5) FetchPriceRoute(ctx context.Context, srcTokenAddress common.A
|
|||
params.Add("network", strconv.FormatUint(c.chainID, 10))
|
||||
params.Add("amount", amountWei.String())
|
||||
params.Add("side", string(side))
|
||||
params.Add("partner", c.partnerID)
|
||||
|
||||
url := pricesURL
|
||||
response, err := c.httpClient.DoGetRequest(ctx, url, params)
|
||||
|
|
Loading…
Reference in New Issue