Skip to content

Commit ceacac1

Browse files
feat: wallet manager can now handle multiple wallet accounts and types
1 parent 5888159 commit ceacac1

File tree

19 files changed

+482
-201
lines changed

19 files changed

+482
-201
lines changed

packages/e2e/src/factories.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
CommunicationType,
3131
InMemoryKeyAgent,
3232
KeyAgentDependencies,
33+
Witnesser,
3334
util
3435
} from '@cardano-sdk/key-management';
3536
import {
@@ -282,6 +283,7 @@ export type GetWalletProps = {
282283
stores?: storage.WalletStores;
283284
customKeyParams?: KeyAgentFactoryProps;
284285
keyAgent?: AsyncKeyAgent;
286+
witnesser?: Witnesser;
285287
};
286288

287289
/** Delays initializing tx when nearing the epoch boundary. Relies on system clock being accurate. */
@@ -307,7 +309,7 @@ const patchInitializeTxToRespectEpochBoundary = <T extends ObservableWallet>(
307309
* @returns an object containing the wallet and providers passed to it
308310
*/
309311
export const getWallet = async (props: GetWalletProps) => {
310-
const { env, idx, logger, name, polling, stores, customKeyParams, keyAgent } = props;
312+
const { env, idx, logger, name, polling, stores, customKeyParams, keyAgent, witnesser } = props;
311313
const providers = {
312314
addressDiscovery: await addressDiscoveryFactory.create(
313315
env.ADDRESS_DISCOVERY,
@@ -365,7 +367,7 @@ export const getWallet = async (props: GetWalletProps) => {
365367
bip32Account,
366368
logger,
367369
stores,
368-
witnesser: util.createBip32Ed25519Witnesser(asyncKeyAgent)
370+
witnesser: witnesser || util.createBip32Ed25519Witnesser(asyncKeyAgent)
369371
}
370372
);
371373

packages/e2e/test/web-extension/extension/background/walletManager.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import {
33
WalletFactory,
44
WalletManagerActivateProps,
55
WalletManagerWorker,
6+
WalletRepository,
67
exposeApi,
78
walletManagerChannel,
89
walletManagerProperties
910
} from '@cardano-sdk/web-extension';
1011

11-
import { AsyncKeyAgent } from '@cardano-sdk/key-management';
1212
import { storage as WebExtensionStorage, runtime } from 'webextension-polyfill';
13+
import { Witnesser } from '@cardano-sdk/key-management';
1314
import { env, logger } from '../util';
1415
import { from, merge, of } from 'rxjs';
1516
import { getWallet } from '../../../../src';
@@ -18,7 +19,7 @@ import { toEmpty } from '@cardano-sdk/util-rxjs';
1819
import { walletName } from '../const';
1920

2021
export interface WalletFactoryDependencies {
21-
keyAgent: AsyncKeyAgent;
22+
witnesser: Witnesser;
2223
stores: storage.WalletStores;
2324
}
2425

@@ -28,26 +29,31 @@ export interface WalletFactoryDependencies {
2829
* Please check its documentation for examples.
2930
*/
3031
const walletFactory: WalletFactory = {
31-
create: async (props: WalletManagerActivateProps, { keyAgent, stores }: WalletFactoryDependencies) =>
32+
create: async (props: WalletManagerActivateProps, { witnesser, stores }: WalletFactoryDependencies) =>
3233
(
3334
await getWallet({
3435
env,
35-
keyAgent,
3636
logger,
3737
name: props.observableWalletName,
38-
stores
38+
stores,
39+
witnesser
3940
})
4041
).wallet
4142
};
4243

4344
const storesFactory: StoresFactory = {
44-
create: ({ walletId }) => storage.createPouchDbWalletStores(walletId, { logger })
45+
create: ({ name }) => storage.createPouchDbWalletStores(name, { logger })
4546
};
4647

4748
export const wallet$ = (() => {
49+
const walletRepository = new WalletRepository<any>({
50+
logger,
51+
store: new storage.InMemoryCollectionStore()
52+
});
53+
4854
const walletManager = new WalletManagerWorker(
4955
{ walletName },
50-
{ logger, managerStorage: WebExtensionStorage.local, runtime, storesFactory, walletFactory }
56+
{ logger, managerStorage: WebExtensionStorage.local, runtime, storesFactory, walletFactory, walletRepository }
5157
);
5258
exposeApi(
5359
{ api$: of(walletManager), baseChannel: walletManagerChannel(walletName), properties: walletManagerProperties },

packages/e2e/test/web-extension/extension/ui.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
WalletManagerUi,
1313
consumeRemoteApi,
1414
consumeSupplyDistributionTracker,
15-
exposeApi
15+
exposeApi,
16+
getWalletId
1617
} from '@cardano-sdk/web-extension';
1718
import {
1819
adaPriceServiceChannel,
@@ -234,8 +235,14 @@ const createWallet = async (accountIndex: number) => {
234235
)
235236
)({ bip32Ed25519: new SodiumBip32Ed25519(), logger });
236237

238+
const walletId = await getWalletId(keyAgent);
237239
await walletManager.destroy();
238-
await walletManager.activate({ keyAgent, observableWalletName: getObservableWalletName(accountIndex) });
240+
await walletManager.activate({
241+
accountIndex,
242+
chainId: env.KEY_MANAGEMENT_PARAMS.chainId,
243+
observableWalletName: getObservableWalletName(accountIndex),
244+
walletId
245+
});
239246

240247
// Same wallet object will return different names, based on which wallet is active
241248
// Calling this method before any wallet is active, will resolve only once a wallet becomes active

packages/key-management/src/types.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as Crypto from '@cardano-sdk/crypto';
2-
import { Cardano } from '@cardano-sdk/core';
2+
import { Cardano, Serialization } from '@cardano-sdk/core';
33
import { HexBlob, OpaqueString, Shutdown } from '@cardano-sdk/util';
44
import { Logger } from 'ts-log';
55
import type { Runtime } from 'webextension-polyfill';
@@ -222,12 +222,13 @@ export interface Witnesser {
222222
/**
223223
* Generates the witness data for a given transaction.
224224
*
225-
* @param txInternals The transaction body along with its hash for which the witness data is to be generated.
225+
* @param transaction The transaction along with its hash for which the witness data is to be generated.
226+
* @param context The witness sign transaction context
226227
* @param options Optional additional parameters that may influence how the witness data is generated.
227228
* @returns A promise that resolves to the generated witness data for the transaction.
228229
*/
229230
witness(
230-
txInternals: Cardano.TxBodyWithHash,
231+
transaction: Serialization.Transaction,
231232
context: SignTransactionContext,
232233
options?: WitnessOptions
233234
): Promise<Cardano.Witness>;

packages/key-management/src/util/createWitnesser.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
WitnessOptions,
88
Witnesser
99
} from '../types';
10-
import { Cardano } from '@cardano-sdk/core';
10+
import { Cardano, Serialization } from '@cardano-sdk/core';
1111
import { HexBlob } from '@cardano-sdk/util';
1212

1313
/** A witnesser that uses a {@link KeyAgent} to generate witness data for a transaction. */
@@ -19,11 +19,20 @@ export class Bip32Ed25519Witnesser implements Witnesser {
1919
}
2020

2121
async witness(
22-
txInternals: Cardano.TxBodyWithHash,
22+
tx: Serialization.Transaction,
2323
context: SignTransactionContext,
2424
options: WitnessOptions
2525
): Promise<Cardano.Witness> {
26-
return { signatures: await this.#keyAgent.signTransaction(txInternals, context, options) };
26+
return {
27+
signatures: await this.#keyAgent.signTransaction(
28+
{
29+
body: tx.body().toCore(),
30+
hash: tx.getId()
31+
},
32+
context,
33+
options
34+
)
35+
};
2736
}
2837

2938
async signBlob(

packages/key-management/test/util/createWitnesser.test.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AsyncKeyAgent, SignBlobResult, Witnesser, util } from '../../src';
2-
import { Cardano } from '@cardano-sdk/core';
2+
import { Cardano, Serialization } from '@cardano-sdk/core';
33
import { HexBlob } from '@cardano-sdk/util';
44

55
describe('createBip32Ed25519Witnesser', () => {
@@ -24,14 +24,20 @@ describe('createBip32Ed25519Witnesser', () => {
2424
});
2525

2626
it('signTransaction is unchanged', async () => {
27+
const transaction = new Serialization.Transaction(
28+
Serialization.TransactionBody.fromCore({ fee: 20_000n, inputs: [], outputs: [], validityInterval: {} }),
29+
new Serialization.TransactionWitnessSet()
30+
);
31+
2732
const txInternals = {
28-
body: { fee: 20_000n, inputs: [], outputs: [], validityInterval: {} } as Cardano.HydratedTxBody,
29-
hash: Cardano.TransactionId('8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec')
33+
body: transaction.body().toCore(),
34+
hash: Cardano.TransactionId('3643bb5fe745ba0532977f82ecf54699963c97adef2626f7c780225d218e9ba6')
3035
};
36+
3137
const options = { knownAddresses: [], txInKeyPathMap: {} };
3238
const result = {} as Cardano.Signatures;
3339
asyncKeyAgent.signTransaction.mockResolvedValueOnce(result);
34-
await expect(witnesser.witness(txInternals, options)).resolves.toEqual({ signatures: result });
40+
await expect(witnesser.witness(transaction, options)).resolves.toEqual({ signatures: result });
3541
expect(asyncKeyAgent.signTransaction).toBeCalledWith(txInternals, options, void 0);
3642
});
3743
});

packages/tx-construction/src/tx-builder/finalizeTx.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Cardano, TxCBOR } from '@cardano-sdk/core';
1+
import { Cardano, Serialization, TxCBOR } from '@cardano-sdk/core';
22
import { FinalizeTxDependencies, SignedTx, TxContext } from './types';
33
import {
44
SignTransactionContext,
@@ -15,7 +15,14 @@ const getSignatures = async (
1515
signingOptions?: SignTransactionOptions,
1616
extraSigners?: TransactionSigner[]
1717
) => {
18-
const { signatures } = await witnesser.witness(txInternals, signingContext, signingOptions);
18+
const { signatures } = await witnesser.witness(
19+
new Serialization.Transaction(
20+
Serialization.TransactionBody.fromCore(txInternals.body),
21+
new Serialization.TransactionWitnessSet()
22+
),
23+
signingContext,
24+
signingOptions
25+
);
1926

2027
if (extraSigners) {
2128
for (const extraSigner of extraSigners) {

packages/web-extension/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@
5656
"@cardano-sdk/core": "workspace:~",
5757
"@cardano-sdk/crypto": "workspace:~",
5858
"@cardano-sdk/dapp-connector": "workspace:~",
59-
"@cardano-sdk/key-management": "workspace:~",
6059
"@cardano-sdk/hardware-ledger": "workspace:~",
6160
"@cardano-sdk/hardware-trezor": "workspace:~",
61+
"@cardano-sdk/key-management": "workspace:~",
6262
"@cardano-sdk/tx-construction": "workspace:~",
6363
"@cardano-sdk/util": "workspace:~",
6464
"@cardano-sdk/util-rxjs": "workspace:~",

packages/web-extension/src/observableWallet/util.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AnyWallet, WalletRepository } from '../walletManager';
12
import { ObservableWallet } from '@cardano-sdk/wallet';
23
import { OutputBuilder, TxBuilder } from '@cardano-sdk/tx-construction';
34
import { RemoteApiProperties, RemoteApiPropertyType } from '../messaging';
@@ -138,3 +139,12 @@ export const observableWalletProperties: RemoteApiProperties<ObservableWallet> =
138139
unspendable$: RemoteApiPropertyType.HotObservable
139140
}
140141
};
142+
143+
export const walletRepositoryProperties: RemoteApiProperties<WalletRepository<AnyWallet<any>>> = {
144+
addAccount: RemoteApiPropertyType.MethodReturningPromise,
145+
addWallet: RemoteApiPropertyType.MethodReturningPromise,
146+
removeAccount: RemoteApiPropertyType.MethodReturningPromise,
147+
removeWallet: RemoteApiPropertyType.MethodReturningPromise,
148+
updateMetadata: RemoteApiPropertyType.MethodReturningPromise,
149+
wallets$: RemoteApiPropertyType.HotObservable
150+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { MessengerDependencies, consumeRemoteApi } from '../../messaging';
2+
import { signerManagerApiChannel, signerManagerApiProperties } from './util';
3+
4+
export const consumeSignerManagerApi = (dependencies: MessengerDependencies) =>
5+
consumeRemoteApi(
6+
{
7+
baseChannel: signerManagerApiChannel,
8+
properties: signerManagerApiProperties
9+
},
10+
dependencies
11+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { MessengerDependencies, exposeApi } from '../../messaging';
2+
import { SignerManagerSignApi } from './types';
3+
import { of } from 'rxjs';
4+
import { signerManagerApiChannel, signerManagerApiProperties } from './util';
5+
6+
export interface ExposeSignerManagerProps {
7+
signerManager: SignerManagerSignApi<any>;
8+
}
9+
10+
export const exposeSignerManagerApi = (
11+
{ signerManager }: ExposeSignerManagerProps,
12+
dependencies: MessengerDependencies
13+
) =>
14+
exposeApi(
15+
{
16+
api$: of(signerManager),
17+
baseChannel: signerManagerApiChannel,
18+
properties: signerManagerApiProperties
19+
},
20+
dependencies
21+
);
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export * from './types';
22
export * from './SignerManager';
3+
export * from './util';
4+
export * from './exposeSignerManagerApi';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { RemoteApiProperties, RemoteApiPropertyType } from '../../messaging';
2+
import { SignerManagerSignApi } from './types';
3+
4+
export const signerManagerApiChannel = 'signerManager';
5+
6+
export const signerManagerApiProperties: RemoteApiProperties<SignerManagerSignApi<any>> = {
7+
signData: RemoteApiPropertyType.MethodReturningPromise,
8+
signTransaction: RemoteApiPropertyType.MethodReturningPromise
9+
};

packages/web-extension/src/walletManager/util.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ import { AsyncKeyAgent } from '@cardano-sdk/key-management';
33

44
import { Cardano, Serialization } from '@cardano-sdk/core';
55
import { RemoteApiProperties, RemoteApiPropertyType } from '../messaging';
6-
import { WalletManagerApi, WalletManagerProps } from './walletManager.types';
6+
import { WalletManagerApi } from './walletManager.types';
77

8-
export const walletManagerChannel = (walletName: WalletManagerProps['walletName']) => `${walletName}-wallet-manager`;
9-
export const walletChannel = (walletName: WalletManagerProps['walletName']) =>
10-
`${walletManagerChannel(walletName)}-wallet`;
8+
export const walletManagerChannel = (channelName: string) => `${channelName}-wallet-manager`;
9+
export const walletChannel = (channelName: string) => `${walletManagerChannel(channelName)}-wallet`;
10+
export const repositoryChannel = (channelName: string) => `${channelName}-wallet-repository`;
1111

1212
export const walletManagerProperties: RemoteApiProperties<WalletManagerApi> = {
1313
activate: RemoteApiPropertyType.MethodReturningPromise,
14+
activeWallet$: RemoteApiPropertyType.HotObservable,
15+
chainId$: RemoteApiPropertyType.HotObservable,
1416
deactivate: RemoteApiPropertyType.MethodReturningPromise,
15-
destroy: RemoteApiPropertyType.MethodReturningPromise
17+
destroy: RemoteApiPropertyType.MethodReturningPromise,
18+
isActive: RemoteApiPropertyType.MethodReturningPromise,
19+
switchNetwork: RemoteApiPropertyType.MethodReturningPromise
1620
};
1721

1822
/**
@@ -64,3 +68,5 @@ export const getWalletId = async (
6468

6569
return getKeyAgentWalletId(walletIdParam);
6670
};
71+
72+
// Add create ID and parse ID {account index and the key)

0 commit comments

Comments
 (0)