@mysten/sui 2.28.0 → 2.30.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 (58) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/client/core-resolver.d.mts.map +1 -1
  3. package/dist/client/core-resolver.mjs +2 -5
  4. package/dist/client/core-resolver.mjs.map +1 -1
  5. package/dist/cryptography/signature.d.mts +14 -14
  6. package/dist/grpc/client.d.mts +1 -1
  7. package/dist/grpc/client.d.mts.map +1 -1
  8. package/dist/grpc/client.mjs +3 -5
  9. package/dist/grpc/client.mjs.map +1 -1
  10. package/dist/grpc/core.d.mts.map +1 -1
  11. package/dist/grpc/core.mjs +10 -10
  12. package/dist/grpc/core.mjs.map +1 -1
  13. package/dist/grpc/index.d.mts +4 -3
  14. package/dist/grpc/index.mjs +4 -2
  15. package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts +4 -4
  16. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  17. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  18. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  19. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  20. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  21. package/dist/grpc/transport.d.mts +18 -0
  22. package/dist/grpc/transport.d.mts.map +1 -0
  23. package/dist/grpc/transport.mjs +97 -0
  24. package/dist/grpc/transport.mjs.map +1 -0
  25. package/dist/transactions/Transaction.d.mts +7 -10
  26. package/dist/transactions/Transaction.d.mts.map +1 -1
  27. package/dist/transactions/Transaction.mjs.map +1 -1
  28. package/dist/transactions/data/internal.d.mts +127 -127
  29. package/dist/transactions/data/v1.d.mts +239 -239
  30. package/dist/transactions/data/v1.d.mts.map +1 -1
  31. package/dist/transactions/data/v2.d.mts +16 -16
  32. package/dist/transactions/data/v2.d.mts.map +1 -1
  33. package/dist/transactions/intents/CoinWithBalance.mjs +4 -2
  34. package/dist/transactions/intents/CoinWithBalance.mjs.map +1 -1
  35. package/dist/transactions/resolution-utils.mjs +13 -0
  36. package/dist/transactions/resolution-utils.mjs.map +1 -0
  37. package/dist/transactions/resolve.d.mts +8 -0
  38. package/dist/transactions/resolve.d.mts.map +1 -1
  39. package/dist/transactions/resolve.mjs +6 -2
  40. package/dist/transactions/resolve.mjs.map +1 -1
  41. package/dist/version.mjs +1 -1
  42. package/dist/version.mjs.map +1 -1
  43. package/dist/zklogin/bcs.d.mts +14 -14
  44. package/docs/clients/grpc.md +31 -7
  45. package/docs/migrations/sui-2.0/json-rpc-migration.md +50 -1
  46. package/docs/transactions/basics.md +19 -14
  47. package/docs/transactions/offline.md +137 -88
  48. package/package.json +2 -2
  49. package/src/client/core-resolver.ts +2 -7
  50. package/src/grpc/client.ts +12 -4
  51. package/src/grpc/core.ts +18 -14
  52. package/src/grpc/index.ts +7 -3
  53. package/src/grpc/transport.ts +160 -0
  54. package/src/transactions/Transaction.ts +1 -4
  55. package/src/transactions/intents/CoinWithBalance.ts +32 -25
  56. package/src/transactions/resolution-utils.ts +18 -0
  57. package/src/transactions/resolve.ts +29 -2
  58. package/src/version.ts +1 -1
@@ -1,12 +1,12 @@
1
1
  import { EnumInputShape } from "@mysten/bcs";
2
- import * as valibot1337 from "valibot";
2
+ import * as valibot0 from "valibot";
3
3
  import { GenericSchema, InferOutput } from "valibot";
4
4
 
5
5
  //#region src/transactions/data/v2.d.ts
6
- declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
7
- readonly version: valibot1337.LiteralSchema<2, undefined>;
8
- readonly sender: valibot1337.NullishSchema<valibot1337.SchemaWithPipe<readonly [valibot1337.StringSchema<undefined>, valibot1337.TransformAction<string, string>, valibot1337.CheckAction<string, undefined>]>, undefined>;
9
- readonly expiration: valibot1337.NullishSchema<GenericSchema<EnumInputShape<{
6
+ declare const SerializedTransactionDataV2Schema: valibot0.ObjectSchema<{
7
+ readonly version: valibot0.LiteralSchema<2, undefined>;
8
+ readonly sender: valibot0.NullishSchema<valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>, undefined>;
9
+ readonly expiration: valibot0.NullishSchema<GenericSchema<EnumInputShape<{
10
10
  None: true;
11
11
  Epoch: string | number;
12
12
  ValidDuring: {
@@ -30,17 +30,17 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
30
30
  nonce: number;
31
31
  };
32
32
  }>>, undefined>;
33
- readonly gasData: valibot1337.ObjectSchema<{
34
- readonly budget: valibot1337.NullableSchema<valibot1337.SchemaWithPipe<readonly [valibot1337.UnionSchema<[valibot1337.StringSchema<undefined>, valibot1337.SchemaWithPipe<readonly [valibot1337.NumberSchema<undefined>, valibot1337.IntegerAction<number, undefined>]>], undefined>, valibot1337.CheckAction<string | number, "Invalid u64">]>, undefined>;
35
- readonly price: valibot1337.NullableSchema<valibot1337.SchemaWithPipe<readonly [valibot1337.UnionSchema<[valibot1337.StringSchema<undefined>, valibot1337.SchemaWithPipe<readonly [valibot1337.NumberSchema<undefined>, valibot1337.IntegerAction<number, undefined>]>], undefined>, valibot1337.CheckAction<string | number, "Invalid u64">]>, undefined>;
36
- readonly owner: valibot1337.NullableSchema<valibot1337.SchemaWithPipe<readonly [valibot1337.StringSchema<undefined>, valibot1337.TransformAction<string, string>, valibot1337.CheckAction<string, undefined>]>, undefined>;
37
- readonly payment: valibot1337.NullableSchema<valibot1337.ArraySchema<valibot1337.ObjectSchema<{
38
- readonly objectId: valibot1337.SchemaWithPipe<readonly [valibot1337.StringSchema<undefined>, valibot1337.TransformAction<string, string>, valibot1337.CheckAction<string, undefined>]>;
39
- readonly version: valibot1337.SchemaWithPipe<readonly [valibot1337.UnionSchema<[valibot1337.StringSchema<undefined>, valibot1337.SchemaWithPipe<readonly [valibot1337.NumberSchema<undefined>, valibot1337.IntegerAction<number, undefined>]>], undefined>, valibot1337.CheckAction<string | number, "Invalid u64">]>;
40
- readonly digest: valibot1337.StringSchema<undefined>;
33
+ readonly gasData: valibot0.ObjectSchema<{
34
+ readonly budget: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>, undefined>;
35
+ readonly price: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>, undefined>;
36
+ readonly owner: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>, undefined>;
37
+ readonly payment: valibot0.NullableSchema<valibot0.ArraySchema<valibot0.ObjectSchema<{
38
+ readonly objectId: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>;
39
+ readonly version: valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>;
40
+ readonly digest: valibot0.StringSchema<undefined>;
41
41
  }, undefined>, undefined>, undefined>;
42
42
  }, undefined>;
43
- readonly inputs: valibot1337.ArraySchema<GenericSchema<EnumInputShape<{
43
+ readonly inputs: valibot0.ArraySchema<GenericSchema<EnumInputShape<{
44
44
  Object: EnumInputShape<{
45
45
  ImmOrOwnedObject: {
46
46
  objectId: string;
@@ -85,7 +85,7 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
85
85
  };
86
86
  };
87
87
  }>>, undefined>;
88
- readonly commands: valibot1337.ArraySchema<GenericSchema<EnumInputShape<{
88
+ readonly commands: valibot0.ArraySchema<GenericSchema<EnumInputShape<{
89
89
  MoveCall: {
90
90
  package: string;
91
91
  module: string;
@@ -184,7 +184,7 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
184
184
  };
185
185
  };
186
186
  }>>, undefined>;
187
- readonly digest: valibot1337.OptionalSchema<valibot1337.NullableSchema<valibot1337.StringSchema<undefined>, undefined>, undefined>;
187
+ readonly digest: valibot0.OptionalSchema<valibot0.NullableSchema<valibot0.StringSchema<undefined>, undefined>, undefined>;
188
188
  }, undefined>;
189
189
  type SerializedTransactionDataV2 = InferOutput<typeof SerializedTransactionDataV2Schema>;
190
190
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"v2.d.mts","names":[],"sources":["../../../src/transactions/data/v2.ts"],"mappings":";;;;;cAqJa,iCAAA,cAAiC,YAAA;EAAA,kBAQ5C,WAAA,CAAA,aAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAEU,2BAAA,GAA8B,WAAA,QAAmB,iCAAA"}
1
+ {"version":3,"file":"v2.d.mts","names":[],"sources":["../../../src/transactions/data/v2.ts"],"mappings":";;;;;cAqJa,iCAAA,WAAiC,YAAA;EAAA,kBAQ5C,QAAA,CAAA,aAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAEU,2BAAA,GAA8B,WAAA,QAAmB,iCAAA"}
@@ -85,8 +85,10 @@ async function resolveCoinBalance(transactionData, buildOptions, next) {
85
85
  const coinsByType = /* @__PURE__ */ new Map();
86
86
  const addressBalanceByType = /* @__PURE__ */ new Map();
87
87
  const client = buildOptions.client;
88
- if (!client) throw new Error("Client must be provided to build or serialize transactions with CoinWithBalance intents");
89
- await Promise.all([...[...coinTypes].map(async (coinType) => {
88
+ const assumeSufficientAddressBalances = buildOptions.assumeSufficientAddressBalances;
89
+ if (!client && !assumeSufficientAddressBalances) throw new Error("Client must be provided to build or serialize transactions with CoinWithBalance intents");
90
+ if (assumeSufficientAddressBalances) for (const [coinType, balance] of totalByType) addressBalanceByType.set(coinType, balance);
91
+ else await Promise.all([...[...coinTypes].map(async (coinType) => {
90
92
  const { coins, addressBalance } = await getCoinsAndBalanceOfType({
91
93
  coinType,
92
94
  balance: totalByType.get(coinType),
@@ -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\n\tif (!client) {\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\tawait Promise.all([\n\t\t...[...coinTypes].map(async (coinType) => {\n\t\t\tconst { coins, addressBalance } = await getCoinsAndBalanceOfType({\n\t\t\t\tcoinType,\n\t\t\t\tbalance: totalByType.get(coinType)!,\n\t\t\t\tclient,\n\t\t\t\towner: transactionData.sender!,\n\t\t\t\tusedIds,\n\t\t\t});\n\n\t\t\tcoinsByType.set(coinType, coins);\n\t\t\taddressBalanceByType.set(coinType, addressBalance);\n\t\t}),\n\t\ttotalByType.has('gas')\n\t\t\t? await client.core\n\t\t\t\t\t.getBalance({\n\t\t\t\t\t\towner: transactionData.sender!,\n\t\t\t\t\t\tcoinType: SUI_TYPE,\n\t\t\t\t\t})\n\t\t\t\t\t.then(({ balance }) => {\n\t\t\t\t\t\taddressBalanceByType.set('gas', BigInt(balance.addressBalance));\n\t\t\t\t\t})\n\t\t\t: null,\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 { 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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"}
@@ -0,0 +1,13 @@
1
+ //#region src/transactions/resolution-utils.ts
2
+ function transactionUsesGasCoin(transactionData) {
3
+ let usesGasCoin = false;
4
+ transactionData.mapArguments((arg) => {
5
+ if (arg.$kind === "GasCoin") usesGasCoin = true;
6
+ return arg;
7
+ });
8
+ return usesGasCoin;
9
+ }
10
+
11
+ //#endregion
12
+ export { transactionUsesGasCoin };
13
+ //# sourceMappingURL=resolution-utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution-utils.mjs","names":[],"sources":["../../src/transactions/resolution-utils.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { TransactionDataBuilder } from './TransactionData.js';\n\nexport function transactionUsesGasCoin(transactionData: TransactionDataBuilder) {\n\tlet usesGasCoin = false;\n\n\ttransactionData.mapArguments((arg) => {\n\t\tif (arg.$kind === 'GasCoin') {\n\t\t\tusesGasCoin = true;\n\t\t}\n\n\t\treturn arg;\n\t});\n\n\treturn usesGasCoin;\n}\n"],"mappings":";AAKA,SAAgB,uBAAuB,iBAAyC;CAC/E,IAAI,cAAc;AAElB,iBAAgB,cAAc,QAAQ;AACrC,MAAI,IAAI,UAAU,UACjB,eAAc;AAGf,SAAO;GACN;AAEF,QAAO"}
@@ -6,6 +6,14 @@ import "../client/index.mjs";
6
6
  interface BuildTransactionOptions {
7
7
  client?: ClientWithCoreApi;
8
8
  onlyTransactionKind?: boolean;
9
+ /**
10
+ * Resolve `CoinWithBalance` intents from address balance without looking up balances or coins.
11
+ *
12
+ * If nothing else needs resolution, the transaction doesn't use `GasCoin`, and it already has a
13
+ * `ValidDuring` or `Validity` expiration, an unset gas payment is also set to `[]` (pay gas from
14
+ * address balance). Execution fails if the balances aren't there.
15
+ */
16
+ assumeSufficientAddressBalances?: boolean;
9
17
  }
10
18
  interface SerializeTransactionOptions extends BuildTransactionOptions {
11
19
  supportedIntents?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.mts","names":[],"sources":["../../src/transactions/resolve.ts"],"mappings":";;;;;UAYiB,uBAAA;EAChB,MAAA,GAAS,iBAAA;EACT,mBAAA;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,KAGW,iBAAA,IACX,eAAA,EAAiB,sBAAA,EACjB,OAAA,EAAS,uBAAA,EACT,IAAA,QAAY,OAAA,WACR,OAAA"}
@@ -1,5 +1,6 @@
1
1
  import { bcs as suiBcs } from "../bcs/index.mjs";
2
2
  import { Inputs } from "./Inputs.mjs";
3
+ import { transactionUsesGasCoin } from "./resolution-utils.mjs";
3
4
  import { coreClientResolveTransactionPlugin } from "../client/core-resolver.mjs";
4
5
 
5
6
  //#region src/transactions/resolve.ts
@@ -8,14 +9,17 @@ function needsTransactionResolution(data, options) {
8
9
  return input.UnresolvedObject || input.UnresolvedPure;
9
10
  })) return true;
10
11
  if (!options.onlyTransactionKind) {
11
- if (!data.gasData.price || !data.gasData.budget || !data.gasData.payment) return true;
12
- if (data.gasData.payment.length === 0 && !data.expiration) return true;
12
+ if (!data.gasData.price || !data.gasData.budget) return true;
13
+ if (!data.gasData.payment) {
14
+ if (!(options.assumeSufficientAddressBalances && !transactionUsesGasCoin(data)) || data.expiration?.$kind !== "ValidDuring" && data.expiration?.$kind !== "Validity") return true;
15
+ } else if (data.gasData.payment.length === 0 && !data.expiration) return true;
13
16
  }
14
17
  return false;
15
18
  }
16
19
  async function resolveTransactionPlugin(transactionData, options, next) {
17
20
  normalizeRawArguments(transactionData);
18
21
  if (!needsTransactionResolution(transactionData, options)) {
22
+ if (!options.onlyTransactionKind && !transactionData.gasData.payment) transactionData.gasData.payment = [];
19
23
  await validate(transactionData);
20
24
  return next();
21
25
  }
@@ -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';\n\nexport interface BuildTransactionOptions {\n\tclient?: ClientWithCoreApi;\n\tonlyTransactionKind?: 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 || !data.gasData.payment) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (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\tif (!needsTransactionResolution(transactionData, options)) {\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":";;;;;AA2BA,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,UAAU,CAAC,KAAK,QAAQ,QAChE,QAAO;AAGR,MAAI,KAAK,QAAQ,QAAQ,WAAW,KAAK,CAAC,KAAK,WAC9C,QAAO;;AAIT,QAAO;;AAGR,eAAsB,yBACrB,iBACA,SACA,MACC;AACD,uBAAsB,gBAAgB;AACtC,KAAI,CAAC,2BAA2B,iBAAiB,QAAQ,EAAE;AAC1D,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\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"}
package/dist/version.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/version.ts
2
- const PACKAGE_VERSION = "2.28.0";
2
+ const PACKAGE_VERSION = "2.30.0";
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.28.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.30.0';\n"],"mappings":";AAKA,MAAa,kBAAkB"}
@@ -1,31 +1,31 @@
1
- import * as _mysten_bcs1245 from "@mysten/bcs";
1
+ import * as _mysten_bcs1238 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_bcs1245.BcsStruct<{
6
- inputs: _mysten_bcs1245.BcsStruct<{
7
- proofPoints: _mysten_bcs1245.BcsStruct<{
8
- a: _mysten_bcs1245.BcsType<string[], Iterable<string> & {
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> & {
9
9
  length: number;
10
10
  }, string>;
11
- b: _mysten_bcs1245.BcsType<string[][], Iterable<Iterable<string> & {
11
+ b: _mysten_bcs1238.BcsType<string[][], Iterable<Iterable<string> & {
12
12
  length: number;
13
13
  }> & {
14
14
  length: number;
15
15
  }, string>;
16
- c: _mysten_bcs1245.BcsType<string[], Iterable<string> & {
16
+ c: _mysten_bcs1238.BcsType<string[], Iterable<string> & {
17
17
  length: number;
18
18
  }, string>;
19
19
  }, string>;
20
- issBase64Details: _mysten_bcs1245.BcsStruct<{
21
- value: _mysten_bcs1245.BcsType<string, string, "string">;
22
- indexMod4: _mysten_bcs1245.BcsType<number, number, "u8">;
20
+ issBase64Details: _mysten_bcs1238.BcsStruct<{
21
+ value: _mysten_bcs1238.BcsType<string, string, "string">;
22
+ indexMod4: _mysten_bcs1238.BcsType<number, number, "u8">;
23
23
  }, string>;
24
- headerBase64: _mysten_bcs1245.BcsType<string, string, "string">;
25
- addressSeed: _mysten_bcs1245.BcsType<string, string, "string">;
24
+ headerBase64: _mysten_bcs1238.BcsType<string, string, "string">;
25
+ addressSeed: _mysten_bcs1238.BcsType<string, string, "string">;
26
26
  }, string>;
27
- maxEpoch: _mysten_bcs1245.BcsType<string, string | number | bigint, "u64">;
28
- userSignature: _mysten_bcs1245.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
27
+ maxEpoch: _mysten_bcs1238.BcsType<string, string | number | bigint, "u64">;
28
+ userSignature: _mysten_bcs1238.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
29
29
  }, string>;
30
30
  type ZkLoginSignature = InferBcsInput<typeof zkLoginSignature>;
31
31
  type ZkLoginSignatureInputs = ZkLoginSignature['inputs'];
@@ -55,16 +55,39 @@ console.log(transaction.digest, result.protoJson);
55
55
 
56
56
  ## Transport options
57
57
 
58
- By default, `SuiGrpcClient` uses `GrpcWebFetchTransport` from
59
- [protobuf-ts](https://github.com/timostamm/protobuf-ts), which works in browsers and Node.js through
60
- the Fetch API. The `GrpcWebFetchTransport` class, `GrpcWebOptions` type, and `RpcTransport` type are
61
- re-exported from `@mysten/sui/grpc`, so you can configure a transport without adding
62
- `@protobuf-ts/*` as a direct dependency.
58
+ By default, `SuiGrpcClient` uses `GrpcWebFetchTransport`, which works in browsers and Node.js
59
+ through the Fetch API. `@mysten/sui/grpc` exports it alongside the `GrpcWebOptions` and
60
+ `RpcTransport` types, and the `RpcError` class and `GrpcStatusCode` enum that gRPC failures use, so
61
+ you can configure a transport and handle its errors without adding `@protobuf-ts/*` as a direct
62
+ dependency.
63
+
64
+ That class extends the transport of the same name from
65
+ [protobuf-ts](https://github.com/timostamm/protobuf-ts) and fixes two defects in it:
66
+
67
+ - It decodes status messages, rather than delivering them as `Object%20not%20found:%200x1`.
68
+ - It codes a call that an abort ended from the reason the abort carried: `DEADLINE_EXCEEDED` for an
69
+ `AbortSignal.timeout`, the status on a reason carrying one, and `CANCELLED` otherwise. Upstream
70
+ reports all but a standard `AbortError` cancellation as `INTERNAL`.
71
+
72
+ Build custom transports from this class. `SuiGrpcClient` uses a transport you pass it as given.
63
73
 
64
74
  ### gRPC-web transport (default)
65
75
 
66
- The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. Pass
67
- `GrpcWebFetchTransport` options to customize it:
76
+ The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. `SuiGrpcClient` passes the
77
+ `GrpcWebOptions` it is given to the transport it builds, so most configuration needs no transport of
78
+ your own. The one exception is `abort`: Core API calls each carry their own signal, so a client-wide
79
+ one is not forwarded.
80
+
81
+ ```typescript
82
+
83
+ const client = new SuiGrpcClient({
84
+ network: 'testnet',
85
+ baseUrl: 'https://your-custom-grpc-endpoint.com',
86
+ format: 'binary', // default is 'text' (base64-encoded)
87
+ });
88
+ ```
89
+
90
+ Build the transport yourself to share one between clients, or to subclass it:
68
91
 
69
92
  ```typescript
70
93
 
@@ -228,6 +251,7 @@ const result = parseGrpcTransactionResponse(response.transaction!, {
228
251
  | `parseGrpcTransactionResponse` | Raw `ExecutedTransaction` to the unified result shape |
229
252
  | `parseGrpcSimulateTransactionResponse` | Raw simulation response to the unified result shape |
230
253
  | `GrpcWebFetchTransport`, `GrpcWebOptions`, `RpcTransport` | Configuring a [transport](#transport-options) |
254
+ | `RpcError`, `GrpcStatusCode` | Narrowing and coding gRPC failures |
231
255
  | `SuiGrpcClientOptions`, `GrpcTransactionInclude`, … | Typing your own wrappers around the client |
232
256
  | `isSuiGrpcClient` | Type guard for narrowing an unknown client |
233
257
 
@@ -95,7 +95,7 @@ use the same replacement under `client.core`.
95
95
  | `getBalance` | `getBalance` |
96
96
  | `getCoinMetadata` | `getCoinMetadata` |
97
97
  | `getDynamicFields` | `listDynamicFields` |
98
- | `getDynamicFieldObject` | `getDynamicField` or `client.core.getDynamicObjectField` |
98
+ | `getDynamicFieldObject` | `getDynamicField` or `getDynamicObjectField`, depending on field kind |
99
99
  | `getTransactionBlock` | `getTransaction` |
100
100
  | `multiGetTransactionBlocks` | Multiple `getTransaction` calls |
101
101
  | `executeTransactionBlock` | `executeTransaction` |
@@ -109,6 +109,55 @@ use the same replacement under `client.core`.
109
109
  | `resolveNameServiceAddress` | `resolveNameServiceAddress` (returns `{ address }`) |
110
110
  | `resolveNameServiceNames` | No direct equivalent for listing every name assigned to an address |
111
111
 
112
+ The JSON-RPC `getDynamicFieldObject` method returned an object for both field kinds. For a regular
113
+ dynamic field, it returned the `0x2::dynamic_field::Field<Name, Value>` object. For a dynamic object
114
+ field, it derived the wrapper field, extracted its child ID, and returned the referenced child
115
+ object instead of the wrapper.
116
+
117
+ The replacement APIs expose these operations separately. `getDynamicField` returns a normalized
118
+ field entry and its BCS-encoded value. `getDynamicObjectField` derives the wrapper, extracts the
119
+ child ID, and loads the referenced object. If the field kind is not known in advance, call
120
+ `listDynamicFields` and check whether its `$kind` is `DynamicField` or `DynamicObject`. SDKs can
121
+ call the same methods through `client.core`.
122
+
123
+ ```typescript
124
+ const page = await client.listDynamicFields({ parentId });
125
+
126
+ for (const field of page.dynamicFields) {
127
+ if (field.$kind === 'DynamicObject') {
128
+ const { object } = await client.getDynamicObjectField({
129
+ parentId,
130
+ name: field.name,
131
+ include: { content: true },
132
+ });
133
+
134
+ console.log(object.objectId, object.content);
135
+ } else {
136
+ const { dynamicField } = await client.getDynamicField({
137
+ parentId,
138
+ name: field.name,
139
+ });
140
+
141
+ console.log(dynamicField.value.type, dynamicField.value.bcs);
142
+ }
143
+ }
144
+ ```
145
+
146
+ The dynamic object field wrapper remains accessible through `getDynamicField`. Wrap the name type
147
+ explicitly and pass the original name BCS bytes:
148
+
149
+ ```typescript
150
+ const { dynamicField } = await client.getDynamicField({
151
+ parentId,
152
+ name: {
153
+ type: `0x2::dynamic_object_field::Wrapper<${fieldName.type}>`,
154
+ bcs: fieldName.bcs,
155
+ },
156
+ });
157
+
158
+ console.log(dynamicField.fieldId, dynamicField.childId);
159
+ ```
160
+
112
161
  `getMoveFunction` exposes normalized parameter signatures, but it does not reproduce the legacy
113
162
  `Pure`, `Object`, and object-access classifications from `getMoveFunctionArgTypes`. Likewise,
114
163
  `defaultNameServiceName` and raw gRPC `nameService.reverseLookupName` return only the configured
@@ -126,14 +126,13 @@ incorrect target strings. See the [codegen documentation](/codegen) for setup in
126
126
 
127
127
  ### Return values
128
128
 
129
- Commands return results that you can use as input to subsequent commands. For example, `splitCoins`
130
- returns the new coins it creates:
129
+ Helpers and commands return results that you can use as input to subsequent commands. For example,
130
+ `tx.coin()` creates a coin with the requested balance:
131
131
 
132
132
  ```typescript
133
133
  const tx = new Transaction();
134
134
 
135
- // splitCoins returns one result per amount
136
- const [coin] = tx.splitCoins(tx.gas, [1_000_000]);
135
+ const coin = tx.coin({ balance: 1_000_000 });
137
136
 
138
137
  // Use that result as input to another command
139
138
  tx.transferObjects([coin], '0xRecipientAddress');
@@ -256,21 +255,27 @@ libraries.
256
255
 
257
256
  ## Serializing transactions
258
257
 
259
- Serialize a transaction to JSON for storage, transmission, or later reconstruction:
258
+ `build()` produces BCS bytes for signing. With a client it resolves object versions, gas, and
259
+ intents like `tx.coin()` first. If everything is already set, no client is needed (see
260
+ [Building Offline](./offline)):
260
261
 
261
262
  ```typescript
262
- // Serialize to JSON with a client, resolves any unresolved data first
263
- const json = await tx.toJSON({ client: grpcClient });
264
-
265
- // Serialize without a client — intents like tx.coin() are preserved as-is
266
- const json = await tx.toJSON();
263
+ const bytes = await tx.build({ client: grpcClient });
267
264
 
268
- // Reconstruct from JSON
269
- const tx = Transaction.from(json);
265
+ // Only the inputs and commands, without sender or gas data
266
+ const kindBytes = await tx.build({ client: grpcClient, onlyTransactionKind: true });
270
267
  ```
271
268
 
272
- This is useful for passing transactions between a frontend and backend, or for storing pre-built
273
- transactions.
269
+ `toJSON()` produces an editable representation for storage or for passing between a frontend and
270
+ backend. It resolves intents like `tx.coin()` but leaves sender and gas data as they are:
271
+
272
+ ```typescript
273
+ const json = await tx.toJSON({ client: grpcClient });
274
+ const restored = Transaction.from(json);
275
+
276
+ // Or keep tx.coin() intents for the receiver to resolve
277
+ const jsonWithIntents = await tx.toJSON({ supportedIntents: ['CoinWithBalance'] });
278
+ ```
274
279
 
275
280
  ## Executing transactions
276
281