@layerzerolabs/protocol-stellar-v2 0.2.29 → 0.2.30

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 (205) hide show
  1. package/.turbo/turbo-build.log +371 -321
  2. package/.turbo/turbo-lint.log +211 -202
  3. package/.turbo/turbo-test.log +1766 -1673
  4. package/Cargo.lock +11 -1
  5. package/contracts/common-macros/src/lib.rs +0 -2
  6. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +1 -0
  7. package/contracts/endpoint-v2/src/messaging_channel.rs +32 -3
  8. package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +1 -1
  9. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +1 -1
  10. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +6 -6
  11. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_payload_hash.rs +1 -1
  12. package/contracts/endpoint-v2/src/tests/messaging_channel/outbound.rs +16 -10
  13. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +10 -10
  14. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +3 -3
  15. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +4 -3
  16. package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +1 -57
  17. package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +0 -30
  18. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -3
  19. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +6 -4
  20. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -3
  21. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -3
  22. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +0 -30
  23. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +0 -30
  24. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/basic.rs +0 -2
  25. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/multisig_contract.rs +0 -2
  26. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_migration.rs +0 -2
  27. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_user_contractimpl.rs +1 -3
  28. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +3 -6
  29. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +10 -0
  30. package/contracts/message-libs/message-lib-common/src/worker_options.rs +6 -2
  31. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +3 -3
  32. package/contracts/message-libs/treasury/src/lib.rs +2 -1
  33. package/contracts/message-libs/treasury/src/tests/setup.rs +1 -1
  34. package/contracts/message-libs/treasury/src/treasury.rs +5 -2
  35. package/contracts/message-libs/uln-302/src/errors.rs +2 -0
  36. package/contracts/message-libs/uln-302/src/events.rs +3 -3
  37. package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +8 -0
  38. package/contracts/message-libs/uln-302/src/lib.rs +2 -1
  39. package/contracts/message-libs/uln-302/src/receive_uln.rs +16 -13
  40. package/contracts/message-libs/uln-302/src/send_uln.rs +51 -24
  41. package/contracts/message-libs/uln-302/src/storage.rs +2 -2
  42. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +45 -1
  43. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verifiable.rs +63 -0
  44. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +47 -2
  45. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +50 -1
  46. package/contracts/message-libs/uln-302/src/uln302.rs +0 -8
  47. package/contracts/oapps/counter/Cargo.toml +4 -4
  48. package/contracts/oapps/counter/integration_tests/setup_uln.rs +22 -2
  49. package/contracts/oapps/counter/src/counter.rs +8 -8
  50. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +33 -10
  51. package/contracts/oapps/oapp/src/lib.rs +6 -2
  52. package/contracts/oapps/oapp/src/oapp_core.rs +49 -24
  53. package/contracts/oapps/oapp/src/oapp_options_type3.rs +21 -14
  54. package/contracts/oapps/oapp/src/oapp_receiver.rs +17 -16
  55. package/contracts/oapps/oapp/src/oapp_sender.rs +66 -15
  56. package/contracts/oapps/oapp/src/tests/oapp_core.rs +5 -5
  57. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +18 -18
  58. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +4 -4
  59. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +3 -3
  60. package/contracts/oapps/oapp-macros/Cargo.toml +0 -1
  61. package/contracts/oapps/oapp-macros/src/generators.rs +87 -46
  62. package/contracts/oapps/oapp-macros/src/lib.rs +3 -61
  63. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +9 -23
  64. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +15 -11
  65. package/contracts/oapps/oft/Cargo.toml +1 -1
  66. package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +3 -3
  67. package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +4 -4
  68. package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +144 -8
  69. package/contracts/oapps/oft/integration-tests/setup.rs +4 -2
  70. package/contracts/oapps/oft/integration-tests/utils.rs +25 -11
  71. package/contracts/oapps/oft/src/extensions/oft_fee.rs +65 -63
  72. package/contracts/oapps/oft/src/extensions/pausable.rs +2 -3
  73. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +22 -5
  74. package/contracts/oapps/oft/src/interfaces/mint_burnable.rs +18 -0
  75. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  76. package/contracts/oapps/oft/src/lib.rs +4 -2
  77. package/contracts/oapps/oft/src/oft.rs +35 -36
  78. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +13 -9
  79. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +14 -9
  80. package/contracts/oapps/oft/src/oft_types/mod.rs +14 -12
  81. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +28 -20
  82. package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +136 -2
  83. package/contracts/oapps/oft/src/tests/oft_types/lock_unlock.rs +12 -8
  84. package/contracts/oapps/oft-core/integration-tests/setup.rs +8 -9
  85. package/contracts/oapps/oft-core/integration-tests/test_with_sml.rs +7 -6
  86. package/contracts/oapps/oft-core/integration-tests/utils.rs +5 -4
  87. package/contracts/oapps/oft-core/src/codec/oft_compose_msg_codec.rs +2 -2
  88. package/contracts/oapps/oft-core/src/codec/oft_msg_codec.rs +33 -37
  89. package/contracts/oapps/oft-core/src/errors.rs +2 -1
  90. package/contracts/oapps/oft-core/src/events.rs +6 -0
  91. package/contracts/oapps/oft-core/src/lib.rs +8 -4
  92. package/contracts/oapps/oft-core/src/oft_core.rs +205 -148
  93. package/contracts/oapps/oft-core/src/storage.rs +4 -2
  94. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +2 -2
  95. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +6 -6
  96. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +7 -6
  97. package/contracts/oapps/oft-core/src/tests/test_oft_msg_codec.rs +11 -82
  98. package/contracts/oapps/oft-core/src/tests/test_quote_oft.rs +13 -13
  99. package/contracts/oapps/oft-core/src/tests/test_quote_send.rs +1 -1
  100. package/contracts/oapps/oft-core/src/tests/test_resolve_address.rs +2 -2
  101. package/contracts/oapps/oft-core/src/tests/test_send.rs +22 -22
  102. package/contracts/oapps/oft-core/src/tests/test_utils.rs +20 -22
  103. package/contracts/oapps/oft-core/src/utils.rs +12 -8
  104. package/contracts/sac-manager/Cargo.toml +25 -0
  105. package/contracts/sac-manager/src/errors.rs +18 -0
  106. package/contracts/sac-manager/src/extensions/mod.rs +6 -0
  107. package/contracts/sac-manager/src/extensions/redistribution.rs +109 -0
  108. package/contracts/sac-manager/src/extensions/supply_control/mod.rs +488 -0
  109. package/contracts/sac-manager/src/extensions/supply_control/rate_limit.rs +126 -0
  110. package/contracts/sac-manager/src/interfaces/mod.rs +3 -0
  111. package/contracts/sac-manager/src/interfaces/sac_manager.rs +52 -0
  112. package/contracts/sac-manager/src/lib.rs +23 -0
  113. package/contracts/sac-manager/src/sac_manager.rs +193 -0
  114. package/contracts/sac-manager/src/storage.rs +20 -0
  115. package/contracts/sac-manager/src/tests/mod.rs +14 -0
  116. package/contracts/sac-manager/src/tests/redistribution/mod.rs +1 -0
  117. package/contracts/sac-manager/src/tests/redistribution/redistribute_funds.rs +82 -0
  118. package/contracts/sac-manager/src/tests/sac_manager/admin_mint.rs +206 -0
  119. package/contracts/sac-manager/src/tests/sac_manager/burn.rs +215 -0
  120. package/contracts/sac-manager/src/tests/sac_manager/clawback.rs +209 -0
  121. package/contracts/sac-manager/src/tests/sac_manager/mint.rs +252 -0
  122. package/contracts/sac-manager/src/tests/sac_manager/mod.rs +9 -0
  123. package/contracts/sac-manager/src/tests/sac_manager/set_admin.rs +36 -0
  124. package/contracts/sac-manager/src/tests/sac_manager/set_authorized.rs +43 -0
  125. package/contracts/sac-manager/src/tests/sac_manager/set_oft_address.rs +47 -0
  126. package/contracts/sac-manager/src/tests/sac_manager/test_helper.rs +75 -0
  127. package/contracts/sac-manager/src/tests/sac_manager/view_functions.rs +60 -0
  128. package/contracts/sac-manager/src/tests/supply_control/enumerable_set.rs +256 -0
  129. package/contracts/sac-manager/src/tests/supply_control/mod.rs +8 -0
  130. package/contracts/sac-manager/src/tests/supply_control/refill.rs +90 -0
  131. package/contracts/sac-manager/src/tests/supply_control/set_mint_whitelist.rs +245 -0
  132. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller.rs +267 -0
  133. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller_manager.rs +122 -0
  134. package/contracts/sac-manager/src/tests/supply_control/test_helper.rs +38 -0
  135. package/contracts/sac-manager/src/tests/supply_control/update_allow_any_mint_burn.rs +114 -0
  136. package/contracts/sac-manager/src/tests/supply_control/update_limit_config.rs +257 -0
  137. package/contracts/sac-manager/src/tests/test_helper.rs +190 -0
  138. package/contracts/upgrader/src/lib.rs +2 -1
  139. package/contracts/utils/src/errors.rs +0 -1
  140. package/contracts/utils/src/tests/upgradeable.rs +0 -66
  141. package/contracts/utils/src/upgradeable.rs +0 -18
  142. package/contracts/workers/dvn/src/dvn.rs +2 -2
  143. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  144. package/contracts/workers/dvn/src/lib.rs +2 -1
  145. package/contracts/workers/dvn-fee-lib/src/lib.rs +3 -1
  146. package/contracts/workers/executor/src/auth.rs +42 -26
  147. package/contracts/workers/executor/src/executor.rs +28 -3
  148. package/contracts/workers/executor/src/lib.rs +4 -2
  149. package/contracts/workers/executor/src/storage.rs +21 -1
  150. package/contracts/workers/executor/src/tests/auth.rs +64 -20
  151. package/contracts/workers/executor/src/tests/executor.rs +1 -1
  152. package/contracts/workers/executor/src/tests/setup.rs +18 -0
  153. package/contracts/workers/executor-fee-lib/src/lib.rs +4 -1
  154. package/contracts/workers/executor-helper/src/executor_helper.rs +24 -10
  155. package/contracts/workers/executor-helper/src/tests/setup.rs +147 -34
  156. package/contracts/workers/price-feed/src/lib.rs +3 -1
  157. package/contracts/workers/worker/src/lib.rs +2 -1
  158. package/contracts/workers/worker/src/worker.rs +31 -17
  159. package/docs/oapp-guide.md +17 -8
  160. package/docs/oft-guide.md +3 -3
  161. package/package.json +3 -3
  162. package/sdk/.turbo/turbo-test.log +512 -351
  163. package/sdk/dist/generated/bml.d.ts +3 -9
  164. package/sdk/dist/generated/bml.js +6 -7
  165. package/sdk/dist/generated/counter.d.ts +22 -28
  166. package/sdk/dist/generated/counter.js +11 -12
  167. package/sdk/dist/generated/dvn.d.ts +36 -54
  168. package/sdk/dist/generated/dvn.js +10 -15
  169. package/sdk/dist/generated/dvn_fee_lib.d.ts +3 -21
  170. package/sdk/dist/generated/dvn_fee_lib.js +6 -11
  171. package/sdk/dist/generated/endpoint.d.ts +3 -9
  172. package/sdk/dist/generated/endpoint.js +6 -7
  173. package/sdk/dist/generated/executor.d.ts +80 -54
  174. package/sdk/dist/generated/executor.js +16 -16
  175. package/sdk/dist/generated/executor_fee_lib.d.ts +3 -21
  176. package/sdk/dist/generated/executor_fee_lib.js +6 -11
  177. package/sdk/dist/generated/executor_helper.d.ts +36 -42
  178. package/sdk/dist/generated/executor_helper.js +9 -10
  179. package/sdk/dist/generated/layerzero_view.d.ts +20 -32
  180. package/sdk/dist/generated/layerzero_view.js +25 -26
  181. package/sdk/dist/generated/oft.d.ts +147 -79
  182. package/sdk/dist/generated/oft.js +47 -54
  183. package/sdk/dist/generated/price_feed.d.ts +20 -38
  184. package/sdk/dist/generated/price_feed.js +15 -20
  185. package/sdk/dist/generated/sac_manager.d.ts +1309 -0
  186. package/sdk/dist/generated/sac_manager.js +484 -0
  187. package/sdk/dist/generated/sml.d.ts +3 -9
  188. package/sdk/dist/generated/sml.js +6 -7
  189. package/sdk/dist/generated/treasury.d.ts +3 -9
  190. package/sdk/dist/generated/treasury.js +8 -9
  191. package/sdk/dist/generated/uln302.d.ts +20 -20
  192. package/sdk/dist/generated/uln302.js +25 -22
  193. package/sdk/dist/generated/upgrader.d.ts +3 -9
  194. package/sdk/dist/generated/upgrader.js +6 -7
  195. package/sdk/dist/index.d.ts +1 -0
  196. package/sdk/dist/index.js +1 -0
  197. package/sdk/package.json +1 -1
  198. package/sdk/src/index.ts +1 -0
  199. package/sdk/test/oft-sml.test.ts +7 -5
  200. package/sdk/test/sac-manager-redistribution.test.ts +578 -0
  201. package/sdk/test/suites/globalSetup.ts +11 -6
  202. package/sdk/test/test_data/test_upgradeable_dvn.wasm +0 -0
  203. package/sdk/test/upgrader.test.ts +75 -202
  204. package/sdk/test/utils.ts +40 -0
  205. package/tools/ts-bindings-gen/src/main.rs +1 -0
