@recoillink/deploy-sdk 0.0.0-stage → 11.0.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 (103) hide show
  1. package/LICENSE.md +193 -0
  2. package/dist/AltVMFileSubmitter.d.ts +12 -0
  3. package/dist/AltVMFileSubmitter.d.ts.map +1 -0
  4. package/dist/AltVMFileSubmitter.js +35 -0
  5. package/dist/AltVMFileSubmitter.js.map +1 -0
  6. package/dist/AltVMFileSubmitter.test.d.ts +2 -0
  7. package/dist/AltVMFileSubmitter.test.d.ts.map +1 -0
  8. package/dist/AltVMFileSubmitter.test.js +46 -0
  9. package/dist/AltVMFileSubmitter.test.js.map +1 -0
  10. package/dist/AltVMJsonRpcSubmitter.d.ts +2 -0
  11. package/dist/AltVMJsonRpcSubmitter.d.ts.map +1 -0
  12. package/dist/AltVMJsonRpcSubmitter.js +2 -0
  13. package/dist/AltVMJsonRpcSubmitter.js.map +1 -0
  14. package/dist/core/core-artifact-reader.d.ts +67 -0
  15. package/dist/core/core-artifact-reader.d.ts.map +1 -0
  16. package/dist/core/core-artifact-reader.js +118 -0
  17. package/dist/core/core-artifact-reader.js.map +1 -0
  18. package/dist/core/core-artifact-reader.test.d.ts +2 -0
  19. package/dist/core/core-artifact-reader.test.d.ts.map +1 -0
  20. package/dist/core/core-artifact-reader.test.js +310 -0
  21. package/dist/core/core-artifact-reader.test.js.map +1 -0
  22. package/dist/core/core-writer.d.ts +43 -0
  23. package/dist/core/core-writer.d.ts.map +1 -0
  24. package/dist/core/core-writer.js +238 -0
  25. package/dist/core/core-writer.js.map +1 -0
  26. package/dist/core/core-writer.test.d.ts +2 -0
  27. package/dist/core/core-writer.test.d.ts.map +1 -0
  28. package/dist/core/core-writer.test.js +1315 -0
  29. package/dist/core/core-writer.test.js.map +1 -0
  30. package/dist/fee/fee-reader.d.ts +25 -0
  31. package/dist/fee/fee-reader.d.ts.map +1 -0
  32. package/dist/fee/fee-reader.js +35 -0
  33. package/dist/fee/fee-reader.js.map +1 -0
  34. package/dist/fee/fee-writer.d.ts +31 -0
  35. package/dist/fee/fee-writer.d.ts.map +1 -0
  36. package/dist/fee/fee-writer.js +45 -0
  37. package/dist/fee/fee-writer.js.map +1 -0
  38. package/dist/hook/hook-reader.d.ts +41 -0
  39. package/dist/hook/hook-reader.d.ts.map +1 -0
  40. package/dist/hook/hook-reader.js +52 -0
  41. package/dist/hook/hook-reader.js.map +1 -0
  42. package/dist/hook/hook-writer.d.ts +101 -0
  43. package/dist/hook/hook-writer.d.ts.map +1 -0
  44. package/dist/hook/hook-writer.js +140 -0
  45. package/dist/hook/hook-writer.js.map +1 -0
  46. package/dist/hook/hook-writer.test.d.ts +2 -0
  47. package/dist/hook/hook-writer.test.d.ts.map +1 -0
  48. package/dist/hook/hook-writer.test.js +70 -0
  49. package/dist/hook/hook-writer.test.js.map +1 -0
  50. package/dist/index.d.ts +14 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +17 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/ism/generic-ism-context.test.d.ts +2 -0
  55. package/dist/ism/generic-ism-context.test.d.ts.map +1 -0
  56. package/dist/ism/generic-ism-context.test.js +112 -0
  57. package/dist/ism/generic-ism-context.test.js.map +1 -0
  58. package/dist/ism/generic-ism-writer.d.ts +62 -0
  59. package/dist/ism/generic-ism-writer.d.ts.map +1 -0
  60. package/dist/ism/generic-ism-writer.js +100 -0
  61. package/dist/ism/generic-ism-writer.js.map +1 -0
  62. package/dist/ism/generic-ism.d.ts +43 -0
  63. package/dist/ism/generic-ism.d.ts.map +1 -0
  64. package/dist/ism/generic-ism.js +105 -0
  65. package/dist/ism/generic-ism.js.map +1 -0
  66. package/dist/ism/generic-ism.test.d.ts +2 -0
  67. package/dist/ism/generic-ism.test.d.ts.map +1 -0
  68. package/dist/ism/generic-ism.test.js +145 -0
  69. package/dist/ism/generic-ism.test.js.map +1 -0
  70. package/dist/ism/routing-ism.d.ts +21 -0
  71. package/dist/ism/routing-ism.d.ts.map +1 -0
  72. package/dist/ism/routing-ism.js +149 -0
  73. package/dist/ism/routing-ism.js.map +1 -0
  74. package/dist/protocol.d.ts +3 -0
  75. package/dist/protocol.d.ts.map +1 -0
  76. package/dist/protocol.js +38 -0
  77. package/dist/protocol.js.map +1 -0
  78. package/dist/protocol.test.d.ts +2 -0
  79. package/dist/protocol.test.d.ts.map +1 -0
  80. package/dist/protocol.test.js +14 -0
  81. package/dist/protocol.test.js.map +1 -0
  82. package/dist/utils/validation.d.ts +46 -0
  83. package/dist/utils/validation.d.ts.map +1 -0
  84. package/dist/utils/validation.js +132 -0
  85. package/dist/utils/validation.js.map +1 -0
  86. package/dist/utils/validation.test.d.ts +2 -0
  87. package/dist/utils/validation.test.d.ts.map +1 -0
  88. package/dist/utils/validation.test.js +160 -0
  89. package/dist/utils/validation.test.js.map +1 -0
  90. package/dist/warp/warp-reader.d.ts +57 -0
  91. package/dist/warp/warp-reader.d.ts.map +1 -0
  92. package/dist/warp/warp-reader.js +135 -0
  93. package/dist/warp/warp-reader.js.map +1 -0
  94. package/dist/warp/warp-writer.d.ts +67 -0
  95. package/dist/warp/warp-writer.d.ts.map +1 -0
  96. package/dist/warp/warp-writer.js +381 -0
  97. package/dist/warp/warp-writer.js.map +1 -0
  98. package/dist/warp/warp-writer.test.d.ts +2 -0
  99. package/dist/warp/warp-writer.test.d.ts.map +1 -0
  100. package/dist/warp/warp-writer.test.js +1855 -0
  101. package/dist/warp/warp-writer.test.js.map +1 -0
  102. package/package.json +54 -3
  103. package/README.md +0 -3
