diff --git a/packages/e2e/test/wallet_epoch_0/PersonalWallet/metadata.test.ts b/packages/e2e/test/wallet_epoch_0/PersonalWallet/metadata.test.ts index 45d460ab558..7418c673a7e 100644 --- a/packages/e2e/test/wallet_epoch_0/PersonalWallet/metadata.test.ts +++ b/packages/e2e/test/wallet_epoch_0/PersonalWallet/metadata.test.ts @@ -22,7 +22,12 @@ describe('PersonalWallet/metadata', () => { test('can submit tx with metadata and then query it', async () => { const metadata: Cardano.TxMetadata = new Map([[123n, '1234']]); const walletUtil = createWalletUtil(wallet); - const { minimumCoin } = await walletUtil.validateValue({ coins: 0n }); + const { minimumCoin } = await walletUtil.validateOutput({ + address: Cardano.PaymentAddress( + 'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' + ), + value: { coins: 0n } + }); // Make sure the wallet has sufficient funds to run this test await walletReady(wallet, minimumCoin); diff --git a/packages/tx-construction/src/output-validation/outputValidator.ts b/packages/tx-construction/src/output-validation/outputValidator.ts index d9344bcf785..612278a96a1 100644 --- a/packages/tx-construction/src/output-validation/outputValidator.ts +++ b/packages/tx-construction/src/output-validation/outputValidator.ts @@ -12,15 +12,12 @@ import { computeMinimumCoinQuantity, tokenBundleSizeExceedsLimit } from '../inpu export const createOutputValidator = ({ protocolParameters: protocolParametersGetter }: OutputValidatorContext): OutputValidator => { - const validateValue = async ( - value: Cardano.Value, + const validateOutput = async ( + { address, value }: Cardano.TxOut, protocolParameters?: ProtocolParametersRequiredByOutputValidator - ): Promise => { + ) => { const { coinsPerUtxoByte, maxValueSize } = protocolParameters || (await protocolParametersGetter()); - const stubMaxSizeAddress = Cardano.PaymentAddress( - 'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' - ); - const stubTxOut: Cardano.TxOut = { address: stubMaxSizeAddress, value }; + const stubTxOut: Cardano.TxOut = { address, value }; const negativeAssetQty = value.assets ? [...value.assets.values()].some((qty) => qty <= 0) : false; if (negativeAssetQty) { // return early, otherwise 'minimumCoin/maxValueSize' will fail with error: "ParseIntError { kind: InvalidDigit }" @@ -39,18 +36,6 @@ export const createOutputValidator = ({ tokenBundleSizeExceedsLimit: tokenBundleSizeExceedsLimit(maxValueSize)(value.assets) }; }; - const validateValues = async (values: Iterable) => { - const protocolParameters = await protocolParametersGetter(); - const validations = new Map(); - for (const value of values) { - validations.set(value, await validateValue(value, protocolParameters)); - } - return validations; - }; - const validateOutput = async ( - output: Cardano.TxOut, - protocolParameters?: ProtocolParametersRequiredByOutputValidator - ) => validateValue(output.value, protocolParameters); return { validateOutput, @@ -61,8 +46,6 @@ export const createOutputValidator = ({ validations.set(output, await validateOutput(output, protocolParameters)); } return validations; - }, - validateValue, - validateValues + } }; }; diff --git a/packages/tx-construction/src/output-validation/types.ts b/packages/tx-construction/src/output-validation/types.ts index 8d46d1a57c0..785940f3e80 100644 --- a/packages/tx-construction/src/output-validation/types.ts +++ b/packages/tx-construction/src/output-validation/types.ts @@ -17,22 +17,6 @@ export interface OutputValidation { } export interface OutputValidator { - /** - * Assumes that value will be used with an output that has: - * - no datum - * - grouped address (Shelley era+) - * - * @returns Validates that token bundle size is within limits and computes minimum coin quantity - */ - validateValue(output: Cardano.Value): Promise; - /** - * Assumes that values will be used with outputs that have: - * - no datum - * - grouped address (Shelley era+) - * - * @returns For every value, validates that token bundle size is within limits and computes minimum coin quantity - */ - validateValues(outputs: Iterable): Promise>; /** * @returns Validates that token bundle size is within limits and computes minimum coin quantity */ diff --git a/packages/tx-construction/test/output-validation/outputValidator.test.ts b/packages/tx-construction/test/output-validation/outputValidator.test.ts index 18e3d7b4a84..b1c31161902 100644 --- a/packages/tx-construction/test/output-validation/outputValidator.test.ts +++ b/packages/tx-construction/test/output-validation/outputValidator.test.ts @@ -3,6 +3,9 @@ import { Cardano } from '@cardano-sdk/core'; import { OutputValidator, createOutputValidator } from '../../src'; describe('createOutputValidator', () => { + const address = Cardano.PaymentAddress( + 'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' + ); let validator: OutputValidator; beforeAll(() => { @@ -14,67 +17,101 @@ describe('createOutputValidator', () => { }); }); - it('validateValue validates minimum coin quantity', async () => { - expect((await validator.validateValue({ coins: 2_000_000n })).coinMissing).toBe(0n); - expect((await validator.validateValue({ coins: 500_000n })).coinMissing).toBeGreaterThan(0n); - }); + describe('validateOutput', () => { + it('validates minimum coin quantity', async () => { + expect((await validator.validateOutput({ address, value: { coins: 2_000_000n } })).coinMissing).toBe(0n); + expect((await validator.validateOutput({ address, value: { coins: 500_000n } })).coinMissing).toBeGreaterThan(0n); + }); - it('validateValue validates bundle size', async () => { - expect( - ( - await validator.validateValue({ - assets: new Map([ - [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n] - ]), - coins: 2_000_000n - }) - ).tokenBundleSizeExceedsLimit - ).toBe(false); - expect( - ( - await validator.validateValue({ - assets: new Map([ - [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], - [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 2n] - ]), - coins: 2_000_000n - }) - ).tokenBundleSizeExceedsLimit - ).toBe(true); - }); + it('validates bundle size', async () => { + expect( + ( + await validator.validateOutput({ + address, + value: { + assets: new Map([ + [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n] + ]), + coins: 2_000_000n + } + }) + ).tokenBundleSizeExceedsLimit + ).toBe(false); + expect( + ( + await validator.validateOutput({ + address, + value: { + assets: new Map([ + [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], + [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 2n] + ]), + coins: 2_000_000n + } + }) + ).tokenBundleSizeExceedsLimit + ).toBe(true); + }); - it('validateValue validates negative asset quantity', async () => { - expect( - ( - await validator.validateValue({ - assets: new Map([ - [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n] - ]), - coins: 2_000_000n - }) - ).negativeAssetQty - ).toBe(false); - expect( - ( - await validator.validateValue({ - assets: new Map([ - [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], - [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), -2n] - ]), - coins: 2_000_000n - }) - ).negativeAssetQty - ).toBe(true); - expect( - ( - await validator.validateValue({ - assets: new Map([ - [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], - [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 0n] - ]), - coins: 2_000_000n - }) - ).negativeAssetQty - ).toBe(true); + it('validates negative asset quantity', async () => { + expect( + ( + await validator.validateOutput({ + address, + value: { + assets: new Map([ + [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n] + ]), + coins: 2_000_000n + } + }) + ).negativeAssetQty + ).toBe(false); + expect( + ( + await validator.validateOutput({ + address, + value: { + assets: new Map([ + [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], + [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), -2n] + ]), + coins: 2_000_000n + } + }) + ).negativeAssetQty + ).toBe(true); + expect( + ( + await validator.validateOutput({ + address, + value: { + assets: new Map([ + [Cardano.AssetId('b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 1n], + [Cardano.AssetId('c01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'), 0n] + ]), + coins: 2_000_000n + } + }) + ).negativeAssetQty + ).toBe(true); + }); + + it('uses output address size as minimum coin computation parameter', async () => { + const value: Cardano.Value = { coins: 123n }; + const { minimumCoin: byronAddressMinimumCoin } = await validator.validateOutput({ + address: Cardano.PaymentAddress( + 'DdzFFzCqrht4PWfBGtmrQz4x1GkZHYLVGbK7aaBkjWxujxzz3L5GxCgPiTsks5RjUr3yX9KvwKjNJBt7ZzPCmS3fUQrGeRvo9Y1YBQKQ' + ), + value + }); + const { minimumCoin: shelleyAddressMinimumCoin } = await validator.validateOutput({ + address: Cardano.PaymentAddress( + 'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' + ), + value + }); + expect(byronAddressMinimumCoin).toBeGreaterThan(shelleyAddressMinimumCoin); + }); }); });