{"version":3,"file":"manager.mjs","names":["#state","#matchesPolicy","#canAutoApprove","#removePendingDigest","#revertBalanceFlows"],"sources":["../../src/auto-approvals/manager.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { SuiClientTypes } from '@mysten/sui/client';\nimport { parse, safeParse } from 'valibot';\nimport type { AutoApprovalState } from './schemas/state.js';\nimport { AutoApprovalStateSchema } from './schemas/state.js';\nimport type { AutoApprovalSettings } from './schemas/policy.js';\nimport { AutoApprovalPolicySchema, AutoApprovalSettingsSchema } from './schemas/policy.js';\nimport { normalizeSuiAddress, parseStructTag } from '@mysten/sui/utils';\nimport type { AutoApprovalResult } from './analyzer.js';\nimport type { CoinFlow } from '../transaction-analyzer/rules/balance-flows.js';\n\ntype SuccessfulAutoApprovalResult = Extract<AutoApprovalResult, { status: 'success' }>;\n\nexport interface AutoApprovalManagerOptions {\n\tpolicy: string;\n\tstate: string | null;\n}\n\nexport interface AutoApprovalIssue {\n\tmessage: string;\n}\n\nexport interface AutoApprovalCheck {\n\tmatchesPolicy: boolean;\n\tcanAutoApprove: boolean;\n\tpolicyIssues: AutoApprovalIssue[];\n\tsettingsIssues: AutoApprovalIssue[];\n\tanalysisIssues: AutoApprovalIssue[];\n}\n\nexport class AutoApprovalManager {\n\t#state: AutoApprovalState;\n\n\tconstructor(options: AutoApprovalManagerOptions) {\n\t\tlet state: AutoApprovalState | null = null;\n\n\t\tif (options.state) {\n\t\t\tconst parseResult = safeParse(AutoApprovalStateSchema, JSON.parse(options.state));\n\t\t\t// TODO: how do we want to handle failures\n\t\t\tif (parseResult.success) {\n\t\t\t\tconst providedPolicy = parse(AutoApprovalPolicySchema, JSON.parse(options.policy));\n\t\t\t\tconst currentPolicy = parseResult.output.policy;\n\n\t\t\t\tif (JSON.stringify(currentPolicy) === JSON.stringify(providedPolicy)) {\n\t\t\t\t\tstate = parseResult.output;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.#state =\n\t\t\tstate ??\n\t\t\tparse(AutoApprovalStateSchema, {\n\t\t\t\tschemaVersion: '1.0.0',\n\t\t\t\tpolicy: parse(AutoApprovalPolicySchema, JSON.parse(options.policy)),\n\t\t\t\tsettings: null,\n\t\t\t\tpendingDigests: [],\n\t\t\t} satisfies AutoApprovalState);\n\t}\n\n\tcheckTransaction(analysis: AutoApprovalResult): AutoApprovalCheck {\n\t\tconst results: AutoApprovalCheck = {\n\t\t\tmatchesPolicy: false,\n\t\t\tcanAutoApprove: false,\n\t\t\tanalysisIssues:\n\t\t\t\tanalysis.status === 'success'\n\t\t\t\t\t? []\n\t\t\t\t\t: analysis.issues.length\n\t\t\t\t\t\t? [...analysis.issues]\n\t\t\t\t\t\t: [{ message: 'Transaction analysis failed' }],\n\t\t\tpolicyIssues: [],\n\t\t\tsettingsIssues: [],\n\t\t};\n\n\t\tif (results.analysisIssues.length > 0) {\n\t\t\treturn results;\n\t\t}\n\n\t\tconst policyIssues = this.#matchesPolicy(analysis);\n\n\t\tif (policyIssues.length > 0) {\n\t\t\tresults.policyIssues = policyIssues;\n\t\t\treturn results;\n\t\t} else {\n\t\t\tresults.matchesPolicy = true;\n\t\t}\n\n\t\tconst settingsIssues = this.#canAutoApprove(analysis);\n\n\t\tif (settingsIssues.length > 0) {\n\t\t\tresults.settingsIssues = settingsIssues;\n\t\t\treturn results;\n\t\t} else {\n\t\t\tresults.canAutoApprove = true;\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t#matchesPolicy(analysis: AutoApprovalResult): AutoApprovalIssue[] {\n\t\tconst issues: AutoApprovalIssue[] = [];\n\n\t\tif (analysis.status !== 'success') {\n\t\t\tissues.push({ message: 'Transaction analysis failed' });\n\t\t\treturn issues;\n\t\t}\n\n\t\tif (!analysis.result.operationType) {\n\t\t\tissues.push({ message: 'Operation type not found in Transaction' });\n\n\t\t\treturn issues;\n\t\t}\n\n\t\tconst operation = this.#state.policy.operations.find(\n\t\t\t(op) => op.id === analysis.result.operationType,\n\t\t);\n\n\t\tif (!operation) {\n\t\t\tissues.push({ message: 'Operation not found in policy' });\n\t\t\treturn issues;\n\t\t}\n\n\t\tif (!operation.permissions.anyBalance) {\n\t\t\tfor (const outflow of getSenderOutflows(analysis)) {\n\t\t\t\tif (!operation.permissions.balances?.find((b) => b.coinType === outflow.coinType)) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Operation does not have permission to use coin type ${outflow.coinType}`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const obj of analysis.result.ownedObjects) {\n\t\t\tif (isCoinType(obj.type)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst accessLevel = analysis.result.accessLevel[obj.objectId];\n\n\t\t\tif (!accessLevel) {\n\t\t\t\tissues.push({ message: `Access level could not be determined for object ${obj.objectId}` });\n\t\t\t}\n\n\t\t\tconst ownedObjectsPermission = operation.permissions.ownedObjects?.find(\n\t\t\t\t(p) => p.objectType === obj.type,\n\t\t\t);\n\n\t\t\tif (!ownedObjectsPermission) {\n\t\t\t\tissues.push({ message: `No permission found for object ${obj.objectId}` });\n\t\t\t} else if (!compareAccessLevel(ownedObjectsPermission.accessLevel, accessLevel)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: `Insufficient access level for object ${obj.objectId}: required ${ownedObjectsPermission.accessLevel}, got ${accessLevel}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn issues;\n\n\t\tfunction compareAccessLevel(\n\t\t\trequired: 'read' | 'mutate' | 'transfer',\n\t\t\tactual: 'read' | 'mutate' | 'transfer',\n\t\t): boolean {\n\t\t\tif (required === 'read') {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (required === 'mutate') {\n\t\t\t\treturn actual === 'mutate' || actual === 'transfer';\n\t\t\t}\n\t\t\treturn actual === 'transfer';\n\t\t}\n\t}\n\n\t#canAutoApprove(analysis: AutoApprovalResult): AutoApprovalIssue[] {\n\t\tconst issues: AutoApprovalIssue[] = [];\n\n\t\tif (!this.#state.settings) {\n\t\t\tissues.push({ message: 'No auto-approval settings configured' });\n\t\t\treturn issues;\n\t\t}\n\n\t\tif (analysis.status !== 'success') {\n\t\t\tissues.push({ message: 'Transaction analysis failed' });\n\t\t\treturn issues;\n\t\t}\n\n\t\tif (new Date() > new Date(this.#state.settings.expiration)) {\n\t\t\tissues.push({ message: 'Auto-approval settings have expired' });\n\t\t}\n\n\t\tif (\n\t\t\tthis.#state.settings.remainingTransactions !== null &&\n\t\t\tthis.#state.settings.remainingTransactions <= 0\n\t\t) {\n\t\t\tissues.push({ message: 'No remaining auto-approved transactions' });\n\t\t}\n\n\t\tif (\n\t\t\t!analysis.result.operationType ||\n\t\t\t!this.#state.settings.approvedOperations.includes(analysis.result.operationType)\n\t\t) {\n\t\t\tissues.push({ message: 'Operation type not approved for auto-approval' });\n\t\t}\n\n\t\tfor (const outflow of getSenderOutflows(analysis)) {\n\t\t\tif (this.#state.settings.coinBudgets[outflow.coinType] !== undefined) {\n\t\t\t\tconst coinBudget = this.#state.settings.coinBudgets[outflow.coinType];\n\n\t\t\t\tif (coinBudget) {\n\t\t\t\t\tif (BigInt(coinBudget) < outflow.amount) {\n\t\t\t\t\t\tissues.push({\n\t\t\t\t\t\t\tmessage: `Insufficient budget for coin type ${outflow.coinType}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst coinAmount = analysis.result.coinValues.coinTypes.find(\n\t\t\t\t\t(ct) => ct.coinType === outflow.coinType,\n\t\t\t\t);\n\n\t\t\t\tif (!coinAmount) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `No budget configured for coin type ${outflow.coinType}`,\n\t\t\t\t\t});\n\t\t\t\t} else if ((this.#state.settings.sharedBudget ?? 0) < coinAmount.convertedAmount) {\n\t\t\t\t\tissues.push({\n\t\t\t\t\t\tmessage: `Insufficient budget for coin type ${outflow.coinType}`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn issues;\n\t}\n\n\t// TODO: we should ensure that only 1 tx is pending at a time, and pending txs can't increase budgets\n\tcommitTransaction(analysis: AutoApprovalResult): void {\n\t\tassertSuccessfulAnalysis(analysis);\n\n\t\tif (!this.#state.settings) {\n\t\t\tthrow new Error('No auto-approval settings configured');\n\t\t}\n\n\t\tif (this.#state.settings.remainingTransactions !== null && this.#state.settings) {\n\t\t\tthis.#state.settings.remainingTransactions = Math.max(\n\t\t\t\t0,\n\t\t\t\tthis.#state.settings.remainingTransactions - 1,\n\t\t\t);\n\t\t}\n\n\t\tfor (const outflow of getSenderOutflows(analysis)) {\n\t\t\tif (this.#state.settings.coinBudgets[outflow.coinType] !== undefined) {\n\t\t\t\tconst currentBudget = BigInt(this.#state.settings?.coinBudgets[outflow.coinType] ?? '0');\n\t\t\t\tconst newBalance = currentBudget - outflow.amount;\n\t\t\t\tthis.#state.settings.coinBudgets[outflow.coinType] = newBalance.toString();\n\t\t\t} else {\n\t\t\t\tif (this.#state.settings.sharedBudget === null) {\n\t\t\t\t\tthrow new Error('No budget available for coin type ' + outflow.coinType);\n\t\t\t\t}\n\n\t\t\t\tconst coinValue = analysis.result.coinValues.coinTypes.find(\n\t\t\t\t\t(ct) => ct.coinType === outflow.coinType,\n\t\t\t\t);\n\n\t\t\t\tif (!coinValue) {\n\t\t\t\t\tthrow new Error('No value available for coin type ' + outflow.coinType);\n\t\t\t\t}\n\n\t\t\t\tthis.#state.settings.sharedBudget -= coinValue.convertedAmount;\n\t\t\t}\n\t\t}\n\n\t\tthis.#state.pendingDigests.push(analysis.result.digest);\n\t}\n\n\trevertTransaction(analysis: AutoApprovalResult): void {\n\t\tassertSuccessfulAnalysis(analysis);\n\n\t\tif (analysis.result?.digest) {\n\t\t\tthis.#removePendingDigest(analysis.result?.digest);\n\t\t}\n\n\t\tif (this.#state.settings?.remainingTransactions !== null && this.#state.settings) {\n\t\t\tthis.#state.settings.remainingTransactions += 1;\n\t\t}\n\n\t\tthis.#revertBalanceFlows(analysis);\n\t}\n\n\t#revertBalanceFlows(analysis: AutoApprovalResult): void {\n\t\tassertSuccessfulAnalysis(analysis);\n\n\t\tif (!this.#state.settings) {\n\t\t\tthrow new Error('No auto-approval settings configured');\n\t\t}\n\n\t\tfor (const outflow of getSenderOutflows(analysis)) {\n\t\t\tif (this.#state.settings?.coinBudgets[outflow.coinType] !== undefined) {\n\t\t\t\tconst currentBudget = BigInt(this.#state.settings?.coinBudgets[outflow.coinType] ?? '0');\n\t\t\t\tconst newBalance = currentBudget + outflow.amount;\n\t\t\t\tthis.#state.settings.coinBudgets[outflow.coinType] = newBalance.toString();\n\t\t\t} else {\n\t\t\t\tif (this.#state.settings.sharedBudget === null) {\n\t\t\t\t\tthrow new Error('No budget available for coin type ' + outflow.coinType);\n\t\t\t\t}\n\n\t\t\t\tconst coinValue = analysis.result.coinValues.coinTypes.find(\n\t\t\t\t\t(ct) => ct.coinType === outflow.coinType,\n\t\t\t\t);\n\n\t\t\t\tif (!coinValue) {\n\t\t\t\t\tthrow new Error('No value available for coin type ' + outflow.coinType);\n\t\t\t\t}\n\n\t\t\t\tthis.#state.settings.sharedBudget += coinValue.convertedAmount;\n\t\t\t}\n\t\t}\n\t}\n\n\t#removePendingDigest(digest: string): void {\n\t\tconst pendingIndex = this.#state.pendingDigests.indexOf(digest);\n\t\tif (pendingIndex >= 0) {\n\t\t\tthis.#state.pendingDigests.splice(pendingIndex, 1);\n\t\t} else {\n\t\t\tthrow new Error(`Transaction with digest ${digest} not found in pending digests`);\n\t\t}\n\t}\n\n\tapplyTransactionEffects(\n\t\tanalysis: AutoApprovalResult,\n\t\tresult: SuiClientTypes.Transaction<{ balanceChanges: true }>,\n\t): void {\n\t\tassertSuccessfulAnalysis(analysis);\n\n\t\tthis.#removePendingDigest(result.digest);\n\n\t\tif (!this.#state.settings) {\n\t\t\tthrow new Error('No auto-approval settings configured');\n\t\t}\n\n\t\t// Revert provisional balance flows and use real sender balance changes instead.\n\t\tthis.#revertBalanceFlows(analysis);\n\n\t\tconst outflowCoinTypes = new Set(getSenderOutflows(analysis).map((flow) => flow.coinType));\n\n\t\tfor (const change of result.balanceChanges) {\n\t\t\tif (\n\t\t\t\t!analysis.result.senderAddresses.includes(normalizeSuiAddress(change.address)) ||\n\t\t\t\t!outflowCoinTypes.has(change.coinType)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (this.#state.settings.coinBudgets[change.coinType] !== undefined) {\n\t\t\t\tconst currentBudget = BigInt(this.#state.settings?.coinBudgets[change.coinType] ?? '0');\n\t\t\t\tconst newBalance = currentBudget + BigInt(change.amount);\n\t\t\t\tif (this.#state.settings) {\n\t\t\t\t\tthis.#state.settings.coinBudgets[change.coinType] = newBalance.toString();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.#state.settings.sharedBudget === null) {\n\t\t\t\t\tthrow new Error('No budget available for coin type ' + change.coinType);\n\t\t\t\t}\n\n\t\t\t\tconst coinValue = analysis.result.coinValues.coinTypes.find(\n\t\t\t\t\t(ct) => ct.coinType === change.coinType,\n\t\t\t\t);\n\n\t\t\t\tif (!coinValue) {\n\t\t\t\t\tthrow new Error('No value available for coin type ' + change.coinType);\n\t\t\t\t}\n\n\t\t\t\tconst convertedChange =\n\t\t\t\t\t(Number(change.amount) / 10 ** coinValue.decimals) * coinValue.price;\n\n\t\t\t\tthis.#state.settings.sharedBudget += convertedChange;\n\t\t\t}\n\t\t}\n\t}\n\n\treset() {\n\t\tthis.#state = {\n\t\t\tschemaVersion: '1.0.0',\n\t\t\tpolicy: this.#state.policy,\n\t\t\tsettings: null,\n\t\t\tpendingDigests: [],\n\t\t};\n\t}\n\n\texport(): string {\n\t\treturn JSON.stringify(parse(AutoApprovalStateSchema, this.#state));\n\t}\n\n\tgetState(): AutoApprovalState {\n\t\treturn parse(AutoApprovalStateSchema, this.#state);\n\t}\n\n\tgetSettings(): AutoApprovalSettings | null {\n\t\treturn this.#state.settings;\n\t}\n\n\tupdateSettings(settings: AutoApprovalSettings): void {\n\t\tconst validatedSettings = parse(AutoApprovalSettingsSchema, settings);\n\t\tthis.#state.settings = validatedSettings;\n\t}\n}\n\nfunction getSenderOutflows(analysis: SuccessfulAutoApprovalResult): CoinFlow[] {\n\treturn analysis.result.senderBalanceFlows\n\t\t.filter((flow) => flow.amount < 0n)\n\t\t.map((flow) => ({\n\t\t\tcoinType: flow.coinType,\n\t\t\tamount: -flow.amount,\n\t\t}));\n}\n\nconst parsedCoinType = parseStructTag('0x2::coin::Coin');\n\nfunction isCoinType(type: string): boolean {\n\tconst parsedType = parseStructTag(type);\n\treturn (\n\t\tparsedType.address === parsedCoinType.address &&\n\t\tparsedType.module === parsedCoinType.module &&\n\t\tparsedType.name === parsedCoinType.name &&\n\t\tparsedType.typeParams.length === 1\n\t);\n}\n\nfunction assertSuccessfulAnalysis(\n\tanalysis: AutoApprovalResult,\n): asserts analysis is SuccessfulAutoApprovalResult {\n\tif (analysis.status !== 'success') {\n\t\tthrow new Error('Transaction analysis failed');\n\t}\n}\n"],"mappings":"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"}