@across-protocol/contracts 3.0.17 → 3.0.19
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/README.md +2 -0
- package/artifacts/build-info/9cb910e5bb5dd730cd01af84a0fb0466.json +1 -0
- package/artifacts/contracts/Ink_SpokePool.sol/Ink_SpokePool.dbg.json +4 -0
- package/artifacts/contracts/Ink_SpokePool.sol/Ink_SpokePool.json +2316 -0
- package/contracts/Ink_SpokePool.sol +72 -0
- package/dist/deploy/consts.js +8 -2
- package/dist/deployments/deployments.json +7 -1
- package/dist/hardhat.config.js +17 -0
- package/dist/scripts/svm/addressToPublicKey.d.ts +1 -0
- package/dist/scripts/svm/addressToPublicKey.js +20 -0
- package/dist/scripts/svm/bridgeLiabilityToHubPool.d.ts +25 -0
- package/dist/scripts/svm/bridgeLiabilityToHubPool.js +221 -0
- package/dist/scripts/svm/closeRelayerPdas.js +8 -9
- package/dist/scripts/svm/enableRoute.js +7 -1
- package/dist/scripts/svm/executeRebalanceToHubPool.d.ts +29 -0
- package/dist/scripts/svm/executeRebalanceToHubPool.js +345 -0
- package/dist/scripts/svm/executeRebalanceToSpokePool.d.ts +1 -0
- package/dist/scripts/svm/executeRebalanceToSpokePool.js +240 -0
- package/dist/scripts/svm/fakeFillWithRandomDistribution.js +6 -7
- package/dist/scripts/svm/initialize.js +2 -2
- 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 +97 -0
- package/dist/scripts/svm/publicKeyToAddress.d.ts +1 -0
- package/dist/scripts/svm/publicKeyToAddress.js +20 -0
- package/dist/scripts/svm/queryDeposits.js +3 -2
- package/dist/scripts/svm/queryFills.js +12 -14
- 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 +191 -0
- package/dist/scripts/svm/remoteHubPoolSetDepositRoute.js +16 -29
- package/dist/scripts/svm/remotePauseDeposits.js +21 -27
- package/dist/scripts/svm/simpleDeposit.js +7 -1
- package/dist/scripts/svm/simpleFakeRelayerRepayment.js +3 -3
- package/dist/scripts/svm/simpleFill.js +6 -6
- 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 +33 -0
- package/dist/scripts/svm/utils/helpers.js +60 -1
- package/dist/scripts/svm/utils/poolRebalanceTree.d.ts +22 -0
- package/dist/scripts/svm/utils/poolRebalanceTree.js +20 -0
- package/dist/src/svm/coders.d.ts +37 -0
- package/dist/src/svm/coders.js +250 -0
- package/dist/src/svm/conversionUtils.d.ts +22 -0
- package/dist/src/svm/conversionUtils.js +73 -0
- package/dist/src/svm/index.d.ts +6 -0
- package/dist/src/svm/index.js +22 -0
- package/dist/src/svm/instructionParamsUtils.d.ts +31 -0
- package/dist/src/svm/instructionParamsUtils.js +128 -0
- package/dist/src/svm/relayHashUtils.d.ts +30 -0
- package/dist/src/svm/relayHashUtils.js +209 -0
- package/dist/src/svm/solanaProgramUtils.d.ts +38 -0
- package/dist/src/svm/solanaProgramUtils.js +147 -0
- package/dist/src/svm/transactionUtils.d.ts +8 -0
- package/dist/src/svm/transactionUtils.js +55 -0
- package/dist/src/types/svm.d.ts +118 -0
- package/dist/src/types/svm.js +2 -0
- 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/MulticallHandler.js +2 -2
- package/dist/test/svm/SvmSpoke.Bundle.js +59 -60
- package/dist/test/svm/SvmSpoke.Deposit.js +23 -31
- package/dist/test/svm/SvmSpoke.Fill.AcrossPlus.js +16 -18
- package/dist/test/svm/SvmSpoke.Fill.js +38 -40
- package/dist/test/svm/SvmSpoke.HandleReceiveMessage.js +2 -2
- package/dist/test/svm/SvmSpoke.Ownership.js +11 -17
- package/dist/test/svm/SvmSpoke.RefundClaims.js +12 -11
- package/dist/test/svm/SvmSpoke.Routes.js +11 -7
- package/dist/test/svm/SvmSpoke.SlowFill.AcrossPlus.js +72 -16
- package/dist/test/svm/SvmSpoke.SlowFill.js +33 -28
- package/dist/test/svm/SvmSpoke.TokenBridge.js +15 -17
- package/dist/test/svm/SvmSpoke.common.d.ts +1 -49
- package/dist/test/svm/SvmSpoke.common.js +6 -5
- package/dist/test/svm/cctpHelpers.js +2 -2
- package/dist/test/svm/utils.d.ts +5 -67
- package/dist/test/svm/utils.js +10 -220
- package/dist/typechain/contracts/Ink_SpokePool.d.ts +1251 -0
- package/dist/typechain/contracts/Ink_SpokePool.js +2 -0
- package/dist/typechain/contracts/index.d.ts +1 -0
- package/dist/typechain/factories/contracts/Ink_SpokePool__factory.d.ts +1833 -0
- package/dist/typechain/factories/contracts/Ink_SpokePool__factory.js +2347 -0
- package/dist/typechain/factories/contracts/index.d.ts +1 -0
- package/dist/typechain/factories/contracts/index.js +3 -1
- package/dist/typechain/hardhat.d.ts +9 -0
- package/dist/typechain/index.d.ts +2 -0
- package/dist/typechain/index.js +5 -3
- package/package.json +2 -2
- package/dist/src/SvmUtils.d.ts +0 -50
- package/dist/src/SvmUtils.js +0 -415
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Script: Execute USDC Rebalance to Hub Pool
|
|
4
|
+
*
|
|
5
|
+
* This script executes a previously proposed root bundle on the Hub Pool to rebalance USDC from the Solana Spoke Pool
|
|
6
|
+
* to the Ethereum Hub Pool. It handles CCTP attestations, relayer refund leaf execution, and prepares pending
|
|
7
|
+
* liabilities for bridging back to the Hub Pool.
|
|
8
|
+
*
|
|
9
|
+
* Required Environment Variables:
|
|
10
|
+
* - MNEMONIC: Wallet mnemonic to sign the Ethereum transaction.
|
|
11
|
+
* - HUB_POOL_ADDRESS: Ethereum address of the Hub Pool.
|
|
12
|
+
* - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
|
|
13
|
+
*
|
|
14
|
+
* Required Arguments:
|
|
15
|
+
* - `--netSendAmount`: The unscaled amount of USDC to rebalance (e.g., for USDC with 6 decimals, 1 = 0.000001 USDC).
|
|
16
|
+
* - `--resumeRemoteTx`: (Optional) Hash of a previously submitted remote transaction to resume.
|
|
17
|
+
*
|
|
18
|
+
* Example Usage:
|
|
19
|
+
* NODE_URL_11155111=$NODE_URL_11155111 \
|
|
20
|
+
* MNEMONIC=$MNEMONIC \
|
|
21
|
+
* HUB_POOL_ADDRESS=$HUB_POOL_ADDRESS \
|
|
22
|
+
* anchor run executeRebalanceToHubPool \
|
|
23
|
+
* --provider.cluster "devnet" \
|
|
24
|
+
* --provider.wallet $SOLANA_PKEY_PATH \
|
|
25
|
+
* -- --netSendAmount 7
|
|
26
|
+
*
|
|
27
|
+
* Note:
|
|
28
|
+
* - Ensure all required environment variables are properly configured.
|
|
29
|
+
*/
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
42
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
43
|
+
}) : function(o, v) {
|
|
44
|
+
o["default"] = v;
|
|
45
|
+
});
|
|
46
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
54
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
|
+
};
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
58
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
59
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
60
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
61
|
+
// eslint-disable-next-line camelcase
|
|
62
|
+
const ethers_1 = require("ethers");
|
|
63
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
64
|
+
const helpers_1 = require("yargs/helpers");
|
|
65
|
+
const constants_1 = require("../../utils/constants");
|
|
66
|
+
// eslint-disable-next-line camelcase
|
|
67
|
+
const typechain_1 = require("../../typechain");
|
|
68
|
+
const constants_2 = require("./utils/constants");
|
|
69
|
+
const helpers_2 = require("./utils/helpers");
|
|
70
|
+
const common_1 = require("@uma/common");
|
|
71
|
+
const cctpHelpers_1 = require("../../test/svm/cctpHelpers");
|
|
72
|
+
const svm_1 = require("../../src/svm");
|
|
73
|
+
// Set up Solana provider.
|
|
74
|
+
const provider = anchor_1.AnchorProvider.env();
|
|
75
|
+
anchor.setProvider(provider);
|
|
76
|
+
// Get Solana programs.
|
|
77
|
+
const svmSpokeIdl = require("../../target/idl/svm_spoke.json");
|
|
78
|
+
const svmSpokeProgram = new anchor_1.Program(svmSpokeIdl, provider);
|
|
79
|
+
const messageTransmitterIdl = require("../../target/idl/message_transmitter.json");
|
|
80
|
+
const messageTransmitterProgram = new anchor_1.Program(messageTransmitterIdl, provider);
|
|
81
|
+
const [messageTransmitterState] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter")], messageTransmitterProgram.programId);
|
|
82
|
+
const [authorityPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter_authority"), svmSpokeProgram.programId.toBuffer()], messageTransmitterProgram.programId);
|
|
83
|
+
const [selfAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("self_authority")], svmSpokeProgram.programId);
|
|
84
|
+
const [eventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], svmSpokeProgram.programId);
|
|
85
|
+
// Set up Ethereum provider and signer.
|
|
86
|
+
const isDevnet = (0, helpers_2.isSolanaDevnet)(provider);
|
|
87
|
+
const nodeURL = isDevnet ? (0, common_1.getNodeUrl)("sepolia", true) : (0, common_1.getNodeUrl)("mainnet", true);
|
|
88
|
+
const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(nodeURL);
|
|
89
|
+
const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_2.requireEnv)("MNEMONIC")).connect(ethersProvider);
|
|
90
|
+
// Get the HubPool contract instance.
|
|
91
|
+
const hubPoolAddress = ethers_1.ethers.utils.getAddress((0, helpers_2.requireEnv)("HUB_POOL_ADDRESS"));
|
|
92
|
+
const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
|
|
93
|
+
// CCTP domains.
|
|
94
|
+
const ethereumDomain = 0; // Ethereum
|
|
95
|
+
// Parse arguments
|
|
96
|
+
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
97
|
+
// Net send amount is always required as we need it to calculate the relayer refund leaf.
|
|
98
|
+
.option("netSendAmount", { type: "string", demandOption: true, describe: "Net send amount to Hub Pool" })
|
|
99
|
+
.option("resumeRemoteTx", { type: "string", demandOption: false, describe: "Resume receiving remote tx" }).argv;
|
|
100
|
+
async function executeRebalanceToHubPool() {
|
|
101
|
+
const resolvedArgv = await argv;
|
|
102
|
+
const seed = constants_2.SOLANA_SPOKE_STATE_SEED; // Seed is always 0 for the state account PDA in public networks.
|
|
103
|
+
const netSendAmount = ethers_1.BigNumber.from(resolvedArgv.netSendAmount);
|
|
104
|
+
const resumeRemoteTx = resolvedArgv.resumeRemoteTx;
|
|
105
|
+
// Resolve Solana cluster, EVM chain ID, Iris API URL and USDC addresses.
|
|
106
|
+
const solanaCluster = isDevnet ? "devnet" : "mainnet";
|
|
107
|
+
const solanaChainId = (0, helpers_2.getSolanaChainId)(solanaCluster);
|
|
108
|
+
const irisApiUrl = isDevnet ? constants_2.CIRCLE_IRIS_API_URL_DEVNET : constants_2.CIRCLE_IRIS_API_URL_MAINNET;
|
|
109
|
+
const supportedEvmChainId = isDevnet ? constants_1.CHAIN_IDs.SEPOLIA : constants_1.CHAIN_IDs.MAINNET; // Sepolia is bridged to devnet, Ethereum to mainnet in CCTP.
|
|
110
|
+
const evmChainId = (await ethersProvider.getNetwork()).chainId;
|
|
111
|
+
if (evmChainId !== supportedEvmChainId) {
|
|
112
|
+
throw new Error(`Chain ID ${evmChainId} does not match expected Solana cluster ${solanaCluster}`);
|
|
113
|
+
}
|
|
114
|
+
const svmUsdc = isDevnet ? constants_2.SOLANA_USDC_DEVNET : constants_2.SOLANA_USDC_MAINNET;
|
|
115
|
+
const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId);
|
|
116
|
+
const state = await svmSpokeProgram.account.state.fetch(statePda);
|
|
117
|
+
const [rootBundlePda] = getRootBundlePda(state.rootBundleId, seed);
|
|
118
|
+
console.log("Executing rebalance to hub pool...");
|
|
119
|
+
console.table([
|
|
120
|
+
{ Property: "isTestnet", Value: isDevnet },
|
|
121
|
+
{ Property: "originChainId", Value: evmChainId.toString() },
|
|
122
|
+
{ Property: "targetChainId", Value: solanaChainId.toString() },
|
|
123
|
+
{ Property: "hubPoolAddress", Value: hubPool.address },
|
|
124
|
+
{ Property: "svmSpokeProgramProgramId", Value: svmSpokeProgram.programId.toString() },
|
|
125
|
+
{ Property: "svmProviderPublicKey", Value: provider.wallet.publicKey.toString() },
|
|
126
|
+
{ Property: "netSendAmount (formatted)", Value: (0, helpers_2.formatUsdc)(netSendAmount) },
|
|
127
|
+
]);
|
|
128
|
+
// Send executeRootBundle call from Ethereum, unless resuming a remote transaction.
|
|
129
|
+
let remoteTxHash;
|
|
130
|
+
if (!resumeRemoteTx) {
|
|
131
|
+
remoteTxHash = await executeRootBalanceOnHubPool(solanaChainId);
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
remoteTxHash = resumeRemoteTx;
|
|
135
|
+
// Fetch attestation from CCTP attestation service.
|
|
136
|
+
const attestationResponse = await (0, cctpHelpers_1.getMessages)(remoteTxHash, ethereumDomain, irisApiUrl);
|
|
137
|
+
const { attestation, message } = attestationResponse.messages[0];
|
|
138
|
+
console.log("CCTP attestation response:", attestationResponse.messages[0]);
|
|
139
|
+
// Accounts in CCTP message_transmitter receive_message instruction.
|
|
140
|
+
const nonce = (0, cctpHelpers_1.decodeMessageHeader)(Buffer.from(message.replace("0x", ""), "hex")).nonce;
|
|
141
|
+
const usedNonces = (await messageTransmitterProgram.methods
|
|
142
|
+
.getNoncePda({
|
|
143
|
+
nonce: new anchor_1.BN(nonce.toString()),
|
|
144
|
+
sourceDomain: ethereumDomain,
|
|
145
|
+
})
|
|
146
|
+
.accounts({
|
|
147
|
+
messageTransmitter: messageTransmitterState,
|
|
148
|
+
})
|
|
149
|
+
.view());
|
|
150
|
+
const receiveMessageAccounts = {
|
|
151
|
+
payer: provider.wallet.publicKey,
|
|
152
|
+
caller: provider.wallet.publicKey,
|
|
153
|
+
authorityPda,
|
|
154
|
+
messageTransmitter: messageTransmitterState,
|
|
155
|
+
usedNonces,
|
|
156
|
+
receiver: svmSpokeProgram.programId,
|
|
157
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
158
|
+
};
|
|
159
|
+
// accountMetas list to pass to remaining accounts when receiving message via CCTP.
|
|
160
|
+
const remainingAccounts = [];
|
|
161
|
+
// state in HandleReceiveMessage accounts (used for remote domain and sender authentication).
|
|
162
|
+
remainingAccounts.push({
|
|
163
|
+
isSigner: false,
|
|
164
|
+
isWritable: false,
|
|
165
|
+
pubkey: statePda,
|
|
166
|
+
});
|
|
167
|
+
// self_authority in HandleReceiveMessage accounts, also signer in self-invoked CPIs.
|
|
168
|
+
remainingAccounts.push({
|
|
169
|
+
isSigner: false,
|
|
170
|
+
isWritable: false,
|
|
171
|
+
pubkey: selfAuthority,
|
|
172
|
+
});
|
|
173
|
+
// program in HandleReceiveMessage accounts.
|
|
174
|
+
remainingAccounts.push({
|
|
175
|
+
isSigner: false,
|
|
176
|
+
isWritable: false,
|
|
177
|
+
pubkey: svmSpokeProgram.programId,
|
|
178
|
+
});
|
|
179
|
+
// payer
|
|
180
|
+
remainingAccounts.push({
|
|
181
|
+
isSigner: true,
|
|
182
|
+
isWritable: true,
|
|
183
|
+
pubkey: provider.wallet.publicKey,
|
|
184
|
+
});
|
|
185
|
+
// state in self-invoked CPIs (state can change as a result of remote call).
|
|
186
|
+
remainingAccounts.push({
|
|
187
|
+
isSigner: false,
|
|
188
|
+
isWritable: true,
|
|
189
|
+
pubkey: statePda,
|
|
190
|
+
});
|
|
191
|
+
// root_bundle
|
|
192
|
+
remainingAccounts.push({
|
|
193
|
+
isSigner: false,
|
|
194
|
+
isWritable: true,
|
|
195
|
+
pubkey: rootBundlePda,
|
|
196
|
+
});
|
|
197
|
+
// system_program
|
|
198
|
+
remainingAccounts.push({
|
|
199
|
+
isSigner: false,
|
|
200
|
+
isWritable: false,
|
|
201
|
+
pubkey: web3_js_1.SystemProgram.programId,
|
|
202
|
+
});
|
|
203
|
+
// event_authority in self-invoked CPIs (appended by Anchor with event_cpi macro).
|
|
204
|
+
remainingAccounts.push({
|
|
205
|
+
isSigner: false,
|
|
206
|
+
isWritable: true,
|
|
207
|
+
pubkey: eventAuthority,
|
|
208
|
+
});
|
|
209
|
+
// program
|
|
210
|
+
remainingAccounts.push({
|
|
211
|
+
isSigner: false,
|
|
212
|
+
isWritable: true,
|
|
213
|
+
pubkey: svmSpokeProgram.programId,
|
|
214
|
+
});
|
|
215
|
+
// Receive remote message on Solana.
|
|
216
|
+
console.log("Receiving message on Solana...");
|
|
217
|
+
const receiveMessageTx = await messageTransmitterProgram.methods
|
|
218
|
+
.receiveMessage({
|
|
219
|
+
message: Buffer.from(message.replace("0x", ""), "hex"),
|
|
220
|
+
attestation: Buffer.from(attestation.replace("0x", ""), "hex"),
|
|
221
|
+
})
|
|
222
|
+
.accounts(receiveMessageAccounts)
|
|
223
|
+
.remainingAccounts(remainingAccounts)
|
|
224
|
+
.rpc();
|
|
225
|
+
console.log("\nReceived remote message");
|
|
226
|
+
console.log("Your transaction signature", receiveMessageTx);
|
|
227
|
+
const finalState = await svmSpokeProgram.account.state.fetch(statePda);
|
|
228
|
+
// The state stores the next root bundle ID, so we need to subtract one to get the last executed root bundle ID.
|
|
229
|
+
const lastRootBundleId = finalState.rootBundleId - 1;
|
|
230
|
+
// Reconstruct the merkle tree for the relayer refund leaf.
|
|
231
|
+
const { merkleTree, leaves } = (0, helpers_2.constructSimpleRebalanceTreeToHubPool)(netSendAmount, solanaChainId, new web3_js_1.PublicKey(svmUsdc));
|
|
232
|
+
const [rootBundlePdaNew] = getRootBundlePda(lastRootBundleId, seed);
|
|
233
|
+
console.log(`Executing relayer refund leaf for root bundle ID: ${lastRootBundleId}`);
|
|
234
|
+
await executeRelayerRefundLeaf(provider.wallet, svmSpokeProgram, statePda, rootBundlePdaNew, leaves[0], merkleTree, new web3_js_1.PublicKey(svmUsdc), lastRootBundleId);
|
|
235
|
+
console.log("✅ executed relayer refund leaf");
|
|
236
|
+
const [transferLiability] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("transfer_liability"), new web3_js_1.PublicKey(svmUsdc).toBuffer()], svmSpokeProgram.programId);
|
|
237
|
+
const liability = await svmSpokeProgram.account.transferLiability.fetch(transferLiability);
|
|
238
|
+
console.log(`Pending transfer liability: ${(0, helpers_2.formatUsdc)(ethers_1.BigNumber.from(liability.pendingToHubPool.toString()))} USDC`);
|
|
239
|
+
console.log("You can now send the pending liability to hub pool with bridgeLiabilityToHubPool.ts");
|
|
240
|
+
}
|
|
241
|
+
function getRootBundlePda(rootBundleId, seed) {
|
|
242
|
+
const rootBundleIdBuffer = Buffer.alloc(4);
|
|
243
|
+
rootBundleIdBuffer.writeUInt32LE(rootBundleId);
|
|
244
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("root_bundle"), seed.toArrayLike(Buffer, "le", 8), rootBundleIdBuffer], svmSpokeProgram.programId);
|
|
245
|
+
}
|
|
246
|
+
async function executeRootBalanceOnHubPool(solanaChainId) {
|
|
247
|
+
// Reconstruct the merkle tree for the pool rebalance.
|
|
248
|
+
const { poolRebalanceLeaf, poolRebalanceTree } = (0, helpers_2.constructEmptyPoolRebalanceTree)(solanaChainId, 0);
|
|
249
|
+
// Make sure the proposal liveness has passed, it has not been executed and rebalance root matches.
|
|
250
|
+
const currentRootBundleProposal = await hubPool.connect(ethersSigner).callStatic.rootBundleProposal();
|
|
251
|
+
if (currentRootBundleProposal.challengePeriodEndTimestamp > (await hubPool.callStatic.getCurrentTime()).toNumber())
|
|
252
|
+
throw new Error("Not passed liveness");
|
|
253
|
+
if (!currentRootBundleProposal.claimedBitMap.isZero())
|
|
254
|
+
throw new Error("Already claimed");
|
|
255
|
+
if (currentRootBundleProposal.poolRebalanceRoot !== poolRebalanceTree.getHexRoot())
|
|
256
|
+
throw new Error("Rebalance root mismatch");
|
|
257
|
+
// Execute the rebalance bundle on the HubPool.
|
|
258
|
+
const tx = await hubPool.connect(ethersSigner).executeRootBundle(solanaChainId, 0, // groupIndex
|
|
259
|
+
poolRebalanceLeaf.bundleLpFees, poolRebalanceLeaf.netSendAmounts, poolRebalanceLeaf.runningBalances, poolRebalanceLeaf.leafId, poolRebalanceLeaf.l1Tokens, poolRebalanceTree.getHexProof(poolRebalanceLeaf));
|
|
260
|
+
console.log(`✅ submitted tx hash: ${tx.hash}`);
|
|
261
|
+
await tx.wait();
|
|
262
|
+
console.log("✅ tx confirmed");
|
|
263
|
+
return tx.hash;
|
|
264
|
+
}
|
|
265
|
+
async function executeRelayerRefundLeaf(signer, program, statePda, rootBundle, relayerRefundLeaf, merkleTree, inputToken, rootBundleId) {
|
|
266
|
+
// Execute the single leaf
|
|
267
|
+
const proof = merkleTree.getProof(relayerRefundLeaf).map((p) => Array.from(p));
|
|
268
|
+
const leaf = relayerRefundLeaf;
|
|
269
|
+
const vault = (0, spl_token_1.getAssociatedTokenAddressSync)(inputToken, statePda, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
270
|
+
// Derive the transferLiability PDA
|
|
271
|
+
const [transferLiability] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("transfer_liability"), inputToken.toBuffer()], program.programId);
|
|
272
|
+
// Load the instruction parameters
|
|
273
|
+
const proofAsNumbers = proof.map((p) => Array.from(p));
|
|
274
|
+
console.log("loading execute relayer refund leaf params...");
|
|
275
|
+
const [instructionParams] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("instruction_params"), signer.publicKey.toBuffer()], program.programId);
|
|
276
|
+
const staticAccounts = {
|
|
277
|
+
instructionParams,
|
|
278
|
+
state: statePda,
|
|
279
|
+
rootBundle: rootBundle,
|
|
280
|
+
signer: signer.publicKey,
|
|
281
|
+
vault: vault,
|
|
282
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
283
|
+
mint: inputToken,
|
|
284
|
+
transferLiability,
|
|
285
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
286
|
+
// Appended by Acnhor `event_cpi` macro:
|
|
287
|
+
eventAuthority: web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], program.programId)[0],
|
|
288
|
+
program: program.programId,
|
|
289
|
+
};
|
|
290
|
+
const refundAccounts = [];
|
|
291
|
+
// Consolidate all above addresses into a single array for the Address Lookup Table (ALT).
|
|
292
|
+
const [lookupTableInstruction, lookupTableAddress] = await web3_js_1.AddressLookupTableProgram.createLookupTable({
|
|
293
|
+
authority: signer.publicKey,
|
|
294
|
+
payer: signer.publicKey,
|
|
295
|
+
recentSlot: await provider.connection.getSlot(),
|
|
296
|
+
});
|
|
297
|
+
// Submit the ALT creation transaction
|
|
298
|
+
await anchor.web3.sendAndConfirmTransaction(provider.connection, new anchor.web3.Transaction().add(lookupTableInstruction), [anchor.AnchorProvider.env().wallet.payer], { skipPreflight: true });
|
|
299
|
+
const lookupAddresses = [...Object.values(staticAccounts), ...refundAccounts];
|
|
300
|
+
// Create the transaction with the compute budget expansion instruction & use extended ALT account.
|
|
301
|
+
// Extend the ALT with all accounts
|
|
302
|
+
const maxExtendedAccounts = 30; // Maximum number of accounts that can be added to ALT in a single transaction.
|
|
303
|
+
for (let i = 0; i < lookupAddresses.length; i += maxExtendedAccounts) {
|
|
304
|
+
const extendInstruction = web3_js_1.AddressLookupTableProgram.extendLookupTable({
|
|
305
|
+
lookupTable: lookupTableAddress,
|
|
306
|
+
authority: signer.publicKey,
|
|
307
|
+
payer: signer.publicKey,
|
|
308
|
+
addresses: lookupAddresses.slice(i, i + maxExtendedAccounts),
|
|
309
|
+
});
|
|
310
|
+
await anchor.web3.sendAndConfirmTransaction(provider.connection, new anchor.web3.Transaction().add(extendInstruction), [anchor.AnchorProvider.env().wallet.payer], { skipPreflight: true });
|
|
311
|
+
}
|
|
312
|
+
// Fetch the AddressLookupTableAccount
|
|
313
|
+
const lookupTableAccount = (await provider.connection.getAddressLookupTable(lookupTableAddress)).value;
|
|
314
|
+
if (!lookupTableAccount) {
|
|
315
|
+
throw new Error("AddressLookupTableAccount not fetched");
|
|
316
|
+
}
|
|
317
|
+
await (0, svm_1.loadExecuteRelayerRefundLeafParams)(program, signer.publicKey, rootBundleId, leaf, proofAsNumbers);
|
|
318
|
+
console.log(`loaded execute relayer refund leaf params ${instructionParams}. \nExecuting relayer refund leaf...`);
|
|
319
|
+
const executeInstruction = await program.methods
|
|
320
|
+
.executeRelayerRefundLeaf()
|
|
321
|
+
.accounts(staticAccounts)
|
|
322
|
+
.remainingAccounts([])
|
|
323
|
+
.instruction();
|
|
324
|
+
// Create the versioned transaction
|
|
325
|
+
const computeBudgetInstruction = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 });
|
|
326
|
+
const versionedTx = new web3_js_1.VersionedTransaction(new web3_js_1.TransactionMessage({
|
|
327
|
+
payerKey: signer.publicKey,
|
|
328
|
+
recentBlockhash: (await provider.connection.getLatestBlockhash()).blockhash,
|
|
329
|
+
instructions: [computeBudgetInstruction, executeInstruction],
|
|
330
|
+
}).compileToV0Message([lookupTableAccount]));
|
|
331
|
+
// Sign and submit the versioned transaction
|
|
332
|
+
versionedTx.sign([anchor.AnchorProvider.env().wallet.payer]);
|
|
333
|
+
const tx = await provider.connection.sendTransaction(versionedTx);
|
|
334
|
+
console.log(`Execute relayer refund leaf transaction sent: ${tx}`);
|
|
335
|
+
// Close the instruction parameters account
|
|
336
|
+
console.log("Closing instruction params...");
|
|
337
|
+
await new Promise((resolve) => setTimeout(resolve, 15000)); // Wait for the previous transaction to be processed.
|
|
338
|
+
const closeInstructionParamsTx = await program.methods.closeInstructionParams()
|
|
339
|
+
.accounts({ signer: signer.publicKey, instructionParams: instructionParams })
|
|
340
|
+
.rpc();
|
|
341
|
+
console.log(`Close instruction params transaction sent: ${closeInstructionParamsTx}`);
|
|
342
|
+
// Note we cant close the lookup table account as it needs to be both deactivated and expired at to do this.
|
|
343
|
+
}
|
|
344
|
+
// Run the executeRebalanceToHubPool function
|
|
345
|
+
executeRebalanceToHubPool();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This script executes root bundle on HubPool that rebalances tokens to Solana Spoke Pool. Required environment:
|
|
3
|
+
// - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
|
|
4
|
+
// - MNEMONIC: Mnemonic of the wallet that will sign the sending transaction on Ethereum
|
|
5
|
+
// - HUB_POOL_ADDRESS: Hub Pool address
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
34
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
35
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
36
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
37
|
+
const common_1 = require("@uma/common");
|
|
38
|
+
// eslint-disable-next-line camelcase
|
|
39
|
+
const constants_1 = require("../../utils/constants");
|
|
40
|
+
const yargs_1 = __importDefault(require("yargs"));
|
|
41
|
+
const helpers_1 = require("yargs/helpers");
|
|
42
|
+
const svm_1 = require("../../src/svm");
|
|
43
|
+
const ethers_1 = require("ethers");
|
|
44
|
+
// eslint-disable-next-line camelcase
|
|
45
|
+
const typechain_1 = require("../../typechain");
|
|
46
|
+
const constants_2 = require("./utils/constants");
|
|
47
|
+
const poolRebalanceTree_1 = require("./utils/poolRebalanceTree");
|
|
48
|
+
const cctpHelpers_1 = require("../../test/svm/cctpHelpers");
|
|
49
|
+
const helpers_2 = require("./utils/helpers");
|
|
50
|
+
// Set up Solana provider.
|
|
51
|
+
const provider = anchor_1.AnchorProvider.env();
|
|
52
|
+
anchor.setProvider(provider);
|
|
53
|
+
// Get Solana programs.
|
|
54
|
+
const svmSpokeIdl = require("../../target/idl/svm_spoke.json");
|
|
55
|
+
const svmSpokeProgram = new anchor_1.Program(svmSpokeIdl, provider);
|
|
56
|
+
const messageTransmitterIdl = require("../../target/idl/message_transmitter.json");
|
|
57
|
+
const messageTransmitterProgram = new anchor_1.Program(messageTransmitterIdl, provider);
|
|
58
|
+
const tokenMessengerMinterIdl = require("../../target/idl/token_messenger_minter.json");
|
|
59
|
+
const tokenMessengerMinterProgram = new anchor_1.Program(tokenMessengerMinterIdl, provider);
|
|
60
|
+
// Set up Ethereum provider and signer.
|
|
61
|
+
const isDevnet = (0, helpers_2.isSolanaDevnet)(provider);
|
|
62
|
+
const nodeURL = isDevnet ? (0, common_1.getNodeUrl)("sepolia", true) : (0, common_1.getNodeUrl)("mainnet", true);
|
|
63
|
+
const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(nodeURL);
|
|
64
|
+
const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_2.requireEnv)("MNEMONIC")).connect(ethersProvider);
|
|
65
|
+
// Get the HubPool contract instance.
|
|
66
|
+
const hubPoolAddress = ethers_1.ethers.utils.getAddress((0, helpers_2.requireEnv)("HUB_POOL_ADDRESS"));
|
|
67
|
+
const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
|
|
68
|
+
// CCTP domains.
|
|
69
|
+
const remoteDomain = 0; // Ethereum
|
|
70
|
+
// Parse arguments
|
|
71
|
+
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
72
|
+
.option("netSendAmount", { type: "string", demandOption: false, describe: "Net send amount to spoke" })
|
|
73
|
+
.option("resumeRemoteTx", { type: "string", demandOption: false, describe: "Resume receiving remote tx" })
|
|
74
|
+
.check((argv) => {
|
|
75
|
+
if (argv.netSendAmount !== undefined && argv.resumeRemoteTx !== undefined) {
|
|
76
|
+
throw new Error("Options --netSendAmount and --resumeRemoteTx are mutually exclusive");
|
|
77
|
+
}
|
|
78
|
+
if (argv.netSendAmount === undefined && argv.resumeRemoteTx === undefined) {
|
|
79
|
+
throw new Error("One of the options --netSendAmount or --resumeRemoteTx is required");
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}).argv;
|
|
83
|
+
async function executeRebalanceToSpokePool() {
|
|
84
|
+
const resolvedArgv = await argv;
|
|
85
|
+
const seed = new anchor_1.BN(0); // Seed is always 0 for the state account PDA in public networks.
|
|
86
|
+
const netSendAmount = resolvedArgv.netSendAmount ? ethers_1.BigNumber.from(resolvedArgv.netSendAmount) : ethers_1.BigNumber.from(0);
|
|
87
|
+
const resumeRemoteTx = resolvedArgv.resumeRemoteTx;
|
|
88
|
+
// Resolve chain IDs, Iris API URL and USDC addresses.
|
|
89
|
+
const solanaCluster = isDevnet ? "devnet" : "mainnet";
|
|
90
|
+
const solanaChainId = (0, helpers_2.getSolanaChainId)(solanaCluster);
|
|
91
|
+
const irisApiUrl = isDevnet ? constants_2.CIRCLE_IRIS_API_URL_DEVNET : constants_2.CIRCLE_IRIS_API_URL_MAINNET;
|
|
92
|
+
const supportedEvmChainId = isDevnet ? constants_1.CHAIN_IDs.SEPOLIA : constants_1.CHAIN_IDs.MAINNET; // Sepolia is bridged to devnet, Ethereum to mainnet in CCTP.
|
|
93
|
+
const evmChainId = (await ethersProvider.getNetwork()).chainId;
|
|
94
|
+
if (evmChainId !== supportedEvmChainId) {
|
|
95
|
+
throw new Error(`Chain ID ${evmChainId} does not match expected Solana cluster ${solanaCluster}`);
|
|
96
|
+
}
|
|
97
|
+
const l1TokenAddress = constants_1.TOKEN_SYMBOLS_MAP.USDC.addresses[evmChainId];
|
|
98
|
+
const solanaTokenKey = isDevnet ? new web3_js_1.PublicKey(constants_2.SOLANA_USDC_DEVNET) : new web3_js_1.PublicKey(constants_2.SOLANA_USDC_MAINNET);
|
|
99
|
+
console.log("Executing rebalance pool bundle to spoke...");
|
|
100
|
+
console.table([
|
|
101
|
+
{ Property: "originChainId", Value: evmChainId.toString() },
|
|
102
|
+
{ Property: "targetChainId", Value: solanaChainId.toString() },
|
|
103
|
+
{ Property: "hubPoolAddress", Value: hubPool.address },
|
|
104
|
+
{ Property: "l1TokenAddress", Value: l1TokenAddress },
|
|
105
|
+
{ Property: "solanaTokenKey", Value: solanaTokenKey.toString() },
|
|
106
|
+
{ Property: "svmSpokeProgramProgramId", Value: svmSpokeProgram.programId.toString() },
|
|
107
|
+
{ Property: "providerPublicKey", Value: provider.wallet.publicKey.toString() },
|
|
108
|
+
{ Property: "netSendAmount", Value: netSendAmount.toString() },
|
|
109
|
+
]);
|
|
110
|
+
// Send executeRootBundle call from Ethereum, unless resuming a remote transaction.
|
|
111
|
+
let remoteTxHash;
|
|
112
|
+
if (!resumeRemoteTx) {
|
|
113
|
+
remoteTxHash = await executeRebalanceOnHubPool(l1TokenAddress, netSendAmount, solanaChainId);
|
|
114
|
+
}
|
|
115
|
+
else
|
|
116
|
+
remoteTxHash = resumeRemoteTx;
|
|
117
|
+
// Get Solana accounts required to receive tokens over CCTP.
|
|
118
|
+
const [statePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId);
|
|
119
|
+
const vault = (0, spl_token_1.getAssociatedTokenAddressSync)(solanaTokenKey, statePda, true);
|
|
120
|
+
const [messageTransmitterState] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter")], messageTransmitterProgram.programId);
|
|
121
|
+
const [authorityPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter_authority"), tokenMessengerMinterProgram.programId.toBuffer()], messageTransmitterProgram.programId);
|
|
122
|
+
const [tokenMessengerAccount] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_messenger")], tokenMessengerMinterProgram.programId);
|
|
123
|
+
const [remoteTokenMessengerKey] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("remote_token_messenger"), Buffer.from(remoteDomain.toString())], tokenMessengerMinterProgram.programId);
|
|
124
|
+
const [tokenMinterAccount] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_minter")], tokenMessengerMinterProgram.programId);
|
|
125
|
+
const [localToken] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("local_token"), solanaTokenKey.toBuffer()], tokenMessengerMinterProgram.programId);
|
|
126
|
+
const [tokenPair] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_pair"), Buffer.from(remoteDomain.toString()), (0, svm_1.evmAddressToPublicKey)(l1TokenAddress).toBuffer()], tokenMessengerMinterProgram.programId);
|
|
127
|
+
const [custodyTokenAccount] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("custody"), solanaTokenKey.toBuffer()], tokenMessengerMinterProgram.programId);
|
|
128
|
+
const [tokenMessengerEventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], tokenMessengerMinterProgram.programId);
|
|
129
|
+
// Fetch attestation from CCTP attestation service.
|
|
130
|
+
const attestationResponse = await (0, cctpHelpers_1.getMessages)(remoteTxHash, remoteDomain, irisApiUrl);
|
|
131
|
+
const { attestation, message } = attestationResponse.messages[0];
|
|
132
|
+
console.log("CCTP attestation response:", attestationResponse.messages[0]);
|
|
133
|
+
// Accounts in CCTP message_transmitter receive_message instruction.
|
|
134
|
+
const nonce = (0, cctpHelpers_1.decodeMessageHeader)(Buffer.from(message.replace("0x", ""), "hex")).nonce;
|
|
135
|
+
const usedNonces = (await messageTransmitterProgram.methods
|
|
136
|
+
.getNoncePda({
|
|
137
|
+
nonce: new anchor_1.BN(nonce.toString()),
|
|
138
|
+
sourceDomain: remoteDomain,
|
|
139
|
+
})
|
|
140
|
+
.accounts({
|
|
141
|
+
messageTransmitter: messageTransmitterState,
|
|
142
|
+
})
|
|
143
|
+
.view());
|
|
144
|
+
const receiveMessageAccounts = {
|
|
145
|
+
payer: provider.wallet.publicKey,
|
|
146
|
+
caller: provider.wallet.publicKey,
|
|
147
|
+
authorityPda,
|
|
148
|
+
messageTransmitter: messageTransmitterState,
|
|
149
|
+
usedNonces,
|
|
150
|
+
receiver: tokenMessengerMinterProgram.programId,
|
|
151
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
152
|
+
};
|
|
153
|
+
// accountMetas list to pass to remaining accounts when receiving token bridge message via CCTP.
|
|
154
|
+
const remainingAccounts = [];
|
|
155
|
+
remainingAccounts.push({
|
|
156
|
+
isSigner: false,
|
|
157
|
+
isWritable: false,
|
|
158
|
+
pubkey: tokenMessengerAccount,
|
|
159
|
+
});
|
|
160
|
+
remainingAccounts.push({
|
|
161
|
+
isSigner: false,
|
|
162
|
+
isWritable: false,
|
|
163
|
+
pubkey: remoteTokenMessengerKey,
|
|
164
|
+
});
|
|
165
|
+
remainingAccounts.push({
|
|
166
|
+
isSigner: false,
|
|
167
|
+
isWritable: true,
|
|
168
|
+
pubkey: tokenMinterAccount,
|
|
169
|
+
});
|
|
170
|
+
remainingAccounts.push({
|
|
171
|
+
isSigner: false,
|
|
172
|
+
isWritable: true,
|
|
173
|
+
pubkey: localToken,
|
|
174
|
+
});
|
|
175
|
+
remainingAccounts.push({
|
|
176
|
+
isSigner: false,
|
|
177
|
+
isWritable: false,
|
|
178
|
+
pubkey: tokenPair,
|
|
179
|
+
});
|
|
180
|
+
remainingAccounts.push({
|
|
181
|
+
isSigner: false,
|
|
182
|
+
isWritable: true,
|
|
183
|
+
pubkey: vault,
|
|
184
|
+
});
|
|
185
|
+
remainingAccounts.push({
|
|
186
|
+
isSigner: false,
|
|
187
|
+
isWritable: true,
|
|
188
|
+
pubkey: custodyTokenAccount,
|
|
189
|
+
});
|
|
190
|
+
remainingAccounts.push({
|
|
191
|
+
isSigner: false,
|
|
192
|
+
isWritable: false,
|
|
193
|
+
pubkey: spl_token_1.TOKEN_PROGRAM_ID,
|
|
194
|
+
});
|
|
195
|
+
remainingAccounts.push({
|
|
196
|
+
isSigner: false,
|
|
197
|
+
isWritable: false,
|
|
198
|
+
pubkey: tokenMessengerEventAuthority,
|
|
199
|
+
});
|
|
200
|
+
remainingAccounts.push({
|
|
201
|
+
isSigner: false,
|
|
202
|
+
isWritable: false,
|
|
203
|
+
pubkey: tokenMessengerMinterProgram.programId,
|
|
204
|
+
});
|
|
205
|
+
// Receive tokens on Solana.
|
|
206
|
+
console.log(`Receiving ${netSendAmount.toString()} tokens on Solana...`);
|
|
207
|
+
const receiveMessageTx = await messageTransmitterProgram.methods
|
|
208
|
+
.receiveMessage({
|
|
209
|
+
message: Buffer.from(message.replace("0x", ""), "hex"),
|
|
210
|
+
attestation: Buffer.from(attestation.replace("0x", ""), "hex"),
|
|
211
|
+
})
|
|
212
|
+
.accounts(receiveMessageAccounts)
|
|
213
|
+
.remainingAccounts(remainingAccounts)
|
|
214
|
+
.rpc();
|
|
215
|
+
console.log("\nReceived remote message");
|
|
216
|
+
console.log("Your transaction signature", receiveMessageTx);
|
|
217
|
+
}
|
|
218
|
+
async function executeRebalanceOnHubPool(l1TokenAddress, netSendAmount, solanaChainId) {
|
|
219
|
+
// Reconstruct the merkle tree for the pool rebalance.
|
|
220
|
+
const { poolRebalanceTree, poolRebalanceLeaf } = (0, poolRebalanceTree_1.constructSimpleRebalanceTree)(l1TokenAddress, netSendAmount, solanaChainId);
|
|
221
|
+
// Make sure the proposal liveness has passed, it has not been executed and rebalance root matches.
|
|
222
|
+
const currentRootBundleProposal = await hubPool.connect(ethersSigner).callStatic.rootBundleProposal();
|
|
223
|
+
if (currentRootBundleProposal.challengePeriodEndTimestamp > (await hubPool.callStatic.getCurrentTime()).toNumber())
|
|
224
|
+
throw new Error("Not passed liveness");
|
|
225
|
+
if (!currentRootBundleProposal.claimedBitMap.isZero())
|
|
226
|
+
throw new Error("Already claimed");
|
|
227
|
+
if (currentRootBundleProposal.poolRebalanceRoot !== poolRebalanceTree.getHexRoot())
|
|
228
|
+
throw new Error("Rebalance root mismatch");
|
|
229
|
+
// Execute the rebalance bundle on the HubPool.
|
|
230
|
+
console.log(`Executing ${netSendAmount.toString()} rebalance to spoke pool:`);
|
|
231
|
+
const tx = await hubPool
|
|
232
|
+
.connect(ethersSigner)
|
|
233
|
+
.executeRootBundle(poolRebalanceLeaf.chainId, poolRebalanceLeaf.groupIndex, poolRebalanceLeaf.bundleLpFees, poolRebalanceLeaf.netSendAmounts, poolRebalanceLeaf.runningBalances, poolRebalanceLeaf.leafId, poolRebalanceLeaf.l1Tokens, poolRebalanceTree.getHexProof(poolRebalanceLeaf));
|
|
234
|
+
console.log(`✔️ submitted tx hash: ${tx.hash}`);
|
|
235
|
+
await tx.wait();
|
|
236
|
+
console.log(`✔️ tx confirmed`);
|
|
237
|
+
return tx.hash;
|
|
238
|
+
}
|
|
239
|
+
// Run the executeRebalanceToSpokePool function
|
|
240
|
+
executeRebalanceToSpokePool();
|
|
@@ -34,8 +34,7 @@ const web3_js_1 = require("@solana/web3.js");
|
|
|
34
34
|
const spl_token_1 = require("@solana/spl-token");
|
|
35
35
|
const yargs_1 = __importDefault(require("yargs"));
|
|
36
36
|
const helpers_1 = require("yargs/helpers");
|
|
37
|
-
const
|
|
38
|
-
const utils_1 = require("../../test/svm/utils");
|
|
37
|
+
const svm_1 = require("../../src/svm");
|
|
39
38
|
// Set up the provider and signer.
|
|
40
39
|
const provider = anchor_1.AnchorProvider.env();
|
|
41
40
|
anchor.setProvider(provider);
|
|
@@ -98,9 +97,9 @@ async function fillV3RelayToRandom() {
|
|
|
98
97
|
transferInstructions.push(transferInstruction);
|
|
99
98
|
}
|
|
100
99
|
// Encode handler message for the token distribution.
|
|
101
|
-
const multicallHandlerCoder = new
|
|
100
|
+
const multicallHandlerCoder = new svm_1.MulticallHandlerCoder(transferInstructions);
|
|
102
101
|
const handlerMessage = multicallHandlerCoder.encode();
|
|
103
|
-
const message = new
|
|
102
|
+
const message = new svm_1.AcrossPlusMessageCoder({
|
|
104
103
|
handler,
|
|
105
104
|
readOnlyLen: multicallHandlerCoder.readOnlyLen,
|
|
106
105
|
valueAmount: new anchor_1.BN(0),
|
|
@@ -128,7 +127,7 @@ async function fillV3RelayToRandom() {
|
|
|
128
127
|
// Fetch the state from the on-chain program to get chainId
|
|
129
128
|
const state = await program.account.state.fetch(statePda);
|
|
130
129
|
const chainId = new anchor_1.BN(state.chainId);
|
|
131
|
-
const relayHashUint8Array = (0,
|
|
130
|
+
const relayHashUint8Array = (0, svm_1.calculateRelayHashUint8Array)(relayData, chainId);
|
|
132
131
|
const relayHash = Array.from(relayHashUint8Array);
|
|
133
132
|
// Define the fill status account PDA
|
|
134
133
|
const [fillStatusPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fills"), relayHashUint8Array], programId);
|
|
@@ -155,7 +154,7 @@ async function fillV3RelayToRandom() {
|
|
|
155
154
|
// Prepare fill instruction as we will need to use Address Lookup Table (ALT).
|
|
156
155
|
const fillV3RelayValues = [relayHash, relayData, repaymentChain, repaymentAddress];
|
|
157
156
|
if (bufferParams) {
|
|
158
|
-
await (0,
|
|
157
|
+
await (0, svm_1.loadFillV3RelayParams)(program, signer, fillV3RelayValues[1], fillV3RelayValues[2], fillV3RelayValues[3]);
|
|
159
158
|
}
|
|
160
159
|
const fillV3RelayParams = bufferParams ? [fillV3RelayValues[0], null, null, null] : fillV3RelayValues;
|
|
161
160
|
const [instructionParams] = bufferParams
|
|
@@ -184,7 +183,7 @@ async function fillV3RelayToRandom() {
|
|
|
184
183
|
.remainingAccounts(remainingAccounts)
|
|
185
184
|
.instruction();
|
|
186
185
|
// Fill using the ALT.
|
|
187
|
-
const { txSignature } = await (0,
|
|
186
|
+
const { txSignature } = await (0, svm_1.sendTransactionWithLookupTable)(provider.connection, [approveInstruction, fillInstruction], signer);
|
|
188
187
|
console.log("Transaction signature:", txSignature);
|
|
189
188
|
}
|
|
190
189
|
// Run the fillV3RelayToRandom function
|
|
@@ -32,7 +32,7 @@ const anchor_1 = require("@coral-xyz/anchor");
|
|
|
32
32
|
const web3_js_1 = require("@solana/web3.js");
|
|
33
33
|
const yargs_1 = __importDefault(require("yargs"));
|
|
34
34
|
const helpers_1 = require("yargs/helpers");
|
|
35
|
-
const
|
|
35
|
+
const svm_1 = require("../../src/svm");
|
|
36
36
|
// Set up the provider
|
|
37
37
|
const provider = anchor_1.AnchorProvider.env();
|
|
38
38
|
anchor.setProvider(provider);
|
|
@@ -64,7 +64,7 @@ async function initialize() {
|
|
|
64
64
|
const initialNumberOfDeposits = new anchor_1.BN(resolvedArgv.initNumbDeposits);
|
|
65
65
|
const chainId = new anchor_1.BN(resolvedArgv.chainId);
|
|
66
66
|
const remoteDomain = resolvedArgv.remoteDomain;
|
|
67
|
-
const crossDomainAdmin = (0,
|
|
67
|
+
const crossDomainAdmin = (0, svm_1.evmAddressToPublicKey)(resolvedArgv.crossDomainAdmin); // Use the function to cast the value
|
|
68
68
|
const depositQuoteTimeBuffer = resolvedArgv.depositQuoteTimeBuffer;
|
|
69
69
|
const fillDeadlineBuffer = resolvedArgv.fillDeadlineBuffer;
|
|
70
70
|
// Define the state account PDA
|