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 (
|
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 (
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue