Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Samples] Solana v0 Transactions #314

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ app.*.map.json
# fvm
.fvm/

**/secrets.properties
**/*.keystore

# vscode
.vscode/

Expand Down
31 changes: 20 additions & 11 deletions example/dapp/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,11 @@ class _MyHomePageState extends State<MyHomePage> {
_web3App!.onSessionAuthResponse.subscribe(_onSessionAuthResponse);

await _web3App!.init();
await _registerEventHandlers();

DeepLinkHandler.init(_web3App!);
DeepLinkHandler.checkInitialLink();

// Loop through all the chain data
for (final ChainMetadata chain in ChainData.allChains) {
// Loop through the events for that chain
for (final event in getChainEvents(chain.type)) {
_web3App!.registerEventHandler(
chainId: chain.chainId,
event: event,
);
}
}

setState(() {
_pageDatas = [
PageData(
Expand Down Expand Up @@ -167,6 +157,25 @@ class _MyHomePageState extends State<MyHomePage> {
});
}

Future<void> _registerEventHandlers() async {
if (!_web3App!.core.connectivity.isOnline.value) {
await Future.delayed(const Duration(milliseconds: 500));
_registerEventHandlers();
return;
}

// Loop through all the chain data
for (final ChainMetadata chain in ChainData.allChains) {
// Loop through the events for that chain
for (final event in getChainEvents(chain.type)) {
_web3App!.registerEventHandler(
chainId: chain.chainId,
event: event,
);
}
}
}

void _onSessionConnect(SessionConnect? event) {
debugPrint('[SampleDapp] _onSessionConnect $event');
Future.delayed(const Duration(milliseconds: 500), () {
Expand Down
1 change: 1 addition & 0 deletions example/dapp/lib/models/chain_metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum ChainType {
kadena,
cosmos,
polkadot,
bip122,
}

class ChainMetadata {
Expand Down
11 changes: 11 additions & 0 deletions example/dapp/lib/pages/connect_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:qr_flutter/qr_flutter.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/bitcoin.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';
Expand Down Expand Up @@ -91,6 +92,16 @@ class ConnectPageState extends State<ConnectPage> {
);
}

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

final solanaChains =
_selectedChains.where((e) => e.type == ChainType.solana).toList();
if (solanaChains.isNotEmpty) {
Expand Down
86 changes: 86 additions & 0 deletions example/dapp/lib/utils/crypto/bitcoin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart';
import 'package:walletconnect_flutter_v2_dapp/imports.dart';

enum BitcoinMethods {
bitcoinSignTransaction,
bitcoinSignMessage,
}

enum BitcoinEvents {
none,
}

class Bitcoin {
static final Map<BitcoinMethods, String> methods = {
BitcoinMethods.bitcoinSignTransaction: 'btc_sendTransaction',
BitcoinMethods.bitcoinSignMessage: 'btc_signMessage'
};

static final Map<BitcoinEvents, String> events = {};

static Future<dynamic> callMethod({
required Web3App web3App,
required String topic,
required String method,
required ChainMetadata chainData,
required String address,
bool isV0 = false,
}) async {
switch (method) {
case 'btc_signMessage':
const message = "This is a message to be signed for BIP122";
final result = await web3App.request(
topic: topic,
chainId: chainData.chainId,
request: SessionRequestParams(
method: method,
params: [message, address],
),
);
// final checkSegwitAlways = (result.segwitType == "p2wpkh") ||
// (result.segwitType == 'p2sh(p2wpkh)');
return {
'method': method,
'address': address,
// 'valid': verifyBitcoinMessage(
// message,
// result.signature,
// address,
// // undefined,
// // checkSegwitAlways
// ),
'result': ''
'signature: ${result["signature"]}\n'
'segwitType: ${result["segwitType"]}',
};
case 'btc_sendTransaction':
// final utxos = await apiGetAddressUtxos(address, chainId);
// final availableBalance = getAvailableBalanceFromUtxos(utxos); // in satoshis
return web3App.request(
topic: topic,
chainId: chainData.chainId,
request: SessionRequestParams(
method: method,
params: {
'address': address,
'value': 0.0000001, // availableBalance,
'transactionType': 'p2wpkh',
},
),
);
default:
throw 'Method unimplemented';
}
}
}

// Future<void> apiGetAddressUtxos(String address, String chainId) {
// return await (await fetch(`https://mempool.space/signet/api/address/${address}/utxo`)).json();
// }

// int getAvailableBalanceFromUtxos(List utxos) {
// if (!utxos || !utxos.length) {
// return 0;
// }
// return utxos.reduce((acc, { value }) => acc + value, 0);
// }
46 changes: 31 additions & 15 deletions example/dapp/lib/utils/crypto/chain_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,19 @@ class ChainData {
static final List<ChainMetadata> solanaChains = [
const ChainMetadata(
type: ChainType.solana,
chainId: 'solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ',
name: 'Solana Mainnet 1',
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
name: 'Solana Mainnet',
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',
],
rpc: ['https://api.mainnet-beta.solana.com'],
),
const ChainMetadata(
type: ChainType.solana,
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
name: 'Solana Mainnet 2',
chainId: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
name: 'Solana Devnet',
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',
],
rpc: ['https://api.devnet.solana.com'],
),
const ChainMetadata(
type: ChainType.solana,
Expand All @@ -125,9 +119,7 @@ class ChainData {
logo: '/chain-logos/solana.png',
color: Colors.black,
isTestnet: true,
rpc: [
'https://api.testnet.solana.com',
],
rpc: ['https://api.testnet.solana.com'],
),
];

Expand Down Expand Up @@ -197,10 +189,34 @@ class ChainData {
),
];

static final List<ChainMetadata> bitcoinChains = [
const ChainMetadata(
type: ChainType.bip122,
chainId:
'bip122:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
name: 'Bitcoin Mainnet',
logo: '/chain-logos/bitcoin.png',
color: Color.fromARGB(255, 255, 161, 9),
rpc: ['https://bitcoin.drpc.org/'],
),
const ChainMetadata(
type: ChainType.bip122,
chainId:
'bip122:000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943',
name: 'Bitcoin Signet',
logo: '/chain-logos/bitcoin.png',
color: Color.fromARGB(255, 255, 161, 9),
rpc: ['https://signet.bitcoinrpc.org'],
// https://bitcoin-testnet.drpc.org
isTestnet: true,
),
];

static final List<ChainMetadata> allChains = [
...eip155Chains,
...solanaChains,
...polkadotChains,
...bitcoinChains,
// ...kadenaChains,
// ...cosmosChains,
];
Expand Down
3 changes: 3 additions & 0 deletions example/dapp/lib/utils/crypto/helpers.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart';
import 'package:walletconnect_flutter_v2_dapp/utils/crypto/bitcoin.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';
Expand Down Expand Up @@ -36,6 +37,8 @@ List<String> getChainMethods(ChainType value) {
return Solana.methods.values.toList();
case ChainType.polkadot:
return Polkadot.methods.values.toList();
case ChainType.bip122:
return Bitcoin.methods.values.toList();
default:
return [];
}
Expand Down
89 changes: 63 additions & 26 deletions example/dapp/lib/utils/crypto/solana.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import 'dart:convert';
// ignore: depend_on_referenced_packages
import 'package:bs58/bs58.dart';

import 'package:solana_web3/solana_web3.dart' as solana;
// import 'package:solana_web3/programs.dart';
// import 'package:solana_web3/src/encodings/lamports.dart';
// import 'package:solana_web3/src/rpc/models/blockhash_with_expiry_block_height.dart';

import 'package:walletconnect_flutter_v2_dapp/models/chain_metadata.dart';
import 'package:walletconnect_flutter_v2_dapp/imports.dart';

Expand All @@ -28,13 +33,14 @@ class Solana {
required String method,
required ChainMetadata chainData,
required String address,
}) {
final bytes = utf8.encode(
'This is an example message to be signed - ${DateTime.now()}',
);
final message = base58.encode(bytes);
bool isV0 = false,
}) async {
switch (method) {
case 'solana_signMessage':
final bytes = utf8.encode(
'This is an example message to be signed - ${DateTime.now()}',
);
final message = base58.encode(bytes);
return web3App.request(
topic: topic,
chainId: chainData.chainId,
Expand All @@ -47,32 +53,63 @@ class Solana {
),
);
case 'solana_signTransaction':
return web3App.request(
// Create a connection to the devnet cluster.
final cluster = solana.Cluster.https(
Uri.parse(chainData.rpc.first).authority,
);
// final cluster = solana.Cluster.devnet;
final connection = solana.Connection(cluster);

// Fetch the latest blockhash.
final blockhash = await connection.getLatestBlockhash();

// Create a System Program instruction to transfer 0.5 SOL from [address1] to [address2].
final transactionv0 = solana.Transaction.v0(
payer: solana.Pubkey.fromBase58(address),
recentBlockhash: blockhash.blockhash,
instructions: [
solana.TransactionInstruction.fromJson({
"programId": "11111111111111111111111111111111",
"data": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
"keys": [
{
"isSigner": true,
"isWritable": true,
"pubkey": address,
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR"
}
]
}),
// SystemProgram.transfer(
// fromPubkey: solana.Pubkey.fromBase58(address),
// toPubkey: solana.Pubkey.fromBase58(
// '8vCyX7oB6Pc3pbWMGYYZF5pbSnAdQ7Gyr32JqxqCy8ZR',
// ),
// lamports: solana.solToLamports(0.5),
// ),
],
);

const config = solana.TransactionSerializableConfig(
verifySignatures: false,
);
final bytes = transactionv0.serialize(config).asUint8List();
final encodedV0Trx = base64.encode(bytes);

return web3App.signEngine.request(
topic: topic,
chainId: chainData.chainId,
request: SessionRequestParams(
method: method,
params: {
"feePayer": address,
"recentBlockhash": "H32Ss1hxpP2ZJM4whREVNyUWRgzFLVA97UXJUjBrEsgx",
"instructions": [
{
"programId": "11111111111111111111111111111111",
"data": [2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
"keys": [
{
"isSigner": true,
"isWritable": true,
"pubkey": "EbdEmCpKGvEwfwV4ACmVYHFRkwvXdogJhMZeEekDFVVJ"
},
{
"isSigner": false,
"isWritable": true,
"pubkey": "4SzUq9NNYSYGp41ED5NgSDoCrEh9MoD7zSvmtkwseW8s"
}
]
}
]
'transaction': encodedV0Trx,
'pubkey': address,
'feePayer': address,
...transactionv0.message.toJson(),
},
),
);
Expand Down
Loading