From f41d37c2f9197eff0cc35d835abd6900df1da899 Mon Sep 17 00:00:00 2001 From: Martynas Kazlauskas Date: Tue, 28 Sep 2021 09:33:21 +0300 Subject: [PATCH] feat(cip2): add functions to create selection constraints (wip) --- packages/cip2/src/selectionConstraints.ts | 51 +++++++++++++++++ packages/cip2/src/types.ts | 3 +- .../cip2/test/selectionConstraints.test.ts | 56 +++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 packages/cip2/src/selectionConstraints.ts create mode 100644 packages/cip2/test/selectionConstraints.test.ts diff --git a/packages/cip2/src/selectionConstraints.ts b/packages/cip2/src/selectionConstraints.ts new file mode 100644 index 00000000000..bda83ce02b7 --- /dev/null +++ b/packages/cip2/src/selectionConstraints.ts @@ -0,0 +1,51 @@ +import { CardanoSerializationLib, CSL, ProtocolParametersRequiredByWallet } from '@cardano-sdk/core'; +import { ComputeMinimumCoinQuantity, EstimateTxFee, SelectionSkeleton } from './types'; + +export type BuildTx = (selection: SelectionSkeleton) => Promise; + +export const computeMinimumCost = + ( + csl: CardanoSerializationLib, + { minFeeCoefficient, minFeeConstant }: ProtocolParametersRequiredByWallet, + buildTx: BuildTx + ): EstimateTxFee => + async (selection) => { + const tx = await buildTx(selection); + return BigInt( + csl + .min_fee( + tx, + csl.LinearFee.new( + csl.BigNum.from_str(minFeeCoefficient.toString()), + csl.BigNum.from_str(minFeeConstant.toString()) + ) + ) + .to_str() + ); + }; + +// +export const computeMinimumCoinQuantity = + ( + csl: CardanoSerializationLib, + { coinsPerUtxoWord }: ProtocolParametersRequiredByWallet + ): ComputeMinimumCoinQuantity => + (multiasset) => { + const minUTxOValue = CSL.BigNum.from_str((BigInt(coinsPerUtxoWord) * 29n).toString()); + const value = csl.Value.new(csl.BigNum.from_str('0')); + if (multiasset) { + value.set_multiasset(multiasset); + } + return BigInt(csl.min_ada_required(value, minUTxOValue).to_str()); + }; + +// tokenBundleSizeExceedsLimit: (tokenBundle) => { +// logger.debug('SelectionConstraint: tokenBundleSizeExceedsLimit', tokenBundle); +// // Todo: Replace with real implementation +// return false; +// }, +// computeSelectionLimit: async (selectionSkeleton) => { +// logger.debug('SelectionConstraint: computeSelectionLimit', selectionSkeleton); +// // Todo: Replace with real implementation +// return 5; +// } diff --git a/packages/cip2/src/types.ts b/packages/cip2/src/types.ts index b375cf63721..7a2e87d7dad 100644 --- a/packages/cip2/src/types.ts +++ b/packages/cip2/src/types.ts @@ -22,6 +22,7 @@ export interface SelectionResult { /** * Estimated fee for the transaction. * This value is included in 'change', so the actual change returned by the transaction is change-fee. + * TODO: refactor this to return CSL.BigNum? */ fee: bigint; }; @@ -53,7 +54,7 @@ export type TokenBundleSizeExceedsLimit = (tokenBundle: CSL.MultiAsset) => boole /** * @returns minimum lovelace amount in a UTxO */ -export type ComputeMinimumCoinQuantity = (assetQuantities: CSL.MultiAsset) => bigint; +export type ComputeMinimumCoinQuantity = (assetQuantities?: CSL.MultiAsset) => bigint; /** * @returns an upper bound for the number of ordinary inputs to diff --git a/packages/cip2/test/selectionConstraints.test.ts b/packages/cip2/test/selectionConstraints.test.ts new file mode 100644 index 00000000000..8dcf392c712 --- /dev/null +++ b/packages/cip2/test/selectionConstraints.test.ts @@ -0,0 +1,56 @@ +import { + CardanoSerializationLib, + loadCardanoSerializationLib, + ProtocolParametersRequiredByWallet +} from '@cardano-sdk/core'; +import { PXL_Asset, TSLA_Asset } from './util'; +import { computeMinimumCoinQuantity, computeMinimumCost } from '../src/selectionConstraints'; +import { SelectionSkeleton } from '../src/types'; +import { valueQuantitiesToValue } from '../src/util'; + +describe('selectionConstraints', () => { + let csl: CardanoSerializationLib; + beforeAll(async () => { + csl = await loadCardanoSerializationLib(); + }); + + it('computeMinimumCost', async () => { + const fee = 200_000n; + // Need this to not have to build Tx + const stubCsl = { + min_fee: jest.fn().mockReturnValueOnce(csl.BigNum.from_str(fee.toString())), + LinearFee: csl.LinearFee, + BigNum: csl.BigNum + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any as CardanoSerializationLib; + const buildTx = jest.fn(); + const selectionSkeleton = {} as SelectionSkeleton; + const result = await computeMinimumCost( + stubCsl, + { minFeeCoefficient: 44, minFeeConstant: 155_381 } as ProtocolParametersRequiredByWallet, + buildTx + )(selectionSkeleton); + expect(result).toEqual(fee); + expect(buildTx).toBeCalledTimes(1); + expect(buildTx).toBeCalledWith(selectionSkeleton); + }); + + it('computeMinimumCoinQuantity', () => { + const withAssets = valueQuantitiesToValue( + { + coins: 10_000n, + assets: { + [TSLA_Asset]: 5000n, + [PXL_Asset]: 3000n + } + }, + csl + ).multiasset(); + const protocolParams = { coinsPerUtxoWord: 34_482 } as ProtocolParametersRequiredByWallet; + const minCoinWithAssets = computeMinimumCoinQuantity(csl, protocolParams)(withAssets); + const minCoinWithoutAssets = computeMinimumCoinQuantity(csl, protocolParams)(); + expect(typeof minCoinWithAssets).toBe('bigint'); + expect(typeof minCoinWithoutAssets).toBe('bigint'); + expect(minCoinWithAssets).toBeGreaterThan(minCoinWithoutAssets); + }); +});