keyring.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. import 'dart:convert';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:polkawallet_sdk/api/apiKeyring.dart';
  5. import 'package:polkawallet_sdk/api/types/addressIconData.dart';
  6. import 'package:polkawallet_sdk/polkawallet_sdk.dart';
  7. import 'package:polkawallet_sdk/storage/keyring.dart';
  8. import 'package:polkawallet_sdk/storage/types/keyPairData.dart';
  9. class KeyringPage extends StatefulWidget {
  10. KeyringPage(this.sdk, this.keyring, this.showResult);
  11. final WalletSDK sdk;
  12. final Keyring keyring;
  13. final Function(BuildContext, String, String) showResult;
  14. static const String route = '/keyring';
  15. @override
  16. _KeyringPageState createState() => _KeyringPageState();
  17. }
  18. class _KeyringPageState extends State<KeyringPage> {
  19. final String _testJson = '''{
  20. "pubKey":"0xa2d1d33cc490d34ccc6938f8b30430428da815a85bf5927adc85d9e27cbbfc1a",
  21. "address":"14gV68QsGAEUGkcuV5JA1hx2ZFTuKJthMFfnkDyLMZyn8nnb",
  22. "encoded":"G3BHvs9tVTSf1Qe02bcOGpj7vjLdgqyS+/s0/J3EfRMAgAAAAQAAAAgAAADpWTEOs5/06DmEZaeuoExpf9+y1xcUhIzmEr6dUxyl67VQRX2KNGVmTqq05/sEIUDPVeOqqLbjBEPaNRoC0lZTQlKM5u38lX4PzKivGHM9ZJkvtQxf7RAndN/vgfIX4X76gX60bqrUY8Qr2ZswtuPTeGVKQOD7y0GtoPOcR2RzFg6rs44NuugTR0UwA8HWTDkh0c/KOnUc1FJDb4rV",
  23. "encoding":{"content":["pkcs8","sr25519"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},
  24. "meta": {
  25. "name": "testName-3",
  26. "whenCreated": 1598270113026,
  27. "whenEdited": 1598270113026
  28. }}''';
  29. final String _testPass = 'a123456';
  30. KeyPairData? _testAcc;
  31. int _ss58 = 0;
  32. bool _submitting = false;
  33. void _setSS58(int ss58) {
  34. final res = widget.keyring.setSS58(ss58);
  35. if (res != null) {
  36. setState(() {
  37. _ss58 = res;
  38. });
  39. }
  40. }
  41. Future<void> _generateMnemonic() async {
  42. setState(() {
  43. _submitting = true;
  44. });
  45. final AddressIconDataWithMnemonic seed =
  46. await widget.sdk.api.keyring.generateMnemonic(_ss58);
  47. widget.showResult(context, 'generateMnemonic', seed.mnemonic!);
  48. setState(() {
  49. _submitting = false;
  50. });
  51. }
  52. Future<void> _getAccountList() async {
  53. final List<KeyPairData> ls = widget.keyring.keyPairs;
  54. widget.showResult(
  55. context,
  56. 'getAccountList',
  57. JsonEncoder.withIndent(' ')
  58. .convert(ls.map((e) => '${e.name}: ${e.address}').toList()),
  59. );
  60. }
  61. Future<void> _getDecryptedSeed() async {
  62. if (_testAcc == null) {
  63. widget.showResult(
  64. context,
  65. 'getDecryptedSeeds',
  66. 'should import keyPair to init test account.',
  67. );
  68. return;
  69. }
  70. setState(() {
  71. _submitting = true;
  72. });
  73. final seed = await widget.sdk.api.keyring
  74. .getDecryptedSeed(widget.keyring, _testPass);
  75. // await widget.sdk.api.keyring.getDecryptedSeed(_testAcc, 'a654321');
  76. widget.showResult(
  77. context,
  78. 'getAccountList',
  79. seed == null
  80. ? 'null'
  81. : JsonEncoder.withIndent(' ').convert({
  82. 'address': _testAcc?.address,
  83. 'type': seed.type,
  84. 'seed': seed.seed,
  85. 'error': seed.error,
  86. }),
  87. );
  88. setState(() {
  89. _submitting = false;
  90. });
  91. }
  92. Future<void> _importFromMnemonic() async {
  93. setState(() {
  94. _submitting = true;
  95. });
  96. final json = await widget.sdk.api.keyring.importAccount(
  97. widget.keyring,
  98. keyType: KeyType.mnemonic,
  99. key:
  100. 'wing know chapter eight shed lens mandate lake twenty useless bless glory',
  101. name: 'testName01',
  102. password: _testPass,
  103. );
  104. final acc = await widget.sdk.api.keyring.addAccount(
  105. widget.keyring,
  106. keyType: KeyType.mnemonic,
  107. acc: json!,
  108. password: _testPass,
  109. );
  110. widget.showResult(
  111. context,
  112. 'importFromMnemonic',
  113. JsonEncoder.withIndent(' ').convert(acc.toJson()),
  114. );
  115. setState(() {
  116. _submitting = false;
  117. _testAcc = acc;
  118. });
  119. }
  120. Future<void> _importFromRawSeed() async {
  121. setState(() {
  122. _submitting = true;
  123. });
  124. final json = await widget.sdk.api.keyring.importAccount(
  125. widget.keyring,
  126. keyType: KeyType.rawSeed,
  127. key: 'Alice',
  128. name: 'testName02',
  129. password: _testPass,
  130. );
  131. final acc = await widget.sdk.api.keyring.addAccount(
  132. widget.keyring,
  133. keyType: KeyType.mnemonic,
  134. acc: json!,
  135. password: _testPass,
  136. );
  137. widget.showResult(
  138. context,
  139. 'importFromRawSeed',
  140. JsonEncoder.withIndent(' ').convert(acc.toJson()),
  141. );
  142. setState(() {
  143. _submitting = false;
  144. _testAcc = acc;
  145. });
  146. }
  147. Future<void> _importFromKeystore() async {
  148. setState(() {
  149. _submitting = true;
  150. });
  151. final json = await widget.sdk.api.keyring.importAccount(
  152. widget.keyring,
  153. keyType: KeyType.keystore,
  154. key: _testJson,
  155. name: 'testName03',
  156. password: _testPass,
  157. );
  158. final acc = await widget.sdk.api.keyring.addAccount(
  159. widget.keyring,
  160. keyType: KeyType.mnemonic,
  161. acc: json!,
  162. password: _testPass,
  163. );
  164. widget.showResult(
  165. context,
  166. 'importFromKeystore',
  167. JsonEncoder.withIndent(' ').convert(acc.toJson()),
  168. );
  169. setState(() {
  170. _submitting = false;
  171. _testAcc = acc;
  172. });
  173. }
  174. Future<void> _deleteAccount() async {
  175. if (_testAcc == null) {
  176. widget.showResult(
  177. context,
  178. 'deleteAccount',
  179. 'should import keyPair to init test account.',
  180. );
  181. return;
  182. }
  183. setState(() {
  184. _submitting = true;
  185. });
  186. await widget.sdk.api.keyring.deleteAccount(widget.keyring, _testAcc!);
  187. widget.showResult(
  188. context,
  189. 'deleteAccount',
  190. 'ok',
  191. );
  192. setState(() {
  193. _submitting = false;
  194. });
  195. }
  196. Future<void> _checkPassword() async {
  197. if (_testAcc == null) {
  198. widget.showResult(
  199. context,
  200. 'checkPassword',
  201. 'should import keyPair to init test account.',
  202. );
  203. return;
  204. }
  205. setState(() {
  206. _submitting = true;
  207. });
  208. final bool passed =
  209. await widget.sdk.api.keyring.checkPassword(_testAcc!, _testPass);
  210. // await widget.sdk.api.keyring.checkPassword(_testAcc, 'a654321');
  211. widget.showResult(
  212. context,
  213. 'checkPassword',
  214. passed.toString(),
  215. );
  216. setState(() {
  217. _submitting = false;
  218. });
  219. }
  220. Future<void> _changePassword() async {
  221. if (_testAcc == null) {
  222. widget.showResult(
  223. context,
  224. 'changePassword',
  225. 'should import keyPair to init test account.',
  226. );
  227. return;
  228. }
  229. setState(() {
  230. _submitting = true;
  231. });
  232. final res = await widget.sdk.api.keyring
  233. // .changePassword(widget.keyring, _testAcc, _testPass, 'a654321');
  234. .changePassword(widget.keyring, 'a654321', _testPass);
  235. widget.showResult(
  236. context,
  237. 'changePassword',
  238. res == null ? 'null' : JsonEncoder.withIndent(' ').convert(res.toJson()),
  239. );
  240. setState(() {
  241. _submitting = false;
  242. });
  243. }
  244. Future<void> _changeName() async {
  245. if (_testAcc == null) {
  246. widget.showResult(
  247. context,
  248. 'changeName',
  249. 'should import keyPair to init test account.',
  250. );
  251. return;
  252. }
  253. setState(() {
  254. _submitting = true;
  255. });
  256. final res =
  257. await widget.sdk.api.keyring.changeName(widget.keyring, 'newName');
  258. widget.showResult(
  259. context,
  260. 'changeName', JsonEncoder.withIndent(' ').convert(res.toJson()),
  261. );
  262. setState(() {
  263. _submitting = false;
  264. });
  265. }
  266. Future<void> _checkDerivePath() async {
  267. setState(() {
  268. _submitting = true;
  269. });
  270. final err = await widget.sdk.api.keyring
  271. .checkDerivePath('Alice', '///', CryptoType.sr25519);
  272. widget.showResult(
  273. context,
  274. 'checkDerivePath',
  275. 'error: $err',
  276. );
  277. setState(() {
  278. _submitting = false;
  279. });
  280. }
  281. @override
  282. void initState() {
  283. super.initState();
  284. WidgetsBinding.instance.addPostFrameCallback((_) {
  285. if (widget.keyring.keyPairs.length > 0) {
  286. setState(() {
  287. _testAcc = widget.keyring.keyPairs[0];
  288. });
  289. }
  290. });
  291. }
  292. @override
  293. Widget build(BuildContext context) {
  294. return Scaffold(
  295. appBar: AppBar(
  296. title: Text('keyring API'),
  297. ),
  298. body: SafeArea(
  299. child: ListView(
  300. children: [
  301. Padding(
  302. padding: EdgeInsets.all(16),
  303. child: Column(
  304. crossAxisAlignment: CrossAxisAlignment.start,
  305. children: [
  306. Text('address ss58Format: ${widget.keyring.ss58}'),
  307. Row(
  308. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  309. children: [
  310. CupertinoButton(
  311. child: Text('Polkadot: 0'),
  312. color:
  313. _ss58 == 0 ? Theme.of(context).primaryColor : null,
  314. onPressed: () => _setSS58(0),
  315. ),
  316. CupertinoButton(
  317. child: Text('Kusama: 2'),
  318. color:
  319. _ss58 == 2 ? Theme.of(context).primaryColor : null,
  320. onPressed: () => _setSS58(2),
  321. ),
  322. CupertinoButton(
  323. child: Text('Substrate: 42'),
  324. color:
  325. _ss58 == 42 ? Theme.of(context).primaryColor : null,
  326. onPressed: () => _setSS58(42),
  327. )
  328. ],
  329. )
  330. ],
  331. ),
  332. ),
  333. Divider(),
  334. ListTile(
  335. title: Text('getAccountList'),
  336. subtitle: Text('''
  337. sdk.api.keyring.accountList'''),
  338. trailing: SubmitButton(
  339. submitting: _submitting,
  340. call: _getAccountList,
  341. ),
  342. ),
  343. Divider(),
  344. ListTile(
  345. title: Text('generateMnemonic'),
  346. subtitle: Text('sdk.api.keyring.generateMnemonic()'),
  347. trailing: SubmitButton(
  348. submitting: _submitting,
  349. call: _generateMnemonic,
  350. ),
  351. ),
  352. Divider(),
  353. ListTile(
  354. title: Text('importFromMnemonic'),
  355. subtitle: Text('''
  356. sdk.api.keyring.importAccount(
  357. keyType: KeyType.mnemonic,
  358. key: 'wing know chapter eight shed lens mandate lake twenty useless bless glory',
  359. name: 'testName01',
  360. password: 'a123456',
  361. )'''),
  362. trailing: SubmitButton(
  363. submitting: _submitting,
  364. call: _importFromMnemonic,
  365. ),
  366. ),
  367. Divider(),
  368. ListTile(
  369. title: Text('importFromRawSeed'),
  370. subtitle: Text('''
  371. sdk.api.keyring.importAccount(
  372. keyType: KeyType.rawSeed,
  373. key: 'Alice',
  374. name: 'testName02',
  375. password: 'a123456',
  376. )'''),
  377. trailing: SubmitButton(
  378. submitting: _submitting,
  379. call: _importFromRawSeed,
  380. ),
  381. ),
  382. Divider(),
  383. ListTile(
  384. title: Text('importFromKeystore'),
  385. subtitle: Text('''
  386. sdk.api.keyring.importAccount(
  387. keyType: KeyType.keystore,
  388. key: '{xxx...xxx}',
  389. name: 'testName03',
  390. password: 'a123456',
  391. )'''),
  392. trailing: SubmitButton(
  393. submitting: _submitting,
  394. call: _importFromKeystore,
  395. ),
  396. ),
  397. Divider(),
  398. ListTile(
  399. title: Text('getDecryptedSeed'),
  400. subtitle: Text('''
  401. sdk.api.keyring.getDecryptedSeed(
  402. '${_testAcc?.toString()}',
  403. 'a123456',
  404. )'''),
  405. trailing: SubmitButton(
  406. submitting: _submitting,
  407. call: _getDecryptedSeed,
  408. ),
  409. ),
  410. Divider(),
  411. ListTile(
  412. title: Text('deleteAccount'),
  413. subtitle: Text('''
  414. sdk.api.keyring.deleteAccount'''),
  415. trailing: SubmitButton(
  416. submitting: _submitting,
  417. call: _deleteAccount,
  418. ),
  419. ),
  420. Divider(),
  421. ListTile(
  422. title: Text('checkPassword'),
  423. subtitle: Text('''
  424. sdk.api.keyring.checkPassword(
  425. '${_testAcc?.toString()}',
  426. 'a123456',
  427. )'''),
  428. trailing: SubmitButton(
  429. submitting: _submitting,
  430. call: _checkPassword,
  431. ),
  432. ),
  433. Divider(),
  434. ListTile(
  435. title: Text('changePassword'),
  436. subtitle: Text('''
  437. sdk.api.keyring.changePassword(
  438. '${_testAcc?.toString()}',
  439. 'a123456',
  440. 'a654321',
  441. )'''),
  442. trailing: SubmitButton(
  443. submitting: _submitting,
  444. call: _changePassword,
  445. ),
  446. ),
  447. Divider(),
  448. ListTile(
  449. title: Text('changeName'),
  450. subtitle: Text('''
  451. sdk.api.keyring.changeName(
  452. '${_testAcc?.toString()}',
  453. 'newName',
  454. )'''),
  455. trailing: SubmitButton(
  456. submitting: _submitting,
  457. call: _changeName,
  458. ),
  459. ),
  460. Divider(),
  461. ListTile(
  462. title: Text('checkDerivePath'),
  463. subtitle: Text('''
  464. sdk.api.keyring.checkDerivePath(
  465. 'Alice',
  466. '///',
  467. CryptoType.sr25519,
  468. )'''),
  469. trailing: SubmitButton(
  470. submitting: _submitting,
  471. call: _checkDerivePath,
  472. ),
  473. ),
  474. Divider(),
  475. ],
  476. ),
  477. ), // This trailing comma makes auto-formatting nicer for build methods.
  478. );
  479. }
  480. }
  481. class SubmitButton extends StatelessWidget {
  482. SubmitButton(
  483. {required this.call, required this.submitting, this.needConnect = false});
  484. final bool submitting;
  485. final bool needConnect;
  486. final Function call;
  487. @override
  488. Widget build(BuildContext context) {
  489. return needConnect
  490. ? Column(
  491. children: [
  492. Icon(
  493. Icons.play_circle_outline,
  494. color: Theme.of(context).disabledColor,
  495. ),
  496. Text('Connection\nRequired', style: TextStyle(fontSize: 10))
  497. ],
  498. )
  499. : IconButton(
  500. color: submitting
  501. ? Theme.of(context).disabledColor
  502. : Theme.of(context).primaryColor,
  503. icon: submitting
  504. ? Icon(Icons.refresh)
  505. : Icon(Icons.play_circle_outline),
  506. onPressed: () => call(),
  507. );
  508. }
  509. }