diff --git a/CHANGELOG.md b/CHANGELOG.md index 600256c8..afaa040f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0-beta01 + +- Added Smart Contract interactions to SignEngine + ## 2.1.14 - Minor improvements diff --git a/example/dapp/lib/utils/crypto/eip155.dart b/example/dapp/lib/utils/crypto/eip155.dart index cd6e6bcd..8fbf05d8 100644 --- a/example/dapp/lib/utils/crypto/eip155.dart +++ b/example/dapp/lib/utils/crypto/eip155.dart @@ -245,19 +245,19 @@ class EIP155 { }) async { final results = await Future.wait([ // results[0] - web3App.readContractCall( + web3App.requestReadContract( deployedContract: contract, functionName: 'name', rpcUrl: rpcUrl, ), // results[1] - web3App.readContractCall( + web3App.requestReadContract( deployedContract: contract, functionName: 'totalSupply', rpcUrl: rpcUrl, ), // results[2] - web3App.readContractCall( + web3App.requestReadContract( deployedContract: contract, functionName: 'balanceOf', rpcUrl: rpcUrl, @@ -288,7 +288,7 @@ class EIP155 { required DeployedContract contract, required Transaction transaction, }) async { - return await web3App.writeContractCall( + return await web3App.requestWriteContract( topic: topic, chainId: chainId, rpcUrl: rpcUrl, diff --git a/example/dapp/lib/widgets/method_dialog.dart b/example/dapp/lib/widgets/method_dialog.dart index e745cf00..571b2df3 100644 --- a/example/dapp/lib/widgets/method_dialog.dart +++ b/example/dapp/lib/widgets/method_dialog.dart @@ -44,9 +44,12 @@ class MethodDialogState extends State { content: FutureBuilder( future: widget.response, builder: (BuildContext context, AsyncSnapshot snapshot) { - debugPrint('snapshot: $snapshot'); if (snapshot.hasData) { - final String t = jsonEncode(snapshot.data); + dynamic data = snapshot.data; + if (snapshot.data is String) { + data = jsonDecode(snapshot.data); + } + final String t = jsonEncode(data); return InkWell( onTap: () { Clipboard.setData(ClipboardData(text: t)).then( @@ -77,7 +80,7 @@ class MethodDialogState extends State { return const SizedBox( width: StyleConstants.linear48, height: StyleConstants.linear48, - child: CircularProgressIndicator(), + child: Center(child: CircularProgressIndicator()), ); } }, diff --git a/example/wallet/lib/dependencies/chains/evm_service.dart b/example/wallet/lib/dependencies/chains/evm_service.dart index cfbbcca5..1f2521c5 100644 --- a/example/wallet/lib/dependencies/chains/evm_service.dart +++ b/example/wallet/lib/dependencies/chains/evm_service.dart @@ -98,14 +98,13 @@ class EVMService { } } - Future personalSign(String topic, dynamic parameters) async { + Future personalSign(String topic, dynamic parameters) async { debugPrint('[$runtimeType] personalSign request: $parameters'); - String? result; + dynamic result; final data = EthUtils.getDataFromParamsList(parameters); final message = EthUtils.getUtf8Message(data.toString()); - result = await requestApproval(message); - if (result == null) { + if (await requestApproval(message)) { try { // Load the private key final keys = GetIt.I().getKeysForChain( @@ -122,9 +121,10 @@ class EVMService { result = '0x$signature'; } catch (e) { debugPrint('[$runtimeType] personalSign error $e'); - result = e.toString(); - // _web3Wallet.respondSessionRequest(topic: topic, response: response) + result = JsonRpcError(code: 0, message: e.toString()); } + } else { + result = const JsonRpcError(code: 5001, message: 'User rejected method'); } final session = _web3Wallet.sessions.get(topic); @@ -134,14 +134,13 @@ class EVMService { return result; } - Future ethSign(String topic, dynamic parameters) async { + Future ethSign(String topic, dynamic parameters) async { debugPrint('[$runtimeType] ethSign request: $parameters'); - String? result; + dynamic result; final data = EthUtils.getDataFromParamsList(parameters); final message = EthUtils.getUtf8Message(data.toString()); - result = await requestApproval(message); - if (result == null) { + if (await requestApproval(message)) { try { // Load the private key final keys = GetIt.I().getKeysForChain( @@ -158,8 +157,10 @@ class EVMService { result = '0x$signature'; } catch (e) { debugPrint('[$runtimeType] ethSign error $e'); - result = e.toString(); + result = JsonRpcError(code: 0, message: e.toString()); } + } else { + result = const JsonRpcError(code: 5001, message: 'User rejected method'); } final session = _web3Wallet.sessions.get(topic); @@ -169,12 +170,59 @@ class EVMService { return result; } - Future ethSignTransaction(String topic, dynamic parameters) async { + Future ethSignTypedData(String topic, dynamic parameters) async { + debugPrint('[$runtimeType] ethSignTypedData request: $parameters'); + dynamic result; + final data = parameters[1] as String; + + if (await requestApproval(data)) { + try { + final keys = GetIt.I().getKeysForChain( + chainSupported.chain(), + ); + + result = EthSigUtil.signTypedData( + privateKey: keys[0].privateKey, + jsonData: data, + version: TypedDataVersion.V4, + ); + } catch (e) { + debugPrint('[$runtimeType] ethSignTypedData error $e'); + result = JsonRpcError(code: 0, message: e.toString()); + } + } else { + result = const JsonRpcError(code: 5001, message: 'User rejected method'); + } + + final session = _web3Wallet.sessions.get(topic); + final scheme = session?.peer.metadata.redirect?.native ?? ''; + DeepLinkHandler.goTo(scheme, delay: 300, modalTitle: 'Success'); + + return result; + } + + Future switchChain(String topic, dynamic parameters) async { + debugPrint('received switchChain request: $topic $parameters'); + final params = (parameters as List).first as Map; + final hexChainId = params['chainId'].toString().replaceFirst('0x', ''); + final chainId = int.parse(hexChainId, radix: 16); + final web3wallet = _web3WalletService.getWeb3Wallet(); + await web3wallet.emitSessionEvent( + topic: topic, + chainId: 'eip155:$chainId', + event: SessionEventParams( + name: 'chainChanged', + data: chainId, + ), + ); + } + + Future ethSignTransaction(String topic, dynamic parameters) async { debugPrint('[$runtimeType] ethSignTransaction request: $parameters'); - String? result; + dynamic result; final tJson = parameters[0] as Map; - final transaction = await approveTransaction(jsonEncode(tJson)); + final transaction = await approveTransaction(tJson); if (transaction != null) { try { // Load the private key @@ -182,9 +230,7 @@ class EVMService { chainSupported.chain(), ); final credentials = EthPrivateKey.fromHex('0x${keys[0].privateKey}'); - final chainId = chainSupported.chain().split(':').last; - debugPrint('[$runtimeType] ethSignTransaction chainId: $chainId'); final signature = await ethClient.signTransaction( credentials, @@ -197,11 +243,12 @@ class EVMService { // Return the signed transaction as a hexadecimal string result = '0x$signedTx'; - } catch (e, s) { + } catch (e) { debugPrint('[$runtimeType] ethSignTransaction error $e'); - print(s); - result = e.toString(); + result = JsonRpcError(code: 0, message: e.toString()); } + } else { + result = const JsonRpcError(code: 5001, message: 'User rejected method'); } final session = _web3Wallet.sessions.get(topic); @@ -211,12 +258,12 @@ class EVMService { return result; } - Future ethSendTransaction(String topic, dynamic parameters) async { + Future ethSendTransaction(String topic, dynamic parameters) async { debugPrint('[$runtimeType] ethSendTransaction request: $parameters'); - String? result; + dynamic result; final tJson = parameters[0] as Map; - final transaction = await approveTransaction(jsonEncode(tJson)); + final transaction = await approveTransaction(tJson); if (transaction != null) { try { // Load the private key @@ -235,41 +282,12 @@ class EVMService { ); result = '0x$signedTx'; - } catch (e, s) { - debugPrint('[$runtimeType] ethSendTransaction error $e'); - print(s); - result = e.toString(); - } - } - - final session = _web3Wallet.sessions.get(topic); - final scheme = session?.peer.metadata.redirect?.native ?? ''; - DeepLinkHandler.goTo(scheme, delay: 300, modalTitle: 'Success'); - - return result; - } - - Future ethSignTypedData(String topic, dynamic parameters) async { - debugPrint('[$runtimeType] ethSignTypedData request: $parameters'); - String? result; - final data = parameters[1] as String; - - result = await requestApproval(data); - if (result == null) { - try { - final keys = GetIt.I().getKeysForChain( - chainSupported.chain(), - ); - - result = EthSigUtil.signTypedData( - privateKey: keys[0].privateKey, - jsonData: data, - version: TypedDataVersion.V4, - ); } catch (e) { - debugPrint('[$runtimeType] ethSignTypedData error $e'); - result = e.toString(); + debugPrint('[$runtimeType] ethSendTransaction error $e'); + result = JsonRpcError(code: 0, message: e.toString()); } + } else { + result = const JsonRpcError(code: 5001, message: 'User rejected method'); } final session = _web3Wallet.sessions.get(topic); @@ -279,31 +297,15 @@ class EVMService { return result; } - Future switchChain(String topic, dynamic parameters) async { - debugPrint('received switchChain request: $topic $parameters'); - final params = (parameters as List).first as Map; - final hexChainId = params['chainId'].toString().replaceFirst('0x', ''); - final chainId = int.parse(hexChainId, radix: 16); - final web3wallet = _web3WalletService.getWeb3Wallet(); - await web3wallet.emitSessionEvent( - topic: topic, - chainId: 'eip155:$chainId', - event: SessionEventParams( - name: 'chainChanged', - data: chainId, - ), - ); - } - Future addChain(String topic, dynamic parameters) async { debugPrint('received addChain request: $topic $parameters'); } - Future requestApproval(String text) async { + Future requestApproval(String text) async { final approved = await _bottomSheetService.queueBottomSheet( widget: WCRequestWidget( child: WCConnectionWidget( - title: 'Sign Transaction', + title: 'Approve Request', info: [ WCConnectionModel(text: text), ], @@ -311,17 +313,11 @@ class EVMService { ), ); - if (approved != null && approved == false) { - return 'User rejected signature'; - } - - return null; + return approved ?? false; } - Future approveTransaction(String text) async { - final tJson = jsonDecode(text) as Map; + Future approveTransaction(Map tJson) async { String? tValue = tJson['value']; - debugPrint('tValue $tValue'); EtherAmount? value; if (tValue != null) { tValue = tValue.replaceFirst('0x', ''); @@ -344,68 +340,42 @@ class EVMService { : null, ); - // Gas limit + final gasPrice = await ethClient.getGasPrice(); final gasLimit = await ethClient.estimateGas( sender: transaction.from, to: transaction.to, value: transaction.value, data: transaction.data, + gasPrice: gasPrice, ); - print(gasLimit.toInt().toString()); - // BigInt estimateGas; - // BigInt maxFee; - - final gasPrice = await ethClient.getGasPrice(); - print(gasPrice.getInWei); transaction = transaction.copyWith( - maxGas: gasLimit.toInt(), gasPrice: gasPrice, + maxGas: gasLimit.toInt(), ); - final estimateGas = gasLimit * gasPrice.getInWei; - final maxFee = estimateGas; - final trxValue = transaction.value ?? EtherAmount.zero(); - - BigInt total = estimateGas + trxValue.getInWei; - BigInt maxAmount = maxFee + trxValue.getInWei; - - // Adjust the amount if it exceeds the balance - if (trxValue.getInWei > BigInt.zero) { - final chainId = await ethClient.getNetworkId(); - debugPrint(chainId.toString()); - final balance = await ethClient.getBalance(transaction.from!); - if (maxAmount > balance.getInWei) { - final amountLeft = balance.getInWei - maxFee; - if (amountLeft <= BigInt.zero) { - throw Exception( - 'Insufficient funds for transfer, maybe it needs gas fee.', - ); - } - - transaction = transaction.copyWith( - value: EtherAmount.inWei(amountLeft), - ); - total = estimateGas + trxValue.getInWei; - maxAmount = maxFee + trxValue.getInWei; - } - } + final gweiGasPrice = (transaction.gasPrice?.getInWei ?? BigInt.zero) / + BigInt.from(1000000000); final approved = await _bottomSheetService.queueBottomSheet( widget: WCRequestWidget( child: WCConnectionWidget( - title: 'Sign Transaction', + title: 'Approve Transaction', info: [ - WCConnectionModel(text: text), + WCConnectionModel(elements: [jsonEncode(tJson)]), + WCConnectionModel( + title: 'Gas price', + elements: ['${gweiGasPrice.toStringAsFixed(2)} GWEI'], + ), ], ), ), ); - if (approved != null && approved == false) { - return null; + if (approved == true) { + return transaction; } - return transaction; + return null; } } diff --git a/example/wallet/lib/widgets/wc_connection_widget/wc_connection_widget_info.dart b/example/wallet/lib/widgets/wc_connection_widget/wc_connection_widget_info.dart index da3060d2..20537660 100644 --- a/example/wallet/lib/widgets/wc_connection_widget/wc_connection_widget_info.dart +++ b/example/wallet/lib/widgets/wc_connection_widget/wc_connection_widget_info.dart @@ -34,11 +34,12 @@ class WCConnectionWidgetInfo extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - model.title!, - style: StyleConstants.layerTextStyle3, - ), - const SizedBox(height: StyleConstants.linear8), + if (model.title != null) + Text( + model.title!, + style: StyleConstants.layerTextStyle3, + ), + if (model.title != null) const SizedBox(height: StyleConstants.linear8), Wrap( spacing: 4, runSpacing: 4, diff --git a/lib/apis/sign_api/i_sign_client.dart b/lib/apis/sign_api/i_sign_client.dart index 72deb859..8c4c2f74 100644 --- a/lib/apis/sign_api/i_sign_client.dart +++ b/lib/apis/sign_api/i_sign_client.dart @@ -78,6 +78,23 @@ abstract class ISignClient { required String chainId, required SessionRequestParams request, }); + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }); + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }); + void registerEventHandler({ required String chainId, required String event, diff --git a/lib/apis/sign_api/i_sign_engine_app.dart b/lib/apis/sign_api/i_sign_engine_app.dart index c393d4ea..a0764a0d 100644 --- a/lib/apis/sign_api/i_sign_engine_app.dart +++ b/lib/apis/sign_api/i_sign_engine_app.dart @@ -1,10 +1,5 @@ -import 'package:event/event.dart'; -import 'package:walletconnect_flutter_v2/apis/core/relay_client/relay_client_models.dart'; import 'package:walletconnect_flutter_v2/apis/sign_api/i_sign_engine_common.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/json_rpc_models.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/proposal_models.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/sign_client_events.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/sign_client_models.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; abstract class ISignEngineApp extends ISignEngineCommon { abstract final Event onSessionUpdate; @@ -24,6 +19,23 @@ abstract class ISignEngineApp extends ISignEngineCommon { required String chainId, required SessionRequestParams request, }); + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }); + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }); + void registerEventHandler({ required String chainId, required String event, diff --git a/lib/apis/sign_api/sign_client.dart b/lib/apis/sign_api/sign_client.dart index 888089b7..7b75f063 100644 --- a/lib/apis/sign_api/sign_client.dart +++ b/lib/apis/sign_api/sign_client.dart @@ -20,6 +20,7 @@ import 'package:walletconnect_flutter_v2/apis/sign_api/models/session_models.dar import 'package:walletconnect_flutter_v2/apis/sign_api/sessions.dart'; import 'package:walletconnect_flutter_v2/apis/utils/constants.dart'; import 'package:walletconnect_flutter_v2/apis/utils/log_level.dart'; +import 'package:web3dart/web3dart.dart'; class SignClient implements ISignClient { bool _initialized = false; @@ -246,7 +247,7 @@ class SignClient implements ISignClient { } @override - Future request({ + Future request({ required String topic, required String chainId, required SessionRequestParams request, @@ -262,6 +263,52 @@ class SignClient implements ISignClient { } } + @override + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }) async { + try { + return await engine.requestReadContract( + deployedContract: deployedContract, + functionName: functionName, + rpcUrl: rpcUrl, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + + @override + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }) async { + try { + return await engine.requestWriteContract( + topic: topic, + chainId: chainId, + rpcUrl: rpcUrl, + deployedContract: deployedContract, + functionName: functionName, + transaction: transaction, + method: method, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + @override Future respond({ required String topic, diff --git a/lib/apis/sign_api/sign_engine.dart b/lib/apis/sign_api/sign_engine.dart index 10770b3c..417e2784 100644 --- a/lib/apis/sign_api/sign_engine.dart +++ b/lib/apis/sign_api/sign_engine.dart @@ -1,31 +1,15 @@ import 'dart:async'; import 'dart:convert'; +import 'package:http/http.dart' as http; -import 'package:event/event.dart'; -import 'package:walletconnect_flutter_v2/apis/core/i_core.dart'; -import 'package:walletconnect_flutter_v2/apis/core/pairing/i_pairing_store.dart'; import 'package:walletconnect_flutter_v2/apis/core/pairing/utils/json_rpc_utils.dart'; -import 'package:walletconnect_flutter_v2/apis/core/pairing/utils/pairing_models.dart'; -import 'package:walletconnect_flutter_v2/apis/core/relay_client/relay_client_models.dart'; import 'package:walletconnect_flutter_v2/apis/core/store/i_generic_store.dart'; import 'package:walletconnect_flutter_v2/apis/core/verify/models/verify_context.dart'; -import 'package:walletconnect_flutter_v2/apis/models/basic_models.dart'; -import 'package:walletconnect_flutter_v2/apis/models/json_rpc_error.dart'; import 'package:walletconnect_flutter_v2/apis/models/json_rpc_request.dart'; -import 'package:walletconnect_flutter_v2/apis/models/json_rpc_response.dart'; import 'package:walletconnect_flutter_v2/apis/sign_api/i_sessions.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/i_sign_engine.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/json_rpc_models.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/proposal_models.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/session_models.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/sign_client_events.dart'; -import 'package:walletconnect_flutter_v2/apis/sign_api/models/sign_client_models.dart'; +import 'package:walletconnect_flutter_v2/apis/sign_api/utils/custom_credentials.dart'; import 'package:walletconnect_flutter_v2/apis/sign_api/utils/sign_api_validator_utils.dart'; -import 'package:walletconnect_flutter_v2/apis/utils/constants.dart'; -import 'package:walletconnect_flutter_v2/apis/utils/errors.dart'; -import 'package:walletconnect_flutter_v2/apis/utils/method_constants.dart'; -import 'package:walletconnect_flutter_v2/apis/utils/namespace_utils.dart'; -import 'package:walletconnect_flutter_v2/apis/utils/walletconnect_utils.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; class SignEngine implements ISignEngine { static const List> DEFAULT_METHODS = [ @@ -488,6 +472,68 @@ class SignEngine implements ISignEngine { ); } + @override + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }) async { + try { + core.logger.i('readContractCall: with function $functionName'); + final result = await Web3Client(rpcUrl, http.Client()).call( + contract: deployedContract, + function: deployedContract.function(functionName), + params: parameters, + ); + + core.logger.i( + 'readContractCall - function: $functionName - result: ${result.first}'); + return result.first; + } catch (e) { + rethrow; + } + } + + @override + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }) async { + final credentials = CustomCredentials( + signEngine: this, + topic: topic, + chainId: chainId, + address: transaction.from!, + method: method, + ); + final trx = Transaction.callContract( + contract: deployedContract, + function: deployedContract.function(functionName), + from: credentials.address, + parameters: [ + if (transaction.to != null) transaction.to, + if (transaction.value != null) transaction.value!.getInWei, + ...parameters, + ], + ); + + if (chainId.contains(':')) { + chainId = chainId.split(':').last; + } + return await Web3Client(rpcUrl, http.Client()).sendTransaction( + credentials, + trx, + chainId: int.parse(chainId), + ); + } + @override Future respondSessionRequest({ required String topic, diff --git a/lib/apis/sign_api/utils/custom_credentials.dart b/lib/apis/sign_api/utils/custom_credentials.dart new file mode 100644 index 00000000..1db2c80e --- /dev/null +++ b/lib/apis/sign_api/utils/custom_credentials.dart @@ -0,0 +1,80 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:web3dart/crypto.dart' as crypto; + +class CustomCredentials extends CustomTransactionSender { + CustomCredentials({ + required ISignEngine signEngine, + required String topic, + required String chainId, + required EthereumAddress address, + String? method, + }) : _signEngine = signEngine, + _topic = topic, + _chainId = chainId, + _address = address, + _method = method; + + final ISignEngine _signEngine; + final String _topic; + final String _chainId; + final EthereumAddress _address; + final String? _method; + + @override + EthereumAddress get address => _address; + + @override + Future sendTransaction(Transaction transaction) async { + final result = await _sendTransaction(transaction); + if (result is Map) { + return jsonEncode(result); + } + return result.toString(); + } + + Future _sendTransaction(Transaction transaction) async { + final sessionRequestParams = SessionRequestParams( + method: _method ?? MethodsConstants.ethSendTransaction, + params: [ + transaction.toJson(), + ], + ); + + final result = await _signEngine.request( + topic: _topic, + chainId: _chainId, + request: sessionRequestParams, + ); + return result; + } + + @override + Future extractAddress() => Future.value(_address); + + @override + Future signToSignature( + Uint8List payload, { + int? chainId, + bool isEIP1559 = false, + }) { + final signature = signToEcSignature( + payload, + chainId: chainId, + isEIP1559: isEIP1559, + ); + return Future.value(signature); + } + + @override + crypto.MsgSignature signToEcSignature( + Uint8List payload, { + int? chainId, + bool isEIP1559 = false, + }) { + // TODO: implement signToEcSignature + throw UnimplementedError(); + } +} diff --git a/lib/apis/utils/extensions.dart b/lib/apis/utils/extensions.dart index 2c2858f3..fa855498 100644 --- a/lib/apis/utils/extensions.dart +++ b/lib/apis/utils/extensions.dart @@ -1,11 +1,7 @@ -import 'dart:typed_data'; - import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; import 'package:web3dart/crypto.dart' as crypto; -import 'package:http/http.dart' as http; extension TransactionExtension on Transaction { - // https://github.com/WalletConnect/WalletConnectFlutterV2/issues/246#issuecomment-1905455072 Map toJson() { return { if (from != null) 'from': from!.hex, @@ -24,140 +20,3 @@ extension TransactionExtension on Transaction { }; } } - -extension ContractsInteractionExtension on Web3App { - Future readContractCall({ - required DeployedContract deployedContract, - required String functionName, - required String rpcUrl, - List parameters = const [], - }) async { - try { - core.logger.i('readContractCall: with function $functionName'); - final result = await Web3Client(rpcUrl, http.Client()).call( - contract: deployedContract, - function: deployedContract.function(functionName), - params: parameters, - ); - - core.logger.i( - 'readContractCall - function: $functionName - result: ${result.first}'); - return result.first; - } catch (e) { - rethrow; - } - } - - Future writeContractCall({ - required String topic, - required String chainId, - required String rpcUrl, - required DeployedContract deployedContract, - required String functionName, - required Transaction transaction, - String? method, - List parameters = const [], - }) async { - try { - final credentials = _CustomCredentialsSender( - topic: topic, - chainId: chainId, - signEngine: signEngine, - address: transaction.from!, - method: method, - ); - final trx = Transaction.callContract( - contract: deployedContract, - function: deployedContract.function(functionName), - from: credentials.address, - parameters: [ - if (transaction.to != null) transaction.to, - if (transaction.value != null) transaction.value!.getInWei, - ...parameters, - ], - ); - - if (chainId.contains(':')) { - chainId = chainId.split(':').last; - } - return await Web3Client(rpcUrl, http.Client()).sendTransaction( - credentials, - trx, - chainId: int.parse(chainId), - ); - } catch (e) { - rethrow; - } - } -} - -class _CustomCredentialsSender extends CustomTransactionSender { - _CustomCredentialsSender({ - required ISignEngine signEngine, - required String topic, - required String chainId, - required EthereumAddress address, - String? method, - }) : _signEngine = signEngine, - _topic = topic, - _chainId = chainId, - _address = address, - _method = method; - - final ISignEngine _signEngine; - final String _topic; - final String _chainId; - final EthereumAddress _address; - final String? _method; - - @override - EthereumAddress get address => _address; - - @override - Future sendTransaction(Transaction transaction) async { - try { - final sessionRequestParams = SessionRequestParams( - method: _method ?? MethodsConstants.ethSendTransaction, - params: [ - transaction.toJson(), - ], - ); - - final result = await _signEngine.request( - topic: _topic, - chainId: _chainId, - request: sessionRequestParams, - ); - return result; - } catch (e) { - rethrow; - } - } - - @override - Future extractAddress() => Future.value(_address); - - @override - Future signToSignature( - Uint8List payload, { - int? chainId, - bool isEIP1559 = false, - }) { - final signature = signToEcSignature( - payload, - chainId: chainId, - isEIP1559: isEIP1559, - ); - return Future.value(signature); - } - - @override - crypto.MsgSignature signToEcSignature( - Uint8List payload, { - int? chainId, - bool isEIP1559 = false, - }) { - // TODO: implement signToEcSignature - throw UnimplementedError(); - } -} diff --git a/lib/apis/web3app/web3app.dart b/lib/apis/web3app/web3app.dart index eebc0c0c..11f1167c 100644 --- a/lib/apis/web3app/web3app.dart +++ b/lib/apis/web3app/web3app.dart @@ -194,7 +194,7 @@ class Web3App implements IWeb3App { } @override - Future request({ + Future request({ required String topic, required String chainId, required SessionRequestParams request, @@ -210,6 +210,52 @@ class Web3App implements IWeb3App { } } + @override + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }) async { + try { + return await signEngine.requestReadContract( + deployedContract: deployedContract, + functionName: functionName, + rpcUrl: rpcUrl, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + + @override + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }) async { + try { + return await signEngine.requestWriteContract( + topic: topic, + chainId: chainId, + rpcUrl: rpcUrl, + deployedContract: deployedContract, + functionName: functionName, + transaction: transaction, + method: method, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + @override void registerEventHandler({ required String chainId, diff --git a/lib/src/version.dart b/lib/src/version.dart index 8651b1cf..c79752e5 100644 --- a/lib/src/version.dart +++ b/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '2.1.14'; +const packageVersion = '2.2.0-beta01'; diff --git a/pubspec.yaml b/pubspec.yaml index e37bc619..b319543a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: walletconnect_flutter_v2 description: This repository contains oficial implementation of WalletConnect v2 protocols for Flutter applications. The communications protocol for web3. -version: 2.1.14 +version: 2.2.0-beta01 repository: https://github.com/WalletConnect/WalletConnectFlutterV2 environment: diff --git a/test/sign_api/utils/sign_client_test_wrapper.dart b/test/sign_api/utils/sign_client_test_wrapper.dart index 56a3d628..bf6d76f2 100644 --- a/test/sign_api/utils/sign_client_test_wrapper.dart +++ b/test/sign_api/utils/sign_client_test_wrapper.dart @@ -201,7 +201,7 @@ class SignClientTestWrapper implements ISignEngine { } @override - Future request({ + Future request({ required String topic, required String chainId, required SessionRequestParams request, @@ -217,6 +217,52 @@ class SignClientTestWrapper implements ISignEngine { } } + @override + Future requestReadContract({ + required DeployedContract deployedContract, + required String functionName, + required String rpcUrl, + List parameters = const [], + }) async { + try { + return await client.requestReadContract( + deployedContract: deployedContract, + functionName: functionName, + rpcUrl: rpcUrl, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + + @override + Future requestWriteContract({ + required String topic, + required String chainId, + required String rpcUrl, + required DeployedContract deployedContract, + required String functionName, + required Transaction transaction, + String? method, + List parameters = const [], + }) async { + try { + return await client.requestWriteContract( + topic: topic, + chainId: chainId, + rpcUrl: rpcUrl, + deployedContract: deployedContract, + functionName: functionName, + transaction: transaction, + method: method, + parameters: parameters, + ); + } catch (e) { + rethrow; + } + } + @override Future respondSessionRequest({ required String topic,