@harmoniclabs/use-cardano-wallet 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 MartinSchere
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # use-cardano-wallet
2
+
3
+ A simple react hook to connect your application to cardano wallets. This library does not take care of transaction building. For that, you should use something like [Lucid](https://github.com/Berry-Pool/lucid).
4
+
5
+ > ℹ This library does not depend on `cardano-serialization-lib`, so you don't have to worry about big budle sizes!
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ npm i @asterium-dev/use-cardano-wallet
11
+ ```
12
+
13
+ ```
14
+ yarn add @asterium-dev/use-cardano-wallet
15
+ ```
16
+
17
+ ## Basic usage
18
+
19
+ ```ts
20
+ const {
21
+ connect,
22
+ isConnecting,
23
+ isConnected,
24
+ lovelaceBalance,
25
+ address,
26
+ network,
27
+ selectedWallet,
28
+ connectedWallet,
29
+ api,
30
+ disconnect,
31
+ isRefetchingBalance,
32
+ refetchBalance,
33
+ } = useCardanoWallet();
34
+ ```
35
+
36
+ ## Example
37
+
38
+ Check out the example in [this](./example/) folder
39
+
40
+ ---
41
+
42
+ ## API
43
+
44
+ ▸ **useCardanoWallet**(`options?`): ReturnVal
45
+
46
+ ### Parameters
47
+
48
+ | Name | Type | Default value |
49
+ | :-------- | :---------------------- | :--------------- |
50
+ | `options` | UseCardanoWalletOptions | `defaultOptions` |
51
+
52
+ ### UseCardanoWalletOptions:
53
+
54
+ | Name | Type | Description |
55
+ | :----------------- | :------ | :-------------------------------------------------------------------------------------------------------------------- |
56
+ | `autoConnect?` | boolean | Specify if the connector should automatically try to connect to previously connected wallets. Relies on localStorage. |
57
+ | `localStorageKey?` | string | Specify a local storage key to store the connected wallet name |
58
+
59
+ ### Returns object:
60
+
61
+ | Name | Type | Description |
62
+ | :-------------------- | :---------------- | :------------------------------------------------------------------------------- |
63
+ | `address` | null \| string | bech32 representation of the wallet address |
64
+ | `api` | null \| WalletApi | The [CIP30](https://cips.cardano.org/cips/cip30/) object of the connected wallet |
65
+ | `connect` | Function | Primary function to connect the desired wallet |
66
+ | `connectedWallet` | null \| string | The wallet that is currently connected. |
67
+ | `disconnect` | Function | Disconnects the current wallet |
68
+ | `isConnected` | boolean | True if the wallet is connected |
69
+ | `isConnecting` | boolean | Loading indicator for the wallet connection |
70
+ | `isRefetchingBalance` | boolean | Loading indicator for balance refetch |
71
+ | `lovelaceBalance` | null \| number | Wallet balance, in lovelace (1 ADA = 1000000 lovelace) |
72
+ | `network` | null \| NetworkId | 0 if testnet, 1 if mainnet |
73
+ | `refetchBalance` | Function | Refresh the wallet's balance |
74
+ | `selectedWallet` | null \| string | The wallet that was selected to connect. |
@@ -0,0 +1,18 @@
1
+ import { State } from './store';
2
+ import { WalletName } from './typescript/cip30';
3
+ export type UseCardanoWalletOptions = {
4
+ /**
5
+ * Specify if the connector should automatically try to connect
6
+ * to previously connected wallets. Relies on localStorage.
7
+ */
8
+ autoConnect?: boolean;
9
+ /**
10
+ * Specify a local storage key to store the connected wallet name
11
+ */
12
+ localStorageKey?: string;
13
+ };
14
+ type ReturnVal = Omit<State, 'connect' | 'getDetectedWallets'> & {
15
+ connect: (walletName: WalletName) => Promise<void>;
16
+ };
17
+ export declare const useCardanoWallet: ({ autoConnect, localStorageKey, }?: UseCardanoWalletOptions) => ReturnVal;
18
+ export * from './typescript/cip30';
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ import { __assign, __awaiter, __generator, __rest } from "tslib";
2
+ import { useEffect } from 'react';
3
+ import { useStore } from './store';
4
+ var defaultOptions = {
5
+ autoConnect: true,
6
+ localStorageKey: 'cardano-wallet-name',
7
+ };
8
+ export var useCardanoWallet = function (_a) {
9
+ var _b = _a === void 0 ? defaultOptions : _a, _c = _b.autoConnect, autoConnect = _c === void 0 ? true : _c, _d = _b.localStorageKey, localStorageKey = _d === void 0 ? 'cardano-wallet-name' : _d;
10
+ var _e = useStore(), connectWithStore = _e.connect, getDetectedWallets = _e.getDetectedWallets, storeDisconnect = _e.disconnect, rest = __rest(_e, ["connect", "getDetectedWallets", "disconnect"]);
11
+ useEffect(function () {
12
+ getDetectedWallets();
13
+ }, [getDetectedWallets]);
14
+ useEffect(function () {
15
+ // Check localStorage in case we are in SSR
16
+ if (autoConnect && typeof localStorage !== 'undefined') {
17
+ var connectedWalletName_1 = localStorage.getItem(localStorageKey);
18
+ if (!connectedWalletName_1) {
19
+ return;
20
+ }
21
+ // Setting timeout to let the wallet be injected and not get undefined
22
+ setTimeout(function () {
23
+ connectWithStore(connectedWalletName_1, localStorageKey);
24
+ }, 10);
25
+ }
26
+ }, [autoConnect, localStorageKey, connectWithStore]);
27
+ var connect = function (walletName) { return __awaiter(void 0, void 0, void 0, function () {
28
+ return __generator(this, function (_a) {
29
+ switch (_a.label) {
30
+ case 0: return [4 /*yield*/, connectWithStore(walletName, localStorageKey)];
31
+ case 1:
32
+ _a.sent();
33
+ return [2 /*return*/];
34
+ }
35
+ });
36
+ }); };
37
+ var disconnect = function () {
38
+ localStorage.removeItem(localStorageKey);
39
+ storeDisconnect();
40
+ };
41
+ return __assign({ connect: connect, disconnect: disconnect }, rest);
42
+ };
43
+ export * from './typescript/cip30';
44
+ // export default useCardanoWallet;
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAAE,QAAQ,EAA0B,MAAM,SAAS,CAAC;AAyB3D,IAAM,cAAc,GAA4B;IAC9C,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,qBAAqB;CACvC,CAAC;AAMF,MAAM,CAAC,IAAM,gBAAgB,GAAG,UAAC,EAGU;QAHV,qBAGJ,cAAc,KAAA,EAFzC,mBAAkB,EAAlB,WAAW,mBAAG,IAAI,KAAA,EAClB,uBAAuC,EAAvC,eAAe,mBAAG,qBAAqB,KAAA;IAEvC,IAAM,KAKF,QAAQ,EAAE,EAJH,gBAAgB,aAAA,EACzB,kBAAkB,wBAAA,EACN,eAAe,gBAAA,EACxB,IAAI,cAJH,+CAKL,CAAa,CAAC;IAEf,SAAS,CAAC;QACR,kBAAkB,EAAE,CAAC;IACvB,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAEzB,SAAS,CAAC;QACR,2CAA2C;QAC3C,IAAI,WAAW,IAAI,OAAO,YAAY,KAAK,WAAW,EAAE,CAAC;YACvD,IAAM,qBAAmB,GAAG,YAAY,CAAC,OAAO,CAC9C,eAAe,CACK,CAAC;YAEvB,IAAI,CAAC,qBAAmB,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,sEAAsE;YACtE,UAAU,CAAC;gBACT,gBAAgB,CAAC,qBAAmB,EAAE,eAAe,CAAC,CAAC;YACzD,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAErD,IAAM,OAAO,GAAG,UAAO,UAAsB;;;wBAC3C,qBAAM,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,EAAA;;oBAAnD,SAAmD,CAAC;;;;SACrD,CAAC;IACF,IAAM,UAAU,GAAG;QACjB,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QACzC,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC;IAEF,kBAAS,OAAO,SAAA,EAAE,UAAU,YAAA,IAAK,IAAI,EAAG;AAC1C,CAAC,CAAC;AAEF,cAAc,oBAAoB,CAAC;AAEnC,mCAAmC"}
@@ -0,0 +1,30 @@
1
+ import { NetworkId, WalletApi, WalletName } from '../typescript/cip30';
2
+ export interface WalletInfo {
3
+ name: WalletName;
4
+ icon?: string | undefined;
5
+ displayName: string;
6
+ }
7
+ export type State = {
8
+ isConnected: boolean;
9
+ isConnecting: boolean;
10
+ isRefetchingBalance: boolean;
11
+ detectedWallets: WalletInfo[];
12
+ address: null | string;
13
+ rewardAddress: null | string;
14
+ /**
15
+ * The wallet that was selected to connect.
16
+ */
17
+ selectedWallet: null | WalletInfo;
18
+ /**
19
+ * The wallet that is currently connected.
20
+ */
21
+ connectedWallet: null | WalletInfo;
22
+ lovelaceBalance: null | number;
23
+ api: null | WalletApi;
24
+ network: null | NetworkId;
25
+ connect: (walletName: WalletName, localStorageKey: string) => Promise<void>;
26
+ getDetectedWallets: () => void;
27
+ disconnect: () => void;
28
+ refetchBalance: () => Promise<void>;
29
+ };
30
+ export declare const useStore: import("zustand").UseBoundStore<import("zustand").StoreApi<State>>;
@@ -0,0 +1,134 @@
1
+ import { __assign, __awaiter, __generator } from "tslib";
2
+ import produce from 'immer';
3
+ import { create } from 'zustand';
4
+ import { NetworkId, WalletName } from '../typescript/cip30';
5
+ import { parseBalance, toWalletInfo } from '../utils';
6
+ import { fromHex } from '@harmoniclabs/uint8array-utils';
7
+ import { encodeBech32 } from '@harmoniclabs/crypto';
8
+ var defaults = {
9
+ isConnecting: false,
10
+ isConnected: false,
11
+ detectedWallets: [],
12
+ address: null,
13
+ lovelaceBalance: null,
14
+ api: null,
15
+ selectedWallet: null,
16
+ connectedWallet: null,
17
+ network: null,
18
+ isRefetchingBalance: false,
19
+ rewardAddress: null,
20
+ };
21
+ export var useStore = create()(function (set, get) { return (__assign(__assign({}, defaults), { disconnect: function () {
22
+ set(function (prev) {
23
+ return __assign(__assign({}, defaults), {
24
+ // Keep detected wallets
25
+ detectedWallets: prev.detectedWallets });
26
+ });
27
+ }, refetchBalance: function () { return __awaiter(void 0, void 0, void 0, function () {
28
+ var api, balance, lovelace;
29
+ return __generator(this, function (_a) {
30
+ switch (_a.label) {
31
+ case 0:
32
+ api = get().api;
33
+ if (api === null)
34
+ return [2 /*return*/];
35
+ set(produce(function (draft) {
36
+ draft.isRefetchingBalance = true;
37
+ }));
38
+ return [4 /*yield*/, api.getBalance()];
39
+ case 1:
40
+ balance = _a.sent();
41
+ lovelace = parseBalance(balance);
42
+ set(produce(function (draft) {
43
+ draft.isRefetchingBalance = false;
44
+ draft.lovelaceBalance = lovelace;
45
+ }));
46
+ return [2 /*return*/];
47
+ }
48
+ });
49
+ }); }, getDetectedWallets: function () { return __awaiter(void 0, void 0, void 0, function () {
50
+ var ns;
51
+ return __generator(this, function (_a) {
52
+ if (typeof window === 'undefined' || !window.cardano) {
53
+ return [2 /*return*/];
54
+ }
55
+ ns = window.cardano;
56
+ set(produce(function (draft) {
57
+ draft.detectedWallets = Object.keys(ns)
58
+ .filter(function (ns) { return Object.values(WalletName).includes(ns); })
59
+ .map(function (n) { return toWalletInfo(n); });
60
+ }));
61
+ return [2 /*return*/];
62
+ });
63
+ }); }, connect: function (walletName, localStorageKey) { return __awaiter(void 0, void 0, void 0, function () {
64
+ var api_1, rawAddress, rewardAddress_1, buf, addressBytes_1, balance, decodedBalance_1, bechAddr_1, e_1;
65
+ return __generator(this, function (_a) {
66
+ switch (_a.label) {
67
+ case 0:
68
+ set(produce(function (draft) {
69
+ draft.isConnecting = true;
70
+ draft.selectedWallet = toWalletInfo(walletName);
71
+ }));
72
+ _a.label = 1;
73
+ case 1:
74
+ _a.trys.push([1, 10, , 11]);
75
+ // Exit early if the Cardano dApp-Wallet Web Bridge (CIP 30) has not been injected
76
+ // This can happen in a SSR scenario for example
77
+ if (typeof window === 'undefined' || !window.cardano) {
78
+ throw Error('window.cardano is undefined');
79
+ }
80
+ return [4 /*yield*/, window.cardano[walletName].enable()];
81
+ case 2:
82
+ api_1 = _a.sent();
83
+ return [4 /*yield*/, api_1.getChangeAddress()];
84
+ case 3:
85
+ rawAddress = _a.sent();
86
+ if (!!rawAddress) return [3 /*break*/, 5];
87
+ return [4 /*yield*/, api_1.getUsedAddresses()];
88
+ case 4:
89
+ rawAddress = (_a.sent())[0];
90
+ _a.label = 5;
91
+ case 5:
92
+ if (!!rawAddress) return [3 /*break*/, 7];
93
+ return [4 /*yield*/, api_1.getUnusedAddresses()];
94
+ case 6:
95
+ rawAddress = (_a.sent())[0];
96
+ _a.label = 7;
97
+ case 7: return [4 /*yield*/, api_1.getRewardAddresses()];
98
+ case 8:
99
+ rewardAddress_1 = (_a.sent())[0];
100
+ if (rewardAddress_1) {
101
+ buf = fromHex(rewardAddress_1);
102
+ rewardAddress_1 = encodeBech32(buf[0] === NetworkId.MAINNET ? 'stake' : 'stake_test', buf.slice(1));
103
+ }
104
+ addressBytes_1 = fromHex(rawAddress);
105
+ return [4 /*yield*/, api_1.getBalance()];
106
+ case 9:
107
+ balance = _a.sent();
108
+ decodedBalance_1 = parseBalance(balance);
109
+ bechAddr_1 = encodeBech32(addressBytes_1[0] === NetworkId.MAINNET ? 'addr' : 'addr_test', addressBytes_1.slice(1));
110
+ localStorage.setItem(localStorageKey, walletName);
111
+ set(produce(function (draft) {
112
+ draft.isConnecting = false;
113
+ draft.isConnected = true;
114
+ draft.api = api_1;
115
+ draft.lovelaceBalance = decodedBalance_1;
116
+ draft.rewardAddress = rewardAddress_1;
117
+ draft.address = bechAddr_1;
118
+ draft.network = addressBytes_1[0];
119
+ draft.connectedWallet = toWalletInfo(walletName);
120
+ }));
121
+ return [3 /*break*/, 11];
122
+ case 10:
123
+ e_1 = _a.sent();
124
+ console.error(e_1);
125
+ set(produce(function (draft) {
126
+ draft.isConnecting = false;
127
+ draft.selectedWallet = null;
128
+ }));
129
+ return [3 /*break*/, 11];
130
+ case 11: return [2 /*return*/];
131
+ }
132
+ });
133
+ }); } })); });
134
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ export declare enum NetworkId {
2
+ TESTNET = 0,
3
+ MAINNET = 1
4
+ }
5
+ /**
6
+ * A hex-encoded string of the corresponding bytes.
7
+ */
8
+ export type Bytes = string;
9
+ /**
10
+ * A hex-encoded string representing CBOR either inside
11
+ * of the Shelley Multi-asset binary spec or, if not present there,
12
+ * from the CIP-0008 signing spec.
13
+ */
14
+ export type Cbor = string;
15
+ /**
16
+ * Used to specify optional pagination for some API calls.
17
+ Limits results to {limit} each page, and uses a 0-indexing {page}
18
+ to refer to which of those pages of {limit} items each.
19
+ */
20
+ export type Paginate = {
21
+ page: number;
22
+ limit: number;
23
+ };
24
+ /**
25
+ * DataSignature type as described in CIP-0030.
26
+ */
27
+ type CoseSign1CborHex = string;
28
+ type CoseKeyCborHex = string;
29
+ export interface Cip30DataSignature {
30
+ key: CoseKeyCborHex;
31
+ signature: CoseSign1CborHex;
32
+ }
33
+ /**
34
+ * Returns the network id of the currently connected account.
35
+ * 0 is testnet and 1 is mainnet but other networks can possibly be returned by wallets.
36
+ * Those other network ID values are not governed by this document.
37
+ *
38
+ * This result will stay the same unless the connected account has changed.
39
+ *
40
+ * @throws ApiError
41
+ */
42
+ export type GetNetworkId = () => Promise<NetworkId>;
43
+ /**
44
+ * If `amount` is `undefined`, this shall return a list of all UTxOs (unspent transaction outputs)
45
+ * controlled by the wallet.
46
+ *
47
+ * If `amount` is not `undefined`, this request shall be limited to just the UTxOs that are required
48
+ * to reach the combined ADA/multi-asset value target specified in `amount`,
49
+ * and if this cannot be attained, `undefined` shall be returned.
50
+ *
51
+ * The results can be further paginated by `paginate` if it is not `undefined`.
52
+ *
53
+ * @throws ApiError
54
+ * @throws PaginateError
55
+ */
56
+ export type GetUtxos = (amount?: Cbor, paginate?: Paginate) => Promise<Cbor[] | undefined>;
57
+ /**
58
+ * @returns a list of one or more UTxOs (unspent transaction outputs) controlled by the wallet
59
+ * that are required to reach AT LEAST the combined ADA value target specified in amount
60
+ * AND the best suitable to be used as collateral inputs
61
+ * for transactions with plutus script inputs (pure ADA-only UTxOs).
62
+ * @throws ApiError
63
+ */
64
+ export type GetCollateral = (params?: {
65
+ amount?: Cbor;
66
+ }) => Promise<Cbor[] | null>;
67
+ /**
68
+ * Returns the total balance available of the wallet.
69
+ *
70
+ * This is the same as summing the results of `api.getUtxos()`, but it is both useful to dApps
71
+ * and likely already maintained by the implementing wallet in a more efficient manner
72
+ * so it has been included in the API as well.
73
+ *
74
+ * @throws ApiError
75
+ */
76
+ export type GetBalance = () => Promise<Cbor>;
77
+ /**
78
+ * Returns a list of all used (included in some on-chain transaction) addresses controlled by the wallet.
79
+ *
80
+ * The results can be further paginated by `paginate` if it is not `undefined`.
81
+ *
82
+ * @throws ApiError
83
+ * @throws PaginateError
84
+ */
85
+ export type GetUsedAddresses = (paginate?: Paginate) => Promise<Cbor[]>;
86
+ /**
87
+ * Returns a list of unused addresses controlled by the wallet.
88
+ *
89
+ * @throws ApiError
90
+ */
91
+ export type GetUnusedAddresses = () => Promise<Cbor[]>;
92
+ /**
93
+ * Returns an address owned by the wallet that should be used as a change address to return
94
+ * leftover assets during transaction creation back to the connected wallet.
95
+ *
96
+ * This can be used as a generic receive address as well.
97
+ *
98
+ * @throws ApiError
99
+ */
100
+ export type GetChangeAddress = () => Promise<Cbor>;
101
+ /**
102
+ * Returns the reward addresses owned by the wallet. This can return multiple addresses e.g. CIP-0018.
103
+ *
104
+ * @throws ApiError
105
+ */
106
+ export type GetRewardAddresses = () => Promise<Cbor[]>;
107
+ /**
108
+ * Requests that a user sign the unsigned portions of the supplied transaction.
109
+ *
110
+ * The wallet should ask the user for permission, and if given,
111
+ * try to sign the supplied body and return a signed transaction.
112
+ *
113
+ * If `partialSign` is `true`, the wallet only tries to sign what it can.
114
+ *
115
+ * If `partialSign` is `false` and the wallet could not sign the entire transaction,
116
+ * `TxSignError` shall be returned with the `ProofGeneration` code.
117
+ *
118
+ * Likewise if the user declined in either case it shall return the `UserDeclined` code.
119
+ *
120
+ * Only the portions of the witness set that were signed as a result of this call are
121
+ * returned to encourage dApps to verify the contents returned by this endpoint while building the final transaction.
122
+ *
123
+ * @throws ApiError
124
+ * @throws TxSignError
125
+ */
126
+ export type SignTx = (tx: Cbor, partialSign?: boolean) => Promise<Cbor>;
127
+ /**
128
+ * This endpoint utilizes the CIP-0008 signing spec for standardization/safety reasons.
129
+ *
130
+ * It allows the dApp to request the user to sign data conforming to said spec.
131
+ *
132
+ * The user's consent should be requested and the details of `sig_structure` shown to them in an informative way.
133
+ *
134
+ * Please refer to the CIP-0008 spec for details on how to construct the sig structure.
135
+ *
136
+ * @throws ApiError
137
+ * @throws DataSignError
138
+ */
139
+ export type SignData = (addr: string, payload: Bytes) => Promise<Cip30DataSignature>;
140
+ /**
141
+ * As wallets should already have this ability, we allow dApps to request that a transaction be sent through it.
142
+ *
143
+ * If the wallet accepts the transaction and tries to send it, it shall return the transaction id for the dApp to track.
144
+ *
145
+ * The wallet is free to return the `TxSendError` with code `Refused` if they do not wish to send it,
146
+ * or `Failure` if there was an error in sending it (e.g. preliminary checks failed on signatures).
147
+ *
148
+ * @throws ApiError
149
+ * @throws TxSendError
150
+ */
151
+ export type SubmitTx = (tx: Cbor) => Promise<string>;
152
+ export declare enum WalletName {
153
+ NAMI = "nami",
154
+ ETERNL = "eternl",
155
+ FLINT = "flint",
156
+ BEGIN = "begin",
157
+ GERO = "gero",
158
+ TYPHON = "typhoncip30",
159
+ LACE = "lace",
160
+ VESPR = "vespr"
161
+ }
162
+ export interface WalletApi {
163
+ getNetworkId: GetNetworkId;
164
+ getUtxos: GetUtxos;
165
+ getBalance: GetBalance;
166
+ getCollateral: GetCollateral;
167
+ getUsedAddresses: GetUsedAddresses;
168
+ getUnusedAddresses: GetUnusedAddresses;
169
+ getChangeAddress: GetChangeAddress;
170
+ getRewardAddresses: GetRewardAddresses;
171
+ signTx: SignTx;
172
+ signData: SignData;
173
+ submitTx: SubmitTx;
174
+ }
175
+ export type WalletMethod = keyof WalletApi;
176
+ export {};
@@ -0,0 +1,17 @@
1
+ export var NetworkId;
2
+ (function (NetworkId) {
3
+ NetworkId[NetworkId["TESTNET"] = 0] = "TESTNET";
4
+ NetworkId[NetworkId["MAINNET"] = 1] = "MAINNET";
5
+ })(NetworkId || (NetworkId = {}));
6
+ export var WalletName;
7
+ (function (WalletName) {
8
+ WalletName["NAMI"] = "nami";
9
+ WalletName["ETERNL"] = "eternl";
10
+ WalletName["FLINT"] = "flint";
11
+ WalletName["BEGIN"] = "begin";
12
+ WalletName["GERO"] = "gero";
13
+ WalletName["TYPHON"] = "typhoncip30";
14
+ WalletName["LACE"] = "lace";
15
+ WalletName["VESPR"] = "vespr";
16
+ })(WalletName || (WalletName = {}));
17
+ //# sourceMappingURL=cip30.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cip30.js","sourceRoot":"","sources":["../../src/typescript/cip30.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,+CAAW,CAAA;IACX,+CAAW,CAAA;AACb,CAAC,EAHW,SAAS,KAAT,SAAS,QAGpB;AAuKD,MAAM,CAAN,IAAY,UASX;AATD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,+BAAiB,CAAA;IACjB,6BAAe,CAAA;IACf,6BAAe,CAAA;IACf,2BAAa,CAAA;IACb,oCAAsB,CAAA;IACtB,2BAAa,CAAA;IACb,6BAAe,CAAA;AACjB,CAAC,EATW,UAAU,KAAV,UAAU,QASrB"}
@@ -0,0 +1,4 @@
1
+ import { WalletName } from './typescript/cip30';
2
+ import { WalletInfo } from './store';
3
+ export declare function parseBalance(balance: string): number;
4
+ export declare function toWalletInfo(walletName: WalletName): WalletInfo;
package/dist/utils.js ADDED
@@ -0,0 +1,21 @@
1
+ import { walletPrettyNames } from './wallets';
2
+ import { Cbor, CborNegInt, CborUInt } from '@harmoniclabs/cbor';
3
+ export function parseBalance(balance) {
4
+ var cbor = Cbor.parse(balance);
5
+ if (cbor instanceof CborNegInt
6
+ || cbor instanceof CborUInt)
7
+ return Number(cbor.num);
8
+ return 0;
9
+ }
10
+ ;
11
+ export function toWalletInfo(walletName) {
12
+ var _a, _b, _c;
13
+ var ns = (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.cardano) !== null && _a !== void 0 ? _a : {};
14
+ return {
15
+ name: walletName,
16
+ displayName: (_b = walletPrettyNames[walletName]) !== null && _b !== void 0 ? _b : walletName,
17
+ icon: (_c = ns[walletName]) === null || _c === void 0 ? void 0 : _c.icon,
18
+ };
19
+ }
20
+ ;
21
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,UAAU,YAAY,CAAE,OAAe;IAE3C,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAE,OAAO,CAAE,CAAC;IACnC,IACE,IAAI,YAAY,UAAU;WACvB,IAAI,YAAY,QAAQ;QAC3B,OAAO,MAAM,CAAE,IAAI,CAAC,GAAG,CAAE,CAAC;IAC5B,OAAO,CAAC,CAAC;AACX,CAAC;AAAA,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,UAAsB;;IAEjD,IAAM,EAAE,GAAG,MAAC,UAAkB,aAAlB,UAAU,uBAAV,UAAU,CAAU,OAAO,mCAAI,EAAE,CAAC;IAE9C,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,MAAA,iBAAiB,CAAC,UAAU,CAAC,mCAAI,UAAU;QACxD,IAAI,EAAE,MAAA,EAAE,CAAC,UAAU,CAAC,0CAAE,IAAI;KAC3B,CAAC;AACJ,CAAC;AAAA,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { WalletName } from './typescript/cip30';
2
+ export declare const walletPrettyNames: Record<WalletName, string>;
@@ -0,0 +1,11 @@
1
+ export var walletPrettyNames = {
2
+ begin: 'Begin',
3
+ eternl: 'Eternl',
4
+ flint: 'Flint',
5
+ gero: 'Gero',
6
+ lace: 'Lace',
7
+ nami: 'Nami',
8
+ typhoncip30: 'Typhon',
9
+ vespr: 'Vespr',
10
+ };
11
+ //# sourceMappingURL=wallets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallets.js","sourceRoot":"","sources":["../src/wallets.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,IAAM,iBAAiB,GAA+B;IAC3D,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,QAAQ;IACrB,KAAK,EAAE,OAAO;CACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@harmoniclabs/use-cardano-wallet",
3
+ "author": "Harmonic Labs",
4
+ "version": "0.0.1",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "typings": "dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "src",
12
+ "!src/test"
13
+ ],
14
+ "engines": {
15
+ "node": ">=10"
16
+ },
17
+ "scripts": {
18
+ "start": "tsdx watch",
19
+ "build": "tsc --project tsconfig.json",
20
+ "test": "tsdx test --passWithNoTests",
21
+ "lint": "tsdx lint",
22
+ "prepare": "npm run build",
23
+ "size": "size-limit",
24
+ "analyze": "size-limit --why"
25
+ },
26
+ "peerDependencies": {
27
+ "react": ">=16"
28
+ },
29
+ "husky": {
30
+ "hooks": {
31
+ "pre-commit": "tsdx lint"
32
+ }
33
+ },
34
+ "prettier": {
35
+ "printWidth": 80,
36
+ "semi": true,
37
+ "singleQuote": true,
38
+ "trailingComma": "es5"
39
+ },
40
+ "size-limit": [
41
+ {
42
+ "path": "dist/index.js",
43
+ "limit": "10 KB"
44
+ }
45
+ ],
46
+ "devDependencies": {
47
+ "@size-limit/preset-small-lib": "^12.0.0",
48
+ "@types/react": "^18.0.17",
49
+ "@types/react-dom": "^18.0.6",
50
+ "husky": "^8.0.1",
51
+ "react": "^18.2.0",
52
+ "react-dom": "^18.2.0",
53
+ "size-limit": "^8.0.1",
54
+ "tsdx": "^2.0.0",
55
+ "tslib": "^2.4.0",
56
+ "typedoc-plugin-markdown": "^3.13.4",
57
+ "typescript": "^5.9.3"
58
+ },
59
+ "dependencies": {
60
+ "@harmoniclabs/cbor": "^2.0.1",
61
+ "@harmoniclabs/crypto": "^0.3.0",
62
+ "@harmoniclabs/uint8array-utils": "^1.0.4",
63
+ "immer": "^9.0.15",
64
+ "zustand": "^4.0.0"
65
+ }
66
+ }
package/src/index.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import { useStore, WalletInfo as _, State } from './store';
4
+ import { WalletName } from './typescript/cip30';
5
+
6
+ export type UseCardanoWalletOptions = {
7
+ /**
8
+ * Specify if the connector should automatically try to connect
9
+ * to previously connected wallets. Relies on localStorage.
10
+ */
11
+ autoConnect?: boolean;
12
+
13
+ /**
14
+ * Specify a local storage key to store the connected wallet name
15
+ */
16
+ localStorageKey?: string;
17
+
18
+ /**
19
+ * If specified, it will call `api.getUtxos()` using the specified
20
+ * interval (in milliseconds). When `utxos` is referenced from the
21
+ * hook, it will return the prefetched array.
22
+ *
23
+ * This is useful for saving on load times when building transactions.
24
+ */
25
+ // prefetchUtxosInterval?: number | null;
26
+ };
27
+
28
+ const defaultOptions: UseCardanoWalletOptions = {
29
+ autoConnect: true,
30
+ localStorageKey: 'cardano-wallet-name',
31
+ };
32
+
33
+ type ReturnVal = Omit<State, 'connect' | 'getDetectedWallets'> & {
34
+ connect: (walletName: WalletName) => Promise<void>;
35
+ };
36
+
37
+ export const useCardanoWallet = ({
38
+ autoConnect = true,
39
+ localStorageKey = 'cardano-wallet-name',
40
+ }: UseCardanoWalletOptions = defaultOptions): ReturnVal => {
41
+ const {
42
+ connect: connectWithStore,
43
+ getDetectedWallets,
44
+ disconnect: storeDisconnect,
45
+ ...rest
46
+ } = useStore();
47
+
48
+ useEffect(() => {
49
+ getDetectedWallets();
50
+ }, [getDetectedWallets]);
51
+
52
+ useEffect(() => {
53
+ // Check localStorage in case we are in SSR
54
+ if (autoConnect && typeof localStorage !== 'undefined') {
55
+ const connectedWalletName = localStorage.getItem(
56
+ localStorageKey
57
+ ) as WalletName | null;
58
+
59
+ if (!connectedWalletName) {
60
+ return;
61
+ }
62
+
63
+ // Setting timeout to let the wallet be injected and not get undefined
64
+ setTimeout(() => {
65
+ connectWithStore(connectedWalletName, localStorageKey);
66
+ }, 10);
67
+ }
68
+ }, [autoConnect, localStorageKey, connectWithStore]);
69
+
70
+ const connect = async (walletName: WalletName) => {
71
+ await connectWithStore(walletName, localStorageKey);
72
+ };
73
+ const disconnect = () => {
74
+ localStorage.removeItem(localStorageKey);
75
+ storeDisconnect();
76
+ };
77
+
78
+ return { connect, disconnect, ...rest };
79
+ };
80
+
81
+ export * from './typescript/cip30';
82
+
83
+ // export default useCardanoWallet;
@@ -0,0 +1,178 @@
1
+ import produce from 'immer';
2
+ import { create } from 'zustand';
3
+
4
+ import { NetworkId, WalletApi, WalletName } from '../typescript/cip30';
5
+ import { parseBalance, toWalletInfo } from '../utils';
6
+ import { fromHex } from '@harmoniclabs/uint8array-utils';
7
+ import { encodeBech32 } from '@harmoniclabs/crypto';
8
+
9
+ export interface WalletInfo {
10
+ name: WalletName;
11
+ icon?: string | undefined;
12
+ displayName: string;
13
+ }
14
+
15
+ export type State = {
16
+ isConnected: boolean;
17
+ isConnecting: boolean;
18
+ isRefetchingBalance: boolean;
19
+
20
+ detectedWallets: WalletInfo[];
21
+
22
+ address: null | string;
23
+
24
+ rewardAddress: null | string;
25
+
26
+ /**
27
+ * The wallet that was selected to connect.
28
+ */
29
+ selectedWallet: null | WalletInfo;
30
+
31
+ /**
32
+ * The wallet that is currently connected.
33
+ */
34
+ connectedWallet: null | WalletInfo;
35
+
36
+ lovelaceBalance: null | number;
37
+ api: null | WalletApi;
38
+ network: null | NetworkId;
39
+
40
+ connect: (walletName: WalletName, localStorageKey: string) => Promise<void>;
41
+ getDetectedWallets: () => void;
42
+ disconnect: () => void;
43
+ refetchBalance: () => Promise<void>;
44
+ };
45
+
46
+ const defaults = {
47
+ isConnecting: false,
48
+ isConnected: false,
49
+ detectedWallets: [],
50
+ address: null,
51
+ lovelaceBalance: null,
52
+ api: null,
53
+ selectedWallet: null,
54
+ connectedWallet: null,
55
+ network: null,
56
+ isRefetchingBalance: false,
57
+ rewardAddress: null,
58
+ };
59
+
60
+ export const useStore = create<State>()((set, get) => ({
61
+ ...defaults,
62
+ disconnect: () => {
63
+ set((prev: State) => {
64
+ return {
65
+ ...defaults,
66
+ // Keep detected wallets
67
+ detectedWallets: prev.detectedWallets,
68
+ };
69
+ });
70
+ },
71
+
72
+ refetchBalance: async () => {
73
+ const api = get().api;
74
+
75
+ if (api === null) return;
76
+
77
+ set(
78
+ produce((draft: State) => {
79
+ draft.isRefetchingBalance = true;
80
+ })
81
+ );
82
+
83
+ const balance = await api.getBalance();
84
+ const lovelace = parseBalance(balance);
85
+
86
+ set(
87
+ produce((draft: State) => {
88
+ draft.isRefetchingBalance = false;
89
+ draft.lovelaceBalance = lovelace;
90
+ })
91
+ );
92
+ },
93
+
94
+ getDetectedWallets: async () => {
95
+ if (typeof window === 'undefined' || !(window as any).cardano) {
96
+ return;
97
+ }
98
+
99
+ const ns = (window as any).cardano;
100
+
101
+ set(
102
+ produce((draft: State) => {
103
+ draft.detectedWallets = Object.keys(ns)
104
+ .filter(ns => Object.values(WalletName).includes(ns as WalletName))
105
+ .map(n => toWalletInfo(n as WalletName));
106
+ })
107
+ );
108
+ },
109
+
110
+ connect: async (walletName: WalletName, localStorageKey: string) => {
111
+ set(
112
+ produce((draft: State) => {
113
+ draft.isConnecting = true;
114
+ draft.selectedWallet = toWalletInfo(walletName);
115
+ })
116
+ );
117
+
118
+ try {
119
+ // Exit early if the Cardano dApp-Wallet Web Bridge (CIP 30) has not been injected
120
+ // This can happen in a SSR scenario for example
121
+ if (typeof window === 'undefined' || !(window as any).cardano) {
122
+ throw Error('window.cardano is undefined');
123
+ }
124
+
125
+ const api: WalletApi = await (window as any).cardano[walletName].enable();
126
+ let rawAddress = await api.getChangeAddress();
127
+ if (!rawAddress) {
128
+ [rawAddress] = await api.getUsedAddresses();
129
+ }
130
+ if (!rawAddress) {
131
+ [rawAddress] = await api.getUnusedAddresses();
132
+ }
133
+
134
+ let [rewardAddress] = await api.getRewardAddresses();
135
+
136
+ if (rewardAddress) {
137
+ const buf = fromHex( rewardAddress );
138
+ rewardAddress = encodeBech32(
139
+ buf[0] === NetworkId.MAINNET ? 'stake' : 'stake_test',
140
+ buf.slice(1)
141
+ );
142
+ }
143
+
144
+ const addressBytes = fromHex( rawAddress );
145
+ const balance = await api.getBalance();
146
+
147
+ const decodedBalance = parseBalance( balance );
148
+
149
+ const bechAddr = encodeBech32(
150
+ addressBytes[0] === NetworkId.MAINNET ? 'addr' : 'addr_test',
151
+ addressBytes.slice( 1 )
152
+ );
153
+
154
+ localStorage.setItem( localStorageKey, walletName );
155
+
156
+ set(
157
+ produce((draft: State) => {
158
+ draft.isConnecting = false;
159
+ draft.isConnected = true;
160
+ draft.api = api;
161
+ draft.lovelaceBalance = decodedBalance;
162
+ draft.rewardAddress = rewardAddress;
163
+ draft.address = bechAddr;
164
+ draft.network = addressBytes[0] as NetworkId;
165
+ draft.connectedWallet = toWalletInfo(walletName);
166
+ })
167
+ );
168
+ } catch (e) {
169
+ console.error(e);
170
+ set(
171
+ produce((draft: State) => {
172
+ draft.isConnecting = false;
173
+ draft.selectedWallet = null;
174
+ })
175
+ );
176
+ }
177
+ },
178
+ }));
@@ -0,0 +1,206 @@
1
+ export enum NetworkId {
2
+ TESTNET = 0,
3
+ MAINNET = 1,
4
+ }
5
+ /**
6
+ * A hex-encoded string of the corresponding bytes.
7
+ */
8
+ export type Bytes = string;
9
+
10
+ /**
11
+ * A hex-encoded string representing CBOR either inside
12
+ * of the Shelley Multi-asset binary spec or, if not present there,
13
+ * from the CIP-0008 signing spec.
14
+ */
15
+ export type Cbor = string;
16
+
17
+ /**
18
+ * Used to specify optional pagination for some API calls.
19
+ Limits results to {limit} each page, and uses a 0-indexing {page}
20
+ to refer to which of those pages of {limit} items each.
21
+ */
22
+ export type Paginate = { page: number; limit: number };
23
+
24
+ /**
25
+ * DataSignature type as described in CIP-0030.
26
+ */
27
+
28
+ type CoseSign1CborHex = string;
29
+ type CoseKeyCborHex = string;
30
+
31
+ export interface Cip30DataSignature {
32
+ key: CoseKeyCborHex;
33
+ signature: CoseSign1CborHex;
34
+ }
35
+
36
+ /**
37
+ * Returns the network id of the currently connected account.
38
+ * 0 is testnet and 1 is mainnet but other networks can possibly be returned by wallets.
39
+ * Those other network ID values are not governed by this document.
40
+ *
41
+ * This result will stay the same unless the connected account has changed.
42
+ *
43
+ * @throws ApiError
44
+ */
45
+ export type GetNetworkId = () => Promise<NetworkId>;
46
+ /**
47
+ * If `amount` is `undefined`, this shall return a list of all UTxOs (unspent transaction outputs)
48
+ * controlled by the wallet.
49
+ *
50
+ * If `amount` is not `undefined`, this request shall be limited to just the UTxOs that are required
51
+ * to reach the combined ADA/multi-asset value target specified in `amount`,
52
+ * and if this cannot be attained, `undefined` shall be returned.
53
+ *
54
+ * The results can be further paginated by `paginate` if it is not `undefined`.
55
+ *
56
+ * @throws ApiError
57
+ * @throws PaginateError
58
+ */
59
+ export type GetUtxos = (
60
+ amount?: Cbor,
61
+ paginate?: Paginate
62
+ ) => Promise<Cbor[] | undefined>;
63
+
64
+ /**
65
+ * @returns a list of one or more UTxOs (unspent transaction outputs) controlled by the wallet
66
+ * that are required to reach AT LEAST the combined ADA value target specified in amount
67
+ * AND the best suitable to be used as collateral inputs
68
+ * for transactions with plutus script inputs (pure ADA-only UTxOs).
69
+ * @throws ApiError
70
+ */
71
+ export type GetCollateral = (params?: {
72
+ amount?: Cbor;
73
+ }) => Promise<Cbor[] | null>;
74
+
75
+ /**
76
+ * Returns the total balance available of the wallet.
77
+ *
78
+ * This is the same as summing the results of `api.getUtxos()`, but it is both useful to dApps
79
+ * and likely already maintained by the implementing wallet in a more efficient manner
80
+ * so it has been included in the API as well.
81
+ *
82
+ * @throws ApiError
83
+ */
84
+ export type GetBalance = () => Promise<Cbor>;
85
+
86
+ /**
87
+ * Returns a list of all used (included in some on-chain transaction) addresses controlled by the wallet.
88
+ *
89
+ * The results can be further paginated by `paginate` if it is not `undefined`.
90
+ *
91
+ * @throws ApiError
92
+ * @throws PaginateError
93
+ */
94
+ export type GetUsedAddresses = (paginate?: Paginate) => Promise<Cbor[]>;
95
+
96
+ /**
97
+ * Returns a list of unused addresses controlled by the wallet.
98
+ *
99
+ * @throws ApiError
100
+ */
101
+ export type GetUnusedAddresses = () => Promise<Cbor[]>;
102
+
103
+ /**
104
+ * Returns an address owned by the wallet that should be used as a change address to return
105
+ * leftover assets during transaction creation back to the connected wallet.
106
+ *
107
+ * This can be used as a generic receive address as well.
108
+ *
109
+ * @throws ApiError
110
+ */
111
+ export type GetChangeAddress = () => Promise<Cbor>;
112
+
113
+ /**
114
+ * Returns the reward addresses owned by the wallet. This can return multiple addresses e.g. CIP-0018.
115
+ *
116
+ * @throws ApiError
117
+ */
118
+ export type GetRewardAddresses = () => Promise<Cbor[]>;
119
+
120
+ /**
121
+ * Requests that a user sign the unsigned portions of the supplied transaction.
122
+ *
123
+ * The wallet should ask the user for permission, and if given,
124
+ * try to sign the supplied body and return a signed transaction.
125
+ *
126
+ * If `partialSign` is `true`, the wallet only tries to sign what it can.
127
+ *
128
+ * If `partialSign` is `false` and the wallet could not sign the entire transaction,
129
+ * `TxSignError` shall be returned with the `ProofGeneration` code.
130
+ *
131
+ * Likewise if the user declined in either case it shall return the `UserDeclined` code.
132
+ *
133
+ * Only the portions of the witness set that were signed as a result of this call are
134
+ * returned to encourage dApps to verify the contents returned by this endpoint while building the final transaction.
135
+ *
136
+ * @throws ApiError
137
+ * @throws TxSignError
138
+ */
139
+ export type SignTx = (tx: Cbor, partialSign?: boolean) => Promise<Cbor>;
140
+
141
+ /**
142
+ * This endpoint utilizes the CIP-0008 signing spec for standardization/safety reasons.
143
+ *
144
+ * It allows the dApp to request the user to sign data conforming to said spec.
145
+ *
146
+ * The user's consent should be requested and the details of `sig_structure` shown to them in an informative way.
147
+ *
148
+ * Please refer to the CIP-0008 spec for details on how to construct the sig structure.
149
+ *
150
+ * @throws ApiError
151
+ * @throws DataSignError
152
+ */
153
+ export type SignData = (
154
+ addr: string,
155
+ payload: Bytes
156
+ ) => Promise<Cip30DataSignature>;
157
+
158
+ /**
159
+ * As wallets should already have this ability, we allow dApps to request that a transaction be sent through it.
160
+ *
161
+ * If the wallet accepts the transaction and tries to send it, it shall return the transaction id for the dApp to track.
162
+ *
163
+ * The wallet is free to return the `TxSendError` with code `Refused` if they do not wish to send it,
164
+ * or `Failure` if there was an error in sending it (e.g. preliminary checks failed on signatures).
165
+ *
166
+ * @throws ApiError
167
+ * @throws TxSendError
168
+ */
169
+ export type SubmitTx = (tx: Cbor) => Promise<string>;
170
+
171
+ export enum WalletName {
172
+ NAMI = 'nami',
173
+ ETERNL = 'eternl',
174
+ FLINT = 'flint',
175
+ BEGIN = 'begin',
176
+ GERO = 'gero',
177
+ TYPHON = 'typhoncip30',
178
+ LACE = 'lace',
179
+ VESPR = 'vespr',
180
+ }
181
+
182
+ export interface WalletApi {
183
+ getNetworkId: GetNetworkId;
184
+
185
+ getUtxos: GetUtxos;
186
+
187
+ getBalance: GetBalance;
188
+
189
+ getCollateral: GetCollateral;
190
+
191
+ getUsedAddresses: GetUsedAddresses;
192
+
193
+ getUnusedAddresses: GetUnusedAddresses;
194
+
195
+ getChangeAddress: GetChangeAddress;
196
+
197
+ getRewardAddresses: GetRewardAddresses;
198
+
199
+ signTx: SignTx;
200
+
201
+ signData: SignData;
202
+
203
+ submitTx: SubmitTx;
204
+ }
205
+
206
+ export type WalletMethod = keyof WalletApi;
package/src/utils.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { WalletName } from './typescript/cip30';
2
+ import { WalletInfo } from './store';
3
+ import { walletPrettyNames } from './wallets';
4
+ import { Cbor, CborNegInt, CborUInt } from '@harmoniclabs/cbor';
5
+
6
+ export function parseBalance( balance: string): number
7
+ {
8
+ const cbor = Cbor.parse( balance );
9
+ if(
10
+ cbor instanceof CborNegInt
11
+ || cbor instanceof CborUInt
12
+ ) return Number( cbor.num );
13
+ return 0;
14
+ };
15
+
16
+ export function toWalletInfo(walletName: WalletName): WalletInfo
17
+ {
18
+ const ns = (globalThis as any)?.cardano ?? {};
19
+
20
+ return {
21
+ name: walletName,
22
+ displayName: walletPrettyNames[walletName] ?? walletName,
23
+ icon: ns[walletName]?.icon,
24
+ };
25
+ };
package/src/wallets.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { WalletName } from './typescript/cip30';
2
+
3
+ export const walletPrettyNames: Record<WalletName, string> = {
4
+ begin: 'Begin',
5
+ eternl: 'Eternl',
6
+ flint: 'Flint',
7
+ gero: 'Gero',
8
+ lace: 'Lace',
9
+ nami: 'Nami',
10
+ typhoncip30: 'Typhon',
11
+ vespr: 'Vespr',
12
+ };