diff --git a/yarn-project/aztec.js/src/api/abi.ts b/yarn-project/aztec.js/src/api/abi.ts index 7e94b7eb30f..03a5b41d4f2 100644 --- a/yarn-project/aztec.js/src/api/abi.ts +++ b/yarn-project/aztec.js/src/api/abi.ts @@ -1,3 +1,3 @@ export { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; -export { loadContractArtifact } from '@aztec/types/abi'; +export { loadContractArtifact, contractArtifactToBuffer, contractArtifactFromBuffer } from '@aztec/types/abi'; export { NoirCompiledContract } from '@aztec/types/noir'; diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index af425afcc90..bcdac6b8dc9 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -105,6 +105,8 @@ describe('Contract Class', () => { globals: {}, }, fileMap: {}, + storageLayout: {}, + notes: {}, }; beforeEach(() => { diff --git a/yarn-project/aztec.js/src/contract/contract_base.ts b/yarn-project/aztec.js/src/contract/contract_base.ts index 5758973849e..0ec2240a404 100644 --- a/yarn-project/aztec.js/src/contract/contract_base.ts +++ b/yarn-project/aztec.js/src/contract/contract_base.ts @@ -1,5 +1,11 @@ -import { type Fr, computePartialAddress } from '@aztec/circuits.js'; -import { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; +import { computePartialAddress } from '@aztec/circuits.js'; +import { + type ContractArtifact, + type ContractNote, + type FieldLayout, + type FunctionArtifact, + FunctionSelector, +} from '@aztec/foundation/abi'; import { type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { type Wallet } from '../account/index.js'; @@ -16,34 +22,6 @@ export type ContractMethod = ((...args: any[]) => ContractFunctionInteraction) & readonly selector: FunctionSelector; }; -/** - * Type representing a field layout in the storage of a contract. - */ -type FieldLayout = { - /** - * Slot in which the field is stored. - */ - slot: Fr; - /** - * Type being stored at the slot - */ - typ: string; -}; - -/** - * Type representing a note in use in the contract. - */ -type ContractNote = { - /** - * Note identifier - */ - id: Fr; - /** - * Type of the note - */ - typ: string; -}; - /** * Type representing the storage layout of a contract. */ diff --git a/yarn-project/builder/package.json b/yarn-project/builder/package.json index 5f87dd34488..cac014a948c 100644 --- a/yarn-project/builder/package.json +++ b/yarn-project/builder/package.json @@ -58,7 +58,6 @@ "fs-extra": "^11.1.1", "lodash.camelcase": "^4.3.0", "lodash.capitalize": "^4.2.1", - "lodash.uniqby": "^4.7.0", "memfs": "^4.6.0", "pako": "^2.1.0", "semver": "^7.5.4", @@ -71,7 +70,6 @@ "@types/jest": "^29.5.0", "@types/lodash.camelcase": "^4.3.7", "@types/lodash.capitalize": "^4.2.7", - "@types/lodash.uniqby": "^4.7.9", "@types/node": "^18.7.23", "@types/pako": "^2.0.0", "@types/semver": "^7.5.4", diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index b86a32dcbdb..4c2911fba97 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -1,12 +1,7 @@ import { type ABIParameter, - type BasicValue, type ContractArtifact, type FunctionArtifact, - type IntegerValue, - type StructValue, - type TupleValue, - type TypedStructFieldValue, getDefaultInitializer, isAztecAddressStruct, isEthAddressStruct, @@ -14,8 +9,6 @@ import { isWrappedFieldStruct, } from '@aztec/foundation/abi'; -import uniqBy from 'lodash.uniqby'; - /** * Returns the corresponding typescript type for a given Noir type. * @param type - The input Noir type. @@ -192,33 +185,29 @@ function generateAbiStatement(name: string, artifactImportPath: string) { * @param input - The contract artifact. */ function generateStorageLayoutGetter(input: ContractArtifact) { - const storage = input.outputs.globals.storage ? (input.outputs.globals.storage[0] as StructValue) : { fields: [] }; - const storageFields = storage.fields as TypedStructFieldValue[]; - const storageFieldsUnionType = storageFields.map(f => `'${f.name}'`).join(' | '); - const layout = storageFields + const entries = Object.entries(input.storageLayout); + + if (entries.length === 0) { + return ''; + } + + const storageFieldsUnionType = entries.map(([name]) => `'${name}'`).join(' | '); + const layout = entries .map( - ({ - name, - value: { - fields: [slot, typ], - }, - }) => + ([name, { slot, typ }]) => `${name}: { - slot: new Fr(${(slot.value as IntegerValue).value}n), - typ: "${(typ.value as BasicValue<'string', string>).value}", - } - `, + slot: new Fr(${slot.toBigInt()}n), + typ: "${typ}", + }`, ) .join(',\n'); - return storageFields.length > 0 - ? ` - public static get storage(): ContractStorageLayout<${storageFieldsUnionType}> { + + return `public static get storage(): ContractStorageLayout<${storageFieldsUnionType}> { return { ${layout} } as ContractStorageLayout<${storageFieldsUnionType}>; } - ` - : ''; + `; } /** @@ -226,30 +215,28 @@ function generateStorageLayoutGetter(input: ContractArtifact) { * @param input - The contract artifact. */ function generateNotesGetter(input: ContractArtifact) { - const notes = input.outputs.globals.notes - ? uniqBy(input.outputs.globals.notes as TupleValue[], n => (n.fields[1] as BasicValue<'string', string>).value) - : []; - const notesUnionType = notes.map(n => `'${(n.fields[1] as BasicValue<'string', string>).value}'`).join(' | '); + const entries = Object.entries(input.notes); - const noteMetadata = notes + if (entries.length === 0) { + return ''; + } + + const notesUnionType = entries.map(([name]) => `'${name}'`).join(' | '); + const noteMetadata = entries .map( - ({ fields: [id, typ] }) => - `${(typ as BasicValue<'string', string>).value}: { - id: new Fr(${(id as IntegerValue).value}n), - } - `, + ([name, { id }]) => + `${name}: { + id: new Fr(${id.toBigInt()}n), + }`, ) .join(',\n'); - return notes.length > 0 - ? ` - public static get notes(): ContractNotes<${notesUnionType}> { - const notes = this.artifact.outputs.globals.notes ? (this.artifact.outputs.globals.notes as any) : []; + + return `public static get notes(): ContractNotes<${notesUnionType}> { return { ${noteMetadata} } as ContractNotes<${notesUnionType}>; } - ` - : ''; + `; } /** diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 7f0442ffd3e..9cc201d2feb 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -125,6 +125,8 @@ export const randomContractArtifact = (): ContractArtifact => ({ globals: {}, }, fileMap: {}, + storageLayout: {}, + notes: {}, }); export const randomContractInstanceWithAddress = (opts: { contractClassId?: Fr } = {}): ContractInstanceWithAddress => diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index 2953da240c1..2d17740c9c2 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -12,6 +12,8 @@ describe('ArtifactHash', () => { globals: {}, structs: {}, }, + storageLayout: {}, + notes: {}, }; expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot( `"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`, diff --git a/yarn-project/end-to-end/src/e2e_account_init_fees.test.ts b/yarn-project/end-to-end/src/e2e_account_init_fees.test.ts index 75c225f4d52..2f488cca319 100644 --- a/yarn-project/end-to-end/src/e2e_account_init_fees.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_init_fees.test.ts @@ -333,8 +333,8 @@ describe('e2e_fees_account_init', () => { }); async function addTransparentNoteToPxe(owner: AztecAddress, amount: bigint, secretHash: Fr, txHash: TxHash) { - const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. - const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const storageSlot = bananaCoin.artifact.storageLayout['pending_shields'].slot; + const noteTypeId = bananaCoin.artifact.notes['TransparentNote'].id; const note = new Note([new Fr(amount), secretHash]); // this note isn't encrypted but we need to provide a registered public key diff --git a/yarn-project/end-to-end/src/sample-dapp/index.mjs b/yarn-project/end-to-end/src/sample-dapp/index.mjs index 6f421f61f3a..f575fe89569 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.mjs @@ -37,8 +37,8 @@ async function mintPrivateFunds(pxe) { const secretHash = await computeSecretHash(secret); const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait(); - const storageSlot = new Fr(5); - const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const storageSlot = token.artifact.storageLayout['pending_shields'].slot; + const noteTypeId = token.artifact.notes['TransparentNote'].id; const note = new Note([new Fr(mintAmount), secretHash]); const extendedNote = new ExtendedNote( diff --git a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs index 9508ab1631b..49a6156046d 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs @@ -22,8 +22,8 @@ describe('token', () => { const secretHash = await computeSecretHash(secret); const receipt = await token.methods.mint_private(initialBalance, secretHash).send().wait(); - const storageSlot = new Fr(5); - const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const storageSlot = token.artifact.storageLayout['pending_shields'].slot; + const noteTypeId = token.artifact.notes['TransparentNote'].id; const note = new Note([new Fr(initialBalance), secretHash]); const extendedNote = new ExtendedNote( note, diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 370a7698337..9a94d89db75 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -227,11 +227,11 @@ export const browserTestSuite = ( INITIAL_TEST_SIGNING_KEYS, INITIAL_TEST_ACCOUNT_SALTS, Buffer, + contractArtifactFromBuffer, } = window.AztecJs; // We serialize the artifact since buffers (used for bytecode) do not cross well from one realm to another - const TokenContractArtifact = JSON.parse( - Buffer.from(serializedTokenContractArtifact, 'base64').toString('utf-8'), - (key, value) => (key === 'bytecode' && typeof value === 'string' ? Buffer.from(value, 'base64') : value), + const TokenContractArtifact = contractArtifactFromBuffer( + Buffer.from(serializedTokenContractArtifact, 'base64'), ); const pxe = createPXEClient(rpcUrl!); @@ -264,9 +264,9 @@ export const browserTestSuite = ( const secretHash = computeSecretHash(secret); const mintPrivateReceipt = await token.methods.mint_private(initialBalance, secretHash).send().wait(); - const storageSlot = new Fr(5); + const storageSlot = token.artifact.storageLayout['pending_shields'].slot; - const noteTypeId = new Fr(84114971101151129711410111011678111116101n); + const noteTypeId = token.artifact.notes['TransparentNote'].id; const note = new Note([new Fr(initialBalance), secretHash]); const extendedNote = new ExtendedNote( note, diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 9f2d48800f8..3dbe2dc7036 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -1,5 +1,6 @@ import { inflate } from 'pako'; +import { type Fr } from '../fields/fields.js'; import { type FunctionSelector } from './function_selector.js'; /** @@ -267,6 +268,34 @@ export type DebugFileMap = Record< } >; +/** + * Type representing a note in use in the contract. + */ +export type ContractNote = { + /** + * Note identifier + */ + id: Fr; + /** + * Type of the note (e.g., 'TransparentNote') + */ + typ: string; +}; + +/** + * Type representing a field layout in the storage of a contract. + */ +export type FieldLayout = { + /** + * Slot in which the field is stored. + */ + slot: Fr; + /** + * Type being stored at the slot (e.g., 'Map>') + */ + typ: string; +}; + /** * Defines artifact of a contract. */ @@ -292,6 +321,14 @@ export interface ContractArtifact { structs: Record; globals: Record; }; + /** + * Storage layout + */ + storageLayout: Record; + /** + * The notes used in the contract. + */ + notes: Record; /** * The map of file ID to the source code and path of the file. diff --git a/yarn-project/p2p/src/service/discv5_service.test.ts b/yarn-project/p2p/src/service/discv5_service.test.ts index 9228e1b87d0..4ce6a233075 100644 --- a/yarn-project/p2p/src/service/discv5_service.test.ts +++ b/yarn-project/p2p/src/service/discv5_service.test.ts @@ -1,3 +1,4 @@ +import { jest } from '@jest/globals'; import type { PeerId } from '@libp2p/interface'; import { BootstrapNode } from '../bootstrap/bootstrap.js'; @@ -21,6 +22,8 @@ const waitForPeers = (node: DiscV5Service, expectedCount: number): Promise }; describe('Discv5Service', () => { + jest.setTimeout(10_000); + let bootNode: BootstrapNode; let bootNodePeerId: PeerId; let port = 1234; diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index ecff905aea0..b34939ce8ee 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -340,10 +340,13 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const recipientStorageSlot = computeSlotForMapping(new Fr(1n), recipient); + const storageSlot = computeSlotForMapping(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); + const recipientStorageSlot = computeSlotForMapping( + StatefulTestContractArtifact.storageLayout['notes'].slot, + recipient, + ); - const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + const noteTypeId = StatefulTestContractArtifact.notes['ValueNote'].id; const notes = [buildNote(60n, owner, storageSlot, noteTypeId), buildNote(80n, owner, storageSlot, noteTypeId)]; oracle.getNotes.mockResolvedValue(notes); @@ -398,7 +401,7 @@ describe('Private Execution test suite', () => { const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + const noteTypeId = StatefulTestContractArtifact.notes['ValueNote'].id; const notes = [buildNote(balance, owner, storageSlot, noteTypeId)]; oracle.getNotes.mockResolvedValue(notes); @@ -729,7 +732,8 @@ describe('Private Execution test suite', () => { const secret = new Fr(1n); const secretHash = computeSecretHash(secret); const note = new Note([secretHash]); - const storageSlot = new Fr(5); + // @todo @LHerskind (#6001) Need to investigate why this was working with `new Fr(5)` as the `example_set = 2` should have caused a failure. + const storageSlot = TestContractArtifact.storageLayout['example_set'].slot; oracle.getNotes.mockResolvedValue([ { contractAddress, @@ -861,8 +865,11 @@ describe('Private Execution test suite', () => { expect(newNoteHashes).toHaveLength(1); const noteHash = newNoteHashes[0]; - const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + const storageSlot = computeSlotForMapping( + PendingNoteHashesContractArtifact.storageLayout['balances'].slot, + owner, + ); + const noteTypeId = PendingNoteHashesContractArtifact.notes['ValueNote'].id; const innerNoteHash = await acirSimulator.computeInnerNoteHash( contractAddress, @@ -917,8 +924,11 @@ describe('Private Execution test suite', () => { const execInsert = result.nestedExecutions[0]; const execGetThenNullify = result.nestedExecutions[1]; - const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + const storageSlot = computeSlotForMapping( + PendingNoteHashesContractArtifact.storageLayout['balances'].slot, + owner, + ); + const noteTypeId = PendingNoteHashesContractArtifact.notes['ValueNote'].id; expect(execInsert.newNotes).toHaveLength(1); const noteAndSlot = execInsert.newNotes[0]; diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index 62bb0c13b07..ef9fd366291 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -59,8 +59,8 @@ describe('Simulator', () => { describe('computeNoteHashAndNullifier', () => { const artifact = getFunctionArtifact(TokenContractArtifact, 'compute_note_hash_and_nullifier'); const nonce = Fr.random(); - const storageSlot = Fr.random(); - const noteTypeId = new Fr(8411110710111078111116101n); // TODO(#5833): This can be imported from artifact now + const storageSlot = TokenContractArtifact.storageLayout['balances'].slot; + const noteTypeId = TokenContractArtifact.notes['TokenNote'].id; const createNote = (amount = 123n) => new Note([new Fr(amount), owner.toField(), Fr.random()]); diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index 886ebf355fb..8e8fb651afe 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -335,7 +335,7 @@ describe('ACIR public execution simulator', () => { expect(result.newNoteHashes.length).toEqual(1); const expectedNoteHash = computeNoteContentHash([amount, secretHash]); - const storageSlot = new Fr(5); // for pending_shields + const storageSlot = TokenContractArtifact.storageLayout['pending_shields'].slot; const expectedInnerNoteHash = computeInnerNoteHash(storageSlot, expectedNoteHash); expect(result.newNoteHashes[0].value).toEqual(expectedInnerNoteHash); }); diff --git a/yarn-project/types/src/abi/contract_artifact.test.ts b/yarn-project/types/src/abi/contract_artifact.test.ts index de830666e09..1e1bb30e6e7 100644 --- a/yarn-project/types/src/abi/contract_artifact.test.ts +++ b/yarn-project/types/src/abi/contract_artifact.test.ts @@ -4,7 +4,6 @@ import { contractArtifactFromBuffer, contractArtifactToBuffer } from './contract describe('contract_artifact', () => { it('serializes and deserializes an instance', () => { const artifact = getSampleContractArtifact(); - delete artifact.aztecNrVersion; const serialized = contractArtifactToBuffer(artifact); const deserialized = contractArtifactFromBuffer(serialized); expect(deserialized).toEqual(artifact); diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index f2c605a0886..0998899167e 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -2,10 +2,17 @@ import { type ABIParameter, type ABIParameterVisibility, type AbiType, + type BasicValue, type ContractArtifact, + type ContractNote, + type FieldLayout, type FunctionArtifact, FunctionType, + type IntegerValue, + type StructValue, + type TypedStructFieldValue, } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; import { AZTEC_INITIALIZER_ATTRIBUTE, @@ -50,6 +57,9 @@ export function contractArtifactFromBuffer(buffer: Buffer): ContractArtifact { if (key === 'bytecode' && typeof value === 'string') { return Buffer.from(value, 'base64'); } + if (typeof value === 'object' && value !== null && value.type === 'Fr') { + return new Fr(BigInt(value.value)); + } return value; }); } @@ -198,6 +208,59 @@ function hasKernelFunctionInputs(params: ABIParameter[]): boolean { return firstParam?.type.kind === 'struct' && firstParam.type.path.includes('ContextInputs'); } +/** + * Generates a storage layout for the contract artifact. + * @param input - The compiled noir contract to get storage layout for + * @returns A storage layout for the contract. + */ +function getStorageLayout(input: NoirCompiledContract) { + const storage = input.outputs.globals.storage ? (input.outputs.globals.storage[0] as StructValue) : { fields: [] }; + const storageFields = storage.fields as TypedStructFieldValue[]; + + if (!storageFields) { + return {}; + } + + return storageFields.reduce((acc: Record, field) => { + const name = field.name; + const slot = field.value.fields[0].value as IntegerValue; + const typ = field.value.fields[1].value as BasicValue<'string', string>; + acc[name] = { + slot: new Fr(BigInt(slot.value)), + typ: typ.value, + }; + return acc; + }, {}); +} + +/** + * Generates records of the notes with note type ids of the artifact. + * @param input - The compiled noir contract to get note types for + * @return A record of the note types and their ids + */ +function getNoteTypes(input: NoirCompiledContract) { + type t = { + kind: string; + fields: [{ kind: string; sign: boolean; value: string }, { kind: string; value: string }]; + }; + + const notes = input.outputs.globals.notes as t[]; + + if (!notes) { + return {}; + } + + return notes.reduce((acc: Record, note) => { + const name = note.fields[1].value as string; + const id = new Fr(BigInt(note.fields[0].value)); + acc[name] = { + id, + typ: name, + }; + return acc; + }, {}); +} + /** * Given a Nargo output generates an Aztec-compatible contract artifact. * @param compiled - Noir build output. @@ -208,6 +271,8 @@ function generateContractArtifact(contract: NoirCompiledContract, aztecNrVersion name: contract.name, functions: contract.functions.map(f => generateFunctionArtifact(f, contract)), outputs: contract.outputs, + storageLayout: getStorageLayout(contract), + notes: getNoteTypes(contract), fileMap: contract.file_map, aztecNrVersion, }; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 12ef01aca7c..95c28c7b512 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -252,7 +252,6 @@ __metadata: "@types/jest": ^29.5.0 "@types/lodash.camelcase": ^4.3.7 "@types/lodash.capitalize": ^4.2.7 - "@types/lodash.uniqby": ^4.7.9 "@types/node": ^18.7.23 "@types/pako": ^2.0.0 "@types/semver": ^7.5.4 @@ -262,7 +261,6 @@ __metadata: jest: ^29.5.0 lodash.camelcase: ^4.3.0 lodash.capitalize: ^4.2.1 - lodash.uniqby: ^4.7.0 memfs: ^4.6.0 pako: ^2.1.0 semver: ^7.5.4 @@ -3710,15 +3708,6 @@ __metadata: languageName: node linkType: hard -"@types/lodash.uniqby@npm:^4.7.9": - version: 4.7.9 - resolution: "@types/lodash.uniqby@npm:4.7.9" - dependencies: - "@types/lodash": "*" - checksum: 24cc8af36e0d4c52b7294c7ba7d814c89ce2c8118d94350bbed21031fef850fa1a280bfd2b30a47e0b5f7aa6ac649a36a5089aa76bc23787963a5ee6443f631e - languageName: node - linkType: hard - "@types/lodash@npm:*": version: 4.17.0 resolution: "@types/lodash@npm:4.17.0" @@ -9867,13 +9856,6 @@ __metadata: languageName: node linkType: hard -"lodash.uniqby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.uniqby@npm:4.7.0" - checksum: 659264545a95726d1493123345aad8cbf56e17810fa9a0b029852c6d42bc80517696af09d99b23bef1845d10d95e01b8b4a1da578f22aeba7a30d3e0022a4938 - languageName: node - linkType: hard - "lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21"