2023-11-06 19:04:42 +00:00
|
|
|
package walletconnect
|
|
|
|
|
|
|
|
import (
|
2023-11-17 15:28:37 +00:00
|
|
|
"encoding/hex"
|
2023-11-06 19:04:42 +00:00
|
|
|
"encoding/json"
|
2023-11-17 15:28:37 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
2023-11-06 19:04:42 +00:00
|
|
|
"strings"
|
|
|
|
|
2023-11-17 15:28:37 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
2023-11-24 15:39:36 +00:00
|
|
|
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
2023-11-06 19:04:42 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
|
|
"github.com/status-im/status-go/transactions"
|
|
|
|
)
|
|
|
|
|
|
|
|
// sendTransactionParams instead of transactions.SendTxArgs to allow parsing of hex Uint64 with leading 0 ("0x01") and empty hex value ("0x")
|
|
|
|
type sendTransactionParams struct {
|
|
|
|
transactions.SendTxArgs
|
|
|
|
Nonce JSONProxyType `json:"nonce"`
|
|
|
|
Gas JSONProxyType `json:"gas"`
|
|
|
|
GasPrice JSONProxyType `json:"gasPrice"`
|
|
|
|
Value JSONProxyType `json:"value"`
|
|
|
|
MaxFeePerGas JSONProxyType `json:"maxFeePerGas"`
|
|
|
|
MaxPriorityFeePerGas JSONProxyType `json:"maxPriorityFeePerGas"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *sendTransactionParams) UnmarshalJSON(data []byte) error {
|
|
|
|
// Avoid recursion
|
|
|
|
type Alias sendTransactionParams
|
|
|
|
var alias Alias
|
|
|
|
// Fix hex values with leading 0 or empty
|
|
|
|
fixWCHexValues := func(input []byte) ([]byte, error) {
|
|
|
|
hexStr := string(input)
|
|
|
|
if !strings.HasPrefix(hexStr, "\"0x") {
|
|
|
|
return input, nil
|
|
|
|
}
|
|
|
|
trimmedStr := strings.TrimPrefix(hexStr, "\"0x")
|
|
|
|
fixedStrNoPrefix := strings.TrimLeft(trimmedStr, "0")
|
|
|
|
fixedStr := "\"0x" + fixedStrNoPrefix
|
|
|
|
if fixedStr == "\"0x\"" {
|
|
|
|
fixedStr = "\"0x0\""
|
|
|
|
}
|
|
|
|
|
|
|
|
return []byte(fixedStr), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
alias.Nonce = JSONProxyType{target: &alias.SendTxArgs.Nonce, transform: fixWCHexValues}
|
|
|
|
alias.Gas = JSONProxyType{target: &alias.SendTxArgs.Gas, transform: fixWCHexValues}
|
|
|
|
alias.GasPrice = JSONProxyType{target: &alias.SendTxArgs.GasPrice, transform: fixWCHexValues}
|
|
|
|
alias.Value = JSONProxyType{target: &alias.SendTxArgs.Value, transform: fixWCHexValues}
|
|
|
|
alias.MaxFeePerGas = JSONProxyType{target: &alias.SendTxArgs.MaxFeePerGas, transform: fixWCHexValues}
|
|
|
|
alias.MaxPriorityFeePerGas = JSONProxyType{target: &alias.SendTxArgs.MaxPriorityFeePerGas, transform: fixWCHexValues}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &alias); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*n = sendTransactionParams(alias)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *sendTransactionParams) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(n.SendTxArgs)
|
|
|
|
}
|
|
|
|
|
2023-11-17 15:28:37 +00:00
|
|
|
func (s *Service) buildTransaction(request SessionRequest) (response *SessionRequestResponse, err error) {
|
2023-11-06 19:04:42 +00:00
|
|
|
if len(request.Params.Request.Params) != 1 {
|
|
|
|
return nil, ErrorInvalidParamsCount
|
|
|
|
}
|
|
|
|
|
|
|
|
var params sendTransactionParams
|
2023-11-17 15:28:37 +00:00
|
|
|
if err = json.Unmarshal(request.Params.Request.Params[0], ¶ms); err != nil {
|
2023-11-06 19:04:42 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-17 15:28:37 +00:00
|
|
|
account, err := s.accountsDB.GetAccountByAddress(params.From)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get active account: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
kp, err := s.accountsDB.GetKeypairByKeyUID(account.KeyUID)
|
2023-11-06 19:04:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:27:05 +00:00
|
|
|
_, chainID, err := parseCaip2ChainID(request.Params.ChainID)
|
2023-11-06 19:04:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-17 15:28:37 +00:00
|
|
|
// In this case we can ignore `unlock` function received from `ValidateAndBuildTransaction` cause `Nonce`
|
|
|
|
// will be always set by the initiator of this transaction (by the dapp).
|
|
|
|
// Though we will need sort out completely that part since Nonce kept in the local cache is not the most recent one,
|
|
|
|
// instead of that we should always ask network what's the most recent known Nonce for the account.
|
|
|
|
// Logged issue to handle that: https://github.com/status-im/status-go/issues/4335
|
|
|
|
txBeingSigned, _, err := s.transactor.ValidateAndBuildTransaction(chainID, params.SendTxArgs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.txSignDetails = &txSigningDetails{
|
|
|
|
from: common.Address(account.Address),
|
|
|
|
chainID: chainID,
|
|
|
|
txBeingSigned: txBeingSigned,
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
signer := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
2023-11-17 15:28:37 +00:00
|
|
|
return &SessionRequestResponse{
|
|
|
|
KeyUID: account.KeyUID,
|
|
|
|
Address: account.Address,
|
|
|
|
AddressPath: account.Path,
|
|
|
|
SignOnKeycard: kp.MigratedToKeycard(),
|
2023-11-26 15:50:12 +00:00
|
|
|
MessageToSign: signer.Hash(txBeingSigned),
|
2023-11-17 15:28:37 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
func (s *Service) addSignatureToTransaction(signature string) (*ethTypes.Transaction, error) {
|
2023-11-17 15:28:37 +00:00
|
|
|
if s.txSignDetails.txBeingSigned == nil {
|
2023-11-24 15:39:36 +00:00
|
|
|
return nil, errors.New("no tx to sign")
|
2023-11-17 15:28:37 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
signatureBytes, err := hex.DecodeString(signature)
|
2023-11-06 19:04:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
return s.transactor.AddSignatureToTransaction(s.txSignDetails.chainID, s.txSignDetails.txBeingSigned, signatureBytes)
|
2023-11-06 19:04:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
func (s *Service) buildMessage(request SessionRequest, addressIndex int, messageIndex int,
|
|
|
|
handleTypedData bool) (response *SessionRequestResponse, err error) {
|
2023-11-06 19:04:42 +00:00
|
|
|
if len(request.Params.Request.Params) != 2 {
|
|
|
|
return nil, ErrorInvalidParamsCount
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
if addressIndex > 1 || addressIndex < 0 || messageIndex > 1 || messageIndex < 0 {
|
|
|
|
return nil, ErrorInvalidAddressMsgIndex
|
|
|
|
}
|
|
|
|
|
2023-11-06 19:04:42 +00:00
|
|
|
var address types.Address
|
2023-11-24 15:39:36 +00:00
|
|
|
if err := json.Unmarshal(request.Params.Request.Params[addressIndex], &address); err != nil {
|
2023-11-06 19:04:42 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-17 15:28:37 +00:00
|
|
|
account, err := s.accountsDB.GetAccountByAddress(address)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get active account: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
kp, err := s.accountsDB.GetKeypairByKeyUID(account.KeyUID)
|
2023-11-06 19:04:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
var hash []byte
|
|
|
|
if !handleTypedData {
|
|
|
|
var dBytes types.HexBytes
|
|
|
|
if err := json.Unmarshal(request.Params.Request.Params[messageIndex], &dBytes); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hash = crypto.TextHash(dBytes)
|
|
|
|
} else {
|
|
|
|
var typedDataJSON string
|
|
|
|
if err := json.Unmarshal(request.Params.Request.Params[messageIndex], &typedDataJSON); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-06 19:04:42 +00:00
|
|
|
|
2023-11-24 15:39:36 +00:00
|
|
|
var typedData apitypes.TypedData
|
|
|
|
if err := json.Unmarshal([]byte(typedDataJSON), &typedData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, _, err = apitypes.TypedDataAndHash(typedData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-11-06 19:04:42 +00:00
|
|
|
|
|
|
|
return &SessionRequestResponse{
|
2023-11-17 15:28:37 +00:00
|
|
|
KeyUID: account.KeyUID,
|
|
|
|
Address: account.Address,
|
|
|
|
AddressPath: account.Path,
|
|
|
|
SignOnKeycard: kp.MigratedToKeycard(),
|
2023-11-26 15:50:12 +00:00
|
|
|
MessageToSign: types.HexBytes(hash),
|
2023-11-06 19:04:42 +00:00
|
|
|
}, nil
|
|
|
|
}
|