@@ -0,0 +1,578 @@
1
+ import {
2
+ Address,
3
+ Asset,
4
+ AuthClawbackEnabledFlag,
5
+ type AuthFlag,
6
+ AuthRevocableFlag,
7
+ BASE_FEE,
8
+ Keypair,
9
+ Operation,
10
+ rpc,
11
+ TransactionBuilder,
12
+ } from '@stellar/stellar-sdk';
13
+ import path from 'path';
14
+ import { beforeAll, describe, expect, it } from 'vitest';
15
+
16
+ import { getFullyQualifiedRepoRootPath } from '@layerzerolabs/common-node-utils';
17
+
18
+ import { Client as SacManagerClient } from '../src/generated/sac_manager';
19
+ import { DEFAULT_DEPLOYER, NETWORK_PASSPHRASE, RPC_URL } from './suites/constants';
20
+ import { deployAssetSac, deployContract } from './suites/deploy';
21
+ import { fundAccount } from './suites/localnet';
22
+ import { getTokenAuthorized, getTokenBalance } from './utils';
23
+
24
+ // ============================================================================
25
+ // Test Accounts
26
+ // ============================================================================
27
+
28
+ const TOKEN_ISSUER = Keypair.random();
29
+ const REDISTRIBUTION_ADMIN = Keypair.random();
30
+ const USER_A = Keypair.random();
31
+ const USER_B = Keypair.random();
32
+ const USER_C = Keypair.random();
33
+
34
+ // Token configuration
35
+ const TOKEN_CODE = 'RTKN';
36
+ let TOKEN_ASSET: Asset;
37
+
38
+ // Contract addresses
39
+ let sacTokenAddress = '';
40
+ let sacManagerAddress = '';
41
+
42
+ // Clients
43
+ let sacManagerClient: SacManagerClient;
44
+
45
+ // Initial token amounts
46
+ const INITIAL_TOKEN_AMOUNT = '1000'; // 1000 tokens
47
+ const TRANSFER_AMOUNT = 100_0000000n; // 100 tokens (7 decimals)
48
+ const REDISTRIBUTE_AMOUNT = 50_0000000n; // 50 tokens (7 decimals)
49
+
50
+ // ============================================================================
51
+ // Helper Functions
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Creates a SacManagerClient with a specific signer
56
+ */
57
+ function createClientWithSigner(contractId: string, signer: Keypair): SacManagerClient {
58
+ return new SacManagerClient({
59
+ contractId,
60
+ publicKey: signer.publicKey(),
61
+ signTransaction: async (tx: string) => {
62
+ const transaction = TransactionBuilder.fromXDR(tx, NETWORK_PASSPHRASE);
63
+ transaction.sign(signer);
64
+ return {
65
+ signedTxXdr: transaction.toXDR(),
66
+ signerAddress: signer.publicKey(),
67
+ };
68
+ },
69
+ rpcUrl: RPC_URL,
70
+ networkPassphrase: NETWORK_PASSPHRASE,
71
+ allowHttp: true,
72
+ });
73
+ }
74
+
75
+ // Note: The old executeRedistributeFundsWithAuth function has been removed
76
+ // because we now use the SacManagerClient directly which handles authorization properly
77
+
78
+ /**
79
+ * Invokes a SAC contract function using raw transaction building
80
+ */
81
+ async function invokeSacFunction(
82
+ sacAddress: string,
83
+ functionName: string,
84
+ args: any[],
85
+ signer: Keypair,
86
+ ): Promise<void> {
87
+ const server = new rpc.Server(RPC_URL, { allowHttp: true });
88
+ const account = await server.getAccount(signer.publicKey());
89
+
90
+ const tx = new TransactionBuilder(account, {
91
+ fee: BASE_FEE,
92
+ networkPassphrase: NETWORK_PASSPHRASE,
93
+ })
94
+ .addOperation(
95
+ Operation.invokeContractFunction({
96
+ contract: sacAddress,
97
+ function: functionName,
98
+ args,
99
+ }),
100
+ )
101
+ .setTimeout(30)
102
+ .build();
103
+
104
+ const simulated = await server.simulateTransaction(tx);
105
+ if (rpc.Api.isSimulationError(simulated)) {
106
+ throw new Error(`Simulation failed: ${JSON.stringify(simulated)}`);
107
+ }
108
+
109
+ const preparedTx = rpc.assembleTransaction(tx, simulated).build();
110
+ preparedTx.sign(signer);
111
+
112
+ const sendResult = await server.sendTransaction(preparedTx);
113
+ if (sendResult.status !== 'PENDING') {
114
+ throw new Error(`Transaction failed to send: ${JSON.stringify(sendResult)}`);
115
+ }
116
+
117
+ const txResult = await server.pollTransaction(sendResult.hash);
118
+ if (txResult.status !== 'SUCCESS') {
119
+ throw new Error(`Transaction not successful: ${JSON.stringify(txResult)}`);
120
+ }
121
+ }
122
+
123
+ // ============================================================================
124
+ // Test Suite
125
+ // ============================================================================
126
+
127
+ describe('SAC Manager Redistribution E2E Tests', async () => {
128
+ const repoRoot = await getFullyQualifiedRepoRootPath();
129
+ const wasmDir = path.join(
130
+ repoRoot,
131
+ 'contracts',
132
+ 'protocol',
133
+ 'stellar',
134
+ 'target',
135
+ 'wasm32v1-none',
136
+ 'release',
137
+ );
138
+
139
+ const SAC_MANAGER_WASM_PATH = path.join(wasmDir, 'sac_manager.wasm');
140
+
141
+ beforeAll(async () => {
142
+ console.log('\n====================================');
143
+ console.log('SAC Manager Redistribution E2E Tests');
144
+ console.log('====================================\n');
145
+
146
+ // Fund test accounts (including DEFAULT_DEPLOYER which is normally funded by globalSetup)
147
+ console.log('Funding test accounts...');
148
+ await fundAccount(DEFAULT_DEPLOYER.publicKey());
149
+ await fundAccount(TOKEN_ISSUER.publicKey());
150
+ await fundAccount(REDISTRIBUTION_ADMIN.publicKey());
151
+ await fundAccount(USER_A.publicKey());
152
+ await fundAccount(USER_B.publicKey());
153
+ await fundAccount(USER_C.publicKey());
154
+ console.log('Test accounts funded');
155
+
156
+ // Create the token asset
157
+ TOKEN_ASSET = new Asset(TOKEN_CODE, TOKEN_ISSUER.publicKey());
158
+ });
159
+
160
+ // ========================================================================
161
+ // Setup SAC Manager with SAC (redistribution enabled at construction)
162
+ // ========================================================================
163
+
164
+ describe('Setup SAC Manager with SAC', () => {
165
+ it('Set AUTH_REVOCABLE and AUTH_CLAWBACK_ENABLED flags on issuer account', async () => {
166
+ const server = new rpc.Server(RPC_URL, { allowHttp: true });
167
+ const issuerAccount = await server.getAccount(TOKEN_ISSUER.publicKey());
168
+
169
+ // Set AUTH_REVOCABLE and AUTH_CLAWBACK_ENABLED flags on issuer
170
+ // AUTH_REVOCABLE allows revoking authorization (blacklisting)
171
+ // AUTH_CLAWBACK_ENABLED allows clawback operations (required for redistribute_funds)
172
+ const authFlags = (AuthRevocableFlag | AuthClawbackEnabledFlag) as AuthFlag;
173
+ const setOptionsTx = new TransactionBuilder(issuerAccount, {
174
+ fee: BASE_FEE,
175
+ networkPassphrase: NETWORK_PASSPHRASE,
176
+ })
177
+ .addOperation(
178
+ Operation.setOptions({
179
+ setFlags: authFlags,
180
+ }),
181
+ )
182
+ .setTimeout(30)
183
+ .build();
184
+
185
+ setOptionsTx.sign(TOKEN_ISSUER);
186
+
187
+ const sendResult = await server.sendTransaction(setOptionsTx);
188
+ if (sendResult.status !== 'PENDING') {
189
+ throw new Error(`Failed to set auth flags: ${JSON.stringify(sendResult)}`);
190
+ }
191
+
192
+ const txResult = await server.pollTransaction(sendResult.hash);
193
+ if (txResult.status !== 'SUCCESS') {
194
+ throw new Error(`Failed to set auth flags: ${JSON.stringify(txResult)}`);
195
+ }
196
+
197
+ console.log('AUTH_REVOCABLE and AUTH_CLAWBACK_ENABLED flags set on issuer');
198
+ });
199
+
200
+ it('Deploy SAC with trustlines and issue tokens', async () => {
201
+ const server = new rpc.Server(RPC_URL, { allowHttp: true });
202
+
203
+ // Create trustlines for all test accounts and issue tokens
204
+ const issuerAccount = await server.getAccount(TOKEN_ISSUER.publicKey());
205
+ const issueTx = new TransactionBuilder(issuerAccount, {
206
+ fee: BASE_FEE,
207
+ networkPassphrase: NETWORK_PASSPHRASE,
208
+ })
209
+ // Trustline for DEFAULT_DEPLOYER (will be the SAC manager admin)
210
+ .addOperation(
211
+ Operation.changeTrust({
212
+ asset: TOKEN_ASSET,
213
+ source: DEFAULT_DEPLOYER.publicKey(),
214
+ }),
215
+ )
216
+ // Trustline for REDISTRIBUTION_ADMIN
217
+ .addOperation(
218
+ Operation.changeTrust({
219
+ asset: TOKEN_ASSET,
220
+ source: REDISTRIBUTION_ADMIN.publicKey(),
221
+ }),
222
+ )
223
+ // Trustline for USER_A
224
+ .addOperation(
225
+ Operation.changeTrust({
226
+ asset: TOKEN_ASSET,
227
+ source: USER_A.publicKey(),
228
+ }),
229
+ )
230
+ // Trustline for USER_B
231
+ .addOperation(
232
+ Operation.changeTrust({
233
+ asset: TOKEN_ASSET,
234
+ source: USER_B.publicKey(),
235
+ }),
236
+ )
237
+ // Trustline for USER_C
238
+ .addOperation(
239
+ Operation.changeTrust({
240
+ asset: TOKEN_ASSET,
241
+ source: USER_C.publicKey(),
242
+ }),
243
+ )
244
+ // Issue tokens to USER_A (sender)
245
+ .addOperation(
246
+ Operation.payment({
247
+ asset: TOKEN_ASSET,
248
+ amount: INITIAL_TOKEN_AMOUNT,
249
+ destination: USER_A.publicKey(),
250
+ }),
251
+ )
252
+ // Issue tokens to USER_B (will be blacklisted)
253
+ .addOperation(
254
+ Operation.payment({
255
+ asset: TOKEN_ASSET,
256
+ amount: INITIAL_TOKEN_AMOUNT,
257
+ destination: USER_B.publicKey(),
258
+ }),
259
+ )
260
+ .setTimeout(30)
261
+ .build();
262
+
263
+ issueTx.sign(
264
+ TOKEN_ISSUER,
265
+ DEFAULT_DEPLOYER,
266
+ REDISTRIBUTION_ADMIN,
267
+ USER_A,
268
+ USER_B,
269
+ USER_C,
270
+ );
271
+
272
+ const sendResult = await server.sendTransaction(issueTx);
273
+ if (sendResult.status !== 'PENDING') {
274
+ throw new Error(`Failed to setup trustlines: ${JSON.stringify(sendResult)}`);
275
+ }
276
+
277
+ const txResult = await server.pollTransaction(sendResult.hash);
278
+ if (txResult.status !== 'SUCCESS') {
279
+ throw new Error(`Failed to setup trustlines: ${JSON.stringify(txResult)}`);
280
+ }
281
+
282
+ console.log('Trustlines created and tokens issued');
283
+
284
+ // Deploy the SAC for the token
285
+ sacTokenAddress = await deployAssetSac(TOKEN_ASSET);
286
+ console.log('SAC deployed at:', sacTokenAddress);
287
+ });
288
+
289
+ it('Deploy SAC Manager contract with redistribution enabled', async () => {
290
+ sacManagerClient = await deployContract<SacManagerClient>(
291
+ SacManagerClient,
292
+ SAC_MANAGER_WASM_PATH,
293
+ {
294
+ sac_token: sacTokenAddress,
295
+ owner: DEFAULT_DEPLOYER.publicKey(),
296
+ redistribution_enabled: true,
297
+ supply_control_enabled: false,
298
+ },
299
+ DEFAULT_DEPLOYER,
300
+ );
301
+
302
+ sacManagerAddress = sacManagerClient.options.contractId;
303
+ console.log('SAC Manager deployed at:', sacManagerAddress);
304
+ });
305
+
306
+ it('Set SAC admin to SAC Manager', async () => {
307
+ // Set the SAC admin to the SAC manager contract
308
+ // This allows the SAC manager to call clawback, set_authorized, mint, etc.
309
+ await invokeSacFunction(
310
+ sacTokenAddress,
311
+ 'set_admin',
312
+ [Address.fromString(sacManagerAddress).toScVal()],
313
+ TOKEN_ISSUER,
314
+ );
315
+
316
+ console.log('SAC admin set to SAC Manager');
317
+ });
318
+
319
+ it('Verify redistribution is enabled at construction', async () => {
320
+ const { result: enabled } = await sacManagerClient.redistribution_enabled();
321
+ expect(enabled).toBe(true);
322
+ console.log('Redistribution enabled:', enabled);
323
+ });
324
+
325
+ it('Verify owner (DEFAULT_DEPLOYER) starts with zero balance', async () => {
326
+ const ownerBalance = await getTokenBalance(
327
+ DEFAULT_DEPLOYER.publicKey(),
328
+ sacTokenAddress,
329
+ );
330
+ console.log('Owner (DEFAULT_DEPLOYER) balance:', ownerBalance.toString());
331
+ expect(ownerBalance).toBe(0n);
332
+ });
333
+ });
334
+
335
+ // ========================================================================
336
+ // Blacklist Management
337
+ // ========================================================================
338
+
339
+ describe('Blacklist Management', () => {
340
+ it('Verify all accounts start authorized (AUTH_REQUIRED=false)', async () => {
341
+ // Check authorization status directly on the SAC
342
+ const userAAuthorized = await getTokenAuthorized(USER_A.publicKey(), sacTokenAddress);
343
+ const userBAuthorized = await getTokenAuthorized(USER_B.publicKey(), sacTokenAddress);
344
+ const userCAuthorized = await getTokenAuthorized(USER_C.publicKey(), sacTokenAddress);
345
+
346
+ expect(userAAuthorized).toBe(true);
347
+ expect(userBAuthorized).toBe(true);
348
+ expect(userCAuthorized).toBe(true);
349
+
350
+ console.log('All users start authorized');
351
+ });
352
+
353
+ it('Blacklist USER_B via set_authorized', async () => {
354
+ // Use DEFAULT_DEPLOYER (owner) to blacklist USER_B
355
+ const assembledTx = await sacManagerClient.set_authorized({
356
+ id: USER_B.publicKey(),
357
+ authorize: false,
358
+ });
359
+
360
+ await assembledTx.signAndSend();
361
+ console.log('USER_B blacklisted (authorized=false)');
362
+ });
363
+
364
+ it('Verify USER_B is blacklisted (not authorized on SAC)', async () => {
365
+ const authorized = await getTokenAuthorized(USER_B.publicKey(), sacTokenAddress);
366
+ expect(authorized).toBe(false);
367
+ console.log('USER_B authorized:', authorized);
368
+ });
369
+
370
+ it('Non-admin cannot call set_authorized', async () => {
371
+ const userClient = createClientWithSigner(sacManagerAddress, USER_A);
372
+
373
+ const assembledTx = await userClient.set_authorized({
374
+ id: USER_C.publicKey(),
375
+ authorize: false,
376
+ });
377
+
378
+ await expect(assembledTx.signAndSend()).rejects.toThrow();
379
+ console.log('Non-admin correctly rejected from calling set_authorized');
380
+ });
381
+
382
+ it('Un-blacklist USER_B via set_authorized', async () => {
383
+ // Re-authorize USER_B
384
+ const assembledTx = await sacManagerClient.set_authorized({
385
+ id: USER_B.publicKey(),
386
+ authorize: true,
387
+ });
388
+
389
+ await assembledTx.signAndSend();
390
+
391
+ // Verify
392
+ const authorized = await getTokenAuthorized(USER_B.publicKey(), sacTokenAddress);
393
+ expect(authorized).toBe(true);
394
+
395
+ const isAuthorized = await getTokenAuthorized(USER_B.publicKey(), sacTokenAddress);
396
+ expect(isAuthorized).toBe(true);
397
+
398
+ console.log('USER_B un-blacklisted');
399
+ });
400
+
401
+ it('Re-blacklist USER_B for mint redirection tests', async () => {
402
+ const assembledTx = await sacManagerClient.set_authorized({
403
+ id: USER_B.publicKey(),
404
+ authorize: false,
405
+ });
406
+
407
+ await assembledTx.signAndSend();
408
+
409
+ const isAuthorized = await getTokenAuthorized(USER_B.publicKey(), sacTokenAddress);
410
+ expect(isAuthorized).toBe(false);
411
+ console.log('USER_B re-blacklisted for transfer tests');
412
+ });
413
+ });
414
+
415
+ // ========================================================================
416
+ // Mint Redirection (redistribution enabled)
417
+ // ========================================================================
418
+
419
+ describe('Mint Redirection', () => {
420
+ it('Verify initial balances', async () => {
421
+ const userABalance = await getTokenBalance(USER_A.publicKey(), sacTokenAddress);
422
+ const userBBalance = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
423
+ const userCBalance = await getTokenBalance(USER_C.publicKey(), sacTokenAddress);
424
+ const ownerBalance = await getTokenBalance(
425
+ DEFAULT_DEPLOYER.publicKey(),
426
+ sacTokenAddress,
427
+ );
428
+
429
+ console.log('\nInitial Balances:');
430
+ console.log(` USER_A: ${userABalance}`);
431
+ console.log(` USER_B: ${userBBalance}`);
432
+ console.log(` USER_C: ${userCBalance}`);
433
+ console.log(` Owner (DEFAULT_DEPLOYER): ${ownerBalance}`);
434
+
435
+ expect(userABalance).toBe(10000000000n); // 1000 tokens
436
+ expect(userBBalance).toBe(10000000000n); // 1000 tokens
437
+ expect(userCBalance).toBe(0n);
438
+ expect(ownerBalance).toBe(0n);
439
+ });
440
+
441
+ it('Admin mint to non-blacklisted address succeeds normally', async () => {
442
+ const userCBalanceBefore = await getTokenBalance(USER_C.publicKey(), sacTokenAddress);
443
+
444
+ const assembledTx = await sacManagerClient.authorized_mint({
445
+ sender: DEFAULT_DEPLOYER.publicKey(),
446
+ to: USER_C.publicKey(),
447
+ amount: TRANSFER_AMOUNT,
448
+ });
449
+ await assembledTx.signAndSend();
450
+
451
+ // Verify balance increased
452
+ const userCBalance = await getTokenBalance(USER_C.publicKey(), sacTokenAddress);
453
+
454
+ console.log('\nAfter authorized_mint to non-blacklisted (USER_C):');
455
+ console.log(` USER_C: ${userCBalance}`);
456
+
457
+ expect(userCBalance).toBe(userCBalanceBefore + TRANSFER_AMOUNT);
458
+ });
459
+
460
+ it('Admin mint to blacklisted address redirects to redistribution target (owner)', async () => {
461
+ const ownerBalanceBefore = await getTokenBalance(
462
+ DEFAULT_DEPLOYER.publicKey(),
463
+ sacTokenAddress,
464
+ );
465
+ const userBBalanceBefore = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
466
+
467
+ const assembledTx = await sacManagerClient.authorized_mint({
468
+ sender: DEFAULT_DEPLOYER.publicKey(),
469
+ to: USER_B.publicKey(), // Blacklisted!
470
+ amount: TRANSFER_AMOUNT,
471
+ });
472
+ await assembledTx.signAndSend();
473
+
474
+ // Verify balances - USER_B should not receive, owner should
475
+ const userBBalance = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
476
+ const ownerBalance = await getTokenBalance(
477
+ DEFAULT_DEPLOYER.publicKey(),
478
+ sacTokenAddress,
479
+ );
480
+
481
+ console.log('\nAfter authorized_mint to blacklisted (USER_B):');
482
+ console.log(` USER_B: ${userBBalance} (unchanged)`);
483
+ console.log(` Owner (received redirected): ${ownerBalance}`);
484
+
485
+ // USER_B balance should be unchanged
486
+ expect(userBBalance).toBe(userBBalanceBefore);
487
+ // Owner should receive the redirected tokens
488
+ expect(ownerBalance).toBe(ownerBalanceBefore + TRANSFER_AMOUNT);
489
+ });
490
+ });
491
+
492
+ // ========================================================================
493
+ // Redistribute Funds
494
+ // ========================================================================
495
+
496
+ describe('Redistribute Funds', () => {
497
+ it('Redistribute funds from blacklisted account', async () => {
498
+ const ownerBalanceBefore = await getTokenBalance(
499
+ DEFAULT_DEPLOYER.publicKey(),
500
+ sacTokenAddress,
501
+ );
502
+ const userBBalanceBefore = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
503
+
504
+ console.log('\nBefore redistribute_blacklisted_funds:');
505
+ console.log(` USER_B: ${userBBalanceBefore}`);
506
+ console.log(` Owner: ${ownerBalanceBefore}`);
507
+
508
+ // Only owner can redistribute - use DEFAULT_DEPLOYER
509
+ const assembledTx = await sacManagerClient.redistribute_blacklisted_funds({
510
+ from: USER_B.publicKey(),
511
+ amount: REDISTRIBUTE_AMOUNT,
512
+ });
513
+ await assembledTx.signAndSend();
514
+
515
+ // Verify balances
516
+ const userBBalance = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
517
+ const ownerBalance = await getTokenBalance(
518
+ DEFAULT_DEPLOYER.publicKey(),
519
+ sacTokenAddress,
520
+ );
521
+
522
+ console.log('\nAfter redistribute_blacklisted_funds:');
523
+ console.log(` USER_B: ${userBBalance}`);
524
+ console.log(` Owner: ${ownerBalance}`);
525
+
526
+ expect(userBBalance).toBe(userBBalanceBefore - REDISTRIBUTE_AMOUNT);
527
+ expect(ownerBalance).toBe(ownerBalanceBefore + REDISTRIBUTE_AMOUNT);
528
+ });
529
+
530
+ it('Cannot redistribute from non-blacklisted account', async () => {
531
+ // USER_A is not blacklisted - this should fail even for owner
532
+ const assembledTx = await sacManagerClient.redistribute_blacklisted_funds({
533
+ from: USER_A.publicKey(),
534
+ amount: REDISTRIBUTE_AMOUNT,
535
+ });
536
+ await expect(assembledTx.signAndSend()).rejects.toThrow();
537
+ console.log('Correctly rejected redistribution from non-blacklisted account');
538
+ });
539
+
540
+ it('Non-owner cannot redistribute', async () => {
541
+ // USER_A is not the owner - this should fail
542
+ const userClient = createClientWithSigner(sacManagerAddress, USER_A);
543
+ const assembledTx = await userClient.redistribute_blacklisted_funds({
544
+ from: USER_B.publicKey(),
545
+ amount: REDISTRIBUTE_AMOUNT,
546
+ });
547
+ await expect(assembledTx.signAndSend()).rejects.toThrow();
548
+ console.log('Non-owner correctly rejected from redistributing');
549
+ });
550
+ });
551
+
552
+ // ========================================================================
553
+ // Final Summary
554
+ // ========================================================================
555
+
556
+ describe('Final Summary', () => {
557
+ it('Print final balances', async () => {
558
+ const userABalance = await getTokenBalance(USER_A.publicKey(), sacTokenAddress);
559
+ const userBBalance = await getTokenBalance(USER_B.publicKey(), sacTokenAddress);
560
+ const userCBalance = await getTokenBalance(USER_C.publicKey(), sacTokenAddress);
561
+ const ownerBalance = await getTokenBalance(
562
+ DEFAULT_DEPLOYER.publicKey(),
563
+ sacTokenAddress,
564
+ );
565
+
566
+ console.log('\n========================================');
567
+ console.log('Final Balance Summary');
568
+ console.log('========================================');
569
+ console.log(` USER_A: ${userABalance}`);
570
+ console.log(` USER_B (blacklisted): ${userBBalance}`);
571
+ console.log(` USER_C: ${userCBalance}`);
572
+ console.log(` Owner (redistribution target): ${ownerBalance}`);
573
+ console.log('========================================\n');
574
+
575
+ console.log('SAC Manager Redistribution E2E tests completed successfully!');
576
+ });
577
+ });
578
+ });
@@ -230,18 +230,12 @@ async function deployChainContracts(eid: number, chainLabel: string): Promise<Ch
230
230
 
