feat(wallet)_: common endpoints added to wallet api

- `SignMessage`
- `BuildTransaction`
- `SendTransactionWithSignature`
This commit is contained in:
Sale Djenic 2023-11-28 18:59:23 +01:00 committed by saledjenic
parent 568371e836
commit dea0b62650
6 changed files with 278 additions and 113 deletions

View File

@ -1 +1 @@
0.171.21 0.171.22

View File

@ -2,6 +2,7 @@ package wallet
import ( import (
"context" "context"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -524,6 +525,37 @@ func (api *API) GetAddressDetails(ctx context.Context, chainID uint64, address s
return result, nil return result, nil
} }
func (api *API) SignMessage(ctx context.Context, message types.HexBytes, address common.Address, password string) (string, error) {
log.Debug("[WalletAPI::SignMessage]", "message", message, "address", address)
return api.s.transactionManager.SignMessage(message, address, password)
}
func (api *API) BuildTransaction(ctx context.Context, chainID uint64, sendTxArgsJSON string) (response *transfer.TxResponse, err error) {
log.Debug("[WalletAPI::BuildTransaction]", "chainID", chainID, "sendTxArgsJSON", sendTxArgsJSON)
var params transactions.SendTxArgs
err = json.Unmarshal([]byte(sendTxArgsJSON), &params)
if err != nil {
return nil, err
}
return api.s.transactionManager.BuildTransaction(chainID, params)
}
func (api *API) SendTransactionWithSignature(ctx context.Context, chainID uint64, txType transactions.PendingTrxType,
sendTxArgsJSON string, signature string) (hash types.Hash, err error) {
log.Debug("[WalletAPI::SendTransactionWithSignature]", "chainID", chainID, "txType", txType, "sendTxArgsJSON", sendTxArgsJSON, "signature", signature)
sig, err := hex.DecodeString(signature)
if err != nil {
return hash, err
}
var params transactions.SendTxArgs
err = json.Unmarshal([]byte(sendTxArgsJSON), &params)
if err != nil {
return hash, err
}
return api.s.transactionManager.SendTransactionWithSignature(chainID, txType, params, sig)
}
func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*bridge.TransactionBridge, password string) (*transfer.MultiTransactionCommandResult, error) { func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*bridge.TransactionBridge, password string) (*transfer.MultiTransactionCommandResult, error) {
log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction") log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction")
return api.s.transactionManager.CreateMultiTransactionFromCommand(ctx, multiTransactionCommand, data, api.router.bridges, password) return api.s.transactionManager.CreateMultiTransactionFromCommand(ctx, multiTransactionCommand, data, api.router.bridges, password)

View File

