evm.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. import { ethers } from "ethers"
  2. import { NETWORKS } from "./networks"
  3. import { encode, Encoding, impossible, Payload, typeWidth } from "./vaa"
  4. import axios from "axios";
  5. import * as celo from "@celo-tools/celo-ethers-wrapper";
  6. import { solidityKeccak256 } from "ethers/lib/utils"
  7. import { CHAINS, CONTRACTS, Contracts, EVMChainName } from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
  8. import { BridgeImplementation__factory, Implementation__factory, NFTBridgeImplementation__factory } from "@certusone/wormhole-sdk/lib/cjs/ethers-contracts";
  9. const _IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
  10. export async function query_contract_evm(
  11. network: "MAINNET" | "TESTNET" | "DEVNET",
  12. chain: EVMChainName,
  13. module: "Core" | "NFTBridge" | "TokenBridge",
  14. contract_address: string | undefined,
  15. _rpc: string | undefined
  16. ): Promise<object> {
  17. let n = NETWORKS[network][chain]
  18. let rpc: string | undefined = _rpc ?? n.rpc;
  19. if (rpc === undefined) {
  20. throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`)
  21. }
  22. let contracts: Contracts = CONTRACTS[network][chain]
  23. const provider = new ethers.providers.JsonRpcProvider(rpc)
  24. let result: any = {}
  25. switch (module) {
  26. case "Core":
  27. contract_address = contract_address ? contract_address : contracts.core;
  28. if (contract_address === undefined) {
  29. throw Error(`Unknown core contract on ${network} for ${chain}`)
  30. }
  31. const core = Implementation__factory.connect(contract_address, provider)
  32. result.address = contract_address
  33. result.currentGuardianSetIndex = await core.getCurrentGuardianSetIndex()
  34. let guardianSetsPromise = Promise.all([...Array(result.currentGuardianSetIndex + 1).keys()].map((i) => core.getGuardianSet(i)))
  35. let [
  36. guardianSetExpiry,
  37. chainId,
  38. evmChainId,
  39. isFork,
  40. governanceChainId,
  41. governanceContract,
  42. messageFee,
  43. implementationSlot,
  44. guardianSets
  45. ] = await Promise.all([
  46. core.getGuardianSetExpiry(),
  47. core.chainId(),
  48. maybeUnsupported(core.evmChainId()),
  49. maybeUnsupported(core.isFork()),
  50. core.governanceChainId(),
  51. core.governanceContract(),
  52. core.messageFee(),
  53. getStorageAt(rpc, contract_address, _IMPLEMENTATION_SLOT, ["address"]),
  54. guardianSetsPromise
  55. ])
  56. result.guardianSetExpiry = guardianSetExpiry
  57. result.chainId = chainId
  58. result.evmChainId = evmChainId.toString()
  59. result.isFork = isFork
  60. result.governanceChainId = governanceChainId
  61. result.governanceContract = governanceContract
  62. result.messageFee = messageFee
  63. result.implementation = implementationSlot[0]
  64. result.isInitialized = await core.isInitialized(result.implementation)
  65. result.guardianSet = {}
  66. for (let [i, guardianSet] of guardianSets.entries()) {
  67. result.guardianSet[i] = { keys: guardianSet[0], expiry: guardianSet[1] }
  68. }
  69. break
  70. case "TokenBridge":
  71. contract_address = contract_address ? contract_address : contracts.token_bridge;
  72. if (contract_address === undefined) {
  73. throw Error(`Unknown token bridge contract on ${network} for ${chain}`)
  74. }
  75. const tb = BridgeImplementation__factory.connect(contract_address, provider)
  76. result.address = contract_address
  77. const registrationsPromise = Promise.all(
  78. Object.entries(CHAINS)
  79. .filter(([c_name, _]) => c_name !== chain && c_name !== "unset")
  80. .map(async ([c_name, c_id]) => [c_name, await tb.bridgeContracts(c_id)])
  81. )
  82. let [
  83. wormhole,
  84. implementationSlotTb,
  85. tokenImplementation,
  86. chainIdTb,
  87. finality,
  88. evmChainIdTb,
  89. isForkTb,
  90. governanceChainIdTb,
  91. governanceContractTb,
  92. WETH,
  93. registrations
  94. ] = await Promise.all([
  95. tb.wormhole(),
  96. getStorageAt(rpc, contract_address, _IMPLEMENTATION_SLOT, ["address"]),
  97. tb.tokenImplementation(),
  98. tb.chainId(),
  99. tb.finality(),
  100. maybeUnsupported(tb.evmChainId()),
  101. maybeUnsupported(tb.isFork()),
  102. tb.governanceChainId(),
  103. tb.governanceContract(),
  104. tb.WETH(),
  105. registrationsPromise
  106. ])
  107. result.wormhole = wormhole
  108. result.implementation = implementationSlotTb[0]
  109. result.isInitialized = await tb.isInitialized(result.implementation)
  110. result.tokenImplementation = tokenImplementation
  111. result.chainId = chainIdTb
  112. result.finality = finality
  113. result.evmChainId = evmChainIdTb.toString()
  114. result.isFork = isForkTb
  115. result.governanceChainId = governanceChainIdTb
  116. result.governanceContract = governanceContractTb
  117. result.WETH = WETH
  118. result.registrations = {}
  119. for (let [c_name, c] of registrations) {
  120. result.registrations[c_name] = c
  121. }
  122. break
  123. case "NFTBridge":
  124. contract_address = contract_address ? contract_address : contracts.nft_bridge;
  125. if (contract_address === undefined) {
  126. throw Error(`Unknown nft bridge contract on ${network} for ${chain}`)
  127. }
  128. const nb = NFTBridgeImplementation__factory.connect(contract_address, provider)
  129. result.address = contract_address
  130. const registrationsPromiseNb = Promise.all(
  131. Object.entries(CHAINS)
  132. .filter(([c_name, _]) => c_name !== chain && c_name !== "unset")
  133. .map(async ([c_name, c_id]) => [c_name, await nb.bridgeContracts(c_id)])
  134. )
  135. let [
  136. wormholeNb,
  137. implementationSlotNb,
  138. tokenImplementationNb,
  139. chainIdNb,
  140. finalityNb,
  141. evmChainIdNb,
  142. isForkNb,
  143. governanceChainIdNb,
  144. governanceContractNb,
  145. registrationsNb
  146. ] = await Promise.all([
  147. nb.wormhole(),
  148. getStorageAt(rpc, contract_address, _IMPLEMENTATION_SLOT, ["address"]),
  149. nb.tokenImplementation(),
  150. nb.chainId(),
  151. nb.finality(),
  152. maybeUnsupported(nb.evmChainId()),
  153. maybeUnsupported(nb.isFork()),
  154. nb.governanceChainId(),
  155. nb.governanceContract(),
  156. registrationsPromiseNb
  157. ])
  158. result.wormhole = wormholeNb
  159. result.implementation = implementationSlotNb[0]
  160. result.isInitialized = await nb.isInitialized(result.implementation)
  161. result.tokenImplementation = tokenImplementationNb
  162. result.chainId = chainIdNb
  163. result.finality = finalityNb
  164. result.evmChainId = evmChainIdNb.toString()
  165. result.isFork = isForkNb
  166. result.governanceChainId = governanceChainIdNb
  167. result.governanceContract = governanceContractNb
  168. result.registrations = {}
  169. for (let [c_name, c] of registrationsNb) {
  170. result.registrations[c_name] = c
  171. }
  172. break
  173. default:
  174. impossible(module)
  175. }
  176. return result
  177. }
  178. export async function getImplementation(
  179. network: "MAINNET" | "TESTNET" | "DEVNET",
  180. chain: EVMChainName,
  181. module: "Core" | "NFTBridge" | "TokenBridge",
  182. contract_address: string | undefined,
  183. _rpc: string | undefined
  184. ): Promise<ethers.BigNumber> {
  185. let n = NETWORKS[network][chain]
  186. let rpc: string | undefined = _rpc ?? n.rpc;
  187. if (rpc === undefined) {
  188. throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`)
  189. }
  190. let contracts: Contracts = CONTRACTS[network][chain]
  191. switch (module) {
  192. case "Core":
  193. contract_address = contract_address ? contract_address : contracts.core;
  194. break
  195. case "TokenBridge":
  196. contract_address = contract_address ? contract_address : contracts.token_bridge;
  197. break
  198. case "NFTBridge":
  199. contract_address = contract_address ? contract_address : contracts.nft_bridge;
  200. break
  201. default:
  202. impossible(module)
  203. }
  204. return (await getStorageAt(rpc, contract_address, _IMPLEMENTATION_SLOT, ["address"]))[0]
  205. }
  206. export async function execute_evm(
  207. payload: Payload,
  208. vaa: Buffer,
  209. network: "MAINNET" | "TESTNET" | "DEVNET",
  210. chain: EVMChainName,
  211. contract_address: string | undefined,
  212. _rpc: string | undefined
  213. ) {
  214. let n = NETWORKS[network][chain]
  215. let rpc: string | undefined = _rpc ?? n.rpc;
  216. if (rpc === undefined) {
  217. throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`)
  218. }
  219. if (!n.key) {
  220. throw Error(`No ${network} key defined for ${chain} (see networks.ts)`)
  221. }
  222. let key: string = n.key
  223. let contracts: Contracts = CONTRACTS[network][chain]
  224. let provider: ethers.providers.JsonRpcProvider;
  225. let signer: ethers.Wallet;
  226. if (chain === "celo") {
  227. provider = new celo.CeloProvider(rpc)
  228. await provider.ready
  229. signer = new celo.CeloWallet(key, provider)
  230. } else {
  231. provider = new ethers.providers.JsonRpcProvider(rpc)
  232. signer = new ethers.Wallet(key, provider)
  233. }
  234. // Here we apply a set of chain-specific overrides.
  235. // NOTE: some of these might have only been tested on mainnet. If it fails in
  236. // testnet (or devnet), they might require additional guards
  237. let overrides: ethers.Overrides = {}
  238. if (chain === "karura" || chain == "acala") {
  239. overrides = await getKaruraGasParams(n.rpc)
  240. } else if (chain === "polygon") {
  241. let feeData = await provider.getFeeData();
  242. overrides = {
  243. maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
  244. maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
  245. };
  246. } else if (chain === "klaytn" || chain === "fantom") {
  247. overrides = { gasPrice: (await signer.getGasPrice()).toString() }
  248. }
  249. switch (payload.module) {
  250. case "Core":
  251. contract_address = contract_address ? contract_address : contracts.core;
  252. if (contract_address === undefined) {
  253. throw Error(`Unknown core contract on ${network} for ${chain}`)
  254. }
  255. let c = new Implementation__factory(signer)
  256. let cb = c.attach(contract_address)
  257. switch (payload.type) {
  258. case "GuardianSetUpgrade":
  259. console.log("Submitting new guardian set")
  260. console.log("Hash: " + (await cb.submitNewGuardianSet(vaa, overrides)).hash)
  261. break
  262. case "ContractUpgrade":
  263. console.log("Upgrading core contract")
  264. console.log("Hash: " + (await cb.submitContractUpgrade(vaa, overrides)).hash)
  265. break
  266. default:
  267. impossible(payload)
  268. }
  269. break
  270. case "NFTBridge":
  271. contract_address = contract_address ? contract_address : contracts.nft_bridge;
  272. if (contract_address === undefined) {
  273. throw Error(`Unknown nft bridge contract on ${network} for ${chain}`)
  274. }
  275. let n = new NFTBridgeImplementation__factory(signer)
  276. let nb = n.attach(contract_address)
  277. switch (payload.type) {
  278. case "ContractUpgrade":
  279. console.log("Upgrading contract")
  280. console.log("Hash: " + (await nb.upgrade(vaa, overrides)).hash)
  281. console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
  282. break
  283. case "RegisterChain":
  284. console.log("Registering chain")
  285. console.log("Hash: " + (await nb.registerChain(vaa, overrides)).hash)
  286. break
  287. case "Transfer":
  288. console.log("Completing transfer")
  289. console.log("Hash: " + (await nb.completeTransfer(vaa, overrides)).hash)
  290. break
  291. default:
  292. impossible(payload)
  293. }
  294. break
  295. case "TokenBridge":
  296. contract_address = contract_address ? contract_address : contracts.token_bridge;
  297. if (contract_address === undefined) {
  298. throw Error(`Unknown token bridge contract on ${network} for ${chain}`)
  299. }
  300. let t = new BridgeImplementation__factory(signer)
  301. let tb = t.attach(contract_address)
  302. switch (payload.type) {
  303. case "ContractUpgrade":
  304. console.log("Upgrading contract")
  305. console.log("Hash: " + (await tb.upgrade(vaa, overrides)).hash)
  306. console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
  307. break
  308. case "RegisterChain":
  309. console.log("Registering chain")
  310. console.log("Hash: " + (await tb.registerChain(vaa, overrides)).hash)
  311. break
  312. case "Transfer":
  313. console.log("Completing transfer")
  314. console.log("Hash: " + (await tb.completeTransfer(vaa, overrides)).hash)
  315. break
  316. case "AttestMeta":
  317. console.log("Creating wrapped token")
  318. console.log("Hash: " + (await tb.createWrapped(vaa, overrides)).hash)
  319. break
  320. case "TransferWithPayload":
  321. console.log("Completing transfer with payload")
  322. console.log("Hash: " + (await tb.completeTransferWithPayload(vaa, overrides)).hash)
  323. break
  324. default:
  325. impossible(payload)
  326. break
  327. }
  328. break
  329. default:
  330. impossible(payload)
  331. }
  332. }
  333. /**
  334. *
  335. * Hijack a core contract. This function is useful when working with a mainnet
  336. * fork (hardhat or anvil). A fork of the mainnet contract will naturally store
  337. * the mainnet guardian set, so we can't readily interact with these contracts,
  338. * because we can't forge signed VAAs for those guardians. This function uses
  339. * [[setStorageAt]] to override the guardian set to something we have the
  340. * private keys for (typically the devnet guardian used for testing).
  341. * This way we can test contract upgrades before rolling them out on mainnet.
  342. *
  343. * @param rpc the JSON RPC endpoint (needs to be hardhat of anvil)
  344. * @param contract_address address of the core bridge contract
  345. * @param guardian_addresses addresses of the desired guardian set to upgrade to
  346. * @param new_guardian_set_index if specified, the new guardian set will be
  347. * written into this guardian set index, and the guardian set index of the
  348. * contract changed to it.
  349. * If unspecified, then the current guardian set index will be overridden.
  350. * In particular, it's possible to both upgrade or downgrade the guardian set
  351. * this way. The latter is useful for testing locally if you already have some
  352. * VAAs handy that are signed by guardian set 0.
  353. */
  354. export async function hijack_evm(
  355. rpc: string,
  356. contract_address: string,
  357. guardian_addresses: string[],
  358. new_guardian_set_index: number | undefined
  359. ): Promise<void> {
  360. const GUARDIAN_SETS_SLOT = 0x02
  361. const GUARDIAN_SET_INDEX_SLOT = 0x3
  362. const provider = new ethers.providers.JsonRpcProvider(rpc)
  363. const core = Implementation__factory.connect(contract_address, provider)
  364. let guardianSetIndex: number
  365. let guardianSetExpiry: number
  366. [guardianSetIndex, guardianSetExpiry] = await getStorageAt(rpc, contract_address, GUARDIAN_SET_INDEX_SLOT, ["uint32", "uint32"])
  367. console.log("Attempting to hijack core bridge guardian set.")
  368. const current_set = await core.getGuardianSet(guardianSetIndex)
  369. console.log(`Current guardian set (index ${guardianSetIndex}):`)
  370. console.log(current_set[0])
  371. if (new_guardian_set_index !== undefined) {
  372. await setStorageAt(rpc, contract_address, GUARDIAN_SET_INDEX_SLOT, ["uint32", "uint32"], [new_guardian_set_index, guardianSetExpiry])
  373. guardianSetIndex = await core.getCurrentGuardianSetIndex()
  374. if (new_guardian_set_index !== guardianSetIndex) {
  375. throw Error("Failed to update guardian set index.")
  376. } else {
  377. console.log(`Guardian set index updated to ${new_guardian_set_index}`)
  378. }
  379. }
  380. const addresses_slot = computeMappingElemSlot(GUARDIAN_SETS_SLOT, guardianSetIndex)
  381. console.log(`Writing new set of guardians into set ${guardianSetIndex}...`)
  382. guardian_addresses.forEach(async (address, i) => {
  383. await setStorageAt(rpc, contract_address, computeArrayElemSlot(addresses_slot, i), ["address"], [address])
  384. })
  385. await setStorageAt(rpc, contract_address, addresses_slot, ["uint256"], [guardian_addresses.length])
  386. const after_guardian_set_index = await core.getCurrentGuardianSetIndex()
  387. const new_set = await core.getGuardianSet(after_guardian_set_index)
  388. console.log(`Current guardian set (index ${after_guardian_set_index}):`)
  389. console.log(new_set[0])
  390. console.log("Success.")
  391. }
  392. async function getKaruraGasParams(rpc: string): Promise<{
  393. gasPrice: number;
  394. gasLimit: number;
  395. }> {
  396. const gasLimit = 21000000;
  397. const storageLimit = 64001;
  398. const res = (
  399. await axios.post(rpc, {
  400. id: 0,
  401. jsonrpc: "2.0",
  402. method: "eth_getEthGas",
  403. params: [
  404. {
  405. gasLimit,
  406. storageLimit,
  407. },
  408. ],
  409. })
  410. ).data.result;
  411. return {
  412. gasLimit: parseInt(res.gasLimit, 16),
  413. gasPrice: parseInt(res.gasPrice, 16),
  414. };
  415. }
  416. ////////////////////////////////////////////////////////////////////////////////
  417. // Storage manipulation
  418. //
  419. // Below we define a set of utilities for working with the EVM storage. For
  420. // reference on storage layout, see [1].
  421. //
  422. // [1]: https://docs.soliditylang.org/en/v0.8.14/internals/layout_in_storage.html
  423. export type StorageSlot = ethers.BigNumber
  424. // we're a little more permissive in contravariant positions...
  425. export type StorageSlotish = ethers.BigNumberish
  426. /**
  427. *
  428. * Compute the storage slot of an array element.
  429. *
  430. * @param array_slot the storage slot of the array variable
  431. * @param offset the index of the element to compute the storage slot for
  432. */
  433. export function computeArrayElemSlot(array_slot: StorageSlotish, offset: number): StorageSlot {
  434. return ethers.BigNumber.from(solidityKeccak256(["bytes"], [array_slot])).add(offset)
  435. }
  436. /**
  437. *
  438. * Compute the storage slot of a mapping key.
  439. *
  440. * @param map_slot the storage slot of the mapping variable
  441. * @param key the key to compute the storage slot for
  442. */
  443. export function computeMappingElemSlot(map_slot: StorageSlotish, key: any): StorageSlot {
  444. const slot_preimage = ethers.utils.defaultAbiCoder.encode(["uint256", "uint256"], [key, map_slot])
  445. return ethers.BigNumber.from(solidityKeccak256(["bytes"], [slot_preimage]))
  446. }
  447. /**
  448. *
  449. * Get the values stored in a storage slot. [[ethers.Provider.getStorageAt]]
  450. * returns the whole slot as one 32 byte value, but if there are multiple values
  451. * stored in the slot (which solidity does to save gas), it is useful to parse
  452. * the output accordingly. This function is a wrapper around the storage query
  453. * provided by [[ethers]] that does the additional parsing.
  454. *
  455. * @param rpc the JSON RPC endpoint
  456. * @param contract_address address of the contract to be queried
  457. * @param storage_slot the storage slot to query
  458. * @param types The types of values stored in the storage slot. It's a list,
  459. * because solidity packs multiple values into a single storage slot to save gas
  460. * when the elements fit.
  461. *
  462. * @returns _values the values to write into the slot (packed)
  463. */
  464. async function getStorageAt(rpc: string, contract_address: string, storage_slot: StorageSlotish, types: Encoding[]): Promise<any[]> {
  465. const total = types.map((typ) => typeWidth(typ)).reduce((x, y) => (x + y))
  466. if (total > 32) {
  467. throw new Error(`Storage slots can contain a maximum of 32 bytes. Total size of ${types} is ${total} bytes.`)
  468. }
  469. const string_val: string =
  470. await (new ethers.providers.JsonRpcProvider(rpc).getStorageAt(contract_address, storage_slot))
  471. let val = ethers.BigNumber.from(string_val)
  472. let ret: any[] = []
  473. // we decode the elements one by one, by shifting down the stuff we've parsed already
  474. types.forEach((typ) => {
  475. const padded = ethers.utils.defaultAbiCoder.encode(["uint256"], [val])
  476. ret.push(ethers.utils.defaultAbiCoder.decode([typ], padded)[0])
  477. val = val.shr(typeWidth(typ) * 8)
  478. })
  479. return ret
  480. }
  481. /**
  482. *
  483. * Use the 'hardhat_setStorageAt' rpc method to override a storage slot of a
  484. * contract. This method is understood by both hardhat and anvil (from foundry).
  485. * Useful for manipulating the storage of a forked mainnet contract (such as for
  486. * changing the guardian set to allow submitting VAAs to).
  487. *
  488. * @param rpc the JSON RPC endpoint (needs to be hardhat of anvil)
  489. * @param contract_address address of the contract to be queried
  490. * @param storage_slot the storage slot to query
  491. * @param types The types of values stored in the storage slot. It's a list,
  492. * because solidity packs multiple values into a single storage slot to save gas
  493. * when the elements fit. This means that when writing into the slot, all values
  494. * must be accounted for, otherwise we end up zeroing out some fields.
  495. * @param values the values to write into the slot (packed)
  496. *
  497. * @returns the `data` property of the JSON response
  498. */
  499. export async function setStorageAt(rpc: string, contract_address: string, storage_slot: StorageSlotish, types: Encoding[], values: any[]): Promise<any> {
  500. // we need to reverse the values and types arrays, because the first element
  501. // is stored at the rightmost bytes.
  502. //
  503. // for example:
  504. // uint32 a
  505. // uint32 b
  506. // will be stored as 0x...b...a
  507. const _values = values.reverse()
  508. const _types = types.reverse()
  509. const total = _types.map((typ) => typeWidth(typ)).reduce((x, y) => (x + y))
  510. // ensure that the types fit into a slot
  511. if (total > 32) {
  512. throw new Error(`Storage slots can contain a maximum of 32 bytes. Total size of ${_types} is ${total} bytes.`)
  513. }
  514. if (_types.length !== _values.length) {
  515. throw new Error(`Expected ${_types.length} value(s), but got ${_values.length}.`)
  516. }
  517. // as far as I could tell, `ethers` doesn't provide a way to pack multiple
  518. // values into a single slot (the abi coder pads everything to 32 bytes), so we do it ourselves
  519. const val = "0x" + _types.map((typ, i) => encode(typ, _values[i])).reduce((x, y) => x + y).padStart(64, "0")
  520. // format the storage slot
  521. const slot = ethers.utils.defaultAbiCoder.encode(["uint256"], [storage_slot])
  522. console.log(`slot ${slot} := ${val}`)
  523. return (await axios.post(rpc, {
  524. id: 0,
  525. jsonrpc: "2.0",
  526. method: "hardhat_setStorageAt",
  527. params: [
  528. contract_address,
  529. slot,
  530. val,
  531. ],
  532. })).data
  533. }
  534. async function maybeUnsupported<T>(query: Promise<T>): Promise<T> {
  535. try {
  536. return await query
  537. } catch (e) {
  538. if (e.reason === "unsupported") {
  539. return e.reason
  540. }
  541. throw e
  542. }
  543. }