{"version":3,"file":"objects.mjs","names":["data","objectIds","obj","objects"],"sources":["../../../src/transaction-analyzer/rules/objects.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { bcs } from '@mysten/sui/bcs';\nimport type { ClientWithCoreApi, SuiClientTypes } from '@mysten/sui/client';\nimport { normalizeStructTag, normalizeSuiAddress } from '@mysten/sui/utils';\nimport {\n\tisCoinReservationDigest,\n\tparseAccumulatorFieldCoinType,\n\tparseCoinReservationBalance,\n\txorCoinReservationObjectId,\n} from '../coin-reservation.js';\nimport { createAnalyzer } from '../analyzer.js';\nimport type { TransactionAnalysisIssue } from '../analyzer.js';\n\nimport { data } from './core.js';\n\nexport const Coin = bcs.struct('Coin', {\n\tid: bcs.Address,\n\tbalance: bcs.U64,\n});\n\nexport type AnalyzedObject = SuiClientTypes.Object<{\n\tcontent: true;\n}> & {\n\townerAddress: string | null;\n\tisCoinReservation?: boolean;\n};\n\nexport const objectIds = createAnalyzer({\n\tdependencies: { data },\n\tanalyze:\n\t\t() =>\n\t\t({ data }) => {\n\t\t\tconst issues: TransactionAnalysisIssue[] = [];\n\n\t\t\tconst inputObjectIds: string[] = [];\n\t\t\tfor (const input of data.inputs) {\n\t\t\t\tswitch (input.$kind) {\n\t\t\t\t\tcase 'UnresolvedObject':\n\t\t\t\t\tcase 'UnresolvedPure':\n\t\t\t\t\t\tissues.push({ message: `Unexpected unresolved input: ${JSON.stringify(input)}` });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase 'Pure':\n\t\t\t\t\tcase 'FundsWithdrawal':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase 'Object': {\n\t\t\t\t\t\tlet objectId: string;\n\t\t\t\t\t\tlet digest: string | null | undefined;\n\t\t\t\t\t\tswitch (input.Object.$kind) {\n\t\t\t\t\t\t\tcase 'ImmOrOwnedObject':\n\t\t\t\t\t\t\t\tobjectId = input.Object.ImmOrOwnedObject.objectId;\n\t\t\t\t\t\t\t\tdigest = input.Object.ImmOrOwnedObject.digest;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 'SharedObject':\n\t\t\t\t\t\t\t\tobjectId = input.Object.SharedObject.objectId;\n\t\t\t\t\t\t\t\tdigest = null;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 'Receiving':\n\t\t\t\t\t\t\t\tobjectId = input.Object.Receiving.objectId;\n\t\t\t\t\t\t\t\tdigest = input.Object.Receiving.digest;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tthrow new Error(`Unknown object type: ${JSON.stringify(input)}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (digest && isCoinReservationDigest(digest)) continue;\n\t\t\t\t\t\tinputObjectIds.push(objectId);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tissues.push({ message: `Unknown input type: ${JSON.stringify(input)}` });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (issues.length) return { issues };\n\n\t\t\tconst gasObjectIds =\n\t\t\t\tdata.gasData.payment\n\t\t\t\t\t?.filter((obj) => !isCoinReservationDigest(obj.digest))\n\t\t\t\t\t.map((obj) => obj.objectId) ?? [];\n\n\t\t\treturn {\n\t\t\t\tresult: Array.from(new Set([...inputObjectIds, ...gasObjectIds])),\n\t\t\t};\n\t\t},\n});\n\nfunction makeReservationObject(\n\tref: { objectId: string; digest: string; version?: string | number | null },\n\tcoinType: string,\n\towner: string,\n): AnalyzedObject {\n\tconst balance = parseCoinReservationBalance(ref.digest);\n\tconst content = Coin.serialize({ id: ref.objectId, balance }).toBytes();\n\treturn {\n\t\tobjectId: ref.objectId,\n\t\tversion: String(ref.version ?? '0'),\n\t\tdigest: ref.digest,\n\t\ttype: normalizeStructTag(`0x2::coin::Coin<${coinType}>`),\n\t\towner: { $kind: 'AddressOwner', AddressOwner: owner },\n\t\tcontent,\n\t\tpreviousTransaction: undefined,\n\t\tobjectBcs: undefined,\n\t\tjson: undefined,\n\t\tdisplay: undefined,\n\t\townerAddress: owner,\n\t\tisCoinReservation: true,\n\t};\n}\n\nconst SUI_TYPE = normalizeStructTag('0x2::sui::SUI');\n\ntype FetchRequest =\n\t| { kind: 'regular'; id: string }\n\t| {\n\t\t\tkind: 'reservation';\n\t\t\tmaskedId: string;\n\t\t\tunmaskedId: string;\n\t\t\tdigest: string;\n\t\t\tversion?: string | number | null;\n\t  };\n\nfunction ownerAddressOf(\n\towner: SuiClientTypes.ObjectOwner,\n\tissues: TransactionAnalysisIssue[],\n): string | null {\n\tswitch (owner.$kind) {\n\t\tcase 'AddressOwner':\n\t\t\treturn owner.AddressOwner;\n\t\tcase 'ObjectOwner':\n\t\t\treturn owner.ObjectOwner;\n\t\tcase 'ConsensusAddressOwner':\n\t\t\treturn owner.ConsensusAddressOwner.owner;\n\t\tcase 'Shared':\n\t\tcase 'Immutable':\n\t\t\treturn null;\n\t\tdefault:\n\t\t\tissues.push({ message: `Unknown owner type: ${JSON.stringify(owner)}` });\n\t\t\treturn null;\n\t}\n}\n\nexport const objects = createAnalyzer({\n\tcacheKey: 'objects@1.0.0',\n\tdependencies: { objectIds, data },\n\tanalyze:\n\t\t({ client }: { client: ClientWithCoreApi }) =>\n\t\tasync ({ objectIds, data }) => {\n\t\t\tconst issues: TransactionAnalysisIssue[] = [];\n\t\t\tconst result: AnalyzedObject[] = [];\n\t\t\tconst senderAddress = data.sender ? normalizeSuiAddress(data.sender) : null;\n\n\t\t\t// Input reservation refs need an accumulator-field lookup to resolve\n\t\t\t// their coin type; batch it into the same getObjects call.\n\t\t\tconst requests: FetchRequest[] = objectIds.map((id) => ({ kind: 'regular', id }));\n\t\t\tlet chainIdentifier: string | null = null;\n\t\t\tfor (const input of data.inputs) {\n\t\t\t\tif (input.$kind !== 'Object' || input.Object.$kind !== 'ImmOrOwnedObject') continue;\n\t\t\t\tconst { objectId, digest, version } = input.Object.ImmOrOwnedObject;\n\t\t\t\tif (!digest || !isCoinReservationDigest(digest)) continue;\n\t\t\t\tif (chainIdentifier == null) {\n\t\t\t\t\tchainIdentifier = (await client.core.getChainIdentifier()).chainIdentifier;\n\t\t\t\t}\n\t\t\t\trequests.push({\n\t\t\t\t\tkind: 'reservation',\n\t\t\t\t\tmaskedId: objectId,\n\t\t\t\t\tunmaskedId: xorCoinReservationObjectId(objectId, chainIdentifier),\n\t\t\t\t\tdigest,\n\t\t\t\t\tversion,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst fetchIds = Array.from(\n\t\t\t\tnew Set(requests.map((r) => (r.kind === 'regular' ? r.id : r.unmaskedId))),\n\t\t\t);\n\t\t\tconst { objects: fetched } = await client.core.getObjects({\n\t\t\t\tobjectIds: fetchIds,\n\t\t\t\tinclude: { content: true },\n\t\t\t});\n\t\t\tconst fetchedById = new Map(fetchIds.map((id, i) => [id, fetched[i]]));\n\n\t\t\tfor (const req of requests) {\n\t\t\t\tif (req.kind === 'regular') {\n\t\t\t\t\tconst obj = fetchedById.get(req.id);\n\t\t\t\t\tif (obj instanceof Error) {\n\t\t\t\t\t\tissues.push({ message: `Failed to fetch object: ${obj.message}`, error: obj });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!obj) continue;\n\t\t\t\t\tresult.push({ ...obj, ownerAddress: ownerAddressOf(obj.owner, issues) });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst obj = fetchedById.get(req.unmaskedId);\n\t\t\t\tif (!obj || obj instanceof Error) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Coin reservation object ${req.maskedId} could not be resolved`,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst coinType = parseAccumulatorFieldCoinType(obj.type);\n\t\t\t\tif (!coinType) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Coin reservation object ${req.maskedId} (accumulator id ${req.unmaskedId}) is not a balance accumulator field (type ${obj.type})`,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!senderAddress) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Coin reservation input ${req.maskedId} present but transaction has no sender`,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tresult.push(\n\t\t\t\t\tmakeReservationObject(\n\t\t\t\t\t\t{ objectId: req.maskedId, digest: req.digest, version: req.version },\n\t\t\t\t\t\tcoinType,\n\t\t\t\t\t\tsenderAddress,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gas-payment reservations: always Coin<SUI>, owned by the gas payer.\n\t\t\tconst gasReservationOwner = data.gasData.owner\n\t\t\t\t? normalizeSuiAddress(data.gasData.owner)\n\t\t\t\t: senderAddress;\n\t\t\tfor (const ref of data.gasData.payment ?? []) {\n\t\t\t\tif (!ref.digest || !isCoinReservationDigest(ref.digest)) continue;\n\t\t\t\tif (!gasReservationOwner) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Gas payment includes a coin reservation but the transaction has no gas owner or sender`,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tresult.push(\n\t\t\t\t\tmakeReservationObject(\n\t\t\t\t\t\t{ objectId: ref.objectId, digest: ref.digest, version: ref.version },\n\t\t\t\t\t\tSUI_TYPE,\n\t\t\t\t\t\tgasReservationOwner,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (issues.length) return { issues };\n\t\t\treturn { result };\n\t\t},\n});\n\nexport const ownedObjects = createAnalyzer({\n\tdependencies: { objects },\n\tanalyze:\n\t\t() =>\n\t\t({ objects }) => {\n\t\t\treturn { result: objects.filter((obj) => obj.ownerAddress !== null) };\n\t\t},\n});\n\nexport const objectsById = createAnalyzer({\n\tdependencies: { objects },\n\tanalyze:\n\t\t() =>\n\t\t({ objects }) => ({\n\t\t\tresult: new Map(objects.map((obj) => [obj.objectId, obj])),\n\t\t}),\n});\n"],"mappings":"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"}