@ -0,0 +1,225 @@
package transfer
import (
"database/sql"
"fmt"
"math/big"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/event"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/bridge"
wallet_common "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/walletevent"
"github.com/status-im/status-go/transactions"
)
type MultiTransactionIDType int64
const (
NoMultiTransactionID = MultiTransactionIDType(0)
// EventMTTransactionUpdate is emitted when a multi-transaction is updated (added or deleted)
EventMTTransactionUpdate walletevent.EventType = "multi-transaction-update"
)
type SignatureDetails struct {
R string `json:"r"`
S string `json:"s"`
V string `json:"v"`
}
type TransactionDescription struct {
chainID uint64
builtTx *ethTypes.Transaction
signature []byte
unlock transactions.UnlockNonceFunc
}
type TransactionManager struct {
db *sql.DB
gethManager *account.GethManager
transactor *transactions.Transactor
config *params.NodeConfig
accountsDB *accounts.Database
pendingTracker *transactions.PendingTxTracker
eventFeed *event.Feed
multiTransactionForKeycardSigning *MultiTransaction
transactionsBridgeData []*bridge.TransactionBridge
transactionsForKeycardSingning map[common.Hash]*TransactionDescription
}
func NewTransactionManager(
db *sql.DB,
gethManager *account.GethManager,
transactor *transactions.Transactor,
config *params.NodeConfig,
accountsDB *accounts.Database,
pendingTxManager *transactions.PendingTxTracker,
eventFeed *event.Feed,
) *TransactionManager {
return &TransactionManager{
db: db,
gethManager: gethManager,
transactor: transactor,
config: config,
accountsDB: accountsDB,
pendingTracker: pendingTxManager,
eventFeed: eventFeed,
}
}
var (
emptyHash = common.Hash{}
)
type MultiTransactionType uint8
const (
MultiTransactionSend = iota
MultiTransactionSwap
MultiTransactionBridge
)
type MultiTransaction struct {
ID uint `json:"id"`
Timestamp uint64 `json:"timestamp"`
FromNetworkID uint64 `json:"fromNetworkID"`
ToNetworkID uint64 `json:"toNetworkID"`
FromTxHash common.Hash `json:"fromTxHash"`
ToTxHash common.Hash `json:"toTxHash"`
FromAddress common.Address `json:"fromAddress"`
ToAddress common.Address `json:"toAddress"`
FromAsset string `json:"fromAsset"`
ToAsset string `json:"toAsset"`
FromAmount *hexutil.Big `json:"fromAmount"`
ToAmount *hexutil.Big `json:"toAmount"`
Type MultiTransactionType `json:"type"`
CrossTxID string
}
type MultiTransactionCommand struct {
FromAddress common.Address `json:"fromAddress"`
ToAddress common.Address `json:"toAddress"`
FromAsset string `json:"fromAsset"`
ToAsset string `json:"toAsset"`
FromAmount *hexutil.Big `json:"fromAmount"`
Type MultiTransactionType `json:"type"`
}
type MultiTransactionCommandResult struct {
ID int64 `json:"id"`
Hashes map[uint64][]types.Hash `json:"hashes"`
}
type TransactionIdentity struct {
ChainID wallet_common.ChainID `json:"chainId"`
Hash common.Hash `json:"hash"`
Address common.Address `json:"address"`
}
type TxResponse struct {
KeyUID string `json:"keyUid,omitempty"`
Address types.Address `json:"address,omitempty"`
AddressPath string `json:"addressPath,omitempty"`
SignOnKeycard bool `json:"signOnKeycard,omitempty"`
ChainID uint64 `json:"chainId,omitempty"`
MesageToSign interface{} `json:"messageToSign,omitempty"`
TxArgs transactions.SendTxArgs `json:"txArgs,omitempty"`
}
func (tm *TransactionManager) SignMessage(message types.HexBytes, address common.Address, password string) (string, error) {
selectedAccount, err := tm.gethManager.VerifyAccountPassword(tm.config.KeyStoreDir, address.Hex(), password)
if err != nil {
return "", err
}
signature, err := crypto.Sign(message[:], selectedAccount.PrivateKey)
return types.EncodeHex(signature), err
}
func (tm *TransactionManager) BuildTransaction(chainID uint64, sendArgs transactions.SendTxArgs) (response *TxResponse, err error) {
account, err := tm.accountsDB.GetAccountByAddress(sendArgs.From)
if err != nil {
return nil, fmt.Errorf("failed to resolve account: %w", err)
}
kp, err := tm.accountsDB.GetKeypairByKeyUID(account.KeyUID)
if err != nil {
return nil, err
}
txBeingSigned, unlock, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs)
if err != nil {
return nil, err
}
// We have to unlock the nonce, cause we don't know what will happen on the client side (will user accept/reject) an action.
if unlock != nil {
defer func() {
unlock(false, 0)
}()
}
// Set potential missing fields that were added while building the transaction
if sendArgs.Value == nil {
value := hexutil.Big(*txBeingSigned.Value())
sendArgs.Value = &value
}
if sendArgs.Nonce == nil {
nonce := hexutil.Uint64(txBeingSigned.Nonce())
sendArgs.Nonce = &nonce
}
if sendArgs.GasPrice == nil {
gasPrice := hexutil.Big(*txBeingSigned.GasPrice())
sendArgs.GasPrice = &gasPrice
}
if sendArgs.MaxPriorityFeePerGas == nil {
maxPriorityFeePerGas := hexutil.Big(*txBeingSigned.GasTipCap())
sendArgs.MaxPriorityFeePerGas = &maxPriorityFeePerGas
}
if sendArgs.MaxFeePerGas == nil {
maxFeePerGas := hexutil.Big(*txBeingSigned.GasFeeCap())
sendArgs.MaxFeePerGas = &maxFeePerGas
}
signer := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
return &TxResponse{
KeyUID: account.KeyUID,
Address: account.Address,
AddressPath: account.Path,
SignOnKeycard: kp.MigratedToKeycard(),
ChainID: chainID,
MesageToSign: signer.Hash(txBeingSigned),
TxArgs: sendArgs,
}, nil
}
func (tm *TransactionManager) SendTransactionWithSignature(chainID uint64, txType transactions.PendingTrxType, sendArgs transactions.SendTxArgs, signature []byte) (hash types.Hash, err error) {
hash, err = tm.transactor.BuildTransactionAndSendWithSignature(chainID, sendArgs, signature)
if err != nil {
return hash, err
}
err = tm.pendingTracker.TrackPendingTransaction(
wallet_common.ChainID(chainID),
common.Hash(hash),
common.Address(sendArgs.From),
txType,
transactions.AutoDelete,
)
if err != nil {
return hash, err
}
return hash, nil
}

View File

