feat_: recalculate route fees with every new block
This commit is contained in:
parent
1bb9cbc573
commit
506a76bf9f
|
@ -507,6 +507,12 @@ func (api *API) StopSuggestedRoutesAsyncCalculation(ctx context.Context) {
|
||||||
api.router.StopSuggestedRoutesAsyncCalculation()
|
api.router.StopSuggestedRoutesAsyncCalculation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) StopSuggestedRoutesCalculation(ctx context.Context) {
|
||||||
|
log.Debug("call to StopSuggestedRoutesCalculation")
|
||||||
|
|
||||||
|
api.router.StopSuggestedRoutesCalculation()
|
||||||
|
}
|
||||||
|
|
||||||
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
|
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
|
||||||
func (api *API) GetDerivedAddresses(ctx context.Context, password string, derivedFrom string, paths []string) ([]*DerivedAddress, error) {
|
func (api *API) GetDerivedAddresses(ctx context.Context, password string, derivedFrom string, paths []string) ([]*DerivedAddress, error) {
|
||||||
info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password)
|
info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password)
|
||||||
|
|
|
@ -73,6 +73,14 @@ type Router struct {
|
||||||
feesManager *fees.FeeManager
|
feesManager *fees.FeeManager
|
||||||
pathProcessors map[string]pathprocessor.PathProcessor
|
pathProcessors map[string]pathprocessor.PathProcessor
|
||||||
scheduler *async.Scheduler
|
scheduler *async.Scheduler
|
||||||
|
|
||||||
|
activeRoutesMutex sync.Mutex
|
||||||
|
activeRoutes *SuggestedRoutes
|
||||||
|
|
||||||
|
lastInputParamsMutex sync.Mutex
|
||||||
|
lastInputParams *requests.RouteInputParams
|
||||||
|
|
||||||
|
clientsForUpdatesPerChains sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager, marketManager *market.Manager,
|
func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager, marketManager *market.Manager,
|
||||||
|
@ -147,41 +155,46 @@ func newSuggestedRoutes(
|
||||||
return suggestedRoutes, allRoutes
|
return suggestedRoutes, allRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendRouterResult(uuid string, result interface{}, err error) {
|
||||||
|
routesResponse := responses.RouterSuggestedRoutes{
|
||||||
|
Uuid: uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errorResponse := errors.CreateErrorResponseFromError(err)
|
||||||
|
routesResponse.ErrorResponse = errorResponse.(*errors.ErrorResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
if suggestedRoutes, ok := result.(*SuggestedRoutes); ok && suggestedRoutes != nil {
|
||||||
|
routesResponse.Best = suggestedRoutes.Best
|
||||||
|
routesResponse.Candidates = suggestedRoutes.Candidates
|
||||||
|
routesResponse.TokenPrice = &suggestedRoutes.TokenPrice
|
||||||
|
routesResponse.NativeChainTokenPrice = &suggestedRoutes.NativeChainTokenPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
signal.SendWalletEvent(signal.SuggestedRoutes, routesResponse)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) SuggestedRoutesAsync(input *requests.RouteInputParams) {
|
func (r *Router) SuggestedRoutesAsync(input *requests.RouteInputParams) {
|
||||||
r.scheduler.Enqueue(routerTask, func(ctx context.Context) (interface{}, error) {
|
r.scheduler.Enqueue(routerTask, func(ctx context.Context) (interface{}, error) {
|
||||||
return r.SuggestedRoutes(ctx, input)
|
return r.SuggestedRoutes(ctx, input)
|
||||||
}, func(result interface{}, taskType async.TaskType, err error) {
|
}, func(result interface{}, taskType async.TaskType, err error) {
|
||||||
routesResponse := responses.RouterSuggestedRoutes{
|
sendRouterResult(input.Uuid, result, err)
|
||||||
Uuid: input.Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errorResponse := errors.CreateErrorResponseFromError(err)
|
|
||||||
routesResponse.ErrorResponse = errorResponse.(*errors.ErrorResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
if suggestedRoutes, ok := result.(*SuggestedRoutes); ok && suggestedRoutes != nil {
|
|
||||||
routesResponse.Best = suggestedRoutes.Best
|
|
||||||
routesResponse.Candidates = suggestedRoutes.Candidates
|
|
||||||
routesResponse.TokenPrice = &suggestedRoutes.TokenPrice
|
|
||||||
routesResponse.NativeChainTokenPrice = &suggestedRoutes.NativeChainTokenPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
signal.SendWalletEvent(signal.SuggestedRoutes, routesResponse)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) StopSuggestedRoutesAsyncCalculation() {
|
func (r *Router) StopSuggestedRoutesAsyncCalculation() {
|
||||||
|
r.unsubscribeFeesUpdateAccrossAllChains()
|
||||||
r.scheduler.Stop()
|
r.scheduler.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) SuggestedRoutes(ctx context.Context, input *requests.RouteInputParams) (*SuggestedRoutes, error) {
|
func (r *Router) StopSuggestedRoutesCalculation() {
|
||||||
testnetMode, err := r.rpcClient.NetworkManager.GetTestNetworksEnabled()
|
r.unsubscribeFeesUpdateAccrossAllChains()
|
||||||
if err != nil {
|
}
|
||||||
return nil, errors.CreateErrorResponseFromError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
input.TestnetMode = testnetMode
|
func (r *Router) SuggestedRoutes(ctx context.Context, input *requests.RouteInputParams) (suggestedRoutes *SuggestedRoutes, err error) {
|
||||||
|
// unsubscribe from updates
|
||||||
|
r.unsubscribeFeesUpdateAccrossAllChains()
|
||||||
|
|
||||||
// clear all processors
|
// clear all processors
|
||||||
for _, processor := range r.pathProcessors {
|
for _, processor := range r.pathProcessors {
|
||||||
|
@ -190,6 +203,29 @@ func (r *Router) SuggestedRoutes(ctx context.Context, input *requests.RouteInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.lastInputParamsMutex.Lock()
|
||||||
|
r.lastInputParams = input
|
||||||
|
r.lastInputParamsMutex.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r.activeRoutesMutex.Lock()
|
||||||
|
r.activeRoutes = suggestedRoutes
|
||||||
|
r.activeRoutesMutex.Unlock()
|
||||||
|
if suggestedRoutes != nil && err == nil {
|
||||||
|
// subscribe for updates
|
||||||
|
for _, path := range suggestedRoutes.Best {
|
||||||
|
err = r.subscribeForUdates(path.FromChain.ChainID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
testnetMode, err := r.rpcClient.NetworkManager.GetTestNetworksEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.CreateErrorResponseFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
input.TestnetMode = testnetMode
|
||||||
|
|
||||||
err = input.Validate()
|
err = input.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.CreateErrorResponseFromError(err)
|
return nil, errors.CreateErrorResponseFromError(err)
|
||||||
|
@ -224,7 +260,7 @@ func (r *Router) SuggestedRoutes(ctx context.Context, input *requests.RouteInput
|
||||||
return nil, errors.CreateErrorResponseFromError(err)
|
return nil, errors.CreateErrorResponseFromError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestedRoutes, err := r.resolveRoutes(ctx, input, candidates, balanceMap)
|
suggestedRoutes, err = r.resolveRoutes(ctx, input, candidates, balanceMap)
|
||||||
|
|
||||||
if err == nil && (suggestedRoutes == nil || len(suggestedRoutes.Best) == 0) {
|
if err == nil && (suggestedRoutes == nil || len(suggestedRoutes.Best) == 0) {
|
||||||
// No best route found, but no error given.
|
// No best route found, but no error given.
|
||||||
|
@ -657,22 +693,24 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
|
||||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
approvalRequired, approvalAmountRequired, approvalGasLimit, l1ApprovalFee, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams)
|
approvalRequired, approvalAmountRequired, err := r.requireApproval(ctx, input.SendType, &approvalContractAddress, processorInputParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: keep l1 fees at 0 until we have the correct algorithm, as we do base fee x 2 that should cover the l1 fees
|
var approvalGasLimit uint64
|
||||||
var l1FeeWei uint64 = 0
|
if approvalRequired {
|
||||||
// if input.SendType.needL1Fee() {
|
if processorInputParams.TestsMode {
|
||||||
// txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
approvalGasLimit = processorInputParams.TestApprovalGasEstimation
|
||||||
// if err != nil {
|
} else {
|
||||||
// continue
|
approvalGasLimit, err = r.estimateGasForApproval(processorInputParams, &approvalContractAddress)
|
||||||
// }
|
if err != nil {
|
||||||
|
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||||
// l1FeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData)
|
continue
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
|
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -687,44 +725,7 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
|
||||||
estimatedTime += 1
|
estimatedTime += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate ETH fees
|
path := &routes.Path{
|
||||||
ethTotalFees := big.NewInt(0)
|
|
||||||
txFeeInWei := new(big.Int).Mul(maxFeesPerGas, big.NewInt(int64(gasLimit)))
|
|
||||||
ethTotalFees.Add(ethTotalFees, txFeeInWei)
|
|
||||||
|
|
||||||
txL1FeeInWei := big.NewInt(0)
|
|
||||||
if l1FeeWei > 0 {
|
|
||||||
txL1FeeInWei = big.NewInt(int64(l1FeeWei))
|
|
||||||
ethTotalFees.Add(ethTotalFees, txL1FeeInWei)
|
|
||||||
}
|
|
||||||
|
|
||||||
approvalFeeInWei := big.NewInt(0)
|
|
||||||
approvalL1FeeInWei := big.NewInt(0)
|
|
||||||
if approvalRequired {
|
|
||||||
approvalFeeInWei.Mul(maxFeesPerGas, big.NewInt(int64(approvalGasLimit)))
|
|
||||||
ethTotalFees.Add(ethTotalFees, approvalFeeInWei)
|
|
||||||
|
|
||||||
if l1ApprovalFee > 0 {
|
|
||||||
approvalL1FeeInWei = big.NewInt(int64(l1ApprovalFee))
|
|
||||||
ethTotalFees.Add(ethTotalFees, approvalL1FeeInWei)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate required balances (bonder and token fees are already included in the amountIn by Hop bridge (once we include Celar we need to check how they handle the fees))
|
|
||||||
requiredNativeBalance := big.NewInt(0)
|
|
||||||
requiredTokenBalance := big.NewInt(0)
|
|
||||||
|
|
||||||
if token.IsNative() {
|
|
||||||
requiredNativeBalance.Add(requiredNativeBalance, amountOption.amount)
|
|
||||||
if !amountOption.subtractFees {
|
|
||||||
requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requiredTokenBalance.Add(requiredTokenBalance, amountOption.amount)
|
|
||||||
requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees)
|
|
||||||
}
|
|
||||||
|
|
||||||
appendPathFn(&routes.Path{
|
|
||||||
ProcessorName: pProcessor.Name(),
|
ProcessorName: pProcessor.Name(),
|
||||||
FromChain: network,
|
FromChain: network,
|
||||||
ToChain: dest,
|
ToChain: dest,
|
||||||
|
@ -734,36 +735,28 @@ func (r *Router) resolveCandidates(ctx context.Context, input *requests.RouteInp
|
||||||
AmountInLocked: amountOption.locked,
|
AmountInLocked: amountOption.locked,
|
||||||
AmountOut: (*hexutil.Big)(amountOut),
|
AmountOut: (*hexutil.Big)(amountOut),
|
||||||
|
|
||||||
SuggestedLevelsForMaxFeesPerGas: fetchedFees.MaxFeesLevels,
|
// set params that we don't want to be recalculated with every new block creation
|
||||||
MaxFeesPerGas: (*hexutil.Big)(maxFeesPerGas),
|
TxGasAmount: gasLimit,
|
||||||
|
TxBonderFees: (*hexutil.Big)(bonderFees),
|
||||||
TxBaseFee: (*hexutil.Big)(fetchedFees.BaseFee),
|
TxTokenFees: (*hexutil.Big)(tokenFees),
|
||||||
TxPriorityFee: (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas),
|
|
||||||
TxGasAmount: gasLimit,
|
|
||||||
TxBonderFees: (*hexutil.Big)(bonderFees),
|
|
||||||
TxTokenFees: (*hexutil.Big)(tokenFees),
|
|
||||||
|
|
||||||
TxFee: (*hexutil.Big)(txFeeInWei),
|
|
||||||
TxL1Fee: (*hexutil.Big)(txL1FeeInWei),
|
|
||||||
|
|
||||||
ApprovalRequired: approvalRequired,
|
ApprovalRequired: approvalRequired,
|
||||||
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
|
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
|
||||||
ApprovalContractAddress: &approvalContractAddress,
|
ApprovalContractAddress: &approvalContractAddress,
|
||||||
ApprovalBaseFee: (*hexutil.Big)(fetchedFees.BaseFee),
|
|
||||||
ApprovalPriorityFee: (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas),
|
|
||||||
ApprovalGasAmount: approvalGasLimit,
|
ApprovalGasAmount: approvalGasLimit,
|
||||||
|
|
||||||
ApprovalFee: (*hexutil.Big)(approvalFeeInWei),
|
|
||||||
ApprovalL1Fee: (*hexutil.Big)(approvalL1FeeInWei),
|
|
||||||
|
|
||||||
TxTotalFee: (*hexutil.Big)(ethTotalFees),
|
|
||||||
|
|
||||||
EstimatedTime: estimatedTime,
|
EstimatedTime: estimatedTime,
|
||||||
|
|
||||||
SubtractFees: amountOption.subtractFees,
|
SubtractFees: amountOption.subtractFees,
|
||||||
RequiredTokenBalance: requiredTokenBalance,
|
}
|
||||||
RequiredNativeBalance: requiredNativeBalance,
|
|
||||||
})
|
err = r.cacluateFees(ctx, path, fetchedFees, processorInputParams.TestsMode, processorInputParams.TestApprovalL1Fee)
|
||||||
|
if err != nil {
|
||||||
|
appendProcessorErrorFn(pProcessor.Name(), input.SendType, processorInputParams.FromChain.ChainID, processorInputParams.ToChain.ChainID, processorInputParams.AmountIn, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPathFn(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,43 +10,46 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/status-im/status-go/contracts"
|
"github.com/status-im/status-go/contracts"
|
||||||
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
gaspriceoracle "github.com/status-im/status-go/contracts/gas-price-oracle"
|
||||||
"github.com/status-im/status-go/contracts/ierc20"
|
"github.com/status-im/status-go/contracts/ierc20"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/services/wallet/bigint"
|
"github.com/status-im/status-go/services/wallet/bigint"
|
||||||
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||||
|
"github.com/status-im/status-go/services/wallet/router/fees"
|
||||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||||
|
routs "github.com/status-im/status-go/services/wallet/router/routes"
|
||||||
"github.com/status-im/status-go/services/wallet/router/sendtype"
|
"github.com/status-im/status-go/services/wallet/router/sendtype"
|
||||||
"github.com/status-im/status-go/services/wallet/token"
|
"github.com/status-im/status-go/services/wallet/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) (
|
func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType, approvalContractAddress *common.Address, params pathprocessor.ProcessorInputParams) (
|
||||||
bool, *big.Int, uint64, uint64, error) {
|
bool, *big.Int, error) {
|
||||||
if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() || sendType.IsStickersTransfer() {
|
if sendType.IsCollectiblesTransfer() || sendType.IsEnsTransfer() || sendType.IsStickersTransfer() {
|
||||||
return false, nil, 0, 0, nil
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.FromToken.IsNative() {
|
if params.FromToken.IsNative() {
|
||||||
return false, nil, 0, 0, nil
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
contractMaker, err := contracts.NewContractMaker(r.rpcClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address)
|
contract, err := contractMaker.NewERC20(params.FromChain.ChainID, params.FromToken.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress {
|
if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress {
|
||||||
return false, nil, 0, 0, nil
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.TestsMode {
|
if params.TestsMode {
|
||||||
return true, params.AmountIn, params.TestApprovalGasEstimation, params.TestApprovalL1Fee, nil
|
return true, params.AmountIn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
allowance, err := contract.Allowance(&bind.CallOpts{
|
allowance, err := contract.Allowance(&bind.CallOpts{
|
||||||
|
@ -54,45 +57,65 @@ func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType
|
||||||
}, params.FromAddr, *approvalContractAddress)
|
}, params.FromAddr, *approvalContractAddress)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowance.Cmp(params.AmountIn) >= 0 {
|
if allowance.Cmp(params.AmountIn) >= 0 {
|
||||||
return false, nil, 0, 0, nil
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID)
|
return true, params.AmountIn, nil
|
||||||
if err != nil {
|
}
|
||||||
return false, nil, 0, 0, err
|
|
||||||
|
func (r *Router) packApprovalInputData(amountIn *big.Int, approvalContractAddress *common.Address) ([]byte, error) {
|
||||||
|
if approvalContractAddress == nil || *approvalContractAddress == pathprocessor.ZeroAddress {
|
||||||
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
erc20ABI, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := erc20ABI.Pack("approve", approvalContractAddress, params.AmountIn)
|
return erc20ABI.Pack("approve", approvalContractAddress, amountIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) estimateGasForApproval(params pathprocessor.ProcessorInputParams, approvalContractAddress *common.Address) (uint64, error) {
|
||||||
|
data, err := r.packApprovalInputData(params.AmountIn, approvalContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
ethClient, err := r.rpcClient.EthClient(params.FromChain.ChainID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
|
||||||
From: params.FromAddr,
|
From: params.FromAddr,
|
||||||
To: ¶ms.FromToken.Address,
|
To: ¶ms.FromToken.Address,
|
||||||
Value: pathprocessor.ZeroBigIntValue,
|
Value: pathprocessor.ZeroBigIntValue,
|
||||||
Data: data,
|
Data: data,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) calculateApprovalL1Fee(amountIn *big.Int, chainID uint64, approvalContractAddress *common.Address) (uint64, error) {
|
||||||
|
data, err := r.packApprovalInputData(amountIn, approvalContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ethClient, err := r.rpcClient.EthClient(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetching l1 fee
|
|
||||||
var l1Fee uint64
|
var l1Fee uint64
|
||||||
oracleContractAddress, err := gaspriceoracle.ContractAddress(params.FromChain.ChainID)
|
oracleContractAddress, err := gaspriceoracle.ContractAddress(chainID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
oracleContract, err := gaspriceoracle.NewGaspriceoracleCaller(oracleContractAddress, ethClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, 0, 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
callOpt := &bind.CallOpts{}
|
callOpt := &bind.CallOpts{}
|
||||||
|
@ -101,7 +124,7 @@ func (r *Router) requireApproval(ctx context.Context, sendType sendtype.SendType
|
||||||
l1Fee = l1FeeResult.Uint64()
|
l1Fee = l1FeeResult.Uint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, params.AmountIn, estimate, l1Fee, nil
|
return l1Fee, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
func (r *Router) getERC1155Balance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
|
||||||
|
@ -136,3 +159,96 @@ func (r *Router) getBalance(ctx context.Context, chainID uint64, token *token.To
|
||||||
|
|
||||||
return r.tokenManager.GetBalance(ctx, client, account, token.Address)
|
return r.tokenManager.GetBalance(ctx, client, account, token.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) cacluateFees(ctx context.Context, path *routs.Path, fetchedFees *fees.SuggestedFees, testsMode bool, testApprovalL1Fee uint64) (err error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
l1ApprovalFee uint64
|
||||||
|
)
|
||||||
|
if path.ApprovalRequired {
|
||||||
|
if testsMode {
|
||||||
|
l1ApprovalFee = testApprovalL1Fee
|
||||||
|
} else {
|
||||||
|
l1ApprovalFee, err = r.calculateApprovalL1Fee(path.AmountIn.ToInt(), path.FromChain.ChainID, path.ApprovalContractAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: keep l1 fees at 0 until we have the correct algorithm, as we do base fee x 2 that should cover the l1 fees
|
||||||
|
var l1FeeWei uint64 = 0
|
||||||
|
// if input.SendType.needL1Fee() {
|
||||||
|
// txInputData, err := pProcessor.PackTxInputData(processorInputParams)
|
||||||
|
// if err != nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// l1FeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData)
|
||||||
|
// }
|
||||||
|
|
||||||
|
r.lastInputParamsMutex.Lock()
|
||||||
|
gasFeeMode := r.lastInputParams.GasFeeMode
|
||||||
|
r.lastInputParamsMutex.Unlock()
|
||||||
|
maxFeesPerGas := fetchedFees.FeeFor(gasFeeMode)
|
||||||
|
|
||||||
|
// calculate ETH fees
|
||||||
|
ethTotalFees := big.NewInt(0)
|
||||||
|
txFeeInWei := new(big.Int).Mul(maxFeesPerGas, big.NewInt(int64(path.TxGasAmount)))
|
||||||
|
ethTotalFees.Add(ethTotalFees, txFeeInWei)
|
||||||
|
|
||||||
|
txL1FeeInWei := big.NewInt(0)
|
||||||
|
if l1FeeWei > 0 {
|
||||||
|
txL1FeeInWei = big.NewInt(int64(l1FeeWei))
|
||||||
|
ethTotalFees.Add(ethTotalFees, txL1FeeInWei)
|
||||||
|
}
|
||||||
|
|
||||||
|
approvalFeeInWei := big.NewInt(0)
|
||||||
|
approvalL1FeeInWei := big.NewInt(0)
|
||||||
|
if path.ApprovalRequired {
|
||||||
|
approvalFeeInWei.Mul(maxFeesPerGas, big.NewInt(int64(path.ApprovalGasAmount)))
|
||||||
|
ethTotalFees.Add(ethTotalFees, approvalFeeInWei)
|
||||||
|
|
||||||
|
if l1ApprovalFee > 0 {
|
||||||
|
approvalL1FeeInWei = big.NewInt(int64(l1ApprovalFee))
|
||||||
|
ethTotalFees.Add(ethTotalFees, approvalL1FeeInWei)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate required balances (bonder and token fees are already included in the amountIn by Hop bridge (once we include Celar we need to check how they handle the fees))
|
||||||
|
requiredNativeBalance := big.NewInt(0)
|
||||||
|
requiredTokenBalance := big.NewInt(0)
|
||||||
|
|
||||||
|
if path.FromToken.IsNative() {
|
||||||
|
requiredNativeBalance.Add(requiredNativeBalance, path.AmountIn.ToInt())
|
||||||
|
if !path.SubtractFees {
|
||||||
|
requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requiredTokenBalance.Add(requiredTokenBalance, path.AmountIn.ToInt())
|
||||||
|
requiredNativeBalance.Add(requiredNativeBalance, ethTotalFees)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the values
|
||||||
|
path.SuggestedLevelsForMaxFeesPerGas = fetchedFees.MaxFeesLevels
|
||||||
|
path.MaxFeesPerGas = (*hexutil.Big)(maxFeesPerGas)
|
||||||
|
|
||||||
|
path.TxBaseFee = (*hexutil.Big)(fetchedFees.BaseFee)
|
||||||
|
path.TxPriorityFee = (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas)
|
||||||
|
|
||||||
|
path.TxFee = (*hexutil.Big)(txFeeInWei)
|
||||||
|
path.TxL1Fee = (*hexutil.Big)(txL1FeeInWei)
|
||||||
|
|
||||||
|
path.ApprovalBaseFee = (*hexutil.Big)(fetchedFees.BaseFee)
|
||||||
|
path.ApprovalPriorityFee = (*hexutil.Big)(fetchedFees.MaxPriorityFeePerGas)
|
||||||
|
|
||||||
|
path.ApprovalFee = (*hexutil.Big)(approvalFeeInWei)
|
||||||
|
path.ApprovalL1Fee = (*hexutil.Big)(approvalL1FeeInWei)
|
||||||
|
|
||||||
|
path.TxTotalFee = (*hexutil.Big)(ethTotalFees)
|
||||||
|
|
||||||
|
path.RequiredTokenBalance = requiredTokenBalance
|
||||||
|
path.RequiredNativeBalance = requiredNativeBalance
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/rpc/chain"
|
||||||
|
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
newBlockCheckIntervalMainnet = 3 * time.Second
|
||||||
|
newBlockCheckIntervalOptimism = 1 * time.Second
|
||||||
|
newBlockCheckIntervalArbitrum = 200 * time.Millisecond
|
||||||
|
|
||||||
|
feeRecalculationTimeout = 5 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type fetchingLastBlock struct {
|
||||||
|
client chain.ClientInterface
|
||||||
|
lastBlock uint64
|
||||||
|
closeCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) subscribeForUdates(chainID uint64) error {
|
||||||
|
if _, ok := r.clientsForUpdatesPerChains.Load(chainID); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ethClient, err := r.rpcClient.EthClient(chainID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get eth client", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flb := fetchingLastBlock{
|
||||||
|
client: ethClient,
|
||||||
|
lastBlock: 0,
|
||||||
|
closeCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
r.clientsForUpdatesPerChains.Store(chainID, flb)
|
||||||
|
|
||||||
|
r.startTimeoutForUpdates(flb.closeCh)
|
||||||
|
|
||||||
|
var ticker *time.Ticker
|
||||||
|
switch chainID {
|
||||||
|
case walletCommon.EthereumMainnet,
|
||||||
|
walletCommon.EthereumSepolia:
|
||||||
|
ticker = time.NewTicker(newBlockCheckIntervalMainnet)
|
||||||
|
case walletCommon.OptimismMainnet,
|
||||||
|
walletCommon.OptimismSepolia:
|
||||||
|
ticker = time.NewTicker(newBlockCheckIntervalOptimism)
|
||||||
|
case walletCommon.ArbitrumMainnet,
|
||||||
|
walletCommon.ArbitrumSepolia:
|
||||||
|
ticker = time.NewTicker(newBlockCheckIntervalArbitrum)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
var blockNumber uint64
|
||||||
|
blockNumber, err := ethClient.BlockNumber(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get block number", "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := r.clientsForUpdatesPerChains.Load(chainID)
|
||||||
|
if !ok {
|
||||||
|
log.Error("Failed to get fetchingLastBlock", "chain", chainID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
flbLoaded, ok := val.(fetchingLastBlock)
|
||||||
|
if !ok {
|
||||||
|
log.Error("Failed to get fetchingLastBlock", "chain", chainID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNumber > flbLoaded.lastBlock {
|
||||||
|
flbLoaded.lastBlock = blockNumber
|
||||||
|
r.clientsForUpdatesPerChains.Store(chainID, flbLoaded)
|
||||||
|
|
||||||
|
fees, err := r.feesManager.SuggestedFees(ctx, chainID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get suggested fees", "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.lastInputParamsMutex.Lock()
|
||||||
|
uuid := r.lastInputParams.Uuid
|
||||||
|
r.lastInputParamsMutex.Unlock()
|
||||||
|
|
||||||
|
r.activeRoutesMutex.Lock()
|
||||||
|
if r.activeRoutes != nil && r.activeRoutes.Best != nil && len(r.activeRoutes.Best) > 0 {
|
||||||
|
for _, path := range r.activeRoutes.Best {
|
||||||
|
err = r.cacluateFees(ctx, path, fees, false, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to calculate fees", "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRouterResult(uuid, r.activeRoutes, nil)
|
||||||
|
}
|
||||||
|
r.activeRoutesMutex.Unlock()
|
||||||
|
}
|
||||||
|
case <-flb.closeCh:
|
||||||
|
ticker.Stop()
|
||||||
|
cancelCtx()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) startTimeoutForUpdates(closeCh chan struct{}) {
|
||||||
|
dedlineTicker := time.NewTicker(feeRecalculationTimeout)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-dedlineTicker.C:
|
||||||
|
r.unsubscribeFeesUpdateAccrossAllChains()
|
||||||
|
return
|
||||||
|
case <-closeCh:
|
||||||
|
dedlineTicker.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) unsubscribeFeesUpdateAccrossAllChains() {
|
||||||
|
r.clientsForUpdatesPerChains.Range(func(key, value interface{}) bool {
|
||||||
|
flb, ok := value.(fetchingLastBlock)
|
||||||
|
if !ok {
|
||||||
|
log.Error("Failed to get fetchingLastBlock", "chain", key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
close(flb.closeCh)
|
||||||
|
r.clientsForUpdatesPerChains.Delete(key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue