StargateClient.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { ethers } from 'ethers'
  2. import { Router__factory, RouterETH, RouterETH__factory, StargateFeeLibraryV07__factory } from '../contract'
  3. import { ChainId, chainInfoMap } from '../config/chain'
  4. import polly from 'polly-js'
  5. import { newLogger } from '../utils/logger'
  6. export class StargateClient {
  7. static logger = newLogger('StargateClient')
  8. wallet: ethers.Wallet
  9. chainId: number
  10. provider: ethers.JsonRpcProvider
  11. constructor(privateKey: string, chainId: number) {
  12. this.chainId = chainId
  13. this.provider = new ethers.JsonRpcProvider(chainInfoMap[chainId].rpcUrl)
  14. this.wallet = new ethers.Wallet(privateKey, this.provider)
  15. }
  16. async getFeeInfo(fromChainId: number, toChainId: number, sendAmount: bigint) {
  17. const dummyAddress = '0x0000000000000000000000000000000000000001'
  18. const fee = StargateFeeLibraryV07__factory.connect(chainInfoMap[fromChainId].feeAddress, this.wallet)
  19. const { eqFee, eqReward, lpFee, protocolFee, lkbRemove } = await fee.getFees(
  20. 13n,
  21. 13n,
  22. toChainId,
  23. dummyAddress,
  24. sendAmount,
  25. )
  26. const finalFee = eqFee - eqReward + lpFee + protocolFee + lkbRemove
  27. console.log('fee', ethers.formatEther(finalFee))
  28. }
  29. async getL0GasCost(toChainId: number) {
  30. const dummyAddress = '0x0000000000000000000000000000000000000001'
  31. const router = Router__factory.connect(chainInfoMap[this.chainId].routerAddress, this.wallet)
  32. const [gasCost] = await router.quoteLayerZeroFee(toChainId, 1, dummyAddress, '0x', {
  33. dstGasForCall: 0n,
  34. dstNativeAmount: 0n,
  35. dstNativeAddr: dummyAddress,
  36. })
  37. return (gasCost * 120n) / 100n
  38. }
  39. async bridge(toChainId: number) {
  40. if(toChainId === ChainId.ZKSYNC) throw new Error('Unsupported.')
  41. return await polly()
  42. .waitAndRetry([1000 * 10, 1000 * 20, 1000 * 30, 1000 * 40])
  43. .executeForPromise(async info => {
  44. try {
  45. const routerEth = RouterETH__factory.connect(chainInfoMap[this.chainId].ethRouterAddress, this.wallet)
  46. const lzGasCost = await this.getL0GasCost(toChainId)
  47. const { gasCost, gasPrice, gasLimit } = await this.calculateGasCost(toChainId, routerEth, lzGasCost)
  48. let cost = gasCost
  49. let limit = gasLimit
  50. const balance = await this.provider.getBalance(this.wallet.address)
  51. // the cost is not precisely calculated, should be sufficient for common L2s
  52. if (balance < gasCost + lzGasCost) throw new Error('Insufficient balance')
  53. if (info.count > 0) {
  54. StargateClient.logger.info(`${this.wallet.address}: Retry ${info.count} times`)
  55. }
  56. const rate = BigInt(info.count) * 50n + 100n
  57. // add 20% gas cost for each retry
  58. cost = (cost * rate) / 100n
  59. limit = (limit * rate) / 100n
  60. const sendAmount = balance - lzGasCost - cost
  61. // const sendAmount = ethers.parseEther('0.01')
  62. const minReceiveAmount = (sendAmount * 995n) / 1000n
  63. const res = await routerEth.swapETH(
  64. toChainId,
  65. this.wallet.address,
  66. this.wallet.address,
  67. sendAmount,
  68. minReceiveAmount,
  69. { value: sendAmount + lzGasCost, gasLimit: limit, gasPrice },
  70. )
  71. const receipt = await res.wait()
  72. if (!receipt.status) {
  73. throw new Error('Transaction reverted')
  74. }
  75. return res.hash
  76. } catch (e) {
  77. StargateClient.logger.error(e.message, `Failed to bridge to ${toChainId}`)
  78. throw e
  79. }
  80. })
  81. }
  82. async calculateGasCost(toChainId: number, routerEth: RouterETH, lzGasCost: bigint) {
  83. const feeData = await this.provider.getFeeData()
  84. const gasPrice = feeData.gasPrice
  85. const amount = ethers.parseEther('0.001')
  86. const gasLimit = await routerEth.swapETH.estimateGas(
  87. toChainId,
  88. this.wallet.address,
  89. this.wallet.address,
  90. amount,
  91. 0n,
  92. {
  93. value: amount + lzGasCost,
  94. },
  95. )
  96. // const gasCost = ((this.chainId === 110 ? gasPrice : ethers.parseUnits('0.6', 'gwei')) * gasLimit * 110n) / 100n
  97. return { gasCost: ethers.parseEther('0.00015'), gasPrice, gasLimit: (gasLimit * 150n) / 100n }
  98. }
  99. }