@mysten/sui 2.22.1 → 2.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +38 -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 +1 -19
  5. package/dist/bcs/bcs.mjs.map +1 -1
  6. package/dist/bcs/index.d.mts +3812 -906
  7. package/dist/bcs/index.d.mts.map +1 -1
  8. package/dist/bcs/index.mjs +2 -1
  9. package/dist/bcs/index.mjs.map +1 -1
  10. package/dist/bcs/transactions.mjs +147 -0
  11. package/dist/bcs/transactions.mjs.map +1 -0
  12. package/dist/client/core-resolver.mjs +2 -1
  13. package/dist/client/core-resolver.mjs.map +1 -1
  14. package/dist/client/core.d.mts +2 -0
  15. package/dist/client/core.d.mts.map +1 -1
  16. package/dist/client/core.mjs.map +1 -1
  17. package/dist/client/query-filters.mjs +119 -0
  18. package/dist/client/query-filters.mjs.map +1 -0
  19. package/dist/client/types.d.mts +166 -1
  20. package/dist/client/types.d.mts.map +1 -1
  21. package/dist/client/utils.d.mts.map +1 -1
  22. package/dist/client/utils.mjs +13 -1
  23. package/dist/client/utils.mjs.map +1 -1
  24. package/dist/cryptography/signature.d.mts +6 -6
  25. package/dist/graphql/client.d.mts +15 -2
  26. package/dist/graphql/client.d.mts.map +1 -1
  27. package/dist/graphql/client.mjs +6 -0
  28. package/dist/graphql/client.mjs.map +1 -1
  29. package/dist/graphql/core.d.mts +5 -1
  30. package/dist/graphql/core.d.mts.map +1 -1
  31. package/dist/graphql/core.mjs +85 -3
  32. package/dist/graphql/core.mjs.map +1 -1
  33. package/dist/graphql/generated/queries.d.mts.map +1 -1
  34. package/dist/graphql/generated/queries.mjs +137 -1
  35. package/dist/graphql/generated/queries.mjs.map +1 -1
  36. package/dist/graphql/generated/tada-env.d.mts +108 -0
  37. package/dist/graphql/index.d.mts +2 -2
  38. package/dist/grpc/client.d.mts +11 -0
  39. package/dist/grpc/client.d.mts.map +1 -1
  40. package/dist/grpc/client.mjs +6 -0
  41. package/dist/grpc/client.mjs.map +1 -1
  42. package/dist/grpc/core.d.mts +5 -1
  43. package/dist/grpc/core.d.mts.map +1 -1
  44. package/dist/grpc/core.mjs +100 -2
  45. package/dist/grpc/core.mjs.map +1 -1
  46. package/dist/grpc/filters.mjs +45 -0
  47. package/dist/grpc/filters.mjs.map +1 -0
  48. package/dist/grpc/proto/sui/rpc/v2/event.d.mts +31 -0
  49. package/dist/grpc/proto/sui/rpc/v2/event.d.mts.map +1 -1
  50. package/dist/grpc/proto/sui/rpc/v2/event.mjs +30 -0
  51. package/dist/grpc/proto/sui/rpc/v2/event.mjs.map +1 -1
  52. package/dist/grpc/proto/sui/rpc/v2/executed_transaction.d.mts +7 -0
  53. package/dist/grpc/proto/sui/rpc/v2/executed_transaction.d.mts.map +1 -1
  54. package/dist/grpc/proto/sui/rpc/v2/executed_transaction.mjs +8 -0
  55. package/dist/grpc/proto/sui/rpc/v2/executed_transaction.mjs.map +1 -1
  56. package/dist/grpc/proto/sui/rpc/v2/filter.d.mts +423 -0
  57. package/dist/grpc/proto/sui/rpc/v2/filter.d.mts.map +1 -0
  58. package/dist/grpc/proto/sui/rpc/v2/filter.mjs +297 -0
  59. package/dist/grpc/proto/sui/rpc/v2/filter.mjs.map +1 -0
  60. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts +62 -6
  61. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.d.mts.map +1 -1
  62. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.mjs +37 -0
  63. package/dist/grpc/proto/sui/rpc/v2/ledger_service.client.mjs.map +1 -1
  64. package/dist/grpc/proto/sui/rpc/v2/ledger_service.d.mts +293 -1
  65. package/dist/grpc/proto/sui/rpc/v2/ledger_service.d.mts.map +1 -1
  66. package/dist/grpc/proto/sui/rpc/v2/ledger_service.mjs +241 -1
  67. package/dist/grpc/proto/sui/rpc/v2/ledger_service.mjs.map +1 -1
  68. package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
  69. package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
  70. package/dist/grpc/proto/sui/rpc/v2/protocol_config.d.mts +15 -2
  71. package/dist/grpc/proto/sui/rpc/v2/protocol_config.d.mts.map +1 -1
  72. package/dist/grpc/proto/sui/rpc/v2/protocol_config.mjs +11 -0
  73. package/dist/grpc/proto/sui/rpc/v2/protocol_config.mjs.map +1 -1
  74. package/dist/grpc/proto/sui/rpc/v2/query_options.d.mts +211 -0
  75. package/dist/grpc/proto/sui/rpc/v2/query_options.d.mts.map +1 -0
  76. package/dist/grpc/proto/sui/rpc/v2/query_options.mjs +163 -0
  77. package/dist/grpc/proto/sui/rpc/v2/query_options.mjs.map +1 -0
  78. package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
  79. package/dist/grpc/proto/sui/rpc/v2/state_service.client.d.mts +4 -4
  80. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +105 -23
  81. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts.map +1 -1
  82. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.mjs +56 -9
  83. package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.mjs.map +1 -1
  84. package/dist/grpc/proto/sui/rpc/v2/subscription_service.d.mts +148 -8
  85. package/dist/grpc/proto/sui/rpc/v2/subscription_service.d.mts.map +1 -1
  86. package/dist/grpc/proto/sui/rpc/v2/subscription_service.mjs +109 -8
  87. package/dist/grpc/proto/sui/rpc/v2/subscription_service.mjs.map +1 -1
  88. package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
  89. package/dist/grpc/proto/types.d.mts +5 -3
  90. package/dist/grpc/proto/types.mjs +33 -2
  91. package/dist/grpc/proto/types.mjs.map +1 -1
  92. package/dist/jsonRpc/core.d.mts +10 -0
  93. package/dist/jsonRpc/core.d.mts.map +1 -1
  94. package/dist/jsonRpc/core.mjs +85 -2
  95. package/dist/jsonRpc/core.mjs.map +1 -1
  96. package/dist/transactions/Transaction.d.mts +9 -9
  97. package/dist/transactions/Transaction.d.mts.map +1 -1
  98. package/dist/transactions/intents/CoinWithBalance.mjs +14 -18
  99. package/dist/transactions/intents/CoinWithBalance.mjs.map +1 -1
  100. package/dist/version.mjs +1 -1
  101. package/dist/version.mjs.map +1 -1
  102. package/dist/zklogin/bcs.d.mts +14 -14
  103. package/dist/zklogin/bcs.d.mts.map +1 -1
  104. package/docs/clients/core.md +115 -1
  105. package/docs/clients/grpc.md +68 -0
  106. package/docs/migrations/sui-2.0/json-rpc-migration.md +104 -101
  107. package/package.json +2 -2
  108. package/src/bcs/bcs.ts +0 -27
  109. package/src/bcs/index.ts +7 -5
  110. package/src/bcs/transactions.ts +226 -0
  111. package/src/client/core-resolver.ts +5 -1
  112. package/src/client/core.ts +8 -0
  113. package/src/client/query-filters.ts +195 -0
  114. package/src/client/types.ts +186 -1
  115. package/src/client/utils.ts +13 -0
  116. package/src/graphql/client.ts +25 -1
  117. package/src/graphql/core.ts +143 -3
  118. package/src/graphql/generated/queries.ts +226 -0
  119. package/src/graphql/generated/schema.graphql +75 -0
  120. package/src/graphql/generated/tada-env.ts +133 -0
  121. package/src/graphql/index.ts +1 -0
  122. package/src/graphql/queries/listEvents.graphql +40 -0
  123. package/src/graphql/queries/transactions.graphql +26 -0
  124. package/src/grpc/client.ts +19 -0
  125. package/src/grpc/core.ts +187 -3
  126. package/src/grpc/filters.ts +68 -0
  127. package/src/grpc/proto/sui/rpc/v2/event.ts +49 -0
  128. package/src/grpc/proto/sui/rpc/v2/executed_transaction.ts +15 -0
  129. package/src/grpc/proto/sui/rpc/v2/filter.ts +589 -0
  130. package/src/grpc/proto/sui/rpc/v2/ledger_service.client.ts +111 -0
  131. package/src/grpc/proto/sui/rpc/v2/ledger_service.ts +406 -0
  132. package/src/grpc/proto/sui/rpc/v2/protocol_config.ts +22 -2
  133. package/src/grpc/proto/sui/rpc/v2/query_options.ts +255 -0
  134. package/src/grpc/proto/sui/rpc/v2/subscription_service.client.ts +136 -18
  135. package/src/grpc/proto/sui/rpc/v2/subscription_service.ts +187 -7
  136. package/src/grpc/proto/types.ts +2 -0
  137. package/src/jsonRpc/core.ts +136 -2
  138. package/src/transactions/intents/CoinWithBalance.ts +41 -41
  139. package/src/version.ts +1 -1
