@mysten/sui 2.30.0 → 2.31.0

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 (79) hide show
  1. package/CHANGELOG.md +34 -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 +103 -11
  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.mjs +2 -1
  10. package/dist/client/core-resolver.mjs.map +1 -1
  11. package/dist/client/transaction-resolver.mjs +35 -8
  12. package/dist/client/transaction-resolver.mjs.map +1 -1
  13. package/dist/cryptography/signature.d.mts +6 -6
  14. package/dist/graphql/generated/tada-env.d.mts +74 -0
  15. package/dist/grpc/proto/sui/rpc/v2/input.d.mts +21 -1
  16. package/dist/grpc/proto/sui/rpc/v2/input.d.mts.map +1 -1
  17. package/dist/grpc/proto/sui/rpc/v2/input.mjs +21 -0
  18. package/dist/grpc/proto/sui/rpc/v2/input.mjs.map +1 -1
  19. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts +4 -4
  20. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  21. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  22. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  23. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  24. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
  25. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  26. package/dist/jsonRpc/core.mjs +1 -1
  27. package/dist/jsonRpc/core.mjs.map +1 -1
  28. package/dist/transactions/Transaction.d.mts +44 -39
  29. package/dist/transactions/Transaction.d.mts.map +1 -1
  30. package/dist/transactions/Transaction.mjs +65 -24
  31. package/dist/transactions/Transaction.mjs.map +1 -1
  32. package/dist/transactions/data/internal.d.mts +12 -0
  33. package/dist/transactions/data/internal.d.mts.map +1 -1
  34. package/dist/transactions/data/internal.mjs +5 -1
  35. package/dist/transactions/data/internal.mjs.map +1 -1
  36. package/dist/transactions/data/v2.d.mts +5 -0
  37. package/dist/transactions/data/v2.d.mts.map +1 -1
  38. package/dist/transactions/index.d.mts +3 -2
  39. package/dist/transactions/intents/AllowanceBalance.mjs +147 -0
  40. package/dist/transactions/intents/AllowanceBalance.mjs.map +1 -0
  41. package/dist/transactions/intents/BalanceIntentNames.mjs +7 -0
  42. package/dist/transactions/intents/BalanceIntentNames.mjs.map +1 -0
  43. package/dist/transactions/intents/BalanceOptions.d.mts +27 -0
  44. package/dist/transactions/intents/BalanceOptions.d.mts.map +1 -0
  45. package/dist/transactions/intents/BalanceOptions.mjs +12 -0
  46. package/dist/transactions/intents/BalanceOptions.mjs.map +1 -0
  47. package/dist/transactions/intents/CoinWithBalance.d.mts.map +1 -1
  48. package/dist/transactions/intents/CoinWithBalance.mjs +31 -10
  49. package/dist/transactions/intents/CoinWithBalance.mjs.map +1 -1
  50. package/dist/transactions/intents/ResolveBalances.mjs +16 -0
  51. package/dist/transactions/intents/ResolveBalances.mjs.map +1 -0
  52. package/dist/transactions/resolve.d.mts.map +1 -1
  53. package/dist/transactions/resolve.mjs +1 -1
  54. package/dist/transactions/resolve.mjs.map +1 -1
  55. package/dist/version.mjs +1 -1
  56. package/dist/version.mjs.map +1 -1
  57. package/dist/zklogin/bcs.d.mts +14 -14
  58. package/docs/llms-index.md +1 -0
  59. package/docs/transactions/allowances.md +176 -0
  60. package/docs/transactions/coins-and-balances.md +12 -9
  61. package/package.json +1 -1
  62. package/src/bcs/bcs.ts +4 -0
  63. package/src/bcs/types.ts +2 -1
  64. package/src/client/core-resolver.ts +6 -4
  65. package/src/client/transaction-resolver.ts +43 -9
  66. package/src/graphql/generated/schema.graphql +87 -42
  67. package/src/graphql/generated/tada-env.ts +84 -0
  68. package/src/grpc/proto/sui/rpc/v2/input.ts +22 -0
  69. package/src/jsonRpc/core.ts +3 -4
  70. package/src/transactions/Transaction.ts +70 -49
  71. package/src/transactions/data/internal.ts +4 -0
  72. package/src/transactions/index.ts +3 -0
  73. package/src/transactions/intents/AllowanceBalance.ts +204 -0
  74. package/src/transactions/intents/BalanceIntentNames.ts +5 -0
  75. package/src/transactions/intents/BalanceOptions.ts +45 -0
  76. package/src/transactions/intents/CoinWithBalance.ts +51 -8
  77. package/src/transactions/intents/ResolveBalances.ts +30 -0
  78. package/src/transactions/resolve.ts +6 -0
  79. package/src/version.ts +1 -1
@@ -18,6 +18,7 @@ export {
18
18
  type TransactionObjectArgument,
19
19
  type TransactionResult,
20
20
  type TransactionCopyOptions,
21
+ type WithdrawalOptions,
21
22
  } from './Transaction.js';
22
23
 
23
24
  export { type SerializedTransactionDataV2 } from './data/v2.js';
@@ -48,3 +49,5 @@ export type {
48
49
  export { Arguments } from './Arguments.js';
49
50
 
50
51
  export { isArgument } from './utils.js';
52
+
53
+ export type { BalanceOptions, AllowanceReference } from './intents/BalanceOptions.js';
@@ -0,0 +1,204 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { object, optional, parse, picklist, string } from 'valibot';
5
+
6
+ import { hasMvrName } from '../../client/mvr.js';
7
+
8
+ import { ALLOWANCE_BALANCE } from './BalanceIntentNames.js';
9
+ import { resolveBalances } from './ResolveBalances.js';
10
+
11
+ import { bcs } from '../../bcs/index.js';
12
+ import type { SuiClientTypes } from '../../client/index.js';
13
+ import { normalizeStructTag, normalizeSuiAddress } from '../../utils/sui-types.js';
14
+ import { TransactionCommands } from '../Commands.js';
15
+ import type { Argument } from '../data/internal.js';
16
+ import { Inputs } from '../Inputs.js';
17
+ import { getClient } from '../resolve.js';
18
+ import type { TransactionPlugin } from '../resolve.js';
19
+ import type { Transaction } from '../Transaction.js';
20
+ import type { AllowanceReference } from './BalanceOptions.js';
21
+
22
+ export { ALLOWANCE_BALANCE } from './BalanceIntentNames.js';
23
+
24
+ const AllowanceBalanceData = object({
25
+ objectId: string(),
26
+ funder: optional(string()),
27
+ appType: optional(string()),
28
+ type: string(),
29
+ amount: string(),
30
+ outputKind: picklist(['coin', 'balance']),
31
+ });
32
+
33
+ // Read only the prefix needed for spending. The remaining settings and current_spend
34
+ // are checked by Move, so this does not need to model rate limits or their policy.
35
+ const AllowanceMetadata = bcs.struct('AllowanceMetadata', {
36
+ id: bcs.Address,
37
+ funder: bcs.Address,
38
+ spender: bcs.option(bcs.Address),
39
+ app: bcs.option(bcs.string()),
40
+ });
41
+
42
+ export function allowanceBalance({
43
+ allowance,
44
+ amount,
45
+ type = '0x2::sui::SUI',
46
+ outputKind,
47
+ }: {
48
+ allowance: string | AllowanceReference;
49
+ amount: bigint;
50
+ type?: string;
51
+ outputKind: 'coin' | 'balance';
52
+ }) {
53
+ bcs.U64.validate(amount);
54
+ const objectId = normalizeSuiAddress(
55
+ typeof allowance === 'string' ? allowance : allowance.objectId,
56
+ );
57
+ const reference = typeof allowance === 'string' ? { objectId } : allowance;
58
+ return (tx: Transaction) => {
59
+ tx.addIntentResolver(ALLOWANCE_BALANCE, resolveBalances);
60
+ return tx.add(
61
+ TransactionCommands.Intent({
62
+ name: ALLOWANCE_BALANCE,
63
+ inputs: {
64
+ allowance: tx.object(objectId),
65
+ clock: tx.object.clock(),
66
+ ...(reference.app ? { permit: tx.object(reference.app.permit) } : {}),
67
+ },
68
+ data: {
69
+ objectId,
70
+ funder: reference.funder ? normalizeSuiAddress(reference.funder) : undefined,
71
+ appType: reference.app ? normalizeStructTag(reference.app.type) : undefined,
72
+ type: normalizeStructTag(type),
73
+ amount: String(amount),
74
+ outputKind,
75
+ },
76
+ }),
77
+ );
78
+ };
79
+ }
80
+
81
+ export const resolveAllowanceBalance: TransactionPlugin = async (
82
+ transactionData,
83
+ options,
84
+ next,
85
+ ) => {
86
+ const ids = new Set<string>();
87
+ const namedTypes = new Set<string>();
88
+ for (const command of transactionData.commands) {
89
+ if (command.$kind !== '$Intent' || command.$Intent.name !== ALLOWANCE_BALANCE) continue;
90
+ const data = parse(AllowanceBalanceData, command.$Intent.data);
91
+ if (!data.funder) ids.add(data.objectId);
92
+ for (const type of [data.type, data.appType]) {
93
+ if (type && hasMvrName(type)) namedTypes.add(type);
94
+ }
95
+ }
96
+
97
+ const [resolvedTypes, objects] = await Promise.all([
98
+ namedTypes.size ? getClient(options).core.mvr.resolve({ types: [...namedTypes] }) : undefined,
99
+ ids.size
100
+ ? getClient(options)
101
+ .core.getObjects({ objectIds: [...ids], include: { content: true } })
102
+ .then((result) => result.objects)
103
+ : [],
104
+ ]);
105
+ const allowances = new Map<string, SuiClientTypes.Object<{ content: true }>>();
106
+ for (const object of objects) {
107
+ if (object instanceof Error) throw object;
108
+ allowances.set(object.objectId, object);
109
+ }
110
+
111
+ for (let index = 0; index < transactionData.commands.length; index++) {
112
+ const command = transactionData.commands[index];
113
+ if (command.$kind !== '$Intent' || command.$Intent.name !== ALLOWANCE_BALANCE) continue;
114
+ const data = parse(AllowanceBalanceData, command.$Intent.data);
115
+ if (hasMvrName(data.type)) {
116
+ const resolved = resolvedTypes?.types[data.type];
117
+ if (!resolved) throw new Error(`No resolution found for type: ${data.type}`);
118
+ data.type = normalizeStructTag(resolved.type);
119
+ }
120
+ if (data.appType && hasMvrName(data.appType)) {
121
+ const resolved = resolvedTypes?.types[data.appType];
122
+ if (!resolved) throw new Error(`No resolution found for type: ${data.appType}`);
123
+ data.appType = normalizeStructTag(resolved.type);
124
+ }
125
+ let funder = data.funder;
126
+ if (!funder) {
127
+ const allowance = allowances.get(data.objectId);
128
+ const expectedType = normalizeStructTag(
129
+ `0x2::allowance::Allowance<0x2::balance::Balance<${data.type}>>`,
130
+ );
131
+ if (!allowance || allowance.type !== expectedType || allowance.owner.$kind !== 'Shared') {
132
+ throw new Error(`Expected a shared ${expectedType} allowance at ${data.objectId}`);
133
+ }
134
+ const metadata = AllowanceMetadata.parse(allowance.content);
135
+ if (metadata.app !== null && !data.appType) {
136
+ throw new Error(
137
+ `Allowance ${data.objectId} is app-bound; pass allowance.app with the app type and SpendPermit`,
138
+ );
139
+ }
140
+ if (
141
+ data.appType &&
142
+ (metadata.app === null || normalizeStructTag(metadata.app) !== data.appType)
143
+ ) {
144
+ throw new Error(`Allowance ${data.objectId} is not bound to app ${data.appType}`);
145
+ }
146
+ funder = metadata.funder;
147
+ const input = command.$Intent.inputs.allowance as Argument;
148
+ if (input.$kind === 'Input') {
149
+ transactionData.inputs[input.Input] = Inputs.SharedObjectRef({
150
+ objectId: data.objectId,
151
+ initialSharedVersion: allowance.owner.Shared.initialSharedVersion,
152
+ mutable: true,
153
+ });
154
+ }
155
+ }
156
+ // Spending mutates the allowance even when the caller supplied the funder
157
+ // and we did not fetch metadata. Reuse and upgrade its existing input.
158
+ const allowanceArgument = command.$Intent.inputs.allowance as Argument;
159
+ if (allowanceArgument.$kind === 'Input') {
160
+ const input = transactionData.inputs[allowanceArgument.Input];
161
+ if (input.Object?.SharedObject) input.Object.SharedObject.mutable = true;
162
+ if (input.UnresolvedObject) input.UnresolvedObject.mutable = true;
163
+ }
164
+ const withdrawal = transactionData.addInput(
165
+ 'withdrawal',
166
+ Inputs.FundsWithdrawal({
167
+ reservation: { $kind: 'MaxAmountU64', MaxAmountU64: data.amount },
168
+ typeArg: { $kind: 'Balance', Balance: data.type },
169
+ withdrawFrom: {
170
+ $kind: 'SenderAllowance',
171
+ SenderAllowance: { funder, allowance: data.objectId },
172
+ },
173
+ }),
174
+ );
175
+ const commands = [
176
+ TransactionCommands.MoveCall({
177
+ target: data.appType
178
+ ? '0x2::allowance::app_balance_spend'
179
+ : '0x2::allowance::balance_spend',
180
+ typeArguments: data.appType ? [data.type, data.appType] : [data.type],
181
+ arguments: [
182
+ command.$Intent.inputs.allowance as Argument,
183
+ ...(data.appType ? [command.$Intent.inputs.permit as Argument] : []),
184
+ withdrawal,
185
+ command.$Intent.inputs.clock as Argument,
186
+ ],
187
+ }),
188
+ ];
189
+ if (data.outputKind === 'coin') {
190
+ commands.push(
191
+ TransactionCommands.MoveCall({
192
+ target: '0x2::coin::from_balance',
193
+ typeArguments: [data.type],
194
+ arguments: [{ $kind: 'Result', Result: index }],
195
+ }),
196
+ );
197
+ }
198
+ transactionData.replaceCommand(index, commands, {
199
+ NestedResult: [index + commands.length - 1, 0],
200
+ });
201
+ index += commands.length - 1;
202
+ }
203
+ return next();
204
+ };
@@ -0,0 +1,5 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export const ALLOWANCE_BALANCE = 'AllowanceBalance';
5
+ export const COIN_WITH_BALANCE = 'CoinWithBalance';
@@ -0,0 +1,45 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { TransactionObjectArgument } from '../Transaction.js';
5
+
6
+ /** An allowance with optional funder metadata and app authorization. */
7
+ export interface AllowanceReference {
8
+ objectId: string;
9
+ /** Skip the metadata lookup when the funder is already known. */
10
+ funder?: string;
11
+ /** Authorization for an app-bound allowance. */
12
+ app?: {
13
+ /** The app type A in SpendPermit<A>. */
14
+ type: string;
15
+ /** A SpendPermit<A> from the app, consumed by this spend. */
16
+ permit: TransactionObjectArgument;
17
+ };
18
+ }
19
+
20
+ export type BalanceOptions = {
21
+ /** The coin type T, not Balance<T>. Defaults to SUI. */
22
+ type?: string;
23
+ balance: bigint | number | string;
24
+ } & (
25
+ | { allowance?: never; useGasCoin?: boolean }
26
+ | {
27
+ /** An allowance ID or reference with optional app authorization. Never falls back to sender funds. */
28
+ allowance: string | AllowanceReference;
29
+ useGasCoin?: never;
30
+ }
31
+ );
32
+
33
+ export function normalizeBalance(options: BalanceOptions): bigint {
34
+ if (options.allowance !== undefined && options.useGasCoin !== undefined) {
35
+ throw new Error('useGasCoin cannot be combined with allowance');
36
+ }
37
+ const amount = options.balance;
38
+ if (typeof amount === 'string' && !/^[0-9]+$/.test(amount)) {
39
+ throw new Error('Amount must be a non-empty string of decimal digits');
40
+ }
41
+ if (typeof amount === 'number' && !Number.isSafeInteger(amount)) {
42
+ throw new Error('Amount must be a safe integer; use bigint or a string for larger amounts');
43
+ }
44
+ return BigInt(amount);
45
+ }
@@ -16,8 +16,11 @@ import {
16
16
  union,
17
17
  } from 'valibot';
18
18
 
19
+ import { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from './BalanceIntentNames.js';
20
+ import { resolveBalances } from './ResolveBalances.js';
21
+
19
22
  import { bcs } from '../../bcs/index.js';
20
- import { normalizeStructTag } from '../../utils/sui-types.js';
23
+ import { normalizeStructTag, normalizeSuiAddress } from '../../utils/sui-types.js';
21
24
  import { TransactionCommands } from '../Commands.js';
22
25
  import type { Argument } from '../data/internal.js';
23
26
  import { Inputs } from '../Inputs.js';
@@ -26,7 +29,7 @@ import type { Transaction, TransactionResult } from '../Transaction.js';
26
29
  import type { TransactionDataBuilder } from '../TransactionData.js';
27
30
  import type { ClientWithCoreApi, SuiClientTypes } from '../../client/index.js';
28
31
 
29
- export const COIN_WITH_BALANCE = 'CoinWithBalance';
32
+ export { COIN_WITH_BALANCE } from './BalanceIntentNames.js';
30
33
  const SUI_TYPE = normalizeStructTag('0x2::sui::SUI');
31
34
 
32
35
  export function coinWithBalance({
@@ -45,7 +48,7 @@ export function coinWithBalance({
45
48
  return coinResult;
46
49
  }
47
50
 
48
- tx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);
51
+ tx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);
49
52
  const coinType = type === 'gas' ? type : normalizeStructTag(type);
50
53
 
51
54
  coinResult = tx.add(
@@ -80,7 +83,7 @@ export function createBalance({
80
83
  return balanceResult;
81
84
  }
82
85
 
83
- tx.addIntentResolver(COIN_WITH_BALANCE, resolveCoinBalance);
86
+ tx.addIntentResolver(COIN_WITH_BALANCE, resolveBalances);
84
87
  const coinType = type === 'gas' ? type : normalizeStructTag(type);
85
88
 
86
89
  balanceResult = tx.add(
@@ -126,6 +129,25 @@ export async function resolveCoinBalance(
126
129
  throw new Error('Sender must be set to resolve CoinWithBalance');
127
130
  }
128
131
 
132
+ if (transactionData.commands.some((command) => command.$Intent?.name === ALLOWANCE_BALANCE)) {
133
+ throw new Error(
134
+ 'Resolve AllowanceBalance together with CoinWithBalance, or preserve both intents',
135
+ );
136
+ }
137
+ const reservedByType = new Map<string, bigint>();
138
+ for (const input of transactionData.inputs) {
139
+ if (!input.FundsWithdrawal) continue;
140
+ const { withdrawFrom, typeArg, reservation } = input.FundsWithdrawal;
141
+ const owner =
142
+ withdrawFrom.SenderAllowance?.funder ??
143
+ (withdrawFrom.Sender
144
+ ? transactionData.sender
145
+ : (transactionData.gasData.owner ?? transactionData.sender));
146
+ if (normalizeSuiAddress(owner) !== normalizeSuiAddress(transactionData.sender)) continue;
147
+ const type = normalizeStructTag(typeArg.Balance);
148
+ reservedByType.set(type, (reservedByType.get(type) ?? 0n) + BigInt(reservation.MaxAmountU64));
149
+ }
150
+
129
151
  // First pass: scan intents, collect per-type data, and resolve zero-balance intents in place.
130
152
  for (const [i, command] of transactionData.commands.entries()) {
131
153
  if (command.$kind !== '$Intent' || command.$Intent.name !== COIN_WITH_BALANCE) {
@@ -199,6 +221,7 @@ export async function resolveCoinBalance(
199
221
  client: client!,
200
222
  owner: transactionData.sender!,
201
223
  usedIds,
224
+ reserved: reservedByType.get(coinType) ?? 0n,
202
225
  });
203
226
 
204
227
  coinsByType.set(coinType, coins);
@@ -211,7 +234,10 @@ export async function resolveCoinBalance(
211
234
  coinType: SUI_TYPE,
212
235
  })
213
236
  .then(({ balance }) => {
214
- addressBalanceByType.set('gas', BigInt(balance.addressBalance));
237
+ addressBalanceByType.set(
238
+ 'gas',
239
+ availableAddressBalance(balance.addressBalance, reservedByType.get(SUI_TYPE) ?? 0n),
240
+ );
215
241
  })
216
242
  : null,
217
243
  ]);
@@ -462,12 +488,14 @@ async function getCoinsAndBalanceOfType({
462
488
  client,
463
489
  owner,
464
490
  usedIds,
491
+ reserved,
465
492
  }: {
466
493
  coinType: string;
467
494
  balance: bigint;
468
495
  client: ClientWithCoreApi;
469
496
  owner: string;
470
497
  usedIds: Set<string>;
498
+ reserved: bigint;
471
499
  }): Promise<{
472
500
  coins: SuiClientTypes.Coin[];
473
501
  balance: bigint;
@@ -477,9 +505,14 @@ async function getCoinsAndBalanceOfType({
477
505
  let remainingBalance = balance;
478
506
  const coins: SuiClientTypes.Coin[] = [];
479
507
  const balanceRequest = client.core.getBalance({ owner, coinType }).then(({ balance }) => {
480
- remainingBalance -= BigInt(balance.addressBalance);
481
-
482
- return balance;
508
+ const addressBalance = availableAddressBalance(balance.addressBalance, reserved);
509
+ remainingBalance -= addressBalance;
510
+
511
+ return {
512
+ ...balance,
513
+ addressBalance: String(addressBalance),
514
+ balance: String(BigInt(balance.balance) - reserved),
515
+ };
483
516
  });
484
517
 
485
518
  const [allCoins, balanceResponse] = await Promise.all([loadMoreCoins(), balanceRequest]);
@@ -531,3 +564,13 @@ async function getCoinsAndBalanceOfType({
531
564
  return coins;
532
565
  }
533
566
  }
567
+
568
+ function availableAddressBalance(balance: string, reserved: bigint): bigint {
569
+ const available = BigInt(balance) - reserved;
570
+ if (available < 0n) {
571
+ throw new Error(
572
+ `Insufficient address balance for existing withdrawals. Required: ${reserved}, Available: ${balance}`,
573
+ );
574
+ }
575
+ return available;
576
+ }
@@ -0,0 +1,30 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { IntentResolverOptions } from '../resolve.js';
5
+ import type { TransactionDataBuilder } from '../TransactionData.js';
6
+ import { ALLOWANCE_BALANCE, COIN_WITH_BALANCE } from './BalanceIntentNames.js';
7
+ import { resolveAllowanceBalance } from './AllowanceBalance.js';
8
+ import { resolveCoinBalance } from './CoinWithBalance.js';
9
+
10
+ export async function resolveBalances(
11
+ transactionData: TransactionDataBuilder,
12
+ options: IntentResolverOptions,
13
+ next: () => Promise<void>,
14
+ ) {
15
+ const intents = new Set(options.intentNames ?? [ALLOWANCE_BALANCE, COIN_WITH_BALANCE]);
16
+ const shouldResolve = (name: string) =>
17
+ intents.has(name) &&
18
+ !options.supportedIntents?.includes(name) &&
19
+ transactionData.commands.some((command) => command.$Intent?.name === name);
20
+
21
+ // Reserve allowance withdrawals before ordinary coin selection, including when
22
+ // the sender is also the funder. Keep separate intents for wallet negotiation.
23
+ if (shouldResolve(ALLOWANCE_BALANCE)) {
24
+ await resolveAllowanceBalance(transactionData, options, async () => {});
25
+ }
26
+ if (shouldResolve(COIN_WITH_BALANCE)) {
27
+ await resolveCoinBalance(transactionData, options, async () => {});
28
+ }
29
+ await next();
30
+ }
@@ -28,6 +28,12 @@ export interface SerializeTransactionOptions extends BuildTransactionOptions {
28
28
  supportedIntents?: string[];
29
29
  }
30
30
 
31
+ /** Options supplied when a registered intent resolver runs. */
32
+ export interface IntentResolverOptions extends SerializeTransactionOptions {
33
+ /** Intent names assigned to this resolver for this serialization pass. */
34
+ intentNames?: readonly string[];
35
+ }
36
+
31
37
  export type TransactionPlugin = (
32
38
  transactionData: TransactionDataBuilder,
33
39
  options: BuildTransactionOptions,
package/src/version.ts CHANGED
@@ -3,4 +3,4 @@
3
3
 
4
4
  // This file is generated by genversion.mjs. Do not edit it directly.
5
5
 
6
- export const PACKAGE_VERSION = '2.30.0';
6
+ export const PACKAGE_VERSION = '2.31.0';