196
vendor/github.com/status-im/status-go/services/wallet/walletconnect/database.go
generated
vendored
Normal file
196
vendor/github.com/status-im/status-go/services/wallet/walletconnect/database.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
package walletconnect
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type DbSession struct {
|
||||
Topic Topic `json:"topic"`
|
||||
PairingTopic Topic `json:"pairingTopic"`
|
||||
Expiry int64 `json:"expiry"`
|
||||
Active bool `json:"active"`
|
||||
DappName string `json:"dappName"`
|
||||
DappURL string `json:"dappUrl"`
|
||||
DappDescription string `json:"dappDescription"`
|
||||
DappIcon string `json:"dappIcon"`
|
||||
DappVerifyURL string `json:"dappVerifyUrl"`
|
||||
DappPublicKey string `json:"dappPublicKey"`
|
||||
}
|
||||
|
||||
func UpsertSession(db *sql.DB, session DbSession) error {
|
||||
insertSQL := `
|
||||
INSERT OR IGNORE INTO
|
||||
wallet_connect_sessions (topic, pairing_topic, expiry, active)
|
||||
VALUES
|
||||
(?, ?, ?, ?);
|
||||
|
||||
UPDATE
|
||||
wallet_connect_sessions
|
||||
SET
|
||||
pairing_topic = ?,
|
||||
expiry = ?,
|
||||
active = ?,
|
||||
dapp_name = ?,
|
||||
dapp_url = ?,
|
||||
dapp_description = ?,
|
||||
dapp_icon = ?,
|
||||
dapp_verify_url = ?,
|
||||
dapp_publicKey = ?
|
||||
WHERE
|
||||
topic = ?;`
|
||||
|
||||
_, err := db.Exec(insertSQL,
|
||||
session.Topic,
|
||||
session.PairingTopic,
|
||||
session.Expiry,
|
||||
session.Active,
|
||||
session.PairingTopic,
|
||||
session.Expiry,
|
||||
session.Active,
|
||||
session.DappName,
|
||||
session.DappURL,
|
||||
session.DappDescription,
|
||||
session.DappIcon,
|
||||
session.DappVerifyURL,
|
||||
session.DappPublicKey,
|
||||
session.Topic,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func ChangeSessionState(db *sql.DB, topic Topic, active bool) error {
|
||||
stmt, err := db.Prepare("UPDATE wallet_connect_sessions SET active = ? WHERE topic = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := stmt.Exec(active, topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.New("unable to locate session for DB state change")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSessionByTopic(db *sql.DB, topic Topic) (*DbSession, error) {
|
||||
querySQL := `
|
||||
SELECT *
|
||||
FROM
|
||||
wallet_connect_sessions
|
||||
WHERE
|
||||
topic = ?`
|
||||
|
||||
row := db.QueryRow(querySQL, topic)
|
||||
|
||||
var session DbSession
|
||||
err := row.Scan(&session.Topic,
|
||||
&session.PairingTopic,
|
||||
&session.Expiry,
|
||||
&session.Active,
|
||||
&session.DappName,
|
||||
&session.DappURL,
|
||||
&session.DappDescription,
|
||||
&session.DappIcon,
|
||||
&session.DappVerifyURL,
|
||||
&session.DappPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
func GetSessionsByPairingTopic(db *sql.DB, pairingTopic Topic) ([]DbSession, error) {
|
||||
querySQL := `
|
||||
SELECT *
|
||||
FROM
|
||||
wallet_connect_sessions
|
||||
WHERE
|
||||
pairing_topic = ?`
|
||||
|
||||
rows, err := db.Query(querySQL, pairingTopic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
sessions := make([]DbSession, 0, 2)
|
||||
for rows.Next() {
|
||||
var session DbSession
|
||||
err := rows.Scan(&session.Topic,
|
||||
&session.PairingTopic,
|
||||
&session.Expiry,
|
||||
&session.Active,
|
||||
&session.DappName,
|
||||
&session.DappURL,
|
||||
&session.DappDescription,
|
||||
&session.DappIcon,
|
||||
&session.DappVerifyURL,
|
||||
&session.DappPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// GetActiveSessions returns all active sessions (active and not expired) that have an expiry timestamp newer or equal to the given timestamp.
|
||||
func GetActiveSessions(db *sql.DB, expiryNotOlderThanTimestamp int64) ([]DbSession, error) {
|
||||
querySQL := `
|
||||
SELECT *
|
||||
FROM
|
||||
wallet_connect_sessions
|
||||
WHERE
|
||||
active != 0 AND
|
||||
expiry >= ?
|
||||
ORDER BY
|
||||
expiry DESC`
|
||||
|
||||
rows, err := db.Query(querySQL, expiryNotOlderThanTimestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
sessions := make([]DbSession, 0, 2)
|
||||
for rows.Next() {
|
||||
var session DbSession
|
||||
err := rows.Scan(&session.Topic,
|
||||
&session.PairingTopic,
|
||||
&session.Expiry,
|
||||
&session.Active,
|
||||
&session.DappName,
|
||||
&session.DappURL,
|
||||
&session.DappDescription,
|
||||
&session.DappIcon,
|
||||
&session.DappVerifyURL,
|
||||
&session.DappPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
71
vendor/github.com/status-im/status-go/services/wallet/walletconnect/helpers.go
generated
vendored
Normal file
71
vendor/github.com/status-im/status-go/services/wallet/walletconnect/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package walletconnect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// func sendResponseEvent(eventFeed *event.Feed, eventType walletevent.EventType, payloadObj interface{}, resErr error) {
|
||||
// payload, err := json.Marshal(payloadObj)
|
||||
// if err != nil {
|
||||
// log.Error("Error marshaling WC response: %v; result error: %w", err, resErr)
|
||||
// } else {
|
||||
// err = resErr
|
||||
// }
|
||||
|
||||
// event := walletevent.Event{
|
||||
// Type: eventType,
|
||||
// Message: string(payload),
|
||||
// }
|
||||
|
||||
// sentCount := eventFeed.Send(event)
|
||||
|
||||
// log.Debug("wallet.api.wc RESPONSE", "eventType", eventType, "error", err, "payload.len", len(payload), "sentCount", sentCount)
|
||||
// }
|
||||
|
||||
// Returns namspace name, chainID and error
|
||||
func parseCaip2ChainID(str string) (string, uint64, error) {
|
||||
caip2 := strings.Split(str, ":")
|
||||
if len(caip2) != 2 {
|
||||
return "", 0, errors.New("CAIP-2 string is not valid")
|
||||
}
|
||||
|
||||
chainIDStr := caip2[1]
|
||||
chainID, err := strconv.ParseUint(chainIDStr, 10, 64)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("CAIP-2 second value not valid Chain ID: %w", err)
|
||||
}
|
||||
return caip2[0], chainID, nil
|
||||
}
|
||||
|
||||
// JSONProxyType provides a generic way of changing the JSON value before unmarshalling it into the target.
|
||||
// transform function is called before unmarshalling.
|
||||
type JSONProxyType struct {
|
||||
target interface{}
|
||||
transform func([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func (b *JSONProxyType) UnmarshalJSON(input []byte) error {
|
||||
if b.transform == nil {
|
||||
return errors.New("transform function is not set")
|
||||
}
|
||||
|
||||
output, err := b.transform(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(output, b.target)
|
||||
}
|
||||
|
||||
func isValidNamespaceName(namespaceName string) bool {
|
||||
pattern := "^[a-z0-9-]{3,8}$"
|
||||
|
||||
regex := regexp.MustCompile(pattern)
|
||||
|
||||
return regex.MatchString(namespaceName)
|
||||
}
|
||||
138
vendor/github.com/status-im/status-go/services/wallet/walletconnect/rpc.go
generated
vendored
Normal file
138
vendor/github.com/status-im/status-go/services/wallet/walletconnect/rpc.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
package walletconnect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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/services/wallet/transfer"
|
||||
"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)
|
||||
}
|
||||
|
||||
func (s *Service) buildTransaction(request SessionRequest) (response *transfer.TxResponse, err error) {
|
||||
if len(request.Params.Request.Params) != 1 {
|
||||
return nil, ErrorInvalidParamsCount
|
||||
}
|
||||
|
||||
var params sendTransactionParams
|
||||
if err = json.Unmarshal(request.Params.Request.Params[0], ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, chainID, err := parseCaip2ChainID(request.Params.ChainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.transactionManager.BuildTransaction(chainID, params.SendTxArgs)
|
||||
}
|
||||
|
||||
func (s *Service) buildMessage(request SessionRequest, addressIndex int, messageIndex int,
|
||||
handleTypedData bool) (response *transfer.TxResponse, 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[addressIndex], &address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 &transfer.TxResponse{
|
||||
KeyUID: account.KeyUID,
|
||||
Address: account.Address,
|
||||
AddressPath: account.Path,
|
||||
SignOnKeycard: kp.MigratedToKeycard(),
|
||||
MessageToSign: types.HexBytes(hash),
|
||||
}, nil
|
||||
}
|
||||
197
vendor/github.com/status-im/status-go/services/wallet/walletconnect/service.go
generated
vendored
Normal file
197
vendor/github.com/status-im/status-go/services/wallet/walletconnect/service.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
package walletconnect
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"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/rpc/network"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
networkManager *network.Manager
|
||||
accountsDB *accounts.Database
|
||||
eventFeed *event.Feed
|
||||
|
||||
transactionManager *transfer.TransactionManager
|
||||
gethManager *account.GethManager
|
||||
|
||||
config *params.NodeConfig
|
||||
}
|
||||
|
||||
func NewService(db *sql.DB, networkManager *network.Manager, accountsDB *accounts.Database,
|
||||
transactionManager *transfer.TransactionManager, gethManager *account.GethManager, eventFeed *event.Feed,
|
||||
config *params.NodeConfig) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
networkManager: networkManager,
|
||||
accountsDB: accountsDB,
|
||||
eventFeed: eventFeed,
|
||||
transactionManager: transactionManager,
|
||||
gethManager: gethManager,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionResponse, error) {
|
||||
if !proposal.Valid() {
|
||||
return nil, ErrorInvalidSessionProposal
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
activeAccounts, err := s.accountsDB.GetActiveAccounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get active accounts: %w", err)
|
||||
}
|
||||
|
||||
allWalletAccountsReadyForTransaction := make([]*accounts.Account, 0, 1)
|
||||
for _, acc := range activeAccounts {
|
||||
if !acc.IsWalletAccountReadyForTransaction() {
|
||||
continue
|
||||
}
|
||||
allWalletAccountsReadyForTransaction = append(allWalletAccountsReadyForTransaction, acc)
|
||||
}
|
||||
|
||||
result := &PairSessionResponse{
|
||||
SessionProposal: proposal,
|
||||
SupportedNamespaces: map[string]Namespace{
|
||||
SupportedEip155Namespace: Namespace{
|
||||
Methods: []string{params.SendTransactionMethodName,
|
||||
params.SendRawTransactionMethodName,
|
||||
params.PersonalSignMethodName,
|
||||
params.SignMethodName,
|
||||
params.SignTransactionMethodName,
|
||||
params.SignTypedDataMethodName,
|
||||
params.SignTypedDataV3MethodName,
|
||||
params.SignTypedDataV4MethodName,
|
||||
params.WalletSwitchEthereumChainMethodName,
|
||||
},
|
||||
Events: []string{"accountsChanged", "chainChanged"},
|
||||
Chains: eipChains,
|
||||
Accounts: caip10Accounts(allWalletAccountsReadyForTransaction, chains),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO #12434: respond async
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) SaveOrUpdateSession(session Session) error {
|
||||
var icon string
|
||||
if len(session.Peer.Metadata.Icons) > 0 {
|
||||
icon = session.Peer.Metadata.Icons[0]
|
||||
}
|
||||
|
||||
return UpsertSession(s.db, DbSession{
|
||||
Topic: session.Topic,
|
||||
PairingTopic: session.PairingTopic,
|
||||
Expiry: session.Expiry,
|
||||
Active: true,
|
||||
DappName: session.Peer.Metadata.Name,
|
||||
DappURL: session.Peer.Metadata.URL,
|
||||
DappDescription: session.Peer.Metadata.Description,
|
||||
DappIcon: icon,
|
||||
DappVerifyURL: session.Peer.Metadata.VerifyURL,
|
||||
DappPublicKey: session.Peer.PublicKey,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) ChangeSessionState(topic Topic, active bool) error {
|
||||
return ChangeSessionState(s.db, topic, active)
|
||||
}
|
||||
|
||||
func (s *Service) SessionRequest(request SessionRequest) (response *transfer.TxResponse, err error) {
|
||||
// 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 {
|
||||
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.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 ||
|
||||
request.Params.Request.Method == params.SignTypedDataV3MethodName ||
|
||||
request.Params.Request.Method == params.SignTypedDataV4MethodName {
|
||||
return s.buildMessage(request, 0, 1, true)
|
||||
}
|
||||
|
||||
// TODO #12434: respond async
|
||||
return nil, ErrorMethodNotSupported
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
202
vendor/github.com/status-im/status-go/services/wallet/walletconnect/walletconnect.go
generated
vendored
Normal file
202
vendor/github.com/status-im/status-go/services/wallet/walletconnect/walletconnect.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
package walletconnect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
||||
const (
|
||||
SupportedEip155Namespace = "eip155"
|
||||
|
||||
ProposeUserPairEvent = walletevent.EventType("WalletConnectProposeUserPair")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorInvalidSessionProposal = errors.New("invalid session proposal")
|
||||
ErrorNamespaceNotSupported = errors.New("namespace not supported")
|
||||
ErrorChainsNotSupported = errors.New("chains not supported")
|
||||
ErrorInvalidParamsCount = errors.New("invalid params count")
|
||||
ErrorInvalidAddressMsgIndex = errors.New("invalid address and/or msg index (must be 0 or 1)")
|
||||
ErrorMethodNotSupported = errors.New("method not supported")
|
||||
)
|
||||
|
||||
type Topic string
|
||||
|
||||
type Namespace struct {
|
||||
Methods []string `json:"methods"`
|
||||
Chains []string `json:"chains"` // CAIP-2 format e.g. ["eip155:1"]
|
||||
Events []string `json:"events"`
|
||||
Accounts []string `json:"accounts,omitempty"` // CAIP-10 format e.g. ["eip155:1:0x453...228"]
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
Icons []string `json:"icons"`
|
||||
Name string `json:"name"`
|
||||
VerifyURL string `json:"verifyUrl"`
|
||||
}
|
||||
|
||||
type Proposer struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
}
|
||||
|
||||
type Verified struct {
|
||||
VerifyURL string `json:"verifyUrl"`
|
||||
Validation string `json:"validation"`
|
||||
Origin string `json:"origin"`
|
||||
IsScam bool `json:"isScam,omitempty"`
|
||||
}
|
||||
|
||||
type VerifyContext struct {
|
||||
Verified Verified `json:"verified"`
|
||||
}
|
||||
|
||||
type Params struct {
|
||||
ID int64 `json:"id"`
|
||||
PairingTopic Topic `json:"pairingTopic"`
|
||||
Expiry int64 `json:"expiry"`
|
||||
RequiredNamespaces map[string]Namespace `json:"requiredNamespaces"`
|
||||
OptionalNamespaces map[string]Namespace `json:"optionalNamespaces"`
|
||||
Proposer Proposer `json:"proposer"`
|
||||
Verify VerifyContext `json:"verifyContext"`
|
||||
}
|
||||
|
||||
type SessionProposal struct {
|
||||
ID int64 `json:"id"`
|
||||
Params Params `json:"params"`
|
||||
}
|
||||
|
||||
type PairSessionResponse struct {
|
||||
SessionProposal SessionProposal `json:"sessionProposal"`
|
||||
SupportedNamespaces map[string]Namespace `json:"supportedNamespaces"`
|
||||
}
|
||||
|
||||
type RequestParams struct {
|
||||
Request struct {
|
||||
Method string `json:"method"`
|
||||
Params []json.RawMessage `json:"params"`
|
||||
} `json:"request"`
|
||||
ChainID string `json:"chainId"`
|
||||
}
|
||||
|
||||
type SessionRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
Topic Topic `json:"topic"`
|
||||
Params RequestParams `json:"params"`
|
||||
Verify VerifyContext `json:"verifyContext"`
|
||||
}
|
||||
|
||||
type SessionDelete struct {
|
||||
ID int64 `json:"id"`
|
||||
Topic Topic `json:"topic"`
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
Controller string `json:"controller"`
|
||||
Expiry int64 `json:"expiry"`
|
||||
Namespaces map[string]Namespace `json:"namespaces"`
|
||||
OptionalNamespaces map[string]Namespace `json:"optionalNamespaces"`
|
||||
PairingTopic Topic `json:"pairingTopic"`
|
||||
Peer Proposer `json:"peer"`
|
||||
Relay json.RawMessage `json:"relay"`
|
||||
RequiredNamespaces map[string]Namespace `json:"requiredNamespaces"`
|
||||
Self Proposer `json:"self"`
|
||||
Topic Topic `json:"topic"`
|
||||
}
|
||||
|
||||
// Valid namespace
|
||||
func (n *Namespace) Valid(namespaceName string, chainID *uint64) bool {
|
||||
if chainID == nil {
|
||||
if len(n.Chains) == 0 {
|
||||
log.Warn("namespace doesn't refer to any chain")
|
||||
return false
|
||||
}
|
||||
for _, caip2Str := range n.Chains {
|
||||
resolvedNamespaceName, _, err := parseCaip2ChainID(caip2Str)
|
||||
if err != nil {
|
||||
log.Warn("namespace chain not in caip2 format", "chain", caip2Str, "error", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if resolvedNamespaceName != namespaceName {
|
||||
log.Warn("namespace name doesn't match", "namespace", namespaceName, "chain", caip2Str)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Valid params
|
||||
func (p *Params) Valid() bool {
|
||||
for key, ns := range p.RequiredNamespaces {
|
||||
var chainID *uint64
|
||||
if strings.Contains(key, ":") {
|
||||
resolvedNamespaceName, cID, err := parseCaip2ChainID(key)
|
||||
if err != nil {
|
||||
log.Warn("params validation failed CAIP-2", "str", key, "error", err)
|
||||
return false
|
||||
}
|
||||
key = resolvedNamespaceName
|
||||
chainID = &cID
|
||||
}
|
||||
|
||||
if !isValidNamespaceName(key) {
|
||||
log.Warn("invalid namespace name", "namespace", key)
|
||||
return false
|
||||
}
|
||||
|
||||
if !ns.Valid(key, chainID) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Valid session propsal
|
||||
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#controller-side-validation-of-incoming-proposal-namespaces-wallet
|
||||
func (p *SessionProposal) Valid() bool {
|
||||
return p.Params.Valid()
|
||||
}
|
||||
|
||||
func sessionProposalToSupportedChain(caipChains []string, supportsChain func(uint64) bool) (chains []uint64, eipChains []string) {
|
||||
chains = make([]uint64, 0, 1)
|
||||
eipChains = make([]string, 0, 1)
|
||||
for _, caip2Str := range caipChains {
|
||||
_, chainID, err := parseCaip2ChainID(caip2Str)
|
||||
if err != nil {
|
||||
log.Warn("Failed parsing CAIP-2", "str", caip2Str, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !supportsChain(chainID) {
|
||||
continue
|
||||
}
|
||||
|
||||
eipChains = append(eipChains, caip2Str)
|
||||
chains = append(chains, chainID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func caip10Accounts(accounts []*accounts.Account, chains []uint64) []string {
|
||||
addresses := make([]string, 0, len(accounts)*len(chains))
|
||||
for _, acc := range accounts {
|
||||
for _, chainID := range chains {
|
||||
addresses = append(addresses, fmt.Sprintf("%s:%s:%s", SupportedEip155Namespace, strconv.FormatUint(chainID, 10), acc.Address.Hex()))
|
||||
}
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
Reference in New Issue
Block a user