@@ -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 mergedCoins = new Map<string, Argument>();\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\tmergedCoins.set(type, baseCoin);\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\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\t// Step 3: Remainder handling\n\tfor (const [type, mergedCoin] of mergedCoins) {\n\t\t// When gas type used GasCoin (not AB), leftover stays in the gas coin — no remainder needed.\n\t\tif (type === 'gas' && !usedAddressBalance.has(type)) continue;\n\n\t\tconst coinType = type === 'gas' ? SUI_TYPE : type;\n\t\tconst hasBalanceIntent = intentsByType.get(type)?.some((i) => i.outputKind === 'balance');\n\t\tconst sourcedFromAB = usedAddressBalance.has(type);\n\n\t\tif (hasBalanceIntent || sourcedFromAB) {\n\t\t\t// Sourced from AB or balance intents exist: send remainder back to sender's address balance.\n\t\t\t// coin::send_funds is gasless-eligible and handles zero amounts.\n\t\t\ttransactionData.commands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: '0x2::coin::send_funds',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\targuments: [\n\t\t\t\t\t\tmergedCoin,\n\t\t\t\t\t\ttransactionData.addInput(\n\t\t\t\t\t\t\t'pure',\n\t\t\t\t\t\t\tInputs.Pure(bcs.Address.serialize(transactionData.sender!)),\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\t\t} else if (exactBalanceByType.get(type)) {\n\t\t\t// Coin-only with exact match: destroy the zero-value dust coin.\n\t\t\ttransactionData.commands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\ttarget: '0x2::coin::destroy_zero',\n\t\t\t\t\ttypeArguments: [coinType],\n\t\t\t\t\targuments: [mergedCoin],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// Coin-only with surplus: merged coin stays with sender as an owned object\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\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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"}
package/dist/version.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/version.ts
2
- const PACKAGE_VERSION = "2.22.1";
2
+ const PACKAGE_VERSION = "2.23.1";
3
3
 
4
4
  //#endregion
5
5
  export { PACKAGE_VERSION };
@@ -1 +1 @@
1
- {"version":3,"file":"version.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n// This file is generated by genversion.mjs. Do not edit it directly.\n\nexport const PACKAGE_VERSION = '2.22.1';\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.23.1';\n"],"mappings":";AAKA,MAAa,kBAAkB"}
@@ -1,31 +1,31 @@
1
- import * as _mysten_bcs810 from "@mysten/bcs";
1
+ import * as _mysten_bcs1343 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_bcs810.BcsStruct<{
6
- inputs: _mysten_bcs810.BcsStruct<{
7
- proofPoints: _mysten_bcs810.BcsStruct<{
8
- a: _mysten_bcs810.BcsType<string[], Iterable<string> & {
5
+ declare const zkLoginSignature: _mysten_bcs1343.BcsStruct<{
6
+ inputs: _mysten_bcs1343.BcsStruct<{
7
+ proofPoints: _mysten_bcs1343.BcsStruct<{
8
+ a: _mysten_bcs1343.BcsType<string[], Iterable<string> & {
9
9
  length: number;
10
10
  }, string>;
11
- b: _mysten_bcs810.BcsType<string[][], Iterable<Iterable<string> & {
11
+ b: _mysten_bcs1343.BcsType<string[][], Iterable<Iterable<string> & {
12
12
  length: number;
13
13
  }> & {
14
14
  length: number;
15
15
  }, string>;
16
- c: _mysten_bcs810.BcsType<string[], Iterable<string> & {
16
+ c: _mysten_bcs1343.BcsType<string[], Iterable<string> & {
17
17
  length: number;
18
18
  }, string>;
19
19
  }, string>;
20
- issBase64Details: _mysten_bcs810.BcsStruct<{
21
- value: _mysten_bcs810.BcsType<string, string, "string">;
22
- indexMod4: _mysten_bcs810.BcsType<number, number, "u8">;
20
+ issBase64Details: _mysten_bcs1343.BcsStruct<{
21
+ value: _mysten_bcs1343.BcsType<string, string, "string">;
22
+ indexMod4: _mysten_bcs1343.BcsType<number, number, "u8">;
23
23
  }, string>;
24
- headerBase64: _mysten_bcs810.BcsType<string, string, "string">;
25
- addressSeed: _mysten_bcs810.BcsType<string, string, "string">;
24
+ headerBase64: _mysten_bcs1343.BcsType<string, string, "string">;
25
+ addressSeed: _mysten_bcs1343.BcsType<string, string, "string">;
26
26
  }, string>;
27
- maxEpoch: _mysten_bcs810.BcsType<string, string | number | bigint, "u64">;
28
- userSignature: _mysten_bcs810.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
27
+ maxEpoch: _mysten_bcs1343.BcsType<string, string | number | bigint, "u64">;
28
+ userSignature: _mysten_bcs1343.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
29
29
  }, string>;
30
30
  type ZkLoginSignature = InferBcsInput<typeof zkLoginSignature>;
31
31
  type ZkLoginSignatureInputs = ZkLoginSignature['inputs'];
@@ -1 +1 @@
1
- {"version":3,"file":"bcs.d.mts","names":[],"sources":["../../src/zklogin/bcs.ts"],"mappings":";;;;cAMa,gBAAA,iBAAgB,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;KAkBjB,gBAAA,GAAmB,aAAA,QAAqB,gBAAA;AAAA,KACxC,sBAAA,GAAyB,gBAAA"}
1
+ {"version":3,"file":"bcs.d.mts","names":[],"sources":["../../src/zklogin/bcs.ts"],"mappings":";;;;cAMa,gBAAA,kBAAgB,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;KAkBjB,gBAAA,GAAmB,aAAA,QAAqB,gBAAA;AAAA,KACxC,sBAAA,GAAyB,gBAAA"}
@@ -439,6 +439,115 @@ const finalResult = await client.core.waitForTransaction({
439
439
  });
440
440
  ```
441
441
 
442
+ ## Query methods
443
+
444
+ Query methods return pages of transactions or events. They expose the same filters, pagination, and
445
+ results on every transport, and share a common set of options. A few transport-specific caveats
446
+ (page-size caps, gRPC scan-limited pages, generic event types and missing event checkpoints on the
447
+ deprecated JSON-RPC transport, and GraphQL's per-transaction event limit) are documented on the
448
+ corresponding option and response types in `SuiClientTypes`.
449
+
450
+ | Option | Type | Description |
451
+ | -------- | ---------------------------------- | ----------------------------------------------------- |
452
+ | `filter` | `TransactionFilter \| EventFilter` | Filter to apply (transactions and events) |
453
+ | `limit` | `number` | Maximum number of items to return (defaults to 50) |
454
+ | `after` | `string \| null` | Return items strictly after this cursor (ascending) |
455
+ | `before` | `string \| null` | Return items strictly before this cursor (descending) |
456
+ | `order` | `'ascending' \| 'descending'` | Order of results (defaults to `ascending`) |
457
+
458
+ A filter specifies exactly one predicate. MVR names in `function` and `eventType` predicates are
459
+ resolved automatically.
460
+
461
+ `after` and `before` are exclusive ledger-position bounds, and each page reports the positions of
462
+ its first and last items as `startCursor` and `endCursor`. You can provide at most one bound per
463
+ query, and the bound implies the traversal direction (`after` reads ascending, `before` reads
464
+ descending), so a feed of recent activity can page in both directions from any point:
465
+
466
+ ```typescript
467
+ // Fetch the most recent transactions
468
+ const latest = await client.core.listTransactions({
469
+ filter: { sender: '0xabc...' },
470
+ order: 'descending',
471
+ limit: 10,
472
+ });
473
+
474
+ // Load older transactions
475
+ const older = await client.core.listTransactions({
476
+ filter: { sender: '0xabc...' },
477
+ before: latest.endCursor,
478
+ limit: 10,
479
+ });
480
+
481
+ // Poll for transactions executed since
482
+ const newer = await client.core.listTransactions({
483
+ filter: { sender: '0xabc...' },
484
+ after: latest.startCursor,
485
+ });
486
+ ```
487
+
488
+ > **Note:** Richer filtering (combined predicates, additional predicates like affected addresses and objects,
489
+ > and checkpoint ranges on transaction and event queries) is available through the [raw gRPC ledger
490
+ > service](/sui/clients/grpc#ledger-service) and [raw GraphQL queries](/sui/clients/graphql).
491
+
492
+ ### `listTransactions`
493
+
494
+ Query transactions matching a filter. Results support the same `include` options as
495
+ [`getTransaction`](#gettransaction).
496
+
497
+ ```typescript
498
+ const result = await client.core.listTransactions({
499
+ filter: { function: '0x2::coin::transfer' },
500
+ limit: 10,
501
+ include: { effects: true },
502
+ });
503
+
504
+ for (const tx of result.transactions) {
505
+ const transaction = tx.Transaction ?? tx.FailedTransaction;
506
+ console.log(transaction.digest, tx.$kind);
507
+ }
508
+
509
+ // Paginate
510
+ if (result.hasNextPage) {
511
+ const nextPage = await client.core.listTransactions({
512
+ filter: { function: '0x2::coin::transfer' },
513
+ after: result.endCursor,
514
+ });
515
+ }
516
+ ```
517
+
518
+ Transaction filters support one of the following predicates:
519
+
520
+ | Predicate | Description |
521
+ | ---------- | --------------------------------------------------------------------------- |
522
+ | `sender` | Transactions sent by an address |
523
+ | `function` | Transactions calling a Move function (`pkg`, `pkg::mod`, or `pkg::mod::fn`) |
524
+
525
+ ### `listEvents`
526
+
527
+ Query events matching a filter. Each event includes its position in the ledger (`transactionDigest`
528
+ and `eventIndex`, plus `checkpoint` on transports that can provide it; `checkpoint` is `null` on
529
+ JSON-RPC).
530
+
531
+ ```typescript
532
+ const result = await client.core.listEvents({
533
+ filter: { eventType: '0x2::coin::CoinCreated' },
534
+ order: 'descending',
535
+ limit: 10,
536
+ });
537
+
538
+ for (const event of result.events) {
539
+ console.log(event.eventType, event.transactionDigest, event.json);
540
+ }
541
+ ```
542
+
543
+ Event filters support one of the following predicates:
544
+
545
+ | Predicate | Description |
546
+ | ------------ | --------------------------------------------------------------------------------- |
547
+ | `sender` | Events from transactions sent by an address |
548
+ | `emitModule` | Events emitted by a module (`pkg::mod`) |
549
+ | `eventType` | Events with types defined in a module (`pkg::mod`) or a fully qualified type name |
550
+
442
551
  ## System methods
443
552
 
444
553
  ### `getReferenceGasPrice`
@@ -591,6 +700,9 @@ const options: SuiClientTypes.GetObjectOptions<{ content: true }> = {
591
700
  | `TransactionResult` | Success or failure result from execution |
592
701
  | `TransactionEffects` | Detailed effects from transaction execution |
593
702
  | `Event` | Emitted event from a transaction |
703
+ | `EventEntry` | Queried event with its ledger position |
704
+ | `TransactionFilter` | Filter for transaction queries |
705
+ | `EventFilter` | Filter for event queries |
594
706
  | `ObjectOwner` | Union of all owner types |
595
707
  | `ExecutionStatus` | Success/failure status with error details |
596
708
  | `DynamicFieldName` | Name identifier for dynamic fields |
@@ -620,4 +732,6 @@ const options: SuiClientTypes.GetObjectOptions<{ content: true }> = {
620
732
  | `SimulateTransactionOptions` | Options for `simulateTransaction` |
621
733
  | `SignAndExecuteTransactionOptions` | Options for `signAndExecuteTransaction` |
622
734
  | `GetTransactionOptions` | Options for `getTransaction` |
623
- | `WaitForTransactionOptions` | Options for `waitForTransaction` |
735
+ | `WaitForTransactionOptions` | Options for `waitForTransaction` |
736
+ | `ListTransactionsOptions` | Options for `listTransactions` |
737
+ | `ListEventsOptions` | Options for `listEvents` |
@@ -157,6 +157,74 @@ const { response } = await grpcClient.ledgerService.getTransaction({
157
157
  const { response: epochInfo } = await grpcClient.ledgerService.getEpoch({});
158
158
  ```
159
159
 
160
+ The ledger service also provides streaming `listCheckpoints`, `listTransactions`, and `listEvents`
161
+ RPCs. For most use cases, prefer the corresponding
162
+ [core API query methods](/sui/clients/core#query-methods) (`listTransactions` and `listEvents`),
163
+ which handle pagination and filter construction for you. The raw RPCs additionally support DNF
164
+ filters (combined, negated, and additional predicates like `affected_address` and `package_write`)
165
+ and checkpoint range bounds that the core API does not expose:
166
+
167
+ ```typescript
168
+ // Transactions that affected an address but were not sent by it:
169
+ const stream = grpcClient.ledgerService.listTransactions({
170
+ filter: {
171
+ terms: [
172
+ {
173
+ literals: [
174
+ {
175
+ negated: false,
176
+ predicate: {
177
+ oneofKind: 'affectedAddress',
178
+ affectedAddress: { address: '0xabc...' },
179
+ },
180
+ },
181
+ {
182
+ negated: true,
183
+ predicate: { oneofKind: 'sender', sender: { address: '0xabc...' } },
184
+ },
185
+ ],
186
+ },
187
+ ],
188
+ },
189
+ readMask: { paths: ['digest'] },
190
+ });
191
+
192
+ for await (const frame of stream.responses) {
193
+ if (frame.transaction) {
194
+ console.log(frame.transaction.digest);
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### Subscription service
200
+
201
+ Subscribe to filtered, real-time streams of checkpoints, transactions, or events. Streams begin at
202
+ the current tip of the chain:
203
+
204
+ ```typescript
205
+ const stream = grpcClient.subscriptionService.subscribeTransactions({
206
+ filter: {
207
+ terms: [
208
+ {
209
+ literals: [
210
+ {
211
+ negated: false,
212
+ predicate: { oneofKind: 'sender', sender: { address: '0xabc...' } },
213
+ },
214
+ ],
215
+ },
216
+ ],
217
+ },
218
+ readMask: { paths: ['digest', 'effects.status'] },
219
+ });
220
+
221
+ for await (const frame of stream.responses) {
222
+ if (frame.transaction) {
223
+ console.log(frame.transaction.digest);
224
+ }
225
+ }
226
+ ```
227
+
160
228
  ### State service
161
229
 
162
230
  ```typescript
@@ -5,15 +5,16 @@
5
5
  This guide covers migrating from `SuiJsonRpcClient` to the new client APIs. The JSON-RPC API is
6
6
  being deprecated in favor of `SuiGrpcClient` and `SuiGraphQLClient`.
7
7
 
8
- > **Note:** We recommend using `SuiGrpcClient` for most operations and `SuiGraphQLClient` for complex queries
9
- > like filtering transactions and events.
8
+ > **Note:** Use `SuiGrpcClient` for most operations, including filtering transactions and events with the core
9
+ > query API. `SuiGraphQLClient` remains useful for complex queries that aren't covered by the core
10
+ > API.
10
11
 
11
12
  ## Choosing a client
12
13
 
13
- | Client | Best For |
14
- | ------------------ | --------------------------------------------------------------- |
15
- | `SuiGrpcClient` | Most operations, SDK integrations, real-time data |
16
- | `SuiGraphQLClient` | Complex queries, filtering transactions/events, historical data |
14
+ | Client | Best For |
15
+ | ------------------ | -------------------------------------------------------------------- |
16
+ | `SuiGrpcClient` | Most operations, SDK integrations, queries, real-time data |
17
+ | `SuiGraphQLClient` | Complex queries not covered by the core API, like historical objects |
17
18
 
18
19
  ## Quick migration to gRPC
19
20
 
@@ -58,6 +59,58 @@ These JSON-RPC methods have direct replacements in the core API:
58
59
  | `dryRunTransactionBlock` | `simulateTransaction` |
59
60
  | `getNormalizedMoveFunction` | `getMoveFunction` |
60
61
  | `getMoveFunctionArgTypes` | `getMoveFunction` |
62
+ | `queryTransactionBlocks` | `listTransactions` |
63
+ | `queryEvents` | `listEvents` |
64
+
65
+ > **Note:** The query methods (`listTransactions` and `listEvents`) behave identically on every client,
66
+ > including `SuiJsonRpcClient`. See [Core API query methods](/sui/clients/core#query-methods) for
67
+ > details.
68
+
69
+ ### Example: Migrating queryTransactionBlocks
70
+
71
+ ```diff
72
+ - const result = await jsonRpcClient.queryTransactionBlocks({
73
+ - filter: { FromAddress: '0xabc...' },
74
+ - options: { showEffects: true },
75
+ - limit: 10,
76
+ - });
77
+ - const digests = result.data.map((tx) => tx.digest);
78
+ + const result = await client.core.listTransactions({
79
+ + filter: { sender: '0xabc...' },
80
+ + include: { effects: true },
81
+ + limit: 10,
82
+ + });
83
+ + const digests = result.transactions.map(
84
+ + (tx) => (tx.Transaction ?? tx.FailedTransaction).digest,
85
+ + );
86
+ ```
87
+
88
+ The core API filters map to the JSON-RPC `TransactionFilter` variants:
89
+
90
+ | JSON-RPC filter | Core API filter predicate |
91
+ | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
92
+ | `FromAddress` | `sender` |
93
+ | `MoveFunction` | `function` |
94
+ | `ToAddress` / `FromOrToAddress` / `ChangedObject` / `AffectedObject` | No core equivalent. Use raw [gRPC](/sui/clients/grpc#ledger-service) or [GraphQL](/sui/clients/graphql) queries |
95
+
96
+ ### Example: Migrating queryEvents
97
+
98
+ ```diff
99
+ - const result = await jsonRpcClient.queryEvents({
100
+ - query: { MoveEventType: '0x2::coin::CoinCreated' },
101
+ - limit: 10,
102
+ - order: 'descending',
103
+ - });
104
+ + const result = await client.core.listEvents({
105
+ + filter: { eventType: '0x2::coin::CoinCreated' },
106
+ + limit: 10,
107
+ + order: 'descending',
108
+ + });
109
+ ```
110
+
111
+ The JSON-RPC `EventFilter` variants map to `sender`, `emitModule` (replaces `MoveModule` /
112
+ `MoveEventModule`), and `eventType` (replaces `MoveEventType`). Each returned event includes its
113
+ ledger position (`checkpoint`, `transactionDigest`, and `eventIndex`).
61
114
 
62
115
  ### Example: Migrating devInspectTransactionBlock
63
116
 
@@ -95,6 +148,7 @@ These JSON-RPC methods can be replaced by calling gRPC service clients directly:
95
148
  | JSON-RPC Method | gRPC Service Replacement |
96
149
  | ----------------------------------- | ---------------------------------------------------------------- |
97
150
  | `getCheckpoint` | `ledgerService.getCheckpoint` |
151
+ | `getCheckpoints` | `ledgerService.listCheckpoints` |
98
152
  | `getLatestCheckpointSequenceNumber` | `ledgerService.getServiceInfo` (read `checkpointHeight`) |
99
153
  | `getCurrentEpoch` | `ledgerService.getEpoch` (omit `epoch` for the current) |
100
154
  | `getLatestSuiSystemState` | `ledgerService.getEpoch` with `system_state` in the read mask |
@@ -108,10 +162,10 @@ These JSON-RPC methods can be replaced by calling gRPC service clients directly:
108
162
  | `resolveNameServiceAddress` | `nameService.lookupName` |
109
163
  | `resolveNameServiceNames` | `nameService.reverseLookupName` |
110
164
 
111
- > **Warning:** `getCheckpoints` and `getEpochs` (paginated listings) have no gRPC equivalent use the
112
- > `checkpoints` / `epochs` GraphQL queries instead. `getValidatorsApy` also has no replacement:
113
- > there is no canonical definition of validator APY, so it must be computed client-side from
114
- > validator exchange rates (see [Validator APY](#validator-apy) below).
165
+ > **Warning:** `getEpochs` (paginated listing) has no gRPC equivalent. Use the `epochs` GraphQL query instead.
166
+ > `getValidatorsApy` also has no replacement: there is no canonical definition of validator APY, so
167
+ > it must be computed client-side from validator exchange rates (see [Validator APY](#validator-apy)
168
+ > below).
115
169
 
116
170
  ### Example: using gRPC service clients
117
171
 
@@ -167,16 +221,53 @@ const { response: address } = await client.nameService.lookupName({
167
221
  });
168
222
  ```
169
223
 
224
+ ## Methods replaced by gRPC subscriptions
225
+
226
+ Replace the deprecated JSON-RPC websocket subscriptions with the gRPC `subscriptionService`, which
227
+ provides filtered, real-time streams:
228
+
229
+ | JSON-RPC Method | gRPC Service Replacement |
230
+ | ---------------------- | ------------------------------------------- |
231
+ | `subscribeTransaction` | `subscriptionService.subscribeTransactions` |
232
+ | `subscribeEvent` | `subscriptionService.subscribeEvents` |
233
+
234
+ ```typescript
235
+ const stream = client.subscriptionService.subscribeEvents({
236
+ filter: {
237
+ terms: [
238
+ {
239
+ literals: [
240
+ {
241
+ negated: false,
242
+ predicate: {
243
+ oneofKind: 'eventType',
244
+ eventType: { eventType: '0x2::coin::CoinCreated' },
245
+ },
246
+ },
247
+ ],
248
+ },
249
+ ],
250
+ },
251
+ readMask: { paths: ['event_type', 'contents', 'json', 'checkpoint', 'transaction_digest'] },
252
+ });
253
+
254
+ for await (const frame of stream.responses) {
255
+ if (frame.event) {
256
+ console.log(frame.event.eventType, frame.event.json);
257
+ }
258
+ }
259
+ ```
260
+
261
+ Subscriptions always begin at the current tip of the chain. To recover events you missed between
262
+ subscriptions, replay the gap with `client.core.listEvents` using the same filter.
263
+
170
264
  ## Methods requiring GraphQL
171
265
 
172
266
  Some JSON-RPC methods don't have gRPC equivalents and require using `SuiGraphQLClient` instead:
173
267
 
174
268
  | JSON-RPC Method | GraphQL Alternative |
175
269
  | --------------------------- | --------------------------------------------------------- |
176
- | `queryTransactionBlocks` | `transactions` query |
177
270
  | `multiGetTransactionBlocks` | `multiGetTransactionEffects` query |
178
- | `queryEvents` | `events` query |
179
- | `getCheckpoints` | `checkpoints` query |
180
271
  | `getEpochs` | `epochs` query |
181
272
  | `getCoinMetadata` | `coinMetadata` query (or gRPC `stateService.getCoinInfo`) |
182
273
  | `getTotalSupply` | `coinMetadata` query (or gRPC `stateService.getCoinInfo`) |
@@ -197,94 +288,6 @@ const graphqlClient = new SuiGraphQLClient({
197
288
  });
198
289
  ```
199
290
 
200
- ### Querying transactions
201
-
202
- Replace `queryTransactionBlocks` with a GraphQL query:
203
-
204
- ```typescript
205
- const result = await graphqlClient.query({
206
- query: `
207
- query QueryTransactions($sender: SuiAddress, $first: Int, $after: String) {
208
- transactions(
209
- first: $first
210
- after: $after
211
- filter: { sentAddress: $sender }
212
- ) {
213
- pageInfo {
214
- hasNextPage
215
- endCursor
216
- }
217
- nodes {
218
- digest
219
- effects {
220
- status
221
- epoch { epochId }
222
- }
223
- }
224
- }
225
- }
226
- `,
227
- variables: {
228
- sender: '0xabc...',
229
- first: 10,
230
- },
231
- });
232
- ```
233
-
234
- **Available transaction filters:**
235
-
236
- - `sentAddress`: Filter by sender address
237
- - `affectedAddress`: Filter by any address involved in the transaction
238
- - `affectedObject`: Filter by object ID that was affected
239
- - `function`: Filter by Move function called (for example, `0x2::coin::transfer`)
240
- - `kind`: Filter by transaction kind (`SYSTEM` or `PROGRAMMABLE`)
241
- - `atCheckpoint` / `beforeCheckpoint` / `afterCheckpoint` - Filter by checkpoint
242
-
243
- ### Querying events
244
-
245
- Replace `queryEvents` with a GraphQL query:
246
-
247
- ```typescript
248
- const result = await graphqlClient.query({
249
- query: `
250
- query QueryEvents($type: String, $first: Int, $after: String) {
251
- events(
252
- first: $first
253
- after: $after
254
- filter: { type: $type }
255
- ) {
256
- pageInfo {
257
- hasNextPage
258
- endCursor
259
- }
260
- nodes {
261
- transactionModule {
262
- package { address }
263
- name
264
- }
265
- sender { address }
266
- contents {
267
- type { repr }
268
- bcs
269
- }
270
- }
271
- }
272
- }
273
- `,
274
- variables: {
275
- type: '0x2::coin::CoinCreated',
276
- first: 10,
277
- },
278
- });
279
- ```
280
-
281
- **Available event filters:**
282
-
283
- - `type`: Filter by event type (package, package::module, or full type)
284
- - `module`: Filter by emitting module
285
- - `sender`: Filter by transaction sender
286
- - `atCheckpoint` / `beforeCheckpoint` / `afterCheckpoint`: Filter by checkpoint
287
-
288
291
  ### Fetching multiple transactions
289
292
 
290
293
  Replace `multiGetTransactionBlocks` with a GraphQL query: