@across-protocol/contracts 3.0.17 → 3.0.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.
- package/dist/scripts/svm/addressToPublicKey.d.ts +1 -0
- package/dist/scripts/svm/addressToPublicKey.js +20 -0
- package/dist/scripts/svm/bridgeLiabilityToHubPool.d.ts +28 -0
- package/dist/scripts/svm/bridgeLiabilityToHubPool.js +231 -0
- package/dist/scripts/svm/closeRelayerPdas.js +6 -7
- package/dist/scripts/svm/enableRoute.js +7 -1
- package/dist/scripts/svm/executeRebalanceToHubPool.d.ts +32 -0
- package/dist/scripts/svm/executeRebalanceToHubPool.js +355 -0
- package/dist/scripts/svm/executeRebalanceToSpokePool.d.ts +1 -0
- package/dist/scripts/svm/executeRebalanceToSpokePool.js +253 -0
- package/dist/scripts/svm/proposeRebalanceToHubPool.d.ts +27 -0
- package/dist/scripts/svm/proposeRebalanceToHubPool.js +117 -0
- package/dist/scripts/svm/proposeRebalanceToSpokePool.d.ts +1 -0
- package/dist/scripts/svm/proposeRebalanceToSpokePool.js +101 -0
- package/dist/scripts/svm/publicKeyToAddress.d.ts +1 -0
- package/dist/scripts/svm/publicKeyToAddress.js +20 -0
- package/dist/scripts/svm/queryDeposits.js +1 -0
- package/dist/scripts/svm/queryFills.js +11 -12
- package/dist/scripts/svm/queryRoute.js +7 -1
- package/dist/scripts/svm/queryState.js +1 -0
- package/dist/scripts/svm/remoteHubPoolPauseDeposits.d.ts +1 -0
- package/dist/scripts/svm/remoteHubPoolPauseDeposits.js +205 -0
- package/dist/scripts/svm/simpleDeposit.js +7 -1
- package/dist/scripts/svm/simpleFill.js +5 -4
- package/dist/scripts/svm/utils/constants.d.ts +4 -0
- package/dist/scripts/svm/utils/constants.js +8 -1
- package/dist/scripts/svm/utils/helpers.d.ts +31 -0
- package/dist/scripts/svm/utils/helpers.js +50 -1
- package/dist/scripts/svm/utils/poolRebalanceTree.d.ts +22 -0
- package/dist/scripts/svm/utils/poolRebalanceTree.js +20 -0
- package/dist/src/SvmUtils.d.ts +24 -2
- package/dist/src/SvmUtils.js +107 -26
- package/dist/target/types/svm_spoke.d.ts +47 -42
- package/dist/tasks/enableL1TokenAcrossEcosystem.js +3 -33
- package/dist/tasks/types.d.ts +2 -0
- package/dist/tasks/types.js +2 -0
- package/dist/tasks/utils.d.ts +12 -0
- package/dist/tasks/utils.js +34 -0
- package/dist/test/svm/SvmSpoke.Bundle.js +15 -16
- package/dist/test/svm/SvmSpoke.Deposit.js +18 -26
- package/dist/test/svm/SvmSpoke.Fill.AcrossPlus.js +1 -1
- package/dist/test/svm/SvmSpoke.Fill.js +13 -14
- package/dist/test/svm/SvmSpoke.Ownership.js +10 -16
- package/dist/test/svm/SvmSpoke.RefundClaims.js +9 -8
- package/dist/test/svm/SvmSpoke.Routes.js +10 -6
- package/dist/test/svm/SvmSpoke.SlowFill.AcrossPlus.js +59 -2
- package/dist/test/svm/SvmSpoke.SlowFill.js +23 -18
- package/dist/test/svm/SvmSpoke.TokenBridge.js +3 -5
- package/dist/test/svm/SvmSpoke.common.js +2 -1
- package/dist/test/svm/utils.d.ts +4 -5
- package/dist/test/svm/utils.js +24 -18
- package/package.json +2 -2
|
@@ -144,11 +144,9 @@ describe("svm_spoke.bundle", () => {
|
|
|
144
144
|
.relayRootBundle(relayerRefundRootArray, slowRelayRootArray)
|
|
145
145
|
.accounts(relayRootBundleAccounts)
|
|
146
146
|
.rpc();
|
|
147
|
-
// Wait for event processing
|
|
148
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
149
147
|
// Check for the emitted event
|
|
150
|
-
|
|
151
|
-
const event = events.find((event) => event.name === "relayedRootBundle")
|
|
148
|
+
let events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
149
|
+
const event = events.find((event) => event.name === "relayedRootBundle")?.data;
|
|
152
150
|
chai_1.assert.isTrue(event.rootBundleId.toString() === rootBundleId.toString(), "Root bundle ID should match");
|
|
153
151
|
chai_1.assert.isTrue(event.relayerRefundRoot.toString() === relayerRefundRootArray.toString(), "Relayer refund root should match");
|
|
154
152
|
chai_1.assert.isTrue(event.slowRelayRoot.toString() === slowRelayRootArray.toString(), "Slow relay root should match");
|
|
@@ -200,15 +198,14 @@ describe("svm_spoke.bundle", () => {
|
|
|
200
198
|
};
|
|
201
199
|
const proofAsNumbers = proof.map((p) => Array.from(p));
|
|
202
200
|
await (0, utils_1.loadExecuteRelayerRefundLeafParams)(program, owner, stateAccountData.rootBundleId, leaf, proofAsNumbers);
|
|
203
|
-
await program.methods
|
|
201
|
+
const tx = await program.methods
|
|
204
202
|
.executeRelayerRefundLeaf()
|
|
205
203
|
.accounts(executeRelayerRefundLeafAccounts)
|
|
206
204
|
.remainingAccounts(remainingAccounts)
|
|
207
205
|
.rpc();
|
|
208
206
|
// Verify the ExecutedRelayerRefundRoot event
|
|
209
|
-
|
|
210
|
-
let
|
|
211
|
-
let event = events.find((event) => event.name === "executedRelayerRefundRoot").data;
|
|
207
|
+
let events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
208
|
+
let event = events.find((event) => event.name === "executedRelayerRefundRoot")?.data;
|
|
212
209
|
// Remove the expectedValues object and use direct assertions
|
|
213
210
|
assertSE(event.amountToReturn, relayerRefundLeaves[0].amountToReturn, "amountToReturn should match");
|
|
214
211
|
assertSE(event.chainId, chainId, "chainId should match");
|
|
@@ -221,6 +218,12 @@ describe("svm_spoke.bundle", () => {
|
|
|
221
218
|
assertSE(event.refundAddresses[1], relayerB.publicKey, "Relayer B address should match");
|
|
222
219
|
chai_1.assert.isFalse(event.deferredRefunds, "deferredRefunds should be false");
|
|
223
220
|
assertSE(event.caller, owner, "caller should match");
|
|
221
|
+
event = events.find((event) => event.name === "tokensBridged")?.data;
|
|
222
|
+
assertSE(event.amountToReturn, relayerRefundLeaves[0].amountToReturn, "amountToReturn should match");
|
|
223
|
+
assertSE(event.chainId, chainId, "chainId should match");
|
|
224
|
+
assertSE(event.leafId, leaf.leafId, "leafId should match");
|
|
225
|
+
assertSE(event.l2TokenAddress, mint, "l2TokenAddress should match");
|
|
226
|
+
assertSE(event.caller, owner, "caller should match");
|
|
224
227
|
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
|
|
225
228
|
const fRelayerABal = (await connection.getTokenAccountBalance(relayerTA)).value.amount;
|
|
226
229
|
const fRelayerBBal = (await connection.getTokenAccountBalance(relayerTB)).value.amount;
|
|
@@ -1180,16 +1183,14 @@ describe("svm_spoke.bundle", () => {
|
|
|
1180
1183
|
};
|
|
1181
1184
|
it("No deferred refunds in all Token Accounts", async () => {
|
|
1182
1185
|
const tx = await executeRelayerRefundLeaf({ deferredRefunds: false });
|
|
1183
|
-
|
|
1184
|
-
const
|
|
1185
|
-
const event = events.find((event) => event.name === "executedRelayerRefundRoot").data;
|
|
1186
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
1187
|
+
const event = events.find((event) => event.name === "executedRelayerRefundRoot")?.data;
|
|
1186
1188
|
chai_1.assert.isFalse(event.deferredRefunds, "deferredRefunds should be false");
|
|
1187
1189
|
});
|
|
1188
1190
|
it("Deferred refunds in all Claim Accounts", async () => {
|
|
1189
1191
|
const tx = await executeRelayerRefundLeaf({ deferredRefunds: true });
|
|
1190
|
-
|
|
1191
|
-
const
|
|
1192
|
-
const event = events.find((event) => event.name === "executedRelayerRefundRoot").data;
|
|
1192
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
1193
|
+
const event = events.find((event) => event.name === "executedRelayerRefundRoot")?.data;
|
|
1193
1194
|
chai_1.assert.isTrue(event.deferredRefunds, "deferredRefunds should be true");
|
|
1194
1195
|
});
|
|
1195
1196
|
});
|
|
@@ -1321,7 +1322,6 @@ describe("svm_spoke.bundle", () => {
|
|
|
1321
1322
|
chai_1.assert.fail("Leaf verification should fail without leading 64 bytes");
|
|
1322
1323
|
}
|
|
1323
1324
|
catch (err) {
|
|
1324
|
-
console.log("err", err);
|
|
1325
1325
|
chai_1.assert.include(err.toString(), "Invalid Merkle proof", "Expected merkle verification to fail");
|
|
1326
1326
|
}
|
|
1327
1327
|
});
|
|
@@ -1399,7 +1399,6 @@ describe("svm_spoke.bundle", () => {
|
|
|
1399
1399
|
chai_1.assert.fail("Leaf verification should fail without leading 64 bytes");
|
|
1400
1400
|
}
|
|
1401
1401
|
catch (err) {
|
|
1402
|
-
console.log("err", err);
|
|
1403
1402
|
chai_1.assert.include(err.toString(), "Invalid Merkle proof", "Expected merkle verification to fail");
|
|
1404
1403
|
}
|
|
1405
1404
|
});
|
|
@@ -85,7 +85,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
85
85
|
.accounts(calledDepositAccounts)
|
|
86
86
|
.instruction();
|
|
87
87
|
const depositTx = new web3_js_1.Transaction().add(approveIx, depositIx);
|
|
88
|
-
await (0, web3_js_1.sendAndConfirmTransaction)(connection, depositTx, [payer, depositor]);
|
|
88
|
+
const tx = await (0, web3_js_1.sendAndConfirmTransaction)(connection, depositTx, [payer, depositor]);
|
|
89
|
+
return tx;
|
|
89
90
|
};
|
|
90
91
|
beforeEach(async () => {
|
|
91
92
|
({ state, seed } = await initializeState());
|
|
@@ -122,9 +123,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
122
123
|
depositData.inputAmount = depositData.inputAmount.add(new anchor_1.BN(69));
|
|
123
124
|
// Execute the first deposit_v3 call
|
|
124
125
|
let depositDataValues = Object.values(depositData);
|
|
125
|
-
await approvedDepositV3(depositDataValues);
|
|
126
|
-
|
|
127
|
-
let events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
126
|
+
const tx = await approvedDepositV3(depositDataValues);
|
|
127
|
+
let events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
128
128
|
let event = events[0].data; // 0th event is the latest event
|
|
129
129
|
const expectedValues1 = { ...depositData, depositId: (0, utils_1.intToU8Array32)(1) }; // Verify the event props emitted match the depositData.
|
|
130
130
|
for (let [key, value] of Object.entries(expectedValues1)) {
|
|
@@ -133,9 +133,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
133
133
|
assertSE(event[key], value, `${key} should match`);
|
|
134
134
|
}
|
|
135
135
|
// Execute the second deposit_v3 call
|
|
136
|
-
await approvedDepositV3(depositDataValues);
|
|
137
|
-
await
|
|
138
|
-
events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
136
|
+
const tx2 = await approvedDepositV3(depositDataValues);
|
|
137
|
+
events = await (0, utils_1.readEventsUntilFound)(connection, tx2, [program]);
|
|
139
138
|
event = events[0].data; // 0th event is the latest event.
|
|
140
139
|
const expectedValues2 = { ...expectedValues1, depositId: (0, utils_1.intToU8Array32)(2) }; // Verify the event props emitted match the depositData.
|
|
141
140
|
for (let [key, value] of Object.entries(expectedValues2)) {
|
|
@@ -286,7 +285,6 @@ describe("svm_spoke.deposit", () => {
|
|
|
286
285
|
program: program.programId,
|
|
287
286
|
};
|
|
288
287
|
await program.methods.setEnableRoute(inputToken, fakeRouteChainId, true).accounts(fakeSetEnableRouteAccounts).rpc();
|
|
289
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
290
288
|
const fakeDepositAccounts = {
|
|
291
289
|
state: fakeState.state,
|
|
292
290
|
route: fakeRoutePda,
|
|
@@ -302,9 +300,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
302
300
|
...depositData,
|
|
303
301
|
destinationChainId: fakeRouteChainId,
|
|
304
302
|
});
|
|
305
|
-
await approvedDepositV3(depositDataValues, fakeDepositAccounts);
|
|
306
|
-
|
|
307
|
-
let events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
303
|
+
const tx = await approvedDepositV3(depositDataValues, fakeDepositAccounts);
|
|
304
|
+
let events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
308
305
|
let event = events[0].data; // 0th event is the latest event.
|
|
309
306
|
const expectedValues = {
|
|
310
307
|
...{ ...depositData, destinationChainId: fakeRouteChainId },
|
|
@@ -346,9 +343,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
346
343
|
.accounts(depositAccounts)
|
|
347
344
|
.instruction();
|
|
348
345
|
const depositTx = new web3_js_1.Transaction().add(approveIx, depositIx);
|
|
349
|
-
await (0, web3_js_1.sendAndConfirmTransaction)(connection, depositTx, [payer, depositor]);
|
|
350
|
-
|
|
351
|
-
const events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
346
|
+
const tx = await (0, web3_js_1.sendAndConfirmTransaction)(connection, depositTx, [payer, depositor]);
|
|
347
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
352
348
|
const event = events[0].data; // 0th event is the latest event.
|
|
353
349
|
// Verify the event props emitted match the expected values
|
|
354
350
|
const currentTime = await getCurrentTime(program, state);
|
|
@@ -407,9 +403,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
407
403
|
depositData.exclusiveRelayer = depositor.publicKey;
|
|
408
404
|
depositData.exclusivityParameter = maxExclusivityOffsetSeconds;
|
|
409
405
|
const depositDataValues = Object.values(depositData);
|
|
410
|
-
await approvedDepositV3(depositDataValues);
|
|
411
|
-
|
|
412
|
-
const events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
406
|
+
const tx = await approvedDepositV3(depositDataValues);
|
|
407
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
413
408
|
const event = events[0].data; // 0th event is the latest event
|
|
414
409
|
assertSE(event.exclusivityDeadline, currentTime.add(maxExclusivityOffsetSeconds), "exclusivityDeadline should be current time + offset");
|
|
415
410
|
});
|
|
@@ -420,9 +415,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
420
415
|
depositData.exclusiveRelayer = depositor.publicKey;
|
|
421
416
|
depositData.exclusivityParameter = exclusivityDeadlineTimestamp;
|
|
422
417
|
const depositDataValues = Object.values(depositData);
|
|
423
|
-
await approvedDepositV3(depositDataValues);
|
|
424
|
-
|
|
425
|
-
const events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
418
|
+
const tx = await approvedDepositV3(depositDataValues);
|
|
419
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
426
420
|
const event = events[0].data; // 0th event is the latest event;
|
|
427
421
|
assertSE(event.exclusivityDeadline, exclusivityDeadlineTimestamp, "exclusivityDeadline should be passed in time");
|
|
428
422
|
});
|
|
@@ -433,9 +427,8 @@ describe("svm_spoke.deposit", () => {
|
|
|
433
427
|
depositData.exclusiveRelayer = depositor.publicKey;
|
|
434
428
|
depositData.exclusivityParameter = zeroExclusivity;
|
|
435
429
|
const depositDataValues = Object.values(depositData);
|
|
436
|
-
await approvedDepositV3(depositDataValues);
|
|
437
|
-
|
|
438
|
-
const events = await (0, utils_1.readProgramEvents)(connection, program);
|
|
430
|
+
const tx = await approvedDepositV3(depositDataValues);
|
|
431
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
439
432
|
const event = events[0].data; // 0th event is the latest event;
|
|
440
433
|
assertSE(event.exclusivityDeadline, zeroExclusivity, "Exclusivity deadline should always be 0");
|
|
441
434
|
});
|
|
@@ -462,11 +455,10 @@ describe("svm_spoke.deposit", () => {
|
|
|
462
455
|
.accounts(depositAccounts) // Assuming depositAccounts is already set up correctly
|
|
463
456
|
.instruction();
|
|
464
457
|
const unsafeDepositTx = new web3_js_1.Transaction().add(approveIx, unsafeDepositIx);
|
|
465
|
-
await (0, web3_js_1.sendAndConfirmTransaction)(connection, unsafeDepositTx, [payer, depositor]);
|
|
458
|
+
const tx = await (0, web3_js_1.sendAndConfirmTransaction)(connection, unsafeDepositTx, [payer, depositor]);
|
|
466
459
|
// Wait for a short period to ensure the event is emitted
|
|
467
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
468
460
|
// Read and verify the event
|
|
469
|
-
const events = await (0, utils_1.
|
|
461
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
470
462
|
const event = events[0].data; // Assuming the latest event is the one we want
|
|
471
463
|
const expectedValues = { ...depositData, depositId: expectedDepositIdArray };
|
|
472
464
|
for (let [key, value] of Object.entries(expectedValues)) {
|
|
@@ -176,7 +176,7 @@ describe("svm_spoke.fill.across_plus", () => {
|
|
|
176
176
|
const computeBudgetIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 });
|
|
177
177
|
await (0, SvmUtils_1.sendTransactionWithLookupTable)(connection, [computeBudgetIx, approveIx, fillIx], relayer);
|
|
178
178
|
// Verify relayer's balance after the fill
|
|
179
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // Make sure token transfers get processed.
|
|
180
180
|
const fRelayerBal = (await (0, spl_token_1.getAccount)(connection, relayerATA)).amount;
|
|
181
181
|
assertSE(fRelayerBal, iRelayerBal - BigInt(distributionAmount * numberOfDistributions), "Relayer's balance should be reduced by the relay amount");
|
|
182
182
|
// Verify all recipient account balances after the fill.
|
|
@@ -71,7 +71,8 @@ describe("svm_spoke.fill", () => {
|
|
|
71
71
|
.remainingAccounts(fillRemainingAccounts)
|
|
72
72
|
.instruction();
|
|
73
73
|
const fillTx = new web3_js_1.Transaction().add(approveIx, fillIx);
|
|
74
|
-
await (0, web3_js_1.sendAndConfirmTransaction)(connection, fillTx, [payer, callingRelayer]);
|
|
74
|
+
const tx = await (0, web3_js_1.sendAndConfirmTransaction)(connection, fillTx, [payer, callingRelayer]);
|
|
75
|
+
return tx;
|
|
75
76
|
};
|
|
76
77
|
before("Funds relayer wallets", async () => {
|
|
77
78
|
await connection.requestAirdrop(relayer.publicKey, 10_000_000_000); // 10 SOL
|
|
@@ -124,23 +125,22 @@ describe("svm_spoke.fill", () => {
|
|
|
124
125
|
});
|
|
125
126
|
it("Verifies FilledV3Relay event after filling a relay", async () => {
|
|
126
127
|
const relayHash = Array.from((0, SvmUtils_1.calculateRelayHashUint8Array)(relayData, chainId));
|
|
127
|
-
await approvedFillV3Relay([relayHash, relayData, new anchor_1.BN(420), otherRelayer.publicKey]);
|
|
128
|
+
const tx = await approvedFillV3Relay([relayHash, relayData, new anchor_1.BN(420), otherRelayer.publicKey]);
|
|
128
129
|
// Fetch and verify the FilledV3Relay event
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
const event = events.find((event) => event.name === "filledV3Relay").data;
|
|
130
|
+
const events = await (0, SvmUtils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
131
|
+
const event = events.find((event) => event.name === "filledV3Relay")?.data;
|
|
132
132
|
assert.isNotNull(event, "FilledV3Relay event should be emitted");
|
|
133
133
|
// Verify that the event data matches the relay data.
|
|
134
134
|
Object.entries(relayData).forEach(([key, value]) => {
|
|
135
135
|
if (key === "message") {
|
|
136
|
-
assertSE(event.messageHash, (0,
|
|
136
|
+
assertSE(event.messageHash, (0, SvmUtils_1.hashNonEmptyMessage)(value), `MessageHash should match`);
|
|
137
137
|
}
|
|
138
138
|
else
|
|
139
139
|
assertSE(event[key], value, `${key.charAt(0).toUpperCase() + key.slice(1)} should match`);
|
|
140
140
|
});
|
|
141
141
|
// RelayExecutionInfo should match.
|
|
142
142
|
assertSE(event.relayExecutionInfo.updatedRecipient, relayData.recipient, "UpdatedRecipient should match");
|
|
143
|
-
assertSE(event.relayExecutionInfo.updatedMessageHash, (0,
|
|
143
|
+
assertSE(event.relayExecutionInfo.updatedMessageHash, (0, SvmUtils_1.hashNonEmptyMessage)(relayData.message), "UpdatedMessageHash should match");
|
|
144
144
|
assertSE(event.relayExecutionInfo.updatedOutputAmount, relayData.outputAmount, "UpdatedOutputAmount should match");
|
|
145
145
|
assert.equal(JSON.stringify(event.relayExecutionInfo.fillType), `{"fastFill":{}}`, "FillType should be FastFill");
|
|
146
146
|
// These props below are not part of relayData.
|
|
@@ -213,7 +213,7 @@ describe("svm_spoke.fill", () => {
|
|
|
213
213
|
assert.isNotNull(fillStatusAccountBefore, "Fill PDA should exist before closing");
|
|
214
214
|
// Attempt to close the fill PDA before the fill deadline should fail.
|
|
215
215
|
try {
|
|
216
|
-
await program.methods.closeFillPda(
|
|
216
|
+
await program.methods.closeFillPda().accounts(closeFillPdaAccounts).signers([relayer]).rpc();
|
|
217
217
|
assert.fail("Closing fill PDA should have failed before fill deadline");
|
|
218
218
|
}
|
|
219
219
|
catch (err) {
|
|
@@ -222,7 +222,7 @@ describe("svm_spoke.fill", () => {
|
|
|
222
222
|
// Set the current time to past the fill deadline
|
|
223
223
|
await setCurrentTime(program, state, relayer, new anchor_1.BN(relayData.fillDeadline + 1));
|
|
224
224
|
// Close the fill PDA
|
|
225
|
-
await program.methods.closeFillPda(
|
|
225
|
+
await program.methods.closeFillPda().accounts(closeFillPdaAccounts).signers([relayer]).rpc();
|
|
226
226
|
// Verify the fill PDA is closed
|
|
227
227
|
const fillStatusAccountAfter = await connection.getAccountInfo(accounts.fillStatus);
|
|
228
228
|
assert.isNull(fillStatusAccountAfter, "Fill PDA should be closed after closing");
|
|
@@ -426,7 +426,7 @@ describe("svm_spoke.fill", () => {
|
|
|
426
426
|
// Fill using the ALT.
|
|
427
427
|
await (0, SvmUtils_1.sendTransactionWithLookupTable)(connection, [createTokenAccountsInstruction, approveInstruction, ...fillInstructions], relayer);
|
|
428
428
|
// Verify balances after the fill
|
|
429
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
429
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for tx processing
|
|
430
430
|
const fRelayerBal = (await (0, spl_token_1.getAccount)(connection, relayerTA)).amount;
|
|
431
431
|
assertSE(fRelayerBal, iRelayerBal - BigInt(relayAmount * numberOfFills), "Relayer's balance should be reduced by total relay amount");
|
|
432
432
|
recipientAssociatedTokens.forEach(async (recipientAssociatedToken) => {
|
|
@@ -466,11 +466,10 @@ describe("svm_spoke.fill", () => {
|
|
|
466
466
|
it("Emits zeroed hash for empty message", async () => {
|
|
467
467
|
updateRelayData({ ...relayData, message: Buffer.alloc(0) });
|
|
468
468
|
const relayHash = Array.from((0, SvmUtils_1.calculateRelayHashUint8Array)(relayData, chainId));
|
|
469
|
-
await approvedFillV3Relay([relayHash, relayData, new anchor_1.BN(420), otherRelayer.publicKey]);
|
|
469
|
+
const tx = await approvedFillV3Relay([relayHash, relayData, new anchor_1.BN(420), otherRelayer.publicKey]);
|
|
470
470
|
// Fetch and verify the FilledV3Relay event
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
const event = events.find((event) => event.name === "filledV3Relay").data;
|
|
471
|
+
const events = await (0, SvmUtils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
472
|
+
const event = events.find((event) => event.name === "filledV3Relay")?.data;
|
|
474
473
|
assert.isNotNull(event, "FilledV3Relay event should be emitted");
|
|
475
474
|
// Verify that the event data has zeroed message hash.
|
|
476
475
|
assertSE(event.messageHash, new Uint8Array(32), `MessageHash should be zeroed`);
|
|
@@ -62,23 +62,21 @@ describe("svm_spoke.ownership", () => {
|
|
|
62
62
|
chai_1.assert.isFalse((await program.account.state.fetch(state)).pausedDeposits, "Deposits should not be paused");
|
|
63
63
|
// Pause deposits as owner
|
|
64
64
|
const pauseDepositsAccounts = { state, signer: owner, program: program.programId };
|
|
65
|
-
await program.methods.pauseDeposits(true).accounts(pauseDepositsAccounts).rpc();
|
|
66
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
65
|
+
const tx = await program.methods.pauseDeposits(true).accounts(pauseDepositsAccounts).rpc();
|
|
67
66
|
// Fetch the updated state
|
|
68
67
|
let stateAccountData = await program.account.state.fetch(state);
|
|
69
68
|
chai_1.assert.isTrue(stateAccountData.pausedDeposits, "Deposits should be paused");
|
|
70
69
|
// Verify the PausedDeposits event
|
|
71
|
-
let events = await (0, utils_1.
|
|
70
|
+
let events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx, [program]);
|
|
72
71
|
let pausedDepositEvents = events.filter((event) => event.name === "pausedDeposits");
|
|
73
72
|
chai_1.assert.isTrue(pausedDepositEvents[0].data.isPaused, "PausedDeposits event should indicate deposits are paused");
|
|
74
73
|
// Unpause deposits as owner
|
|
75
|
-
await program.methods.pauseDeposits(false).accounts(pauseDepositsAccounts).rpc();
|
|
76
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
74
|
+
const tx2 = await program.methods.pauseDeposits(false).accounts(pauseDepositsAccounts).rpc();
|
|
77
75
|
// Fetch the updated state
|
|
78
76
|
stateAccountData = await program.account.state.fetch(state);
|
|
79
77
|
chai_1.assert.isFalse(stateAccountData.pausedDeposits, "Deposits should not be paused");
|
|
80
78
|
// Verify the PausedDeposits event
|
|
81
|
-
events = await (0, utils_1.
|
|
79
|
+
events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx2, [program]);
|
|
82
80
|
pausedDepositEvents = events.filter((event) => event.name === "pausedDeposits");
|
|
83
81
|
chai_1.assert.isFalse(pausedDepositEvents[0].data.isPaused, "PausedDeposits event should indicate deposits are unpaused");
|
|
84
82
|
// Try to pause deposits as non-owner
|
|
@@ -95,23 +93,21 @@ describe("svm_spoke.ownership", () => {
|
|
|
95
93
|
chai_1.assert.isFalse((await program.account.state.fetch(state)).pausedFills, "Fills should not be paused");
|
|
96
94
|
// Pause fills as owner
|
|
97
95
|
const pauseFillsAccounts = { state, signer: owner, program: program.programId };
|
|
98
|
-
await program.methods.pauseFills(true).accounts(pauseFillsAccounts).rpc();
|
|
99
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
96
|
+
const tx = await program.methods.pauseFills(true).accounts(pauseFillsAccounts).rpc();
|
|
100
97
|
// Fetch the updated state
|
|
101
98
|
let stateAccountData = await program.account.state.fetch(state);
|
|
102
99
|
chai_1.assert.isTrue(stateAccountData.pausedFills, "Fills should be paused");
|
|
103
100
|
// Verify the PausedFills event
|
|
104
|
-
let events = await (0, utils_1.
|
|
101
|
+
let events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx, [program]);
|
|
105
102
|
let pausedFillEvents = events.filter((event) => event.name === "pausedFills");
|
|
106
103
|
chai_1.assert.isTrue(pausedFillEvents[0].data.isPaused, "PausedFills event should indicate fills are paused");
|
|
107
104
|
// Unpause fills as owner
|
|
108
|
-
await program.methods.pauseFills(false).accounts(pauseFillsAccounts).rpc();
|
|
109
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
105
|
+
const tx2 = await program.methods.pauseFills(false).accounts(pauseFillsAccounts).rpc();
|
|
110
106
|
// Fetch the updated state
|
|
111
107
|
stateAccountData = await program.account.state.fetch(state);
|
|
112
108
|
chai_1.assert.isFalse(stateAccountData.pausedFills, "Fills should not be paused");
|
|
113
109
|
// Verify the PausedFills event
|
|
114
|
-
events = await (0, utils_1.
|
|
110
|
+
events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx2, [program]);
|
|
115
111
|
pausedFillEvents = events.filter((event) => event.name === "pausedFills");
|
|
116
112
|
chai_1.assert.isFalse(pausedFillEvents[0].data.isPaused, "PausedFills event should indicate fills are unpaused");
|
|
117
113
|
// Try to pause fills as non-owner
|
|
@@ -128,7 +124,6 @@ describe("svm_spoke.ownership", () => {
|
|
|
128
124
|
// Transfer ownership to newOwner
|
|
129
125
|
const transferOwnershipAccounts = { state, signer: owner };
|
|
130
126
|
await program.methods.transferOwnership(newOwner.publicKey).accounts(transferOwnershipAccounts).rpc();
|
|
131
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
132
127
|
// Verify the new owner
|
|
133
128
|
let stateAccountData = await program.account.state.fetch(state);
|
|
134
129
|
chai_1.assert.equal(stateAccountData.owner.toString(), newOwner.publicKey.toString(), "Ownership should be transferred");
|
|
@@ -149,16 +144,15 @@ describe("svm_spoke.ownership", () => {
|
|
|
149
144
|
it("Sets cross-domain admin", async () => {
|
|
150
145
|
// Set cross-domain admin as owner
|
|
151
146
|
const setCrossDomainAdminAccounts = { state, signer: owner, program: program.programId };
|
|
152
|
-
await program.methods
|
|
147
|
+
const tx = await program.methods
|
|
153
148
|
.setCrossDomainAdmin(newCrossDomainAdmin.publicKey)
|
|
154
149
|
.accounts(setCrossDomainAdminAccounts)
|
|
155
150
|
.rpc();
|
|
156
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
157
151
|
// Verify the new cross-domain admin
|
|
158
152
|
let stateAccountData = await program.account.state.fetch(state);
|
|
159
153
|
chai_1.assert.equal(stateAccountData.crossDomainAdmin.toString(), newCrossDomainAdmin.publicKey.toString(), "Cross-domain admin should be set");
|
|
160
154
|
// Verify the SetXDomainAdmin event
|
|
161
|
-
let events = await (0, utils_1.
|
|
155
|
+
let events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx, [program]);
|
|
162
156
|
let setXDomainAdminEvents = events.filter((event) => event.name === "setXDomainAdmin");
|
|
163
157
|
chai_1.assert.equal(setXDomainAdminEvents[0].data.newAdmin.toString(), newCrossDomainAdmin.publicKey.toString(), "SetXDomainAdmin event should indicate the new admin");
|
|
164
158
|
// Try to set cross-domain admin as non-owner
|
|
@@ -142,16 +142,18 @@ describe("svm_spoke.refund_claims", () => {
|
|
|
142
142
|
const iVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
|
|
143
143
|
const iRelayerBal = (await connection.getTokenAccountBalance(tokenAccount)).value.amount;
|
|
144
144
|
// Claim refund for the relayer.
|
|
145
|
-
await program.methods
|
|
145
|
+
const tx = await program.methods
|
|
146
|
+
.claimRelayerRefundFor(relayer.publicKey)
|
|
147
|
+
.accounts(claimRelayerRefundAccounts)
|
|
148
|
+
.rpc();
|
|
146
149
|
// The relayer should have received funds from the vault.
|
|
147
150
|
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
|
|
148
151
|
const fRelayerBal = (await connection.getTokenAccountBalance(tokenAccount)).value.amount;
|
|
149
152
|
assertSE(BigInt(iVaultBal) - BigInt(fVaultBal), relayerRefund, "Vault balance");
|
|
150
153
|
assertSE(BigInt(fRelayerBal) - BigInt(iRelayerBal), relayerRefund, "Relayer balance");
|
|
151
154
|
// Verify the ClaimedRelayerRefund event
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const event = events.find((event) => event.name === "claimedRelayerRefund").data;
|
|
155
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
156
|
+
const event = events.find((event) => event.name === "claimedRelayerRefund")?.data;
|
|
155
157
|
assertSE(event.l2TokenAddress, mint, "l2TokenAddress should match");
|
|
156
158
|
assertSE(event.claimAmount, relayerRefund, "Relayer refund amount should match");
|
|
157
159
|
assertSE(event.refundAddress, relayer.publicKey, "Relayer refund address should match");
|
|
@@ -300,16 +302,15 @@ describe("svm_spoke.refund_claims", () => {
|
|
|
300
302
|
claimRelayerRefundAccounts.tokenAccount = customTokenAccount;
|
|
301
303
|
claimRelayerRefundAccounts.signer = relayer.publicKey; // Only relayer itself should be able to do this.
|
|
302
304
|
// Relayer can claim refund to custom token account.
|
|
303
|
-
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).signers([relayer]).rpc();
|
|
305
|
+
const tx = await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).signers([relayer]).rpc();
|
|
304
306
|
// The relayer should have received funds from the vault.
|
|
305
307
|
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
|
|
306
308
|
const fRelayerBal = (await connection.getTokenAccountBalance(customTokenAccount)).value.amount;
|
|
307
309
|
assertSE(BigInt(iVaultBal) - BigInt(fVaultBal), relayerRefund, "Vault balance");
|
|
308
310
|
assertSE(BigInt(fRelayerBal) - BigInt(iRelayerBal), relayerRefund, "Relayer balance");
|
|
309
311
|
// Verify the ClaimedRelayerRefund event
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
const event = events.find((event) => event.name === "claimedRelayerRefund").data;
|
|
312
|
+
const events = await (0, utils_1.readEventsUntilFound)(connection, tx, [program]);
|
|
313
|
+
const event = events.find((event) => event.name === "claimedRelayerRefund")?.data;
|
|
313
314
|
assertSE(event.l2TokenAddress, mint, "l2TokenAddress should match");
|
|
314
315
|
assertSE(event.claimAmount, relayerRefund, "Relayer refund amount should match");
|
|
315
316
|
assertSE(event.refundAddress, relayer.publicKey, "Relayer refund address should match");
|
|
@@ -60,25 +60,29 @@ describe("svm_spoke.routes", () => {
|
|
|
60
60
|
});
|
|
61
61
|
it("Sets, retrieves, and controls access to route enablement", async () => {
|
|
62
62
|
// Enable the route as owner
|
|
63
|
-
await program.methods
|
|
64
|
-
|
|
63
|
+
const tx = await program.methods
|
|
64
|
+
.setEnableRoute(tokenMint, routeChainId, true)
|
|
65
|
+
.accounts(setEnableRouteAccounts)
|
|
66
|
+
.rpc();
|
|
65
67
|
// Retrieve and verify the route is enabled
|
|
66
68
|
let routeAccount = await program.account.route.fetch(routePda);
|
|
67
69
|
chai_1.assert.isTrue(routeAccount.enabled, "Route should be enabled");
|
|
68
70
|
// Verify the enabledDepositRoute event
|
|
69
|
-
let events =
|
|
71
|
+
let events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx, [program]);
|
|
70
72
|
let event = events[0].data;
|
|
71
73
|
chai_1.assert.strictEqual(event.originToken.toString(), tokenMint.toString(), "originToken event match");
|
|
72
74
|
chai_1.assert.strictEqual(event.destinationChainId.toString(), routeChainId.toString(), "destinationChainId should match");
|
|
73
75
|
chai_1.assert.isTrue(event.enabled, "enabledDepositRoute enabled");
|
|
74
76
|
// Disable the route as owner
|
|
75
|
-
await program.methods
|
|
76
|
-
|
|
77
|
+
const tx2 = await program.methods
|
|
78
|
+
.setEnableRoute(tokenMint, routeChainId, false)
|
|
79
|
+
.accounts(setEnableRouteAccounts)
|
|
80
|
+
.rpc();
|
|
77
81
|
// Retrieve and verify the route is disabled
|
|
78
82
|
routeAccount = await program.account.route.fetch(routePda);
|
|
79
83
|
chai_1.assert.isFalse(routeAccount.enabled, "Route should be disabled");
|
|
80
84
|
// Verify the enabledDepositRoute event
|
|
81
|
-
events =
|
|
85
|
+
events = await (0, utils_1.readEventsUntilFound)(provider.connection, tx2, [program]);
|
|
82
86
|
event = events[0].data; // take most recent event, index 0.
|
|
83
87
|
chai_1.assert.strictEqual(event.originToken.toString(), tokenMint.toString(), "originToken event match");
|
|
84
88
|
chai_1.assert.strictEqual(event.destinationChainId.toString(), routeChainId.toString(), "destinationChainId should match");
|
|
@@ -32,8 +32,8 @@ const MerkleTree_1 = require("@uma/common/dist/MerkleTree");
|
|
|
32
32
|
const SvmUtils_1 = require("../../src/SvmUtils");
|
|
33
33
|
const SvmSpoke_common_1 = require("./SvmSpoke.common");
|
|
34
34
|
const utils_1 = require("./utils");
|
|
35
|
-
const { provider, connection, program, owner, chainId } = SvmSpoke_common_1.common;
|
|
36
|
-
const { initializeState, assertSE } = SvmSpoke_common_1.common;
|
|
35
|
+
const { provider, connection, program, owner, chainId, setCurrentTime } = SvmSpoke_common_1.common;
|
|
36
|
+
const { initializeState, assertSE, assert } = SvmSpoke_common_1.common;
|
|
37
37
|
describe("svm_spoke.slow_fill.across_plus", () => {
|
|
38
38
|
anchor.setProvider(provider);
|
|
39
39
|
const payer = anchor.AnchorProvider.env().wallet.payer;
|
|
@@ -277,4 +277,61 @@ describe("svm_spoke.slow_fill.across_plus", () => {
|
|
|
277
277
|
await fillTokenDistributions(numberOfDistributions, true);
|
|
278
278
|
});
|
|
279
279
|
});
|
|
280
|
+
it("Can recover and close fill status PDA from event data", async () => {
|
|
281
|
+
// Construct ix to transfer all tokens from handler to the final recipient.
|
|
282
|
+
const transferIx = (0, spl_token_1.createTransferCheckedInstruction)(handlerATA, mint, finalRecipientATA, handlerSigner, relayData.outputAmount.toNumber(), tokenDecimals);
|
|
283
|
+
const multicallHandlerCoder = new SvmUtils_1.MulticallHandlerCoder([transferIx]);
|
|
284
|
+
const handlerMessage = multicallHandlerCoder.encode();
|
|
285
|
+
const message = new SvmUtils_1.AcrossPlusMessageCoder({
|
|
286
|
+
handler: handlerProgram.programId,
|
|
287
|
+
readOnlyLen: multicallHandlerCoder.readOnlyLen,
|
|
288
|
+
valueAmount: new anchor_1.BN(0),
|
|
289
|
+
accounts: multicallHandlerCoder.compiledMessage.accountKeys,
|
|
290
|
+
handlerMessage,
|
|
291
|
+
});
|
|
292
|
+
const encodedMessage = message.encode();
|
|
293
|
+
// Update relay data with the encoded message.
|
|
294
|
+
const newRelayData = { ...relayData, message: encodedMessage };
|
|
295
|
+
updateRelayData(newRelayData);
|
|
296
|
+
// Prepare request and execute slow fill instructions as we will need to use Address Lookup Table (ALT).
|
|
297
|
+
// Request and execute slow fill.
|
|
298
|
+
const { loadRequestParamsInstructions, requestIx, loadExecuteParamsInstructions, executeIx } = await createSlowFillIx(multicallHandlerCoder, true);
|
|
299
|
+
// Fill using the ALT and submit load params transactions.
|
|
300
|
+
for (let i = 0; i < loadRequestParamsInstructions.length; i += 1) {
|
|
301
|
+
await (0, web3_js_1.sendAndConfirmTransaction)(program.provider.connection, new web3_js_1.Transaction().add(loadRequestParamsInstructions[i]), [relayer]);
|
|
302
|
+
}
|
|
303
|
+
await (0, SvmUtils_1.sendTransactionWithLookupTable)(connection, [requestIx], relayer);
|
|
304
|
+
await new Promise((resolve) => setTimeout(resolve, 1000)); // Make sure request tx gets processed.
|
|
305
|
+
for (let i = 0; i < loadExecuteParamsInstructions.length; i += 1) {
|
|
306
|
+
await (0, web3_js_1.sendAndConfirmTransaction)(program.provider.connection, new web3_js_1.Transaction().add(loadExecuteParamsInstructions[i]), [relayer]);
|
|
307
|
+
}
|
|
308
|
+
const { txSignature } = await (0, SvmUtils_1.sendTransactionWithLookupTable)(connection, [executeIx], relayer);
|
|
309
|
+
await connection.confirmTransaction(txSignature, "confirmed");
|
|
310
|
+
await connection.getTransaction(txSignature, {
|
|
311
|
+
commitment: "confirmed",
|
|
312
|
+
maxSupportedTransactionVersion: 0,
|
|
313
|
+
});
|
|
314
|
+
// We don't close ALT here as that would require ~4 minutes between deactivation and closing, but we demonstrate
|
|
315
|
+
// being able to close the fill status PDA using only event data.
|
|
316
|
+
const events = await (0, SvmUtils_1.readEventsUntilFound)(connection, txSignature, [program]);
|
|
317
|
+
const eventData = events.find((event) => event.name === "filledV3Relay")?.data;
|
|
318
|
+
assert.isNotNull(eventData, "FilledV3Relay event should be emitted");
|
|
319
|
+
// Recover relay hash and derived fill status from event data.
|
|
320
|
+
const relayHashUint8Array = (0, SvmUtils_1.calculateRelayEventHashUint8Array)(eventData, chainId);
|
|
321
|
+
const [fillStatusPDA] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fills"), relayHashUint8Array], program.programId);
|
|
322
|
+
const fillStatusAccount = await program.account.fillStatusAccount.fetch(fillStatusPDA);
|
|
323
|
+
assert.isTrue("filled" in fillStatusAccount.status, "Fill status account should be marked as filled");
|
|
324
|
+
assertSE(fillStatusAccount.relayer, relayer.publicKey, "Relayer should match in the fill status");
|
|
325
|
+
// Set the current time to past the fill deadline
|
|
326
|
+
await setCurrentTime(program, state, relayer, new anchor_1.BN(fillStatusAccount.fillDeadline + 1));
|
|
327
|
+
const closeFillPdaAccounts = {
|
|
328
|
+
signer: relayer.publicKey,
|
|
329
|
+
state,
|
|
330
|
+
fillStatus: fillStatusPDA,
|
|
331
|
+
};
|
|
332
|
+
await program.methods.closeFillPda().accounts(closeFillPdaAccounts).signers([relayer]).rpc();
|
|
333
|
+
// Verify the fill PDA is closed
|
|
334
|
+
const fillStatusAccountAfter = await connection.getAccountInfo(fillStatusPDA);
|
|
335
|
+
assert.isNull(fillStatusAccountAfter, "Fill PDA should be closed after closing");
|
|
336
|
+
});
|
|
280
337
|
});
|