-
Notifications
You must be signed in to change notification settings - Fork 289
/
Copy pathexec.js
231 lines (207 loc) Β· 8.39 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
const hre = require('hardhat')
const ethers = require('ethers')
const { providers, Wallet } = require('ethers')
const { BigNumber } = require('@ethersproject/bignumber')
const {
arbLog,
requireEnvVariables,
addCustomNetworkFromFile,
} = require('arb-shared-dependencies')
const {
ParentToChildMessageGasEstimator,
ParentTransactionReceipt,
ParentToChildMessageStatus,
EthBridger,
getArbitrumNetwork,
} = require('@arbitrum/sdk')
const { getBaseFee } = require('@arbitrum/sdk/dist/lib/utils/lib')
require('dotenv').config()
requireEnvVariables(['PRIVATE_KEY', 'CHAIN_RPC', 'PARENT_CHAIN_RPC'])
/**
* 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 main = async () => {
await arbLog('Cross-chain Greeter')
/**
* 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 GreeterParent to the parent chain, GreeterChild to the child chain, each with a different "greeting" message.
* After deploying, save each contract's counterparty's address to its state so that they can later talk to each other.
*/
const GreeterParent = (
await hre.ethers.getContractFactory('GreeterParent')
).connect(parentChainWallet)
console.log('Deploying GreeterParent to the parent chain π')
const greeterParent = await GreeterParent.deploy(
'Hello world in the parent chain',
ethers.constants.AddressZero, // temp child addr
inboxAddress
)
await greeterParent.deployed()
console.log(`deployed to ${greeterParent.address}`)
const GreeterChild = (
await hre.ethers.getContractFactory('GreeterChild')
).connect(childChainWallet)
console.log('Deploying GreeterChild to the child chain ππ')
const greeterChild = await GreeterChild.deploy(
'Hello world in the child chain',
ethers.constants.AddressZero // temp parent addr
)
await greeterChild.deployed()
console.log(`deployed to ${greeterChild.address}`)
const updateParentTransaction = await greeterParent.updateChildTarget(
greeterChild.address
)
await updateParentTransaction.wait()
const updateChildTransaction = await greeterChild.updateParentTarget(
greeterParent.address
)
await updateChildTransaction.wait()
console.log('Counterpart contract addresses set in both greeters π')
/**
* Let's log the child chain's greeting string
*/
const currentChildGreeting = await greeterChild.greet()
console.log(`Current child chain's greeting: "${currentChildGreeting}"`)
/**
* Here we have a new greeting message that we want to set as the greeting in the child chain; we'll be setting it by sending it as a message from the parent chain!!!
*/
console.log('Updating greeting from Parent to Child:')
const newGreeting = 'Greeting from far, far away'
/**
* Now we can query the required gas params using the estimateAll method in Arbitrum SDK
*/
const parentToChildMessageGasEstimator = new ParentToChildMessageGasEstimator(
childChainProvider
)
/**
* To be able to estimate the gas related params to our Parent-to-Child message, we need to know how many bytes of calldata out retryable ticket will require
* i.e., we need to calculate the calldata for the function being called (setGreeting())
*/
const ABI = ['function setGreeting(string _greeting)']
const iface = new ethers.utils.Interface(ABI)
const calldata = iface.encodeFunctionData('setGreeting', [newGreeting])
/**
* Users can override the estimated gas params when sending an 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 Parent-to-Child message
* (1) maxSubmissionCost: The maximum cost to be paid for submitting the transaction
* (2) gasLimit: The child chain's gas limit
* (3) deposit: The total amount to deposit on the parent chain to cover the gas and call value for the child chain's message
*/
const parentToChildMessageGasParams =
await parentToChildMessageGasEstimator.estimateAll(
{
from: await greeterParent.address,
to: await greeterChild.address,
l2CallValue: 0,
excessFeeRefundAddress: childChainWallet.address,
callValueRefundAddress: childChainWallet.address,
data: calldata,
},
await getBaseFee(parentChainProvider),
parentChainProvider,
// if provided, it will override the estimated values. Note that providing "RetryablesGasOverrides" is totally optional.
retryablesGasOverrides
)
console.log(
`Current retryable base submission price is: ${parentToChildMessageGasParams.maxSubmissionCost.toString()}`
)
/**
* For the gas price of the child chain, we simply query it from the child chain's provider
*/
const gasPriceBid = await childChainProvider.getGasPrice()
console.log(`Child chain's gas price: ${gasPriceBid.toString()}`)
console.log(
`Sending greeting to the child chain with ${parentToChildMessageGasParams.deposit.toString()} callValue for child chain's fees:`
)
const setGreetingTransaction = await greeterParent.setGreetingInChild(
newGreeting, // string memory _greeting,
parentToChildMessageGasParams.maxSubmissionCost,
parentToChildMessageGasParams.gasLimit,
gasPriceBid,
{
value: parentToChildMessageGasParams.deposit,
}
)
const setGreetingTransactionReceipt = await setGreetingTransaction.wait()
console.log(
`Greeting txn confirmed on the parent chain! π ${setGreetingTransactionReceipt.transactionHash}`
)
const parentChainTransactionReceipt = new ParentTransactionReceipt(
setGreetingTransactionReceipt
)
/**
* In principle, a single transaction can trigger any number of Parent-to-Child messages (each with its own sequencer number).
* In this case, we know our transaction triggered only one
* Here, We check if our Parent-to-Child message is redeemed on the child chain
*/
const messages = await parentChainTransactionReceipt.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 on the child chain π₯³ ${messageResult.childTxReceipt.transactionHash}`
)
} else {
throw new Error(
`Retryable ticket failed to execute on the child chain. Status: ${ParentToChildMessageStatus[status]}`
)
}
/**
* Note that during execution on the child chain, a retryable's sender address is transformed to its alias.
* Thus, when GreeterChild checks that the message came from the parent chain, we check that the sender is this alias.
* See setGreeting in GreeterChild.sol for this check.
*/
/**
* Now when we call greet again, we should see our new string on the child chain!
*/
const newGreetingChild = await greeterChild.greet()
console.log(`Updated greeting in child contract: "${newGreetingChild}" π₯³`)
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})