@@ -0,0 +1,1315 @@
1
+ import { expect } from 'chai';
2
+ import sinon from 'sinon';
3
+ import { MockSigner, ProtocolType, hasProtocol, registerProtocol, } from '@recoillink/provider-sdk';
4
+ import { ArtifactState, } from '@recoillink/provider-sdk/artifact';
5
+ import { CompositeIsmNodeType, IsmType, } from '@recoillink/provider-sdk/ism';
6
+ import { ZERO_ADDRESS_HEX_32 } from '@recoillink/utils';
7
+ import { CoreWriter } from './core-writer.js';
8
+ // Test protocol
9
+ const TestProtocol = 'test-core-writer';
10
+ let mockIsmArtifactManager;
11
+ let mockHookArtifactManager;
12
+ let mockProtocolProvider;
13
+ if (!hasProtocol(TestProtocol)) {
14
+ registerProtocol(TestProtocol, () => mockProtocolProvider);
15
+ }
16
+ if (!hasProtocol(ProtocolType.Starknet)) {
17
+ registerProtocol(ProtocolType.Starknet, () => mockProtocolProvider);
18
+ }
19
+ describe('CoreWriter', () => {
20
+ const mockMailboxAddress = '0xMAILBOX';
21
+ const mockIsmAddress = '0xISM';
22
+ const mockNewIsmAddress = '0xNEWISM';
23
+ const mockDefaultHookAddress = '0xDEFAULTHOOK';
24
+ const mockRequiredHookAddress = '0xREQUIREDHOOK';
25
+ const mockValidatorAnnounceAddress = '0xVA';
26
+ const mockOwner = '0xOWNER';
27
+ const mockSignerAddress = '0xSIGNER';
28
+ const mockDomainId = 1;
29
+ const chainName = 'test-chain';
30
+ let createMailboxWriterStub;
31
+ let createVAWriterStub;
32
+ let getSignerAddressStub;
33
+ let sendAndConfirmTxStub;
34
+ let mailboxArtifactManager;
35
+ let validatorAnnounceArtifactManager;
36
+ let signer;
37
+ let chainLookup;
38
+ let chainMetadata;
39
+ let coreWriter;
40
+ const mockReceipt = {
41
+ transactionHash: '0xHASH',
42
+ blockNumber: 123,
43
+ };
44
+ const mockIsm = {
45
+ artifactState: ArtifactState.DEPLOYED,
46
+ config: {
47
+ type: IsmType.MERKLE_ROOT_MULTISIG,
48
+ validators: ['0xVALIDATOR1'],
49
+ threshold: 1,
50
+ },
51
+ deployed: { address: mockIsmAddress },
52
+ };
53
+ const mockDefaultHook = {
54
+ artifactState: ArtifactState.DEPLOYED,
55
+ config: {
56
+ type: 'merkleTreeHook',
57
+ },
58
+ deployed: { address: mockDefaultHookAddress },
59
+ };
60
+ const mockRequiredHook = {
61
+ artifactState: ArtifactState.DEPLOYED,
62
+ config: {
63
+ type: 'interchainGasPaymaster',
64
+ owner: mockOwner,
65
+ beneficiary: '0xBENEFICIARY',
66
+ oracleKey: '0xORACLE',
67
+ overhead: {},
68
+ oracleConfig: {},
69
+ },
70
+ deployed: { address: mockRequiredHookAddress },
71
+ };
72
+ beforeEach(async () => {
73
+ mockIsmArtifactManager = {
74
+ readIsm: sinon.stub(),
75
+ createReader: sinon.stub().returns({ read: sinon.stub() }),
76
+ createWriter: sinon.stub().returns({
77
+ create: sinon.stub(),
78
+ update: sinon.stub(),
79
+ read: sinon.stub(),
80
+ }),
81
+ };
82
+ mockHookArtifactManager = {
83
+ readHook: sinon.stub(),
84
+ createReader: sinon.stub().returns({ read: sinon.stub() }),
85
+ createWriter: sinon.stub().returns({
86
+ create: sinon.stub(),
87
+ update: sinon.stub(),
88
+ read: sinon.stub(),
89
+ }),
90
+ };
91
+ mockProtocolProvider = {
92
+ createProvider: sinon.stub(),
93
+ createSigner: sinon.stub(),
94
+ createSubmitter: sinon.stub(),
95
+ createIsmArtifactManager: sinon.stub(),
96
+ createHookArtifactManager: sinon.stub().returns(mockHookArtifactManager),
97
+ createMailboxArtifactManager: sinon.stub(),
98
+ createValidatorAnnounceArtifactManager: sinon.stub(),
99
+ createFeeArtifactManager: sinon.stub(),
100
+ getMinGas: sinon.stub(),
101
+ createWarpArtifactManager: sinon.stub(),
102
+ };
103
+ mockProtocolProvider.createIsmArtifactManager = sinon
104
+ .stub()
105
+ .returns(mockIsmArtifactManager);
106
+ signer = await MockSigner.connectWithSigner();
107
+ getSignerAddressStub = sinon.stub().returns(mockSignerAddress);
108
+ sinon.stub(signer, 'getSignerAddress').callsFake(getSignerAddressStub);
109
+ sendAndConfirmTxStub = sinon
110
+ .stub()
111
+ .resolves(mockReceipt);
112
+ sinon
113
+ .stub(signer, 'sendAndConfirmTransaction')
114
+ .callsFake(sendAndConfirmTxStub);
115
+ const mailboxCreateStub = sinon
116
+ .stub()
117
+ .resolves([
118
+ {
119
+ artifactState: ArtifactState.DEPLOYED,
120
+ config: {
121
+ owner: mockSignerAddress,
122
+ defaultIsm: {
123
+ artifactState: ArtifactState.DEPLOYED,
124
+ config: mockIsm.config,
125
+ deployed: mockIsm.deployed,
126
+ },
127
+ defaultHook: {
128
+ artifactState: ArtifactState.UNDERIVED,
129
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
130
+ },
131
+ requiredHook: {
132
+ artifactState: ArtifactState.UNDERIVED,
133
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
134
+ },
135
+ },
136
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
137
+ },
138
+ [mockReceipt],
139
+ ]);
140
+ const mailboxUpdateStub = sinon
141
+ .stub()
142
+ .resolves([]);
143
+ const mockMailboxWriter = {
144
+ create: mailboxCreateStub,
145
+ update: mailboxUpdateStub,
146
+ read: sinon.stub(),
147
+ };
148
+ createMailboxWriterStub = sinon.stub().returns(mockMailboxWriter);
149
+ mailboxArtifactManager = {
150
+ readMailbox: sinon.stub(),
151
+ createReader: sinon.stub(),
152
+ createWriter: createMailboxWriterStub,
153
+ };
154
+ const vaCreateStub = sinon
155
+ .stub()
156
+ .resolves([
157
+ {
158
+ artifactState: ArtifactState.DEPLOYED,
159
+ config: { mailboxAddress: mockMailboxAddress },
160
+ deployed: { address: mockValidatorAnnounceAddress },
161
+ },
162
+ [mockReceipt],
163
+ ]);
164
+ const mockVAWriter = {
165
+ create: vaCreateStub,
166
+ update: sinon.stub(),
167
+ read: sinon.stub(),
168
+ };
169
+ createVAWriterStub = sinon.stub().returns(mockVAWriter);
170
+ validatorAnnounceArtifactManager = {
171
+ readValidatorAnnounce: sinon.stub(),
172
+ createReader: sinon.stub(),
173
+ createWriter: createVAWriterStub,
174
+ };
175
+ chainMetadata = {
176
+ name: chainName,
177
+ domainId: mockDomainId,
178
+ protocol: TestProtocol,
179
+ chainId: mockDomainId,
180
+ };
181
+ chainLookup = {
182
+ getChainMetadata: sinon
183
+ .stub()
184
+ .returns(chainMetadata),
185
+ getChainName: sinon.stub().returns(chainName),
186
+ getDomainId: sinon
187
+ .stub()
188
+ .returns(mockDomainId),
189
+ getKnownChainNames: sinon.stub().returns([chainName]),
190
+ getKnownDomainIds: sinon
191
+ .stub()
192
+ .returns(new Set([mockDomainId])),
193
+ };
194
+ coreWriter = new CoreWriter(mailboxArtifactManager, validatorAnnounceArtifactManager, chainMetadata, chainLookup, signer);
195
+ });
196
+ afterEach(() => {
197
+ sinon.restore();
198
+ });
199
+ describe('create', () => {
200
+ it('should deploy mailbox with NEW ISM and NEW hooks', async () => {
201
+ // ARRANGE
202
+ const artifact = {
203
+ artifactState: ArtifactState.NEW,
204
+ config: {
205
+ owner: mockOwner,
206
+ defaultIsm: {
207
+ artifactState: ArtifactState.NEW,
208
+ config: mockIsm.config,
209
+ },
210
+ defaultHook: {
211
+ artifactState: ArtifactState.NEW,
212
+ config: mockDefaultHook.config,
213
+ },
214
+ requiredHook: {
215
+ artifactState: ArtifactState.NEW,
216
+ config: mockRequiredHook.config,
217
+ },
218
+ },
219
+ };
220
+ const ismCreateStub = sinon
221
+ .stub()
222
+ .callsFake(async () => [mockIsm, [mockReceipt]]);
223
+ const mockIsmWriter = {
224
+ create: ismCreateStub,
225
+ update: sinon.stub(),
226
+ read: sinon.stub(),
227
+ };
228
+ Object.defineProperty(coreWriter, 'ismWriter', {
229
+ value: mockIsmWriter,
230
+ writable: true,
231
+ });
232
+ const hookCreateStub = sinon
233
+ .stub()
234
+ .onFirstCall()
235
+ .callsFake(async () => [mockDefaultHook, [mockReceipt]])
236
+ .onSecondCall()
237
+ .callsFake(async () => [mockRequiredHook, [mockReceipt]]);
238
+ const mockHookWriter = {
239
+ create: hookCreateStub,
240
+ update: sinon.stub(),
241
+ read: sinon.stub(),
242
+ };
243
+ mockHookArtifactManager.createWriter = sinon
244
+ .stub()
245
+ .returns(mockHookWriter);
246
+ // ACT
247
+ const [result, receipts] = await coreWriter.create(artifact);
248
+ // ASSERT
249
+ expect(result.mailbox.deployed.address).to.equal(mockMailboxAddress);
250
+ expect(result.mailbox.config.owner).to.equal(mockOwner);
251
+ expect(result.validatorAnnounce).to.not.be.null;
252
+ expect(result.validatorAnnounce?.deployed.address).to.equal(mockValidatorAnnounceAddress);
253
+ expect(receipts.length).to.be.greaterThan(0);
254
+ sinon.assert.calledOnce(ismCreateStub);
255
+ sinon.assert.calledTwice(hookCreateStub);
256
+ sinon.assert.calledWith(createMailboxWriterStub, 'mailbox', signer);
257
+ sinon.assert.calledWith(createVAWriterStub, 'validatorAnnounce', signer);
258
+ });
259
+ it('should not forward config contractVersion into the create-path mailbox update', async () => {
260
+ // ARRANGE
261
+ const artifact = {
262
+ artifactState: ArtifactState.NEW,
263
+ config: {
264
+ owner: mockOwner,
265
+ defaultIsm: mockIsm,
266
+ defaultHook: mockDefaultHook,
267
+ requiredHook: mockRequiredHook,
268
+ contractVersion: '1.0.0',
269
+ },
270
+ };
271
+ // ACT
272
+ const [result] = await coreWriter.create(artifact);
273
+ // ASSERT
274
+ expect(result.mailbox.config.contractVersion).to.be.undefined;
275
+ });
276
+ it('should use existing ISM when DEPLOYED', async () => {
277
+ // ARRANGE
278
+ const artifact = {
279
+ artifactState: ArtifactState.NEW,
280
+ config: {
281
+ owner: mockOwner,
282
+ defaultIsm: mockIsm,
283
+ defaultHook: mockDefaultHook,
284
+ requiredHook: mockRequiredHook,
285
+ },
286
+ };
287
+ const ismCreateStub = sinon.stub();
288
+ const mockIsmWriter = {
289
+ create: ismCreateStub,
290
+ update: sinon.stub(),
291
+ read: sinon.stub(),
292
+ };
293
+ Object.defineProperty(coreWriter, 'ismWriter', {
294
+ value: mockIsmWriter,
295
+ writable: true,
296
+ });
297
+ // ACT
298
+ await coreWriter.create(artifact);
299
+ // ASSERT
300
+ sinon.assert.notCalled(ismCreateStub);
301
+ });
302
+ it('should handle protocol without validator announce support', async () => {
303
+ // ARRANGE
304
+ const coreWriterNoVA = new CoreWriter(mailboxArtifactManager, null, chainMetadata, chainLookup, signer);
305
+ const artifact = {
306
+ artifactState: ArtifactState.NEW,
307
+ config: {
308
+ owner: mockOwner,
309
+ defaultIsm: mockIsm,
310
+ defaultHook: mockDefaultHook,
311
+ requiredHook: mockRequiredHook,
312
+ },
313
+ };
314
+ const mockIsmWriter = {
315
+ create: sinon.stub(),
316
+ update: sinon.stub(),
317
+ read: sinon.stub(),
318
+ };
319
+ Object.defineProperty(coreWriterNoVA, 'ismWriter', {
320
+ value: mockIsmWriter,
321
+ writable: true,
322
+ });
323
+ // ACT
324
+ const [result] = await coreWriterNoVA.create(artifact);
325
+ // ASSERT
326
+ expect(result.validatorAnnounce).to.be.null;
327
+ });
328
+ it('should create mailbox with signer as initial owner', async () => {
329
+ // ARRANGE
330
+ const artifact = {
331
+ artifactState: ArtifactState.NEW,
332
+ config: {
333
+ owner: mockOwner,
334
+ defaultIsm: mockIsm,
335
+ defaultHook: mockDefaultHook,
336
+ requiredHook: mockRequiredHook,
337
+ },
338
+ };
339
+ const mockIsmWriter = {
340
+ create: sinon.stub(),
341
+ update: sinon.stub(),
342
+ read: sinon.stub(),
343
+ };
344
+ Object.defineProperty(coreWriter, 'ismWriter', {
345
+ value: mockIsmWriter,
346
+ writable: true,
347
+ });
348
+ // ACT
349
+ await coreWriter.create(artifact);
350
+ // ASSERT
351
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
352
+ const mailboxCreateStub = mailboxWriter.create;
353
+ const initialConfig = mailboxCreateStub.firstCall.args[0].config;
354
+ expect(initialConfig.owner).to.equal(mockSignerAddress);
355
+ expect(initialConfig.owner).to.not.equal(mockOwner);
356
+ });
357
+ it('should create mailbox with zero-address hooks initially', async () => {
358
+ // ARRANGE
359
+ const artifact = {
360
+ artifactState: ArtifactState.NEW,
361
+ config: {
362
+ owner: mockOwner,
363
+ defaultIsm: mockIsm,
364
+ defaultHook: {
365
+ artifactState: ArtifactState.NEW,
366
+ config: mockDefaultHook.config,
367
+ },
368
+ requiredHook: {
369
+ artifactState: ArtifactState.NEW,
370
+ config: mockRequiredHook.config,
371
+ },
372
+ },
373
+ };
374
+ const mockIsmWriter = {
375
+ create: sinon.stub(),
376
+ update: sinon.stub(),
377
+ read: sinon.stub(),
378
+ };
379
+ Object.defineProperty(coreWriter, 'ismWriter', {
380
+ value: mockIsmWriter,
381
+ writable: true,
382
+ });
383
+ const mockHookWriter = {
384
+ create: sinon
385
+ .stub()
386
+ .onFirstCall()
387
+ .callsFake(async () => [mockDefaultHook, [mockReceipt]])
388
+ .onSecondCall()
389
+ .callsFake(async () => [mockRequiredHook, [mockReceipt]]),
390
+ update: sinon.stub(),
391
+ read: sinon.stub(),
392
+ };
393
+ mockHookArtifactManager.createWriter = sinon
394
+ .stub()
395
+ .returns(mockHookWriter);
396
+ // ACT
397
+ await coreWriter.create(artifact);
398
+ // ASSERT
399
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
400
+ const mailboxCreateStub = mailboxWriter.create;
401
+ const initialConfig = mailboxCreateStub.firstCall.args[0].config;
402
+ expect(initialConfig.defaultHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
403
+ expect(initialConfig.requiredHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
404
+ expect(initialConfig.defaultHook.artifactState).to.equal(ArtifactState.UNDERIVED);
405
+ });
406
+ it('should keep zero-address hook placeholders for non-Starknet even when hooks are already on-chain', async () => {
407
+ const artifact = {
408
+ artifactState: ArtifactState.NEW,
409
+ config: {
410
+ owner: mockOwner,
411
+ defaultIsm: mockIsm,
412
+ defaultHook: mockDefaultHook,
413
+ requiredHook: mockRequiredHook,
414
+ },
415
+ };
416
+ const mockIsmWriter = {
417
+ create: sinon.stub(),
418
+ update: sinon.stub(),
419
+ read: sinon.stub(),
420
+ };
421
+ Object.defineProperty(coreWriter, 'ismWriter', {
422
+ value: mockIsmWriter,
423
+ writable: true,
424
+ });
425
+ await coreWriter.create(artifact);
426
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
427
+ const initialConfig = mailboxWriter.create.firstCall.args[0].config;
428
+ expect(initialConfig.defaultHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
429
+ expect(initialConfig.requiredHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
430
+ });
431
+ it('should defer Starknet noop hook bootstrap to the mailbox writer', async () => {
432
+ chainMetadata = {
433
+ ...chainMetadata,
434
+ protocol: ProtocolType.Starknet,
435
+ };
436
+ coreWriter = new CoreWriter(mailboxArtifactManager, validatorAnnounceArtifactManager, chainMetadata, chainLookup, signer);
437
+ const artifact = {
438
+ artifactState: ArtifactState.NEW,
439
+ config: {
440
+ owner: mockOwner,
441
+ defaultIsm: mockIsm,
442
+ defaultHook: {
443
+ artifactState: ArtifactState.NEW,
444
+ config: mockDefaultHook.config,
445
+ },
446
+ requiredHook: {
447
+ artifactState: ArtifactState.NEW,
448
+ config: mockRequiredHook.config,
449
+ },
450
+ },
451
+ };
452
+ const mockIsmWriter = {
453
+ create: sinon.stub(),
454
+ update: sinon.stub(),
455
+ read: sinon.stub(),
456
+ };
457
+ Object.defineProperty(coreWriter, 'ismWriter', {
458
+ value: mockIsmWriter,
459
+ writable: true,
460
+ });
461
+ const mockHookWriter = {
462
+ create: sinon
463
+ .stub()
464
+ .onFirstCall()
465
+ .callsFake(async () => [mockDefaultHook, [mockReceipt]])
466
+ .onSecondCall()
467
+ .callsFake(async () => [mockRequiredHook, [mockReceipt]]),
468
+ update: sinon.stub(),
469
+ read: sinon.stub(),
470
+ };
471
+ mockHookArtifactManager.createWriter = sinon
472
+ .stub()
473
+ .returns(mockHookWriter);
474
+ await coreWriter.create(artifact);
475
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
476
+ const initialConfig = mailboxWriter.create.firstCall.args[0].config;
477
+ expect(initialConfig.defaultHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
478
+ expect(initialConfig.requiredHook.deployed.address).to.equal(ZERO_ADDRESS_HEX_32);
479
+ sinon.assert.notCalled(sendAndConfirmTxStub);
480
+ });
481
+ it('should pass through existing hook addresses during Starknet mailbox bootstrap', async () => {
482
+ chainMetadata = {
483
+ ...chainMetadata,
484
+ protocol: ProtocolType.Starknet,
485
+ };
486
+ coreWriter = new CoreWriter(mailboxArtifactManager, validatorAnnounceArtifactManager, chainMetadata, chainLookup, signer);
487
+ const artifact = {
488
+ artifactState: ArtifactState.NEW,
489
+ config: {
490
+ owner: mockOwner,
491
+ defaultIsm: mockIsm,
492
+ defaultHook: mockDefaultHook,
493
+ requiredHook: mockRequiredHook,
494
+ },
495
+ };
496
+ const mockIsmWriter = {
497
+ create: sinon.stub(),
498
+ update: sinon.stub(),
499
+ read: sinon.stub(),
500
+ };
501
+ Object.defineProperty(coreWriter, 'ismWriter', {
502
+ value: mockIsmWriter,
503
+ writable: true,
504
+ });
505
+ await coreWriter.create(artifact);
506
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
507
+ const initialConfig = mailboxWriter.create.firstCall.args[0].config;
508
+ expect(initialConfig.defaultHook.deployed.address).to.equal(mockDefaultHookAddress);
509
+ expect(initialConfig.requiredHook.deployed.address).to.equal(mockRequiredHookAddress);
510
+ });
511
+ it('should update mailbox with hooks and owner after deployment', async () => {
512
+ // ARRANGE
513
+ const artifact = {
514
+ artifactState: ArtifactState.NEW,
515
+ config: {
516
+ owner: mockOwner,
517
+ defaultIsm: mockIsm,
518
+ defaultHook: mockDefaultHook,
519
+ requiredHook: mockRequiredHook,
520
+ },
521
+ };
522
+ const mockIsmWriter = {
523
+ create: sinon.stub(),
524
+ update: sinon.stub(),
525
+ read: sinon.stub(),
526
+ };
527
+ Object.defineProperty(coreWriter, 'ismWriter', {
528
+ value: mockIsmWriter,
529
+ writable: true,
530
+ });
531
+ // ACT
532
+ await coreWriter.create(artifact);
533
+ // ASSERT
534
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
535
+ const mailboxUpdateStub = mailboxWriter.update;
536
+ sinon.assert.calledOnce(mailboxUpdateStub);
537
+ const updatedArtifact = mailboxUpdateStub.firstCall.args[0];
538
+ expect(updatedArtifact.config.owner).to.equal(mockOwner);
539
+ expect(updatedArtifact.config.defaultHook.deployed.address).to.equal(mockDefaultHookAddress);
540
+ expect(updatedArtifact.config.requiredHook.deployed.address).to.equal(mockRequiredHookAddress);
541
+ });
542
+ it('should execute update transactions via signer', async () => {
543
+ // ARRANGE
544
+ const mockUpdateTx = {
545
+ to: mockMailboxAddress,
546
+ data: '0xUPDATE',
547
+ annotation: 'Update hooks',
548
+ };
549
+ const mailboxCreateStub = sinon.stub().resolves([
550
+ {
551
+ artifactState: ArtifactState.DEPLOYED,
552
+ config: {
553
+ owner: mockSignerAddress,
554
+ defaultIsm: {
555
+ artifactState: ArtifactState.DEPLOYED,
556
+ config: mockIsm.config,
557
+ deployed: mockIsm.deployed,
558
+ },
559
+ defaultHook: {
560
+ artifactState: ArtifactState.UNDERIVED,
561
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
562
+ },
563
+ requiredHook: {
564
+ artifactState: ArtifactState.UNDERIVED,
565
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
566
+ },
567
+ },
568
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
569
+ },
570
+ [mockReceipt],
571
+ ]);
572
+ const mailboxUpdateStub = sinon.stub().resolves([mockUpdateTx]);
573
+ const mockMailboxWriter = {
574
+ create: mailboxCreateStub,
575
+ update: mailboxUpdateStub,
576
+ read: sinon.stub(),
577
+ };
578
+ mailboxArtifactManager.createWriter = sinon
579
+ .stub()
580
+ .returns(mockMailboxWriter);
581
+ const artifact = {
582
+ artifactState: ArtifactState.NEW,
583
+ config: {
584
+ owner: mockOwner,
585
+ defaultIsm: mockIsm,
586
+ defaultHook: mockDefaultHook,
587
+ requiredHook: mockRequiredHook,
588
+ },
589
+ };
590
+ const mockIsmWriter = {
591
+ create: sinon.stub(),
592
+ update: sinon.stub(),
593
+ read: sinon.stub(),
594
+ };
595
+ Object.defineProperty(coreWriter, 'ismWriter', {
596
+ value: mockIsmWriter,
597
+ writable: true,
598
+ });
599
+ // ACT
600
+ await coreWriter.create(artifact);
601
+ // ASSERT
602
+ sinon.assert.calledWith(sendAndConfirmTxStub, mockUpdateTx);
603
+ });
604
+ it('should collect receipts from all steps', async () => {
605
+ // ARRANGE
606
+ const artifact = {
607
+ artifactState: ArtifactState.NEW,
608
+ config: {
609
+ owner: mockOwner,
610
+ defaultIsm: {
611
+ artifactState: ArtifactState.NEW,
612
+ config: mockIsm.config,
613
+ },
614
+ defaultHook: mockDefaultHook,
615
+ requiredHook: mockRequiredHook,
616
+ },
617
+ };
618
+ const mockIsmWriter = {
619
+ create: sinon
620
+ .stub()
621
+ .callsFake(async () => [mockIsm, [mockReceipt, mockReceipt]]),
622
+ update: sinon.stub(),
623
+ read: sinon.stub(),
624
+ };
625
+ Object.defineProperty(coreWriter, 'ismWriter', {
626
+ value: mockIsmWriter,
627
+ writable: true,
628
+ });
629
+ // ACT
630
+ const [, receipts] = await coreWriter.create(artifact);
631
+ // ASSERT
632
+ expect(receipts.length).to.be.greaterThan(0);
633
+ expect(receipts.length).to.be.at.least(4);
634
+ });
635
+ it('should propagate ISM deployment errors', async () => {
636
+ // ARRANGE
637
+ const error = new Error('ISM deployment failed');
638
+ const artifact = {
639
+ artifactState: ArtifactState.NEW,
640
+ config: {
641
+ owner: mockOwner,
642
+ defaultIsm: {
643
+ artifactState: ArtifactState.NEW,
644
+ config: mockIsm.config,
645
+ },
646
+ defaultHook: mockDefaultHook,
647
+ requiredHook: mockRequiredHook,
648
+ },
649
+ };
650
+ const mockIsmWriter = {
651
+ create: sinon.stub().rejects(error),
652
+ update: sinon.stub(),
653
+ read: sinon.stub(),
654
+ };
655
+ Object.defineProperty(coreWriter, 'ismWriter', {
656
+ value: mockIsmWriter,
657
+ writable: true,
658
+ });
659
+ // ACT & ASSERT
660
+ await expect(coreWriter.create(artifact)).to.be.rejectedWith('ISM deployment failed');
661
+ });
662
+ it('should propagate mailbox creation errors', async () => {
663
+ // ARRANGE
664
+ const error = new Error('Mailbox creation failed');
665
+ const mockMailboxWriter = {
666
+ create: sinon.stub().rejects(error),
667
+ update: sinon.stub(),
668
+ read: sinon.stub(),
669
+ };
670
+ mailboxArtifactManager.createWriter = sinon
671
+ .stub()
672
+ .returns(mockMailboxWriter);
673
+ const artifact = {
674
+ artifactState: ArtifactState.NEW,
675
+ config: {
676
+ owner: mockOwner,
677
+ defaultIsm: mockIsm,
678
+ defaultHook: mockDefaultHook,
679
+ requiredHook: mockRequiredHook,
680
+ },
681
+ };
682
+ const mockIsmWriter = {
683
+ create: sinon.stub(),
684
+ update: sinon.stub(),
685
+ read: sinon.stub(),
686
+ };
687
+ Object.defineProperty(coreWriter, 'ismWriter', {
688
+ value: mockIsmWriter,
689
+ writable: true,
690
+ });
691
+ // ACT & ASSERT
692
+ await expect(coreWriter.create(artifact)).to.be.rejectedWith('Mailbox creation failed');
693
+ });
694
+ });
695
+ describe('rateLimited default ISM rejection', () => {
696
+ // A rateLimited node meters an amount read from a fixed offset of the
697
+ // message body, which only holds for warp-route traffic — a mailbox
698
+ // default ISM sees every message.
699
+ const compositeIsmWithRateLimited = {
700
+ type: IsmType.COMPOSITE,
701
+ owner: mockOwner,
702
+ root: {
703
+ type: CompositeIsmNodeType.AGGREGATION,
704
+ threshold: 1,
705
+ subIsms: [
706
+ { type: CompositeIsmNodeType.TEST, accept: true },
707
+ {
708
+ type: CompositeIsmNodeType.RATE_LIMITED,
709
+ maxCapacity: '86400',
710
+ mailbox: mockMailboxAddress,
711
+ },
712
+ ],
713
+ },
714
+ };
715
+ const routingIsmWithNestedRateLimited = {
716
+ type: IsmType.ROUTING,
717
+ owner: mockOwner,
718
+ domains: {
719
+ [mockDomainId]: {
720
+ artifactState: ArtifactState.NEW,
721
+ config: compositeIsmWithRateLimited,
722
+ },
723
+ },
724
+ };
725
+ it('rejects it on create before anything is deployed', async () => {
726
+ // ARRANGE
727
+ const artifact = {
728
+ artifactState: ArtifactState.NEW,
729
+ config: {
730
+ owner: mockOwner,
731
+ defaultIsm: {
732
+ artifactState: ArtifactState.NEW,
733
+ config: compositeIsmWithRateLimited,
734
+ },
735
+ defaultHook: mockDefaultHook,
736
+ requiredHook: mockRequiredHook,
737
+ },
738
+ };
739
+ const ismCreateStub = sinon.stub();
740
+ Object.defineProperty(coreWriter, 'ismWriter', {
741
+ value: {
742
+ create: ismCreateStub,
743
+ update: sinon.stub(),
744
+ read: sinon.stub(),
745
+ },
746
+ writable: true,
747
+ });
748
+ // ACT & ASSERT
749
+ await expect(coreWriter.create(artifact)).to.be.rejectedWith(/rateLimited/);
750
+ sinon.assert.notCalled(ismCreateStub);
751
+ sinon.assert.notCalled(createMailboxWriterStub);
752
+ });
753
+ it('rejects it when nested inside a routing artifact', async () => {
754
+ const artifact = {
755
+ artifactState: ArtifactState.NEW,
756
+ config: {
757
+ owner: mockOwner,
758
+ defaultIsm: {
759
+ artifactState: ArtifactState.NEW,
760
+ config: routingIsmWithNestedRateLimited,
761
+ },
762
+ defaultHook: mockDefaultHook,
763
+ requiredHook: mockRequiredHook,
764
+ },
765
+ };
766
+ const ismCreateStub = sinon.stub();
767
+ Object.defineProperty(coreWriter, 'ismWriter', {
768
+ value: {
769
+ create: ismCreateStub,
770
+ update: sinon.stub(),
771
+ read: sinon.stub(),
772
+ },
773
+ writable: true,
774
+ });
775
+ await expect(coreWriter.create(artifact)).to.be.rejectedWith(/rateLimited/);
776
+ sinon.assert.notCalled(ismCreateStub);
777
+ sinon.assert.notCalled(createMailboxWriterStub);
778
+ });
779
+ it('rejects it on update before any transaction is built', async () => {
780
+ // ARRANGE
781
+ const readStub = sinon.stub(coreWriter, 'read');
782
+ const expectedArtifact = {
783
+ artifactState: ArtifactState.DEPLOYED,
784
+ config: {
785
+ owner: mockOwner,
786
+ defaultIsm: {
787
+ artifactState: ArtifactState.NEW,
788
+ config: compositeIsmWithRateLimited,
789
+ },
790
+ defaultHook: mockDefaultHook,
791
+ requiredHook: mockRequiredHook,
792
+ },
793
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
794
+ };
795
+ const ismCreateStub = sinon.stub();
796
+ const ismUpdateStub = sinon.stub();
797
+ Object.defineProperty(coreWriter, 'ismWriter', {
798
+ value: {
799
+ create: ismCreateStub,
800
+ update: ismUpdateStub,
801
+ read: sinon.stub(),
802
+ },
803
+ writable: true,
804
+ });
805
+ // ACT & ASSERT
806
+ await expect(coreWriter.update(expectedArtifact)).to.be.rejectedWith(/rateLimited/);
807
+ sinon.assert.notCalled(readStub);
808
+ sinon.assert.notCalled(ismCreateStub);
809
+ sinon.assert.notCalled(ismUpdateStub);
810
+ });
811
+ it('accepts a composite default ISM without a rateLimited node', async () => {
812
+ // ARRANGE
813
+ const artifact = {
814
+ artifactState: ArtifactState.NEW,
815
+ config: {
816
+ owner: mockOwner,
817
+ defaultIsm: {
818
+ artifactState: ArtifactState.NEW,
819
+ config: {
820
+ type: IsmType.COMPOSITE,
821
+ owner: mockOwner,
822
+ root: { type: CompositeIsmNodeType.TEST, accept: true },
823
+ },
824
+ },
825
+ defaultHook: mockDefaultHook,
826
+ requiredHook: mockRequiredHook,
827
+ },
828
+ };
829
+ const ismCreateStub = sinon.stub().resolves([mockIsm, [mockReceipt]]);
830
+ Object.defineProperty(coreWriter, 'ismWriter', {
831
+ value: {
832
+ create: ismCreateStub,
833
+ update: sinon.stub(),
834
+ read: sinon.stub(),
835
+ },
836
+ writable: true,
837
+ });
838
+ const mockHookWriter = {
839
+ create: sinon.stub(),
840
+ update: sinon.stub().resolves([]),
841
+ read: sinon.stub(),
842
+ };
843
+ mockHookArtifactManager.createWriter = sinon
844
+ .stub()
845
+ .returns(mockHookWriter);
846
+ // ACT
847
+ const [result] = await coreWriter.create(artifact);
848
+ // ASSERT
849
+ expect(result.mailbox.deployed.address).to.equal(mockMailboxAddress);
850
+ sinon.assert.calledOnce(ismCreateStub);
851
+ });
852
+ });
853
+ describe('update', () => {
854
+ it('should handle ISM type change by deploying new ISM', async () => {
855
+ // ARRANGE
856
+ const currentMailbox = {
857
+ artifactState: ArtifactState.DEPLOYED,
858
+ config: {
859
+ owner: mockOwner,
860
+ defaultIsm: mockIsm,
861
+ defaultHook: mockDefaultHook,
862
+ requiredHook: mockRequiredHook,
863
+ },
864
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
865
+ };
866
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
867
+ const newIsm = {
868
+ artifactState: ArtifactState.DEPLOYED,
869
+ config: {
870
+ type: IsmType.MESSAGE_ID_MULTISIG,
871
+ validators: ['0xVALIDATOR2'],
872
+ threshold: 1,
873
+ },
874
+ deployed: { address: mockNewIsmAddress },
875
+ };
876
+ const expectedArtifact = {
877
+ artifactState: ArtifactState.DEPLOYED,
878
+ config: {
879
+ owner: mockOwner,
880
+ defaultIsm: {
881
+ artifactState: ArtifactState.NEW,
882
+ config: newIsm.config,
883
+ },
884
+ defaultHook: mockDefaultHook,
885
+ requiredHook: mockRequiredHook,
886
+ },
887
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
888
+ };
889
+ const ismCreateStub = sinon
890
+ .stub()
891
+ .callsFake(async () => [newIsm, [mockReceipt]]);
892
+ const mockIsmWriter = {
893
+ create: ismCreateStub,
894
+ update: sinon.stub(),
895
+ read: sinon.stub(),
896
+ };
897
+ mockIsmArtifactManager.createWriter = sinon.stub().returns(mockIsmWriter);
898
+ const mockHookWriter = {
899
+ create: sinon.stub(),
900
+ update: sinon.stub().resolves([]),
901
+ read: sinon.stub(),
902
+ };
903
+ mockHookArtifactManager.createWriter = sinon
904
+ .stub()
905
+ .returns(mockHookWriter);
906
+ // ACT
907
+ await coreWriter.update(expectedArtifact);
908
+ // ASSERT
909
+ sinon.assert.calledOnce(ismCreateStub);
910
+ const createArg = ismCreateStub.firstCall.args[0];
911
+ expect(createArg.artifactState).to.equal(ArtifactState.NEW);
912
+ expect(createArg.config.type).to.equal(IsmType.MESSAGE_ID_MULTISIG);
913
+ });
914
+ it('should handle UNDERIVED ISM by using address as-is', async () => {
915
+ // ARRANGE
916
+ const currentMailbox = {
917
+ artifactState: ArtifactState.DEPLOYED,
918
+ config: {
919
+ owner: mockOwner,
920
+ defaultIsm: mockIsm,
921
+ defaultHook: mockDefaultHook,
922
+ requiredHook: mockRequiredHook,
923
+ },
924
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
925
+ };
926
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
927
+ const expectedArtifact = {
928
+ artifactState: ArtifactState.DEPLOYED,
929
+ config: {
930
+ owner: mockOwner,
931
+ defaultIsm: {
932
+ artifactState: ArtifactState.UNDERIVED,
933
+ deployed: { address: mockIsmAddress },
934
+ },
935
+ defaultHook: mockDefaultHook,
936
+ requiredHook: mockRequiredHook,
937
+ },
938
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
939
+ };
940
+ const ismCreateStub = sinon.stub();
941
+ const ismUpdateStub = sinon.stub();
942
+ const mockIsmWriter = {
943
+ create: ismCreateStub,
944
+ update: ismUpdateStub,
945
+ read: sinon.stub(),
946
+ };
947
+ Object.defineProperty(coreWriter, 'ismWriter', {
948
+ value: mockIsmWriter,
949
+ writable: true,
950
+ });
951
+ const mockHookWriter = {
952
+ create: sinon.stub(),
953
+ update: sinon.stub().resolves([]),
954
+ read: sinon.stub(),
955
+ };
956
+ mockHookArtifactManager.createWriter = sinon
957
+ .stub()
958
+ .returns(mockHookWriter);
959
+ // ACT
960
+ await coreWriter.update(expectedArtifact);
961
+ // ASSERT
962
+ sinon.assert.notCalled(ismCreateStub);
963
+ sinon.assert.notCalled(ismUpdateStub);
964
+ });
965
+ it('should return empty array when no updates needed', async () => {
966
+ // ARRANGE
967
+ const currentMailbox = {
968
+ artifactState: ArtifactState.DEPLOYED,
969
+ config: {
970
+ owner: mockOwner,
971
+ defaultIsm: mockIsm,
972
+ defaultHook: mockDefaultHook,
973
+ requiredHook: mockRequiredHook,
974
+ },
975
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
976
+ };
977
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
978
+ const expectedArtifact = {
979
+ artifactState: ArtifactState.DEPLOYED,
980
+ config: currentMailbox.config,
981
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
982
+ };
983
+ const mockIsmWriter = {
984
+ create: sinon.stub(),
985
+ update: sinon.stub().resolves([]),
986
+ read: sinon.stub(),
987
+ };
988
+ Object.defineProperty(coreWriter, 'ismWriter', {
989
+ value: mockIsmWriter,
990
+ writable: true,
991
+ });
992
+ const mockHookWriter = {
993
+ create: sinon.stub(),
994
+ update: sinon.stub().resolves([]),
995
+ read: sinon.stub(),
996
+ };
997
+ mockHookArtifactManager.createWriter = sinon
998
+ .stub()
999
+ .returns(mockHookWriter);
1000
+ // ACT
1001
+ const result = await coreWriter.update(expectedArtifact);
1002
+ // ASSERT
1003
+ expect(result).to.be.an('array').with.lengthOf(0);
1004
+ });
1005
+ it('should deploy NEW ISM when current is zero-address UNDERIVED', async () => {
1006
+ // ARRANGE
1007
+ const currentMailbox = {
1008
+ artifactState: ArtifactState.DEPLOYED,
1009
+ config: {
1010
+ owner: mockOwner,
1011
+ defaultIsm: {
1012
+ artifactState: ArtifactState.UNDERIVED,
1013
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
1014
+ },
1015
+ defaultHook: mockDefaultHook,
1016
+ requiredHook: mockRequiredHook,
1017
+ },
1018
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1019
+ };
1020
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1021
+ const newIsm = {
1022
+ artifactState: ArtifactState.DEPLOYED,
1023
+ config: {
1024
+ type: IsmType.MERKLE_ROOT_MULTISIG,
1025
+ validators: ['0xVALIDATOR1'],
1026
+ threshold: 1,
1027
+ },
1028
+ deployed: { address: mockNewIsmAddress },
1029
+ };
1030
+ const expectedArtifact = {
1031
+ artifactState: ArtifactState.DEPLOYED,
1032
+ config: {
1033
+ owner: mockOwner,
1034
+ defaultIsm: {
1035
+ artifactState: ArtifactState.NEW,
1036
+ config: newIsm.config,
1037
+ },
1038
+ defaultHook: mockDefaultHook,
1039
+ requiredHook: mockRequiredHook,
1040
+ },
1041
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1042
+ };
1043
+ const ismCreateStub = sinon
1044
+ .stub()
1045
+ .callsFake(async () => [newIsm, [mockReceipt]]);
1046
+ const mockIsmWriter = {
1047
+ create: ismCreateStub,
1048
+ update: sinon.stub(),
1049
+ read: sinon.stub(),
1050
+ };
1051
+ Object.defineProperty(coreWriter, 'ismWriter', {
1052
+ value: mockIsmWriter,
1053
+ writable: true,
1054
+ });
1055
+ const mockHookWriter = {
1056
+ create: sinon.stub(),
1057
+ update: sinon.stub().resolves([]),
1058
+ read: sinon.stub(),
1059
+ };
1060
+ mockHookArtifactManager.createWriter = sinon
1061
+ .stub()
1062
+ .returns(mockHookWriter);
1063
+ // ACT
1064
+ const result = await coreWriter.update(expectedArtifact);
1065
+ // ASSERT
1066
+ sinon.assert.calledOnce(ismCreateStub);
1067
+ expect(result).to.be.an('array');
1068
+ });
1069
+ it('should assert artifacts are expanded before updating', async () => {
1070
+ // ARRANGE
1071
+ const currentMailbox = {
1072
+ artifactState: ArtifactState.DEPLOYED,
1073
+ config: {
1074
+ owner: mockOwner,
1075
+ defaultIsm: {
1076
+ artifactState: ArtifactState.UNDERIVED,
1077
+ deployed: { address: mockIsmAddress },
1078
+ },
1079
+ defaultHook: mockDefaultHook,
1080
+ requiredHook: mockRequiredHook,
1081
+ },
1082
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1083
+ };
1084
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1085
+ const expectedArtifact = {
1086
+ artifactState: ArtifactState.DEPLOYED,
1087
+ config: {
1088
+ owner: mockOwner,
1089
+ defaultIsm: mockIsm,
1090
+ defaultHook: mockDefaultHook,
1091
+ requiredHook: mockRequiredHook,
1092
+ },
1093
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1094
+ };
1095
+ // ACT & ASSERT
1096
+ await expect(coreWriter.update(expectedArtifact)).to.be.rejectedWith('Expected defaultIsm to be DEPLOYED or UNDERIVED with zero address');
1097
+ });
1098
+ it('should deploy NEW hook during update', async () => {
1099
+ // ARRANGE
1100
+ const currentMailbox = {
1101
+ artifactState: ArtifactState.DEPLOYED,
1102
+ config: {
1103
+ owner: mockOwner,
1104
+ defaultIsm: mockIsm,
1105
+ defaultHook: {
1106
+ artifactState: ArtifactState.UNDERIVED,
1107
+ deployed: { address: ZERO_ADDRESS_HEX_32 },
1108
+ },
1109
+ requiredHook: mockRequiredHook,
1110
+ },
1111
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1112
+ };
1113
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1114
+ const newDefaultHook = {
1115
+ artifactState: ArtifactState.DEPLOYED,
1116
+ config: { type: 'merkleTreeHook' },
1117
+ deployed: { address: mockDefaultHookAddress },
1118
+ };
1119
+ const expectedArtifact = {
1120
+ artifactState: ArtifactState.DEPLOYED,
1121
+ config: {
1122
+ owner: mockOwner,
1123
+ defaultIsm: mockIsm,
1124
+ defaultHook: {
1125
+ artifactState: ArtifactState.NEW,
1126
+ config: newDefaultHook.config,
1127
+ },
1128
+ requiredHook: mockRequiredHook,
1129
+ },
1130
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1131
+ };
1132
+ const mockIsmWriter = {
1133
+ create: sinon.stub(),
1134
+ update: sinon.stub().resolves([]),
1135
+ read: sinon.stub(),
1136
+ };
1137
+ Object.defineProperty(coreWriter, 'ismWriter', {
1138
+ value: mockIsmWriter,
1139
+ writable: true,
1140
+ });
1141
+ const hookCreateStub = sinon
1142
+ .stub()
1143
+ .callsFake(async () => [newDefaultHook, [mockReceipt]]);
1144
+ const mockHookWriter = {
1145
+ create: hookCreateStub,
1146
+ update: sinon.stub().resolves([]),
1147
+ read: sinon.stub(),
1148
+ };
1149
+ mockHookArtifactManager.createWriter = sinon
1150
+ .stub()
1151
+ .returns(mockHookWriter);
1152
+ // ACT
1153
+ await coreWriter.update(expectedArtifact);
1154
+ // ASSERT
1155
+ sinon.assert.calledOnce(hookCreateStub);
1156
+ });
1157
+ it('should handle owner change', async () => {
1158
+ // ARRANGE
1159
+ const newOwner = '0xNEWOWNER';
1160
+ const currentMailbox = {
1161
+ artifactState: ArtifactState.DEPLOYED,
1162
+ config: {
1163
+ owner: mockOwner,
1164
+ defaultIsm: mockIsm,
1165
+ defaultHook: mockDefaultHook,
1166
+ requiredHook: mockRequiredHook,
1167
+ },
1168
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1169
+ };
1170
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1171
+ const expectedArtifact = {
1172
+ artifactState: ArtifactState.DEPLOYED,
1173
+ config: {
1174
+ owner: newOwner,
1175
+ defaultIsm: mockIsm,
1176
+ defaultHook: mockDefaultHook,
1177
+ requiredHook: mockRequiredHook,
1178
+ },
1179
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1180
+ };
1181
+ const mockIsmWriter = {
1182
+ create: sinon.stub(),
1183
+ update: sinon.stub().resolves([]),
1184
+ read: sinon.stub(),
1185
+ };
1186
+ Object.defineProperty(coreWriter, 'ismWriter', {
1187
+ value: mockIsmWriter,
1188
+ writable: true,
1189
+ });
1190
+ const mockHookWriter = {
1191
+ create: sinon.stub(),
1192
+ update: sinon.stub().resolves([]),
1193
+ read: sinon.stub(),
1194
+ };
1195
+ mockHookArtifactManager.createWriter = sinon
1196
+ .stub()
1197
+ .returns(mockHookWriter);
1198
+ // ACT
1199
+ await coreWriter.update(expectedArtifact);
1200
+ // ASSERT
1201
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
1202
+ const updatedArtifact = mailboxWriter.update.firstCall.args[0];
1203
+ expect(updatedArtifact.config.owner).to.equal(newOwner);
1204
+ });
1205
+ it('should wire correct addresses into mailbox update', async () => {
1206
+ // ARRANGE
1207
+ const newIsm = {
1208
+ artifactState: ArtifactState.DEPLOYED,
1209
+ config: {
1210
+ type: IsmType.MESSAGE_ID_MULTISIG,
1211
+ validators: ['0xVALIDATOR2'],
1212
+ threshold: 1,
1213
+ },
1214
+ deployed: { address: mockNewIsmAddress },
1215
+ };
1216
+ const currentMailbox = {
1217
+ artifactState: ArtifactState.DEPLOYED,
1218
+ config: {
1219
+ owner: mockOwner,
1220
+ defaultIsm: mockIsm,
1221
+ defaultHook: mockDefaultHook,
1222
+ requiredHook: mockRequiredHook,
1223
+ },
1224
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1225
+ };
1226
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1227
+ const expectedArtifact = {
1228
+ artifactState: ArtifactState.DEPLOYED,
1229
+ config: {
1230
+ owner: mockOwner,
1231
+ defaultIsm: {
1232
+ artifactState: ArtifactState.NEW,
1233
+ config: newIsm.config,
1234
+ },
1235
+ defaultHook: mockDefaultHook,
1236
+ requiredHook: mockRequiredHook,
1237
+ },
1238
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1239
+ };
1240
+ const ismCreateStub = sinon
1241
+ .stub()
1242
+ .callsFake(async () => [newIsm, [mockReceipt]]);
1243
+ const mockIsmWriter = {
1244
+ create: ismCreateStub,
1245
+ update: sinon.stub(),
1246
+ read: sinon.stub(),
1247
+ };
1248
+ mockIsmArtifactManager.createWriter = sinon.stub().returns(mockIsmWriter);
1249
+ const mockHookWriter = {
1250
+ create: sinon.stub(),
1251
+ update: sinon.stub().resolves([]),
1252
+ read: sinon.stub(),
1253
+ };
1254
+ mockHookArtifactManager.createWriter = sinon
1255
+ .stub()
1256
+ .returns(mockHookWriter);
1257
+ // ACT
1258
+ await coreWriter.update(expectedArtifact);
1259
+ // ASSERT
1260
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
1261
+ const updatedArtifact = mailboxWriter.update.firstCall.args[0];
1262
+ expect(updatedArtifact.config.defaultIsm.deployed.address).to.equal(mockNewIsmAddress);
1263
+ expect(updatedArtifact.config.defaultHook.deployed.address).to.equal(mockDefaultHookAddress);
1264
+ expect(updatedArtifact.config.requiredHook.deployed.address).to.equal(mockRequiredHookAddress);
1265
+ });
1266
+ it('should forward expected contractVersion into the mailbox update artifact', async () => {
1267
+ // ARRANGE
1268
+ const expectedContractVersion = '2.0.0';
1269
+ const currentMailbox = {
1270
+ artifactState: ArtifactState.DEPLOYED,
1271
+ config: {
1272
+ owner: mockOwner,
1273
+ defaultIsm: mockIsm,
1274
+ defaultHook: mockDefaultHook,
1275
+ requiredHook: mockRequiredHook,
1276
+ contractVersion: '1.0.0',
1277
+ },
1278
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1279
+ };
1280
+ sinon.stub(coreWriter, 'read').resolves(currentMailbox);
1281
+ const expectedArtifact = {
1282
+ artifactState: ArtifactState.DEPLOYED,
1283
+ config: {
1284
+ owner: mockOwner,
1285
+ defaultIsm: mockIsm,
1286
+ defaultHook: mockDefaultHook,
1287
+ requiredHook: mockRequiredHook,
1288
+ contractVersion: expectedContractVersion,
1289
+ },
1290
+ deployed: { address: mockMailboxAddress, domainId: mockDomainId },
1291
+ };
1292
+ const mockIsmWriter = {
1293
+ create: sinon.stub(),
1294
+ update: sinon.stub().resolves([]),
1295
+ read: sinon.stub(),
1296
+ };
1297
+ mockIsmArtifactManager.createWriter = sinon.stub().returns(mockIsmWriter);
1298
+ const mockHookWriter = {
1299
+ create: sinon.stub(),
1300
+ update: sinon.stub().resolves([]),
1301
+ read: sinon.stub(),
1302
+ };
1303
+ mockHookArtifactManager.createWriter = sinon
1304
+ .stub()
1305
+ .returns(mockHookWriter);
1306
+ // ACT
1307
+ await coreWriter.update(expectedArtifact);
1308
+ // ASSERT
1309
+ const mailboxWriter = createMailboxWriterStub.returnValues[0];
1310
+ const updatedArtifact = mailboxWriter.update.firstCall.args[0];
1311
+ expect(updatedArtifact.config.contractVersion).to.equal(expectedContractVersion);
1312
+ });
1313
+ });
1314
+ });
1315
+ //# sourceMappingURL=core-writer.test.js.map