keyring.dart 14 KB

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