chore_: calculating multi tx candidates improvements
- router logic splitted into two more logical functions - locked amount validation improved - hop and swap processors cached data kept per from/to chain and from/to token - Clear function which clears the local cache is added - process of evaluating `amountToSend` if more than a single network is locked is improved - optimized params for require approval function
This commit is contained in:
parent
d1f8064437
commit
68464d949c
|
@ -1,6 +1,7 @@
|
|||
package pathprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -17,3 +18,10 @@ func getSigner(chainID uint64, from types.Address, verifiedAccount *account.Sele
|
|||
return ethTypes.SignTx(tx, s, verifiedAccount.AccountKey.PrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
func makeKey(fromChain, toChain uint64, fromTokenSymbol, toTokenSymbol string) string {
|
||||
if fromTokenSymbol != "" || toTokenSymbol != "" {
|
||||
return fmt.Sprintf("%d-%d-%s-%s", fromChain, toChain, fromTokenSymbol, toTokenSymbol)
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", fromChain, toChain)
|
||||
}
|
||||
|
|
|
@ -13,10 +13,13 @@ var (
|
|||
|
||||
const (
|
||||
IncreaseEstimatedGasFactor = 1.1
|
||||
SevenDaysInSeconds = 60 * 60 * 24 * 7
|
||||
|
||||
EthSymbol = "ETH"
|
||||
SntSymbol = "SNT"
|
||||
SttSymbol = "STT"
|
||||
EthSymbol = "ETH"
|
||||
SntSymbol = "SNT"
|
||||
SttSymbol = "STT"
|
||||
UsdcSymbol = "USDC"
|
||||
HopSymbol = "HOP"
|
||||
|
||||
ProcessorTransferName = "Transfer"
|
||||
ProcessorBridgeHopName = "Hop"
|
||||
|
|
|
@ -13,22 +13,33 @@ import (
|
|||
)
|
||||
|
||||
type PathProcessor interface {
|
||||
// returns the name of the bridge
|
||||
// Name returns the name of the bridge
|
||||
Name() string
|
||||
// checks if the bridge is available for the given networks/tokens
|
||||
// AvailableFor checks if the bridge is available for the given networks/tokens
|
||||
AvailableFor(params ProcessorInputParams) (bool, error)
|
||||
// calculates the fees for the bridge and returns the amount BonderFee and TokenFee (used for bridges)
|
||||
// CalculateFees calculates the fees for the bridge and returns the amount BonderFee and TokenFee (used for bridges)
|
||||
CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error)
|
||||
// Pack the method for sending tx and method call's data
|
||||
// PackTxInputData packs tx for sending
|
||||
PackTxInputData(params ProcessorInputParams) ([]byte, error)
|
||||
// EstimateGas estimates the gas
|
||||
EstimateGas(params ProcessorInputParams) (uint64, error)
|
||||
// CalculateAmountOut calculates the amount out
|
||||
CalculateAmountOut(params ProcessorInputParams) (*big.Int, error)
|
||||
// Send sends the tx
|
||||
Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
|
||||
// GetContractAddress returns the contract address
|
||||
GetContractAddress(params ProcessorInputParams) (common.Address, error)
|
||||
// BuildTransaction builds the transaction based on MultipathProcessorTxArgs
|
||||
BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error)
|
||||
// BuildTx builds the transaction based on ProcessorInputParams
|
||||
BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error)
|
||||
}
|
||||
|
||||
type PathProcessorClearable interface {
|
||||
// Clear clears the local cache
|
||||
Clear()
|
||||
}
|
||||
|
||||
type ProcessorInputParams struct {
|
||||
FromChain *params.Network
|
||||
ToChain *params.Network
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"math/big"
|
||||
netUrl "net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
|
@ -36,14 +37,10 @@ import (
|
|||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
const (
|
||||
SevenDaysInSeconds = 604800
|
||||
hopSymbol = "HOP"
|
||||
)
|
||||
|
||||
type HopBridgeTxArgs struct {
|
||||
transactions.SendTxArgs
|
||||
ChainID uint64 `json:"chainId"`
|
||||
ChainIDTo uint64 `json:"chainIdTo"`
|
||||
Symbol string `json:"symbol"`
|
||||
Recipient common.Address `json:"recipient"`
|
||||
Amount *hexutil.Big `json:"amount"`
|
||||
|
@ -84,6 +81,8 @@ func (bf *BonderFee) UnmarshalJSON(data []byte) error {
|
|||
bf.AmountIn = &bigint.BigInt{Int: new(big.Int)}
|
||||
bf.AmountIn.SetString(aux.AmountIn, 10)
|
||||
|
||||
bf.Slippage = aux.Slippage
|
||||
|
||||
bf.AmountOutMin = &bigint.BigInt{Int: new(big.Int)}
|
||||
bf.AmountOutMin.SetString(aux.AmountOutMin, 10)
|
||||
|
||||
|
@ -110,7 +109,7 @@ type HopBridgeProcessor struct {
|
|||
httpClient *thirdparty.HTTPClient
|
||||
tokenManager *token.Manager
|
||||
contractMaker *contracts.ContractMaker
|
||||
bonderFee *BonderFee
|
||||
bonderFee *sync.Map // [fromChainName-toChainName]BonderFee
|
||||
}
|
||||
|
||||
func NewHopBridgeProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *token.Manager) *HopBridgeProcessor {
|
||||
|
@ -119,6 +118,7 @@ func NewHopBridgeProcessor(rpcClient *rpc.Client, transactor transactions.Transa
|
|||
httpClient: thirdparty.NewHTTPClient(),
|
||||
transactor: transactor,
|
||||
tokenManager: tokenManager,
|
||||
bonderFee: &sync.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,14 @@ func (h *HopBridgeProcessor) Name() string {
|
|||
return ProcessorBridgeHopName
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) Clear() {
|
||||
h.bonderFee = &sync.Map{}
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) AvailableFor(params ProcessorInputParams) (bool, error) {
|
||||
if params.FromChain.ChainID == params.ToChain.ChainID || params.ToToken != nil {
|
||||
return false, nil
|
||||
}
|
||||
// We chcek if the contract is available on the network for the token
|
||||
_, err := h.GetContractAddress(params)
|
||||
// toToken is not nil only if the send type is Swap
|
||||
|
@ -141,7 +148,7 @@ func (c *HopBridgeProcessor) getAppropriateABI(contractType string, chainID uint
|
|||
if token.IsNative() {
|
||||
return abi.JSON(strings.NewReader(hopL1EthBridge.HopL1EthBridgeABI))
|
||||
}
|
||||
if token.Symbol == hopSymbol {
|
||||
if token.Symbol == HopSymbol {
|
||||
return abi.JSON(strings.NewReader(hopL1HopBridge.HopL1HopBridgeABI))
|
||||
}
|
||||
return abi.JSON(strings.NewReader(hopL1Erc20Bridge.HopL1Erc20BridgeABI))
|
||||
|
@ -178,17 +185,24 @@ func (h *HopBridgeProcessor) packTxInputDataInternally(params ProcessorInputPara
|
|||
return []byte{}, err
|
||||
}
|
||||
|
||||
bonderKey := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, "", "")
|
||||
bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no bonder fee found for %s", bonderKey)
|
||||
}
|
||||
bonderFee := bonderFeeIns.(*BonderFee)
|
||||
|
||||
switch contractType {
|
||||
case hop.CctpL1Bridge:
|
||||
return h.packCctpL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr)
|
||||
return h.packCctpL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
|
||||
case hop.L1Bridge:
|
||||
return h.packL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr)
|
||||
return h.packL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
|
||||
case hop.L2AmmWrapper:
|
||||
return h.packL2AmmWrapperTx(abi, params.ToChain.ChainID, params.ToAddr)
|
||||
return h.packL2AmmWrapperTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
|
||||
case hop.CctpL2Bridge:
|
||||
return h.packCctpL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr)
|
||||
return h.packCctpL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
|
||||
case hop.L2Bridge:
|
||||
return h.packL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr)
|
||||
return h.packL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
|
||||
}
|
||||
|
||||
return []byte{}, errors.New("contract type not supported yet")
|
||||
|
@ -296,20 +310,27 @@ func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, sig
|
|||
return tx, err
|
||||
}
|
||||
|
||||
bonderKey := makeKey(sendArgs.HopTx.ChainID, sendArgs.HopTx.ChainIDTo, "", "")
|
||||
bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no bonder fee found for %s", bonderKey)
|
||||
}
|
||||
bonderFee := bonderFeeIns.(*BonderFee)
|
||||
|
||||
switch contractType {
|
||||
case hop.CctpL1Bridge:
|
||||
return h.sendCctpL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts)
|
||||
return h.sendCctpL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, bonderFee)
|
||||
case hop.L1Bridge:
|
||||
return h.sendL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, token)
|
||||
return h.sendL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, token, bonderFee)
|
||||
case hop.L2AmmWrapper:
|
||||
return h.sendL2AmmWrapperTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts)
|
||||
return h.sendL2AmmWrapperTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, bonderFee)
|
||||
case hop.CctpL2Bridge:
|
||||
return h.sendCctpL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts)
|
||||
return h.sendCctpL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, bonderFee)
|
||||
case hop.L2Bridge:
|
||||
return h.sendL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts)
|
||||
return h.sendL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainID, sendArgs.HopTx.Recipient, txOpts, bonderFee)
|
||||
}
|
||||
|
||||
return tx, err
|
||||
return tx, fmt.Errorf("contract type not supported yet")
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
|
||||
|
@ -354,33 +375,42 @@ func (h *HopBridgeProcessor) CalculateFees(params ProcessorInputParams) (*big.In
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
h.bonderFee = &BonderFee{}
|
||||
err = json.Unmarshal(response, h.bonderFee)
|
||||
bonderFee := &BonderFee{}
|
||||
err = json.Unmarshal(response, bonderFee)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
h.bonderFee.Store(bonderKey, bonderFee)
|
||||
|
||||
// Remove token fee from bonder fee as said here:
|
||||
// https://docs.hop.exchange/v/developer-docs/api/api#get-v1-quote
|
||||
// `bonderFee` - The suggested bonder fee for the amount in. The bonder fee also includes the cost of the destination transaction fee.
|
||||
tokenFee := ZeroBigIntValue //new(big.Int).Sub(h.bonderFee.AmountIn.Int, h.bonderFee.EstimatedRecieved.Int)
|
||||
|
||||
return h.bonderFee.BonderFee.Int, tokenFee, nil
|
||||
return bonderFee.BonderFee.Int, tokenFee, nil
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {
|
||||
return h.bonderFee.EstimatedRecieved.Int, nil
|
||||
bonderKey := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, "", "")
|
||||
bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no bonder fee found for %s", bonderKey)
|
||||
}
|
||||
bonderFee := bonderFeeIns.(*BonderFee)
|
||||
return bonderFee.EstimatedRecieved.Int, nil
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) packCctpL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address) ([]byte, error) {
|
||||
func (h *HopBridgeProcessor) packCctpL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
|
||||
return abi.Pack("send",
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int)
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int)
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) sendCctpL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64, to common.Address, txOpts *bind.TransactOpts) (tx *ethTypes.Transaction, err error) {
|
||||
func (h *HopBridgeProcessor) sendCctpL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
|
||||
to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
|
||||
contractInstance, err := hopL1CctpImplementation.NewHopL1CctpImplementation(
|
||||
contractAddress,
|
||||
ethClient,
|
||||
|
@ -393,22 +423,23 @@ func (h *HopBridgeProcessor) sendCctpL1BridgeTx(contractAddress common.Address,
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int)
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int)
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) packL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address) ([]byte, error) {
|
||||
func (h *HopBridgeProcessor) packL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
|
||||
return abi.Pack("sendToL2",
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
common.Address{},
|
||||
ZeroBigIntValue)
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64, to common.Address, txOpts *bind.TransactOpts, token *token.Token) (tx *ethTypes.Transaction, err error) {
|
||||
func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
|
||||
to common.Address, txOpts *bind.TransactOpts, token *token.Token, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
|
||||
if token.IsNative() {
|
||||
contractInstance, err := hopL1EthBridge.NewHopL1EthBridge(
|
||||
contractAddress,
|
||||
|
@ -422,14 +453,14 @@ func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethC
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
common.Address{},
|
||||
ZeroBigIntValue)
|
||||
}
|
||||
|
||||
if token.Symbol == hopSymbol {
|
||||
if token.Symbol == HopSymbol {
|
||||
contractInstance, err := hopL1HopBridge.NewHopL1HopBridge(
|
||||
contractAddress,
|
||||
ethClient,
|
||||
|
@ -442,9 +473,9 @@ func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethC
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
common.Address{},
|
||||
ZeroBigIntValue)
|
||||
}
|
||||
|
@ -461,23 +492,24 @@ func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethC
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
common.Address{},
|
||||
ZeroBigIntValue)
|
||||
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) packCctpL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address) ([]byte, error) {
|
||||
func (h *HopBridgeProcessor) packCctpL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
|
||||
return abi.Pack("send",
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int)
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int)
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) sendCctpL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64, to common.Address, txOpts *bind.TransactOpts) (tx *ethTypes.Transaction, err error) {
|
||||
func (h *HopBridgeProcessor) sendCctpL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
|
||||
to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
|
||||
contractInstance, err := hopL2CctpImplementation.NewHopL2CctpImplementation(
|
||||
contractAddress,
|
||||
ethClient,
|
||||
|
@ -490,24 +522,25 @@ func (h *HopBridgeProcessor) sendCctpL2BridgeTx(contractAddress common.Address,
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) packL2AmmWrapperTx(abi abi.ABI, toChainID uint64, to common.Address) ([]byte, error) {
|
||||
func (h *HopBridgeProcessor) packL2AmmWrapperTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
|
||||
return abi.Pack("swapAndSend",
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
h.bonderFee.DestinationAmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.DestinationDeadline))
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
bonderFee.DestinationAmountOutMin.Int,
|
||||
big.NewInt(bonderFee.DestinationDeadline))
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) sendL2AmmWrapperTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64, to common.Address, txOpts *bind.TransactOpts) (tx *ethTypes.Transaction, err error) {
|
||||
func (h *HopBridgeProcessor) sendL2AmmWrapperTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
|
||||
to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
|
||||
contractInstance, err := hopL2AmmWrapper.NewHopL2AmmWrapper(
|
||||
contractAddress,
|
||||
ethClient,
|
||||
|
@ -520,25 +553,26 @@ func (h *HopBridgeProcessor) sendL2AmmWrapperTx(contractAddress common.Address,
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline),
|
||||
h.bonderFee.DestinationAmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.DestinationDeadline))
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline),
|
||||
bonderFee.DestinationAmountOutMin.Int,
|
||||
big.NewInt(bonderFee.DestinationDeadline))
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) packL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address) ([]byte, error) {
|
||||
func (h *HopBridgeProcessor) packL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
|
||||
return abi.Pack("send",
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline))
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline))
|
||||
}
|
||||
|
||||
func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64, to common.Address, txOpts *bind.TransactOpts) (tx *ethTypes.Transaction, err error) {
|
||||
func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
|
||||
to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
|
||||
fromChainID := ethClient.NetworkID()
|
||||
if fromChainID == walletCommon.OptimismMainnet ||
|
||||
fromChainID == walletCommon.OptimismSepolia {
|
||||
|
@ -554,10 +588,10 @@ func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethC
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline))
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline))
|
||||
}
|
||||
if fromChainID == walletCommon.ArbitrumMainnet ||
|
||||
fromChainID == walletCommon.ArbitrumSepolia {
|
||||
|
@ -573,10 +607,10 @@ func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethC
|
|||
txOpts,
|
||||
big.NewInt(int64(toChainID)),
|
||||
to,
|
||||
h.bonderFee.AmountIn.Int,
|
||||
h.bonderFee.BonderFee.Int,
|
||||
h.bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(h.bonderFee.Deadline))
|
||||
bonderFee.AmountIn.Int,
|
||||
bonderFee.BonderFee.Int,
|
||||
bonderFee.AmountOutMin.Int,
|
||||
big.NewInt(bonderFee.Deadline))
|
||||
}
|
||||
|
||||
return tx, errors.New("tx for chain not supported yet")
|
||||
|
|
|
@ -3,8 +3,10 @@ package pathprocessor
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
@ -21,19 +23,23 @@ import (
|
|||
type SwapParaswapTxArgs struct {
|
||||
transactions.SendTxArgs
|
||||
ChainID uint64 `json:"chainId"`
|
||||
ChainIDTo uint64 `json:"chainIdFrom"`
|
||||
TokenIDFrom string `json:"tokenIdFrom"`
|
||||
TokenIDTo string `json:"tokenIdTo"`
|
||||
SlippagePercentage float32 `json:"slippagePercentage"`
|
||||
}
|
||||
|
||||
type SwapParaswapProcessor struct {
|
||||
paraswapClient *paraswap.ClientV5
|
||||
priceRoute paraswap.Route
|
||||
transactor transactions.TransactorIface
|
||||
priceRoute sync.Map // [fromChainName-toChainName-fromTokenSymbol-toTokenSymbol, paraswap.Route]
|
||||
}
|
||||
|
||||
func NewSwapParaswapProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *walletToken.Manager) *SwapParaswapProcessor {
|
||||
return &SwapParaswapProcessor{
|
||||
paraswapClient: paraswap.NewClientV5(walletCommon.EthereumMainnet),
|
||||
transactor: transactor,
|
||||
priceRoute: sync.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +47,10 @@ func (s *SwapParaswapProcessor) Name() string {
|
|||
return ProcessorSwapParaswapName
|
||||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) Clear() {
|
||||
s.priceRoute = sync.Map{}
|
||||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) AvailableFor(params ProcessorInputParams) (bool, error) {
|
||||
if params.FromToken == nil || params.ToToken == nil {
|
||||
return false, errors.New("token and toToken cannot be nil")
|
||||
|
@ -107,7 +117,8 @@ func (s *SwapParaswapProcessor) EstimateGas(params ProcessorInputParams) (uint64
|
|||
return 0, err
|
||||
}
|
||||
|
||||
s.priceRoute = priceRoute
|
||||
key := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, params.FromToken.Symbol, params.ToToken.Symbol)
|
||||
s.priceRoute.Store(key, &priceRoute)
|
||||
|
||||
return priceRoute.GasCost.Uint64(), nil
|
||||
}
|
||||
|
@ -146,10 +157,17 @@ 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, slippageBP,
|
||||
key := makeKey(sendArgs.SwapTx.ChainID, sendArgs.SwapTx.ChainIDTo, sendArgs.SwapTx.TokenIDFrom, sendArgs.SwapTx.TokenIDTo)
|
||||
priceRouteIns, ok := s.priceRoute.Load(key)
|
||||
if !ok {
|
||||
return errors.New("price route not found")
|
||||
}
|
||||
priceRoute := priceRouteIns.(*paraswap.Route)
|
||||
|
||||
tx, err := s.paraswapClient.BuildTransaction(context.Background(), priceRoute.SrcTokenAddress, priceRoute.SrcTokenDecimals, priceRoute.SrcAmount.Int,
|
||||
priceRoute.DestTokenAddress, priceRoute.DestTokenDecimals, priceRoute.DestAmount.Int, slippageBP,
|
||||
common.Address(sendArgs.SwapTx.From), common.Address(*sendArgs.SwapTx.To),
|
||||
s.priceRoute.RawPriceRoute, s.priceRoute.Side)
|
||||
priceRoute.RawPriceRoute, priceRoute.Side)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -213,5 +231,12 @@ func (s *SwapParaswapProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifie
|
|||
}
|
||||
|
||||
func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {
|
||||
return s.priceRoute.DestAmount.Int, nil
|
||||
key := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, params.FromToken.Symbol, params.ToToken.Symbol)
|
||||
priceRouteIns, ok := s.priceRoute.Load(key)
|
||||
if !ok {
|
||||
return nil, errors.New("price route not found")
|
||||
}
|
||||
priceRoute := priceRouteIns.(*paraswap.Route)
|
||||
|
||||
return priceRoute.DestAmount.Int, nil
|
||||
}
|
||||
|
|
|
@ -350,21 +350,22 @@ type Router struct {
|
|||
featureFlags *protocolCommon.FeatureFlags
|
||||
}
|
||||
|
||||
func (r *Router) requireApproval(ctx context.Context, sendType SendType, approvalContractAddress *common.Address, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) (
|
||||
func (r *Router) requireApproval(ctx context.Context, sendType SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) (
|
||||
bool, *big.Int, uint64, uint64, error) {
|
||||
if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() || sendType.IsStickersTransfer() {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
if token.IsNative() {
|
||||
if params.FromToken.IsNative() {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
contract, err := contractMaker.NewERC20(network.ChainID, token.Address)
|
||||
contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
@ -375,17 +376,17 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, approva
|
|||
|
||||
allowance, err := contract.Allowance(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, account, *approvalContractAddress)
|
||||
}, params.FromAddr, *approvalContractAddress)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
if allowance.Cmp(amountIn) >= 0 {
|
||||
if allowance.Cmp(params.AmountIn) >= 0 {
|
||||
return false, nil, 0, 0, nil
|
||||
}
|
||||
|
||||
ethClient, err := r.rpcClient.EthClient(network.ChainID)
|
||||
ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
@ -395,14 +396,14 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, approva
|
|||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
data, err := erc20ABI.Pack("approve", approvalContractAddress, amountIn)
|
||||
data, err := erc20ABI.Pack("approve", approvalContractAddress, params.AmountIn)
|
||||
if err != nil {
|
||||
return false, nil, 0, 0, err
|
||||
}
|
||||
|
||||
estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||
From: account,
|
||||
To: &token.Address,
|
||||
From: params.FromAddr,
|
||||
To: ¶ms.FromToken.Address,
|
||||
Value: pathprocessor.ZeroBigIntValue,
|
||||
Data: data,
|
||||
})
|
||||
|
@ -412,7 +413,7 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, approva
|
|||
|
||||
// fetching l1 fee
|
||||
var l1Fee uint64
|
||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(network.ChainID)
|
||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(params.FromChain.ChainID)
|
||||
if err == nil {
|
||||
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
||||
if err != nil {
|
||||
|
@ -425,7 +426,7 @@ func (r *Router) requireApproval(ctx context.Context, sendType SendType, approva
|
|||
l1Fee = l1FeeResult.Uint64()
|
||||
}
|
||||
|
||||
return true, amountIn, estimate, l1Fee, nil
|
||||
return true, params.AmountIn, estimate, l1Fee, nil
|
||||
}
|
||||
|
||||
func (r *Router) getBalance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||
|
@ -636,7 +637,7 @@ func (r *Router) SuggestedRoutes(
|
|||
if err != nil {
|
||||
continue
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, sendType, &approvalContractAddress, addrFrom, network, token, amountIn)
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, sendType, &approvalContractAddress, processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -107,6 +107,10 @@ func (s SendType) needL1Fee() bool {
|
|||
func (s SendType) canUseProcessor(p pathprocessor.PathProcessor) bool {
|
||||
pathProcessorName := p.Name()
|
||||
switch s {
|
||||
case Transfer:
|
||||
return pathProcessorName == pathprocessor.ProcessorTransferName ||
|
||||
pathProcessorName == pathprocessor.ProcessorBridgeHopName ||
|
||||
pathProcessorName == pathprocessor.ProcessorBridgeCelerName
|
||||
case Bridge:
|
||||
return pathProcessorName == pathprocessor.ProcessorBridgeHopName ||
|
||||
pathProcessorName == pathprocessor.ProcessorBridgeCelerName
|
||||
|
|
|
@ -220,8 +220,13 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl
|
|||
for _, path := range route {
|
||||
tokenDenominator := big.NewFloat(math.Pow(10, float64(path.FromToken.Decimals)))
|
||||
|
||||
path.requiredTokenBalance = new(big.Int).Set(path.AmountIn.ToInt())
|
||||
path.requiredTokenBalance = big.NewInt(0)
|
||||
path.requiredNativeBalance = big.NewInt(0)
|
||||
if path.FromToken.IsNative() {
|
||||
path.requiredNativeBalance.Add(path.requiredNativeBalance, path.AmountIn.ToInt())
|
||||
} else {
|
||||
path.requiredTokenBalance.Add(path.requiredTokenBalance, path.AmountIn.ToInt())
|
||||
}
|
||||
|
||||
// ecaluate the cost of the path
|
||||
pathCost := big.NewFloat(0)
|
||||
|
@ -237,7 +242,11 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl
|
|||
}
|
||||
|
||||
if path.TxBonderFees != nil && path.TxBonderFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 {
|
||||
path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxBonderFees.ToInt())
|
||||
if path.FromToken.IsNative() {
|
||||
path.requiredNativeBalance.Add(path.requiredNativeBalance, path.TxBonderFees.ToInt())
|
||||
} else {
|
||||
path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxBonderFees.ToInt())
|
||||
}
|
||||
pathCost.Add(pathCost, new(big.Float).Mul(
|
||||
new(big.Float).Quo(new(big.Float).SetInt(path.TxBonderFees.ToInt()), tokenDenominator),
|
||||
new(big.Float).SetFloat64(tokenPrice)))
|
||||
|
@ -253,7 +262,11 @@ func findBestV2(routes [][]*PathV2, tokenPrice float64, nativeChainTokenPrice fl
|
|||
}
|
||||
|
||||
if path.TxTokenFees != nil && path.TxTokenFees.ToInt().Cmp(pathprocessor.ZeroBigIntValue) > 0 && path.FromToken != nil {
|
||||
path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxTokenFees.ToInt())
|
||||
if path.FromToken.IsNative() {
|
||||
path.requiredNativeBalance.Add(path.requiredNativeBalance, path.TxTokenFees.ToInt())
|
||||
} else {
|
||||
path.requiredTokenBalance.Add(path.requiredTokenBalance, path.TxTokenFees.ToInt())
|
||||
}
|
||||
pathCost.Add(pathCost, new(big.Float).Mul(
|
||||
new(big.Float).Quo(new(big.Float).SetInt(path.TxTokenFees.ToInt()), tokenDenominator),
|
||||
new(big.Float).SetFloat64(tokenPrice)))
|
||||
|
@ -350,6 +363,13 @@ func validateInputData(input *RouteInputParams) error {
|
|||
}
|
||||
|
||||
if input.FromLockedAmount != nil && len(input.FromLockedAmount) > 0 {
|
||||
suppNetworks := copyMap(supportedNetworks)
|
||||
if input.TestnetMode {
|
||||
suppNetworks = copyMap(supportedTestNetworks)
|
||||
}
|
||||
|
||||
totalLockedAmount := big.NewInt(0)
|
||||
|
||||
for chainID, amount := range input.FromLockedAmount {
|
||||
if input.TestnetMode {
|
||||
if !supportedTestNetworks[chainID] {
|
||||
|
@ -361,30 +381,55 @@ func validateInputData(input *RouteInputParams) error {
|
|||
}
|
||||
}
|
||||
|
||||
delete(suppNetworks, chainID)
|
||||
|
||||
totalLockedAmount = new(big.Int).Add(totalLockedAmount, amount.ToInt())
|
||||
|
||||
if amount == nil || amount.ToInt().Sign() < 0 {
|
||||
return errors.New("locked amount must be positive")
|
||||
}
|
||||
}
|
||||
|
||||
if totalLockedAmount.Cmp(input.AmountIn.ToInt()) > 0 {
|
||||
return errors.New("locked amount exceeds the total amount to send")
|
||||
} else if totalLockedAmount.Cmp(input.AmountIn.ToInt()) < 0 && len(suppNetworks) == 0 {
|
||||
return errors.New("locked amount is less than the total amount to send, but all networks are locked")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams) (*SuggestedRoutesV2, error) {
|
||||
// clear all processors
|
||||
for _, processor := range r.pathProcessors {
|
||||
if clearable, ok := processor.(pathprocessor.PathProcessorClearable); ok {
|
||||
clearable.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
err := validateInputData(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates, err := r.resolveCandidates(ctx, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.resolveRoutes(ctx, input, candidates)
|
||||
}
|
||||
|
||||
func (r *Router) resolveCandidates(ctx context.Context, input *RouteInputParams) (candidates []*PathV2, err error) {
|
||||
networks, err := r.rpcClient.NetworkManager.Get(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
mu sync.Mutex
|
||||
candidates = make([]*PathV2, 0)
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
for networkIdx := range networks {
|
||||
|
@ -420,8 +465,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
if lockedAmount, ok := input.FromLockedAmount[network.ChainID]; ok {
|
||||
amountToSend = lockedAmount.ToInt()
|
||||
amountLocked = true
|
||||
}
|
||||
if len(input.FromLockedAmount) > 0 {
|
||||
} else if len(input.FromLockedAmount) > 0 {
|
||||
for chainID, lockedAmount := range input.FromLockedAmount {
|
||||
if chainID == network.ChainID {
|
||||
continue
|
||||
|
@ -481,7 +525,7 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
ToAddr: input.AddrTo,
|
||||
FromAddr: input.AddrFrom,
|
||||
AmountIn: amountToSend,
|
||||
AmountOut: input.AmountOut.ToInt(),
|
||||
AmountOut: amountToSend,
|
||||
|
||||
Username: input.Username,
|
||||
PublicKey: input.PublicKey,
|
||||
|
@ -507,14 +551,13 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
if err != nil {
|
||||
continue
|
||||
}
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, input.AddrFrom, network, token, amountToSend)
|
||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var l1FeeWei uint64
|
||||
if input.SendType.needL1Fee() {
|
||||
|
||||
txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
||||
if err != nil {
|
||||
continue
|
||||
|
@ -577,7 +620,10 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
|
|||
}
|
||||
|
||||
group.Wait()
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
func (r *Router) resolveRoutes(ctx context.Context, input *RouteInputParams, candidates []*PathV2) (*SuggestedRoutesV2, error) {
|
||||
prices, err := input.SendType.FetchPrices(r.marketManager, input.TokenID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue