@mysten/sui 2.30.0 → 2.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/bcs/bcs.d.mts +6 -6
  3. package/dist/bcs/bcs.d.mts.map +1 -1
  4. package/dist/bcs/bcs.mjs +5 -1
  5. package/dist/bcs/bcs.mjs.map +1 -1
  6. package/dist/bcs/index.d.mts +139 -47
  7. package/dist/bcs/index.d.mts.map +1 -1
  8. package/dist/bcs/types.d.mts.map +1 -1
  9. package/dist/client/core-resolver.d.mts.map +1 -1
  10. package/dist/client/core-resolver.mjs +5 -3
  11. package/dist/client/core-resolver.mjs.map +1 -1
  12. package/dist/client/transaction-resolver.mjs +35 -8
  13. package/dist/client/transaction-resolver.mjs.map +1 -1
  14. package/dist/graphql/generated/tada-env.d.mts +74 -0
  15. package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts +4 -4
  16. package/dist/grpc/proto/sui/rpc/v2/input.d.mts +21 -1
  17. package/dist/grpc/proto/sui/rpc/v2/input.d.mts.map +1 -1
  18. package/dist/grpc/proto/sui/rpc/v2/input.mjs +21 -0
  19. package/dist/grpc/proto/sui/rpc/v2/input.mjs.map +1 -1
  20. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  21. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  22. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  23. package/dist/jsonRpc/core.mjs +1 -1
  24. package/dist/jsonRpc/core.mjs.map +1 -1
  25. package/dist/transactions/Transaction.d.mts +47 -42
  26. package/dist/transactions/Transaction.d.mts.map +1 -1
  27. package/dist/transactions/Transaction.mjs +65 -24
  28. package/dist/transactions/Transaction.mjs.map +1 -1
  29. package/dist/transactions/data/internal.d.mts +12 -0
  30. package/dist/transactions/data/internal.d.mts.map +1 -1
  31. package/dist/transactions/data/internal.mjs +5 -1
  32. package/dist/transactions/data/internal.mjs.map +1 -1
  33. package/dist/transactions/data/v2.d.mts +5 -0
  34. package/dist/transactions/data/v2.d.mts.map +1 -1
  35. package/dist/transactions/index.d.mts +3 -2
  36. package/dist/transactions/intents/AllowanceBalance.mjs +147 -0
  37. package/dist/transactions/intents/AllowanceBalance.mjs.map +1 -0
  38. package/dist/transactions/intents/BalanceIntentNames.mjs +7 -0
  39. package/dist/transactions/intents/BalanceIntentNames.mjs.map +1 -0
  40. package/dist/transactions/intents/BalanceOptions.d.mts +27 -0
  41. package/dist/transactions/intents/BalanceOptions.d.mts.map +1 -0
  42. package/dist/transactions/intents/BalanceOptions.mjs +12 -0
  43. package/dist/transactions/intents/BalanceOptions.mjs.map +1 -0
  44. package/dist/transactions/intents/CoinWithBalance.d.mts.map +1 -1
  45. package/dist/transactions/intents/CoinWithBalance.mjs +31 -10
  46. package/dist/transactions/intents/CoinWithBalance.mjs.map +1 -1
  47. package/dist/transactions/intents/ResolveBalances.mjs +16 -0
  48. package/dist/transactions/intents/ResolveBalances.mjs.map +1 -0
  49. package/dist/transactions/resolve.d.mts.map +1 -1
  50. package/dist/transactions/resolve.mjs +1 -1
  51. package/dist/transactions/resolve.mjs.map +1 -1
  52. package/dist/version.mjs +1 -1
  53. package/dist/version.mjs.map +1 -1
  54. package/dist/zklogin/bcs.d.mts +14 -14
  55. package/docs/llms-index.md +1 -0
  56. package/docs/transactions/allowances.md +176 -0
  57. package/docs/transactions/coins-and-balances.md +12 -9
  58. package/package.json +1 -1
  59. package/src/bcs/bcs.ts +4 -0
  60. package/src/bcs/types.ts +2 -1
  61. package/src/client/core-resolver.ts +12 -6
  62. package/src/client/transaction-resolver.ts +43 -9
  63. package/src/graphql/generated/schema.graphql +87 -42
  64. package/src/graphql/generated/tada-env.ts +84 -0
  65. package/src/grpc/proto/sui/rpc/v2/input.ts +22 -0
  66. package/src/jsonRpc/core.ts +3 -4
  67. package/src/transactions/Transaction.ts +70 -49
  68. package/src/transactions/data/internal.ts +4 -0
  69. package/src/transactions/index.ts +3 -0
  70. package/src/transactions/intents/AllowanceBalance.ts +204 -0
  71. package/src/transactions/intents/BalanceIntentNames.ts +5 -0
  72. package/src/transactions/intents/BalanceOptions.ts +45 -0
  73. package/src/transactions/intents/CoinWithBalance.ts +51 -8
  74. package/src/transactions/intents/ResolveBalances.ts +30 -0
  75. package/src/transactions/resolve.ts +6 -0
  76. package/src/version.ts +1 -1
@@ -0,0 +1,147 @@
1
+ import { normalizeStructTag, normalizeSuiAddress } from "../../utils/sui-types.mjs";
2
+ import { bcs as suiBcs } from "../../bcs/index.mjs";
3
+ import { hasMvrName } from "../../client/mvr.mjs";
4
+ import { TransactionCommands } from "../Commands.mjs";
5
+ import { Inputs } from "../Inputs.mjs";
6
+ import { getClient } from "../resolve.mjs";
7
+ import { ALLOWANCE_BALANCE } from "./BalanceIntentNames.mjs";
8
+ import { resolveBalances } from "./ResolveBalances.mjs";
9
+ import { object, optional, parse, picklist, string } from "valibot";
10
+
11
+ //#region src/transactions/intents/AllowanceBalance.ts
12
+ const AllowanceBalanceData = object({
13
+ objectId: string(),
14
+ funder: optional(string()),
15
+ appType: optional(string()),
16
+ type: string(),
17
+ amount: string(),
18
+ outputKind: picklist(["coin", "balance"])
19
+ });
20
+ const AllowanceMetadata = suiBcs.struct("AllowanceMetadata", {
21
+ id: suiBcs.Address,
22
+ funder: suiBcs.Address,
23
+ spender: suiBcs.option(suiBcs.Address),
24
+ app: suiBcs.option(suiBcs.string())
25
+ });
26
+ function allowanceBalance({ allowance, amount, type = "0x2::sui::SUI", outputKind }) {
27
+ suiBcs.U64.validate(amount);
28
+ const objectId = normalizeSuiAddress(typeof allowance === "string" ? allowance : allowance.objectId);
29
+ const reference = typeof allowance === "string" ? { objectId } : allowance;
30
+ return (tx) => {
31
+ tx.addIntentResolver(ALLOWANCE_BALANCE, resolveBalances);
32
+ return tx.add(TransactionCommands.Intent({
33
+ name: ALLOWANCE_BALANCE,
34
+ inputs: {
35
+ allowance: tx.object(objectId),
36
+ clock: tx.object.clock(),
37
+ ...reference.app ? { permit: tx.object(reference.app.permit) } : {}
38
+ },
39
+ data: {
40
+ objectId,
41
+ funder: reference.funder ? normalizeSuiAddress(reference.funder) : void 0,
42
+ appType: reference.app ? normalizeStructTag(reference.app.type) : void 0,
43
+ type: normalizeStructTag(type),
44
+ amount: String(amount),
45
+ outputKind
46
+ }
47
+ }));
48
+ };
49
+ }
50
+ const resolveAllowanceBalance = async (transactionData, options, next) => {
51
+ const ids = /* @__PURE__ */ new Set();
52
+ const namedTypes = /* @__PURE__ */ new Set();
53
+ for (const command of transactionData.commands) {
54
+ if (command.$kind !== "$Intent" || command.$Intent.name !== ALLOWANCE_BALANCE) continue;
55
+ const data = parse(AllowanceBalanceData, command.$Intent.data);
56
+ if (!data.funder) ids.add(data.objectId);
57
+ for (const type of [data.type, data.appType]) if (type && hasMvrName(type)) namedTypes.add(type);
58
+ }
59
+ const [resolvedTypes, objects] = await Promise.all([namedTypes.size ? getClient(options).core.mvr.resolve({ types: [...namedTypes] }) : void 0, ids.size ? getClient(options).core.getObjects({
60
+ objectIds: [...ids],
61
+ include: { content: true }
62
+ }).then((result) => result.objects) : []]);
63
+ const allowances = /* @__PURE__ */ new Map();
64
+ for (const object$1 of objects) {
65
+ if (object$1 instanceof Error) throw object$1;
66
+ allowances.set(object$1.objectId, object$1);
67
+ }
68
+ for (let index = 0; index < transactionData.commands.length; index++) {
69
+ const command = transactionData.commands[index];
70
+ if (command.$kind !== "$Intent" || command.$Intent.name !== ALLOWANCE_BALANCE) continue;
71
+ const data = parse(AllowanceBalanceData, command.$Intent.data);
72
+ if (hasMvrName(data.type)) {
73
+ const resolved = resolvedTypes?.types[data.type];
74
+ if (!resolved) throw new Error(`No resolution found for type: ${data.type}`);
75
+ data.type = normalizeStructTag(resolved.type);
76
+ }
77
+ if (data.appType && hasMvrName(data.appType)) {
78
+ const resolved = resolvedTypes?.types[data.appType];
79
+ if (!resolved) throw new Error(`No resolution found for type: ${data.appType}`);
80
+ data.appType = normalizeStructTag(resolved.type);
81
+ }
82
+ let funder = data.funder;
83
+ if (!funder) {
84
+ const allowance = allowances.get(data.objectId);
85
+ const expectedType = normalizeStructTag(`0x2::allowance::Allowance<0x2::balance::Balance<${data.type}>>`);
86
+ if (!allowance || allowance.type !== expectedType || allowance.owner.$kind !== "Shared") throw new Error(`Expected a shared ${expectedType} allowance at ${data.objectId}`);
87
+ const metadata = AllowanceMetadata.parse(allowance.content);
88
+ if (metadata.app !== null && !data.appType) throw new Error(`Allowance ${data.objectId} is app-bound; pass allowance.app with the app type and SpendPermit`);
89
+ if (data.appType && (metadata.app === null || normalizeStructTag(metadata.app) !== data.appType)) throw new Error(`Allowance ${data.objectId} is not bound to app ${data.appType}`);
90
+ funder = metadata.funder;
91
+ const input = command.$Intent.inputs.allowance;
92
+ if (input.$kind === "Input") transactionData.inputs[input.Input] = Inputs.SharedObjectRef({
93
+ objectId: data.objectId,
94
+ initialSharedVersion: allowance.owner.Shared.initialSharedVersion,
95
+ mutable: true
96
+ });
97
+ }
98
+ const allowanceArgument = command.$Intent.inputs.allowance;
99
+ if (allowanceArgument.$kind === "Input") {
100
+ const input = transactionData.inputs[allowanceArgument.Input];
101
+ if (input.Object?.SharedObject) input.Object.SharedObject.mutable = true;
102
+ if (input.UnresolvedObject) input.UnresolvedObject.mutable = true;
103
+ }
104
+ const withdrawal = transactionData.addInput("withdrawal", Inputs.FundsWithdrawal({
105
+ reservation: {
106
+ $kind: "MaxAmountU64",
107
+ MaxAmountU64: data.amount
108
+ },
109
+ typeArg: {
110
+ $kind: "Balance",
111
+ Balance: data.type
112
+ },
113
+ withdrawFrom: {
114
+ $kind: "SenderAllowance",
115
+ SenderAllowance: {
116
+ funder,
117
+ allowance: data.objectId
118
+ }
119
+ }
120
+ }));
121
+ const commands = [TransactionCommands.MoveCall({
122
+ target: data.appType ? "0x2::allowance::app_balance_spend" : "0x2::allowance::balance_spend",
123
+ typeArguments: data.appType ? [data.type, data.appType] : [data.type],
124
+ arguments: [
125
+ command.$Intent.inputs.allowance,
126
+ ...data.appType ? [command.$Intent.inputs.permit] : [],
127
+ withdrawal,
128
+ command.$Intent.inputs.clock
129
+ ]
130
+ })];
131
+ if (data.outputKind === "coin") commands.push(TransactionCommands.MoveCall({
132
+ target: "0x2::coin::from_balance",
133
+ typeArguments: [data.type],
134
+ arguments: [{
135
+ $kind: "Result",
136
+ Result: index
137
+ }]
138
+ }));
139
+ transactionData.replaceCommand(index, commands, { NestedResult: [index + commands.length - 1, 0] });
140
+ index += commands.length - 1;
141
+ }
142
+ return next();
143
+ };
144
+
145
+ //#endregion
146
+ export { allowanceBalance, resolveAllowanceBalance };
147
+ //# sourceMappingURL=AllowanceBalance.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AllowanceBalance.mjs","names":["bcs","object"],"sources":["../../../src/transactions/intents/AllowanceBalance.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { object, optional, parse, picklist, string } from 'valibot';\n\nimport { hasMvrName } from '../../client/mvr.js';\n\nimport { ALLOWANCE_BALANCE } from './BalanceIntentNames.js';\nimport { resolveBalances } from './ResolveBalances.js';\n\nimport { bcs } from '../../bcs/index.js';\nimport type { SuiClientTypes } from '../../client/index.js';\nimport { normalizeStructTag, normalizeSuiAddress } from '../../utils/sui-types.js';\nimport { TransactionCommands } from '../Commands.js';\nimport type { Argument } from '../data/internal.js';\nimport { Inputs } from '../Inputs.js';\nimport { getClient } from '../resolve.js';\nimport type { TransactionPlugin } from '../resolve.js';\nimport type { Transaction } from '../Transaction.js';\nimport type { AllowanceReference } from './BalanceOptions.js';\n\nexport { ALLOWANCE_BALANCE } from './BalanceIntentNames.js';\n\nconst AllowanceBalanceData = object({\n\tobjectId: string(),\n\tfunder: optional(string()),\n\tappType: optional(string()),\n\ttype: string(),\n\tamount: string(),\n\toutputKind: picklist(['coin', 'balance']),\n});\n\n// Read only the prefix needed for spending. The remaining settings and current_spend\n// are checked by Move, so this does not need to model rate limits or their policy.\nconst AllowanceMetadata = bcs.struct('AllowanceMetadata', {\n\tid: bcs.Address,\n\tfunder: bcs.Address,\n\tspender: bcs.option(bcs.Address),\n\tapp: bcs.option(bcs.string()),\n});\n\nexport function allowanceBalance({\n\tallowance,\n\tamount,\n\ttype = '0x2::sui::SUI',\n\toutputKind,\n}: {\n\tallowance: string | AllowanceReference;\n\tamount: bigint;\n\ttype?: string;\n\toutputKind: 'coin' | 'balance';\n}) {\n\tbcs.U64.validate(amount);\n\tconst objectId = normalizeSuiAddress(\n\t\ttypeof allowance === 'string' ? allowance : allowance.objectId,\n\t);\n\tconst reference = typeof allowance === 'string' ? { objectId } : allowance;\n\treturn (tx: Transaction) => {\n\t\ttx.addIntentResolver(ALLOWANCE_BALANCE, resolveBalances);\n\t\treturn tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: ALLOWANCE_BALANCE,\n\t\t\t\tinputs: {\n\t\t\t\t\tallowance: tx.object(objectId),\n\t\t\t\t\tclock: tx.object.clock(),\n\t\t\t\t\t...(reference.app ? { permit: tx.object(reference.app.permit) } : {}),\n\t\t\t\t},\n\t\t\t\tdata: {\n\t\t\t\t\tobjectId,\n\t\t\t\t\tfunder: reference.funder ? normalizeSuiAddress(reference.funder) : undefined,\n\t\t\t\t\tappType: reference.app ? normalizeStructTag(reference.app.type) : undefined,\n\t\t\t\t\ttype: normalizeStructTag(type),\n\t\t\t\t\tamount: String(amount),\n\t\t\t\t\toutputKind,\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t};\n}\n\nexport const resolveAllowanceBalance: TransactionPlugin = async (\n\ttransactionData,\n\toptions,\n\tnext,\n) => {\n\tconst ids = new Set<string>();\n\tconst namedTypes = new Set<string>();\n\tfor (const command of transactionData.commands) {\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== ALLOWANCE_BALANCE) continue;\n\t\tconst data = parse(AllowanceBalanceData, command.$Intent.data);\n\t\tif (!data.funder) ids.add(data.objectId);\n\t\tfor (const type of [data.type, data.appType]) {\n\t\t\tif (type && hasMvrName(type)) namedTypes.add(type);\n\t\t}\n\t}\n\n\tconst [resolvedTypes, objects] = await Promise.all([\n\t\tnamedTypes.size ? getClient(options).core.mvr.resolve({ types: [...namedTypes] }) : undefined,\n\t\tids.size\n\t\t\t? getClient(options)\n\t\t\t\t\t.core.getObjects({ objectIds: [...ids], include: { content: true } })\n\t\t\t\t\t.then((result) => result.objects)\n\t\t\t: [],\n\t]);\n\tconst allowances = new Map<string, SuiClientTypes.Object<{ content: true }>>();\n\tfor (const object of objects) {\n\t\tif (object instanceof Error) throw object;\n\t\tallowances.set(object.objectId, object);\n\t}\n\n\tfor (let index = 0; index < transactionData.commands.length; index++) {\n\t\tconst command = transactionData.commands[index];\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== ALLOWANCE_BALANCE) continue;\n\t\tconst data = parse(AllowanceBalanceData, command.$Intent.data);\n\t\tif (hasMvrName(data.type)) {\n\t\t\tconst resolved = resolvedTypes?.types[data.type];\n\t\t\tif (!resolved) throw new Error(`No resolution found for type: ${data.type}`);\n\t\t\tdata.type = normalizeStructTag(resolved.type);\n\t\t}\n\t\tif (data.appType && hasMvrName(data.appType)) {\n\t\t\tconst resolved = resolvedTypes?.types[data.appType];\n\t\t\tif (!resolved) throw new Error(`No resolution found for type: ${data.appType}`);\n\t\t\tdata.appType = normalizeStructTag(resolved.type);\n\t\t}\n\t\tlet funder = data.funder;\n\t\tif (!funder) {\n\t\t\tconst allowance = allowances.get(data.objectId);\n\t\t\tconst expectedType = normalizeStructTag(\n\t\t\t\t`0x2::allowance::Allowance<0x2::balance::Balance<${data.type}>>`,\n\t\t\t);\n\t\t\tif (!allowance || allowance.type !== expectedType || allowance.owner.$kind !== 'Shared') {\n\t\t\t\tthrow new Error(`Expected a shared ${expectedType} allowance at ${data.objectId}`);\n\t\t\t}\n\t\t\tconst metadata = AllowanceMetadata.parse(allowance.content);\n\t\t\tif (metadata.app !== null && !data.appType) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Allowance ${data.objectId} is app-bound; pass allowance.app with the app type and SpendPermit`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tdata.appType &&\n\t\t\t\t(metadata.app === null || normalizeStructTag(metadata.app) !== data.appType)\n\t\t\t) {\n\t\t\t\tthrow new Error(`Allowance ${data.objectId} is not bound to app ${data.appType}`);\n\t\t\t}\n\t\t\tfunder = metadata.funder;\n\t\t\tconst input = command.$Intent.inputs.allowance as Argument;\n\t\t\tif (input.$kind === 'Input') {\n\t\t\t\ttransactionData.inputs[input.Input] = Inputs.SharedObjectRef({\n\t\t\t\t\tobjectId: data.objectId,\n\t\t\t\t\tinitialSharedVersion: allowance.owner.Shared.initialSharedVersion,\n\t\t\t\t\tmutable: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// Spending mutates the allowance even when the caller supplied the funder\n\t\t// and we did not fetch metadata. Reuse and upgrade its existing input.\n\t\tconst allowanceArgument = command.$Intent.inputs.allowance as Argument;\n\t\tif (allowanceArgument.$kind === 'Input') {\n\t\t\tconst input = transactionData.inputs[allowanceArgument.Input];\n\t\t\tif (input.Object?.SharedObject) input.Object.SharedObject.mutable = true;\n\t\t\tif (input.UnresolvedObject) input.UnresolvedObject.mutable = true;\n\t\t}\n\t\tconst withdrawal = transactionData.addInput(\n\t\t\t'withdrawal',\n\t\t\tInputs.FundsWithdrawal({\n\t\t\t\treservation: { $kind: 'MaxAmountU64', MaxAmountU64: data.amount },\n\t\t\t\ttypeArg: { $kind: 'Balance', Balance: data.type },\n\t\t\t\twithdrawFrom: {\n\t\t\t\t\t$kind: 'SenderAllowance',\n\t\t\t\t\tSenderAllowance: { funder, allowance: data.objectId },\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tconst commands = [\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\ttarget: data.appType\n\t\t\t\t\t? '0x2::allowance::app_balance_spend'\n\t\t\t\t\t: '0x2::allowance::balance_spend',\n\t\t\t\ttypeArguments: data.appType ? [data.type, data.appType] : [data.type],\n\t\t\t\targuments: [\n\t\t\t\t\tcommand.$Intent.inputs.allowance as Argument,\n\t\t\t\t\t...(data.appType ? [command.$Intent.inputs.permit as Argument] : []),\n\t\t\t\t\twithdrawal,\n\t\t\t\t\tcommand.$Intent.inputs.clock as Argument,\n\t\t\t\t],\n\t\t\t}),\n\t\t];\n\t\tif (data.outputKind === 'coin') {\n\t\t\tcommands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: '0x2::coin::from_balance',\n\t\t\t\t\ttypeArguments: [data.type],\n\t\t\t\t\targuments: [{ $kind: 'Result', Result: index }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\ttransactionData.replaceCommand(index, commands, {\n\t\t\tNestedResult: [index + commands.length - 1, 0],\n\t\t});\n\t\tindex += commands.length - 1;\n\t}\n\treturn next();\n};\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ //#region src/transactions/intents/BalanceIntentNames.ts
2
+ const ALLOWANCE_BALANCE = "AllowanceBalance";
3
+ const COIN_WITH_BALANCE = "CoinWithBalance";
4
+
5
+ //#endregion
6
+ export { ALLOWANCE_BALANCE, COIN_WITH_BALANCE };
7
+ //# sourceMappingURL=BalanceIntentNames.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BalanceIntentNames.mjs","names":[],"sources":["../../../src/transactions/intents/BalanceIntentNames.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport const ALLOWANCE_BALANCE = 'AllowanceBalance';\nexport const COIN_WITH_BALANCE = 'CoinWithBalance';\n"],"mappings":";AAGA,MAAa,oBAAoB;AACjC,MAAa,oBAAoB"}
@@ -0,0 +1,27 @@
1
+ import { TransactionObjectArgument } from "../Transaction.mjs";
2
+
3
+ //#region src/transactions/intents/BalanceOptions.d.ts
4
+ /** An allowance with optional funder metadata and app authorization. */
5
+ interface AllowanceReference {
6
+ objectId: string;
7
+ /** Skip the metadata lookup when the funder is already known. */
8
+ funder?: string;
9
+ /** Authorization for an app-bound allowance. */
10
+ app?: {
11
+ /** The app type A in SpendPermit<A>. */type: string; /** A SpendPermit<A> from the app, consumed by this spend. */
12
+ permit: TransactionObjectArgument;
13
+ };
14
+ }
15
+ type BalanceOptions = {
16
+ /** The coin type T, not Balance<T>. Defaults to SUI. */type?: string;
17
+ balance: bigint | number | string;
18
+ } & ({
19
+ allowance?: never;
20
+ useGasCoin?: boolean;
21
+ } | {
22
+ /** An allowance ID or reference with optional app authorization. Never falls back to sender funds. */allowance: string | AllowanceReference;
23
+ useGasCoin?: never;
24
+ });
25
+ //#endregion
26
+ export { AllowanceReference, BalanceOptions };
27
+ //# sourceMappingURL=BalanceOptions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BalanceOptions.d.mts","names":[],"sources":["../../../src/transactions/intents/BalanceOptions.ts"],"mappings":";;;;UAMiB,kBAAA;EAChB,QAAA;EADkC;EAGlC,MAAA;EAMkC;EAJlC,GAAA;IAFA,wCAIC,IAAA,UAAA;IAEA,MAAA,EAAQ,yBAAA;EAAA;AAAA;AAAA,KAIE,cAAA;EAAA,wDAEX,IAAA;EACA,OAAA;AAAA;EAEI,SAAA;EAAmB,UAAA;AAAA;EAAA,sGAGrB,SAAA,WAAoB,kBAAA;EACpB,UAAA;AAAA"}
@@ -0,0 +1,12 @@
1
+ //#region src/transactions/intents/BalanceOptions.ts
2
+ function normalizeBalance(options) {
3
+ if (options.allowance !== void 0 && options.useGasCoin !== void 0) throw new Error("useGasCoin cannot be combined with allowance");
4
+ const amount = options.balance;
5
+ if (typeof amount === "string" && !/^[0-9]+$/.test(amount)) throw new Error("Amount must be a non-empty string of decimal digits");
6
+ if (typeof amount === "number" && !Number.isSafeInteger(amount)) throw new Error("Amount must be a safe integer; use bigint or a string for larger amounts");
7
+ return BigInt(amount);
8
+ }
9
+
10
+ //#endregion
11
+ export { normalizeBalance };
12
+ //# sourceMappingURL=BalanceOptions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BalanceOptions.mjs","names":[],"sources":["../../../src/transactions/intents/BalanceOptions.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { TransactionObjectArgument } from '../Transaction.js';\n\n/** An allowance with optional funder metadata and app authorization. */\nexport interface AllowanceReference {\n\tobjectId: string;\n\t/** Skip the metadata lookup when the funder is already known. */\n\tfunder?: string;\n\t/** Authorization for an app-bound allowance. */\n\tapp?: {\n\t\t/** The app type A in SpendPermit<A>. */\n\t\ttype: string;\n\t\t/** A SpendPermit<A> from the app, consumed by this spend. */\n\t\tpermit: TransactionObjectArgument;\n\t};\n}\n\nexport type BalanceOptions = {\n\t/** The coin type T, not Balance<T>. Defaults to SUI. */\n\ttype?: string;\n\tbalance: bigint | number | string;\n} & (\n\t| { allowance?: never; useGasCoin?: boolean }\n\t| {\n\t\t\t/** An allowance ID or reference with optional app authorization. Never falls back to sender funds. */\n\t\t\tallowance: string | AllowanceReference;\n\t\t\tuseGasCoin?: never;\n\t }\n);\n\nexport function normalizeBalance(options: BalanceOptions): bigint {\n\tif (options.allowance !== undefined && options.useGasCoin !== undefined) {\n\t\tthrow new Error('useGasCoin cannot be combined with allowance');\n\t}\n\tconst amount = options.balance;\n\tif (typeof amount === 'string' && !/^[0-9]+$/.test(amount)) {\n\t\tthrow new Error('Amount must be a non-empty string of decimal digits');\n\t}\n\tif (typeof amount === 'number' && !Number.isSafeInteger(amount)) {\n\t\tthrow new Error('Amount must be a safe integer; use bigint or a string for larger amounts');\n\t}\n\treturn BigInt(amount);\n}\n"],"mappings":";AAgCA,SAAgB,iBAAiB,SAAiC;AACjE,KAAI,QAAQ,cAAc,UAAa,QAAQ,eAAe,OAC7D,OAAM,IAAI,MAAM,+CAA+C;CAEhE,MAAM,SAAS,QAAQ;AACvB,KAAI,OAAO,WAAW,YAAY,CAAC,WAAW,KAAK,OAAO,CACzD,OAAM,IAAI,MAAM,sDAAsD;AAEvE,KAAI,OAAO,WAAW,YAAY,CAAC,OAAO,cAAc,OAAO,CAC9D,OAAM,IAAI,MAAM,2EAA2E;AAE5F,QAAO,OAAO,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoinWithBalance.d.mts","names":[],"sources":["../../../src/transactions/intents/CoinWithBalance.ts"],"mappings":";;;;;iBA+BgB,eAAA,CAAA;EACf,IAAA;EACA,OAAA;EACA;AAAA;EAEA,OAAA;EACA,IAAA;EACA,UAAA;AAAA,KACI,EAAA,EAAI,WAAA,KAAgB,iBAAA"}
1
+ {"version":3,"file":"CoinWithBalance.d.mts","names":[],"sources":["../../../src/transactions/intents/CoinWithBalance.ts"],"mappings":";;;;;iBAkCgB,eAAA,CAAA;EACf,IAAA;EACA,OAAA;EACA;AAAA;EAEA,OAAA;EACA,IAAA;EACA,UAAA;AAAA,KACI,EAAA,EAAI,WAAA,KAAgB,iBAAA"}
@@ -1,17 +1,18 @@
1
- import { normalizeStructTag } from "../../utils/sui-types.mjs";
1
+ import { normalizeStructTag, normalizeSuiAddress } from "../../utils/sui-types.mjs";
2
2
  import { bcs as suiBcs } from "../../bcs/index.mjs";
3
3
  import { TransactionCommands } from "../Commands.mjs";
4
4
  import { Inputs } from "../Inputs.mjs";
5
+ import { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from "./BalanceIntentNames.mjs";
6
+ import { resolveBalances } from "./ResolveBalances.mjs";
5
7
  import { bigint, integer, number, object, optional, parse, picklist, pipe, string, transform, union } from "valibot";
6
8
 
7
9
  //#region src/transactions/intents/CoinWithBalance.ts
8
- const COIN_WITH_BALANCE = "CoinWithBalance";
9
10
  const SUI_TYPE = normalizeStructTag("0x2::sui::SUI");
10
11
  function coinWithBalance({ type = SUI_TYPE, balance, useGasCoin = true }) {
11
12
  let coinResult = null;
12
13
  return (tx) => {
13
14
  if (coinResult) return coinResult;
14
- tx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);
15
+ tx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);
15
16
  const coinType = type === "gas" ? type : normalizeStructTag(type);
16
17
  coinResult = tx.add(TransactionCommands.Intent({
17
18
  name: COIN_WITH_BALANCE,
@@ -29,7 +30,7 @@ function createBalance({ type = SUI_TYPE, balance, useGasCoin = true }) {
29
30
  let balanceResult = null;
30
31
  return (tx) => {
31
32
  if (balanceResult) return balanceResult;
32
- tx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);
33
+ tx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);
33
34
  const coinType = type === "gas" ? type : normalizeStructTag(type);
34
35
  balanceResult = tx.add(TransactionCommands.Intent({
35
36
  name: COIN_WITH_BALANCE,
@@ -57,6 +58,15 @@ async function resolveCoinBalance(transactionData, buildOptions, next) {
57
58
  const totalByType = /* @__PURE__ */ new Map();
58
59
  const intentsByType = /* @__PURE__ */ new Map();
59
60
  if (!transactionData.sender) throw new Error("Sender must be set to resolve CoinWithBalance");
61
+ if (transactionData.commands.some((command) => command.$Intent?.name === ALLOWANCE_BALANCE)) throw new Error("Resolve AllowanceBalance together with CoinWithBalance, or preserve both intents");
62
+ const reservedByType = /* @__PURE__ */ new Map();
63
+ for (const input of transactionData.inputs) {
64
+ if (!input.FundsWithdrawal) continue;
65
+ const { withdrawFrom, typeArg, reservation } = input.FundsWithdrawal;
66
+ if (normalizeSuiAddress(withdrawFrom.SenderAllowance?.funder ?? (withdrawFrom.Sender ? transactionData.sender : transactionData.gasData.owner ?? transactionData.sender)) !== normalizeSuiAddress(transactionData.sender)) continue;
67
+ const type = normalizeStructTag(typeArg.Balance);
68
+ reservedByType.set(type, (reservedByType.get(type) ?? 0n) + BigInt(reservation.MaxAmountU64));
69
+ }
60
70
  for (const [i, command] of transactionData.commands.entries()) {
61
71
  if (command.$kind !== "$Intent" || command.$Intent.name !== COIN_WITH_BALANCE) continue;
62
72
  const { type, balance, outputKind } = parse(CoinWithBalanceData, command.$Intent.data);
@@ -94,7 +104,8 @@ async function resolveCoinBalance(transactionData, buildOptions, next) {
94
104
  balance: totalByType.get(coinType),
95
105
  client,
96
106
  owner: transactionData.sender,
97
- usedIds
107
+ usedIds,
108
+ reserved: reservedByType.get(coinType) ?? 0n
98
109
  });
99
110
  coinsByType.set(coinType, coins);
100
111
  addressBalanceByType.set(coinType, addressBalance);
@@ -102,7 +113,7 @@ async function resolveCoinBalance(transactionData, buildOptions, next) {
102
113
  owner: transactionData.sender,
103
114
  coinType: SUI_TYPE
104
115
  }).then(({ balance }) => {
105
- addressBalanceByType.set("gas", BigInt(balance.addressBalance));
116
+ addressBalanceByType.set("gas", availableAddressBalance(balance.addressBalance, reservedByType.get(SUI_TYPE) ?? 0n));
106
117
  }) : null]);
107
118
  const exactBalanceByType = /* @__PURE__ */ new Map();
108
119
  const usedAddressBalance = /* @__PURE__ */ new Set();
@@ -262,15 +273,20 @@ async function resolveCoinBalance(transactionData, buildOptions, next) {
262
273
  }
263
274
  return next();
264
275
  }
265
- async function getCoinsAndBalanceOfType({ coinType, balance, client, owner, usedIds }) {
276
+ async function getCoinsAndBalanceOfType({ coinType, balance, client, owner, usedIds, reserved }) {
266
277
  let remainingBalance = balance;
267
278
  const coins = [];
268
279
  const balanceRequest = client.core.getBalance({
269
280
  owner,
270
281
  coinType
271
282
  }).then(({ balance: balance$1 }) => {
272
- remainingBalance -= BigInt(balance$1.addressBalance);
273
- return balance$1;
283
+ const addressBalance = availableAddressBalance(balance$1.addressBalance, reserved);
284
+ remainingBalance -= addressBalance;
285
+ return {
286
+ ...balance$1,
287
+ addressBalance: String(addressBalance),
288
+ balance: String(BigInt(balance$1.balance) - reserved)
289
+ };
274
290
  });
275
291
  const [allCoins, balanceResponse] = await Promise.all([loadMoreCoins(), balanceRequest]);
276
292
  if (BigInt(balanceResponse.balance) < balance) throw new Error(`Insufficient balance of ${coinType} for owner ${owner}. Required: ${balance}, Available: ${balance - remainingBalance}`);
@@ -296,7 +312,12 @@ async function getCoinsAndBalanceOfType({ coinType, balance, client, owner, used
296
312
  return coins;
297
313
  }
298
314
  }
315
+ function availableAddressBalance(balance, reserved) {
316
+ const available = BigInt(balance) - reserved;
317
+ if (available < 0n) throw new Error(`Insufficient address balance for existing withdrawals. Required: ${reserved}, Available: ${balance}`);
318
+ return available;
319
+ }
299
320
 
300
321
  //#endregion
301
- export { COIN_WITH_BALANCE, coinWithBalance, createBalance, resolveCoinBalance };
322
+ export { coinWithBalance, createBalance, resolveCoinBalance };
302
323
  //# sourceMappingURL=CoinWithBalance.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"CoinWithBalance.mjs","names":["bcs","balance"],"sources":["../../../src/transactions/intents/CoinWithBalance.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { InferInput } from 'valibot';\nimport {\n\tbigint,\n\tinteger,\n\tnumber,\n\tobject,\n\toptional,\n\tparse,\n\tpicklist,\n\tpipe,\n\tstring,\n\ttransform,\n\tunion,\n} from 'valibot';\n\nimport { bcs } from '../../bcs/index.js';\nimport { normalizeStructTag } from '../../utils/sui-types.js';\nimport { TransactionCommands } from '../Commands.js';\nimport type { Argument } from '../data/internal.js';\nimport { Inputs } from '../Inputs.js';\nimport type { BuildTransactionOptions } from '../resolve.js';\nimport type { Transaction, TransactionResult } from '../Transaction.js';\nimport type { TransactionDataBuilder } from '../TransactionData.js';\nimport type { ClientWithCoreApi, SuiClientTypes } from '../../client/index.js';\n\nexport const COIN_WITH_BALANCE = 'CoinWithBalance';\nconst SUI_TYPE = normalizeStructTag('0x2::sui::SUI');\n\nexport function coinWithBalance({\n\ttype = SUI_TYPE,\n\tbalance,\n\tuseGasCoin = true,\n}: {\n\tbalance: bigint | number;\n\ttype?: string;\n\tuseGasCoin?: boolean;\n}): (tx: Transaction) => TransactionResult {\n\tlet coinResult: TransactionResult | null = null;\n\n\treturn (tx: Transaction) => {\n\t\tif (coinResult) {\n\t\t\treturn coinResult;\n\t\t}\n\n\t\ttx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);\n\t\tconst coinType = type === 'gas' ? type : normalizeStructTag(type);\n\n\t\tcoinResult = tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: COIN_WITH_BALANCE,\n\t\t\t\tinputs: {},\n\t\t\t\tdata: {\n\t\t\t\t\ttype: coinType === SUI_TYPE && useGasCoin ? 'gas' : coinType,\n\t\t\t\t\tbalance: BigInt(balance),\n\t\t\t\t\toutputKind: 'coin',\n\t\t\t\t} satisfies InferInput<typeof CoinWithBalanceData>,\n\t\t\t}),\n\t\t);\n\n\t\treturn coinResult;\n\t};\n}\n\nexport function createBalance({\n\ttype = SUI_TYPE,\n\tbalance,\n\tuseGasCoin = true,\n}: {\n\tbalance: bigint | number;\n\ttype?: string;\n\tuseGasCoin?: boolean;\n}): (tx: Transaction) => TransactionResult {\n\tlet balanceResult: TransactionResult | null = null;\n\n\treturn (tx: Transaction) => {\n\t\tif (balanceResult) {\n\t\t\treturn balanceResult;\n\t\t}\n\n\t\ttx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);\n\t\tconst coinType = type === 'gas' ? type : normalizeStructTag(type);\n\n\t\tbalanceResult = tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: COIN_WITH_BALANCE,\n\t\t\t\tinputs: {},\n\t\t\t\tdata: {\n\t\t\t\t\ttype: coinType === SUI_TYPE && useGasCoin ? 'gas' : coinType,\n\t\t\t\t\tbalance: BigInt(balance),\n\t\t\t\t\toutputKind: 'balance',\n\t\t\t\t} satisfies InferInput<typeof CoinWithBalanceData>,\n\t\t\t}),\n\t\t);\n\n\t\treturn balanceResult;\n\t};\n}\n\n// `balance` is a bigint in memory, but serializing a transaction to JSON converts it to a\n// string. When that JSON is deserialized with `Transaction.from`, the value stays a string\n// (the intent `data` is opaque to the transaction schema), so coerce it back to a bigint here.\nconst CoinWithBalanceData = object({\n\ttype: string(),\n\tbalance: pipe(\n\t\tunion([bigint(), string(), pipe(number(), integer())]),\n\t\ttransform((value) => BigInt(value)),\n\t),\n\toutputKind: optional(picklist(['coin', 'balance'])),\n});\n\nexport async function resolveCoinBalance(\n\ttransactionData: TransactionDataBuilder,\n\tbuildOptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) {\n\ttype IntentInfo = { balance: bigint; outputKind: 'coin' | 'balance' };\n\n\tconst coinTypes = new Set<string>();\n\tconst totalByType = new Map<string, bigint>();\n\tconst intentsByType = new Map<string, IntentInfo[]>();\n\n\tif (!transactionData.sender) {\n\t\tthrow new Error('Sender must be set to resolve CoinWithBalance');\n\t}\n\n\t// First pass: scan intents, collect per-type data, and resolve zero-balance intents in place.\n\tfor (const [i, command] of transactionData.commands.entries()) {\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== COIN_WITH_BALANCE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { type, balance, outputKind } = parse(CoinWithBalanceData, command.$Intent.data);\n\n\t\t// Zero-balance intents are resolved immediately — no coins or AB needed.\n\t\t// This is a 1:1 replacement so indices don't shift.\n\t\tif (balance === 0n) {\n\t\t\tconst coinType = type === 'gas' ? SUI_TYPE : type;\n\t\t\ttransactionData.replaceCommand(\n\t\t\t\ti,\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: (outputKind ?? 'coin') === 'balance' ? '0x2::balance::zero' : '0x2::coin::zero',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (type !== 'gas') {\n\t\t\tcoinTypes.add(type);\n\t\t}\n\n\t\ttotalByType.set(type, (totalByType.get(type) ?? 0n) + balance);\n\n\t\tif (!intentsByType.has(type)) intentsByType.set(type, []);\n\t\tintentsByType.get(type)!.push({ balance, outputKind: outputKind ?? 'coin' });\n\t}\n\n\tif (totalByType.has('gas') && totalByType.has(SUI_TYPE)) {\n\t\tthrow new Error(\n\t\t\t'Cannot mix SUI CoinWithBalance intents that use the gas coin with ones that do not (useGasCoin: false). Use one or the other.',\n\t\t);\n\t}\n\n\tconst usedIds = new Set<string>();\n\n\tfor (const input of transactionData.inputs) {\n\t\tif (input.Object?.ImmOrOwnedObject) {\n\t\t\tusedIds.add(input.Object.ImmOrOwnedObject.objectId);\n\t\t}\n\t\tif (input.UnresolvedObject?.objectId) {\n\t\t\tusedIds.add(input.UnresolvedObject.objectId);\n\t\t}\n\t}\n\n\tconst coinsByType = new Map<string, SuiClientTypes.Coin[]>();\n\tconst addressBalanceByType = new Map<string, bigint>();\n\tconst client = buildOptions.client;\n\tconst assumeSufficientAddressBalances = buildOptions.assumeSufficientAddressBalances;\n\n\tif (!client && !assumeSufficientAddressBalances) {\n\t\tthrow new Error(\n\t\t\t'Client must be provided to build or serialize transactions with CoinWithBalance intents',\n\t\t);\n\t}\n\n\tif (assumeSufficientAddressBalances) {\n\t\tfor (const [coinType, balance] of totalByType) {\n\t\t\taddressBalanceByType.set(coinType, balance);\n\t\t}\n\t} else {\n\t\tawait Promise.all([\n\t\t\t...[...coinTypes].map(async (coinType) => {\n\t\t\t\tconst { coins, addressBalance } = await getCoinsAndBalanceOfType({\n\t\t\t\t\tcoinType,\n\t\t\t\t\tbalance: totalByType.get(coinType)!,\n\t\t\t\t\tclient: client!,\n\t\t\t\t\towner: transactionData.sender!,\n\t\t\t\t\tusedIds,\n\t\t\t\t});\n\n\t\t\t\tcoinsByType.set(coinType, coins);\n\t\t\t\taddressBalanceByType.set(coinType, addressBalance);\n\t\t\t}),\n\t\t\ttotalByType.has('gas')\n\t\t\t\t? await client!.core\n\t\t\t\t\t\t.getBalance({\n\t\t\t\t\t\t\towner: transactionData.sender!,\n\t\t\t\t\t\t\tcoinType: SUI_TYPE,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then(({ balance }) => {\n\t\t\t\t\t\t\taddressBalanceByType.set('gas', BigInt(balance.addressBalance));\n\t\t\t\t\t\t})\n\t\t\t\t: null,\n\t\t]);\n\t}\n\n\tconst exactBalanceByType = new Map<string, boolean>();\n\tconst usedAddressBalance = new Set<string>();\n\n\t// Per-type state for Path 2 combined splits\n\ttype TypeState = { results: Argument[]; nextIntent: number };\n\tconst typeState = new Map<string, TypeState>();\n\n\tlet index = 0;\n\twhile (index < transactionData.commands.length) {\n\t\tconst transaction = transactionData.commands[index];\n\t\tif (transaction.$kind !== '$Intent' || transaction.$Intent.name !== COIN_WITH_BALANCE) {\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { type, balance } = parse(CoinWithBalanceData, transaction.$Intent.data);\n\t\tconst coinType = type === 'gas' ? SUI_TYPE : type;\n\t\tconst totalRequired = totalByType.get(type)!;\n\t\tconst addressBalance = addressBalanceByType.get(type) ?? 0n;\n\n\t\tconst commands = [];\n\t\tlet intentResult: Argument;\n\n\t\tconst intentsForType = intentsByType.get(type) ?? [];\n\t\tconst allBalance = intentsForType.every((i) => i.outputKind === 'balance');\n\n\t\tif (allBalance && addressBalance >= totalRequired) {\n\t\t\t// Path 1: All balance intents and AB sufficient — direct per-intent withdrawal.\n\t\t\t// No coins touched, enables parallel execution.\n\t\t\tcommands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: '0x2::balance::redeem_funds',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\targuments: [\n\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\tMaxAmountU64: String(balance),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tintentResult = {\n\t\t\t\t$kind: 'NestedResult',\n\t\t\t\tNestedResult: [index + commands.length - 1, 0],\n\t\t\t};\n\t\t} else {\n\t\t\t// Path 2: Merge and Split — build a merged coin, split all intents at once.\n\n\t\t\tif (!typeState.has(type)) {\n\t\t\t\tconst intents = intentsForType;\n\n\t\t\t\t// Step 1: Build sources and merge\n\t\t\t\tconst sources: Argument[] = [];\n\n\t\t\t\tif (addressBalance >= totalRequired) {\n\t\t\t\t\t// AB sufficient — source entirely from address balance, no coins needed.\n\t\t\t\t\tusedAddressBalance.add(type);\n\n\t\t\t\t\tcommands.push(\n\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\ttarget: '0x2::coin::redeem_funds',\n\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\t\t\tMaxAmountU64: String(totalRequired),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tsources.push({ $kind: 'Result', Result: index + commands.length - 1 });\n\t\t\t\t} else if (type === 'gas') {\n\t\t\t\t\tsources.push({ $kind: 'GasCoin', GasCoin: true });\n\t\t\t\t} else {\n\t\t\t\t\tconst coins = coinsByType.get(type)!;\n\t\t\t\t\tconst loadedCoinBalance = coins.reduce((sum, c) => sum + BigInt(c.balance), 0n);\n\t\t\t\t\tconst abNeeded =\n\t\t\t\t\t\ttotalRequired > loadedCoinBalance ? totalRequired - loadedCoinBalance : 0n;\n\n\t\t\t\t\texactBalanceByType.set(type, loadedCoinBalance + abNeeded === totalRequired);\n\n\t\t\t\t\tfor (const coin of coins) {\n\t\t\t\t\t\tsources.push(\n\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t'object',\n\t\t\t\t\t\t\t\tInputs.ObjectRef({\n\t\t\t\t\t\t\t\t\tobjectId: coin.objectId,\n\t\t\t\t\t\t\t\t\tdigest: coin.digest,\n\t\t\t\t\t\t\t\t\tversion: coin.version,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (abNeeded > 0n) {\n\t\t\t\t\t\tusedAddressBalance.add(type);\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::redeem_funds',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\t\t\t\tMaxAmountU64: String(abNeeded),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsources.push({ $kind: 'Result', Result: index + commands.length - 1 });\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst baseCoin = sources[0];\n\t\t\t\tconst rest = sources.slice(1);\n\t\t\t\tfor (let i = 0; i < rest.length; i += 500) {\n\t\t\t\t\tcommands.push(TransactionCommands.MergeCoins(baseCoin, rest.slice(i, i + 500)));\n\t\t\t\t}\n\n\t\t\t\t// Step 2: Combined SplitCoins for all intents of this type\n\t\t\t\tconst splitCmdIndex = index + commands.length;\n\t\t\t\tcommands.push(\n\t\t\t\t\tTransactionCommands.SplitCoins(\n\t\t\t\t\t\tbaseCoin,\n\t\t\t\t\t\tintents.map((i) =>\n\t\t\t\t\t\t\ttransactionData.addInput('pure', Inputs.Pure(bcs.u64().serialize(i.balance))),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\t// Build per-intent results, adding into_balance conversions for balance intents\n\t\t\t\tconst results: Argument[] = [];\n\t\t\t\tfor (let i = 0; i < intents.length; i++) {\n\t\t\t\t\tconst splitResult: Argument = {\n\t\t\t\t\t\t$kind: 'NestedResult',\n\t\t\t\t\t\tNestedResult: [splitCmdIndex, i],\n\t\t\t\t\t};\n\n\t\t\t\t\tif (intents[i].outputKind === 'balance') {\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::into_balance',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [splitResult],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresults.push({\n\t\t\t\t\t\t\t$kind: 'NestedResult',\n\t\t\t\t\t\t\tNestedResult: [index + commands.length - 1, 0],\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.push(splitResult);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Step 3: Remainder handling\n\t\t\t\t//\n\t\t\t\t// Add cleanup to this replacement command list rather than appending\n\t\t\t\t// it to the complete transaction. Appending cleanup can place it after\n\t\t\t\t// a MoveCall that consumes Random, which Sui rejects.\n\t\t\t\t//\n\t\t\t\t// When gas type used GasCoin (not AB), leftover stays in the gas coin\n\t\t\t\t// -- no remainder handling is needed.\n\t\t\t\tif (type !== 'gas' || usedAddressBalance.has(type)) {\n\t\t\t\t\tconst hasBalanceIntent = intents.some((intent) => intent.outputKind === 'balance');\n\t\t\t\t\tconst sourcedFromAB = usedAddressBalance.has(type);\n\n\t\t\t\t\tif (hasBalanceIntent || sourcedFromAB) {\n\t\t\t\t\t\t// Sourced from AB or balance intents exist: send remainder back to sender's address\n\t\t\t\t\t\t// balance. `coin::send_funds` is gasless-eligible and handles zero amounts.\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::send_funds',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\t\tbaseCoin,\n\t\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t\t'pure',\n\t\t\t\t\t\t\t\t\t\tInputs.Pure(bcs.Address.serialize(transactionData.sender!)),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (exactBalanceByType.get(type)) {\n\t\t\t\t\t\t// Coin-only with exact match: destroy the zero-value dust coin.\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::destroy_zero',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [baseCoin],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Coin-only with surplus: merged coin stays with sender as an owned object.\n\t\t\t\t}\n\n\t\t\t\ttypeState.set(type, { results, nextIntent: 0 });\n\t\t\t}\n\n\t\t\tconst state = typeState.get(type)!;\n\t\t\tintentResult = state.results[state.nextIntent++];\n\t\t}\n\n\t\ttransactionData.replaceCommand(\n\t\t\tindex,\n\t\t\tcommands,\n\t\t\tintentResult as { NestedResult: [number, number] },\n\t\t);\n\n\t\t// Advance past the replacement. When commands is empty (subsequent intents\n\t\t// of a combined split), the command was removed and the next command shifted\n\t\t// into this position — so we stay at the same index.\n\t\tindex += commands.length;\n\t}\n\n\treturn next();\n}\n\nasync function getCoinsAndBalanceOfType({\n\tcoinType,\n\tbalance,\n\tclient,\n\towner,\n\tusedIds,\n}: {\n\tcoinType: string;\n\tbalance: bigint;\n\tclient: ClientWithCoreApi;\n\towner: string;\n\tusedIds: Set<string>;\n}): Promise<{\n\tcoins: SuiClientTypes.Coin[];\n\tbalance: bigint;\n\taddressBalance: bigint;\n\tcoinBalance: bigint;\n}> {\n\tlet remainingBalance = balance;\n\tconst coins: SuiClientTypes.Coin[] = [];\n\tconst balanceRequest = client.core.getBalance({ owner, coinType }).then(({ balance }) => {\n\t\tremainingBalance -= BigInt(balance.addressBalance);\n\n\t\treturn balance;\n\t});\n\n\tconst [allCoins, balanceResponse] = await Promise.all([loadMoreCoins(), balanceRequest]);\n\n\tif (BigInt(balanceResponse.balance) < balance) {\n\t\tthrow new Error(\n\t\t\t`Insufficient balance of ${coinType} for owner ${owner}. Required: ${balance}, Available: ${\n\t\t\t\tbalance - remainingBalance\n\t\t\t}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tcoins: allCoins,\n\t\tbalance: BigInt(balanceResponse.coinBalance),\n\t\taddressBalance: BigInt(balanceResponse.addressBalance),\n\t\tcoinBalance: BigInt(balanceResponse.coinBalance),\n\t};\n\n\tasync function loadMoreCoins(cursor: string | null = null): Promise<SuiClientTypes.Coin[]> {\n\t\tconst {\n\t\t\tobjects,\n\t\t\thasNextPage,\n\t\t\tcursor: nextCursor,\n\t\t} = await client.core.listCoins({\n\t\t\towner,\n\t\t\tcoinType,\n\t\t\tcursor,\n\t\t});\n\n\t\tawait balanceRequest;\n\n\t\t// Always load all coins from the page (except already-used ones).\n\t\t// This merges all available coins rather than leaving dust.\n\t\tfor (const coin of objects) {\n\t\t\tif (usedIds.has(coin.objectId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcoins.push(coin);\n\t\t\tremainingBalance -= BigInt(coin.balance);\n\t\t}\n\n\t\t// Only paginate if loaded coins + AB are still insufficient\n\t\tif (remainingBalance > 0n && hasNextPage) {\n\t\t\treturn loadMoreCoins(nextCursor);\n\t\t}\n\n\t\treturn coins;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoinWithBalance.mjs","names":["bcs","balance"],"sources":["../../../src/transactions/intents/CoinWithBalance.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { InferInput } from 'valibot';\nimport {\n\tbigint,\n\tinteger,\n\tnumber,\n\tobject,\n\toptional,\n\tparse,\n\tpicklist,\n\tpipe,\n\tstring,\n\ttransform,\n\tunion,\n} from 'valibot';\n\nimport { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from './BalanceIntentNames.js';\nimport { resolveBalances } from './ResolveBalances.js';\n\nimport { bcs } from '../../bcs/index.js';\nimport { normalizeStructTag, normalizeSuiAddress } from '../../utils/sui-types.js';\nimport { TransactionCommands } from '../Commands.js';\nimport type { Argument } from '../data/internal.js';\nimport { Inputs } from '../Inputs.js';\nimport type { BuildTransactionOptions } from '../resolve.js';\nimport type { Transaction, TransactionResult } from '../Transaction.js';\nimport type { TransactionDataBuilder } from '../TransactionData.js';\nimport type { ClientWithCoreApi, SuiClientTypes } from '../../client/index.js';\n\nexport { COIN_WITH_BALANCE } from './BalanceIntentNames.js';\nconst SUI_TYPE = normalizeStructTag('0x2::sui::SUI');\n\nexport function coinWithBalance({\n\ttype = SUI_TYPE,\n\tbalance,\n\tuseGasCoin = true,\n}: {\n\tbalance: bigint | number;\n\ttype?: string;\n\tuseGasCoin?: boolean;\n}): (tx: Transaction) => TransactionResult {\n\tlet coinResult: TransactionResult | null = null;\n\n\treturn (tx: Transaction) => {\n\t\tif (coinResult) {\n\t\t\treturn coinResult;\n\t\t}\n\n\t\ttx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);\n\t\tconst coinType = type === 'gas' ? type : normalizeStructTag(type);\n\n\t\tcoinResult = tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: COIN_WITH_BALANCE,\n\t\t\t\tinputs: {},\n\t\t\t\tdata: {\n\t\t\t\t\ttype: coinType === SUI_TYPE && useGasCoin ? 'gas' : coinType,\n\t\t\t\t\tbalance: BigInt(balance),\n\t\t\t\t\toutputKind: 'coin',\n\t\t\t\t} satisfies InferInput<typeof CoinWithBalanceData>,\n\t\t\t}),\n\t\t);\n\n\t\treturn coinResult;\n\t};\n}\n\nexport function createBalance({\n\ttype = SUI_TYPE,\n\tbalance,\n\tuseGasCoin = true,\n}: {\n\tbalance: bigint | number;\n\ttype?: string;\n\tuseGasCoin?: boolean;\n}): (tx: Transaction) => TransactionResult {\n\tlet balanceResult: TransactionResult | null = null;\n\n\treturn (tx: Transaction) => {\n\t\tif (balanceResult) {\n\t\t\treturn balanceResult;\n\t\t}\n\n\t\ttx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);\n\t\tconst coinType = type === 'gas' ? type : normalizeStructTag(type);\n\n\t\tbalanceResult = tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: COIN_WITH_BALANCE,\n\t\t\t\tinputs: {},\n\t\t\t\tdata: {\n\t\t\t\t\ttype: coinType === SUI_TYPE && useGasCoin ? 'gas' : coinType,\n\t\t\t\t\tbalance: BigInt(balance),\n\t\t\t\t\toutputKind: 'balance',\n\t\t\t\t} satisfies InferInput<typeof CoinWithBalanceData>,\n\t\t\t}),\n\t\t);\n\n\t\treturn balanceResult;\n\t};\n}\n\n// `balance` is a bigint in memory, but serializing a transaction to JSON converts it to a\n// string. When that JSON is deserialized with `Transaction.from`, the value stays a string\n// (the intent `data` is opaque to the transaction schema), so coerce it back to a bigint here.\nconst CoinWithBalanceData = object({\n\ttype: string(),\n\tbalance: pipe(\n\t\tunion([bigint(), string(), pipe(number(), integer())]),\n\t\ttransform((value) => BigInt(value)),\n\t),\n\toutputKind: optional(picklist(['coin', 'balance'])),\n});\n\nexport async function resolveCoinBalance(\n\ttransactionData: TransactionDataBuilder,\n\tbuildOptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) {\n\ttype IntentInfo = { balance: bigint; outputKind: 'coin' | 'balance' };\n\n\tconst coinTypes = new Set<string>();\n\tconst totalByType = new Map<string, bigint>();\n\tconst intentsByType = new Map<string, IntentInfo[]>();\n\n\tif (!transactionData.sender) {\n\t\tthrow new Error('Sender must be set to resolve CoinWithBalance');\n\t}\n\n\tif (transactionData.commands.some((command) => command.$Intent?.name === ALLOWANCE_BALANCE)) {\n\t\tthrow new Error(\n\t\t\t'Resolve AllowanceBalance together with CoinWithBalance, or preserve both intents',\n\t\t);\n\t}\n\tconst reservedByType = new Map<string, bigint>();\n\tfor (const input of transactionData.inputs) {\n\t\tif (!input.FundsWithdrawal) continue;\n\t\tconst { withdrawFrom, typeArg, reservation } = input.FundsWithdrawal;\n\t\tconst owner =\n\t\t\twithdrawFrom.SenderAllowance?.funder ??\n\t\t\t(withdrawFrom.Sender\n\t\t\t\t? transactionData.sender\n\t\t\t\t: (transactionData.gasData.owner ?? transactionData.sender));\n\t\tif (normalizeSuiAddress(owner) !== normalizeSuiAddress(transactionData.sender)) continue;\n\t\tconst type = normalizeStructTag(typeArg.Balance);\n\t\treservedByType.set(type, (reservedByType.get(type) ?? 0n) + BigInt(reservation.MaxAmountU64));\n\t}\n\n\t// First pass: scan intents, collect per-type data, and resolve zero-balance intents in place.\n\tfor (const [i, command] of transactionData.commands.entries()) {\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== COIN_WITH_BALANCE) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { type, balance, outputKind } = parse(CoinWithBalanceData, command.$Intent.data);\n\n\t\t// Zero-balance intents are resolved immediately — no coins or AB needed.\n\t\t// This is a 1:1 replacement so indices don't shift.\n\t\tif (balance === 0n) {\n\t\t\tconst coinType = type === 'gas' ? SUI_TYPE : type;\n\t\t\ttransactionData.replaceCommand(\n\t\t\t\ti,\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: (outputKind ?? 'coin') === 'balance' ? '0x2::balance::zero' : '0x2::coin::zero',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (type !== 'gas') {\n\t\t\tcoinTypes.add(type);\n\t\t}\n\n\t\ttotalByType.set(type, (totalByType.get(type) ?? 0n) + balance);\n\n\t\tif (!intentsByType.has(type)) intentsByType.set(type, []);\n\t\tintentsByType.get(type)!.push({ balance, outputKind: outputKind ?? 'coin' });\n\t}\n\n\tif (totalByType.has('gas') && totalByType.has(SUI_TYPE)) {\n\t\tthrow new Error(\n\t\t\t'Cannot mix SUI CoinWithBalance intents that use the gas coin with ones that do not (useGasCoin: false). Use one or the other.',\n\t\t);\n\t}\n\n\tconst usedIds = new Set<string>();\n\n\tfor (const input of transactionData.inputs) {\n\t\tif (input.Object?.ImmOrOwnedObject) {\n\t\t\tusedIds.add(input.Object.ImmOrOwnedObject.objectId);\n\t\t}\n\t\tif (input.UnresolvedObject?.objectId) {\n\t\t\tusedIds.add(input.UnresolvedObject.objectId);\n\t\t}\n\t}\n\n\tconst coinsByType = new Map<string, SuiClientTypes.Coin[]>();\n\tconst addressBalanceByType = new Map<string, bigint>();\n\tconst client = buildOptions.client;\n\tconst assumeSufficientAddressBalances = buildOptions.assumeSufficientAddressBalances;\n\n\tif (!client && !assumeSufficientAddressBalances) {\n\t\tthrow new Error(\n\t\t\t'Client must be provided to build or serialize transactions with CoinWithBalance intents',\n\t\t);\n\t}\n\n\tif (assumeSufficientAddressBalances) {\n\t\tfor (const [coinType, balance] of totalByType) {\n\t\t\taddressBalanceByType.set(coinType, balance);\n\t\t}\n\t} else {\n\t\tawait Promise.all([\n\t\t\t...[...coinTypes].map(async (coinType) => {\n\t\t\t\tconst { coins, addressBalance } = await getCoinsAndBalanceOfType({\n\t\t\t\t\tcoinType,\n\t\t\t\t\tbalance: totalByType.get(coinType)!,\n\t\t\t\t\tclient: client!,\n\t\t\t\t\towner: transactionData.sender!,\n\t\t\t\t\tusedIds,\n\t\t\t\t\treserved: reservedByType.get(coinType) ?? 0n,\n\t\t\t\t});\n\n\t\t\t\tcoinsByType.set(coinType, coins);\n\t\t\t\taddressBalanceByType.set(coinType, addressBalance);\n\t\t\t}),\n\t\t\ttotalByType.has('gas')\n\t\t\t\t? await client!.core\n\t\t\t\t\t\t.getBalance({\n\t\t\t\t\t\t\towner: transactionData.sender!,\n\t\t\t\t\t\t\tcoinType: SUI_TYPE,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then(({ balance }) => {\n\t\t\t\t\t\t\taddressBalanceByType.set(\n\t\t\t\t\t\t\t\t'gas',\n\t\t\t\t\t\t\t\tavailableAddressBalance(balance.addressBalance, reservedByType.get(SUI_TYPE) ?? 0n),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t: null,\n\t\t]);\n\t}\n\n\tconst exactBalanceByType = new Map<string, boolean>();\n\tconst usedAddressBalance = new Set<string>();\n\n\t// Per-type state for Path 2 combined splits\n\ttype TypeState = { results: Argument[]; nextIntent: number };\n\tconst typeState = new Map<string, TypeState>();\n\n\tlet index = 0;\n\twhile (index < transactionData.commands.length) {\n\t\tconst transaction = transactionData.commands[index];\n\t\tif (transaction.$kind !== '$Intent' || transaction.$Intent.name !== COIN_WITH_BALANCE) {\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { type, balance } = parse(CoinWithBalanceData, transaction.$Intent.data);\n\t\tconst coinType = type === 'gas' ? SUI_TYPE : type;\n\t\tconst totalRequired = totalByType.get(type)!;\n\t\tconst addressBalance = addressBalanceByType.get(type) ?? 0n;\n\n\t\tconst commands = [];\n\t\tlet intentResult: Argument;\n\n\t\tconst intentsForType = intentsByType.get(type) ?? [];\n\t\tconst allBalance = intentsForType.every((i) => i.outputKind === 'balance');\n\n\t\tif (allBalance && addressBalance >= totalRequired) {\n\t\t\t// Path 1: All balance intents and AB sufficient — direct per-intent withdrawal.\n\t\t\t// No coins touched, enables parallel execution.\n\t\t\tcommands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: '0x2::balance::redeem_funds',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\targuments: [\n\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\tMaxAmountU64: String(balance),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tintentResult = {\n\t\t\t\t$kind: 'NestedResult',\n\t\t\t\tNestedResult: [index + commands.length - 1, 0],\n\t\t\t};\n\t\t} else {\n\t\t\t// Path 2: Merge and Split — build a merged coin, split all intents at once.\n\n\t\t\tif (!typeState.has(type)) {\n\t\t\t\tconst intents = intentsForType;\n\n\t\t\t\t// Step 1: Build sources and merge\n\t\t\t\tconst sources: Argument[] = [];\n\n\t\t\t\tif (addressBalance >= totalRequired) {\n\t\t\t\t\t// AB sufficient — source entirely from address balance, no coins needed.\n\t\t\t\t\tusedAddressBalance.add(type);\n\n\t\t\t\t\tcommands.push(\n\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\ttarget: '0x2::coin::redeem_funds',\n\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\t\t\tMaxAmountU64: String(totalRequired),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tsources.push({ $kind: 'Result', Result: index + commands.length - 1 });\n\t\t\t\t} else if (type === 'gas') {\n\t\t\t\t\tsources.push({ $kind: 'GasCoin', GasCoin: true });\n\t\t\t\t} else {\n\t\t\t\t\tconst coins = coinsByType.get(type)!;\n\t\t\t\t\tconst loadedCoinBalance = coins.reduce((sum, c) => sum + BigInt(c.balance), 0n);\n\t\t\t\t\tconst abNeeded =\n\t\t\t\t\t\ttotalRequired > loadedCoinBalance ? totalRequired - loadedCoinBalance : 0n;\n\n\t\t\t\t\texactBalanceByType.set(type, loadedCoinBalance + abNeeded === totalRequired);\n\n\t\t\t\t\tfor (const coin of coins) {\n\t\t\t\t\t\tsources.push(\n\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t'object',\n\t\t\t\t\t\t\t\tInputs.ObjectRef({\n\t\t\t\t\t\t\t\t\tobjectId: coin.objectId,\n\t\t\t\t\t\t\t\t\tdigest: coin.digest,\n\t\t\t\t\t\t\t\t\tversion: coin.version,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (abNeeded > 0n) {\n\t\t\t\t\t\tusedAddressBalance.add(type);\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::redeem_funds',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t\t'withdrawal',\n\t\t\t\t\t\t\t\t\t\tInputs.FundsWithdrawal({\n\t\t\t\t\t\t\t\t\t\t\treservation: {\n\t\t\t\t\t\t\t\t\t\t\t\t$kind: 'MaxAmountU64',\n\t\t\t\t\t\t\t\t\t\t\t\tMaxAmountU64: String(abNeeded),\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\ttypeArg: { $kind: 'Balance', Balance: coinType },\n\t\t\t\t\t\t\t\t\t\t\twithdrawFrom: { $kind: 'Sender', Sender: true },\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsources.push({ $kind: 'Result', Result: index + commands.length - 1 });\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst baseCoin = sources[0];\n\t\t\t\tconst rest = sources.slice(1);\n\t\t\t\tfor (let i = 0; i < rest.length; i += 500) {\n\t\t\t\t\tcommands.push(TransactionCommands.MergeCoins(baseCoin, rest.slice(i, i + 500)));\n\t\t\t\t}\n\n\t\t\t\t// Step 2: Combined SplitCoins for all intents of this type\n\t\t\t\tconst splitCmdIndex = index + commands.length;\n\t\t\t\tcommands.push(\n\t\t\t\t\tTransactionCommands.SplitCoins(\n\t\t\t\t\t\tbaseCoin,\n\t\t\t\t\t\tintents.map((i) =>\n\t\t\t\t\t\t\ttransactionData.addInput('pure', Inputs.Pure(bcs.u64().serialize(i.balance))),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\t// Build per-intent results, adding into_balance conversions for balance intents\n\t\t\t\tconst results: Argument[] = [];\n\t\t\t\tfor (let i = 0; i < intents.length; i++) {\n\t\t\t\t\tconst splitResult: Argument = {\n\t\t\t\t\t\t$kind: 'NestedResult',\n\t\t\t\t\t\tNestedResult: [splitCmdIndex, i],\n\t\t\t\t\t};\n\n\t\t\t\t\tif (intents[i].outputKind === 'balance') {\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::into_balance',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [splitResult],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresults.push({\n\t\t\t\t\t\t\t$kind: 'NestedResult',\n\t\t\t\t\t\t\tNestedResult: [index + commands.length - 1, 0],\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.push(splitResult);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Step 3: Remainder handling\n\t\t\t\t//\n\t\t\t\t// Add cleanup to this replacement command list rather than appending\n\t\t\t\t// it to the complete transaction. Appending cleanup can place it after\n\t\t\t\t// a MoveCall that consumes Random, which Sui rejects.\n\t\t\t\t//\n\t\t\t\t// When gas type used GasCoin (not AB), leftover stays in the gas coin\n\t\t\t\t// -- no remainder handling is needed.\n\t\t\t\tif (type !== 'gas' || usedAddressBalance.has(type)) {\n\t\t\t\t\tconst hasBalanceIntent = intents.some((intent) => intent.outputKind === 'balance');\n\t\t\t\t\tconst sourcedFromAB = usedAddressBalance.has(type);\n\n\t\t\t\t\tif (hasBalanceIntent || sourcedFromAB) {\n\t\t\t\t\t\t// Sourced from AB or balance intents exist: send remainder back to sender's address\n\t\t\t\t\t\t// balance. `coin::send_funds` is gasless-eligible and handles zero amounts.\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::send_funds',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [\n\t\t\t\t\t\t\t\t\tbaseCoin,\n\t\t\t\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t\t\t\t'pure',\n\t\t\t\t\t\t\t\t\t\tInputs.Pure(bcs.Address.serialize(transactionData.sender!)),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (exactBalanceByType.get(type)) {\n\t\t\t\t\t\t// Coin-only with exact match: destroy the zero-value dust coin.\n\t\t\t\t\t\tcommands.push(\n\t\t\t\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\t\t\t\ttarget: '0x2::coin::destroy_zero',\n\t\t\t\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\t\t\t\targuments: [baseCoin],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Coin-only with surplus: merged coin stays with sender as an owned object.\n\t\t\t\t}\n\n\t\t\t\ttypeState.set(type, { results, nextIntent: 0 });\n\t\t\t}\n\n\t\t\tconst state = typeState.get(type)!;\n\t\t\tintentResult = state.results[state.nextIntent++];\n\t\t}\n\n\t\ttransactionData.replaceCommand(\n\t\t\tindex,\n\t\t\tcommands,\n\t\t\tintentResult as { NestedResult: [number, number] },\n\t\t);\n\n\t\t// Advance past the replacement. When commands is empty (subsequent intents\n\t\t// of a combined split), the command was removed and the next command shifted\n\t\t// into this position — so we stay at the same index.\n\t\tindex += commands.length;\n\t}\n\n\treturn next();\n}\n\nasync function getCoinsAndBalanceOfType({\n\tcoinType,\n\tbalance,\n\tclient,\n\towner,\n\tusedIds,\n\treserved,\n}: {\n\tcoinType: string;\n\tbalance: bigint;\n\tclient: ClientWithCoreApi;\n\towner: string;\n\tusedIds: Set<string>;\n\treserved: bigint;\n}): Promise<{\n\tcoins: SuiClientTypes.Coin[];\n\tbalance: bigint;\n\taddressBalance: bigint;\n\tcoinBalance: bigint;\n}> {\n\tlet remainingBalance = balance;\n\tconst coins: SuiClientTypes.Coin[] = [];\n\tconst balanceRequest = client.core.getBalance({ owner, coinType }).then(({ balance }) => {\n\t\tconst addressBalance = availableAddressBalance(balance.addressBalance, reserved);\n\t\tremainingBalance -= addressBalance;\n\n\t\treturn {\n\t\t\t...balance,\n\t\t\taddressBalance: String(addressBalance),\n\t\t\tbalance: String(BigInt(balance.balance) - reserved),\n\t\t};\n\t});\n\n\tconst [allCoins, balanceResponse] = await Promise.all([loadMoreCoins(), balanceRequest]);\n\n\tif (BigInt(balanceResponse.balance) < balance) {\n\t\tthrow new Error(\n\t\t\t`Insufficient balance of ${coinType} for owner ${owner}. Required: ${balance}, Available: ${\n\t\t\t\tbalance - remainingBalance\n\t\t\t}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tcoins: allCoins,\n\t\tbalance: BigInt(balanceResponse.coinBalance),\n\t\taddressBalance: BigInt(balanceResponse.addressBalance),\n\t\tcoinBalance: BigInt(balanceResponse.coinBalance),\n\t};\n\n\tasync function loadMoreCoins(cursor: string | null = null): Promise<SuiClientTypes.Coin[]> {\n\t\tconst {\n\t\t\tobjects,\n\t\t\thasNextPage,\n\t\t\tcursor: nextCursor,\n\t\t} = await client.core.listCoins({\n\t\t\towner,\n\t\t\tcoinType,\n\t\t\tcursor,\n\t\t});\n\n\t\tawait balanceRequest;\n\n\t\t// Always load all coins from the page (except already-used ones).\n\t\t// This merges all available coins rather than leaving dust.\n\t\tfor (const coin of objects) {\n\t\t\tif (usedIds.has(coin.objectId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcoins.push(coin);\n\t\t\tremainingBalance -= BigInt(coin.balance);\n\t\t}\n\n\t\t// Only paginate if loaded coins + AB are still insufficient\n\t\tif (remainingBalance > 0n && hasNextPage) {\n\t\t\treturn loadMoreCoins(nextCursor);\n\t\t}\n\n\t\treturn coins;\n\t}\n}\n\nfunction availableAddressBalance(balance: string, reserved: bigint): bigint {\n\tconst available = BigInt(balance) - reserved;\n\tif (available < 0n) {\n\t\tthrow new Error(\n\t\t\t`Insufficient address balance for existing withdrawals. Required: ${reserved}, Available: ${balance}`,\n\t\t);\n\t}\n\treturn available;\n}\n"],"mappings":";;;;;;;;;AAgCA,MAAM,WAAW,mBAAmB,gBAAgB;AAEpD,SAAgB,gBAAgB,EAC/B,OAAO,UACP,SACA,aAAa,QAK6B;CAC1C,IAAI,aAAuC;AAE3C,SAAQ,OAAoB;AAC3B,MAAI,WACH,QAAO;AAGR,KAAG,kBAAkB,mBAAmB,gBAAgB;EACxD,MAAM,WAAW,SAAS,QAAQ,OAAO,mBAAmB,KAAK;AAEjE,eAAa,GAAG,IACf,oBAAoB,OAAO;GAC1B,MAAM;GACN,QAAQ,EAAE;GACV,MAAM;IACL,MAAM,aAAa,YAAY,aAAa,QAAQ;IACpD,SAAS,OAAO,QAAQ;IACxB,YAAY;IACZ;GACD,CAAC,CACF;AAED,SAAO;;;AAIT,SAAgB,cAAc,EAC7B,OAAO,UACP,SACA,aAAa,QAK6B;CAC1C,IAAI,gBAA0C;AAE9C,SAAQ,OAAoB;AAC3B,MAAI,cACH,QAAO;AAGR,KAAG,kBAAkB,mBAAmB,gBAAgB;EACxD,MAAM,WAAW,SAAS,QAAQ,OAAO,mBAAmB,KAAK;AAEjE,kBAAgB,GAAG,IAClB,oBAAoB,OAAO;GAC1B,MAAM;GACN,QAAQ,EAAE;GACV,MAAM;IACL,MAAM,aAAa,YAAY,aAAa,QAAQ;IACpD,SAAS,OAAO,QAAQ;IACxB,YAAY;IACZ;GACD,CAAC,CACF;AAED,SAAO;;;AAOT,MAAM,sBAAsB,OAAO;CAClC,MAAM,QAAQ;CACd,SAAS,KACR,MAAM;EAAC,QAAQ;EAAE,QAAQ;EAAE,KAAK,QAAQ,EAAE,SAAS,CAAC;EAAC,CAAC,EACtD,WAAW,UAAU,OAAO,MAAM,CAAC,CACnC;CACD,YAAY,SAAS,SAAS,CAAC,QAAQ,UAAU,CAAC,CAAC;CACnD,CAAC;AAEF,eAAsB,mBACrB,iBACA,cACA,MACC;CAGD,MAAM,4BAAY,IAAI,KAAa;CACnC,MAAM,8BAAc,IAAI,KAAqB;CAC7C,MAAM,gCAAgB,IAAI,KAA2B;AAErD,KAAI,CAAC,gBAAgB,OACpB,OAAM,IAAI,MAAM,gDAAgD;AAGjE,KAAI,gBAAgB,SAAS,MAAM,YAAY,QAAQ,SAAS,SAAS,kBAAkB,CAC1F,OAAM,IAAI,MACT,mFACA;CAEF,MAAM,iCAAiB,IAAI,KAAqB;AAChD,MAAK,MAAM,SAAS,gBAAgB,QAAQ;AAC3C,MAAI,CAAC,MAAM,gBAAiB;EAC5B,MAAM,EAAE,cAAc,SAAS,gBAAgB,MAAM;AAMrD,MAAI,oBAJH,aAAa,iBAAiB,WAC7B,aAAa,SACX,gBAAgB,SACf,gBAAgB,QAAQ,SAAS,gBAAgB,QACxB,KAAK,oBAAoB,gBAAgB,OAAO,CAAE;EAChF,MAAM,OAAO,mBAAmB,QAAQ,QAAQ;AAChD,iBAAe,IAAI,OAAO,eAAe,IAAI,KAAK,IAAI,MAAM,OAAO,YAAY,aAAa,CAAC;;AAI9F,MAAK,MAAM,CAAC,GAAG,YAAY,gBAAgB,SAAS,SAAS,EAAE;AAC9D,MAAI,QAAQ,UAAU,aAAa,QAAQ,QAAQ,SAAS,kBAC3D;EAGD,MAAM,EAAE,MAAM,SAAS,eAAe,MAAM,qBAAqB,QAAQ,QAAQ,KAAK;AAItF,MAAI,YAAY,IAAI;GACnB,MAAM,WAAW,SAAS,QAAQ,WAAW;AAC7C,mBAAgB,eACf,GACA,oBAAoB,SAAS;IAC5B,SAAS,cAAc,YAAY,YAAY,uBAAuB;IACtE,eAAe,CAAC,SAAS;IACzB,CAAC,CACF;AACD;;AAGD,MAAI,SAAS,MACZ,WAAU,IAAI,KAAK;AAGpB,cAAY,IAAI,OAAO,YAAY,IAAI,KAAK,IAAI,MAAM,QAAQ;AAE9D,MAAI,CAAC,cAAc,IAAI,KAAK,CAAE,eAAc,IAAI,MAAM,EAAE,CAAC;AACzD,gBAAc,IAAI,KAAK,CAAE,KAAK;GAAE;GAAS,YAAY,cAAc;GAAQ,CAAC;;AAG7E,KAAI,YAAY,IAAI,MAAM,IAAI,YAAY,IAAI,SAAS,CACtD,OAAM,IAAI,MACT,gIACA;CAGF,MAAM,0BAAU,IAAI,KAAa;AAEjC,MAAK,MAAM,SAAS,gBAAgB,QAAQ;AAC3C,MAAI,MAAM,QAAQ,iBACjB,SAAQ,IAAI,MAAM,OAAO,iBAAiB,SAAS;AAEpD,MAAI,MAAM,kBAAkB,SAC3B,SAAQ,IAAI,MAAM,iBAAiB,SAAS;;CAI9C,MAAM,8BAAc,IAAI,KAAoC;CAC5D,MAAM,uCAAuB,IAAI,KAAqB;CACtD,MAAM,SAAS,aAAa;CAC5B,MAAM,kCAAkC,aAAa;AAErD,KAAI,CAAC,UAAU,CAAC,gCACf,OAAM,IAAI,MACT,0FACA;AAGF,KAAI,gCACH,MAAK,MAAM,CAAC,UAAU,YAAY,YACjC,sBAAqB,IAAI,UAAU,QAAQ;KAG5C,OAAM,QAAQ,IAAI,CACjB,GAAG,CAAC,GAAG,UAAU,CAAC,IAAI,OAAO,aAAa;EACzC,MAAM,EAAE,OAAO,mBAAmB,MAAM,yBAAyB;GAChE;GACA,SAAS,YAAY,IAAI,SAAS;GAC1B;GACR,OAAO,gBAAgB;GACvB;GACA,UAAU,eAAe,IAAI,SAAS,IAAI;GAC1C,CAAC;AAEF,cAAY,IAAI,UAAU,MAAM;AAChC,uBAAqB,IAAI,UAAU,eAAe;GACjD,EACF,YAAY,IAAI,MAAM,GACnB,MAAM,OAAQ,KACb,WAAW;EACX,OAAO,gBAAgB;EACvB,UAAU;EACV,CAAC,CACD,MAAM,EAAE,cAAc;AACtB,uBAAqB,IACpB,OACA,wBAAwB,QAAQ,gBAAgB,eAAe,IAAI,SAAS,IAAI,GAAG,CACnF;GACA,GACF,KACH,CAAC;CAGH,MAAM,qCAAqB,IAAI,KAAsB;CACrD,MAAM,qCAAqB,IAAI,KAAa;CAI5C,MAAM,4BAAY,IAAI,KAAwB;CAE9C,IAAI,QAAQ;AACZ,QAAO,QAAQ,gBAAgB,SAAS,QAAQ;EAC/C,MAAM,cAAc,gBAAgB,SAAS;AAC7C,MAAI,YAAY,UAAU,aAAa,YAAY,QAAQ,SAAS,mBAAmB;AACtF;AACA;;EAGD,MAAM,EAAE,MAAM,YAAY,MAAM,qBAAqB,YAAY,QAAQ,KAAK;EAC9E,MAAM,WAAW,SAAS,QAAQ,WAAW;EAC7C,MAAM,gBAAgB,YAAY,IAAI,KAAK;EAC3C,MAAM,iBAAiB,qBAAqB,IAAI,KAAK,IAAI;EAEzD,MAAM,WAAW,EAAE;EACnB,IAAI;EAEJ,MAAM,iBAAiB,cAAc,IAAI,KAAK,IAAI,EAAE;AAGpD,MAFmB,eAAe,OAAO,MAAM,EAAE,eAAe,UAAU,IAExD,kBAAkB,eAAe;AAGlD,YAAS,KACR,oBAAoB,SAAS;IAC5B,QAAQ;IACR,eAAe,CAAC,SAAS;IACzB,WAAW,CACV,gBAAgB,SACf,cACA,OAAO,gBAAgB;KACtB,aAAa;MACZ,OAAO;MACP,cAAc,OAAO,QAAQ;MAC7B;KACD,SAAS;MAAE,OAAO;MAAW,SAAS;MAAU;KAChD,cAAc;MAAE,OAAO;MAAU,QAAQ;MAAM;KAC/C,CAAC,CACF,CACD;IACD,CAAC,CACF;AAED,kBAAe;IACd,OAAO;IACP,cAAc,CAAC,QAAQ,SAAS,SAAS,GAAG,EAAE;IAC9C;SACK;AAGN,OAAI,CAAC,UAAU,IAAI,KAAK,EAAE;IACzB,MAAM,UAAU;IAGhB,MAAM,UAAsB,EAAE;AAE9B,QAAI,kBAAkB,eAAe;AAEpC,wBAAmB,IAAI,KAAK;AAE5B,cAAS,KACR,oBAAoB,SAAS;MAC5B,QAAQ;MACR,eAAe,CAAC,SAAS;MACzB,WAAW,CACV,gBAAgB,SACf,cACA,OAAO,gBAAgB;OACtB,aAAa;QACZ,OAAO;QACP,cAAc,OAAO,cAAc;QACnC;OACD,SAAS;QAAE,OAAO;QAAW,SAAS;QAAU;OAChD,cAAc;QAAE,OAAO;QAAU,QAAQ;QAAM;OAC/C,CAAC,CACF,CACD;MACD,CAAC,CACF;AACD,aAAQ,KAAK;MAAE,OAAO;MAAU,QAAQ,QAAQ,SAAS,SAAS;MAAG,CAAC;eAC5D,SAAS,MACnB,SAAQ,KAAK;KAAE,OAAO;KAAW,SAAS;KAAM,CAAC;SAC3C;KACN,MAAM,QAAQ,YAAY,IAAI,KAAK;KACnC,MAAM,oBAAoB,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO,EAAE,QAAQ,EAAE,GAAG;KAC/E,MAAM,WACL,gBAAgB,oBAAoB,gBAAgB,oBAAoB;AAEzE,wBAAmB,IAAI,MAAM,oBAAoB,aAAa,cAAc;AAE5E,UAAK,MAAM,QAAQ,MAClB,SAAQ,KACP,gBAAgB,SACf,UACA,OAAO,UAAU;MAChB,UAAU,KAAK;MACf,QAAQ,KAAK;MACb,SAAS,KAAK;MACd,CAAC,CACF,CACD;AAGF,SAAI,WAAW,IAAI;AAClB,yBAAmB,IAAI,KAAK;AAC5B,eAAS,KACR,oBAAoB,SAAS;OAC5B,QAAQ;OACR,eAAe,CAAC,SAAS;OACzB,WAAW,CACV,gBAAgB,SACf,cACA,OAAO,gBAAgB;QACtB,aAAa;SACZ,OAAO;SACP,cAAc,OAAO,SAAS;SAC9B;QACD,SAAS;SAAE,OAAO;SAAW,SAAS;SAAU;QAChD,cAAc;SAAE,OAAO;SAAU,QAAQ;SAAM;QAC/C,CAAC,CACF,CACD;OACD,CAAC,CACF;AACD,cAAQ,KAAK;OAAE,OAAO;OAAU,QAAQ,QAAQ,SAAS,SAAS;OAAG,CAAC;;;IAIxE,MAAM,WAAW,QAAQ;IACzB,MAAM,OAAO,QAAQ,MAAM,EAAE;AAC7B,SAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,IACrC,UAAS,KAAK,oBAAoB,WAAW,UAAU,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC;IAIhF,MAAM,gBAAgB,QAAQ,SAAS;AACvC,aAAS,KACR,oBAAoB,WACnB,UACA,QAAQ,KAAK,MACZ,gBAAgB,SAAS,QAAQ,OAAO,KAAKA,OAAI,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAC7E,CACD,CACD;IAGD,MAAM,UAAsB,EAAE;AAC9B,SAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;KACxC,MAAM,cAAwB;MAC7B,OAAO;MACP,cAAc,CAAC,eAAe,EAAE;MAChC;AAED,SAAI,QAAQ,GAAG,eAAe,WAAW;AACxC,eAAS,KACR,oBAAoB,SAAS;OAC5B,QAAQ;OACR,eAAe,CAAC,SAAS;OACzB,WAAW,CAAC,YAAY;OACxB,CAAC,CACF;AACD,cAAQ,KAAK;OACZ,OAAO;OACP,cAAc,CAAC,QAAQ,SAAS,SAAS,GAAG,EAAE;OAC9C,CAAC;WAEF,SAAQ,KAAK,YAAY;;AAY3B,QAAI,SAAS,SAAS,mBAAmB,IAAI,KAAK,EAAE;KACnD,MAAM,mBAAmB,QAAQ,MAAM,WAAW,OAAO,eAAe,UAAU;KAClF,MAAM,gBAAgB,mBAAmB,IAAI,KAAK;AAElD,SAAI,oBAAoB,cAGvB,UAAS,KACR,oBAAoB,SAAS;MAC5B,QAAQ;MACR,eAAe,CAAC,SAAS;MACzB,WAAW,CACV,UACA,gBAAgB,SACf,QACA,OAAO,KAAKA,OAAI,QAAQ,UAAU,gBAAgB,OAAQ,CAAC,CAC3D,CACD;MACD,CAAC,CACF;cACS,mBAAmB,IAAI,KAAK,CAEtC,UAAS,KACR,oBAAoB,SAAS;MAC5B,QAAQ;MACR,eAAe,CAAC,SAAS;MACzB,WAAW,CAAC,SAAS;MACrB,CAAC,CACF;;AAKH,cAAU,IAAI,MAAM;KAAE;KAAS,YAAY;KAAG,CAAC;;GAGhD,MAAM,QAAQ,UAAU,IAAI,KAAK;AACjC,kBAAe,MAAM,QAAQ,MAAM;;AAGpC,kBAAgB,eACf,OACA,UACA,aACA;AAKD,WAAS,SAAS;;AAGnB,QAAO,MAAM;;AAGd,eAAe,yBAAyB,EACvC,UACA,SACA,QACA,OACA,SACA,YAaE;CACF,IAAI,mBAAmB;CACvB,MAAM,QAA+B,EAAE;CACvC,MAAM,iBAAiB,OAAO,KAAK,WAAW;EAAE;EAAO;EAAU,CAAC,CAAC,MAAM,EAAE,yBAAc;EACxF,MAAM,iBAAiB,wBAAwBC,UAAQ,gBAAgB,SAAS;AAChF,sBAAoB;AAEpB,SAAO;GACN,GAAGA;GACH,gBAAgB,OAAO,eAAe;GACtC,SAAS,OAAO,OAAOA,UAAQ,QAAQ,GAAG,SAAS;GACnD;GACA;CAEF,MAAM,CAAC,UAAU,mBAAmB,MAAM,QAAQ,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC;AAExF,KAAI,OAAO,gBAAgB,QAAQ,GAAG,QACrC,OAAM,IAAI,MACT,2BAA2B,SAAS,aAAa,MAAM,cAAc,QAAQ,eAC5E,UAAU,mBAEX;AAGF,QAAO;EACN,OAAO;EACP,SAAS,OAAO,gBAAgB,YAAY;EAC5C,gBAAgB,OAAO,gBAAgB,eAAe;EACtD,aAAa,OAAO,gBAAgB,YAAY;EAChD;CAED,eAAe,cAAc,SAAwB,MAAsC;EAC1F,MAAM,EACL,SACA,aACA,QAAQ,eACL,MAAM,OAAO,KAAK,UAAU;GAC/B;GACA;GACA;GACA,CAAC;AAEF,QAAM;AAIN,OAAK,MAAM,QAAQ,SAAS;AAC3B,OAAI,QAAQ,IAAI,KAAK,SAAS,CAC7B;AAGD,SAAM,KAAK,KAAK;AAChB,uBAAoB,OAAO,KAAK,QAAQ;;AAIzC,MAAI,mBAAmB,MAAM,YAC5B,QAAO,cAAc,WAAW;AAGjC,SAAO;;;AAIT,SAAS,wBAAwB,SAAiB,UAA0B;CAC3E,MAAM,YAAY,OAAO,QAAQ,GAAG;AACpC,KAAI,YAAY,GACf,OAAM,IAAI,MACT,oEAAoE,SAAS,eAAe,UAC5F;AAEF,QAAO"}
@@ -0,0 +1,16 @@
1
+ import { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from "./BalanceIntentNames.mjs";
2
+ import { resolveCoinBalance } from "./CoinWithBalance.mjs";
3
+ import { resolveAllowanceBalance } from "./AllowanceBalance.mjs";
4
+
5
+ //#region src/transactions/intents/ResolveBalances.ts
6
+ async function resolveBalances(transactionData, options, next) {
7
+ const intents = new Set(options.intentNames ?? [ALLOWANCE_BALANCE, COIN_WITH_BALANCE]);
8
+ const shouldResolve = (name) => intents.has(name) && !options.supportedIntents?.includes(name) && transactionData.commands.some((command) => command.$Intent?.name === name);
9
+ if (shouldResolve(ALLOWANCE_BALANCE)) await resolveAllowanceBalance(transactionData, options, async () => {});
10
+ if (shouldResolve(COIN_WITH_BALANCE)) await resolveCoinBalance(transactionData, options, async () => {});
11
+ await next();
12
+ }
13
+
14
+ //#endregion
15
+ export { resolveBalances };
16
+ //# sourceMappingURL=ResolveBalances.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResolveBalances.mjs","names":[],"sources":["../../../src/transactions/intents/ResolveBalances.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { IntentResolverOptions } from '../resolve.js';\nimport type { TransactionDataBuilder } from '../TransactionData.js';\nimport { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from './BalanceIntentNames.js';\nimport { resolveAllowanceBalance } from './AllowanceBalance.js';\nimport { resolveCoinBalance } from './CoinWithBalance.js';\n\nexport async function resolveBalances(\n\ttransactionData: TransactionDataBuilder,\n\toptions: IntentResolverOptions,\n\tnext: () => Promise<void>,\n) {\n\tconst intents = new Set(options.intentNames ?? [ALLOWANCE_BALANCE, COIN_WITH_BALANCE]);\n\tconst shouldResolve = (name: string) =>\n\t\tintents.has(name) &&\n\t\t!options.supportedIntents?.includes(name) &&\n\t\ttransactionData.commands.some((command) => command.$Intent?.name === name);\n\n\t// Reserve allowance withdrawals before ordinary coin selection, including when\n\t// the sender is also the funder. Keep separate intents for wallet negotiation.\n\tif (shouldResolve(ALLOWANCE_BALANCE)) {\n\t\tawait resolveAllowanceBalance(transactionData, options, async () => {});\n\t}\n\tif (shouldResolve(COIN_WITH_BALANCE)) {\n\t\tawait resolveCoinBalance(transactionData, options, async () => {});\n\t}\n\tawait next();\n}\n"],"mappings":";;;;;AASA,eAAsB,gBACrB,iBACA,SACA,MACC;CACD,MAAM,UAAU,IAAI,IAAI,QAAQ,eAAe,CAAC,mBAAmB,kBAAkB,CAAC;CACtF,MAAM,iBAAiB,SACtB,QAAQ,IAAI,KAAK,IACjB,CAAC,QAAQ,kBAAkB,SAAS,KAAK,IACzC,gBAAgB,SAAS,MAAM,YAAY,QAAQ,SAAS,SAAS,KAAK;AAI3E,KAAI,cAAc,kBAAkB,CACnC,OAAM,wBAAwB,iBAAiB,SAAS,YAAY,GAAG;AAExE,KAAI,cAAc,kBAAkB,CACnC,OAAM,mBAAmB,iBAAiB,SAAS,YAAY,GAAG;AAEnE,OAAM,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.mts","names":[],"sources":["../../src/transactions/resolve.ts"],"mappings":";;;;;UAaiB,uBAAA;EAChB,MAAA,GAAS,iBAAA;EACT,mBAAA;EAFgB;;;;;;;EAUhB,+BAAA;AAAA;AAAA,UAGgB,2BAAA,SAAoC,uBAAA;EACpD,gBAAA;AAAA;AAAA,KAGW,iBAAA,IACX,eAAA,EAAiB,sBAAA,EACjB,OAAA,EAAS,uBAAA,EACT,IAAA,QAAY,OAAA,WACR,OAAA"}
1
+ {"version":3,"file":"resolve.d.mts","names":[],"sources":["../../src/transactions/resolve.ts"],"mappings":";;;;;UAaiB,uBAAA;EAChB,MAAA,GAAS,iBAAA;EACT,mBAAA;EAFgB;;;;;;;EAUhB,+BAAA;AAAA;AAAA,UAGgB,2BAAA,SAAoC,uBAAA;EACpD,gBAAA;AAAA;AAAA,KASW,iBAAA,IACX,eAAA,EAAiB,sBAAA,EACjB,OAAA,EAAS,uBAAA,EACT,IAAA,QAAY,OAAA,WACR,OAAA"}
@@ -57,5 +57,5 @@ function normalizeRawArgument(arg, schema, transactionData) {
57
57
  }
58
58
 
59
59
  //#endregion
60
- export { needsTransactionResolution, resolveTransactionPlugin };
60
+ export { getClient, needsTransactionResolution, resolveTransactionPlugin };
61
61
  //# sourceMappingURL=resolve.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.mjs","names":["bcs"],"sources":["../../src/transactions/resolve.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Argument } from './data/internal.js';\n\nimport type { ClientWithCoreApi } from '../client/index.js';\nimport type { TransactionDataBuilder } from './TransactionData.js';\nimport type { BcsType } from '@mysten/bcs';\nimport { Inputs } from './Inputs.js';\nimport { bcs } from '../bcs/index.js';\nimport { coreClientResolveTransactionPlugin } from '../client/core-resolver.js';\nimport { transactionUsesGasCoin } from './resolution-utils.js';\n\nexport interface BuildTransactionOptions {\n\tclient?: ClientWithCoreApi;\n\tonlyTransactionKind?: boolean;\n\t/**\n\t * Resolve `CoinWithBalance` intents from address balance without looking up balances or coins.\n\t *\n\t * If nothing else needs resolution, the transaction doesn't use `GasCoin`, and it already has a\n\t * `ValidDuring` or `Validity` expiration, an unset gas payment is also set to `[]` (pay gas from\n\t * address balance). Execution fails if the balances aren't there.\n\t */\n\tassumeSufficientAddressBalances?: boolean;\n}\n\nexport interface SerializeTransactionOptions extends BuildTransactionOptions {\n\tsupportedIntents?: string[];\n}\n\nexport type TransactionPlugin = (\n\ttransactionData: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) => Promise<void>;\n\nexport function needsTransactionResolution(\n\tdata: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n): boolean {\n\tif (\n\t\tdata.inputs.some((input) => {\n\t\t\treturn input.UnresolvedObject || input.UnresolvedPure;\n\t\t})\n\t) {\n\t\treturn true;\n\t}\n\n\tif (!options.onlyTransactionKind) {\n\t\tif (!data.gasData.price || !data.gasData.budget) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!data.gasData.payment) {\n\t\t\tconst assumesAddressBalanceGas =\n\t\t\t\toptions.assumeSufficientAddressBalances && !transactionUsesGasCoin(data);\n\n\t\t\t// Address balance gas has no object version to protect against replay, so an offline build\n\t\t\t// needs a ValidDuring expiration. Epoch expiration doesn't count.\n\t\t\tif (\n\t\t\t\t!assumesAddressBalanceGas ||\n\t\t\t\t(data.expiration?.$kind !== 'ValidDuring' && data.expiration?.$kind !== 'Validity')\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (data.gasData.payment.length === 0 && !data.expiration) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nexport async function resolveTransactionPlugin(\n\ttransactionData: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) {\n\tnormalizeRawArguments(transactionData);\n\n\tif (!needsTransactionResolution(transactionData, options)) {\n\t\t// Payment can only be unset here when assumeSufficientAddressBalances applies\n\t\tif (!options.onlyTransactionKind && !transactionData.gasData.payment) {\n\t\t\ttransactionData.gasData.payment = [];\n\t\t}\n\n\t\tawait validate(transactionData);\n\t\treturn next();\n\t}\n\n\tconst client = getClient(options);\n\tconst plugin = client.core?.resolveTransactionPlugin() ?? coreClientResolveTransactionPlugin;\n\n\treturn plugin(transactionData, options, async () => {\n\t\tawait validate(transactionData);\n\t\tawait next();\n\t});\n}\n\nfunction validate(transactionData: TransactionDataBuilder) {\n\ttransactionData.inputs.forEach((input, index) => {\n\t\tif (input.$kind !== 'Object' && input.$kind !== 'Pure' && input.$kind !== 'FundsWithdrawal') {\n\t\t\tthrow new Error(\n\t\t\t\t`Input at index ${index} has not been resolved. Expected a Pure, Object, or FundsWithdrawal input, but found ${JSON.stringify(\n\t\t\t\t\tinput,\n\t\t\t\t)}`,\n\t\t\t);\n\t\t}\n\t});\n}\n\nexport function getClient(options: BuildTransactionOptions) {\n\tif (!options.client) {\n\t\tthrow new Error(\n\t\t\t`No sui client passed to Transaction#build, but transaction data was not sufficient to build offline.`,\n\t\t);\n\t}\n\n\treturn options.client;\n}\n\nfunction normalizeRawArguments(transactionData: TransactionDataBuilder) {\n\tfor (const command of transactionData.commands) {\n\t\tswitch (command.$kind) {\n\t\t\tcase 'SplitCoins':\n\t\t\t\tcommand.SplitCoins.amounts.forEach((amount) => {\n\t\t\t\t\tnormalizeRawArgument(amount, bcs.U64, transactionData);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase 'TransferObjects':\n\t\t\t\tnormalizeRawArgument(command.TransferObjects.address, bcs.Address, transactionData);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction normalizeRawArgument(\n\targ: Argument,\n\tschema: BcsType<any>,\n\ttransactionData: TransactionDataBuilder,\n) {\n\tif (arg.$kind !== 'Input') {\n\t\treturn;\n\t}\n\tconst input = transactionData.inputs[arg.Input];\n\n\tif (input.$kind !== 'UnresolvedPure') {\n\t\treturn;\n\t}\n\n\ttransactionData.inputs[arg.Input] = Inputs.Pure(schema.serialize(input.UnresolvedPure.value));\n}\n"],"mappings":";;;;;;AAoCA,SAAgB,2BACf,MACA,SACU;AACV,KACC,KAAK,OAAO,MAAM,UAAU;AAC3B,SAAO,MAAM,oBAAoB,MAAM;GACtC,CAEF,QAAO;AAGR,KAAI,CAAC,QAAQ,qBAAqB;AACjC,MAAI,CAAC,KAAK,QAAQ,SAAS,CAAC,KAAK,QAAQ,OACxC,QAAO;AAGR,MAAI,CAAC,KAAK,QAAQ,SAMjB;OACC,EALA,QAAQ,mCAAmC,CAAC,uBAAuB,KAAK,KAMvE,KAAK,YAAY,UAAU,iBAAiB,KAAK,YAAY,UAAU,WAExE,QAAO;aAEE,KAAK,QAAQ,QAAQ,WAAW,KAAK,CAAC,KAAK,WACrD,QAAO;;AAIT,QAAO;;AAGR,eAAsB,yBACrB,iBACA,SACA,MACC;AACD,uBAAsB,gBAAgB;AAEtC,KAAI,CAAC,2BAA2B,iBAAiB,QAAQ,EAAE;AAE1D,MAAI,CAAC,QAAQ,uBAAuB,CAAC,gBAAgB,QAAQ,QAC5D,iBAAgB,QAAQ,UAAU,EAAE;AAGrC,QAAM,SAAS,gBAAgB;AAC/B,SAAO,MAAM;;AAMd,SAHe,UAAU,QAAQ,CACX,MAAM,0BAA0B,IAAI,oCAE5C,iBAAiB,SAAS,YAAY;AACnD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,MAAM;GACX;;AAGH,SAAS,SAAS,iBAAyC;AAC1D,iBAAgB,OAAO,SAAS,OAAO,UAAU;AAChD,MAAI,MAAM,UAAU,YAAY,MAAM,UAAU,UAAU,MAAM,UAAU,kBACzE,OAAM,IAAI,MACT,kBAAkB,MAAM,wFAAwF,KAAK,UACpH,MACA,GACD;GAED;;AAGH,SAAgB,UAAU,SAAkC;AAC3D,KAAI,CAAC,QAAQ,OACZ,OAAM,IAAI,MACT,uGACA;AAGF,QAAO,QAAQ;;AAGhB,SAAS,sBAAsB,iBAAyC;AACvE,MAAK,MAAM,WAAW,gBAAgB,SACrC,SAAQ,QAAQ,OAAhB;EACC,KAAK;AACJ,WAAQ,WAAW,QAAQ,SAAS,WAAW;AAC9C,yBAAqB,QAAQA,OAAI,KAAK,gBAAgB;KACrD;AACF;EACD,KAAK;AACJ,wBAAqB,QAAQ,gBAAgB,SAASA,OAAI,SAAS,gBAAgB;AACnF;;;AAKJ,SAAS,qBACR,KACA,QACA,iBACC;AACD,KAAI,IAAI,UAAU,QACjB;CAED,MAAM,QAAQ,gBAAgB,OAAO,IAAI;AAEzC,KAAI,MAAM,UAAU,iBACnB;AAGD,iBAAgB,OAAO,IAAI,SAAS,OAAO,KAAK,OAAO,UAAU,MAAM,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"resolve.mjs","names":["bcs"],"sources":["../../src/transactions/resolve.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Argument } from './data/internal.js';\n\nimport type { ClientWithCoreApi } from '../client/index.js';\nimport type { TransactionDataBuilder } from './TransactionData.js';\nimport type { BcsType } from '@mysten/bcs';\nimport { Inputs } from './Inputs.js';\nimport { bcs } from '../bcs/index.js';\nimport { coreClientResolveTransactionPlugin } from '../client/core-resolver.js';\nimport { transactionUsesGasCoin } from './resolution-utils.js';\n\nexport interface BuildTransactionOptions {\n\tclient?: ClientWithCoreApi;\n\tonlyTransactionKind?: boolean;\n\t/**\n\t * Resolve `CoinWithBalance` intents from address balance without looking up balances or coins.\n\t *\n\t * If nothing else needs resolution, the transaction doesn't use `GasCoin`, and it already has a\n\t * `ValidDuring` or `Validity` expiration, an unset gas payment is also set to `[]` (pay gas from\n\t * address balance). Execution fails if the balances aren't there.\n\t */\n\tassumeSufficientAddressBalances?: boolean;\n}\n\nexport interface SerializeTransactionOptions extends BuildTransactionOptions {\n\tsupportedIntents?: string[];\n}\n\n/** Options supplied when a registered intent resolver runs. */\nexport interface IntentResolverOptions extends SerializeTransactionOptions {\n\t/** Intent names assigned to this resolver for this serialization pass. */\n\tintentNames?: readonly string[];\n}\n\nexport type TransactionPlugin = (\n\ttransactionData: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) => Promise<void>;\n\nexport function needsTransactionResolution(\n\tdata: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n): boolean {\n\tif (\n\t\tdata.inputs.some((input) => {\n\t\t\treturn input.UnresolvedObject || input.UnresolvedPure;\n\t\t})\n\t) {\n\t\treturn true;\n\t}\n\n\tif (!options.onlyTransactionKind) {\n\t\tif (!data.gasData.price || !data.gasData.budget) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!data.gasData.payment) {\n\t\t\tconst assumesAddressBalanceGas =\n\t\t\t\toptions.assumeSufficientAddressBalances && !transactionUsesGasCoin(data);\n\n\t\t\t// Address balance gas has no object version to protect against replay, so an offline build\n\t\t\t// needs a ValidDuring expiration. Epoch expiration doesn't count.\n\t\t\tif (\n\t\t\t\t!assumesAddressBalanceGas ||\n\t\t\t\t(data.expiration?.$kind !== 'ValidDuring' && data.expiration?.$kind !== 'Validity')\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (data.gasData.payment.length === 0 && !data.expiration) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nexport async function resolveTransactionPlugin(\n\ttransactionData: TransactionDataBuilder,\n\toptions: BuildTransactionOptions,\n\tnext: () => Promise<void>,\n) {\n\tnormalizeRawArguments(transactionData);\n\n\tif (!needsTransactionResolution(transactionData, options)) {\n\t\t// Payment can only be unset here when assumeSufficientAddressBalances applies\n\t\tif (!options.onlyTransactionKind && !transactionData.gasData.payment) {\n\t\t\ttransactionData.gasData.payment = [];\n\t\t}\n\n\t\tawait validate(transactionData);\n\t\treturn next();\n\t}\n\n\tconst client = getClient(options);\n\tconst plugin = client.core?.resolveTransactionPlugin() ?? coreClientResolveTransactionPlugin;\n\n\treturn plugin(transactionData, options, async () => {\n\t\tawait validate(transactionData);\n\t\tawait next();\n\t});\n}\n\nfunction validate(transactionData: TransactionDataBuilder) {\n\ttransactionData.inputs.forEach((input, index) => {\n\t\tif (input.$kind !== 'Object' && input.$kind !== 'Pure' && input.$kind !== 'FundsWithdrawal') {\n\t\t\tthrow new Error(\n\t\t\t\t`Input at index ${index} has not been resolved. Expected a Pure, Object, or FundsWithdrawal input, but found ${JSON.stringify(\n\t\t\t\t\tinput,\n\t\t\t\t)}`,\n\t\t\t);\n\t\t}\n\t});\n}\n\nexport function getClient(options: BuildTransactionOptions) {\n\tif (!options.client) {\n\t\tthrow new Error(\n\t\t\t`No sui client passed to Transaction#build, but transaction data was not sufficient to build offline.`,\n\t\t);\n\t}\n\n\treturn options.client;\n}\n\nfunction normalizeRawArguments(transactionData: TransactionDataBuilder) {\n\tfor (const command of transactionData.commands) {\n\t\tswitch (command.$kind) {\n\t\t\tcase 'SplitCoins':\n\t\t\t\tcommand.SplitCoins.amounts.forEach((amount) => {\n\t\t\t\t\tnormalizeRawArgument(amount, bcs.U64, transactionData);\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase 'TransferObjects':\n\t\t\t\tnormalizeRawArgument(command.TransferObjects.address, bcs.Address, transactionData);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction normalizeRawArgument(\n\targ: Argument,\n\tschema: BcsType<any>,\n\ttransactionData: TransactionDataBuilder,\n) {\n\tif (arg.$kind !== 'Input') {\n\t\treturn;\n\t}\n\tconst input = transactionData.inputs[arg.Input];\n\n\tif (input.$kind !== 'UnresolvedPure') {\n\t\treturn;\n\t}\n\n\ttransactionData.inputs[arg.Input] = Inputs.Pure(schema.serialize(input.UnresolvedPure.value));\n}\n"],"mappings":";;;;;;AA0CA,SAAgB,2BACf,MACA,SACU;AACV,KACC,KAAK,OAAO,MAAM,UAAU;AAC3B,SAAO,MAAM,oBAAoB,MAAM;GACtC,CAEF,QAAO;AAGR,KAAI,CAAC,QAAQ,qBAAqB;AACjC,MAAI,CAAC,KAAK,QAAQ,SAAS,CAAC,KAAK,QAAQ,OACxC,QAAO;AAGR,MAAI,CAAC,KAAK,QAAQ,SAMjB;OACC,EALA,QAAQ,mCAAmC,CAAC,uBAAuB,KAAK,KAMvE,KAAK,YAAY,UAAU,iBAAiB,KAAK,YAAY,UAAU,WAExE,QAAO;aAEE,KAAK,QAAQ,QAAQ,WAAW,KAAK,CAAC,KAAK,WACrD,QAAO;;AAIT,QAAO;;AAGR,eAAsB,yBACrB,iBACA,SACA,MACC;AACD,uBAAsB,gBAAgB;AAEtC,KAAI,CAAC,2BAA2B,iBAAiB,QAAQ,EAAE;AAE1D,MAAI,CAAC,QAAQ,uBAAuB,CAAC,gBAAgB,QAAQ,QAC5D,iBAAgB,QAAQ,UAAU,EAAE;AAGrC,QAAM,SAAS,gBAAgB;AAC/B,SAAO,MAAM;;AAMd,SAHe,UAAU,QAAQ,CACX,MAAM,0BAA0B,IAAI,oCAE5C,iBAAiB,SAAS,YAAY;AACnD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,MAAM;GACX;;AAGH,SAAS,SAAS,iBAAyC;AAC1D,iBAAgB,OAAO,SAAS,OAAO,UAAU;AAChD,MAAI,MAAM,UAAU,YAAY,MAAM,UAAU,UAAU,MAAM,UAAU,kBACzE,OAAM,IAAI,MACT,kBAAkB,MAAM,wFAAwF,KAAK,UACpH,MACA,GACD;GAED;;AAGH,SAAgB,UAAU,SAAkC;AAC3D,KAAI,CAAC,QAAQ,OACZ,OAAM,IAAI,MACT,uGACA;AAGF,QAAO,QAAQ;;AAGhB,SAAS,sBAAsB,iBAAyC;AACvE,MAAK,MAAM,WAAW,gBAAgB,SACrC,SAAQ,QAAQ,OAAhB;EACC,KAAK;AACJ,WAAQ,WAAW,QAAQ,SAAS,WAAW;AAC9C,yBAAqB,QAAQA,OAAI,KAAK,gBAAgB;KACrD;AACF;EACD,KAAK;AACJ,wBAAqB,QAAQ,gBAAgB,SAASA,OAAI,SAAS,gBAAgB;AACnF;;;AAKJ,SAAS,qBACR,KACA,QACA,iBACC;AACD,KAAI,IAAI,UAAU,QACjB;CAED,MAAM,QAAQ,gBAAgB,OAAO,IAAI;AAEzC,KAAI,MAAM,UAAU,iBACnB;AAGD,iBAAgB,OAAO,IAAI,SAAS,OAAO,KAAK,OAAO,UAAU,MAAM,eAAe,MAAM,CAAC"}
package/dist/version.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/version.ts
2
- const PACKAGE_VERSION = "2.30.0";
2
+ const PACKAGE_VERSION = "2.31.1";
3
3
 
4
4
  //#endregion
5
5
  export { PACKAGE_VERSION };
@@ -1 +1 @@
1
- {"version":3,"file":"version.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n// This file is generated by genversion.mjs. Do not edit it directly.\n\nexport const PACKAGE_VERSION = '2.30.0';\n"],"mappings":";AAKA,MAAa,kBAAkB"}
1
+ {"version":3,"file":"version.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n// This file is generated by genversion.mjs. Do not edit it directly.\n\nexport const PACKAGE_VERSION = '2.31.1';\n"],"mappings":";AAKA,MAAa,kBAAkB"}
@@ -1,31 +1,31 @@
1
- import * as _mysten_bcs1238 from "@mysten/bcs";
1
+ import * as _mysten_bcs1241 from "@mysten/bcs";
2
2
  import { InferBcsInput } from "@mysten/bcs";
3
3
 
4
4
  //#region src/zklogin/bcs.d.ts
5
- declare const zkLoginSignature: _mysten_bcs1238.BcsStruct<{
6
- inputs: _mysten_bcs1238.BcsStruct<{
7
- proofPoints: _mysten_bcs1238.BcsStruct<{
8
- a: _mysten_bcs1238.BcsType<string[], Iterable<string> & {
5
+ declare const zkLoginSignature: _mysten_bcs1241.BcsStruct<{
6
+ inputs: _mysten_bcs1241.BcsStruct<{
7
+ proofPoints: _mysten_bcs1241.BcsStruct<{
8
+ a: _mysten_bcs1241.BcsType<string[], Iterable<string> & {
9
9
  length: number;
10
10
  }, string>;
11
- b: _mysten_bcs1238.BcsType<string[][], Iterable<Iterable<string> & {
11
+ b: _mysten_bcs1241.BcsType<string[][], Iterable<Iterable<string> & {
12
12
  length: number;
13
13
  }> & {
14
14
  length: number;
15
15
  }, string>;
16
- c: _mysten_bcs1238.BcsType<string[], Iterable<string> & {
16
+ c: _mysten_bcs1241.BcsType<string[], Iterable<string> & {
17
17
  length: number;
18
18
  }, string>;
19
19
  }, string>;
20
- issBase64Details: _mysten_bcs1238.BcsStruct<{
21
- value: _mysten_bcs1238.BcsType<string, string, "string">;
22
- indexMod4: _mysten_bcs1238.BcsType<number, number, "u8">;
20
+ issBase64Details: _mysten_bcs1241.BcsStruct<{
21
+ value: _mysten_bcs1241.BcsType<string, string, "string">;
22
+ indexMod4: _mysten_bcs1241.BcsType<number, number, "u8">;
23
23
  }, string>;
24
- headerBase64: _mysten_bcs1238.BcsType<string, string, "string">;
25
- addressSeed: _mysten_bcs1238.BcsType<string, string, "string">;
24
+ headerBase64: _mysten_bcs1241.BcsType<string, string, "string">;
25
+ addressSeed: _mysten_bcs1241.BcsType<string, string, "string">;
26
26
  }, string>;
27
- maxEpoch: _mysten_bcs1238.BcsType<string, string | number | bigint, "u64">;
28
- userSignature: _mysten_bcs1238.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
27
+ maxEpoch: _mysten_bcs1241.BcsType<string, string | number | bigint, "u64">;
28
+ userSignature: _mysten_bcs1241.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
29
29
  }, string>;
30
30
  type ZkLoginSignature = InferBcsInput<typeof zkLoginSignature>;
31
31
  type ZkLoginSignatureInputs = ZkLoginSignature['inputs'];
@@ -12,6 +12,7 @@
12
12
  - [Building Transactions](./transactions/basics.md): Construct programmable transaction blocks with the Transaction API
13
13
  - [Signing and Execution](./transactions/signing-and-execution.md): Sign transactions and execute them on the Sui network
14
14
  - [Coins and Balances](./transactions/coins-and-balances.md): Work with coin objects and address balances in transactions
15
+ - [Allowances](./transactions/allowances.md): Create allowances and spend from another account's address balance
15
16
  - [Commands and Inputs Reference](./transactions/reference.md): Complete reference for transaction commands and input types
16
17
  - [Building Offline](./transactions/offline.md): Build transactions without a network connection
17
18
  - [Cryptography](./cryptography.md): >- Sign and verify Sui transactions and messages with keypairs and external signers