| 1 | import 'dart:async'; |
| 2 | import 'dart:typed_data'; |
| 3 | |
| 4 | import 'package:cw_core/hardware/device_not_connected_exception.dart' as exception; |
| 5 | import 'package:trezor_connect/trezor_connect.dart'; |
| 6 | import 'package:web3dart/crypto.dart'; |
| 7 | import 'package:web3dart/web3dart.dart'; |
| 8 | |
| 9 | import '../utils/rlp_decode.dart' as rlp; |
| 10 | |
| 11 | class EvmTrezorCredentials extends CredentialsWithKnownAddress { |
| 12 | final String _address; |
| 13 | |
| 14 | TrezorConnect? trezorConnect; |
| 15 | String? derivationPath; |
| 16 | |
| 17 | EvmTrezorCredentials(this._address); |
| 18 | |
| 19 | @override |
| 20 | EthereumAddress get address => EthereumAddress.fromHex(_address); |
| 21 | |
| 22 | void setTrezorConnect(TrezorConnect connection, [String? derivationPath_]) { |
| 23 | trezorConnect = connection; |
| 24 | derivationPath = derivationPath_ ?? "m/44'/60'/0'/0/0"; |
| 25 | } |
| 26 | |
| 27 | @override |
| 28 | MsgSignature signToEcSignature(Uint8List payload, {int? chainId, bool isEIP1559 = false}) => |
| 29 | throw UnimplementedError("EvmLedgerCredentials.signToEcSignature"); |
| 30 | |
| 31 | @override |
| 32 | Future<MsgSignature> signToSignature(Uint8List payload, |
| 33 | {int? chainId, bool isEIP1559 = false}) async { |
| 34 | if (trezorConnect == null) { |
| 35 | throw exception.DeviceNotConnectedException(); |
| 36 | } |
| 37 | |
| 38 | if (isEIP1559) payload = payload.sublist(1); |
| 39 | |
| 40 | final data = rlp.decode(payload); |
| 41 | |
| 42 | TrezorEthereumTransaction tx; |
| 43 | if (isEIP1559) { |
| 44 | final chainId = data[0][0]; |
| 45 | final nonce = bytesToHex(data[1], include0x: true); |
| 46 | final maxPriorityFeePerGas = bytesToHex(data[2], include0x: true); |
| 47 | final maxFeePerGas = bytesToHex(data[3], include0x: true); |
| 48 | final maxGas = bytesToHex(data[4], include0x: true); |
| 49 | final to = bytesToHex(data[5], include0x: true); |
| 50 | final value = bytesToHex(data[6], include0x: true); |
| 51 | final txData = bytesToHex(data[7], include0x: true); |
| 52 | |
| 53 | tx = TrezorEthereumTransaction( |
| 54 | to: to, |
| 55 | value: value, |
| 56 | maxFeePerGas: maxFeePerGas, |
| 57 | maxPriorityFeePerGas: maxPriorityFeePerGas, |
| 58 | gasLimit: maxGas, |
| 59 | nonce: nonce, |
| 60 | chainId: chainId, |
| 61 | data: txData, |
| 62 | ); |
| 63 | } else { |
| 64 | final nonce = bytesToHex(data[0]); |
| 65 | final gasPrice = bytesToHex(data[1]); |
| 66 | final maxGas = bytesToHex(data[2]); |
| 67 | final to = bytesToHex(data[3]); |
| 68 | final value = bytesToHex(data[4]); |
| 69 | final txData = bytesToHex(data[5]); |
| 70 | tx = TrezorEthereumTransaction( |
| 71 | to: to, |
| 72 | value: value, |
| 73 | gasPrice: gasPrice, |
| 74 | gasLimit: maxGas, |
| 75 | nonce: nonce, |
| 76 | chainId: chainId ?? 1, |
| 77 | data: txData, |
| 78 | ); |
| 79 | } |
| 80 | |
| 81 | final sig = (await trezorConnect!.ethereumSignTransaction(derivationPath!, transaction: tx))!; |
| 82 | |
| 83 | final v = int.parse(strip0x(sig.v), radix: 16); |
| 84 | |
| 85 | if (isEIP1559) { |
| 86 | return MsgSignature( |
| 87 | BigInt.parse(strip0x(sig.r), radix: 16), BigInt.parse(strip0x(sig.s), radix: 16), v); |
| 88 | } |
| 89 | |
| 90 | return MsgSignature( |
| 91 | BigInt.parse(strip0x(sig.r), radix: 16), BigInt.parse(strip0x(sig.s), radix: 16), v); |
| 92 | } |
| 93 | |
| 94 | @override |
| 95 | Future<Uint8List> signPersonalMessage(Uint8List payload, {int? chainId}) async { |
| 96 | if (isNotConnected) throw exception.DeviceNotConnectedException(); |
| 97 | return hexToBytes((await trezorConnect! |
| 98 | .ethereumSignMessage(derivationPath!, message: bytesToHex(payload), hex: true)) |
| 99 | ?.signature ?? |
| 100 | ''); |
| 101 | } |
| 102 | |
| 103 | @override |
| 104 | Uint8List signPersonalMessageToUint8List(Uint8List payload, {int? chainId}) => |
| 105 | throw UnimplementedError("EvmLedgerCredentials.signPersonalMessageToUint8List"); |
| 106 | |
| 107 | bool get isNotConnected => trezorConnect == null; |
| 108 | } |