@ -19,8 +19,6 @@ import (
"github.com/status-im/status-go/account" "github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"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"
"github.com/status-im/status-go/services/wallet/bridge" "github.com/status-im/status-go/services/wallet/bridge"
wallet_common "github.com/status-im/status-go/services/wallet/common" wallet_common "github.com/status-im/status-go/services/wallet/common"
@ -29,111 +27,6 @@ import (
"github.com/status-im/status-go/transactions" "github.com/status-im/status-go/transactions"
) )
type MultiTransactionIDType int64
const (
NoMultiTransactionID = MultiTransactionIDType(0)
// EventMTTransactionUpdate is emitted when a multi-transaction is updated (added or deleted)
EventMTTransactionUpdate walletevent.EventType = "multi-transaction-update"
)
type SignatureDetails struct {
R string `json:"r"`
S string `json:"s"`
V string `json:"v"`
}
type TransactionDescription struct {
chainID uint64
builtTx *ethTypes.Transaction
signature []byte
unlock transactions.UnlockNonceFunc
}
type TransactionManager struct {
db *sql.DB
gethManager *account.GethManager
transactor *transactions.Transactor
config *params.NodeConfig
accountsDB *accounts.Database
pendingTracker *transactions.PendingTxTracker
eventFeed *event.Feed
multiTransactionForKeycardSigning *MultiTransaction
transactionsBridgeData []*bridge.TransactionBridge
transactionsForKeycardSingning map[common.Hash]*TransactionDescription
}
func NewTransactionManager(
db *sql.DB,
gethManager *account.GethManager,
transactor *transactions.Transactor,
config *params.NodeConfig,
accountsDB *accounts.Database,
pendingTxManager *transactions.PendingTxTracker,
eventFeed *event.Feed,
) *TransactionManager {
return &TransactionManager{
db: db,
gethManager: gethManager,
transactor: transactor,
config: config,
accountsDB: accountsDB,
pendingTracker: pendingTxManager,
eventFeed: eventFeed,
}
}
var (
emptyHash = common.Hash{}
)
type MultiTransactionType uint8
const (
MultiTransactionSend = iota
MultiTransactionSwap
MultiTransactionBridge
)
type MultiTransaction struct {
ID uint `json:"id"`
Timestamp uint64 `json:"timestamp"`
FromNetworkID uint64 `json:"fromNetworkID"`
ToNetworkID uint64 `json:"toNetworkID"`
FromTxHash common.Hash `json:"fromTxHash"`
ToTxHash common.Hash `json:"toTxHash"`
FromAddress common.Address `json:"fromAddress"`
ToAddress common.Address `json:"toAddress"`
FromAsset string `json:"fromAsset"`
ToAsset string `json:"toAsset"`
FromAmount *hexutil.Big `json:"fromAmount"`
ToAmount *hexutil.Big `json:"toAmount"`
Type MultiTransactionType `json:"type"`
CrossTxID string
}
type MultiTransactionCommand struct {
FromAddress common.Address `json:"fromAddress"`
ToAddress common.Address `json:"toAddress"`
FromAsset string `json:"fromAsset"`
ToAsset string `json:"toAsset"`
FromAmount *hexutil.Big `json:"fromAmount"`
Type MultiTransactionType `json:"type"`
}
type MultiTransactionCommandResult struct {
ID int64 `json:"id"`
Hashes map[uint64][]types.Hash `json:"hashes"`
}
type TransactionIdentity struct {
ChainID wallet_common.ChainID `json:"chainId"`
Hash common.Hash `json:"hash"`
Address common.Address `json:"address"`
}
const multiTransactionColumns = "from_network_id, from_tx_hash, from_address, from_asset, from_amount, to_network_id, to_tx_hash, to_address, to_asset, to_amount, type, cross_tx_id, timestamp" const multiTransactionColumns = "from_network_id, from_tx_hash, from_address, from_asset, from_amount, to_network_id, to_tx_hash, to_address, to_asset, to_amount, type, cross_tx_id, timestamp"
const selectMultiTransactionColumns = "COALESCE(from_network_id, 0), from_tx_hash, from_address, from_asset, from_amount, COALESCE(to_network_id, 0), to_tx_hash, to_address, to_asset, to_amount, type, cross_tx_id, timestamp" const selectMultiTransactionColumns = "COALESCE(from_network_id, 0), from_tx_hash, from_address, from_asset, from_amount, COALESCE(to_network_id, 0), to_tx_hash, to_address, to_asset, to_amount, type, cross_tx_id, timestamp"

View File

@ -191,7 +191,8 @@ func (t *Transactor) BuildTransactionAndSendWithSignature(chainID uint64, args S
return hash, &ErrBadNonce{tx.Nonce(), expectedNonce} return hash, &ErrBadNonce{tx.Nonce(), expectedNonce}
} }
return t.AddSignatureToTransactionAndSend(chainID, tx, sig) hash, err = t.AddSignatureToTransactionAndSend(chainID, tx, sig)
return hash, err
} }
func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) { func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) {
@ -387,10 +388,24 @@ func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccoun
} }
func (t *Transactor) buildTransaction(args SendTxArgs) *gethtypes.Transaction { func (t *Transactor) buildTransaction(args SendTxArgs) *gethtypes.Transaction {
nonce := uint64(*args.Nonce) var (
value := (*big.Int)(args.Value) nonce uint64
gas := uint64(*args.Gas) value *big.Int
gasPrice := (*big.Int)(args.GasPrice) gas uint64
gasPrice *big.Int
)
if args.Nonce != nil {
nonce = uint64(*args.Nonce)
}
if args.Value != nil {
value = (*big.Int)(args.Value)
}
if args.Gas != nil {
gas = uint64(*args.Gas)
}
if args.GasPrice != nil {
gasPrice = (*big.Int)(args.GasPrice)
}
return t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args) return t.buildTransactionWithOverrides(nonce, value, gas, gasPrice, args)
} }