web3.service.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. import { Inject, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
  2. import { ethers } from 'ethers'
  3. import { Wallet, Provider, utils } from 'zksync-web3'
  4. import { ConfigType } from '@nestjs/config'
  5. import { AccountsService } from '../accounts/accounts.service'
  6. import erc20 from './erc20.json'
  7. import { web3Config } from './web3config'
  8. import {
  9. ChainId,
  10. initialChainTable,
  11. getGasToken,
  12. point2PriceDecimal,
  13. priceDecimal2Point,
  14. pointDeltaRoundingDown,
  15. pointDeltaRoundingUp,
  16. amount2Decimal,
  17. PriceRoundingType,
  18. fetchToken,
  19. getErc20TokenContract,
  20. TokenInfoFormatted
  21. } from 'iziswap-sdk/lib/base'
  22. import Web3 from 'web3'
  23. import { BigNumber } from 'bignumber.js'
  24. import {} from 'iziswap-sdk/lib/quoter'
  25. import {} from 'iziswap-sdk/lib/swap'
  26. import {
  27. calciZiLiquidityAmountDesired,
  28. getMintCall,
  29. getLiquidityManagerContract,
  30. getPoolAddress,
  31. fetchLiquiditiesOfAccount,
  32. getDecLiquidityCall,
  33. DecLiquidityParam,
  34. getWithdrawLiquidityValue
  35. } from 'iziswap-sdk/lib/liquidityManager'
  36. import { getPoolContract, getPoolState, getPointDelta } from 'iziswap-sdk/lib/pool'
  37. import {
  38. getQuoterContract,
  39. quoterSwapChainWithExactOutput,
  40. quoterSwapChainWithExactInput
  41. } from 'iziswap-sdk/lib/quoter'
  42. import { getSwapContract, getSwapChainWithExactOutputCall, getSwapChainWithExactInputCall } from 'iziswap-sdk/lib/swap'
  43. import mintSquareAbi from './mintSquareAbi.json'
  44. import { Network } from './network.enum'
  45. import { Cron } from '@nestjs/schedule'
  46. import { Web3Result } from './model/web3-result'
  47. import { FileService } from 'src/file/file.service'
  48. import axios from 'axios'
  49. import * as randomString from 'randomstring'
  50. import { Account } from '../accounts/entities/account.entity'
  51. @Injectable()
  52. export class Web3Service {
  53. constructor(private readonly accountService: AccountsService, private readonly fileService: FileService) {}
  54. async zkDeposit(accountId, amount) {
  55. const account = await this.accountService.findById(accountId)
  56. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  57. const ethProvider = new ethers.providers.InfuraProvider(
  58. web3Config[account.network].ethereumNetwork,
  59. web3Config[account.network].infuraApiKey
  60. )
  61. const wallet = new Wallet(account.privateKey, provider, ethProvider)
  62. const deposit = await wallet.deposit({
  63. token: utils.ETH_ADDRESS,
  64. amount: ethers.utils.parseEther(amount)
  65. })
  66. Logger.log('Deposit sent. Awaiting confirmation...')
  67. account.offBridgeNum = (account.offBridgeNum || 0) + 1
  68. account.lastOffBridge = new Date()
  69. await this.accountService.save([account])
  70. return new Web3Result(account.address, deposit.hash, web3Config[account.network], false)
  71. // // Await processing of the deposit on L1
  72. // const ethereumTxReceipt = await deposit.waitL1Commit()
  73. // Logger.log('L1 deposit confirmed! Waiting for zkSync...')
  74. // // Await processing the deposit on zkSync
  75. // const depositReceipt = await deposit.wait()
  76. // Logger.log('zkSync deposit committed.')
  77. // Logger.log('Checking zkSync account balance')
  78. // // Retrieving the current (committed) zkSync ETH balance of an account
  79. // const committedEthBalance = await wallet.getBalance()
  80. // Logger.log('committedEthBalance', ethers.utils.formatEther(committedEthBalance))
  81. // // Retrieving the ETH balance of an account in the last finalized zkSync block.
  82. // const finalizedEthBalance = await wallet.getBalance(utils.ETH_ADDRESS, 'finalized')
  83. // Logger.log('finalizedEthBalance', ethers.utils.formatEther(finalizedEthBalance))
  84. }
  85. async zkWithdraw(accountId, amount) {
  86. const account = await this.accountService.findById(accountId)
  87. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  88. const ethProvider = new ethers.providers.InfuraProvider(
  89. web3Config[account.network].ethereumNetwork,
  90. web3Config[account.network].infuraApiKey
  91. )
  92. const wallet = new Wallet(account.privateKey, provider, ethProvider)
  93. const withdrawL2 = await wallet.withdraw({
  94. token: utils.ETH_ADDRESS,
  95. amount: ethers.utils.parseEther(amount)
  96. })
  97. Logger.log(`Widthdraw sent. ${web3Config[account.network].zksyncExplorer}/tx/${withdrawL2.hash}`)
  98. account.offBridgeNum = (account.offBridgeNum || 0) + 1
  99. account.lastOffBridge = new Date()
  100. await this.accountService.save([account])
  101. return new Web3Result(account.address, withdrawL2.hash, web3Config[account.network], true)
  102. // // Await processing of the deposit on L1
  103. // const ethereumTxReceipt = await deposit.waitL1Commit()
  104. // Logger.log('L1 deposit confirmed! Waiting for zkSync...')
  105. // // Await processing the deposit on zkSync
  106. // const depositReceipt = await deposit.wait()
  107. // Logger.log('zkSync deposit committed.')
  108. // Logger.log('Checking zkSync account balance')
  109. // // Retrieving the current (committed) zkSync ETH balance of an account
  110. // const committedEthBalance = await wallet.getBalance()
  111. // Logger.log('committedEthBalance', ethers.utils.formatEther(committedEthBalance))
  112. // // Retrieving the ETH balance of an account in the last finalized zkSync block.
  113. // const finalizedEthBalance = await wallet.getBalance(utils.ETH_ADDRESS, 'finalized')
  114. // Logger.log('finalizedEthBalance', ethers.utils.formatEther(finalizedEthBalance))
  115. }
  116. async orbiterDeposit(accountId, amount) {
  117. const account = await this.accountService.findById(accountId)
  118. const provider = new ethers.providers.InfuraProvider(
  119. web3Config[account.network].ethereumNetwork,
  120. web3Config[account.network].infuraApiKey
  121. )
  122. const wallet = new ethers.Wallet(account.privateKey, provider)
  123. const tx = await wallet.sendTransaction({
  124. from: wallet.address,
  125. to: web3Config[account.network].orbiterEthAddress,
  126. value: ethers.utils.parseEther(amount).add(web3Config[account.network].orbiterIdCodeZk)
  127. })
  128. account.thirdBridgeNum = (account.thirdBridgeNum || 0) + 1
  129. account.lastThirdBridge = new Date()
  130. await this.accountService.save([account])
  131. return new Web3Result(account.address, tx.hash, web3Config[account.network], false)
  132. }
  133. async orbiterWithdraw(accountId, amount) {
  134. const account = await this.accountService.findById(accountId)
  135. const provider = new ethers.providers.InfuraProvider(
  136. web3Config[account.network].ethereumNetwork,
  137. web3Config[account.network].infuraApiKey
  138. )
  139. const wallet = new ethers.Wallet(account.privateKey, provider)
  140. const tx = await wallet.sendTransaction({
  141. from: wallet.address,
  142. to: web3Config[account.network].orbiterZkAddress,
  143. value: ethers.utils.parseEther(amount).add(web3Config[account.network].orbiterIdCodeEth)
  144. })
  145. account.thirdBridgeNum = (account.thirdBridgeNum || 0) + 1
  146. account.lastThirdBridge = new Date()
  147. await this.accountService.save([account])
  148. return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
  149. }
  150. async addLiquidity(accountId, amount) {
  151. const account = await this.accountService.findById(accountId)
  152. const chainId =
  153. web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
  154. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  155. const zkWallet = new Wallet(account.privateKey).connect(provider)
  156. const chain = initialChainTable[chainId]
  157. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
  158. Logger.log(`address: ${zkWallet.address}`, 'addLiquidity')
  159. const liquidityManagerContract = getLiquidityManagerContract(
  160. web3Config[account.network].liquidityManagerAddress,
  161. web3 as any
  162. )
  163. const tokenA = getGasToken(chainId)
  164. Logger.log(`tokenA: ${JSON.stringify(tokenA, null, 4)}`, 'addLiquidity')
  165. const tokenB = await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  166. Logger.log(`tokenB: ${JSON.stringify(tokenB, null, 4)}`, 'addLiquidity')
  167. await this.approve(
  168. tokenB.address,
  169. web3Config[account.network].liquidityManagerAddress,
  170. zkWallet,
  171. account.network
  172. )
  173. const fee = 2000 // 2000 means 0.2%
  174. const poolAddress = await getPoolAddress(liquidityManagerContract, tokenA, tokenB, fee)
  175. Logger.log(`poolAddress: ${poolAddress}`, 'addLiquidity')
  176. const poolContract = getPoolContract(poolAddress, web3 as any)
  177. const state = await getPoolState(poolContract)
  178. const currentPrice = point2PriceDecimal(tokenA, tokenB, state.currentPoint)
  179. Logger.log(`current point: ${state.currentPoint}`, 'addLiquidity')
  180. const point1 = priceDecimal2Point(tokenA, tokenB, currentPrice * 0.98, PriceRoundingType.PRICE_ROUNDING_NEAREST)
  181. const point2 = priceDecimal2Point(tokenA, tokenB, currentPrice * 1.02, PriceRoundingType.PRICE_ROUNDING_NEAREST)
  182. Logger.log(`point range: ${point1} - ${point2}`, 'addLiquidity')
  183. let leftPoint = Math.min(point1, point2)
  184. let rightPoint = Math.max(point1, point2)
  185. const pointDelta = await getPointDelta(poolContract)
  186. Logger.log(`point delta: ${pointDelta}`, 'addLiquidity')
  187. leftPoint = pointDeltaRoundingDown(leftPoint, pointDelta)
  188. rightPoint = pointDeltaRoundingUp(rightPoint, pointDelta)
  189. const maxTestA = new BigNumber(amount).times(10 ** tokenA.decimal)
  190. const maxTestB = calciZiLiquidityAmountDesired(
  191. leftPoint,
  192. rightPoint,
  193. state.currentPoint,
  194. maxTestA,
  195. true,
  196. tokenA,
  197. tokenB
  198. )
  199. const mintParams = {
  200. tokenA: tokenA,
  201. tokenB: tokenB,
  202. fee,
  203. leftPoint,
  204. rightPoint,
  205. maxAmountA: maxTestA.toFixed(0),
  206. maxAmountB: maxTestB.toFixed(0),
  207. minAmountA: maxTestA.times(0.985).toFixed(0),
  208. minAmountB: maxTestB.times(0.985).toFixed(0)
  209. }
  210. Logger.log(
  211. `tokenAtoPay: ${amount2Decimal(new BigNumber(maxTestA), tokenA)}, tokenBtoPay: ${amount2Decimal(
  212. new BigNumber(maxTestB),
  213. tokenB
  214. )}, price: ${point2PriceDecimal(tokenA, tokenB, state.currentPoint)}`,
  215. 'addLiquidity'
  216. )
  217. const balanceA = await zkWallet.getBalance()
  218. const balanceB = await zkWallet.getBalance(tokenB.address)
  219. if (new BigNumber(balanceA.toString()).lt(maxTestA)) {
  220. throw new InternalServerErrorException(
  221. `${tokenA.symbol}余额不足, 需要${ethers.utils.formatUnits(
  222. maxTestA.toString(),
  223. tokenA.decimal
  224. )}, 当前${ethers.utils.formatUnits(balanceA, tokenA.decimal)}`
  225. )
  226. }
  227. if (new BigNumber(balanceB.toString()).lt(maxTestB)) {
  228. throw new InternalServerErrorException(
  229. `${tokenB.symbol}余额不足, 需要${ethers.utils.formatUnits(
  230. maxTestB.toString(),
  231. tokenB.decimal
  232. )}, 当前${ethers.utils.formatUnits(balanceB, tokenB.decimal)}`
  233. )
  234. }
  235. Logger.log(`mintParams: ${JSON.stringify(mintParams, null, 4)}`, 'addLiquidity')
  236. const gasPrice = await web3.eth.getGasPrice()
  237. const { mintCalling, options } = getMintCall(
  238. liquidityManagerContract,
  239. zkWallet.address,
  240. chain,
  241. mintParams,
  242. gasPrice.toString()
  243. )
  244. let calling = mintCalling
  245. if (calling instanceof Array) {
  246. calling = liquidityManagerContract.methods.multicall(mintCalling)
  247. }
  248. const gasLimit = await calling.estimateGas({ from: zkWallet.address })
  249. Logger.log(`gas limit: ${gasLimit}`, 'addLiquidity')
  250. // sign transaction
  251. const tx = await zkWallet.sendTransaction({
  252. from: options.from,
  253. value: Web3.utils.numberToHex(options.value),
  254. to: web3Config[account.network].liquidityManagerAddress,
  255. data: calling.encodeABI(),
  256. gasLimit
  257. })
  258. Logger.log(`tx hash: ${tx.hash}`, 'addLiquidity')
  259. account.addLiuidityNum = (account.addLiuidityNum || 0) + 1
  260. account.lastAddLiuidity = new Date()
  261. await this.accountService.save([account])
  262. return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
  263. }
  264. async removeLiquidity(accountId) {
  265. const account = await this.accountService.findById(accountId)
  266. const chainId =
  267. web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
  268. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  269. const wallet = new Wallet(account.privateKey).connect(provider)
  270. const chain = initialChainTable[chainId]
  271. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
  272. const liquidityManagerContract = getLiquidityManagerContract(
  273. web3Config[account.network].liquidityManagerAddress,
  274. web3 as any
  275. )
  276. Logger.log('liquidity manager address: ', web3Config[account.network].liquidityManagerAddress)
  277. const tokenA = getGasToken(chainId)
  278. const tokenB = await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  279. Logger.log(`tokenA: ${tokenA.symbol} tokenB: ${tokenB.symbol}`, 'FetchLiquidities')
  280. const liquidities = await fetchLiquiditiesOfAccount(
  281. chain,
  282. web3 as any,
  283. liquidityManagerContract,
  284. account.address,
  285. [tokenA, tokenB]
  286. )
  287. Logger.log(`${liquidities.length} liquidities fetched`, 'FetchLiquidities')
  288. if (liquidities.length <= 0) {
  289. throw new InternalServerErrorException('No liquidity found')
  290. }
  291. const liquidity0 = liquidities.filter((i) => Number(i.liquidity) > 0)[0]
  292. if (!liquidity0) {
  293. throw new InternalServerErrorException('all liquidities are removed')
  294. }
  295. const decRate = 1
  296. const originLiquidity = new BigNumber(liquidity0.liquidity)
  297. const decLiquidity = originLiquidity.times(decRate)
  298. const { amountX, amountY } = getWithdrawLiquidityValue(liquidity0, liquidity0.state, decLiquidity)
  299. // here the slippery is 1.5%
  300. const minAmountX = amountX.times(0.985).toFixed(0)
  301. const minAmountY = amountY.times(0.985).toFixed(0)
  302. const gasPrice = await web3.eth.getGasPrice()
  303. const { decLiquidityCalling, options } = getDecLiquidityCall(
  304. liquidityManagerContract,
  305. account.address,
  306. chain,
  307. {
  308. tokenId: liquidity0.tokenId,
  309. liquidDelta: decLiquidity.toFixed(0),
  310. minAmountX,
  311. minAmountY
  312. } as DecLiquidityParam,
  313. gasPrice.toString()
  314. )
  315. const gasLimit = await decLiquidityCalling.estimateGas(options)
  316. console.log('gas limit: ', gasLimit)
  317. const tx0 = await wallet.sendTransaction({
  318. from: wallet.address,
  319. to: web3Config[account.network].liquidityManagerAddress,
  320. data: decLiquidityCalling.encodeABI(),
  321. gasLimit
  322. })
  323. console.log('decLiquidityCalling tx: ', JSON.stringify(tx0, null, 4))
  324. account.removeLiuidityNum = (account.removeLiuidityNum || 0) + 1
  325. account.lastRemoveLiuidity = new Date()
  326. await this.accountService.save([account])
  327. return new Web3Result(account.address, tx0.hash, web3Config[account.network], true)
  328. }
  329. async allowance(tokenAddress, spender, wallet: Wallet, network: Network): Promise<number> {
  330. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[network].zksyncRpcUrl))
  331. const tokenAContract = new web3.eth.Contract(erc20, tokenAddress)
  332. // @ts-ignore
  333. const allowanceCalling = tokenAContract.methods.allowance(wallet.address, spender)
  334. const out = await allowanceCalling.call()
  335. Logger.log(`allowance: ${out}`, 'GetAllowance')
  336. return out as unknown as number
  337. }
  338. async approve(tokenAddress, spender, wallet: Wallet, network: Network) {
  339. const allowance = await this.allowance(tokenAddress, spender, wallet, network)
  340. if (allowance > 0) {
  341. return
  342. }
  343. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[network].zksyncRpcUrl))
  344. const tokenAContract = new web3.eth.Contract(erc20, tokenAddress)
  345. // @ts-ignore
  346. const approveCalling = tokenAContract.methods.approve(spender, '0xffffffffffffffffffffffffffffffff')
  347. let gasLimit = await approveCalling.estimateGas({ from: wallet.address })
  348. console.log('approve gas limit: ', gasLimit)
  349. const tx0 = await wallet.sendTransaction({
  350. from: wallet.address,
  351. to: tokenAddress,
  352. data: approveCalling.encodeABI(),
  353. gasLimit
  354. })
  355. console.log('approve tx: ', JSON.stringify(tx0, null, 4))
  356. }
  357. async swapWithExactOutput(accountId, amount, outputToken: 'eth' | 'usdc' = 'usdc') {
  358. const account = await this.accountService.findById(accountId)
  359. const chainId =
  360. web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
  361. const chain = initialChainTable[chainId]
  362. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  363. const wallet = new Wallet(account.privateKey).connect(provider)
  364. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
  365. console.log('address: ', wallet.address)
  366. const balance = await web3.eth.getBalance(wallet.address)
  367. console.log('ethBalance: ' + Web3.utils.fromWei(balance, 'ether'))
  368. const fromToken =
  369. outputToken === 'usdc'
  370. ? getGasToken(chainId)
  371. : await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  372. console.log('fromToken: ', fromToken)
  373. const toToken =
  374. outputToken === 'usdc'
  375. ? await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  376. : getGasToken(chainId)
  377. console.log('toToken: ', toToken)
  378. const fee = 2000 // 2000 means 0.2%
  379. const toTokenContract = getErc20TokenContract(toToken.address, web3 as any)
  380. const toTokenBalance = await toTokenContract.methods.balanceOf(wallet.address).call()
  381. console.log(toToken.symbol + ' balance: ' + amount2Decimal(new BigNumber(toTokenBalance), toToken))
  382. const quoterContract = getQuoterContract(web3Config[account.network].quoterAddress, web3 as any)
  383. const receiveAmount = new BigNumber(amount).times(10 ** toToken.decimal)
  384. const params = {
  385. // pay testA to buy testB
  386. tokenChain: [fromToken, toToken],
  387. feeChain: [fee],
  388. outputAmount: receiveAmount.toFixed(0)
  389. }
  390. const { inputAmount } = await quoterSwapChainWithExactOutput(quoterContract, params)
  391. const payAmount = inputAmount
  392. const payAmountDecimal = amount2Decimal(new BigNumber(payAmount), fromToken)
  393. console.log(toToken.symbol + ' to desired: ', amount)
  394. console.log(fromToken.symbol + ' to pay: ', payAmountDecimal)
  395. const swapContract = getSwapContract(web3Config[account.network].swapAddress, web3 as any)
  396. const swapParams = {
  397. ...params,
  398. // slippery is 1.5%
  399. maxInputAmount: new BigNumber(payAmount).times(1.015).toFixed(0),
  400. strictERC20Token: false
  401. }
  402. const gasPrice = await web3.eth.getGasPrice()
  403. const { swapCalling, options } = getSwapChainWithExactOutputCall(
  404. swapContract,
  405. wallet.address,
  406. chain,
  407. swapParams,
  408. gasPrice.toString()
  409. )
  410. console.log(JSON.stringify({ swapCalling, options }, null, 4))
  411. const gasLimit = await swapCalling.estimateGas(options)
  412. console.log('gas limit: ', gasLimit)
  413. const tx = await wallet.sendTransaction({
  414. value: Web3.utils.numberToHex(options.value),
  415. data: swapCalling.encodeABI(),
  416. to: web3Config[account.network].swapAddress,
  417. gasLimit
  418. })
  419. console.log(tx)
  420. account.swapNum = (account.swapNum || 0) + 1
  421. account.lastSwap = new Date()
  422. await this.accountService.save([account])
  423. return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
  424. }
  425. async swapWithExactInput(accountId, amount, inputToken: 'eth' | 'usdc' = 'usdc') {
  426. const account = await this.accountService.findById(accountId)
  427. const chainId =
  428. web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
  429. const chain = initialChainTable[chainId]
  430. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  431. const wallet = new Wallet(account.privateKey).connect(provider)
  432. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
  433. console.log('address: ', wallet.address)
  434. const fromToken =
  435. inputToken === 'eth'
  436. ? getGasToken(chainId)
  437. : await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  438. console.log('fromToken: ', fromToken)
  439. const toToken =
  440. inputToken === 'eth'
  441. ? await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
  442. : getGasToken(chainId)
  443. console.log('toToken: ', toToken)
  444. await this.approve(fromToken.address, web3Config[account.network].swapAddress, wallet, account.network)
  445. const quoterContract = getQuoterContract(web3Config[account.network].quoterAddress, web3 as any)
  446. const payAmount = new BigNumber(amount).times(10 ** fromToken.decimal)
  447. const fee = 2000 // 2000 means 0.2%
  448. const params = {
  449. // pay testA to buy testB
  450. tokenChain: [fromToken, toToken],
  451. feeChain: [fee],
  452. inputAmount: payAmount.toFixed(0)
  453. }
  454. const { outputAmount } = await quoterSwapChainWithExactInput(quoterContract, params)
  455. const receiveAmount = outputAmount
  456. const receiveAmountDecimal = amount2Decimal(new BigNumber(receiveAmount), toToken)
  457. console.log(fromToken.symbol + ' to pay: ', amount)
  458. console.log(toToken.symbol + ' to acquire: ', receiveAmountDecimal)
  459. const swapContract = getSwapContract(web3Config[account.network].swapAddress, web3 as any)
  460. const swapParams = {
  461. ...params,
  462. // slippery is 1.5%
  463. minOutputAmount: new BigNumber(receiveAmount).times(0.985).toFixed(0),
  464. strictERC20Token: false
  465. }
  466. const gasPrice = await web3.eth.getGasPrice()
  467. const { swapCalling, options } = getSwapChainWithExactInputCall(
  468. swapContract,
  469. account.address,
  470. chain,
  471. swapParams,
  472. gasPrice.toString()
  473. )
  474. const gasLimit = await swapCalling.estimateGas(options)
  475. console.log('gas limit: ', gasLimit)
  476. const tx = await wallet.sendTransaction({
  477. value: Web3.utils.numberToHex(options.value),
  478. data: swapCalling.encodeABI(),
  479. to: web3Config[account.network].swapAddress,
  480. gasLimit
  481. })
  482. console.log(JSON.stringify(tx, null, 4))
  483. account.swapNum = (account.swapNum || 0) + 1
  484. account.lastSwap = new Date()
  485. await this.accountService.save([account])
  486. return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
  487. }
  488. async mint(accountId) {
  489. // const { data: buffer } = await axios.get('https://cataas.com/cat', {
  490. // responseType: 'arraybuffer'
  491. // })
  492. // const f = new FormData()
  493. // f.append('file', new Blob([buffer], { type: 'image/jpeg' }), 'cat.jpg')
  494. // const { data: image } = await axios.post('https://api.mintsquare.io/files/upload/', f)
  495. const f1 = new FormData()
  496. f1.append(
  497. 'file',
  498. new Blob([
  499. JSON.stringify({
  500. name: randomString.generate({ length: 8, charset: 'alphanumeric' }),
  501. attributes: [],
  502. image: 'https://mintsquare.sfo3.cdn.digitaloceanspaces.com/mintsquare/mintsquare/RJRohgvdN6xwSBvFefo8z6_1687229689762946549.jpg'
  503. })
  504. ])
  505. )
  506. const { data: res } = await axios.post('https://ipfs.infura.io:5001/api/v0/add', f1, {
  507. auth: {
  508. username: '2RrUoAzIHbABPzXlH4M7Rnc3Fir',
  509. password: 'aaa4218c6944d2b9ad706275e5f1d6f1'
  510. }
  511. })
  512. Logger.log(res)
  513. const account = await this.accountService.findById(accountId)
  514. const chainId =
  515. web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
  516. const chain = initialChainTable[chainId]
  517. const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
  518. const wallet = new Wallet(account.privateKey).connect(provider)
  519. const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
  520. console.log('address: ', wallet.address)
  521. const contract = new web3.eth.Contract(mintSquareAbi, web3Config[account.network].mintAddress, web3 as any)
  522. // @ts-ignore
  523. const mintCall = await contract.methods.mint(`ipfs://${res.Hash}`)
  524. Logger.log(`ipfs://${res.Hash}`, 'mint')
  525. const gasLimit = await mintCall.estimateGas({ from: wallet.address })
  526. console.log('gas limit: ', gasLimit)
  527. const tx = await wallet.sendTransaction({
  528. from: wallet.address,
  529. to: web3Config[account.network].mintAddress,
  530. data: mintCall.encodeABI(),
  531. gasLimit
  532. })
  533. console.log(tx)
  534. account.mintNum = (account.mintNum || 0) + 1
  535. account.lastMint = new Date()
  536. await this.accountService.save([account])
  537. return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
  538. }
  539. async randomPercent(account: Account) {}
  540. }