Skip to content

Commit dbc0a37

Browse files
committed
fix(wallet): accept tagged 'amount' in getCollateral
1 parent 5a53165 commit dbc0a37

File tree

2 files changed

+49
-43
lines changed

2 files changed

+49
-43
lines changed

packages/wallet/src/cip30.ts

+45-43
Original file line numberDiff line numberDiff line change
@@ -199,53 +199,55 @@ export const createWalletApi = (
199199
getCollateral: async ({ amount = cslToCbor(MAX_COLLATERAL_AMOUNT) }: { amount?: Cbor } = {}): Promise<
200200
Cbor[] | null
201201
// eslint-disable-next-line sonarjs/cognitive-complexity
202-
> => {
203-
const scope = new ManagedFreeableScope();
204-
logger.debug('getting collateral');
205-
const wallet = await firstValueFrom(wallet$);
206-
let unspendables = (await firstValueFrom(wallet.utxo.unspendable$)).sort(compareUtxos);
202+
> =>
203+
usingAutoFree(async (scope) => {
204+
logger.debug('getting collateral');
205+
const wallet = await firstValueFrom(wallet$);
206+
let unspendables = (await firstValueFrom(wallet.utxo.unspendable$)).sort(compareUtxos);
207207

208-
// No available unspendable UTXO
209-
if (unspendables.length === 0) return null;
208+
// No available unspendable UTXO
209+
if (unspendables.length === 0) return null;
210210

211-
if (unspendables.some((utxo) => utxo[1].value.assets && utxo[1].value.assets.size > 0)) {
212-
scope.dispose();
213-
throw new ApiError(APIErrorCode.Refused, 'unspendable UTxOs must not contain assets when used as collateral');
214-
}
215-
if (amount) {
216-
try {
217-
const filterAmount = scope.manage(CML.BigNum.from_bytes(Buffer.from(amount, 'hex')));
218-
if (filterAmount.compare(MAX_COLLATERAL_AMOUNT) > 0) {
219-
scope.dispose();
220-
throw new ApiError(APIErrorCode.InvalidRequest, 'requested amount is too big');
221-
}
222-
223-
const utxos = [];
224-
let totalCoins = scope.manage(CML.BigNum.from_str('0'));
225-
for (const utxo of unspendables) {
226-
const coin = scope.manage(CML.BigNum.from_str(utxo[1].value.coins.toString()));
227-
totalCoins = totalCoins.checked_add(coin);
228-
utxos.push(utxo);
229-
if (totalCoins.compare(filterAmount) !== -1) break;
230-
}
231-
if (totalCoins.compare(filterAmount) === -1) {
232-
scope.dispose();
233-
throw new ApiError(APIErrorCode.Refused, 'not enough coins in configured collateral UTxOs');
234-
}
235-
unspendables = utxos;
236-
} catch (error) {
237-
logger.error(error);
238-
scope.dispose();
239-
if (error instanceof ApiError) {
240-
throw error;
211+
if (unspendables.some((utxo) => utxo[1].value.assets && utxo[1].value.assets.size > 0)) {
212+
throw new ApiError(APIErrorCode.Refused, 'unspendable UTxOs must not contain assets when used as collateral');
213+
}
214+
if (amount) {
215+
try {
216+
const filterAmount = (() => {
217+
try {
218+
return scope.manage(CML.BigNum.from_bytes(Buffer.from(amount, 'hex')));
219+
} catch {
220+
return scope.manage(
221+
CML.BigNum.from_str(scope.manage(CML.BigInt.from_bytes(Buffer.from(amount, 'hex'))).to_str())
222+
);
223+
}
224+
})();
225+
if (filterAmount.compare(MAX_COLLATERAL_AMOUNT) > 0) {
226+
throw new ApiError(APIErrorCode.InvalidRequest, 'requested amount is too big');
227+
}
228+
229+
const utxos = [];
230+
let totalCoins = scope.manage(CML.BigNum.from_str('0'));
231+
for (const utxo of unspendables) {
232+
const coin = scope.manage(CML.BigNum.from_str(utxo[1].value.coins.toString()));
233+
totalCoins = totalCoins.checked_add(coin);
234+
utxos.push(utxo);
235+
if (totalCoins.compare(filterAmount) !== -1) break;
236+
}
237+
if (totalCoins.compare(filterAmount) === -1) {
238+
throw new ApiError(APIErrorCode.Refused, 'not enough coins in configured collateral UTxOs');
239+
}
240+
unspendables = utxos;
241+
} catch (error) {
242+
logger.error(error);
243+
if (error instanceof ApiError) {
244+
throw error;
245+
}
246+
throw new ApiError(APIErrorCode.InternalError, formatUnknownError(error));
241247
}
242-
throw new ApiError(APIErrorCode.InternalError, formatUnknownError(error));
243248
}
244-
}
245-
const cbor = coreToCml.utxo(scope, unspendables).map(cslToCbor);
246-
scope.dispose();
247-
return cbor;
248-
},
249+
return coreToCml.utxo(scope, unspendables).map(cslToCbor);
250+
}),
249251
getNetworkId: async (): Promise<Cardano.NetworkId> => {
250252
logger.debug('getting networkId');
251253
const wallet = await firstValueFrom(wallet$);

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

+4
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,10 @@ describe('cip30', () => {
279279
);
280280
});
281281

282+
test('accepts amount as tagged integer', async () => {
283+
await expect(api.getCollateral({ amount: 'c2434c4b40' })).resolves.not.toThrow();
284+
});
285+
282286
test('returns multiple UTxOs when more than 1 utxo needed to satisfy amount', async () => {
283287
// 1a003d0900 Represents a CML.BigNum object of 4 ADA
284288
const utxos = await api2.getCollateral({ amount: '1a003d0900' });

0 commit comments

Comments
 (0)