-
Notifications
You must be signed in to change notification settings - Fork 292
/
Copy pathexec.js
251 lines (223 loc) · 9.17 KB
/
exec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
const { providers, Wallet } = require('ethers');
const { BigNumber } = require('@ethersproject/bignumber');
const hre = require('hardhat');
const ethers = require('ethers');
const {
arbLog,
requireEnvVariables,
addCustomNetworkFromFile,
} = require('arb-shared-dependencies');
const {
EthBridger,
Address,
EthDepositStatus,
ParentToChildMessageGasEstimator,
ParentTransactionReceipt,
ParentToChildMessageStatus,
getArbitrumNetwork,
ParentEthDepositTransactionReceipt,
} = require('@arbitrum/sdk');
const { getBaseFee } = require('@arbitrum/sdk/dist/lib/utils/lib');
require('dotenv').config();
requireEnvVariables(['PRIVATE_KEY', 'CHAIN_RPC', 'PARENT_CHAIN_RPC', 'TransferTo']);
/**
* Set up: instantiate wallets connected to providers
*/
const walletPrivateKey = process.env.PRIVATE_KEY;
const parentChainProvider = new providers.JsonRpcProvider(process.env.PARENT_CHAIN_RPC);
const childChainProvider = new providers.JsonRpcProvider(process.env.CHAIN_RPC);
const parentChainWallet = new Wallet(walletPrivateKey, parentChainProvider);
const childChainWallet = new Wallet(walletPrivateKey, childChainProvider);
const transferTo = process.env.TransferTo;
const main = async () => {
await arbLog('Contract Cross-chain depositer');
/**
* Add the custom network configuration to the SDK if present
*/
addCustomNetworkFromFile();
/**
* Use childChainNetwork to create an Arbitrum SDK EthBridger instance
* We'll use EthBridger to retrieve the Inbox address
*/
const childChainNetwork = await getArbitrumNetwork(childChainProvider);
const ethBridger = new EthBridger(childChainNetwork);
const inboxAddress = ethBridger.childNetwork.ethBridge.inbox;
/**
* We deploy EthDeposit contract to the parent chain first and send eth to
* the child chain via this contract.
* Funds will deposit to the contract's alias address first.
*/
const DepositContract = await (
await hre.ethers.getContractFactory('EthDeposit')
).connect(parentChainWallet);
console.log('Deploying EthDeposit contract...');
const depositContract = await DepositContract.deploy(inboxAddress);
await depositContract.deployed();
console.log(`deployed to ${depositContract.address}`);
/**
* This sdk class will help we to get the alias address of the contract
*/
const contractAddress = new Address(depositContract.address);
const contractAliasAddress = contractAddress.applyAlias().value;
console.log(`Sending deposit transaction...`);
const ethDepositTx = await depositContract.depositToChildChain({
value: ethers.utils.parseEther('0.01'),
});
const ethDepositRec = await ethDepositTx.wait();
console.log(`Deposit txn confirmed on the parent chain! 🙌 ${ethDepositRec.transactionHash}`);
console.log(
'Waiting for the execution of the deposit in the child chain. This may take up to 10-15 minutes ⏰',
);
const parentChainDepositTxReceipt = new ParentEthDepositTransactionReceipt(ethDepositRec);
const childChainDepositResult = await parentChainDepositTxReceipt.waitForChildTransactionReceipt(
childChainProvider,
);
/**
* If deposit success, check the alias address' balance.
* If deposit failed, throw an error.
*/
if (childChainDepositResult.complete) {
console.log(
`Deposit to the child chain is complete, the tx hash (in the child chain) is ${childChainDepositResult.childTxReceipt.transactionHash}`,
);
const beforeAliasBalance = await childChainProvider.getBalance(contractAliasAddress);
console.log(
`The balance on the alias address in the child chain before transfer: "${ethers.utils.formatEther(
beforeAliasBalance,
)} ethers"`,
);
} else {
throw new Error(
`Deposit to the child chain failed, EthDepositStatus is ${
EthDepositStatus[childChainDepositResult.message.status]
}`,
);
}
console.log('Creating retryable ticket to send txn to the child chain to transfer funds...');
/**
* Now we can query the required gas params using the estimateAll method in Arbitrum SDK
*/
const parentToChildMessageGasEstimate = new ParentToChildMessageGasEstimator(childChainProvider);
/**
* Users can override the estimated gas params when sending a parent-to-child message
* Note that this is totally optional
* Here we include and example for how to provide these overriding values
*/
const RetryablesGasOverrides = {
gasLimit: {
base: undefined, // when undefined, the value will be estimated from rpc
min: BigNumber.from(10000), // set a minimum gas limit, using 10000 as an example
percentIncrease: BigNumber.from(30), // how much to increase the base for buffer
},
maxSubmissionFee: {
base: undefined,
percentIncrease: BigNumber.from(30),
},
maxFeePerGas: {
base: undefined,
percentIncrease: BigNumber.from(30),
},
};
/**
* The estimateAll method gives us the following values for sending a ParentToChild message
* (1) maxSubmissionCost: The maximum cost to be paid for submitting the transaction
* (2) gasLimit: The gas limit for execution in the child chain
* (3) deposit: The total amount to deposit on the parent chain to cover gas and call value on the child chain
*/
const parentToChildMessageGasParams = await parentToChildMessageGasEstimate.estimateAll(
{
from: contractAliasAddress,
to: transferTo,
l2CallValue: ethers.utils.parseEther('0.01'), // because we deposited 0.01 ether, so we also transfer 0.01 ether out here.
excessFeeRefundAddress: depositContract.address,
callValueRefundAddress: depositContract.address,
data: [],
},
await getBaseFee(parentChainProvider),
parentChainProvider,
RetryablesGasOverrides, //if provided, it will override the estimated values. Note that providing "RetryablesGasOverrides" is totally optional.
);
console.log(
`Current retryable base submission price is: ${ethers.utils.formatEther(
parentToChildMessageGasParams.maxSubmissionCost.toString(),
)} ethers`,
);
/**
* For the gas price in the child chain, we simply query it from the child chain's provider, as we would when using the parent chain
*/
const gasPriceBid = await childChainProvider.getGasPrice();
console.log(
`Child chain's gas price: ${ethers.utils.formatUnits(gasPriceBid.toString(), 'gwei')} gwei`,
);
/**
* Because parentToChildMessageGasParams.deposit adds the l2callvalue to the estimate,
* we need to subtract it here so the transaction in the parent chain doesn't pay l2callvalue
* and instead uses the alias balance on the child chain directly.
*/
const depositAmount = parentToChildMessageGasParams.deposit.sub(ethers.utils.parseEther('0.01'));
console.log(
`Transfer funds txn needs ${ethers.utils.formatEther(
depositAmount,
)} ethers callValue for fees in the child chain`,
);
/**
* Call the contract's method to transfer the funds from the alias to the address you set
*/
const setTransferTx = await depositContract.moveFundsFromChildChainAliasToAnotherAddress(
transferTo,
ethers.utils.parseEther('0.01'), // because we deposited 0.01 ether, so we also transfer 0.01 ether out here.
parentToChildMessageGasParams.maxSubmissionCost,
parentToChildMessageGasParams.gasLimit,
gasPriceBid,
{
value: depositAmount,
},
);
const setTransferRec = await setTransferTx.wait();
console.log(
`Transfer funds txn confirmed on the parent chain! 🙌 ${setTransferRec.transactionHash}`,
);
const parentTransferTxReceipt = new ParentTransactionReceipt(setTransferRec);
/**
* In principle, a single txn on the parent chain can trigger any number of ParentToChild messages
* (each with its own sequencer number). In this case, we know our txn triggered only one
* Here, We check if our ParentToChild message is redeemed on the child chain
*/
const messages = await parentTransferTxReceipt.getParentToChildMessages(childChainWallet);
const message = messages[0];
console.log(
'Waiting for the execution of the transaction on the child chain. This may take up to 10-15 minutes ⏰',
);
const messageResult = await message.waitForStatus();
const status = messageResult.status;
if (status === ParentToChildMessageStatus.REDEEMED) {
console.log(
`Retryable ticket is executed in the child chain 🥳 ${messageResult.childTxReceipt.transactionHash}`,
);
} else {
throw new Error(
`Retryable ticket execution failed in the child chain with status ${ParentToChildMessageStatus[status]}`,
);
}
/**
* Now when we get the balance again, we should see the funds transferred to the address you set
*/
const afterAliasBalance = await childChainProvider.getBalance(contractAliasAddress);
const balanceOnTransferTo = await childChainProvider.getBalance(transferTo);
console.log(
`The current balance on the alias of the address in the child chain: "${ethers.utils.formatEther(
afterAliasBalance,
)} ethers"`,
);
console.log(
`The current balance on the address you set in the child chain: "${ethers.utils.formatEther(
balanceOnTransferTo,
)} ethers"`,
);
};
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});