231
231
  // 10. Deploy Executor (supports both ULN302 and SML)
232
232
  console.log(`🚀 [${chainLabel}] Deploying Executor...`);
233
- const whitelist = [
234
- [addresses.executorHelper, 'native_drop_and_execute'],
235
- [addresses.executorHelper, 'execute'],
236
- [addresses.executorHelper, 'compose'],
237
- ];
238
233
  const executorClient = await deployContract<ExecutorClient>(
239
234
  ExecutorClient,
240
235
  path.join(wasmDir, 'executor.wasm'),
241
236
  {
242
237
  owner: DEFAULT_DEPLOYER.publicKey(),
243
238
  endpoint: addresses.endpointV2,
244
- whitelist,
245
239
  admins: [EXECUTOR_ADMIN.publicKey(), DEFAULT_DEPLOYER.publicKey()],
246
240
  message_libs: [addresses.uln302, addresses.sml],
247
241
  price_feed: addresses.priceFeed,
@@ -254,6 +248,17 @@ async function deployChainContracts(eid: number, chainLabel: string): Promise<Ch
254
248
  addresses.executor = executorClient.options.contractId;
255
249
  console.log(`✅ [${chainLabel}] Executor deployed:`, addresses.executor);
256
250
 
251
+ // 11. Register Executor Helper with Executor
252
+ console.log(`🚀 [${chainLabel}] Registering Executor Helper with Executor...`);
253
+ await (
254
+ await executorClient.set_executor_helper({
255
+ admin: DEFAULT_DEPLOYER.publicKey(),
256
+ helper: addresses.executorHelper,
257
+ allowed_functions: ['execute', 'compose', 'native_drop_and_execute'],
258
+ })
259
+ ).signAndSend();
260
+ console.log(`✅ [${chainLabel}] Executor Helper registered`);
261
+
257
262
  const clients: ChainClients = {
258
263
  endpointClient,
259
264
  uln302Client,