{"version":3,"file":"analyzer.mjs","names":["operationType","getCoinValuesForFlows"],"sources":["../../src/auto-approvals/analyzer.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { normalizeSuiAddress } from '@mysten/sui/utils';\nimport type { Analyzer, AnalyzerResult } from '../transaction-analyzer/analyzer.js';\nimport { createAnalyzer } from '../transaction-analyzer/index.js';\nimport { analyzers } from '../transaction-analyzer/index.js';\nimport type { CoinFlow } from '../transaction-analyzer/rules/balance-flows.js';\nimport type {\n\tCoinValueAnalysis,\n\tCoinValueAnalyzerOptions,\n} from '../transaction-analyzer/rules/coin-value.js';\nimport { getCoinValues as getCoinValuesForFlows } from '../transaction-analyzer/rules/coin-value.js';\nimport { extractOperationType, OPERATION_INTENT } from './intent.js';\n\nexport interface AutoApprovalAnalyzerOptions {\n\tsenderAddresses?: string[];\n}\n\ninterface AutoApprovalAnalyzeOptions {\n\toperationType?: string;\n\tgetCoinPrices?: CoinValueAnalyzerOptions['getCoinPrices'];\n\tautoApproval?: AutoApprovalAnalyzerOptions;\n}\n\nconst operationType = createAnalyzer({\n\tdependencies: {\n\t\tbytes: analyzers.bytes,\n\t},\n\tanalyze: (options: Pick<AutoApprovalAnalyzeOptions, 'operationType'>, tx) => {\n\t\tlet operationType = options.operationType ?? null;\n\n\t\tif (!operationType) {\n\t\t\ttx.addIntentResolver(\n\t\t\t\tOPERATION_INTENT,\n\t\t\t\textractOperationType((type) => {\n\t\t\t\t\toperationType = type;\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn async () => {\n\t\t\treturn {\n\t\t\t\tresult: operationType,\n\t\t\t};\n\t\t};\n\t},\n});\n\nexport const autoApprovalAnalyzer = createAnalyzer({\n\tdependencies: {\n\t\toperationType,\n\t\tbytes: analyzers.bytes,\n\t\tdata: analyzers.data,\n\t\tbalanceFlows: analyzers.balanceFlows,\n\t\taccessLevel: analyzers.accessLevel,\n\t\townedObjects: analyzers.ownedObjects,\n\t\tdigest: analyzers.digest,\n\t},\n\tanalyze:\n\t\t(options: AutoApprovalAnalyzeOptions) =>\n\t\tasync ({ bytes, data, balanceFlows, accessLevel, ownedObjects, digest, operationType }) => {\n\t\t\tconst senderAddresses = uniqueNormalizedAddresses([\n\t\t\t\t...(data.sender ? [data.sender] : []),\n\t\t\t\t...(options.autoApproval?.senderAddresses ?? []),\n\t\t\t]);\n\t\t\tconst senderBalanceFlows = aggregateBalanceFlowsForAddresses(\n\t\t\t\tbalanceFlows.byAddress,\n\t\t\t\tsenderAddresses,\n\t\t\t);\n\n\t\t\tconst coinValues = options.getCoinPrices\n\t\t\t\t? await getCoinValuesForFlows(senderBalanceFlows, options.getCoinPrices)\n\t\t\t\t: getCoinValuesUnavailable(senderBalanceFlows);\n\n\t\t\treturn {\n\t\t\t\tresult: {\n\t\t\t\t\toperationType,\n\t\t\t\t\tsenderAddresses,\n\t\t\t\t\tbytes,\n\t\t\t\t\tbalanceFlows,\n\t\t\t\t\tsenderBalanceFlows,\n\t\t\t\t\taccessLevel,\n\t\t\t\t\townedObjects,\n\t\t\t\t\tdigest,\n\t\t\t\t\tcoinValues,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n});\n\nexport type AutoApprovalAnalysis =\n\ttypeof autoApprovalAnalyzer extends Analyzer<infer R, any, any> ? R : never;\nexport type AutoApprovalResult = AnalyzerResult<AutoApprovalAnalysis>;\n\nfunction uniqueNormalizedAddresses(addresses: string[]): string[] {\n\treturn [...new Set(addresses.map((address) => normalizeSuiAddress(address)))];\n}\n\nfunction aggregateBalanceFlowsForAddresses(\n\tbyAddress: Record<string, CoinFlow[]>,\n\taddresses: string[],\n): CoinFlow[] {\n\tconst senderAddresses = new Set(addresses);\n\tconst flowsByCoinType = new Map<string, bigint>();\n\n\tfor (const [address, flows] of Object.entries(byAddress)) {\n\t\tif (!senderAddresses.has(normalizeSuiAddress(address))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const flow of flows) {\n\t\t\tflowsByCoinType.set(flow.coinType, (flowsByCoinType.get(flow.coinType) ?? 0n) + flow.amount);\n\t\t}\n\t}\n\n\treturn Array.from(flowsByCoinType, ([coinType, amount]) => ({ coinType, amount })).filter(\n\t\t(flow) => flow.amount !== 0n,\n\t);\n}\n\nfunction getCoinValuesUnavailable(balanceFlows: CoinFlow[]): CoinValueAnalysis {\n\tconst outflows = balanceFlows\n\t\t.filter((flow) => flow.amount < 0n)\n\t\t.map((flow) => ({ coinType: flow.coinType, amount: -flow.amount }));\n\n\tif (outflows.length === 0) {\n\t\treturn { total: 0, coinTypesWithoutPrice: [], coinTypes: [] };\n\t}\n\n\treturn {\n\t\ttotal: 0,\n\t\tcoinTypesWithoutPrice: [...new Set(outflows.map((flow) => flow.coinType))],\n\t\tcoinTypes: [],\n\t};\n}\n"],"mappings":";;;;;;;AAyBA,MAAM,gBAAgB,eAAe;CACpC,cAAc,EACb,OAAO,UAAU,OACjB;CACD,UAAU,SAA4D,OAAO;EAC5E,IAAIA,kBAAgB,QAAQ,iBAAiB;AAE7C,MAAI,CAACA,gBACJ,IAAG,kBACF,kBACA,sBAAsB,SAAS;AAC9B,qBAAgB;IACf,CACF;AAGF,SAAO,YAAY;AAClB,UAAO,EACN,QAAQA,iBACR;;;CAGH,CAAC;AAEF,MAAa,uBAAuB,eAAe;CAClD,cAAc;EACb;EACA,OAAO,UAAU;EACjB,MAAM,UAAU;EAChB,cAAc,UAAU;EACxB,aAAa,UAAU;EACvB,cAAc,UAAU;EACxB,QAAQ,UAAU;EAClB;CACD,UACE,YACD,OAAO,EAAE,OAAO,MAAM,cAAc,aAAa,cAAc,QAAQ,qCAAoB;EAC1F,MAAM,kBAAkB,0BAA0B,CACjD,GAAI,KAAK,SAAS,CAAC,KAAK,OAAO,GAAG,EAAE,EACpC,GAAI,QAAQ,cAAc,mBAAmB,EAAE,CAC/C,CAAC;EACF,MAAM,qBAAqB,kCAC1B,aAAa,WACb,gBACA;AAMD,SAAO,EACN,QAAQ;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,YAdiB,QAAQ,gBACxB,MAAMC,cAAsB,oBAAoB,QAAQ,cAAc,GACtE,yBAAyB,mBAAmB;GAa7C,EACD;;CAEH,CAAC;AAMF,SAAS,0BAA0B,WAA+B;AACjE,QAAO,CAAC,GAAG,IAAI,IAAI,UAAU,KAAK,YAAY,oBAAoB,QAAQ,CAAC,CAAC,CAAC;;AAG9E,SAAS,kCACR,WACA,WACa;CACb,MAAM,kBAAkB,IAAI,IAAI,UAAU;CAC1C,MAAM,kCAAkB,IAAI,KAAqB;AAEjD,MAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,UAAU,EAAE;AACzD,MAAI,CAAC,gBAAgB,IAAI,oBAAoB,QAAQ,CAAC,CACrD;AAGD,OAAK,MAAM,QAAQ,MAClB,iBAAgB,IAAI,KAAK,WAAW,gBAAgB,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,OAAO;;AAI9F,QAAO,MAAM,KAAK,kBAAkB,CAAC,UAAU,aAAa;EAAE;EAAU;EAAQ,EAAE,CAAC,QACjF,SAAS,KAAK,WAAW,GAC1B;;AAGF,SAAS,yBAAyB,cAA6C;CAC9E,MAAM,WAAW,aACf,QAAQ,SAAS,KAAK,SAAS,GAAG,CAClC,KAAK,UAAU;EAAE,UAAU,KAAK;EAAU,QAAQ,CAAC,KAAK;EAAQ,EAAE;AAEpE,KAAI,SAAS,WAAW,EACvB,QAAO;EAAE,OAAO;EAAG,uBAAuB,EAAE;EAAE,WAAW,EAAE;EAAE;AAG9D,QAAO;EACN,OAAO;EACP,uBAAuB,CAAC,GAAG,IAAI,IAAI,SAAS,KAAK,SAAS,KAAK,SAAS,CAAC,CAAC;EAC1E,WAAW,EAAE;EACb"}