@layerzerolabs/protocol-stellar-v2 0.2.15 → 0.2.18

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 (257) hide show
  1. package/.turbo/turbo-build.log +350 -309
  2. package/.turbo/turbo-lint.log +146 -108
  3. package/.turbo/turbo-test.log +1423 -1238
  4. package/Cargo.lock +12 -0
  5. package/Cargo.toml +3 -0
  6. package/contracts/ERROR_SPEC.md +44 -0
  7. package/contracts/common-macros/src/auth.rs +113 -0
  8. package/contracts/common-macros/src/contract_ttl.rs +84 -0
  9. package/contracts/common-macros/src/lib.rs +181 -30
  10. package/contracts/common-macros/src/lz_contract.rs +83 -0
  11. package/contracts/common-macros/src/tests/{ownable.rs → auth.rs} +48 -15
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +662 -0
  13. package/contracts/common-macros/src/tests/mod.rs +2 -2
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +20 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +24 -0
  16. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap → common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap} +4 -4
  17. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap → common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap} +3 -3
  18. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +69 -0
  19. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +7 -21
  20. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +2 -2
  21. package/contracts/common-macros/src/ttl_configurable.rs +19 -34
  22. package/contracts/common-macros/src/ttl_extendable.rs +36 -0
  23. package/contracts/common-macros/src/upgradeable.rs +5 -5
  24. package/contracts/common-macros/src/utils.rs +9 -0
  25. package/contracts/endpoint-v2/src/constants.rs +4 -4
  26. package/contracts/endpoint-v2/src/endpoint_v2.rs +38 -40
  27. package/contracts/endpoint-v2/src/errors.rs +4 -3
  28. package/contracts/endpoint-v2/src/events.rs +1 -1
  29. package/contracts/endpoint-v2/src/message_lib_manager.rs +18 -5
  30. package/contracts/endpoint-v2/src/messaging_channel.rs +11 -1
  31. package/contracts/endpoint-v2/src/messaging_composer.rs +11 -1
  32. package/contracts/endpoint-v2/src/storage.rs +1 -1
  33. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +3 -3
  34. package/contracts/endpoint-v2/src/tests/endpoint_v2/quote.rs +1 -1
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/require_oapp_auth.rs +2 -2
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +3 -3
  37. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  38. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_receive_lib_for_eid.rs +3 -3
  39. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_registered.rs +1 -1
  40. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_send_lib_for_eid.rs +3 -3
  41. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_supported_eid.rs +1 -1
  42. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +4 -4
  43. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +1 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +3 -6
  45. package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +2 -2
  46. package/contracts/macro-integration-tests/tests/runtime/ownable/{only_owner_guard.rs → only_auth_guard.rs} +1 -1
  47. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +1 -1
  48. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/freeze.rs +1 -1
  49. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/mod.rs +0 -1
  50. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.rs → only_auth_missing_env.rs} +3 -3
  51. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.stderr → only_auth_missing_env.stderr} +4 -4
  52. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +2 -3
  53. package/contracts/macro-integration-tests/tests/ui/ownable/pass/{only_owner_env_param_variants.rs → only_auth_env_param_variants.rs} +9 -9
  54. package/contracts/macro-integration-tests/tests/ui/ttl_configurable/pass/minimal_contract.rs +6 -6
  55. package/contracts/message-libs/message-lib-common/src/errors.rs +7 -2
  56. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1/decode_packet_header.rs +3 -3
  57. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_lz_receive_option.rs +1 -2
  58. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_native_drop_option.rs +1 -2
  59. package/contracts/message-libs/message-lib-common/src/tests/worker_options/convert_legacy_options.rs +9 -9
  60. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +1 -1
  61. package/contracts/message-libs/message-lib-common/src/tests/worker_options/left_pad_to_bytes32.rs +1 -1
  62. package/contracts/message-libs/message-lib-common/src/tests/worker_options/split_worker_options.rs +2 -2
  63. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +7 -9
  64. package/contracts/message-libs/treasury/src/errors.rs +2 -2
  65. package/contracts/message-libs/treasury/src/events.rs +1 -1
  66. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +2 -2
  67. package/contracts/message-libs/treasury/src/storage.rs +1 -1
  68. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +1 -1
  69. package/contracts/message-libs/treasury/src/treasury.rs +14 -16
  70. package/contracts/message-libs/uln-302/src/receive_uln.rs +13 -2
  71. package/contracts/message-libs/uln-302/src/send_uln.rs +23 -3
  72. package/contracts/message-libs/uln-302/src/uln302.rs +6 -24
  73. package/contracts/oapps/counter/Cargo.toml +14 -1
  74. package/contracts/oapps/counter/integration_tests/mod.rs +4 -1
  75. package/contracts/oapps/counter/integration_tests/{setup.rs → setup_sml.rs} +48 -80
  76. package/contracts/oapps/counter/integration_tests/setup_uln.rs +997 -0
  77. package/contracts/oapps/counter/integration_tests/signing.rs +62 -0
  78. package/contracts/oapps/counter/integration_tests/test_with_sml.rs +24 -55
  79. package/contracts/oapps/counter/integration_tests/test_with_uln.rs +314 -0
  80. package/contracts/oapps/counter/integration_tests/utils.rs +196 -53
  81. package/contracts/oapps/counter/src/counter.rs +67 -43
  82. package/contracts/oapps/counter/src/tests/mod.rs +0 -13
  83. package/contracts/oapps/counter/src/tests/test_counter.rs +5 -7
  84. package/contracts/oapps/oapp/src/errors.rs +5 -1
  85. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +93 -78
  86. package/contracts/oapps/oapp/src/oapp_core.rs +36 -21
  87. package/contracts/oapps/oapp/src/oapp_options_type3.rs +48 -12
  88. package/contracts/oapps/oapp/src/oapp_receiver.rs +106 -41
  89. package/contracts/oapps/oapp/src/oapp_sender.rs +26 -34
  90. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +9 -8
  91. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +25 -17
  92. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +7 -7
  93. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +14 -15
  94. package/contracts/oapps/oapp-macros/src/generators.rs +128 -0
  95. package/contracts/oapps/oapp-macros/src/lib.rs +113 -56
  96. package/contracts/oapps/oft/integration-tests/setup.rs +25 -7
  97. package/contracts/oapps/oft/src/errors.rs +6 -1
  98. package/contracts/oapps/oft/src/extensions/oft_fee.rs +8 -8
  99. package/contracts/oapps/oft/src/extensions/pausable.rs +4 -4
  100. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +5 -5
  101. package/contracts/oapps/oft/src/lib.rs +4 -2
  102. package/contracts/oapps/oft/src/oft.rs +24 -64
  103. package/contracts/oapps/oft/src/oft_impl.rs +201 -0
  104. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -3
  105. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -4
  106. package/contracts/oapps/oft/src/storage.rs +2 -0
  107. package/contracts/oapps/oft/src/tests/extensions/setup.rs +36 -22
  108. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +5 -3
  109. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +5 -3
  110. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +5 -3
  111. package/contracts/oapps/oft/src/tests/test_decimals.rs +2 -2
  112. package/contracts/oapps/oft/src/tests/test_oft_msg_codec.rs +1 -2
  113. package/contracts/oapps/oft/src/tests/test_utils.rs +45 -23
  114. package/contracts/oapps/oft/src/types.rs +20 -0
  115. package/contracts/oapps/oft-std/integration-tests/setup.rs +4 -2
  116. package/contracts/oapps/oft-std/src/oft.rs +24 -6
  117. package/contracts/upgrader/src/lib.rs +4 -4
  118. package/contracts/utils/src/auth.rs +44 -0
  119. package/contracts/utils/src/errors.rs +27 -5
  120. package/contracts/utils/src/lib.rs +3 -0
  121. package/contracts/utils/src/multisig.rs +211 -0
  122. package/contracts/utils/src/ownable.rs +12 -10
  123. package/contracts/utils/src/tests/buffer_reader.rs +6 -6
  124. package/contracts/utils/src/tests/buffer_writer.rs +6 -6
  125. package/contracts/utils/src/tests/bytes_ext.rs +2 -4
  126. package/contracts/utils/src/tests/mod.rs +1 -0
  127. package/contracts/utils/src/tests/multisig.rs +731 -0
  128. package/contracts/utils/src/tests/option_ext.rs +2 -5
  129. package/contracts/utils/src/tests/ownable.rs +16 -5
  130. package/contracts/utils/src/tests/ttl_configurable.rs +27 -16
  131. package/contracts/utils/src/tests/upgradeable.rs +4 -2
  132. package/contracts/utils/src/ttl_configurable.rs +23 -8
  133. package/contracts/utils/src/ttl_extendable.rs +27 -0
  134. package/contracts/utils/src/upgradeable.rs +2 -0
  135. package/contracts/workers/dvn/Cargo.toml +1 -1
  136. package/contracts/workers/dvn/src/auth.rs +7 -7
  137. package/contracts/workers/dvn/src/dvn.rs +10 -38
  138. package/contracts/workers/dvn/src/errors.rs +0 -7
  139. package/contracts/workers/dvn/src/events.rs +1 -14
  140. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  141. package/contracts/workers/dvn/src/interfaces/mod.rs +0 -2
  142. package/contracts/workers/dvn/src/storage.rs +3 -13
  143. package/contracts/workers/dvn/src/tests/auth.rs +4 -4
  144. package/contracts/workers/dvn/src/tests/dvn.rs +1 -2
  145. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +7 -8
  146. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +11 -8
  147. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +11 -12
  148. package/contracts/workers/dvn/src/tests/setup.rs +5 -5
  149. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  150. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -6
  151. package/contracts/workers/executor/src/auth.rs +80 -16
  152. package/contracts/workers/executor/src/executor.rs +5 -31
  153. package/contracts/workers/executor/src/storage.rs +2 -9
  154. package/contracts/workers/executor-fee-lib/Cargo.toml +1 -1
  155. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +3 -6
  156. package/contracts/workers/executor-helper/Cargo.toml +1 -1
  157. package/contracts/workers/executor-helper/src/executor_helper.rs +53 -73
  158. package/contracts/workers/price-feed/Cargo.toml +1 -1
  159. package/contracts/workers/price-feed/src/price_feed.rs +7 -10
  160. package/contracts/workers/worker/src/errors.rs +4 -0
  161. package/contracts/workers/worker/src/tests/worker.rs +7 -6
  162. package/contracts/workers/worker/src/worker.rs +20 -16
  163. package/package.json +8 -5
  164. package/sdk/.turbo/turbo-build.log +1 -0
  165. package/sdk/.turbo/turbo-test.log +1009 -0
  166. package/sdk/dist/generated/bml.d.ts +65 -8
  167. package/sdk/dist/generated/bml.js +70 -34
  168. package/sdk/dist/generated/counter.d.ts +167 -42
  169. package/sdk/dist/generated/counter.js +86 -45
  170. package/sdk/dist/generated/dvn.d.ts +282 -229
  171. package/sdk/dist/generated/dvn.js +119 -81
  172. package/sdk/dist/generated/dvn_fee_lib.d.ts +142 -67
  173. package/sdk/dist/generated/dvn_fee_lib.js +64 -24
  174. package/sdk/dist/generated/endpoint.d.ts +97 -22
  175. package/sdk/dist/generated/endpoint.js +75 -37
  176. package/sdk/dist/generated/executor.d.ts +117 -85
  177. package/sdk/dist/generated/executor.js +102 -59
  178. package/sdk/dist/generated/executor_fee_lib.d.ts +162 -78
  179. package/sdk/dist/generated/executor_fee_lib.js +104 -57
  180. package/sdk/dist/generated/executor_helper.d.ts +133 -21
  181. package/sdk/dist/generated/executor_helper.js +99 -50
  182. package/sdk/dist/generated/oft_std.d.ts +233 -55
  183. package/sdk/dist/generated/oft_std.js +99 -54
  184. package/sdk/dist/generated/price_feed.d.ts +142 -67
  185. package/sdk/dist/generated/price_feed.js +64 -24
  186. package/sdk/dist/generated/sml.d.ts +113 -32
  187. package/sdk/dist/generated/sml.js +93 -49
  188. package/sdk/dist/generated/treasury.d.ts +896 -0
  189. package/sdk/dist/generated/treasury.js +219 -0
  190. package/sdk/dist/generated/uln302.d.ts +113 -32
  191. package/sdk/dist/generated/uln302.js +93 -49
  192. package/sdk/dist/generated/upgrader.d.ts +2 -2
  193. package/sdk/dist/generated/upgrader.js +1 -1
  194. package/sdk/dist/index.d.ts +2 -0
  195. package/sdk/dist/index.js +3 -0
  196. package/sdk/dist/wasm/blocked-message-lib.d.ts +1 -0
  197. package/sdk/dist/wasm/blocked-message-lib.js +2 -0
  198. package/sdk/dist/wasm/counter.d.ts +1 -0
  199. package/sdk/dist/wasm/counter.js +2 -0
  200. package/sdk/dist/wasm/dvn-fee-lib.d.ts +1 -0
  201. package/sdk/dist/wasm/dvn-fee-lib.js +2 -0
  202. package/sdk/dist/wasm/dvn.d.ts +1 -0
  203. package/sdk/dist/wasm/dvn.js +2 -0
  204. package/sdk/dist/wasm/endpoint-v2.d.ts +1 -0
  205. package/sdk/dist/wasm/endpoint-v2.js +2 -0
  206. package/sdk/dist/wasm/executor-fee-lib.d.ts +1 -0
  207. package/sdk/dist/wasm/executor-fee-lib.js +2 -0
  208. package/sdk/dist/wasm/executor-helper.d.ts +1 -0
  209. package/sdk/dist/wasm/executor-helper.js +2 -0
  210. package/sdk/dist/wasm/executor.d.ts +1 -0
  211. package/sdk/dist/wasm/executor.js +2 -0
  212. package/sdk/dist/wasm/layerzero-views.d.ts +1 -0
  213. package/sdk/dist/wasm/layerzero-views.js +2 -0
  214. package/sdk/dist/wasm/oft-std.d.ts +1 -0
  215. package/sdk/dist/wasm/oft-std.js +2 -0
  216. package/sdk/dist/wasm/price-feed.d.ts +1 -0
  217. package/sdk/dist/wasm/price-feed.js +2 -0
  218. package/sdk/dist/wasm/simple-message-lib.d.ts +1 -0
  219. package/sdk/dist/wasm/simple-message-lib.js +2 -0
  220. package/sdk/dist/wasm/treasury.d.ts +1 -0
  221. package/sdk/dist/wasm/treasury.js +2 -0
  222. package/sdk/dist/wasm/uln302.d.ts +1 -0
  223. package/sdk/dist/wasm/uln302.js +2 -0
  224. package/sdk/dist/wasm/upgrader.d.ts +1 -0
  225. package/sdk/dist/wasm/upgrader.js +2 -0
  226. package/sdk/dist/wasm.d.ts +15 -0
  227. package/sdk/dist/wasm.js +15 -0
  228. package/sdk/package.json +4 -2
  229. package/sdk/src/index.ts +4 -0
  230. package/sdk/test/counter-sml.test.ts +376 -0
  231. package/sdk/test/counter-uln.test.ts +493 -0
  232. package/sdk/test/{oft.test.ts → oft-sml.test.ts} +185 -310
  233. package/sdk/test/suites/constants.ts +22 -2
  234. package/sdk/test/suites/globalSetup.ts +450 -0
  235. package/sdk/test/suites/localnet.ts +23 -6
  236. package/sdk/test/upgrader.test.ts +7 -16
  237. package/sdk/test/utils.ts +558 -85
  238. package/sdk/vitest.config.ts +21 -0
  239. package/tools/ts-bindings-gen/src/main.rs +1 -0
  240. package/turbo.json +2 -0
  241. package/contracts/common-macros/src/contract_impl.rs +0 -52
  242. package/contracts/common-macros/src/ownable.rs +0 -41
  243. package/contracts/common-macros/src/tests/contract_impl.rs +0 -386
  244. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +0 -12
  245. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/extend_instance_ttl.rs +0 -50
  246. package/contracts/oapps/oapp-macros/src/oapp_core.rs +0 -41
  247. package/contracts/oapps/oapp-macros/src/oapp_full.rs +0 -21
  248. package/contracts/oapps/oapp-macros/src/oapp_options_type3.rs +0 -31
  249. package/contracts/oapps/oapp-macros/src/oapp_receiver.rs +0 -48
  250. package/contracts/oapps/oapp-macros/src/oapp_sender.rs +0 -21
  251. package/contracts/oapps/oapp-macros/src/util.rs +0 -107
  252. package/contracts/oapps/oft/src/constants.rs +0 -5
  253. package/contracts/oapps/oft/src/default_oft_impl.rs +0 -152
  254. package/contracts/workers/dvn/src/interfaces/multisig.rs +0 -56
  255. package/contracts/workers/dvn/src/multisig.rs +0 -157
  256. package/sdk/test/index.test.ts +0 -375
  257. /package/sdk/test/suites/{testUpgradeable.ts → dummyContractClient.ts} +0 -0
package/sdk/test/utils.ts CHANGED
@@ -1,26 +1,204 @@
1
- import { Address, contract, hash, Keypair, rpc, xdr } from '@stellar/stellar-sdk';
2
- import { basicNodeSigner } from '@stellar/stellar-sdk/contract';
1
+ import { keccak_256 } from '@noble/hashes/sha3';
2
+ import * as secp from '@noble/secp256k1';
3
+ import {
4
+ Account,
5
+ Address,
6
+ authorizeEntry,
7
+ BASE_FEE,
8
+ Contract,
9
+ contract,
10
+ hash,
11
+ Keypair,
12
+ nativeToScVal,
13
+ scValToNative,
14
+ TransactionBuilder,
15
+ xdr,
16
+ } from '@stellar/stellar-sdk';
17
+ import * as rpc from '@stellar/stellar-sdk/rpc';
18
+
19
+ import {
20
+ DEFAULT_DEPLOYER,
21
+ NATIVE_TOKEN_ADDRESS,
22
+ NETWORK_PASSPHRASE,
23
+ RPC_URL,
24
+ } from './suites/constants';
25
+
26
+ // ============================================================================
27
+ // Client Factory Helper
28
+ // ============================================================================
29
+
30
+ /**
31
+ * Helper to create a Soroban contract client with DEFAULT_DEPLOYER as signer.
32
+ * This is used to create clients for protocol contracts from their addresses.
33
+ */
34
+ export function createClient<T>(
35
+ ClientClass: new (options: {
36
+ contractId: string;
37
+ publicKey: string;
38
+ signTransaction: (tx: string) => Promise<{ signedTxXdr: string; signerAddress: string }>;
39
+ rpcUrl: string;
40
+ networkPassphrase: string;
41
+ allowHttp: boolean;
42
+ }) => T,
43
+ contractId: string,
44
+ ): T {
45
+ return new ClientClass({
46
+ contractId,
47
+ publicKey: DEFAULT_DEPLOYER.publicKey(),
48
+ signTransaction: async (tx: string) => {
49
+ const transaction = TransactionBuilder.fromXDR(tx, NETWORK_PASSPHRASE);
50
+ transaction.sign(DEFAULT_DEPLOYER);
51
+ return {
52
+ signedTxXdr: transaction.toXDR(),
53
+ signerAddress: DEFAULT_DEPLOYER.publicKey(),
54
+ };
55
+ },
56
+ rpcUrl: RPC_URL,
57
+ networkPassphrase: NETWORK_PASSPHRASE,
58
+ allowHttp: true,
59
+ });
60
+ }
61
+
62
+ // ============================================================================
63
+ // Token Balance Helpers
64
+ // ============================================================================
3
65
 
4
66
  /**
5
- * Signs the Executor abstract account's auth entries.
67
+ * Helper to get token balance for an address using SAC (Stellar Asset Contract).
68
+ * Works for any token including native XLM.
6
69
  *
7
- * The Executor contract implements CustomAccountInterface with Signature = ExecutorSignature.
8
- * ExecutorSignature contains:
9
- * - admin: Address of the admin
10
- * - signature: Ed25519 signature (64 bytes) over the signature_payload
70
+ * @param tokenAddress - The SAC contract address (defaults to native XLM)
71
+ * @param accountAddress - The account to check balance for
72
+ */
73
+ export async function getTokenBalance(
74
+ accountAddress: string,
75
+ tokenAddress: string = NATIVE_TOKEN_ADDRESS,
76
+ ): Promise<bigint> {
77
+ const server = new rpc.Server(RPC_URL, { allowHttp: true });
78
+ const tokenContract = new Contract(tokenAddress);
79
+
80
+ // Build the balance call
81
+ const balanceOp = tokenContract.call(
82
+ 'balance',
83
+ nativeToScVal(Address.fromString(accountAddress), { type: 'address' }),
84
+ );
85
+
86
+ const account = await server.getAccount(DEFAULT_DEPLOYER.publicKey());
87
+ const tx = new TransactionBuilder(account, {
88
+ fee: BASE_FEE,
89
+ networkPassphrase: NETWORK_PASSPHRASE,
90
+ })
91
+ .addOperation(balanceOp)
92
+ .setTimeout(30)
93
+ .build();
94
+
95
+ const simulated = await server.simulateTransaction(tx);
96
+ if (rpc.Api.isSimulationError(simulated)) {
97
+ throw new Error(`Balance query failed: ${JSON.stringify(simulated)}`);
98
+ }
99
+
100
+ // Extract result from simulation
101
+ const result = (simulated as rpc.Api.SimulateTransactionSuccessResponse).result;
102
+ if (result?.retval) {
103
+ return scValToNative(result.retval) as bigint;
104
+ }
105
+ return 0n;
106
+ }
107
+
108
+ /**
109
+ * Helper to get native token (XLM) balance for an address.
110
+ * Convenience wrapper around getTokenBalance.
111
+ */
112
+ export async function getNativeBalance(accountAddress: string): Promise<bigint> {
113
+ return getTokenBalance(accountAddress, NATIVE_TOKEN_ADDRESS);
114
+ }
115
+
116
+ // ============================================================================
117
+ // Secp256k1 Key Pair for DVN Multisig
118
+ // ============================================================================
119
+
120
+ /**
121
+ * A secp256k1 key pair with private key and derived Ethereum-style address.
122
+ * Used for DVN multisig signing.
123
+ */
124
+ export class Secp256k1KeyPair {
125
+ private privateKey: Uint8Array;
126
+ public readonly ethAddress: Buffer;
127
+
128
+ constructor(privateKey: Uint8Array | Buffer | string) {
129
+ if (typeof privateKey === 'string') {
130
+ // Remove 0x prefix if present
131
+ const hex = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
132
+ this.privateKey = Buffer.from(hex, 'hex');
133
+ } else {
134
+ this.privateKey = new Uint8Array(privateKey);
135
+ }
136
+ this.ethAddress = this.deriveEthAddress();
137
+ }
138
+
139
+ /**
140
+ * Generate a random key pair.
141
+ */
142
+ static generate(): Secp256k1KeyPair {
143
+ const privateKey = secp.utils.randomPrivateKey();
144
+ return new Secp256k1KeyPair(privateKey);
145
+ }
146
+
147
+ /**
148
+ * Derive Ethereum-style address from the public key.
149
+ * Address = last 20 bytes of keccak256(uncompressed_pubkey[1:65])
150
+ */
151
+ private deriveEthAddress(): Buffer {
152
+ const publicKey = secp.getPublicKey(this.privateKey, false); // uncompressed
153
+ const pubkeyWithoutPrefix = publicKey.slice(1); // remove 0x04 prefix
154
+ const hash = keccak_256(pubkeyWithoutPrefix);
155
+ return Buffer.from(hash.slice(12)); // last 20 bytes
156
+ }
157
+
158
+ /**
159
+ * Sign a 32-byte digest and return a 65-byte signature (r || s || v).
160
+ */
161
+ async sign(digest: Uint8Array): Promise<Buffer> {
162
+ const [signature, recoveryId] = await secp.sign(digest, this.privateKey, {
163
+ canonical: true,
164
+ recovered: true,
165
+ der: false,
166
+ });
167
+
168
+ const v = 27 + recoveryId;
169
+ const result = Buffer.alloc(65);
170
+ result.set(signature, 0); // r (32 bytes) + s (32 bytes)
171
+ result[64] = v;
172
+
173
+ return result;
174
+ }
175
+ }
176
+
177
+ // ============================================================================
178
+ // DVN Abstract Account Auth Signing
179
+ // ============================================================================
180
+
181
+ /**
182
+ * Signs the DVN abstract account's auth entries.
11
183
  *
12
- * This follows the OneSig pattern - verification happens inside __check_auth using ed25519_verify.
184
+ * The DVN contract implements CustomAccountInterface with Signature = TransactionAuthData.
185
+ * TransactionAuthData contains:
186
+ * - vid: u32 - Verifier ID
187
+ * - expiration: u64 - Ledger timestamp for when auth expires
188
+ * - signatures: Vec<BytesN<65>> - Secp256k1 signatures from multisig signers
189
+ * - sender: Sender - Either None or Admin(public_key, ed25519_signature)
13
190
  */
14
- export async function signExecutorAuthEntries<T>(
15
- executorAddress: string,
191
+ export async function signDvnAuthEntries<T>(
192
+ dvnAddress: string,
193
+ vid: number,
16
194
  adminKeypair: Keypair,
195
+ multisigSigners: Secp256k1KeyPair[],
17
196
  assembledTx: contract.AssembledTransaction<T>,
18
197
  networkPassphrase: string,
19
198
  ): Promise<void> {
20
- const executorAddr = Address.fromString(executorAddress);
21
- const adminAddr = Address.fromString(adminKeypair.publicKey());
199
+ const dvnAddr = Address.fromString(dvnAddress);
22
200
 
23
- console.log('\n🔄 Simulating transaction to get the auth entries');
201
+ console.log('\n🔄 Simulating DVN transaction to get the auth entries');
24
202
  await assembledTx.simulate();
25
203
 
26
204
  // Print debug info
@@ -29,7 +207,7 @@ export async function signExecutorAuthEntries<T>(
29
207
 
30
208
  const networkId = hash(Buffer.from(networkPassphrase));
31
209
 
32
- // Sign the Executor's auth entry with ExecutorSignature (admin + signature)
210
+ // Custom authorizer for DVN abstract account
33
211
  const customAuthorizeEntry = async (
34
212
  entry: xdr.SorobanAuthorizationEntry,
35
213
  _signer: Keypair | ((preimage: xdr.HashIdPreimage) => Promise<unknown>),
@@ -45,15 +223,15 @@ export async function signExecutorAuthEntries<T>(
45
223
  const credentialAddress = Address.fromScAddress(addressCred.address());
46
224
  const rootInvocation = entry.rootInvocation();
47
225
 
48
- if (credentialAddress.toString() !== executorAddr.toString()) {
226
+ if (credentialAddress.toString() !== dvnAddr.toString()) {
49
227
  throw new Error('Credential address mismatch');
50
228
  }
51
229
 
52
- // Log the Executor's auth entry tree
53
- console.log('\n🌳 Executor Auth Entry Tree:');
230
+ // Log the DVN's auth entry tree
231
+ console.log('\n🌳 DVN Auth Entry Tree:');
54
232
  logInvocationTree(rootInvocation, 0);
55
233
 
56
- // Compute the signature_payload hash
234
+ // 1. Compute the signature_payload (soroban authorization hash)
57
235
  const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
58
236
  new xdr.HashIdPreimageSorobanAuthorization({
59
237
  networkId,
@@ -63,34 +241,79 @@ export async function signExecutorAuthEntries<T>(
63
241
  }),
64
242
  );
65
243
  const signaturePayload = hash(preimage.toXDR());
66
- console.log('\n📝 Signature payload (hash):', signaturePayload.toString('hex'));
244
+ console.log(
245
+ '\n📝 Signature payload (soroban auth hash):',
246
+ signaturePayload.toString('hex'),
247
+ );
67
248
 
68
- // Sign the signature_payload with admin's Ed25519 key
249
+ // 2. Sign the signature_payload with admin's Ed25519 key
69
250
  const adminSignature = adminKeypair.sign(signaturePayload);
70
251
  console.log(
71
- '✍️ Admin signature created:',
252
+ '✍️ Admin Ed25519 signature created:',
72
253
  adminSignature.toString('hex').slice(0, 32) + '...',
73
254
  );
74
255
 
75
- // Build ExecutorSignature struct as ScVal
76
- // struct ExecutorSignature { public_key: BytesN<32>, signature: BytesN<64> }
77
- const executorSignatureScVal = xdr.ScVal.scvMap([
256
+ // 3. Extract calls from the auth entry and compute the multisig hash
257
+ // The expiration is the ledger timestamp when the auth expires
258
+ // We use validUntilLedgerSeq * 5 as a rough approximation (5 seconds per ledger)
259
+ const expiration = BigInt(validUntilLedgerSeq) * 5n + BigInt(Math.floor(Date.now() / 1000));
260
+ const calls = collectCallsFromInvocation(rootInvocation);
261
+ const callsXdr = serializeCallsToXdr(calls);
262
+ const callHash = computeCallHash(vid, expiration, callsXdr);
263
+ console.log('📝 Call hash for multisig:', Buffer.from(callHash).toString('hex'));
264
+
265
+ // 4. Sign the call hash with multisig signers
266
+ const signatures: Buffer[] = [];
267
+ for (const signer of multisigSigners) {
268
+ const sig = await signer.sign(callHash);
269
+ signatures.push(sig);
270
+ console.log('✍️ Multisig signature created:', sig.toString('hex').slice(0, 32) + '...');
271
+ }
272
+
273
+ // 5. Sort signatures by recovered signer address (ascending)
274
+ // This is required by the DVN contract's verify_signatures function
275
+ const signaturesWithAddresses = await Promise.all(
276
+ signatures.map(async (sig, i) => ({
277
+ sig,
278
+ address: multisigSigners[i].ethAddress,
279
+ })),
280
+ );
281
+ signaturesWithAddresses.sort((a, b) => a.address.compare(b.address));
282
+ const sortedSignatures = signaturesWithAddresses.map((s) => s.sig);
283
+
284
+ // 6. Build TransactionAuthData as ScVal
285
+ // struct TransactionAuthData { vid: u32, expiration: u64, signatures: Vec<BytesN<65>>, sender: Sender }
286
+ // enum Sender { None, Admin(BytesN<32>, BytesN<64>) }
287
+ const transactionAuthDataScVal = xdr.ScVal.scvMap([
78
288
  new xdr.ScMapEntry({
79
- key: xdr.ScVal.scvSymbol('public_key'),
80
- val: xdr.ScVal.scvBytes(adminKeypair.rawPublicKey()),
289
+ key: xdr.ScVal.scvSymbol('expiration'),
290
+ val: xdr.ScVal.scvU64(new xdr.Uint64(expiration)),
81
291
  }),
82
292
  new xdr.ScMapEntry({
83
- key: xdr.ScVal.scvSymbol('signature'),
84
- val: xdr.ScVal.scvBytes(adminSignature),
293
+ key: xdr.ScVal.scvSymbol('sender'),
294
+ // Sender::Admin(public_key, signature)
295
+ val: xdr.ScVal.scvVec([
296
+ xdr.ScVal.scvSymbol('Admin'),
297
+ xdr.ScVal.scvBytes(adminKeypair.rawPublicKey()),
298
+ xdr.ScVal.scvBytes(adminSignature),
299
+ ]),
300
+ }),
301
+ new xdr.ScMapEntry({
302
+ key: xdr.ScVal.scvSymbol('signatures'),
303
+ val: xdr.ScVal.scvVec(sortedSignatures.map((sig) => xdr.ScVal.scvBytes(sig))),
304
+ }),
305
+ new xdr.ScMapEntry({
306
+ key: xdr.ScVal.scvSymbol('vid'),
307
+ val: xdr.ScVal.scvU32(vid),
85
308
  }),
86
309
  ]);
87
310
 
88
- // Return executor's auth entry with ExecutorSignature
311
+ // Return DVN's auth entry with TransactionAuthData
89
312
  const newCred = new xdr.SorobanAddressCredentials({
90
313
  address: addressCred.address(),
91
314
  nonce: addressCred.nonce(),
92
315
  signatureExpirationLedger: validUntilLedgerSeq,
93
- signature: executorSignatureScVal,
316
+ signature: transactionAuthDataScVal,
94
317
  });
95
318
 
96
319
  return new xdr.SorobanAuthorizationEntry({
@@ -99,61 +322,316 @@ export async function signExecutorAuthEntries<T>(
99
322
  });
100
323
  };
101
324
 
102
- // Check if the executor needs to sign
103
- if (remaining.includes(executorAddr.toString())) {
104
- // Sign the executor's auth entry
105
- // Note: signAuthEntries internally calls simulate() after signing
325
+ // Check if the DVN needs to sign
326
+ if (remaining.includes(dvnAddr.toString())) {
106
327
  await assembledTx.signAuthEntries({
107
- address: executorAddr.toString(),
328
+ address: dvnAddr.toString(),
108
329
  authorizeEntry: customAuthorizeEntry,
109
330
  });
110
331
 
111
- // Don't manually re-simulate - signAuthEntries already does this
112
- console.log('\n🔄 Executor auth signed');
332
+ console.log('\n🔄 DVN auth signed');
113
333
 
114
- // Check remaining signers after executor auth
115
334
  remaining = assembledTx.needsNonInvokerSigningBy({
116
335
  includeAlreadySigned: false,
117
336
  });
118
- console.log('📋 Remaining signers after executor:', remaining);
119
- }
120
-
121
- if (remaining.includes(adminAddr.toString())) {
122
- console.log('\n✍️ Admin signing auth entries...');
123
-
124
- // Log the admin's auth entry tree before signing
125
- assembledTx.built?.operations
126
- .flatMap((op) => ('auth' in op ? (op.auth as xdr.SorobanAuthorizationEntry[]) : []))
127
- .filter((entry) => {
128
- const cred = entry.credentials();
129
- return (
130
- cred.switch() === xdr.SorobanCredentialsType.sorobanCredentialsAddress() &&
131
- Address.fromScAddress(cred.address().address()).toString() ===
132
- adminAddr.toString()
133
- );
134
- })
135
- .forEach((entry) => {
136
- console.log('\n🌳 Admin Auth Entry Tree:');
137
- logInvocationTree(entry.rootInvocation(), 0);
138
- });
139
-
140
- // Use SDK's basicNodeSigner for account signing
141
- const adminSigner = basicNodeSigner(adminKeypair, networkPassphrase);
142
- console.log('✅ Using SDK basicNodeSigner for admin account signing');
143
-
144
- // Sign admin's auth entries using signAuthEntries with SDK's signAuthEntry
145
- await assembledTx.signAuthEntries({
146
- address: adminAddr.toString(),
147
- signAuthEntry: adminSigner.signAuthEntry,
148
- });
337
+ console.log('📋 Remaining signers after DVN:', remaining);
149
338
  }
150
339
 
151
- // Final re-simulation after all auth entries are signed to ensure accurate resource estimation.
152
- // This is important because the Executor's custom account __check_auth (Ed25519 verification)
153
- // adds CPU costs that may not be fully captured in earlier simulations.
154
- console.log('\n🔄 Final re-simulation with all auth entries signed to get the fee');
340
+ // Final re-simulation
341
+ console.log('\n🔄 Final re-simulation with DVN auth entries signed');
155
342
  await assembledTx.simulate();
156
- console.log('✅ Final simulation complete to get the fee');
343
+ console.log('✅ Final simulation complete');
344
+ }
345
+
346
+ /**
347
+ * Represents a contract call for multisig authorization.
348
+ */
349
+ interface Call {
350
+ to: string; // Contract address
351
+ func: string; // Function name
352
+ args: xdr.ScVal[]; // Function arguments
353
+ }
354
+
355
+ /**
356
+ * Collects all contract calls from an invocation tree.
357
+ */
358
+ function collectCallsFromInvocation(invocation: xdr.SorobanAuthorizedInvocation): Call[] {
359
+ const calls: Call[] = [];
360
+ collectCallsRecursive(invocation, calls);
361
+ return calls;
362
+ }
363
+
364
+ function collectCallsRecursive(invocation: xdr.SorobanAuthorizedInvocation, calls: Call[]): void {
365
+ const fn = invocation.function();
366
+
367
+ if (
368
+ fn.switch() === xdr.SorobanAuthorizedFunctionType.sorobanAuthorizedFunctionTypeContractFn()
369
+ ) {
370
+ const contractFn = fn.contractFn();
371
+ const contractAddr = Address.fromScAddress(contractFn.contractAddress());
372
+
373
+ calls.push({
374
+ to: contractAddr.toString(),
375
+ func: contractFn.functionName().toString(),
376
+ args: contractFn.args(),
377
+ });
378
+ }
379
+
380
+ // Process sub-invocations
381
+ for (const sub of invocation.subInvocations()) {
382
+ collectCallsRecursive(sub, calls);
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Serializes calls to XDR format matching Soroban's Vec<Call> serialization.
388
+ *
389
+ * Call struct: { args: Vec<Val>, func: Symbol, to: Address }
390
+ * Serialized as ScMap with keys in alphabetical order.
391
+ */
392
+ function serializeCallsToXdr(calls: Call[]): Buffer {
393
+ const callScVals = calls.map((call) =>
394
+ xdr.ScVal.scvMap([
395
+ new xdr.ScMapEntry({
396
+ key: xdr.ScVal.scvSymbol('args'),
397
+ val: xdr.ScVal.scvVec(call.args),
398
+ }),
399
+ new xdr.ScMapEntry({
400
+ key: xdr.ScVal.scvSymbol('func'),
401
+ val: xdr.ScVal.scvSymbol(call.func),
402
+ }),
403
+ new xdr.ScMapEntry({
404
+ key: xdr.ScVal.scvSymbol('to'),
405
+ val: Address.fromString(call.to).toScVal(),
406
+ }),
407
+ ]),
408
+ );
409
+
410
+ const vecScVal = xdr.ScVal.scvVec(callScVals);
411
+ return Buffer.from(vecScVal.toXDR());
412
+ }
413
+
414
+ /**
415
+ * Computes the call hash for multisig signing.
416
+ * hash = keccak256(vid.to_be_bytes(4) || expiration.to_be_bytes(8) || calls_xdr)
417
+ */
418
+ function computeCallHash(vid: number, expiration: bigint, callsXdr: Buffer): Uint8Array {
419
+ // vid as 4-byte big-endian
420
+ const vidBytes = Buffer.alloc(4);
421
+ vidBytes.writeUInt32BE(vid, 0);
422
+
423
+ // expiration as 8-byte big-endian
424
+ const expirationBytes = Buffer.alloc(8);
425
+ expirationBytes.writeBigUInt64BE(expiration, 0);
426
+
427
+ // Concatenate and hash
428
+ const data = Buffer.concat([vidBytes, expirationBytes, callsXdr]);
429
+ return keccak_256(data);
430
+ }
431
+
432
+ // ============================================================================
433
+ // Executor Abstract Account Auth Signing (with Non-Root Auth Support)
434
+ // ============================================================================
435
+
436
+ /**
437
+ * Signs and sends a transaction with Executor abstract account auth entries.
438
+ *
439
+ * The Executor contract implements CustomAccountInterface with Signature = ExecutorSignature.
440
+ * ExecutorSignature contains:
441
+ * - public_key: BytesN<32> - Admin's Ed25519 public key
442
+ * - signature: BytesN<64> - Ed25519 signature over the signature_payload
443
+ *
444
+ * This function uses `record_allow_nonroot` simulation mode to capture non-root auth entries,
445
+ * which is required because the executor authorizes `lz_receive`/`lz_compose` calls that are
446
+ * invoked by the ExecutorHelper contract (not the root invoker).
447
+ *
448
+ * @returns The transaction result after sending
449
+ */
450
+ export async function signAndSendWithExecutorAuth<T>(
451
+ executorAddress: string,
452
+ adminKeypair: Keypair,
453
+ assembledTx: contract.AssembledTransaction<T>,
454
+ networkPassphrase: string,
455
+ ): Promise<rpc.Api.GetSuccessfulTransactionResponse> {
456
+ const executorAddr = Address.fromString(executorAddress);
457
+ const server = new rpc.Server(RPC_URL, { allowHttp: true });
458
+
459
+ console.log('\n🔄 Building transaction for non-root auth...');
460
+
461
+ // 1. Build the raw transaction (don't simulate yet)
462
+ const rawTx = assembledTx.raw!.build();
463
+
464
+ // 2. Simulate with record_allow_nonroot to capture non-root auth entries
465
+ // This is required because the executor's auth is not the root invocation
466
+ console.log('🔄 Simulating with record_allow_nonroot...');
467
+ const sim = await server.simulateTransaction(
468
+ rawTx,
469
+ undefined, // addlResources
470
+ 'record_allow_nonroot', // ← This enables non-root auth recording!
471
+ );
472
+
473
+ if (rpc.Api.isSimulationError(sim)) {
474
+ throw new Error(`Simulation failed: ${JSON.stringify(sim)}`);
475
+ }
476
+
477
+ console.log('✅ Simulation complete');
478
+ console.log(' Auth entries returned:', sim.result?.auth?.length ?? 0);
479
+
480
+ // 3. Sign auth entries
481
+ const latestLedger = sim.latestLedger;
482
+ const validUntilLedger = latestLedger + 100;
483
+ const networkId = hash(Buffer.from(networkPassphrase));
484
+
485
+ if (sim.result && sim.result.auth) {
486
+ sim.result.auth = await Promise.all(
487
+ sim.result.auth.map(async (entry) => {
488
+ const credentials = entry.credentials();
489
+
490
+ // Source account credentials are already signed by tx envelope
491
+ if (
492
+ credentials.switch() ===
493
+ xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount()
494
+ ) {
495
+ console.log(' Skipping source account auth entry');
496
+ return entry;
497
+ }
498
+
499
+ // Address credentials need explicit signature
500
+ const addressCred = credentials.address();
501
+ const addr = Address.fromScAddress(addressCred.address()).toString();
502
+ const rootInvocation = entry.rootInvocation();
503
+
504
+ console.log(' Processing auth entry for address:', addr);
505
+ console.log(' Auth entry tree:');
506
+ logInvocationTree(rootInvocation, 2);
507
+
508
+ // Check if this is the executor's auth entry (Abstract Account)
509
+ if (addr === executorAddr.toString()) {
510
+ console.log(' ✍️ Signing executor auth entry (Abstract Account)...');
511
+
512
+ // Compute the signature_payload hash
513
+ const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
514
+ new xdr.HashIdPreimageSorobanAuthorization({
515
+ networkId,
516
+ nonce: addressCred.nonce(),
517
+ signatureExpirationLedger: validUntilLedger,
518
+ invocation: rootInvocation,
519
+ }),
520
+ );
521
+ const signaturePayload = hash(preimage.toXDR());
522
+
523
+ // Sign the signature_payload with admin's Ed25519 key
524
+ const adminSignature = adminKeypair.sign(signaturePayload);
525
+
526
+ // Build ExecutorSignature struct as ScVal
527
+ // struct ExecutorSignature { public_key: BytesN<32>, signature: BytesN<64> }
528
+ const executorSignatureScVal = xdr.ScVal.scvMap([
529
+ new xdr.ScMapEntry({
530
+ key: xdr.ScVal.scvSymbol('public_key'),
531
+ val: xdr.ScVal.scvBytes(adminKeypair.rawPublicKey()),
532
+ }),
533
+ new xdr.ScMapEntry({
534
+ key: xdr.ScVal.scvSymbol('signature'),
535
+ val: xdr.ScVal.scvBytes(adminSignature),
536
+ }),
537
+ ]);
538
+
539
+ // Return executor's auth entry with ExecutorSignature
540
+ const newCred = new xdr.SorobanAddressCredentials({
541
+ address: addressCred.address(),
542
+ nonce: addressCred.nonce(),
543
+ signatureExpirationLedger: validUntilLedger,
544
+ signature: executorSignatureScVal,
545
+ });
546
+
547
+ return new xdr.SorobanAuthorizationEntry({
548
+ credentials: xdr.SorobanCredentials.sorobanCredentialsAddress(newCred),
549
+ rootInvocation,
550
+ });
551
+ }
552
+
553
+ // Check if this is the admin's auth entry (regular Stellar account)
554
+ // This happens when admin needs to authorize token transfers (e.g., native_drop, value transfers)
555
+ if (addr === adminKeypair.publicKey()) {
556
+ console.log(' ✍️ Signing admin auth entry (regular account)...');
557
+ return authorizeEntry(entry, adminKeypair, validUntilLedger, networkPassphrase);
558
+ }
559
+
560
+ throw new Error(`Unexpected auth signer needed: ${addr}`);
561
+ }),
562
+ );
563
+ console.log('✅ Auth entries signed');
564
+ }
565
+
566
+ // 4. Assemble transaction with signed auth entries
567
+ const txWithSignedAuth = rpc.assembleTransaction(rawTx, sim).build();
568
+
569
+ console.log('✅ Transaction assembled with signed auth entries');
570
+
571
+ // 5. Re-simulate to get correct footprint (includes __check_auth storage accesses)
572
+ const finalSim = await server.simulateTransaction(txWithSignedAuth);
573
+ if (rpc.Api.isSimulationError(finalSim)) {
574
+ throw new Error(`Final simulation failed: ${JSON.stringify(finalSim)}`);
575
+ }
576
+
577
+ console.log('✅ Final simulation completed');
578
+
579
+ // 6. Assemble final transaction with accurate footprint
580
+ const assembledFinalTx = rpc.assembleTransaction(txWithSignedAuth, finalSim).build();
581
+
582
+ // 7. Rebuild the transaction with adminKeypair as the source account
583
+ // The original transaction was built with DEFAULT_DEPLOYER as source,
584
+ // but we want EXECUTOR_ADMIN to be the source so they can sign the envelope
585
+ // Fetch the current sequence number for the admin account from the network
586
+ const adminAccountInfo = await server.getAccount(adminKeypair.publicKey());
587
+ const adminAccount = new Account(adminKeypair.publicKey(), adminAccountInfo.sequenceNumber());
588
+ const finalTxBuilder = new TransactionBuilder(adminAccount, {
589
+ fee: assembledFinalTx.fee,
590
+ networkPassphrase,
591
+ });
592
+
593
+ // Get the transaction XDR to extract the operation and soroban data
594
+ const txXdr = assembledFinalTx.toEnvelope().v1().tx();
595
+
596
+ // Copy the Soroban invoke operation (there's only one operation in Soroban transactions)
597
+ const operationXdr = txXdr.operations()[0];
598
+ finalTxBuilder.addOperation(xdr.Operation.fromXDR(operationXdr.toXDR()));
599
+
600
+ // Copy the Soroban transaction data (footprint, resources, etc.)
601
+ const extSwitch = txXdr.ext().switch();
602
+ if (extSwitch === 1) {
603
+ // Has SorobanTransactionData
604
+ finalTxBuilder.setSorobanData(txXdr.ext().sorobanData());
605
+ }
606
+
607
+ // Set timeout to match original
608
+ finalTxBuilder.setTimeout(30);
609
+
610
+ const finalTx = finalTxBuilder.build();
611
+
612
+ // 8. Sign the transaction envelope
613
+ finalTx.sign(adminKeypair);
614
+
615
+ console.log('✅ Transaction envelope signed');
616
+
617
+ // 9. Send and poll
618
+ const sentResult = await server.sendTransaction(finalTx);
619
+
620
+ if (sentResult.status !== 'PENDING') {
621
+ throw new Error(`Transaction failed to send: ${JSON.stringify(sentResult)}`);
622
+ }
623
+
624
+ console.log('✅ Transaction sent, hash:', sentResult.hash);
625
+
626
+ const txResult = await server.pollTransaction(sentResult.hash);
627
+
628
+ if (txResult.status !== 'SUCCESS') {
629
+ throw new Error(`Transaction failed: ${JSON.stringify(txResult)}`);
630
+ }
631
+
632
+ console.log('✅ Transaction completed successfully');
633
+
634
+ return txResult as rpc.Api.GetSuccessfulTransactionResponse;
157
635
  }
158
636
 
159
637
  /**
@@ -182,18 +660,13 @@ function logInvocationTree(invocation: xdr.SorobanAuthorizedInvocation, depth: n
182
660
  }
183
661
  }
184
662
 
185
- export function assertTransactionsSucceeded(
186
- sentTransactions: contract.SentTransaction<unknown>[] | contract.SentTransaction<unknown>,
663
+ export function assertTransactionSucceeded(
664
+ txResult: rpc.Api.GetTransactionResponse,
187
665
  contextLabel: string,
188
666
  ): void {
189
- sentTransactions = Array.isArray(sentTransactions) ? sentTransactions : [sentTransactions];
190
- for (const sentTx of sentTransactions) {
191
- const txResponse = sentTx.getTransactionResponse;
192
- if (!txResponse || txResponse.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
193
- const status = txResponse ? txResponse.status : 'UNKNOWN';
194
- throw new Error(
195
- `Transaction ${contextLabel} failed with status ${status}. Response: ${JSON.stringify(txResponse)}`,
196
- );
197
- }
667
+ if (txResult.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
668
+ throw new Error(
669
+ `Transaction ${contextLabel} failed with status ${txResult.status}. Response: ${JSON.stringify(txResult)}`,
670
+ );
198
671
  }
199
672
  }