chore_: improvements of the sending route generated by the router process
This commit simplifies the sending process of the best route suggested by the router. It also makes the sending process the same for accounts (key pairs) migrated to a keycard and those stored locally in local keystore files. Deprecated endpoints: - `CreateMultiTransaction` - `ProceedWithTransactionsSignatures` Deprecated signal: - `wallet.sign.transactions` New endpoints: - `BuildTransactionsFromRoute` - `SendRouterTransactionsWithSignatures` The flow for sending the best router suggested by the router: - call `BuildTransactionsFromRoute` - wait for the `wallet.router.sign-transactions` signal - sign received hashes using `SignMessage` call or sign on keycard - call `SendRouterTransactionsWithSignatures` with the signatures of signed hashes from the previous step - `wallet.router.transactions-sent` signal will be sent after transactions are sent or if an error occurs New signals: - `wallet.router.sending-transactions-started` // notifies client that the sending transactions process started - `wallet.router.sign-transactions` // notifies client about the list of transactions that need to be signed - `wallet.router.transactions-sent` // notifies client about transactions that are sent - `wallet.transaction.status-changed` // notifies about status of sent transactions
This commit is contained in:
parent
1128598b03
commit
28506bcd17
|
@ -18,6 +18,7 @@ import (
|
|||
signercore "github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
abi_spec "github.com/status-im/status-go/abi-spec"
|
||||
"github.com/status-im/status-go/account"
|
||||
statusErrors "github.com/status-im/status-go/errors"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
|
@ -30,13 +31,16 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/onramp"
|
||||
"github.com/status-im/status-go/services/wallet/requests"
|
||||
"github.com/status-im/status-go/services/wallet/responses"
|
||||
"github.com/status-im/status-go/services/wallet/router"
|
||||
"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/sendtype"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
"github.com/status-im/status-go/services/wallet/walletconnect"
|
||||
"github.com/status-im/status-go/signal"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
|
@ -712,6 +716,15 @@ func (api *API) SendTransactionWithSignature(ctx context.Context, chainID uint64
|
|||
return api.s.transactionManager.SendTransactionWithSignature(chainID, params, sig)
|
||||
}
|
||||
|
||||
// Deprecated: `CreateMultiTransaction` is the old way of sending transactions and should not be used anymore.
|
||||
//
|
||||
// The flow that should be used instead:
|
||||
// - call `BuildTransactionsFromRoute`
|
||||
// - wait for the `wallet.router.sign-transactions` signal
|
||||
// - sign received hashes using `SignMessage` call or sign on keycard
|
||||
// - call `SendRouterTransactionsWithSignatures` with the signatures of signed hashes from the previous step
|
||||
//
|
||||
// TODO: remove this struct once mobile switches to the new approach
|
||||
func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*pathprocessor.MultipathProcessorTxArgs, password string) (*transfer.MultiTransactionCommandResult, error) {
|
||||
log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction")
|
||||
|
||||
|
@ -742,11 +755,188 @@ func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionComm
|
|||
return nil, api.s.transactionManager.SendTransactionForSigningToKeycard(ctx, cmd, data, api.router.GetPathProcessors())
|
||||
}
|
||||
|
||||
func updateFields(sd *responses.SendDetails, inputParams requests.RouteInputParams) {
|
||||
sd.SendType = int(inputParams.SendType)
|
||||
sd.FromAddress = types.Address(inputParams.AddrFrom)
|
||||
sd.ToAddress = types.Address(inputParams.AddrTo)
|
||||
sd.FromToken = inputParams.TokenID
|
||||
sd.ToToken = inputParams.ToTokenID
|
||||
if inputParams.AmountIn != nil {
|
||||
sd.FromAmount = inputParams.AmountIn.String()
|
||||
}
|
||||
if inputParams.AmountOut != nil {
|
||||
sd.ToAmount = inputParams.AmountOut.String()
|
||||
}
|
||||
sd.OwnerTokenBeingSent = inputParams.TokenIDIsOwnerToken
|
||||
sd.Username = inputParams.Username
|
||||
sd.PublicKey = inputParams.PublicKey
|
||||
if inputParams.PackID != nil {
|
||||
sd.PackID = inputParams.PackID.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) BuildTransactionsFromRoute(ctx context.Context, buildInputParams *requests.RouterBuildTransactionsParams) {
|
||||
log.Debug("[WalletAPI::BuildTransactionsFromRoute] builds transactions from the generated best route", "uuid", buildInputParams.Uuid)
|
||||
|
||||
go func() {
|
||||
api.router.StopSuggestedRoutesAsyncCalculation()
|
||||
|
||||
var err error
|
||||
response := &responses.RouterTransactionsForSigning{
|
||||
SendDetails: &responses.SendDetails{
|
||||
Uuid: buildInputParams.Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
api.s.transactionManager.ClearLocalRouterTransactionsData()
|
||||
err = statusErrors.CreateErrorResponseFromError(err)
|
||||
response.SendDetails.ErrorResponse = err.(*statusErrors.ErrorResponse)
|
||||
}
|
||||
signal.SendWalletEvent(signal.SignRouterTransactions, response)
|
||||
}()
|
||||
|
||||
route, routeInputParams := api.router.GetBestRouteAndAssociatedInputParams()
|
||||
if routeInputParams.Uuid != buildInputParams.Uuid {
|
||||
// should never be here
|
||||
err = ErrCannotResolveRouteId
|
||||
return
|
||||
}
|
||||
|
||||
updateFields(response.SendDetails, routeInputParams)
|
||||
|
||||
// notify client that sending transactions started (has 3 steps, building txs, signing txs, sending txs)
|
||||
signal.SendWalletEvent(signal.RouterSendingTransactionsStarted, response.SendDetails)
|
||||
|
||||
response.SigningDetails, err = api.s.transactionManager.BuildTransactionsFromRoute(
|
||||
route,
|
||||
api.router.GetPathProcessors(),
|
||||
transfer.BuildRouteExtraParams{
|
||||
AddressFrom: routeInputParams.AddrFrom,
|
||||
AddressTo: routeInputParams.AddrTo,
|
||||
Username: routeInputParams.Username,
|
||||
PublicKey: routeInputParams.PublicKey,
|
||||
PackID: routeInputParams.PackID.ToInt(),
|
||||
SlippagePercentage: buildInputParams.SlippagePercentage,
|
||||
},
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
// Deprecated: `ProceedWithTransactionsSignatures` is the endpoint used in the old way of sending transactions and should not be used anymore.
|
||||
//
|
||||
// The flow that should be used instead:
|
||||
// - call `BuildTransactionsFromRoute`
|
||||
// - wait for the `wallet.router.sign-transactions` signal
|
||||
// - sign received hashes using `SignMessage` call or sign on keycard
|
||||
// - call `SendRouterTransactionsWithSignatures` with the signatures of signed hashes from the previous step
|
||||
//
|
||||
// TODO: remove this struct once mobile switches to the new approach
|
||||
func (api *API) ProceedWithTransactionsSignatures(ctx context.Context, signatures map[string]transfer.SignatureDetails) (*transfer.MultiTransactionCommandResult, error) {
|
||||
log.Debug("[WalletAPI:: ProceedWithTransactionsSignatures] sign with signatures and send multi transaction")
|
||||
return api.s.transactionManager.ProceedWithTransactionsSignatures(ctx, signatures)
|
||||
}
|
||||
|
||||
func (api *API) SendRouterTransactionsWithSignatures(ctx context.Context, sendInputParams *requests.RouterSendTransactionsParams) {
|
||||
log.Debug("[WalletAPI:: SendRouterTransactionsWithSignatures] sign with signatures and send")
|
||||
go func() {
|
||||
|
||||
var (
|
||||
err error
|
||||
routeInputParams requests.RouteInputParams
|
||||
)
|
||||
response := &responses.RouterSentTransactions{
|
||||
SendDetails: &responses.SendDetails{
|
||||
Uuid: sendInputParams.Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
defer func() {
|
||||
clearLocalData := true
|
||||
if routeInputParams.SendType == sendtype.Swap {
|
||||
// in case of swap don't clear local data if an approval is placed, but swap tx is not sent yet
|
||||
if api.s.transactionManager.ApprovalRequiredForPath(pathprocessor.ProcessorSwapParaswapName) &&
|
||||
api.s.transactionManager.ApprovalPlacedForPath(pathprocessor.ProcessorSwapParaswapName) &&
|
||||
!api.s.transactionManager.TxPlacedForPath(pathprocessor.ProcessorSwapParaswapName) {
|
||||
clearLocalData = false
|
||||
}
|
||||
}
|
||||
|
||||
if clearLocalData {
|
||||
api.s.transactionManager.ClearLocalRouterTransactionsData()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = statusErrors.CreateErrorResponseFromError(err)
|
||||
response.SendDetails.ErrorResponse = err.(*statusErrors.ErrorResponse)
|
||||
}
|
||||
signal.SendWalletEvent(signal.RouterTransactionsSent, response)
|
||||
}()
|
||||
|
||||
_, routeInputParams = api.router.GetBestRouteAndAssociatedInputParams()
|
||||
if routeInputParams.Uuid != sendInputParams.Uuid {
|
||||
err = ErrCannotResolveRouteId
|
||||
return
|
||||
}
|
||||
|
||||
updateFields(response.SendDetails, routeInputParams)
|
||||
|
||||
err = api.s.transactionManager.ValidateAndAddSignaturesToRouterTransactions(sendInputParams.Signatures)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// prepare multitx
|
||||
var mtType transfer.MultiTransactionType = transfer.MultiTransactionSend
|
||||
if routeInputParams.SendType == sendtype.Bridge {
|
||||
mtType = transfer.MultiTransactionBridge
|
||||
} else if routeInputParams.SendType == sendtype.Swap {
|
||||
mtType = transfer.MultiTransactionSwap
|
||||
}
|
||||
|
||||
multiTx := transfer.NewMultiTransaction(
|
||||
/* Timestamp: */ uint64(time.Now().Unix()),
|
||||
/* FromNetworkID: */ 0,
|
||||
/* ToNetworkID: */ 0,
|
||||
/* FromTxHash: */ common.Hash{},
|
||||
/* ToTxHash: */ common.Hash{},
|
||||
/* FromAddress: */ routeInputParams.AddrFrom,
|
||||
/* ToAddress: */ routeInputParams.AddrTo,
|
||||
/* FromAsset: */ routeInputParams.TokenID,
|
||||
/* ToAsset: */ routeInputParams.ToTokenID,
|
||||
/* FromAmount: */ routeInputParams.AmountIn,
|
||||
/* ToAmount: */ routeInputParams.AmountOut,
|
||||
/* Type: */ mtType,
|
||||
/* CrossTxID: */ "",
|
||||
)
|
||||
|
||||
_, err = api.s.transactionManager.InsertMultiTransaction(multiTx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
response.SentTransactions, err = api.s.transactionManager.SendRouterTransactions(ctx, multiTx)
|
||||
var (
|
||||
chainIDs []uint64
|
||||
addresses []common.Address
|
||||
)
|
||||
for _, tx := range response.SentTransactions {
|
||||
chainIDs = append(chainIDs, tx.FromChain)
|
||||
addresses = append(addresses, common.Address(tx.FromAddress))
|
||||
go func(chainId uint64, txHash common.Hash) {
|
||||
err = api.s.transactionManager.WatchTransaction(context.Background(), chainId, txHash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(tx.FromChain, common.Hash(tx.Hash))
|
||||
}
|
||||
err = api.s.transferController.CheckRecentHistory(chainIDs, addresses)
|
||||
}()
|
||||
}
|
||||
|
||||
func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []wcommon.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) {
|
||||
log.Debug("wallet.api.GetMultiTransactions", "IDs.len", len(transactionIDs))
|
||||
return api.s.transactionManager.GetMultiTransactions(ctx, transactionIDs)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package wallet
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/errors"
|
||||
)
|
||||
|
||||
// Abbreviation `W` for the error code stands for Wallet
|
||||
var (
|
||||
ErrCannotResolveRouteId = &errors.ErrorResponse{Code: errors.ErrorCode("W-001"), Details: "cannot resolve route id"}
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
package requests
|
||||
|
||||
type RouterBuildTransactionsParams struct {
|
||||
Uuid string `json:"uuid"`
|
||||
SlippagePercentage float32 `json:"slippagePercentage"`
|
||||
}
|
|
@ -44,6 +44,7 @@ type RouteInputParams struct {
|
|||
AmountIn *hexutil.Big `json:"amountIn" validate:"required"`
|
||||
AmountOut *hexutil.Big `json:"amountOut"`
|
||||
TokenID string `json:"tokenID" validate:"required"`
|
||||
TokenIDIsOwnerToken bool `json:"tokenIDIsOwnerToken"`
|
||||
ToTokenID string `json:"toTokenID"`
|
||||
DisabledFromChainIDs []uint64 `json:"disabledFromChainIDs"`
|
||||
DisabledToChainIDs []uint64 `json:"disabledToChainIDs"`
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package requests
|
||||
|
||||
import "github.com/status-im/status-go/services/wallet/transfer"
|
||||
|
||||
type RouterSendTransactionsParams struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Signatures map[string]transfer.SignatureDetails `json:"signatures"`
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package responses
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/errors"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type SendDetails struct {
|
||||
Uuid string `json:"uuid"`
|
||||
SendType int `json:"sendType"`
|
||||
FromAddress types.Address `json:"fromAddress"`
|
||||
ToAddress types.Address `json:"toAddress"`
|
||||
FromToken string `json:"fromToken"`
|
||||
ToToken string `json:"toToken"`
|
||||
FromAmount string `json:"fromAmount"` // total amount
|
||||
ToAmount string `json:"toAmount"`
|
||||
OwnerTokenBeingSent bool `json:"ownerTokenBeingSent"`
|
||||
ErrorResponse *errors.ErrorResponse `json:"errorResponse,omitempty"`
|
||||
|
||||
Username string `json:"username"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
PackID string `json:"packId"`
|
||||
}
|
||||
|
||||
type SigningDetails struct {
|
||||
Address types.Address `json:"address"`
|
||||
AddressPath string `json:"addressPath"`
|
||||
KeyUid string `json:"keyUid"`
|
||||
SignOnKeycard bool `json:"signOnKeycard"`
|
||||
Hashes []types.Hash `json:"hashes"`
|
||||
}
|
||||
|
||||
type RouterTransactionsForSigning struct {
|
||||
SendDetails *SendDetails `json:"sendDetails"`
|
||||
SigningDetails *SigningDetails `json:"signingDetails"`
|
||||
}
|
||||
|
||||
type RouterSentTransaction struct {
|
||||
FromAddress types.Address `json:"fromAddress"`
|
||||
ToAddress types.Address `json:"toAddress"`
|
||||
FromChain uint64 `json:"fromChain"`
|
||||
ToChain uint64 `json:"toChain"`
|
||||
FromToken string `json:"fromToken"`
|
||||
ToToken string `json:"toToken"`
|
||||
Amount string `json:"amount"` // amount of the transaction
|
||||
Hash types.Hash `json:"hash"`
|
||||
ApprovalTx bool `json:"approvalTx"`
|
||||
}
|
||||
|
||||
type RouterSentTransactions struct {
|
||||
SendDetails *SendDetails `json:"sendDetails"`
|
||||
SentTransactions []*RouterSentTransaction `json:"sentTransactions"`
|
||||
}
|
||||
|
||||
func NewRouterSentTransaction(sendArgs *transactions.SendTxArgs, hash types.Hash, approvalTx bool) *RouterSentTransaction {
|
||||
addr := types.Address{}
|
||||
if sendArgs.To != nil {
|
||||
addr = *sendArgs.To
|
||||
}
|
||||
return &RouterSentTransaction{
|
||||
FromAddress: sendArgs.From,
|
||||
ToAddress: addr,
|
||||
FromChain: sendArgs.FromChainID,
|
||||
ToChain: sendArgs.ToChainID,
|
||||
FromToken: sendArgs.FromTokenID,
|
||||
ToToken: sendArgs.ToTokenID,
|
||||
Amount: sendArgs.Value.String(),
|
||||
Hash: hash,
|
||||
ApprovalTx: approvalTx,
|
||||
}
|
||||
}
|
|
@ -120,6 +120,20 @@ func (r *Router) GetPathProcessors() map[string]pathprocessor.PathProcessor {
|
|||
return r.pathProcessors
|
||||
}
|
||||
|
||||
func (r *Router) GetBestRouteAndAssociatedInputParams() (routes.Route, requests.RouteInputParams) {
|
||||
r.activeRoutesMutex.Lock()
|
||||
defer r.activeRoutesMutex.Unlock()
|
||||
if r.activeRoutes == nil {
|
||||
return nil, requests.RouteInputParams{}
|
||||
}
|
||||
|
||||
r.lastInputParamsMutex.Lock()
|
||||
defer r.lastInputParamsMutex.Unlock()
|
||||
ip := *r.lastInputParams
|
||||
|
||||
return r.activeRoutes.Best.Copy(), ip
|
||||
}
|
||||
|
||||
func (r *Router) SetTestBalanceMap(balanceMap map[string]*big.Int) {
|
||||
for k, v := range balanceMap {
|
||||
r.activeBalanceMap.Store(k, v)
|
||||
|
|
|
@ -9,6 +9,14 @@ import (
|
|||
|
||||
type Route []*Path
|
||||
|
||||
func (r Route) Copy() Route {
|
||||
newRoute := make(Route, len(r))
|
||||
for i, path := range r {
|
||||
newRoute[i] = path.Copy()
|
||||
}
|
||||
return newRoute
|
||||
}
|
||||
|
||||
func FindBestRoute(routes []Route, tokenPrice float64, nativeTokenPrice float64) Route {
|
||||
var best Route
|
||||
bestCost := big.NewFloat(math.Inf(1))
|
||||
|
|
|
@ -54,3 +54,118 @@ type Path struct {
|
|||
func (p *Path) Equal(o *Path) bool {
|
||||
return p.FromChain.ChainID == o.FromChain.ChainID && p.ToChain.ChainID == o.ToChain.ChainID
|
||||
}
|
||||
|
||||
func (p *Path) Copy() *Path {
|
||||
newPath := &Path{
|
||||
ProcessorName: p.ProcessorName,
|
||||
AmountInLocked: p.AmountInLocked,
|
||||
TxGasAmount: p.TxGasAmount,
|
||||
ApprovalRequired: p.ApprovalRequired,
|
||||
ApprovalGasAmount: p.ApprovalGasAmount,
|
||||
EstimatedTime: p.EstimatedTime,
|
||||
SubtractFees: p.SubtractFees,
|
||||
}
|
||||
|
||||
if p.FromChain != nil {
|
||||
newPath.FromChain = ¶ms.Network{}
|
||||
*newPath.FromChain = *p.FromChain
|
||||
}
|
||||
|
||||
if p.ToChain != nil {
|
||||
newPath.ToChain = ¶ms.Network{}
|
||||
*newPath.ToChain = *p.ToChain
|
||||
}
|
||||
|
||||
if p.FromToken != nil {
|
||||
newPath.FromToken = &walletToken.Token{}
|
||||
*newPath.FromToken = *p.FromToken
|
||||
}
|
||||
|
||||
if p.ToToken != nil {
|
||||
newPath.ToToken = &walletToken.Token{}
|
||||
*newPath.ToToken = *p.ToToken
|
||||
}
|
||||
|
||||
if p.AmountIn != nil {
|
||||
newPath.AmountIn = (*hexutil.Big)(big.NewInt(0).Set(p.AmountIn.ToInt()))
|
||||
}
|
||||
|
||||
if p.AmountOut != nil {
|
||||
newPath.AmountOut = (*hexutil.Big)(big.NewInt(0).Set(p.AmountOut.ToInt()))
|
||||
}
|
||||
|
||||
if p.SuggestedLevelsForMaxFeesPerGas != nil {
|
||||
newPath.SuggestedLevelsForMaxFeesPerGas = &fees.MaxFeesLevels{
|
||||
Low: (*hexutil.Big)(big.NewInt(0).Set(p.SuggestedLevelsForMaxFeesPerGas.Low.ToInt())),
|
||||
Medium: (*hexutil.Big)(big.NewInt(0).Set(p.SuggestedLevelsForMaxFeesPerGas.Medium.ToInt())),
|
||||
High: (*hexutil.Big)(big.NewInt(0).Set(p.SuggestedLevelsForMaxFeesPerGas.High.ToInt())),
|
||||
}
|
||||
}
|
||||
|
||||
if p.MaxFeesPerGas != nil {
|
||||
newPath.MaxFeesPerGas = (*hexutil.Big)(big.NewInt(0).Set(p.MaxFeesPerGas.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxBaseFee != nil {
|
||||
newPath.TxBaseFee = (*hexutil.Big)(big.NewInt(0).Set(p.TxBaseFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxPriorityFee != nil {
|
||||
newPath.TxPriorityFee = (*hexutil.Big)(big.NewInt(0).Set(p.TxPriorityFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxBonderFees != nil {
|
||||
newPath.TxBonderFees = (*hexutil.Big)(big.NewInt(0).Set(p.TxBonderFees.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxTokenFees != nil {
|
||||
newPath.TxTokenFees = (*hexutil.Big)(big.NewInt(0).Set(p.TxTokenFees.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxFee != nil {
|
||||
newPath.TxFee = (*hexutil.Big)(big.NewInt(0).Set(p.TxFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxL1Fee != nil {
|
||||
newPath.TxL1Fee = (*hexutil.Big)(big.NewInt(0).Set(p.TxL1Fee.ToInt()))
|
||||
}
|
||||
|
||||
if p.ApprovalAmountRequired != nil {
|
||||
newPath.ApprovalAmountRequired = (*hexutil.Big)(big.NewInt(0).Set(p.ApprovalAmountRequired.ToInt()))
|
||||
}
|
||||
|
||||
if p.ApprovalContractAddress != nil {
|
||||
addr := common.HexToAddress(p.ApprovalContractAddress.Hex())
|
||||
newPath.ApprovalContractAddress = &addr
|
||||
}
|
||||
|
||||
if p.ApprovalBaseFee != nil {
|
||||
newPath.ApprovalBaseFee = (*hexutil.Big)(big.NewInt(0).Set(p.ApprovalBaseFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.ApprovalPriorityFee != nil {
|
||||
newPath.ApprovalPriorityFee = (*hexutil.Big)(big.NewInt(0).Set(p.ApprovalPriorityFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.ApprovalFee != nil {
|
||||
newPath.ApprovalFee = (*hexutil.Big)(big.NewInt(0).Set(p.ApprovalFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.ApprovalL1Fee != nil {
|
||||
newPath.ApprovalL1Fee = (*hexutil.Big)(big.NewInt(0).Set(p.ApprovalL1Fee.ToInt()))
|
||||
}
|
||||
|
||||
if p.TxTotalFee != nil {
|
||||
newPath.TxTotalFee = (*hexutil.Big)(big.NewInt(0).Set(p.TxTotalFee.ToInt()))
|
||||
}
|
||||
|
||||
if p.RequiredTokenBalance != nil {
|
||||
newPath.RequiredTokenBalance = big.NewInt(0).Set(p.RequiredTokenBalance)
|
||||
}
|
||||
|
||||
if p.RequiredNativeBalance != nil {
|
||||
newPath.RequiredNativeBalance = big.NewInt(0).Set(p.RequiredNativeBalance)
|
||||
}
|
||||
|
||||
return newPath
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/wallet/router/fees"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
)
|
||||
|
||||
func TestCopyPath(t *testing.T) {
|
||||
addr := common.HexToAddress("0x123")
|
||||
path := &Path{
|
||||
ProcessorName: "test",
|
||||
FromChain: ¶ms.Network{ChainID: 1},
|
||||
ToChain: ¶ms.Network{ChainID: 2},
|
||||
FromToken: &token.Token{Symbol: "symbol1"},
|
||||
ToToken: &token.Token{Symbol: "symbol2"},
|
||||
AmountIn: (*hexutil.Big)(big.NewInt(100)),
|
||||
AmountInLocked: true,
|
||||
AmountOut: (*hexutil.Big)(big.NewInt(200)),
|
||||
SuggestedLevelsForMaxFeesPerGas: &fees.MaxFeesLevels{
|
||||
Low: (*hexutil.Big)(big.NewInt(100)),
|
||||
Medium: (*hexutil.Big)(big.NewInt(200)),
|
||||
High: (*hexutil.Big)(big.NewInt(300)),
|
||||
},
|
||||
MaxFeesPerGas: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxBaseFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxPriorityFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxGasAmount: 100,
|
||||
TxBonderFees: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxTokenFees: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxL1Fee: (*hexutil.Big)(big.NewInt(100)),
|
||||
ApprovalRequired: true,
|
||||
ApprovalAmountRequired: (*hexutil.Big)(big.NewInt(100)),
|
||||
ApprovalContractAddress: &addr,
|
||||
ApprovalBaseFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
ApprovalPriorityFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
ApprovalGasAmount: 100,
|
||||
ApprovalFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
ApprovalL1Fee: (*hexutil.Big)(big.NewInt(100)),
|
||||
TxTotalFee: (*hexutil.Big)(big.NewInt(100)),
|
||||
EstimatedTime: fees.TransactionEstimation(100),
|
||||
RequiredTokenBalance: big.NewInt(100),
|
||||
RequiredNativeBalance: big.NewInt(100),
|
||||
SubtractFees: true,
|
||||
}
|
||||
|
||||
newPath := path.Copy()
|
||||
|
||||
assert.True(t, reflect.DeepEqual(path, newPath))
|
||||
}
|
|
@ -1325,7 +1325,7 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
|||
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
tm := &TransactionManager{NewMultiTransactionDB(db), nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
tm := &TransactionManager{NewMultiTransactionDB(db), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
|
||||
mediaServer, err := server.NewMediaServer(appdb, nil, nil, db)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package transfer
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/errors"
|
||||
)
|
||||
|
||||
// Abbreviation `WT` for the error code stands for Wallet Transfer
|
||||
var (
|
||||
ErrNoRoute = &errors.ErrorResponse{Code: errors.ErrorCode("WT-001"), Details: "no generated route"}
|
||||
ErrNoTrsansactionsBeingBuilt = &errors.ErrorResponse{Code: errors.ErrorCode("WT-002"), Details: "no transactions being built"}
|
||||
ErrMissingSignatureForTx = &errors.ErrorResponse{Code: errors.ErrorCode("WT-003"), Details: "missing signature for transaction %s"}
|
||||
ErrInvalidSignatureDetails = &errors.ErrorResponse{Code: errors.ErrorCode("WT-004"), Details: "invalid signature details"}
|
||||
)
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/status-im/status-go/params"
|
||||
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/router/routes"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
|
@ -26,6 +27,15 @@ type SignatureDetails struct {
|
|||
V string `json:"v"`
|
||||
}
|
||||
|
||||
func (sd *SignatureDetails) Validate() error {
|
||||
if len(sd.R) != 64 || len(sd.S) != 64 || len(sd.V) != 2 {
|
||||
return ErrInvalidSignatureDetails
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: remove this struct once mobile switches to the new approach
|
||||
type TransactionDescription struct {
|
||||
chainID uint64
|
||||
from common.Address
|
||||
|
@ -33,6 +43,20 @@ type TransactionDescription struct {
|
|||
signature []byte
|
||||
}
|
||||
|
||||
type RouterTransactionDetails struct {
|
||||
routerPath *routes.Path
|
||||
txArgs *transactions.SendTxArgs
|
||||
tx *ethTypes.Transaction
|
||||
txHashToSign types.Hash
|
||||
txSignature []byte
|
||||
txSentHash types.Hash
|
||||
approvalTxArgs *transactions.SendTxArgs
|
||||
approvalTx *ethTypes.Transaction
|
||||
approvalHashToSign types.Hash
|
||||
approvalSignature []byte
|
||||
approvalTxSentHash types.Hash
|
||||
}
|
||||
|
||||
type TransactionManager struct {
|
||||
storage MultiTransactionStorage
|
||||
gethManager *account.GethManager
|
||||
|
@ -42,9 +66,13 @@ type TransactionManager struct {
|
|||
pendingTracker *transactions.PendingTxTracker
|
||||
eventFeed *event.Feed
|
||||
|
||||
// TODO: remove this struct once mobile switches to the new approach
|
||||
multiTransactionForKeycardSigning *MultiTransaction
|
||||
multipathTransactionsData []*pathprocessor.MultipathProcessorTxArgs
|
||||
transactionsForKeycardSigning map[common.Hash]*TransactionDescription
|
||||
|
||||
// used in a new approach
|
||||
routerTransactions []*RouterTransactionDetails
|
||||
}
|
||||
|
||||
type MultiTransactionStorage interface {
|
||||
|
|
|
@ -198,6 +198,7 @@ func (tm *TransactionManager) WatchTransaction(ctx context.Context, chainID uint
|
|||
return err
|
||||
}
|
||||
if p.ChainID == wallet_common.ChainID(chainID) && p.Hash == transactionHash {
|
||||
signal.SendWalletEvent(signal.TransactionStatusChanged, p)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
package transfer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/errors"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/responses"
|
||||
"github.com/status-im/status-go/services/wallet/router/pathprocessor"
|
||||
"github.com/status-im/status-go/services/wallet/router/routes"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
)
|
||||
|
||||
type BuildRouteExtraParams struct {
|
||||
AddressFrom common.Address
|
||||
AddressTo common.Address
|
||||
Username string
|
||||
PublicKey string
|
||||
PackID *big.Int
|
||||
SlippagePercentage float32
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) ClearLocalRouterTransactionsData() {
|
||||
tm.routerTransactions = nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) ApprovalRequiredForPath(pathProcessorName string) bool {
|
||||
for _, desc := range tm.routerTransactions {
|
||||
if desc.routerPath.ProcessorName == pathProcessorName &&
|
||||
desc.routerPath.ApprovalRequired {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) ApprovalPlacedForPath(pathProcessorName string) bool {
|
||||
for _, desc := range tm.routerTransactions {
|
||||
if desc.routerPath.ProcessorName == pathProcessorName &&
|
||||
desc.approvalTxSentHash != (types.Hash{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) TxPlacedForPath(pathProcessorName string) bool {
|
||||
for _, desc := range tm.routerTransactions {
|
||||
if desc.routerPath.ProcessorName == pathProcessorName &&
|
||||
desc.txSentHash != (types.Hash{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) buildApprovalTxForPath(path *routes.Path, addressFrom common.Address,
|
||||
usedNonces map[uint64]int64, signer ethTypes.Signer) (types.Hash, error) {
|
||||
lastUsedNonce := int64(-1)
|
||||
if nonce, ok := usedNonces[path.FromChain.ChainID]; ok {
|
||||
lastUsedNonce = nonce
|
||||
}
|
||||
|
||||
data, err := walletCommon.PackApprovalInputData(path.AmountIn.ToInt(), path.ApprovalContractAddress)
|
||||
if err != nil {
|
||||
return types.Hash{}, err
|
||||
}
|
||||
|
||||
addrTo := types.Address(path.FromToken.Address)
|
||||
approavalSendArgs := &transactions.SendTxArgs{
|
||||
Version: transactions.SendTxArgsVersion1,
|
||||
|
||||
// tx fields
|
||||
From: types.Address(addressFrom),
|
||||
To: &addrTo,
|
||||
Value: (*hexutil.Big)(big.NewInt(0)),
|
||||
Data: data,
|
||||
Gas: (*hexutil.Uint64)(&path.ApprovalGasAmount),
|
||||
MaxFeePerGas: path.MaxFeesPerGas,
|
||||
MaxPriorityFeePerGas: path.ApprovalPriorityFee,
|
||||
|
||||
// additional fields version 1
|
||||
FromChainID: path.FromChain.ChainID,
|
||||
}
|
||||
if path.FromToken != nil {
|
||||
approavalSendArgs.FromTokenID = path.FromToken.Symbol
|
||||
}
|
||||
|
||||
builtApprovalTx, usedNonce, err := tm.transactor.ValidateAndBuildTransaction(approavalSendArgs.FromChainID, *approavalSendArgs, lastUsedNonce)
|
||||
if err != nil {
|
||||
return types.Hash{}, err
|
||||
}
|
||||
approvalTxHash := signer.Hash(builtApprovalTx)
|
||||
usedNonces[path.FromChain.ChainID] = int64(usedNonce)
|
||||
|
||||
tm.routerTransactions = append(tm.routerTransactions, &RouterTransactionDetails{
|
||||
routerPath: path,
|
||||
approvalTxArgs: approavalSendArgs,
|
||||
approvalTx: builtApprovalTx,
|
||||
approvalHashToSign: types.Hash(approvalTxHash),
|
||||
})
|
||||
|
||||
return types.Hash(approvalTxHash), nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) buildTxForPath(path *routes.Path, pathProcessors map[string]pathprocessor.PathProcessor,
|
||||
usedNonces map[uint64]int64, signer ethTypes.Signer, params BuildRouteExtraParams) (types.Hash, error) {
|
||||
lastUsedNonce := int64(-1)
|
||||
if nonce, ok := usedNonces[path.FromChain.ChainID]; ok {
|
||||
lastUsedNonce = nonce
|
||||
}
|
||||
|
||||
processorInputParams := pathprocessor.ProcessorInputParams{
|
||||
FromAddr: params.AddressFrom,
|
||||
ToAddr: params.AddressTo,
|
||||
FromChain: path.FromChain,
|
||||
ToChain: path.ToChain,
|
||||
FromToken: path.FromToken,
|
||||
ToToken: path.ToToken,
|
||||
AmountIn: path.AmountIn.ToInt(),
|
||||
AmountOut: path.AmountOut.ToInt(),
|
||||
|
||||
Username: params.Username,
|
||||
PublicKey: params.PublicKey,
|
||||
PackID: params.PackID,
|
||||
}
|
||||
|
||||
data, err := pathProcessors[path.ProcessorName].PackTxInputData(processorInputParams)
|
||||
if err != nil {
|
||||
return types.Hash{}, err
|
||||
}
|
||||
|
||||
addrTo := types.Address(params.AddressTo)
|
||||
sendArgs := &transactions.SendTxArgs{
|
||||
Version: transactions.SendTxArgsVersion1,
|
||||
|
||||
// tx fields
|
||||
From: types.Address(params.AddressFrom),
|
||||
To: &addrTo,
|
||||
Value: path.AmountIn,
|
||||
Data: data,
|
||||
Gas: (*hexutil.Uint64)(&path.TxGasAmount),
|
||||
MaxFeePerGas: path.MaxFeesPerGas,
|
||||
MaxPriorityFeePerGas: path.TxPriorityFee,
|
||||
|
||||
// additional fields version 1
|
||||
ValueOut: path.AmountOut,
|
||||
FromChainID: path.FromChain.ChainID,
|
||||
ToChainID: path.ToChain.ChainID,
|
||||
SlippagePercentage: params.SlippagePercentage,
|
||||
}
|
||||
if path.FromToken != nil {
|
||||
sendArgs.FromTokenID = path.FromToken.Symbol
|
||||
sendArgs.ToContractAddress = types.Address(path.FromToken.Address)
|
||||
|
||||
// special handling for transfer tx if selected token is not ETH
|
||||
// TODO: we should fix that in the trasactor, but till then, the best place to handle it is here
|
||||
if !path.FromToken.IsNative() {
|
||||
sendArgs.Value = (*hexutil.Big)(big.NewInt(0))
|
||||
|
||||
if path.ProcessorName == pathprocessor.ProcessorTransferName ||
|
||||
path.ProcessorName == pathprocessor.ProcessorStickersBuyName ||
|
||||
path.ProcessorName == pathprocessor.ProcessorENSRegisterName ||
|
||||
path.ProcessorName == pathprocessor.ProcessorENSReleaseName ||
|
||||
path.ProcessorName == pathprocessor.ProcessorENSPublicKeyName {
|
||||
// TODO: update functions from `TransactorIface` to use `ToContractAddress` (as an address of the contract a transaction should be sent to)
|
||||
// and `To` (as the destination address, recipient) of `SendTxArgs` struct appropriately
|
||||
toContractAddr := types.Address(path.FromToken.Address)
|
||||
sendArgs.To = &toContractAddr
|
||||
}
|
||||
}
|
||||
}
|
||||
if path.ToToken != nil {
|
||||
sendArgs.ToTokenID = path.ToToken.Symbol
|
||||
}
|
||||
|
||||
builtTx, usedNonce, err := pathProcessors[path.ProcessorName].BuildTransactionV2(sendArgs, lastUsedNonce)
|
||||
if err != nil {
|
||||
return types.Hash{}, err
|
||||
}
|
||||
txHash := signer.Hash(builtTx)
|
||||
usedNonces[path.FromChain.ChainID] = int64(usedNonce)
|
||||
|
||||
tm.routerTransactions = append(tm.routerTransactions, &RouterTransactionDetails{
|
||||
routerPath: path,
|
||||
txArgs: sendArgs,
|
||||
tx: builtTx,
|
||||
txHashToSign: types.Hash(txHash),
|
||||
})
|
||||
|
||||
return types.Hash(txHash), nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) BuildTransactionsFromRoute(route routes.Route, pathProcessors map[string]pathprocessor.PathProcessor,
|
||||
params BuildRouteExtraParams) (*responses.SigningDetails, error) {
|
||||
if len(route) == 0 {
|
||||
return nil, ErrNoRoute
|
||||
}
|
||||
|
||||
accFrom, err := tm.accountsDB.GetAccountByAddress(types.Address(params.AddressFrom))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keypair, err := tm.accountsDB.GetKeypairByKeyUID(accFrom.KeyUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &responses.SigningDetails{
|
||||
Address: accFrom.Address,
|
||||
AddressPath: accFrom.Path,
|
||||
KeyUid: accFrom.KeyUID,
|
||||
SignOnKeycard: keypair.MigratedToKeycard(),
|
||||
}
|
||||
|
||||
usedNonces := make(map[uint64]int64)
|
||||
for _, path := range route {
|
||||
signer := ethTypes.NewLondonSigner(big.NewInt(int64(path.FromChain.ChainID)))
|
||||
|
||||
// always check for approval tx first for the path and build it if needed
|
||||
if path.ApprovalRequired && !tm.ApprovalPlacedForPath(path.ProcessorName) {
|
||||
approvalTxHash, err := tm.buildApprovalTxForPath(path, params.AddressFrom, usedNonces, signer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Hashes = append(response.Hashes, approvalTxHash)
|
||||
|
||||
// if approval is needed for swap, we cannot build the swap tx before the approval tx is mined
|
||||
if path.ProcessorName == pathprocessor.ProcessorSwapParaswapName {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// build tx for the path
|
||||
txHash, err := tm.buildTxForPath(path, pathProcessors, usedNonces, signer, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Hashes = append(response.Hashes, txHash)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func getSignatureForTxHash(txHash string, signatures map[string]SignatureDetails) ([]byte, error) {
|
||||
sigDetails, ok := signatures[txHash]
|
||||
if !ok {
|
||||
err := &errors.ErrorResponse{
|
||||
Code: ErrMissingSignatureForTx.Code,
|
||||
Details: fmt.Sprintf(ErrMissingSignatureForTx.Details, txHash),
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := sigDetails.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rBytes, _ := hex.DecodeString(sigDetails.R)
|
||||
sBytes, _ := hex.DecodeString(sigDetails.S)
|
||||
vByte := byte(0)
|
||||
if sigDetails.V == "01" {
|
||||
vByte = 1
|
||||
}
|
||||
|
||||
signature := make([]byte, crypto.SignatureLength)
|
||||
copy(signature[32-len(rBytes):32], rBytes)
|
||||
copy(signature[64-len(rBytes):64], sBytes)
|
||||
signature[64] = vByte
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) ValidateAndAddSignaturesToRouterTransactions(signatures map[string]SignatureDetails) error {
|
||||
if len(tm.routerTransactions) == 0 {
|
||||
return ErrNoTrsansactionsBeingBuilt
|
||||
}
|
||||
|
||||
// check if all transactions have been signed
|
||||
for _, desc := range tm.routerTransactions {
|
||||
if desc.approvalTx != nil && desc.approvalTxSentHash == (types.Hash{}) {
|
||||
sig, err := getSignatureForTxHash(desc.approvalHashToSign.String(), signatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desc.approvalSignature = sig
|
||||
}
|
||||
|
||||
if desc.tx != nil && desc.txSentHash == (types.Hash{}) {
|
||||
sig, err := getSignatureForTxHash(desc.txHashToSign.String(), signatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desc.txSignature = sig
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TransactionManager) SendRouterTransactions(ctx context.Context, multiTx *MultiTransaction) (transactions []*responses.RouterSentTransaction, err error) {
|
||||
transactions = make([]*responses.RouterSentTransaction, 0)
|
||||
|
||||
// send transactions
|
||||
for _, desc := range tm.routerTransactions {
|
||||
if desc.approvalTx != nil && desc.approvalTxSentHash == (types.Hash{}) {
|
||||
var approvalTxWithSignature *ethTypes.Transaction
|
||||
approvalTxWithSignature, err = tm.transactor.AddSignatureToTransaction(desc.approvalTxArgs.FromChainID, desc.approvalTx, desc.approvalSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
desc.approvalTxSentHash, err = tm.transactor.SendTransactionWithSignature(common.Address(desc.approvalTxArgs.From), desc.approvalTxArgs.FromTokenID, multiTx.ID, approvalTxWithSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = append(transactions, responses.NewRouterSentTransaction(desc.approvalTxArgs, desc.approvalTxSentHash, true))
|
||||
|
||||
// if approval is needed for swap, then we need to wait for the approval tx to be mined before sending the swap tx
|
||||
if desc.routerPath.ProcessorName == pathprocessor.ProcessorSwapParaswapName {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if desc.tx != nil && desc.txSentHash == (types.Hash{}) {
|
||||
var txWithSignature *ethTypes.Transaction
|
||||
txWithSignature, err = tm.transactor.AddSignatureToTransaction(desc.txArgs.FromChainID, desc.tx, desc.txSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
desc.txSentHash, err = tm.transactor.SendTransactionWithSignature(common.Address(desc.txArgs.From), desc.txArgs.FromTokenID, multiTx.ID, txWithSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = append(transactions, responses.NewRouterSentTransaction(desc.txArgs, desc.txSentHash, false))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -3,9 +3,13 @@ package signal
|
|||
type SignalType string
|
||||
|
||||
const (
|
||||
Wallet = SignalType("wallet")
|
||||
SignTransactions = SignalType("wallet.sign.transactions")
|
||||
SuggestedRoutes = SignalType("wallet.suggested.routes")
|
||||
Wallet = SignalType("wallet")
|
||||
SignTransactions = SignalType("wallet.sign.transactions")
|
||||
RouterSendingTransactionsStarted = SignalType("wallet.router.sending-transactions-started")
|
||||
SignRouterTransactions = SignalType("wallet.router.sign-transactions")
|
||||
RouterTransactionsSent = SignalType("wallet.router.transactions-sent")
|
||||
TransactionStatusChanged = SignalType("wallet.transaction.status-changed")
|
||||
SuggestedRoutes = SignalType("wallet.suggested.routes")
|
||||
)
|
||||
|
||||
// SendWalletEvent sends event from services/wallet/events.
|
||||
|
|
|
@ -443,6 +443,11 @@ func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args Se
|
|||
}
|
||||
|
||||
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error) {
|
||||
symbol := args.Symbol
|
||||
if args.Version == SendTxArgsVersion1 {
|
||||
symbol = args.FromTokenID
|
||||
}
|
||||
|
||||
if err = t.validateAccount(args, selectedAccount); err != nil {
|
||||
return hash, nonce, err
|
||||
}
|
||||
|
@ -458,7 +463,7 @@ func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccoun
|
|||
return hash, nonce, err
|
||||
}
|
||||
|
||||
hash, err = t.sendTransaction(rpcWrapper, common.Address(args.From), args.Symbol, args.MultiTransactionID, signedTx)
|
||||
hash, err = t.sendTransaction(rpcWrapper, common.Address(args.From), symbol, args.MultiTransactionID, signedTx)
|
||||
return hash, tx.Nonce(), err
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue