Skip to content

Commit

Permalink
Merge pull request #289 from WalletConnect/feature/non_evm_chains_sup…
Browse files Browse the repository at this point in the history
…port

Wallet Sample: support for Solana, Polkadot and Kadena chains
  • Loading branch information
quetool committed May 22, 2024
2 parents 84889c8 + 0b4ab27 commit b733075
Show file tree
Hide file tree
Showing 34 changed files with 1,834 additions and 835 deletions.
5 changes: 4 additions & 1 deletion example/dapp/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ class _MyHomePageState extends State<MyHomePage> {
// Loop through the events for that chain
for (final event in getChainEvents(chain.type)) {
debugPrint('registerEventHandler $event for chain ${chain.chainId}');
_web3App!.registerEventHandler(chainId: chain.chainId, event: event);
_web3App!.registerEventHandler(
chainId: chain.chainId,
event: event,
);
}
}

Expand Down
2 changes: 2 additions & 0 deletions example/dapp/lib/models/chain_metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ enum ChainType {
eip155,
solana,
kadena,
cosmos,
polkadot,
}

class ChainMetadata {
Expand Down
109 changes: 78 additions & 31 deletions example/dapp/lib/pages/connect_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
// ignore: unused_import
import 'package:url_launcher/url_launcher_string.dart';
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/constants.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/crypto/chain_data.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/crypto/eip155.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/crypto/polkadot.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/crypto/solana.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/string_constants.dart';
import 'package:walletconnect_flutter_v2_dapp/widgets/chain_button.dart';

Expand Down Expand Up @@ -52,11 +54,61 @@ class ConnectPageState extends State<ConnectPage> {
_testnetOnly = value;
}

void _selectChain(ChainMetadata chain) {
setState(() {
if (_selectedChains.contains(chain)) {
_selectedChains.remove(chain);
} else {
_selectedChains.add(chain);
}
_updateNamespaces();

debugPrint('$optionalNamespaces');
});
}

Map<String, RequiredNamespace> optionalNamespaces = {};

void _updateNamespaces() {
optionalNamespaces = {};

final evmChains =
_selectedChains.where((e) => e.type == ChainType.eip155).toList();
if (evmChains.isNotEmpty) {
optionalNamespaces['eip155'] = RequiredNamespace(
chains: evmChains.map((c) => c.chainId).toList(),
methods: EIP155.methods.values.toList(),
events: EIP155.events.values.toList(),
);
}

final solanaChains =
_selectedChains.where((e) => e.type == ChainType.solana).toList();
if (solanaChains.isNotEmpty) {
optionalNamespaces['solana'] = RequiredNamespace(
chains: solanaChains.map((c) => c.chainId).toList(),
methods: Solana.methods.values.toList(),
events: Solana.events.values.toList(),
);
}

final polkadotChains =
_selectedChains.where((e) => e.type == ChainType.polkadot).toList();
if (polkadotChains.isNotEmpty) {
optionalNamespaces['polkadot'] = RequiredNamespace(
chains: polkadotChains.map((c) => c.chainId).toList(),
methods: Polkadot.methods.values.toList(),
events: Polkadot.events.values.toList(),
);
}
}

@override
Widget build(BuildContext context) {
// Build the list of chain buttons, clear if the textnet changed
final List<ChainMetadata> chains =
_testnetOnly ? ChainData.testChains : ChainData.mainChains;
final testChains = ChainData.allChains.where((e) => e.isTestnet).toList();
final mainChains = ChainData.allChains.where((e) => !e.isTestnet).toList();
final List<ChainMetadata> chains = _testnetOnly ? testChains : mainChains;

List<Widget> children = [];

Expand All @@ -65,15 +117,7 @@ class ConnectPageState extends State<ConnectPage> {
children.add(
ChainButton(
chain: chain,
onPressed: () {
setState(() {
if (_selectedChains.contains(chain)) {
_selectedChains.remove(chain);
} else {
_selectedChains.add(chain);
}
});
},
onPressed: () => _selectChain(chain),
selected: _selectedChains.contains(chain),
),
);
Expand Down Expand Up @@ -121,6 +165,8 @@ class ConnectPageState extends State<ConnectPage> {
),
);

children.add(const SizedBox.square(dimension: 12.0));

return Center(
child: Container(
padding: const EdgeInsets.all(
Expand Down Expand Up @@ -179,31 +225,32 @@ class ConnectPageState extends State<ConnectPage> {
);
}

// Future<void> _onConnectWeb() async {
// // `Ethereum.isSupported` is the same as `ethereum != null`
// if (ethereum != null) {
// try {
// // Prompt user to connect to the provider, i.e. confirm the connection modal
// final accounts = await ethereum!.requestAccount();
// // Get all accounts in node disposal
// debugPrint('accounts ${accounts.join(', ')}');
// } on EthereumUserRejected {
// debugPrint('User rejected the modal');
// }
// }
// }

Future<void> _onConnect({
Function(String message)? showToast,
VoidCallback? closeModal,
}) async {
debugPrint('Creating connection and session');
// It is currently safer to send chains approvals on optionalNamespaces
// but depending on Wallet implementation you may need to send some (for innstance eip155:1) as required
final ConnectResponse res = await widget.web3App.connect(
// requiredNamespaces: {
// 'eip155': const RequiredNamespace(
// chains: [],
// methods: MethodsConstants.requiredMethods,
// events: EventsConstants.requiredEvents,
// ),
// },
optionalNamespaces: {
'eip155': RequiredNamespace(
chains: _selectedChains.map((c) => c.chainId).toList(),
methods: MethodsConstants.allMethods,
events: EventsConstants.allEvents,
),
},
final connectResponse = await widget.web3App.connect(
optionalNamespaces: optionalNamespaces,
);

final encodedUri = Uri.encodeComponent(res.uri.toString());
final encodedUri = Uri.encodeComponent(connectResponse.uri.toString());
final uri = 'wcflutterwallet://wc?uri=$encodedUri';
// final uri = 'metamask://wc?uri=$encodedUri';
if (await canLaunchUrlString(uri)) {
Expand All @@ -228,14 +275,14 @@ class ConnectPageState extends State<ConnectPage> {
if (openApp) {
launchUrlString(uri, mode: LaunchMode.externalApplication);
} else {
_showQrCode(res);
_showQrCode(connectResponse);
}
} else {
_showQrCode(res);
_showQrCode(connectResponse);
}

debugPrint('Awaiting session proposal settlement');
final _ = await res.session.future;
final _ = await connectResponse.session.future;

showToast?.call(StringConstants.connectionEstablished);
}
Expand Down
157 changes: 146 additions & 11 deletions example/dapp/lib/utils/crypto/chain_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart';

class ChainData {
static final List<ChainMetadata> mainChains = [
static final List<ChainMetadata> eip155Chains = [
ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:1',
Expand All @@ -27,17 +27,54 @@ class ChainData {
color: Colors.blue,
rpc: ['https://arbitrum.blockpi.network/v1/rpc/public'],
),
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:10',
name: 'OP Mainnet',
logo: '/chain-logos/eip155-10.png',
color: Colors.red,
rpc: ['https://mainnet.optimism.io/'],
),
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:43114',
name: 'Avalanche',
logo: '/chain-logos/eip155-43114.png',
color: Colors.red,
color: Colors.orange,
rpc: ['https://api.avax.network/ext/bc/C/rpc'],
),
];

static final List<ChainMetadata> testChains = [
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:56',
name: 'BNB Smart Chain Mainnet',
logo: '/chain-logos/eip155-56.png',
color: Colors.orange,
rpc: ['https://bsc-dataseed1.bnbchain.org'],
),
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:42220',
name: 'Celo',
logo: '/chain-logos/eip155-42220.png',
color: Colors.green,
rpc: ['https://forno.celo.org/'],
),
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:100',
name: 'Gnosis',
logo: '/chain-logos/eip155-100.png',
color: Colors.greenAccent,
rpc: ['https://rpc.gnosischain.com/'],
),
const ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:324',
name: 'zkSync',
logo: '/chain-logos/eip155-324.png',
color: Colors.black,
rpc: ['https://mainnet.era.zksync.io'],
),
ChainMetadata(
type: ChainType.eip155,
chainId: 'eip155:11155111',
Expand All @@ -56,17 +93,115 @@ class ChainData {
isTestnet: true,
rpc: ['https://matic-mumbai.chainstacklabs.com'],
),
ChainMetadata(
];

static final List<ChainMetadata> solanaChains = [
const ChainMetadata(
type: ChainType.solana,
chainId: 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ',
name: 'Solana Mainnet 1',
logo: '/chain-logos/solana.png',
color: Color.fromARGB(255, 247, 0, 255),
rpc: [
'https://rpc.ankr.com/solana',
'https://api.tatum.io/v3/blockchain/node/solana-mainnet',
],
),
const ChainMetadata(
type: ChainType.solana,
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
name: 'Solana Mainnet 2',
logo: '/chain-logos/solana.png',
color: Color.fromARGB(255, 247, 0, 255),
rpc: [
'https://rpc.ankr.com/solana',
'https://api.tatum.io/v3/blockchain/node/solana-mainnet',
],
),
const ChainMetadata(
type: ChainType.solana,
chainId: 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z',
name: 'Solana Testnet',
logo: '/chain-logos/solana.png',
color: Colors.black,
isTestnet: true,
rpc: [
'https://api.testnet.solana.com',
],
),
];

static final List<ChainMetadata> cosmosChains = [
// TODO TO BE SUPPORTED
const ChainMetadata(
type: ChainType.cosmos,
chainId: 'cosmos:cosmoshub-4',
name: 'Cosmos Mainnet',
logo: '/chain-logos/cosmos.png',
color: Colors.purple,
rpc: [
'https://cosmos-rpc.polkachu.com:443',
'https://rpc-cosmoshub-ia.cosmosia.notional.ventures:443',
'https://rpc.cosmos.network:443',
],
),
];

static final List<ChainMetadata> kadenaChains = [
// TODO TO BE SUPPORTED
const ChainMetadata(
type: ChainType.kadena,
chainId: 'kadena:mainnet01',
name: 'Kadena Mainnet',
logo: '/chain-logos/kadena.png',
color: Colors.green,
rpc: [
'https://api.chainweb.com',
],
),
const ChainMetadata(
type: ChainType.kadena,
chainId: 'kadena:testnet04',
name: 'Kadena',
logo: 'TODO',
color: Colors.purple.shade600,
name: 'Kadena Testnet',
logo: '/chain-logos/kadena.png',
color: Colors.green,
isTestnet: true,
rpc: [
"https://api.testnet.chainweb.com",
'https://api.chainweb.com',
],
),
];

static final List<ChainMetadata> allChains = [...mainChains, ...testChains];
static final List<ChainMetadata> polkadotChains = [
const ChainMetadata(
type: ChainType.polkadot,
chainId: 'polkadot:91b171bb158e2d3848fa23a9f1c25182',
name: 'Polkadot Mainnet',
logo: '/chain-logos/polkadot.png',
color: Color.fromARGB(255, 174, 57, 220),
rpc: [
'wss://rpc.polkadot.io',
// 'wss://rpc.matrix.canary.enjin.io'
],
),
const ChainMetadata(
type: ChainType.polkadot,
chainId: 'polkadot:e143f23803ac50e8f6f8e62695d1ce9e',
name: 'Polkadot Testnet (Westend)',
logo: '/chain-logos/polkadot.png',
color: Color.fromARGB(255, 174, 57, 220),
isTestnet: true,
rpc: [
'wss://westend-rpc.polkadot.io',
],
),
];

static final List<ChainMetadata> allChains = [
...eip155Chains,
...solanaChains,
...polkadotChains,
// ...kadenaChains,
// ...cosmosChains,
];
}
Loading

0 comments on commit b733075

Please sign in to comment.