mirror of
https://github.com/status-im/status-go.git
synced 2025-02-19 18:28:18 +00:00
feat(walletconnect)_: support for session proposal for wc 2.0
This commit is contained in:
parent
e32c5546e1
commit
d4ca8616fc
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -26,18 +27,19 @@ import (
|
|||||||
// log.Debug("wallet.api.wc RESPONSE", "eventType", eventType, "error", err, "payload.len", len(payload), "sentCount", sentCount)
|
// log.Debug("wallet.api.wc RESPONSE", "eventType", eventType, "error", err, "payload.len", len(payload), "sentCount", sentCount)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func parseCaip2ChainID(str string) (uint64, error) {
|
// Returns namspace name, chainID and error
|
||||||
|
func parseCaip2ChainID(str string) (string, uint64, error) {
|
||||||
caip2 := strings.Split(str, ":")
|
caip2 := strings.Split(str, ":")
|
||||||
if len(caip2) != 2 {
|
if len(caip2) != 2 {
|
||||||
return 0, errors.New("CAIP-2 string is not valid")
|
return "", 0, errors.New("CAIP-2 string is not valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
chainIDStr := caip2[1]
|
chainIDStr := caip2[1]
|
||||||
chainID, err := strconv.ParseUint(chainIDStr, 10, 64)
|
chainID, err := strconv.ParseUint(chainIDStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("CAIP-2 second value not valid Chain ID: %w", err)
|
return "", 0, fmt.Errorf("CAIP-2 second value not valid Chain ID: %w", err)
|
||||||
}
|
}
|
||||||
return chainID, nil
|
return caip2[0], chainID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONProxyType provides a generic way of changing the JSON value before unmarshalling it into the target.
|
// JSONProxyType provides a generic way of changing the JSON value before unmarshalling it into the target.
|
||||||
@ -59,3 +61,11 @@ func (b *JSONProxyType) UnmarshalJSON(input []byte) error {
|
|||||||
|
|
||||||
return json.Unmarshal(output, b.target)
|
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)
|
||||||
|
}
|
||||||
|
@ -1,60 +1,66 @@
|
|||||||
package walletconnect
|
package walletconnect
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func Test_parseCaip2ChainID(t *testing.T) {
|
func Test_parseCaip2(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
str string
|
str string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want uint64
|
wantChain uint64
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
args: args{
|
args: args{
|
||||||
str: "eip155:5",
|
str: "eip155:5",
|
||||||
},
|
},
|
||||||
want: 5,
|
wantChain: 5,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid_number",
|
name: "invalid_number",
|
||||||
args: args{
|
args: args{
|
||||||
str: "eip155:5a",
|
str: "eip155:5a",
|
||||||
},
|
},
|
||||||
want: 0,
|
wantChain: 0,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid_caip2_too_many",
|
name: "invalid_caip2_too_many",
|
||||||
args: args{
|
args: args{
|
||||||
str: "eip155:1:5",
|
str: "eip155:1:5",
|
||||||
},
|
},
|
||||||
want: 0,
|
wantChain: 0,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid_caip2_not_enough",
|
name: "invalid_caip2_not_enough",
|
||||||
args: args{
|
args: args{
|
||||||
str: "eip1551",
|
str: "eip1551",
|
||||||
},
|
},
|
||||||
want: 0,
|
wantChain: 0,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := parseCaip2ChainID(tt.args.str)
|
gotNamespaceName, gotChainID, err := parseCaip2ChainID(tt.args.str)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("parseCaip2ChainID() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("parseCaip2ChainID() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if got != tt.want {
|
if !tt.wantErr && !strings.Contains(tt.args.str, gotNamespaceName) {
|
||||||
t.Errorf("parseCaip2ChainID() = %v, want %v", got, tt.want)
|
t.Errorf("parseCaip2ChainID() = %v, doesn't match %v", gotNamespaceName, tt.args.str)
|
||||||
|
}
|
||||||
|
if gotChainID != tt.wantChain {
|
||||||
|
t.Errorf("parseCaip2ChainID() = %v, want %v", gotChainID, tt.wantChain)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ func (s *Service) buildTransaction(request SessionRequest) (response *SessionReq
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chainID, err := parseCaip2ChainID(request.Params.ChainID)
|
_, chainID, err := parseCaip2ChainID(request.Params.ChainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package walletconnect
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -66,45 +67,79 @@ func (s *Service) SendTransaction(signature string) (response *SessionRequestRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionResponse, error) {
|
func (s *Service) PairSessionProposal(proposal SessionProposal) (*PairSessionResponse, error) {
|
||||||
namespace := Namespace{
|
if !proposal.Valid() {
|
||||||
Methods: []string{params.SendTransactionMethodName, params.PersonalSignMethodName},
|
return nil, ErrorInvalidSessionProposal
|
||||||
Events: []string{"accountsChanged", "chainChanged"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proposedChains := proposal.Params.RequiredNamespaces.Eip155.Chains
|
var (
|
||||||
chains, eipChains := sessionProposalToSupportedChain(proposedChains, func(chainID uint64) bool {
|
chains []uint64
|
||||||
return s.networkManager.Find(chainID) != nil
|
eipChains []string
|
||||||
})
|
)
|
||||||
if len(chains) != len(proposedChains) {
|
|
||||||
log.Warn("Some chains are not supported; wanted: ", proposedChains, "; supported: ", chains)
|
if len(proposal.Params.RequiredNamespaces) == 0 {
|
||||||
return nil, ErrorChainsNotSupported
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
namespace.Chains = eipChains
|
|
||||||
|
|
||||||
activeAccounts, err := s.accountsDB.GetActiveAccounts()
|
activeAccounts, err := s.accountsDB.GetActiveAccounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get active accounts: %w", err)
|
return nil, fmt.Errorf("failed to get active accounts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out non-own accounts
|
allWalletAccountsReadyForTransaction := make([]*accounts.Account, 0, 1)
|
||||||
usableAccounts := make([]*accounts.Account, 0, 1)
|
|
||||||
for _, acc := range activeAccounts {
|
for _, acc := range activeAccounts {
|
||||||
if !acc.IsWalletAccountReadyForTransaction() {
|
if !acc.IsWalletAccountReadyForTransaction() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
usableAccounts = append(usableAccounts, acc)
|
allWalletAccountsReadyForTransaction = append(allWalletAccountsReadyForTransaction, acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
addresses := activeToOwnedAccounts(usableAccounts)
|
result := &PairSessionResponse{
|
||||||
namespace.Accounts = caip10Accounts(addresses, chains)
|
SessionProposal: proposal,
|
||||||
|
SupportedNamespaces: map[string]Namespace{
|
||||||
|
SupportedEip155Namespace: Namespace{
|
||||||
|
Methods: []string{params.SendTransactionMethodName,
|
||||||
|
params.PersonalSignMethodName,
|
||||||
|
},
|
||||||
|
Events: []string{"accountsChanged", "chainChanged"},
|
||||||
|
Chains: eipChains,
|
||||||
|
Accounts: caip10Accounts(allWalletAccountsReadyForTransaction, chains),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// TODO #12434: respond async
|
// TODO #12434: respond async
|
||||||
return &PairSessionResponse{
|
return result, nil
|
||||||
SessionProposal: proposal,
|
|
||||||
SupportedNamespaces: Namespaces{
|
|
||||||
Eip155: namespace,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) RecordSuccessfulPairing(proposal SessionProposal) error {
|
func (s *Service) RecordSuccessfulPairing(proposal SessionProposal) error {
|
||||||
|
@ -3,7 +3,9 @@ package walletconnect
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
@ -12,11 +14,20 @@ import (
|
|||||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ProposeUserPairEvent = walletevent.EventType("WalletConnectProposeUserPair")
|
const (
|
||||||
|
SupportedEip155Namespace = "eip155"
|
||||||
|
|
||||||
var ErrorChainsNotSupported = errors.New("chains not supported")
|
ProposeUserPairEvent = walletevent.EventType("WalletConnectProposeUserPair")
|
||||||
var ErrorInvalidParamsCount = errors.New("invalid params count")
|
)
|
||||||
var ErrorMethodNotSupported = errors.New("method not supported")
|
|
||||||
|
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 Topic string
|
||||||
|
|
||||||
@ -40,11 +51,6 @@ type Proposer struct {
|
|||||||
Metadata Metadata `json:"metadata"`
|
Metadata Metadata `json:"metadata"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Namespaces struct {
|
|
||||||
Eip155 Namespace `json:"eip155"`
|
|
||||||
// We ignore non ethereum namespaces
|
|
||||||
}
|
|
||||||
|
|
||||||
type Verified struct {
|
type Verified struct {
|
||||||
VerifyURL string `json:"verifyUrl"`
|
VerifyURL string `json:"verifyUrl"`
|
||||||
Validation string `json:"validation"`
|
Validation string `json:"validation"`
|
||||||
@ -57,13 +63,13 @@ type VerifyContext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Params struct {
|
type Params struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
PairingTopic Topic `json:"pairingTopic"`
|
PairingTopic Topic `json:"pairingTopic"`
|
||||||
Expiry int64 `json:"expiry"`
|
Expiry int64 `json:"expiry"`
|
||||||
RequiredNamespaces Namespaces `json:"requiredNamespaces"`
|
RequiredNamespaces map[string]Namespace `json:"requiredNamespaces"`
|
||||||
OptionalNamespaces Namespaces `json:"optionalNamespaces"`
|
OptionalNamespaces map[string]Namespace `json:"optionalNamespaces"`
|
||||||
Proposer Proposer `json:"proposer"`
|
Proposer Proposer `json:"proposer"`
|
||||||
Verify VerifyContext `json:"verifyContext"`
|
Verify VerifyContext `json:"verifyContext"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionProposal struct {
|
type SessionProposal struct {
|
||||||
@ -72,8 +78,8 @@ type SessionProposal struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PairSessionResponse struct {
|
type PairSessionResponse struct {
|
||||||
SessionProposal SessionProposal `json:"sessionProposal"`
|
SessionProposal SessionProposal `json:"sessionProposal"`
|
||||||
SupportedNamespaces Namespaces `json:"supportedNamespaces"`
|
SupportedNamespaces map[string]Namespace `json:"supportedNamespaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestParams struct {
|
type RequestParams struct {
|
||||||
@ -105,11 +111,67 @@ type SessionRequestResponse struct {
|
|||||||
SignedMessage interface{} `json:"signedMessage,omitempty"`
|
SignedMessage interface{} `json:"signedMessage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func sessionProposalToSupportedChain(caipChains []string, supportsChain func(uint64) bool) (chains []uint64, eipChains []string) {
|
||||||
chains = make([]uint64, 0, 1)
|
chains = make([]uint64, 0, 1)
|
||||||
eipChains = make([]string, 0, 1)
|
eipChains = make([]string, 0, 1)
|
||||||
for _, caip2Str := range caipChains {
|
for _, caip2Str := range caipChains {
|
||||||
chainID, err := parseCaip2ChainID(caip2Str)
|
_, chainID, err := parseCaip2ChainID(caip2Str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed parsing CAIP-2", "str", caip2Str, "error", err)
|
log.Warn("Failed parsing CAIP-2", "str", caip2Str, "error", err)
|
||||||
continue
|
continue
|
||||||
@ -125,22 +187,12 @@ func sessionProposalToSupportedChain(caipChains []string, supportsChain func(uin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func activeToOwnedAccounts(activeAccounts []*accounts.Account) []types.Address {
|
func caip10Accounts(accounts []*accounts.Account, chains []uint64) []string {
|
||||||
addresses := make([]types.Address, 0, 1)
|
addresses := make([]string, 0, len(accounts)*len(chains))
|
||||||
for _, account := range activeAccounts {
|
for _, acc := range accounts {
|
||||||
if account.Type != accounts.AccountTypeWatch {
|
for _, chainID := range chains {
|
||||||
addresses = append(addresses, account.Address)
|
addresses = append(addresses, fmt.Sprintf("%s:%s:%s", SupportedEip155Namespace, strconv.FormatUint(chainID, 10), acc.Address.Hex()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addresses
|
return addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
func caip10Accounts(addresses []types.Address, chains []uint64) []string {
|
|
||||||
accounts := make([]string, 0, len(addresses)*len(chains))
|
|
||||||
for _, address := range addresses {
|
|
||||||
for _, chainID := range chains {
|
|
||||||
accounts = append(accounts, "eip155:"+strconv.FormatUint(chainID, 10)+":"+address.Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return accounts
|
|
||||||
}
|
|
||||||
|
@ -4,10 +4,194 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"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/multiaccounts/accounts"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Test_sessionProposalValidity(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
sessionProposalJSON string
|
||||||
|
expectedValidity bool
|
||||||
|
}{
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#11-proposal-namespaces-does-not-include-an-optional-namespace
|
||||||
|
{
|
||||||
|
name: "proposal-namespaces-does-not-include-an-optional-namespace",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155:10": {
|
||||||
|
"methods": ["personal_sign"],
|
||||||
|
"events": ["accountsChanged", "chainChanged"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: true,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#12-proposal-namespaces-must-not-have-chains-empty
|
||||||
|
{
|
||||||
|
name: "proposal-namespaces-must-not-have-chains-empty",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"cosmos": {
|
||||||
|
"chains": [],
|
||||||
|
"methods": ["cosmos_signDirect"],
|
||||||
|
"events": ["someCosmosEvent"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: false,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#13-chains-might-be-omitted-if-the-caip-2-is-defined-in-the-index
|
||||||
|
{
|
||||||
|
name: "chains-might-be-omitted-if-the-caip-2-is-defined-in-the-index",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155": {
|
||||||
|
"chains": ["eip155:1", "eip155:137"],
|
||||||
|
"methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign"],
|
||||||
|
"events": ["accountsChanged", "chainChanged"]
|
||||||
|
},
|
||||||
|
"eip155:10": {
|
||||||
|
"methods": ["personal_sign"],
|
||||||
|
"events": ["accountsChanged", "chainChanged"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: true,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#14-chains-must-be-caip-2-compliant
|
||||||
|
{
|
||||||
|
name: "chains-must-be-caip-2-compliant",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155": {
|
||||||
|
"chains": ["42"],
|
||||||
|
"methods": ["eth_sign"],
|
||||||
|
"events": ["accountsChanged"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: false,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#15-proposal-namespace-methods-and-events-may-be-empty
|
||||||
|
{
|
||||||
|
name: "proposal-namespace-methods-and-events-may-be-empty",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155": {
|
||||||
|
"chains": ["eip155:1"],
|
||||||
|
"methods": [],
|
||||||
|
"events": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: true,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#16-all-chains-in-the-namespace-must-contain-the-namespace-prefix
|
||||||
|
{
|
||||||
|
name: "all-chains-in-the-namespace-must-contain-the-namespace-prefix",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155": {
|
||||||
|
"chains": ["eip155:1", "eip155:137", "cosmos:cosmoshub-4"],
|
||||||
|
"methods": ["eth_sendTransaction"],
|
||||||
|
"events": ["accountsChanged", "chainChanged"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"optionalNamespaces": {
|
||||||
|
"eip155:42161": {
|
||||||
|
"methods": ["personal_sign"],
|
||||||
|
"events": ["accountsChanged", "chainChanged"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: false,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#17-namespace-key-must-comply-with-caip-2-specification
|
||||||
|
{
|
||||||
|
name: "namespace-key-must-comply-with-caip-2-specification",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"": {
|
||||||
|
"chains": [":1"],
|
||||||
|
"methods": ["personalSign"],
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"**": {
|
||||||
|
"chains": ["**:1"],
|
||||||
|
"methods": ["personalSign"],
|
||||||
|
"events": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: false,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#18-all-namespaces-must-be-valid
|
||||||
|
{
|
||||||
|
name: "all-namespaces-must-be-valid",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {
|
||||||
|
"eip155": {
|
||||||
|
"chains": ["eip155:1"],
|
||||||
|
"methods": ["personalSign"],
|
||||||
|
"events": []
|
||||||
|
},
|
||||||
|
"cosmos": {
|
||||||
|
"chains": [],
|
||||||
|
"methods": [],
|
||||||
|
"events": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: false,
|
||||||
|
},
|
||||||
|
// https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces#19-proposal-namespaces-may-be-empty
|
||||||
|
{
|
||||||
|
name: "proposal-namespaces-may-be-empty",
|
||||||
|
sessionProposalJSON: `{
|
||||||
|
"params": {
|
||||||
|
"requiredNamespaces": {}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
expectedValidity: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var sessionProposal SessionProposal
|
||||||
|
err := json.Unmarshal([]byte(tt.sessionProposalJSON), &sessionProposal)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
if tt.expectedValidity {
|
||||||
|
assert.True(t, sessionProposal.Valid())
|
||||||
|
} else {
|
||||||
|
assert.False(t, sessionProposal.Valid())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_sessionProposalToSupportedChain(t *testing.T) {
|
func Test_sessionProposalToSupportedChain(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
chains []string
|
chains []string
|
||||||
@ -66,52 +250,10 @@ func Test_sessionProposalToSupportedChain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_activeToOwnedAccounts(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
activeAccounts []*accounts.Account
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []types.Address
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "filter_out_watch_accounts",
|
|
||||||
args: args{
|
|
||||||
activeAccounts: []*accounts.Account{
|
|
||||||
{
|
|
||||||
Address: types.HexToAddress("0x1"),
|
|
||||||
Type: accounts.AccountTypeWatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: types.HexToAddress("0x2"),
|
|
||||||
Type: accounts.AccountTypeSeed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: types.HexToAddress("0x3"),
|
|
||||||
Type: accounts.AccountTypeSeed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []types.Address{
|
|
||||||
types.HexToAddress("0x2"),
|
|
||||||
types.HexToAddress("0x3"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := activeToOwnedAccounts(tt.args.activeAccounts); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("activeToOwnedAccounts() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_caip10Accounts(t *testing.T) {
|
func Test_caip10Accounts(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
addresses []types.Address
|
accounts []*accounts.Account
|
||||||
chains []uint64
|
chains []uint64
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -121,9 +263,15 @@ func Test_caip10Accounts(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "generate_caip10_accounts",
|
name: "generate_caip10_accounts",
|
||||||
args: args{
|
args: args{
|
||||||
addresses: []types.Address{
|
accounts: []*accounts.Account{
|
||||||
types.HexToAddress("0x1"),
|
{
|
||||||
types.HexToAddress("0x2"),
|
Address: types.HexToAddress("0x1"),
|
||||||
|
Type: accounts.AccountTypeWatch,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: types.HexToAddress("0x2"),
|
||||||
|
Type: accounts.AccountTypeSeed,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
chains: []uint64{1, 2},
|
chains: []uint64{1, 2},
|
||||||
},
|
},
|
||||||
@ -137,17 +285,23 @@ func Test_caip10Accounts(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty_addresses",
|
name: "empty_addresses",
|
||||||
args: args{
|
args: args{
|
||||||
addresses: []types.Address{},
|
accounts: []*accounts.Account{},
|
||||||
chains: []uint64{1, 2},
|
chains: []uint64{1, 2},
|
||||||
},
|
},
|
||||||
want: []string{},
|
want: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty_chains",
|
name: "empty_chains",
|
||||||
args: args{
|
args: args{
|
||||||
addresses: []types.Address{
|
accounts: []*accounts.Account{
|
||||||
types.HexToAddress("0x1"),
|
{
|
||||||
types.HexToAddress("0x2"),
|
Address: types.HexToAddress("0x1"),
|
||||||
|
Type: accounts.AccountTypeWatch,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: types.HexToAddress("0x2"),
|
||||||
|
Type: accounts.AccountTypeSeed,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
chains: []uint64{},
|
chains: []uint64{},
|
||||||
},
|
},
|
||||||
@ -156,7 +310,7 @@ func Test_caip10Accounts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := caip10Accounts(tt.args.addresses, tt.args.chains); !reflect.DeepEqual(got, tt.want) {
|
if got := caip10Accounts(tt.args.accounts, tt.args.chains); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("caip10Accounts() = %v, want %v", got, tt.want)
|
t.Errorf("caip10Accounts() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user