Skip to content

Commit 9082b21

Browse files
authored
Merge pull request #1595 from input-output-hk/feat/LW-12322-remove-auto-collateral-logic
feat(wallet): remove auto-collateral management logic [LW-12322]
2 parents 4451ba9 + 970153f commit 9082b21

File tree

2 files changed

+7
-234
lines changed

2 files changed

+7
-234
lines changed

packages/wallet/src/cip30.ts

+3-84
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ export type Cip30WalletDependencies = {
3535
export enum Cip30ConfirmationCallbackType {
3636
SignData = 'sign_data',
3737
SignTx = 'sign_tx',
38-
SubmitTx = 'submit_tx',
39-
GetCollateral = 'get_collateral'
38+
SubmitTx = 'submit_tx'
4039
}
4140

4241
export type SignDataCallbackParams = {
@@ -59,18 +58,6 @@ export type SubmitTxCallbackParams = {
5958
data: Cardano.Tx;
6059
};
6160

62-
// Optional callback
63-
export type GetCollateralCallbackParams = {
64-
sender: MessageSender;
65-
type: Cip30ConfirmationCallbackType.GetCollateral;
66-
data: {
67-
amount: Cardano.Lovelace;
68-
utxos: Cardano.Utxo[];
69-
};
70-
};
71-
72-
type GetCollateralCallback = (args: GetCollateralCallbackParams) => Promise<Cardano.Utxo[]>;
73-
7461
export type SignConfirmationOk = { cancel$: Observable<void> };
7562
export type SignConfirmationResult = SignConfirmationOk | false;
7663

@@ -81,7 +68,6 @@ export type CallbackConfirmation = {
8168
signData: (args: SignDataCallbackParams) => Promise<SignConfirmationResult>;
8269
signTx: (args: SignTxCallbackParams) => Promise<SignConfirmationResult>;
8370
submitTx: (args: SubmitTxCallbackParams) => Promise<boolean>;
84-
getCollateral?: GetCollateralCallback;
8571
};
8672

8773
const firstValueFromTimed = <T>(observable$: Observable<T>, timeoutAfter: Milliseconds) =>
@@ -194,9 +180,6 @@ const selectUtxo = async (wallet: ObservableWallet, filterAmount: Cardano.Value,
194180
? dumbSelection(await firstValueFrom(wallet.utxo.available$), filterAmount)
195181
: await walletSelection(filterAmount, wallet);
196182

197-
/** Returns an array of UTxOs that do not contain assets */
198-
const getUtxosWithoutAssets = (utxos: Cardano.Utxo[]): Cardano.Utxo[] => utxos.filter((utxo) => !utxo[1].value.assets);
199-
200183
const getFilterAsBigNum = (amount: Cbor): bigint => {
201184
const reader = new Serialization.CborReader(HexBlob(amount));
202185

@@ -224,46 +207,6 @@ const getFilterAmount = (amount: Cbor): bigint => {
224207
}
225208
};
226209

227-
/**
228-
* getCollateralCallback
229-
*
230-
* @param sender The sender of the request
231-
* @param amount ADA collateral required in lovelaces
232-
* @param availableUtxos available UTxOs
233-
* @param callback Callback to execute to attempt setting new collateral
234-
* @param logger The logger instance
235-
* @returns Promise<Cbor[]> or null
236-
*/
237-
const getCollateralCallback = async (
238-
sender: MessageSender,
239-
amount: Cardano.Lovelace,
240-
availableUtxos: Cardano.Utxo[],
241-
callback: GetCollateralCallback,
242-
logger: Logger
243-
) => {
244-
if (availableUtxos.length === 0) return null;
245-
const availableUtxosWithoutAssets = getUtxosWithoutAssets(availableUtxos);
246-
try {
247-
// Send the amount and filtered available UTxOs to the callback
248-
// Client can then choose to mark a UTxO set as unspendable
249-
const newCollateral = await callback({
250-
data: {
251-
amount,
252-
utxos: availableUtxosWithoutAssets
253-
},
254-
sender,
255-
type: Cip30ConfirmationCallbackType.GetCollateral
256-
});
257-
return newCollateral.map((core) => Serialization.TransactionUnspentOutput.fromCore(core).toCbor());
258-
} catch (error) {
259-
logger.error(error);
260-
if (error instanceof ApiError) {
261-
throw error;
262-
}
263-
throw new ApiError(APIErrorCode.InternalError, formatUnknownError(error));
264-
}
265-
};
266-
267210
const getSortedUtxos = async (observableUtxos: Observable<Cardano.Utxo[]>): Promise<Cardano.Utxo[]> => {
268211
const utxos = await firstValueFrom(observableUtxos);
269212
return utxos.sort(compareUtxos);
@@ -333,31 +276,19 @@ const baseCip30WalletApi = (
333276
throw new ApiError(APIErrorCode.InternalError, formatUnknownError(error));
334277
}
335278
},
336-
// eslint-disable-next-line max-statements, sonarjs/cognitive-complexity,complexity
337279
getCollateral: async (
338-
{ sender }: SenderContext,
280+
_: SenderContext,
339281
{ amount = new Serialization.Value(MAX_COLLATERAL_AMOUNT).toCbor() }: { amount?: Cbor } = {}
340282
): Promise<
341283
Cbor[] | null
342-
// eslint-disable-next-line sonarjs/cognitive-complexity, max-statements
284+
// eslint-disable-next-line sonarjs/cognitive-complexity
343285
> => {
344286
logger.debug('getting collateral');
345287
const wallet = await firstValueFrom(wallet$);
346288
await waitForWalletStateSettle(wallet);
347289
let unspendables = await getSortedUtxos(wallet.utxo.unspendable$);
348-
const available = await getSortedUtxos(wallet.utxo.available$);
349290
// No available unspendable UTxO
350291
if (unspendables.length === 0) {
351-
if (available.length > 0 && !!confirmationCallback.getCollateral) {
352-
// available UTxOs could be set as collateral based on user preference
353-
return await getCollateralCallback(
354-
sender,
355-
getFilterAmount(amount),
356-
available,
357-
confirmationCallback.getCollateral,
358-
logger
359-
);
360-
}
361292
return null;
362293
}
363294

@@ -377,18 +308,6 @@ const baseCip30WalletApi = (
377308
if (totalCoins >= filterAmount) break;
378309
}
379310
if (totalCoins < filterAmount) {
380-
// if no collateral available by amount in unspendables, return callback if provided to set unspendables and return in the callback
381-
382-
if (available.length > 0 && !!confirmationCallback.getCollateral) {
383-
return await getCollateralCallback(
384-
sender,
385-
filterAmount,
386-
available,
387-
confirmationCallback.getCollateral,
388-
logger
389-
);
390-
}
391-
392311
throw new ApiError(APIErrorCode.Refused, 'not enough coins in configured collateral UTxOs');
393312
}
394313
unspendables = utxos;

packages/wallet/test/integration/cip30mapping.test.ts

+4-150
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { AddressType, Bip32Account, GroupedAddress, KeyRole, util } from '@cardano-sdk/key-management';
1919
import { AssetId, mockProviders as mocks } from '@cardano-sdk/util-dev';
2020
import { BaseWallet, ObservableWallet, cip30, createPersonalWallet } from '../../src';
21-
import { CallbackConfirmation, GetCollateralCallbackParams } from '../../src/cip30';
21+
import { CallbackConfirmation } from '../../src/cip30';
2222
import {
2323
Cardano,
2424
OutsideOfValidityIntervalData,
@@ -45,14 +45,10 @@ const {
4545
mockRewardsProvider,
4646
mockTxSubmitProvider,
4747
utxo: mockUtxo,
48-
utxosWithLowCoins,
49-
utxosWithLowCoinsAndMixedAssets,
50-
sortedUtxosWithLowCoins,
51-
impureUtxos
48+
utxosWithLowCoins
5249
} = mocks;
5350

5451
type TestProviders = Required<Pick<Providers, 'txSubmitProvider' | 'networkInfoProvider'>>;
55-
const mockCollateralCallback = jest.fn().mockResolvedValue([mockUtxo[3]]);
5652
const createMockGenericCallback = <T>(result: T) => jest.fn().mockResolvedValue(result);
5753
const foreignTx = Serialization.TxCBOR(
5854
'84a70081825820dce442e983f3f5cd5b2644bc57f749075390f1fbae9ab55bf454342959c885db00018182583900d161d64eef0eeb59f9124f520f8c8f3b717ed04198d54c8b17e604aea63c153fb3ea8a4ea4f165574ea91173756de0bf30222ca0e95a649a1a0082607b021a0016360509a1581cb77934706fa311b6568d1070c2d23f092324b35ad623aa571a0e3726a14e4d6573685f476966745f43617264200b5820d8175f3b1276a48939a6ccee220a7f81b6422167317ba3ff6325cba1fb6ccbe70d818258208d68748457cd0f1a8596f41fd2125a415315897d2da4a4b94335829cee7198ae001281825820dce442e983f3f5cd5b2644bc57f749075390f1fbae9ab55bf454342959c885db00a2068259016b590168010000333232323232323223223222253330083232533300d3010002132533300b3370e6eb4c034009200113371e0020122940dd718058008b180700099299980499b8748008c028dd50008a5eb7bdb1804dd5980718059baa001323300100132330010013756601e602060206020602060186ea8c03cc030dd50019129998070008a5eb7bdb1804c8c8c8c94ccc03ccdc8a45000021533300f3371e91010000210031005133013337606ea4008dd3000998030030019bab3010003375c601c0046024004602000244a66601a002298103d87a8000132323232533300e337220140042a66601c66e3c0280084cdd2a4000660246e980052f5c02980103d87a80001330060060033756601e0066eb8c034008c044008c03c00452613656375c0026eb80055cd2ab9d5573caae7d5d02ba157449810f4e4d6573685f476966745f43617264004c011e581cb77934706fa311b6568d1070c2d23f092324b35ad623aa571a0e3726000159023c59023901000033323232323232322322232323225333009323232533300c3007300d3754002264646464a666026602c00426464a666024601a60266ea803854ccc048c034c04cdd5191980080080311299980b8008a60103d87a80001323253330163375e603660306ea800804c4cdd2a40006603400497ae0133004004001301b002301900115333012300c00113371e00402029405854ccc048cdc3800a4002266e3c0080405281bad3013002375c60220022c602800264a66601e601260206ea800452f5bded8c026eacc050c044dd500099191980080099198008009bab3016301730173017301700522533301500114bd6f7b630099191919299980b19b91488100002153330163371e9101000021003100513301a337606ea4008dd3000998030030019bab3017003375c602a0046032004602e00244a666028002298103d87a800013232323253330153372200e0042a66602a66e3c01c0084cdd2a4000660326e980052f5c02980103d87a80001330060060033756602c0066eb8c050008c060008c058004dd7180998081baa00337586024002601c6ea800858c040c044008c03c004c02cdd50008a4c26cac64a66601060060022a66601660146ea8010526161533300830020011533300b300a37540082930b0b18041baa003370e90011b8748000dd7000ab9a5573aaae7955cfaba05742ae8930010f4e4d6573685f476966745f43617264004c012bd8799fd8799f58203159a6f2ae24c5bfbed947fe0ecfe936f088c8d265484e6979cacb607d33c811ff05ff0001058284000040821a006acfc01ab2d05e00840100d87a80821a006acfc01ab2d05e00f5f6'
@@ -61,7 +57,6 @@ const foreignTx = Serialization.TxCBOR(
6157
const createWalletAndApiWithStores = async (
6258
unspendableUtxos: Cardano.Utxo[],
6359
providers?: TestProviders,
64-
getCollateralCallback?: (args: GetCollateralCallbackParams) => Promise<Cardano.Utxo[]>,
6560
settle = true,
6661
availableUtxos?: Cardano.Utxo[]
6762
) => {
@@ -79,8 +74,7 @@ const createWalletAndApiWithStores = async (
7974
const confirmationCallback = {
8075
signData: createMockGenericCallback({ cancel$: NEVER }),
8176
signTx: createMockGenericCallback({ cancel$: NEVER }),
82-
submitTx: createMockGenericCallback(true),
83-
...(!!getCollateralCallback && { getCollateral: getCollateralCallback })
77+
submitTx: createMockGenericCallback(true)
8478
};
8579
wallet.governance.getPubDRepKey = jest.fn(wallet.governance.getPubDRepKey);
8680

@@ -123,12 +117,7 @@ describe('cip30', () => {
123117
})
124118
);
125119
// CREATE A WALLET
126-
({ wallet, api, confirmationCallback } = await createWalletAndApiWithStores(
127-
[mockUtxo[2]],
128-
providers,
129-
undefined,
130-
false
131-
));
120+
({ wallet, api, confirmationCallback } = await createWalletAndApiWithStores([mockUtxo[2]], providers, false));
132121
});
133122

134123
afterEach(() => {
@@ -302,26 +291,6 @@ describe('cip30', () => {
302291
let wallet4: BaseWallet;
303292
let api4: WithSenderContext<WalletApi>;
304293

305-
// Wallet 5
306-
let wallet5: BaseWallet;
307-
let api5: WithSenderContext<WalletApi>;
308-
309-
// Wallet 6
310-
let wallet6: BaseWallet;
311-
let api6: WithSenderContext<WalletApi>;
312-
313-
// Wallet 7
314-
let wallet7: BaseWallet;
315-
let api7: WithSenderContext<WalletApi>;
316-
317-
// Wallet 8
318-
let wallet8: BaseWallet;
319-
let api8: WithSenderContext<WalletApi>;
320-
321-
// Wallet 9
322-
let wallet9: BaseWallet;
323-
let api9: WithSenderContext<WalletApi>;
324-
325294
beforeAll(async () => {
326295
// CREATE A WALLET WITH LOW COINS UTxOs
327296
({ wallet: wallet2, api: api2 } = await createWalletAndApiWithStores(utxosWithLowCoins));
@@ -331,51 +300,6 @@ describe('cip30', () => {
331300

332301
// CREATE A WALLET WITH UTxOS WITH ASSETS
333302
({ wallet: wallet4, api: api4 } = await createWalletAndApiWithStores([mockUtxo[1], mockUtxo[2]]));
334-
335-
// CREATE WALLET WITH CALLBACK FOR GET COLLATERAL (UNSPENDABLES DOES NOT FULFILL AMOUNT, AVAILABLE UTxOs WITH MIXED ASSETS)
336-
({ wallet: wallet5, api: api5 } = await createWalletAndApiWithStores(
337-
utxosWithLowCoins,
338-
providers,
339-
mockCollateralCallback,
340-
true,
341-
utxosWithLowCoinsAndMixedAssets
342-
));
343-
344-
// CREATE WALLET WITH CALLBACK FOR GET COLLATERAL (NO UNSPENDABLES, AVAILABLE UTxOs WITH MIXED ASSETS)
345-
({ wallet: wallet6, api: api6 } = await createWalletAndApiWithStores(
346-
[],
347-
providers,
348-
mockCollateralCallback,
349-
true,
350-
utxosWithLowCoinsAndMixedAssets
351-
));
352-
353-
// WALLET WITH CALLBACK FOR GET COLLATERAL (UNSPENDABLES DOES NOT FULFILL AMOUNT, NO AVAILABLE UTxOS)
354-
({ wallet: wallet7, api: api7 } = await createWalletAndApiWithStores(
355-
utxosWithLowCoins,
356-
providers,
357-
mockCollateralCallback,
358-
true,
359-
[]
360-
));
361-
362-
// WALLET WITH CALLBACK FOR GET COLLATERAL (BRAND NEW WALLET, NO UTXOS)
363-
({ wallet: wallet8, api: api8 } = await createWalletAndApiWithStores(
364-
[],
365-
providers,
366-
mockCollateralCallback,
367-
true,
368-
[]
369-
));
370-
371-
// WALLET WITH CALLBACK FOR GET COLLATERAL (ONLY IMPURE UTXOs)
372-
({ wallet: wallet9, api: api9 } = await createWalletAndApiWithStores(
373-
[],
374-
providers,
375-
mockCollateralCallback,
376-
true,
377-
impureUtxos
378-
));
379303
});
380304

381305
afterAll(() => {
@@ -384,81 +308,11 @@ describe('cip30', () => {
384308
wallet4.shutdown();
385309
});
386310

387-
beforeEach(() => {
388-
mockCollateralCallback.mockClear();
389-
});
390-
391311
test('can handle serialization errors', async () => {
392312
// YYYY is invalid hex that will throw at serialization
393313
await expect(api.getCollateral(context, { amount: 'YYYY' })).rejects.toThrowError(ApiError);
394314
});
395315

396-
it('executes collateral callback if provided and unspendable UTxOs do not meet amount required', async () => {
397-
const collateral = await api5.getCollateral(context);
398-
expect(mockCollateralCallback).toHaveBeenCalledWith({
399-
data: {
400-
amount: 5_000_000n,
401-
utxos: sortedUtxosWithLowCoins
402-
},
403-
sender: {
404-
url: 'https://lace.io'
405-
},
406-
type: 'get_collateral'
407-
});
408-
409-
expect(collateral).toEqual([Serialization.TransactionUnspentOutput.fromCore(mockUtxo[3]).toCbor()]);
410-
wallet5.shutdown();
411-
});
412-
413-
it('executes collateral callback if provided and no unspendable UTxOs are available', async () => {
414-
const collateral = await api6.getCollateral(context);
415-
expect(mockCollateralCallback).toHaveBeenCalledWith({
416-
data: {
417-
amount: 5_000_000n,
418-
utxos: sortedUtxosWithLowCoins
419-
},
420-
sender: {
421-
url: 'https://lace.io'
422-
},
423-
type: 'get_collateral'
424-
});
425-
426-
expect(collateral).toEqual([Serialization.TransactionUnspentOutput.fromCore(mockUtxo[3]).toCbor()]);
427-
wallet6.shutdown();
428-
});
429-
430-
it('does not execute collateral callback if provided with no available UTxOs', async () => {
431-
await expect(api7.getCollateral(context)).rejects.toThrow(ApiError);
432-
expect(mockCollateralCallback).not.toHaveBeenCalled();
433-
wallet7.shutdown();
434-
});
435-
436-
it('does not execute collateral callback and returns null if brand new wallet (no UTXOS)', async () => {
437-
await expect(api8.getCollateral(context)).resolves.toBeNull();
438-
expect(mockCollateralCallback).not.toHaveBeenCalled();
439-
wallet8.shutdown();
440-
});
441-
442-
it('does executes collateral callback with empty array if wallet has only impure UTXOS', async () => {
443-
await expect(api9.getCollateral(context)).resolves.not.toBeNull();
444-
expect(mockCollateralCallback).toHaveBeenCalledWith({
445-
data: {
446-
amount: 5_000_000n,
447-
utxos: []
448-
},
449-
sender: {
450-
url: 'https://lace.io'
451-
},
452-
type: 'get_collateral'
453-
});
454-
wallet9.shutdown();
455-
});
456-
457-
it('does not execute collateral callback if not provided', async () => {
458-
await expect(api2.getCollateral(context)).rejects.toThrow(ApiError);
459-
expect(mockCollateralCallback).not.toHaveBeenCalled();
460-
});
461-
462316
test('accepts amount as tagged integer', async () => {
463317
await expect(api.getCollateral(context, { amount: 'c2434c4b40' })).resolves.not.toThrow();
464318
});

0 commit comments

Comments
 (0)