2023-10-30 16:58:57 +00:00
|
|
|
package walletconnect
|
|
|
|
|
|
|
|
import (
|
2023-11-19 17:29:17 +00:00
|
|
|
"database/sql"
|
2023-11-06 19:04:42 +00:00
|
|
|
"fmt"
|
2023-11-24 15:27:05 +00:00
|
|
|
"strings"
|
2023-11-19 17:29:17 +00:00
|
|
|
"time"
|
2023-11-06 19:04:42 +00:00
|
|
|
|
2023-12-06 13:53:23 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2023-10-30 16:58:57 +00:00
|
|
|
"github.com/ethereum/go-ethereum/event"
|
2023-11-06 19:04:42 +00:00
|
|
|
"github.com/ethereum/go-ethereum/log"
|
2023-11-19 17:29:17 +00:00
|
|
|
|
2023-11-06 19:04:42 +00:00
|
|
|
"github.com/status-im/status-go/account"
|
2023-12-06 13:53:23 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2023-10-30 16:58:57 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
2023-11-06 19:04:42 +00:00
|
|
|
"github.com/status-im/status-go/params"
|
2023-10-30 16:58:57 +00:00
|
|
|
"github.com/status-im/status-go/rpc/network"
|
2023-12-01 16:42:08 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet/transfer"
|
2023-10-30 16:58:57 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Service struct {
|
2023-11-19 17:29:17 +00:00
|
|
|
db *sql.DB
|
2023-10-30 16:58:57 +00:00
|
|
|
networkManager *network.Manager
|
|
|
|
accountsDB *accounts.Database
|
|
|
|
eventFeed *event.Feed
|
2023-11-06 19:04:42 +00:00
|
|
|
|
2023-12-01 16:42:08 +00:00
|
|
|
transactionManager *transfer.TransactionManager
|
|
|
|
gethManager *account.GethManager
|
2023-11-17 15:28:37 +00:00
|
|
|
|
2023-12-01 16:42:08 +00:00
|
|
|
config *params.NodeConfig
|
2023-10-30 16:58:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-01 16:42:08 +00:00
|
|
|
func NewService(db *sql.DB, networkManager *network.Manager, accountsDB *accounts.Database,
|
|
|
|
transactionManager *transfer.TransactionManager, gethManager *account.GethManager, eventFeed *event.Feed,
|
|
|
|
config *params.NodeConfig) *Service {
|
2023-10-30 16:58:57 +00:00
|
|
|
return &Service{
|
2023-12-01 16:42:08 +00:00
|
|
|
db: db,
|
|
|
|
networkManager: networkManager,
|
|
|
|
accountsDB: accountsDB,
|
|
|
|
eventFeed: eventFeed,
|
|
|
|
transactionManager: transactionManager,
|
|
|
|
gethManager: gethManager,
|
|
|
|
config: config,
|
2023-11-24 15:39:36 +00:00
|
|
|
}
|
2023-11-17 15:28:37 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 16:58:57 +00:00
|
|
|
func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionResponse, error) {
|
2023-11-24 15:27:05 +00:00
|
|
|
if !proposal.Valid() {
|
|
|
|
return nil, ErrorInvalidSessionProposal
|
2023-10-30 16:58:57 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 15:27:05 +00:00
|
|
|
var (
|
|
|
|
chains []uint64
|
|
|
|
eipChains []string
|
|
|
|
)
|
|
|
|
|
|
|
|
if len(proposal.Params.RequiredNamespaces) == 0 {
|
|
|
|
// return all we support
|
|
|
|
allChains, err := s.networkManager.GetAll()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get all chains: %w", err)
|
|
|
|
}
|
|
|
|
for _, chain := range allChains {
|
|
|
|
chains = append(chains, chain.ChainID)
|
|
|
|
eipChains = append(eipChains, fmt.Sprintf("%s:%d", SupportedEip155Namespace, chain.ChainID))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var proposedChains []string
|
|
|
|
for key, ns := range proposal.Params.RequiredNamespaces {
|
|
|
|
if !strings.Contains(key, SupportedEip155Namespace) {
|
|
|
|
log.Warn("Some namespaces are not supported; wanted: ", key, "; supported: ", SupportedEip155Namespace)
|
|
|
|
return nil, ErrorNamespaceNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.Contains(key, ":") {
|
|
|
|
proposedChains = append(proposedChains, key)
|
|
|
|
} else {
|
|
|
|
proposedChains = append(proposedChains, ns.Chains...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chains, eipChains = sessionProposalToSupportedChain(proposedChains, func(chainID uint64) bool {
|
|
|
|
return s.networkManager.Find(chainID) != nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if len(chains) != len(proposedChains) {
|
|
|
|
log.Warn("Some chains are not supported; wanted: ", proposedChains, "; supported: ", chains)
|
|
|
|
return nil, ErrorChainsNotSupported
|
|
|
|
}
|
2023-10-30 16:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
activeAccounts, err := s.accountsDB.GetActiveAccounts()
|
|
|
|
if err != nil {
|
2023-11-06 19:04:42 +00:00
|
|
|
return nil, fmt.Errorf("failed to get active accounts: %w", err)
|
2023-10-30 16:58:57 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 15:27:05 +00:00
|
|
|
allWalletAccountsReadyForTransaction := make([]*accounts.Account, 0, 1)
|
2023-11-06 19:04:42 +00:00
|
|
|
for _, acc := range activeAccounts {
|
2023-11-17 15:28:37 +00:00
|
|
|
if !acc.IsWalletAccountReadyForTransaction() {
|
2023-11-06 19:04:42 +00:00
|
|
|
continue
|
|
|
|
}
|
2023-11-24 15:27:05 +00:00
|
|
|
allWalletAccountsReadyForTransaction = append(allWalletAccountsReadyForTransaction, acc)
|
2023-11-06 19:04:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 15:27:05 +00:00
|
|
|
result := &PairSessionResponse{
|
2023-10-30 16:58:57 +00:00
|
|
|
SessionProposal: proposal,
|
2023-11-24 15:27:05 +00:00
|
|
|
SupportedNamespaces: map[string]Namespace{
|
|
|
|
SupportedEip155Namespace: Namespace{
|
|
|
|
Methods: []string{params.SendTransactionMethodName,
|
2023-11-24 15:39:36 +00:00
|
|
|
params.SendRawTransactionMethodName,
|
2023-11-24 15:27:05 +00:00
|
|
|
params.PersonalSignMethodName,
|
2023-11-24 15:39:36 +00:00
|
|
|
params.SignMethodName,
|
|
|
|
params.SignTransactionMethodName,
|
|
|
|
params.SignTypedDataMethodName,
|
2023-12-12 08:35:51 +00:00
|
|
|
params.SignTypedDataV3MethodName,
|
|
|
|
params.SignTypedDataV4MethodName,
|
2023-11-24 15:39:36 +00:00
|
|
|
params.WalletSwitchEthereumChainMethodName,
|
2023-11-24 15:27:05 +00:00
|
|
|
},
|
|
|
|
Events: []string{"accountsChanged", "chainChanged"},
|
|
|
|
Chains: eipChains,
|
|
|
|
Accounts: caip10Accounts(allWalletAccountsReadyForTransaction, chains),
|
|
|
|
},
|
2023-10-30 16:58:57 +00:00
|
|
|
},
|
2023-11-24 15:27:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO #12434: respond async
|
|
|
|
return result, nil
|
2023-10-30 16:58:57 +00:00
|
|
|
}
|
2023-11-06 19:04:42 +00:00
|
|
|
|
2023-11-19 17:29:17 +00:00
|
|
|
func (s *Service) RecordSuccessfulPairing(proposal SessionProposal) error {
|
|
|
|
var icon string
|
|
|
|
if len(proposal.Params.Proposer.Metadata.Icons) > 0 {
|
|
|
|
icon = proposal.Params.Proposer.Metadata.Icons[0]
|
|
|
|
}
|
|
|
|
return InsertPairing(s.db, Pairing{
|
|
|
|
Topic: proposal.Params.PairingTopic,
|
|
|
|
Expiry: proposal.Params.Expiry,
|
|
|
|
Active: true,
|
|
|
|
AppName: proposal.Params.Proposer.Metadata.Name,
|
|
|
|
URL: proposal.Params.Proposer.Metadata.URL,
|
|
|
|
Description: proposal.Params.Proposer.Metadata.Description,
|
|
|
|
Icon: icon,
|
|
|
|
Verified: proposal.Params.Verify.Verified,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-11-26 15:50:12 +00:00
|
|
|
func (s *Service) ChangePairingState(topic Topic, active bool) error {
|
|
|
|
return ChangePairingState(s.db, topic, active)
|
|
|
|
}
|
|
|
|
|
2023-11-19 17:29:17 +00:00
|
|
|
func (s *Service) HasActivePairings() (bool, error) {
|
|
|
|
return HasActivePairings(s.db, time.Now().Unix())
|
|
|
|
}
|
|
|
|
|
2023-12-01 16:42:08 +00:00
|
|
|
func (s *Service) SessionRequest(request SessionRequest) (response *transfer.TxResponse, err error) {
|
2023-11-06 19:04:42 +00:00
|
|
|
// TODO #12434: should we check topic for validity? It might make sense if we
|
|
|
|
// want to cache the paired sessions
|
|
|
|
|
|
|
|
if request.Params.Request.Method == params.SendTransactionMethodName {
|
2023-11-17 15:28:37 +00:00
|
|
|
return s.buildTransaction(request)
|
2023-11-24 15:39:36 +00:00
|
|
|
} else if request.Params.Request.Method == params.SignTransactionMethodName {
|
|
|
|
return s.buildTransaction(request)
|
2023-11-06 19:04:42 +00:00
|
|
|
} else if request.Params.Request.Method == params.PersonalSignMethodName {
|
2023-11-24 15:39:36 +00:00
|
|
|
return s.buildMessage(request, 1, 0, false)
|
|
|
|
} else if request.Params.Request.Method == params.SignMethodName {
|
|
|
|
return s.buildMessage(request, 0, 1, false)
|
2023-12-12 08:35:51 +00:00
|
|
|
} else if request.Params.Request.Method == params.SignTypedDataMethodName ||
|
|
|
|
request.Params.Request.Method == params.SignTypedDataV3MethodName ||
|
|
|
|
request.Params.Request.Method == params.SignTypedDataV4MethodName {
|
2023-11-24 15:39:36 +00:00
|
|
|
return s.buildMessage(request, 0, 1, true)
|
2023-11-06 19:04:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO #12434: respond async
|
|
|
|
return nil, ErrorMethodNotSupported
|
|
|
|
}
|
2023-12-06 13:53:23 +00:00
|
|
|
|
|
|
|
func (s *Service) AuthRequest(address common.Address, authMessage string) (*transfer.TxResponse, error) {
|
|
|
|
account, err := s.accountsDB.GetAccountByAddress(types.Address(address))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get active account: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
kp, err := s.accountsDB.GetKeypairByKeyUID(account.KeyUID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
byteArray := []byte(authMessage)
|
|
|
|
hash := crypto.TextHash(byteArray)
|
|
|
|
|
|
|
|
return &transfer.TxResponse{
|
|
|
|
KeyUID: account.KeyUID,
|
|
|
|
Address: account.Address,
|
|
|
|
AddressPath: account.Path,
|
|
|
|
SignOnKeycard: kp.MigratedToKeycard(),
|
|
|
|
MessageToSign: types.HexBytes(hash),
|
|
|
|
}, nil
|
|
|
|
}
|