feat(walletconnect)_: ethereum rpc calls support
Reference: https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc
This commit is contained in:
parent
d4ca8616fc
commit
8641ec5dd5
|
@ -1919,7 +1919,7 @@ func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs
|
|||
}
|
||||
|
||||
func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
hash, err = b.transactor.SendTransactionWithSignature(b.transactor.NetworkID(), sendArgs, sig)
|
||||
hash, err = b.transactor.BuildTransactionAndSendWithSignature(b.transactor.NetworkID(), sendArgs, sig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,17 +6,31 @@ const (
|
|||
// StatusDatabase path relative to DataDir.
|
||||
StatusDatabase = "status-db"
|
||||
|
||||
// SendTransactionMethodName defines the name for a giving transaction.
|
||||
// SendTransactionMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#eth_sendtransaction
|
||||
SendTransactionMethodName = "eth_sendTransaction"
|
||||
|
||||
// SendTransactionMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#eth_sendrawtransaction
|
||||
SendRawTransactionMethodName = "eth_sendRawTransaction"
|
||||
|
||||
BalanceMethodName = "eth_getBalance"
|
||||
|
||||
// AccountsMethodName defines the name for listing the currently signed accounts.
|
||||
AccountsMethodName = "eth_accounts"
|
||||
|
||||
// PersonalSignMethodName defines the name for `personal.sign` API.
|
||||
// PersonalSignMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#personal_sign
|
||||
PersonalSignMethodName = "personal_sign"
|
||||
|
||||
// SignMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#eth_sign
|
||||
SignMethodName = "eth_sign"
|
||||
|
||||
// SignTransactionMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#eth_signtransaction
|
||||
SignTransactionMethodName = "eth_signTransaction"
|
||||
|
||||
// SignTypedDataMethodName https://docs.walletconnect.com/advanced/rpc-reference/ethereum-rpc#eth_signtypeddata
|
||||
SignTypedDataMethodName = "eth_signTypedData"
|
||||
|
||||
WalletSwitchEthereumChainMethodName = "wallet_switchEthereumChain"
|
||||
|
||||
// PersonalRecoverMethodName defines the name for `personal.recover` API.
|
||||
PersonalRecoverMethodName = "personal_ecRecover"
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"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"
|
||||
wc "github.com/status-im/status-go/services/wallet/walletconnect"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
"github.com/status-im/status-go/transactions"
|
||||
|
@ -609,16 +608,32 @@ func (api *API) FetchChainIDForURL(ctx context.Context, rpcURL string) (*big.Int
|
|||
return client.ChainID(ctx)
|
||||
}
|
||||
|
||||
// WCSignMessage signs a message for the passed address using the provided password and returns the signature
|
||||
func (api *API) WCSignMessage(ctx context.Context, message types.HexBytes, address common.Address, password string) (string, error) {
|
||||
log.Debug("wallet.api.wc.SignMessage", "message", message, "address", address, "password", password)
|
||||
|
||||
return api.s.walletConnect.SignMessage(message, address, password)
|
||||
}
|
||||
|
||||
func (api *API) WCSendTransaction(signature string) (response *walletconnect.SessionRequestResponse, err error) {
|
||||
log.Debug("wallet.api.wc.SendTransaction", "signature", signature)
|
||||
// WCBuildRawTransaction builds raw transaction using the provided signature and returns the RLP-encoded transaction object
|
||||
func (api *API) WCBuildRawTransaction(signature string) (string, error) {
|
||||
log.Debug("wallet.api.wc.BuildRawTransaction", "signature", signature)
|
||||
|
||||
return api.s.walletConnect.SendTransaction(signature)
|
||||
return api.s.walletConnect.BuildRawTransaction(signature)
|
||||
}
|
||||
|
||||
// WCSendRawTransaction sends provided raw transaction and returns the transaction hash
|
||||
func (api *API) WCSendRawTransaction(rawTx string) (string, error) {
|
||||
log.Debug("wallet.api.wc.SendRawTransaction", "rawTx", rawTx)
|
||||
|
||||
return api.s.walletConnect.SendRawTransaction(rawTx)
|
||||
}
|
||||
|
||||
// WCSendTransactionWithSignature sends transaction with the provided signature and returns the transaction hash
|
||||
func (api *API) WCSendTransactionWithSignature(signature string) (string, error) {
|
||||
log.Debug("wallet.api.wc.SendTransactionWithSignature", "signature", signature)
|
||||
|
||||
return api.s.walletConnect.SendTransactionWithSignature(signature)
|
||||
}
|
||||
|
||||
// WCPairSessionProposal responds to "session_proposal" event
|
||||
|
@ -655,11 +670,11 @@ func (api *API) WCHasActivePairings(ctx context.Context) (bool, error) {
|
|||
}
|
||||
|
||||
// WCSessionRequest responds to "session_request" event
|
||||
func (api *API) WCSessionRequest(ctx context.Context, sessionRequestJSON string) (response *wc.SessionRequestResponse, err error) {
|
||||
func (api *API) WCSessionRequest(ctx context.Context, sessionRequestJSON string) (*wc.SessionRequestResponse, error) {
|
||||
log.Debug("wallet.api.wc.SessionRequest", "request.len", len(sessionRequestJSON))
|
||||
|
||||
var request wc.SessionRequest
|
||||
err = json.Unmarshal([]byte(sessionRequestJSON), &request)
|
||||
err := json.Unmarshal([]byte(sessionRequestJSON), &request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -399,7 +399,7 @@ func (tm *TransactionManager) ProceedWithTransactionsSignatures(ctx context.Cont
|
|||
// send transactions
|
||||
hashes := make(map[uint64][]types.Hash)
|
||||
for _, desc := range tm.transactionsForKeycardSingning {
|
||||
hash, err := tm.transactor.SendBuiltTransactionWithSignature(desc.chainID, desc.builtTx, desc.signature)
|
||||
hash, err := tm.transactor.AddSignatureToTransactionAndSend(desc.chainID, desc.builtTx, desc.signature)
|
||||
if desc.unlock != nil {
|
||||
defer func() {
|
||||
desc.unlock(err == nil, desc.builtTx.Nonce())
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
"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"
|
||||
|
@ -106,40 +106,41 @@ func (s *Service) buildTransaction(request SessionRequest) (response *SessionReq
|
|||
txBeingSigned: txBeingSigned,
|
||||
}
|
||||
|
||||
signer := ethTypes.NewLondonSigner(new(big.Int).SetUint64(s.txSignDetails.chainID))
|
||||
signer := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID))
|
||||
return &SessionRequestResponse{
|
||||
KeyUID: account.KeyUID,
|
||||
Address: account.Address,
|
||||
AddressPath: account.Path,
|
||||
SignOnKeycard: kp.MigratedToKeycard(),
|
||||
MesageToSign: signer.Hash(s.txSignDetails.txBeingSigned),
|
||||
MesageToSign: signer.Hash(txBeingSigned),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) sendTransaction(signature string) (response *SessionRequestResponse, err error) {
|
||||
func (s *Service) addSignatureToTransaction(signature string) (*ethTypes.Transaction, error) {
|
||||
if s.txSignDetails.txBeingSigned == nil {
|
||||
return response, errors.New("no tx to sign")
|
||||
return nil, errors.New("no tx to sign")
|
||||
}
|
||||
|
||||
signatureBytes, _ := hex.DecodeString(signature)
|
||||
|
||||
hash, err := s.transactor.SendBuiltTransactionWithSignature(s.txSignDetails.chainID, s.txSignDetails.txBeingSigned, signatureBytes)
|
||||
signatureBytes, err := hex.DecodeString(signature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SessionRequestResponse{
|
||||
SignedMessage: hash,
|
||||
}, nil
|
||||
return s.transactor.AddSignatureToTransaction(s.txSignDetails.chainID, s.txSignDetails.txBeingSigned, signatureBytes)
|
||||
}
|
||||
|
||||
func (s *Service) buildPersonalSingMessage(request SessionRequest) (response *SessionRequestResponse, err error) {
|
||||
func (s *Service) buildMessage(request SessionRequest, addressIndex int, messageIndex int,
|
||||
handleTypedData bool) (response *SessionRequestResponse, err error) {
|
||||
if len(request.Params.Request.Params) != 2 {
|
||||
return nil, ErrorInvalidParamsCount
|
||||
}
|
||||
|
||||
if addressIndex > 1 || addressIndex < 0 || messageIndex > 1 || messageIndex < 0 {
|
||||
return nil, ErrorInvalidAddressMsgIndex
|
||||
}
|
||||
|
||||
var address types.Address
|
||||
if err := json.Unmarshal(request.Params.Request.Params[1], &address); err != nil {
|
||||
if err := json.Unmarshal(request.Params.Request.Params[addressIndex], &address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -153,12 +154,29 @@ func (s *Service) buildPersonalSingMessage(request SessionRequest) (response *Se
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var hash []byte
|
||||
if !handleTypedData {
|
||||
var dBytes types.HexBytes
|
||||
if err := json.Unmarshal(request.Params.Request.Params[0], &dBytes); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
hash := crypto.TextHash(dBytes)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return &SessionRequestResponse{
|
||||
KeyUID: account.KeyUID,
|
||||
|
|
|
@ -24,6 +24,7 @@ type txSigningDetails struct {
|
|||
chainID uint64
|
||||
from common.Address
|
||||
txBeingSigned *ethTypes.Transaction
|
||||
txHash common.Hash
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
|
@ -62,8 +63,43 @@ func (s *Service) SignMessage(message types.HexBytes, address common.Address, pa
|
|||
return types.EncodeHex(signature), err
|
||||
}
|
||||
|
||||
func (s *Service) SendTransaction(signature string) (response *SessionRequestResponse, err error) {
|
||||
return s.sendTransaction(signature)
|
||||
func (s *Service) BuildRawTransaction(signature string) (string, error) {
|
||||
txWithSignature, err := s.addSignatureToTransaction(signature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, err := txWithSignature.MarshalBinary()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s.txSignDetails.txHash = txWithSignature.Hash()
|
||||
|
||||
return types.EncodeHex(data), nil
|
||||
}
|
||||
|
||||
func (s *Service) SendRawTransaction(rawTx string) (string, error) {
|
||||
err := s.transactor.SendRawTransaction(s.txSignDetails.chainID, rawTx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return s.txSignDetails.txHash.Hex(), nil
|
||||
}
|
||||
|
||||
func (s *Service) SendTransactionWithSignature(signature string) (string, error) {
|
||||
txWithSignature, err := s.addSignatureToTransaction(signature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash, err := s.transactor.SendTransactionWithSignature(txWithSignature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hash.Hex(), nil
|
||||
}
|
||||
|
||||
func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionResponse, error) {
|
||||
|
@ -129,7 +165,12 @@ func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionRes
|
|||
SupportedNamespaces: map[string]Namespace{
|
||||
SupportedEip155Namespace: Namespace{
|
||||
Methods: []string{params.SendTransactionMethodName,
|
||||
params.SendRawTransactionMethodName,
|
||||
params.PersonalSignMethodName,
|
||||
params.SignMethodName,
|
||||
params.SignTransactionMethodName,
|
||||
params.SignTypedDataMethodName,
|
||||
params.WalletSwitchEthereumChainMethodName,
|
||||
},
|
||||
Events: []string{"accountsChanged", "chainChanged"},
|
||||
Chains: eipChains,
|
||||
|
@ -169,8 +210,14 @@ func (s *Service) SessionRequest(request SessionRequest) (response *SessionReque
|
|||
|
||||
if request.Params.Request.Method == params.SendTransactionMethodName {
|
||||
return s.buildTransaction(request)
|
||||
} else if request.Params.Request.Method == params.SignTransactionMethodName {
|
||||
return s.buildTransaction(request)
|
||||
} else if request.Params.Request.Method == params.PersonalSignMethodName {
|
||||
return s.buildPersonalSingMessage(request)
|
||||
return s.buildMessage(request, 1, 0, false)
|
||||
} else if request.Params.Request.Method == params.SignMethodName {
|
||||
return s.buildMessage(request, 0, 1, false)
|
||||
} else if request.Params.Request.Method == params.SignTypedDataMethodName {
|
||||
return s.buildMessage(request, 0, 1, true)
|
||||
}
|
||||
|
||||
// TODO #12434: respond async
|
||||
|
|
|
@ -108,7 +108,6 @@ type SessionRequestResponse struct {
|
|||
AddressPath string `json:"addressPath,omitempty"`
|
||||
SignOnKeycard bool `json:"signOnKeycard,omitempty"`
|
||||
MesageToSign interface{} `json:"messageToSign,omitempty"`
|
||||
SignedMessage interface{} `json:"signedMessage,omitempty"`
|
||||
}
|
||||
|
||||
// Valid namespace
|
||||
|
|
|
@ -55,6 +55,11 @@ func (w *rpcWrapper) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uin
|
|||
return uint64(hex), nil
|
||||
}
|
||||
|
||||
// Does the `eth_sendRawTransaction` call with the given raw transaction hex string.
|
||||
func (w *rpcWrapper) SendRawTransaction(ctx context.Context, rawTx string) error {
|
||||
return w.RPCClient.CallContext(ctx, nil, w.chainID, "eth_sendRawTransaction", rawTx)
|
||||
}
|
||||
|
||||
// SendTransaction injects a signed transaction into the pending pool for execution.
|
||||
//
|
||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||
|
@ -64,7 +69,7 @@ func (w *rpcWrapper) SendTransaction(ctx context.Context, tx *gethtypes.Transact
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.RPCClient.CallContext(ctx, nil, w.chainID, "eth_sendRawTransaction", types.EncodeHex(data))
|
||||
return w.SendRawTransaction(ctx, types.EncodeHex(data))
|
||||
}
|
||||
|
||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
|
|
|
@ -116,33 +116,57 @@ func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTx
|
|||
return
|
||||
}
|
||||
|
||||
func (t *Transactor) SendBuiltTransactionWithSignature(chainID uint64, tx *gethtypes.Transaction, sig []byte) (hash types.Hash, err error) {
|
||||
func (t *Transactor) AddSignatureToTransaction(chainID uint64, tx *gethtypes.Transaction, sig []byte) (*gethtypes.Transaction, error) {
|
||||
if len(sig) != ValidSignatureSize {
|
||||
return hash, ErrInvalidSignatureSize
|
||||
return nil, ErrInvalidSignatureSize
|
||||
}
|
||||
|
||||
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
||||
chID := big.NewInt(int64(rpcWrapper.chainID))
|
||||
|
||||
signer := gethtypes.NewLondonSigner(chID)
|
||||
signedTx, err := tx.WithSignature(signer, sig)
|
||||
txWithSignature, err := tx.WithSignature(signer, sig)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txWithSignature, nil
|
||||
}
|
||||
|
||||
func (t *Transactor) SendRawTransaction(chainID uint64, rawTx string) error {
|
||||
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := rpcWrapper.SendTransaction(ctx, signedTx); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(signedTx.Hash()), nil
|
||||
return rpcWrapper.SendRawTransaction(ctx, rawTx)
|
||||
}
|
||||
|
||||
// SendTransactionWithSignature receive a transaction and a signature, serialize them together and propage it to the network.
|
||||
func (t *Transactor) SendTransactionWithSignature(tx *gethtypes.Transaction) (hash types.Hash, err error) {
|
||||
rpcWrapper := newRPCWrapper(t.rpcWrapper.RPCClient, tx.ChainId().Uint64())
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), t.rpcCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := rpcWrapper.SendTransaction(ctx, tx); err != nil {
|
||||
return hash, err
|
||||
}
|
||||
return types.Hash(tx.Hash()), nil
|
||||
}
|
||||
|
||||
func (t *Transactor) AddSignatureToTransactionAndSend(chainID uint64, tx *gethtypes.Transaction, sig []byte) (hash types.Hash, err error) {
|
||||
txWithSignature, err := t.AddSignatureToTransaction(chainID, tx, sig)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
||||
return t.SendTransactionWithSignature(txWithSignature)
|
||||
}
|
||||
|
||||
// BuildTransactionAndSendWithSignature receive a transaction and a signature, serialize them together and propage it to the network.
|
||||
// It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature.
|
||||
// Since the transactions is already signed, we assume it was validated and used the right nonce.
|
||||
func (t *Transactor) SendTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
func (t *Transactor) BuildTransactionAndSendWithSignature(chainID uint64, args SendTxArgs, sig []byte) (hash types.Hash, err error) {
|
||||
if !args.Valid() {
|
||||
return hash, ErrInvalidSendTxArgs
|
||||
}
|
||||
|
@ -167,7 +191,7 @@ func (t *Transactor) SendTransactionWithSignature(chainID uint64, args SendTxArg
|
|||
return hash, &ErrBadNonce{tx.Nonce(), expectedNonce}
|
||||
}
|
||||
|
||||
return t.SendBuiltTransactionWithSignature(chainID, tx, sig)
|
||||
return t.AddSignatureToTransactionAndSend(chainID, tx, sig)
|
||||
}
|
||||
|
||||
func (t *Transactor) HashTransaction(args SendTxArgs) (validatedArgs SendTxArgs, hash types.Hash, err error) {
|
||||
|
|
|
@ -374,7 +374,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
Return(common.Hash{}, nil)
|
||||
}
|
||||
|
||||
_, err = s.manager.SendTransactionWithSignature(s.nodeConfig.NetworkID, args, sig)
|
||||
_, err = s.manager.BuildTransactionAndSendWithSignature(s.nodeConfig.NetworkID, args, sig)
|
||||
if scenario.expectError {
|
||||
s.Error(err)
|
||||
// local nonce should not be incremented
|
||||
|
@ -393,7 +393,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
|
|||
|
||||
func (s *TransactorSuite) TestSendTransactionWithSignature_InvalidSignature() {
|
||||
args := SendTxArgs{}
|
||||
_, err := s.manager.SendTransactionWithSignature(1, args, []byte{})
|
||||
_, err := s.manager.BuildTransactionAndSendWithSignature(1, args, []byte{})
|
||||
s.Equal(ErrInvalidSignatureSize, err)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue