xiongzhu пре 2 година
родитељ
комит
eed2a1d57a

+ 33 - 0
src/accounts/accounts.service.ts

@@ -91,6 +91,39 @@ export class AccountsService {
         }
     }
 
+    async getZkBalance(account: Account) {
+        const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
+        const ethProvider = new ethers.providers.InfuraProvider(
+            web3Config[account.network].ethereumNetwork,
+            web3Config[account.network].infuraApiKey
+        )
+        const wallet = new Wallet(account.privateKey, provider, ethProvider)
+        const zksyncBalance = await wallet.getBalance()
+        return ethers.utils.formatEther(zksyncBalance)
+    }
+
+    async getEthBalance(account: Account) {
+        const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
+        const ethProvider = new ethers.providers.InfuraProvider(
+            web3Config[account.network].ethereumNetwork,
+            web3Config[account.network].infuraApiKey
+        )
+        const wallet = new Wallet(account.privateKey, provider, ethProvider)
+        const ethBalance = await wallet.getBalanceL1()
+        return ethers.utils.formatEther(ethBalance)
+    }
+
+    async getUsdcBalance(account: Account) {
+        const provider = new Provider(web3Config[account.network].zksyncRpcUrl)
+        const ethProvider = new ethers.providers.InfuraProvider(
+            web3Config[account.network].ethereumNetwork,
+            web3Config[account.network].infuraApiKey
+        )
+        const wallet = new Wallet(account.privateKey, provider, ethProvider)
+        const zkUsdcBalance = await wallet.getBalance(web3Config[account.network].zkUsdcAddress)
+        return ethers.utils.formatUnits(zkUsdcBalance, 6)
+    }
+
     async refreshBalance(userId: number) {
         const accounts = await this.findByUserId(userId)
         for (let account of accounts) {

+ 11 - 7
src/task/dto/create-task.dto.ts

@@ -1,10 +1,9 @@
-import { IsString, IsArray, IsDate, IsEnum, IsOptional, ArrayMinSize } from 'class-validator'
-import { TaskType } from '../enums/task-type.enum'
+import { IsString, IsArray, IsDate, IsEnum, IsOptional, ArrayMinSize, IsObject, Length, ValidateNested, ArrayMaxSize } from 'class-validator'
+import { TaskItem } from '../model/task-item.model'
+import { Delay } from '../model/delay.model'
+import { Type } from 'class-transformer'
 
 export class CreateTaskDto {
-    @IsEnum(TaskType)
-    type: TaskType
-
     @IsArray()
     @ArrayMinSize(1)
     accounts: number[]
@@ -13,6 +12,11 @@ export class CreateTaskDto {
     @IsOptional()
     startTime?: Date
 
-    @IsOptional()
-    params?: any
+    @ValidateNested({ each: true })
+    @ArrayMinSize(1)
+    @Type(() => TaskItem)
+    items: TaskItem[]
+
+    @IsObject()
+    delay: Delay
 }

+ 0 - 32
src/task/entities/task-item.entity.ts

@@ -1,32 +0,0 @@
-import { Column, CreateDateColumn, PrimaryGeneratedColumn } from 'typeorm'
-import { TaskStatus } from '../enums/task-status.enum'
-import { TaskType } from '../enums/task-type.enum'
-
-export class TaskItem {
-    @PrimaryGeneratedColumn()
-    id: number
-
-    @Column()
-    taskId: number
-
-    @Column()
-    userId: number
-
-    @Column({ type: 'enum', enum: TaskType })
-    type: TaskType
-
-    @Column({ type: 'enum', enum: TaskStatus, default: TaskStatus.Pending })
-    status: TaskStatus
-
-    @CreateDateColumn()
-    createdAt: Date
-
-    @Column()
-    accountId: number
-
-    @Column()
-    startTime: Date
-
-    @Column()
-    endTime: Date
-}

+ 9 - 33
src/task/entities/task.entity.ts

@@ -1,6 +1,8 @@
 import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm'
 import { TaskStatus } from '../enums/task-status.enum'
-import { TaskType } from '../enums/task-type.enum'
+import { JsonTransformer } from '../../transformers/json.transformer'
+import { TaskItem } from '../model/task-item.model'
+import { Delay } from '../model/delay.model'
 
 @Entity()
 export class Task {
@@ -10,27 +12,11 @@ export class Task {
     @Column()
     userId: number
 
-    @Column({ type: 'enum', enum: TaskType })
-    type: TaskType
-
     @Column({
         type: 'text',
-        transformer: {
-            from: (value: string) => {
-                if (!value) {
-                    return null
-                }
-                return JSON.parse(value)
-            },
-            to: (value: any) => {
-                if (!value) {
-                    return null
-                }
-                return JSON.stringify(value)
-            }
-        }
+        transformer: new JsonTransformer()
     })
-    params: any
+    items: TaskItem[]
 
     @Column({ type: 'enum', enum: TaskStatus, default: TaskStatus.Pending })
     status: TaskStatus
@@ -40,20 +26,7 @@ export class Task {
 
     @Column({
         type: 'text',
-        transformer: {
-            from: (value: string) => {
-                if (!value) {
-                    return null
-                }
-                return JSON.parse(value)
-            },
-            to: (value: any) => {
-                if (!value) {
-                    return null
-                }
-                return JSON.stringify(value)
-            }
-        }
+        transformer: new JsonTransformer()
     })
     accounts: number[]
 
@@ -62,4 +35,7 @@ export class Task {
 
     @Column()
     startTime: Date
+
+    @Column({ type: 'varchar', transformer: new JsonTransformer() })
+    delay: Delay
 }

+ 4 - 0
src/task/model/delay.model.ts

@@ -0,0 +1,4 @@
+export class Delay {
+    min: number
+    max: number
+}

+ 14 - 0
src/task/model/task-item.model.ts

@@ -0,0 +1,14 @@
+import { TaskType } from '../enums/task-type.enum'
+
+export class TaskItem {
+    type: TaskType
+    amountType: 'amount' | 'percent'
+    amount?: {
+        min?: number
+        max?: number
+    }
+    percent?: {
+        min?: number
+        max?: number
+    }
+}

+ 120 - 74
src/task/task.service.ts

@@ -12,7 +12,7 @@ import { TaskType } from './enums/task-type.enum'
 import { AccountsService } from 'src/accounts/accounts.service'
 import { LogStatus } from './enums/log-status.enum'
 import { Web3Result } from 'src/web3/model/web3-result'
-import { randomAmount } from 'src/utils/common'
+import { randomAmount, randomBalancePercent } from 'src/utils/common'
 import { EventEmitter2 } from '@nestjs/event-emitter'
 import { setTimeout } from 'timers/promises'
 import schedule from 'node-schedule'
@@ -88,81 +88,127 @@ export class TaskService implements OnModuleInit {
         task.status = TaskStatus.InProgress
         await this.taskRepository.save(task)
         for (let accountId of task.accounts) {
-            const taskLog = new TaskLog()
-            taskLog.taskId = task.id
-            taskLog.accountId = accountId
-            taskLog.startTime = new Date()
-            taskLog.type = task.type
-            try {
-                const account = await this.accountsService.findById(accountId)
-                taskLog.address = account.address
-                taskLog.accountName = account.name
-                let web3Result: Web3Result
-                switch (task.type) {
-                    case TaskType.bridge2zk:
-                        web3Result = await this.web3Service.zkDeposit(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.bridge2eth:
-                        web3Result = await this.web3Service.zkWithdraw(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.orbiter2zk:
-                        web3Result = await this.web3Service.orbiterDeposit(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.orbiter2eth:
-                        web3Result = await this.web3Service.orbiterWithdraw(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.swapUsdc:
-                        web3Result = await this.web3Service.swapWithExactOutput(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.swapEth:
-                        web3Result = await this.web3Service.swapWithExactInput(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.addLiquidity:
-                        web3Result = await this.web3Service.addLiquidity(
-                            accountId,
-                            randomAmount(task.params.minAmount, task.params.maxAmount)
-                        )
-                        break
-                    case TaskType.removeLiquidity:
-                        web3Result = await this.web3Service.removeLiquidity(accountId)
-                        break
-                    case TaskType.mint:
-                        web3Result = await this.web3Service.mint(accountId)
-                        break
+            for (let taskItem of task.items) {
+                const taskLog = new TaskLog()
+                taskLog.taskId = task.id
+                taskLog.accountId = accountId
+                taskLog.startTime = new Date()
+                taskLog.type = taskItem.type
+                try {
+                    const account = await this.accountsService.findById(accountId)
+                    taskLog.address = account.address
+                    taskLog.accountName = account.name
+                    let web3Result: Web3Result
+                    switch (taskItem.type) {
+                        case TaskType.bridge2zk:
+                            web3Result = await this.web3Service.zkDeposit(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getEthBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      )
+                            )
+                            break
+                        case TaskType.bridge2eth:
+                            web3Result = await this.web3Service.zkWithdraw(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getZkBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      )
+                            )
+                            break
+                        case TaskType.orbiter2zk:
+                            web3Result = await this.web3Service.orbiterDeposit(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getEthBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      )
+                            )
+                            break
+                        case TaskType.orbiter2eth:
+                            web3Result = await this.web3Service.orbiterWithdraw(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getZkBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      )
+                            )
+                            break
+                        case TaskType.swapUsdc:
+                            web3Result = await this.web3Service.swapWithExactInput(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getZkBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      ),
+                                'eth'
+                            )
+                            break
+                        case TaskType.swapEth:
+                            web3Result = await this.web3Service.swapWithExactInput(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getUsdcBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      ),
+                                'usdc'
+                            )
+                            break
+                        case TaskType.addLiquidity:
+                            web3Result = await this.web3Service.addLiquidity(
+                                accountId,
+                                taskItem.amountType === 'amount'
+                                    ? randomAmount(taskItem.amount.min, taskItem.amount.max)
+                                    : randomBalancePercent(
+                                          await this.accountsService.getZkBalance(account),
+                                          taskItem.percent.min,
+                                          taskItem.percent.max
+                                      )
+                            )
+                            break
+                        case TaskType.removeLiquidity:
+                            web3Result = await this.web3Service.removeLiquidity(accountId)
+                            break
+                        case TaskType.mint:
+                            web3Result = await this.web3Service.mint(accountId)
+                            break
+                    }
+                    taskLog.status = LogStatus.Success
+                    taskLog.txHash = web3Result.hash
+                    taskLog.url = web3Result.url
+                } catch (error) {
+                    Logger.error(error, 'TaskService.runTask')
+                    Logger.error(error.stack, 'TaskService.runTask')
+                    taskLog.status = LogStatus.Failed
+                    taskLog.error = error.message
                 }
-                taskLog.status = LogStatus.Success
-                taskLog.txHash = web3Result.hash
-                taskLog.url = web3Result.url
-            } catch (error) {
-                Logger.error(error, 'TaskService.runTask')
-                Logger.error(error.stack, 'TaskService.runTask')
-                taskLog.status = LogStatus.Failed
-                taskLog.error = error.message
+                taskLog.finishTime = new Date()
+                await this.taskLogRepository.save(taskLog)
+                task.progress += 1
+                await this.taskRepository.save(task)
+                this.eventEmitter.emit('refreshBalance', accountId)
+                await setTimeout(parseInt(randomAmount(task.delay.min, task.delay.max, 0)))
             }
-            taskLog.finishTime = new Date()
-            await this.taskLogRepository.save(taskLog)
-            task.progress += 1
-            await this.taskRepository.save(task)
-            this.eventEmitter.emit('refreshBalance', accountId)
-            await setTimeout(parseInt(randomAmount(task.params.minInterval, task.params.maxInterval, 0)))
         }
         task.status = TaskStatus.Done
         await this.taskRepository.save(task)

+ 17 - 0
src/transformers/json.transformer.ts

@@ -0,0 +1,17 @@
+import { ValueTransformer } from 'typeorm'
+import BigNumber from 'bignumber.js'
+
+export class JsonTransformer implements ValueTransformer {
+    from(value: string) {
+        if (!value) {
+            return null
+        }
+        return JSON.parse(value)
+    }
+    to(value: any) {
+        if (!value) {
+            return null
+        }
+        return JSON.stringify(value)
+    }
+}

+ 18 - 0
src/utils/common.ts

@@ -12,3 +12,21 @@ export function randomAmount(min, max, decimals = 6) {
         .plus(minNum)
         .toString()
 }
+
+export function randomBalancePercent(balance, min, max) {
+    let minNum = new BigNumber(min)
+    let maxNum = new BigNumber(max)
+    let d = maxNum
+        .minus(minNum)
+        .times(balance)
+        .times(new BigNumber(10 ** 6))
+    let random = new BigNumber(Math.random()).times(d).integerValue()
+    const res = random
+        .dividedBy(new BigNumber(10 ** 6))
+        .plus(minNum)
+        .dividedBy(new BigNumber(100))
+        .times(balance)
+        .precision(6)
+        .toString()
+    return res
+}

+ 26 - 8
src/web3/web3.service.ts

@@ -47,6 +47,7 @@ import { Web3Result } from './model/web3-result'
 import { FileService } from 'src/file/file.service'
 import axios from 'axios'
 import * as randomString from 'randomstring'
+import { Account } from '../accounts/entities/account.entity'
 
 @Injectable()
 export class Web3Service {
@@ -225,11 +226,12 @@ export class Web3Service {
             minAmountA: maxTestA.times(0.985).toFixed(0),
             minAmountB: maxTestB.times(0.985).toFixed(0)
         }
+
         Logger.log(
             `tokenAtoPay: ${amount2Decimal(new BigNumber(maxTestA), tokenA)}, tokenBtoPay: ${amount2Decimal(
                 new BigNumber(maxTestB),
                 tokenB
-            )}`,
+            )}, price: ${point2PriceDecimal(tokenA, tokenB, state.currentPoint)}`,
             'addLiquidity'
         )
         const balanceA = await zkWallet.getBalance()
@@ -361,7 +363,7 @@ export class Web3Service {
         // @ts-ignore
         const allowanceCalling = tokenAContract.methods.allowance(wallet.address, spender)
         const out = await allowanceCalling.call()
-        Logger.log(`allowance: ${out} hex: ${Web3.utils.numberToHex(out + '')}`, 'GetAllowance')
+        Logger.log(`allowance: ${out}`, 'GetAllowance')
         return out as unknown as number
     }
 
@@ -386,7 +388,7 @@ export class Web3Service {
         console.log('approve tx: ', JSON.stringify(tx0, null, 4))
     }
 
-    async swapWithExactOutput(accountId, amount) {
+    async swapWithExactOutput(accountId, amount, outputToken: 'eth' | 'usdc' = 'usdc') {
         const account = await this.accountService.findById(accountId)
         const chainId =
             web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
@@ -399,9 +401,17 @@ export class Web3Service {
         const balance = await web3.eth.getBalance(wallet.address)
         console.log('ethBalance: ' + Web3.utils.fromWei(balance, 'ether'))
 
-        const fromToken = getGasToken(chainId)
+        const fromToken =
+            outputToken === 'usdc'
+                ? getGasToken(chainId)
+                : await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
+
         console.log('fromToken: ', fromToken)
-        const toToken = await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
+        const toToken =
+            outputToken === 'usdc'
+                ? await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
+                : getGasToken(chainId)
+
         console.log('toToken: ', toToken)
         const fee = 2000 // 2000 means 0.2%
 
@@ -463,7 +473,7 @@ export class Web3Service {
         return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
     }
 
-    async swapWithExactInput(accountId, amount) {
+    async swapWithExactInput(accountId, amount, inputToken: 'eth' | 'usdc' = 'usdc') {
         const account = await this.accountService.findById(accountId)
         const chainId =
             web3Config[account.network].ethereumNetwork == 'goerli' ? ChainId.ZkSyncAlphaTest : ChainId.ZkSyncEra
@@ -473,9 +483,15 @@ export class Web3Service {
         const web3 = new Web3(new Web3.providers.HttpProvider(web3Config[account.network].zksyncRpcUrl))
         console.log('address: ', wallet.address)
 
-        const fromToken = await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
+        const fromToken =
+            inputToken === 'eth'
+                ? getGasToken(chainId)
+                : await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
         console.log('fromToken: ', fromToken)
-        const toToken = getGasToken(chainId)
+        const toToken =
+            inputToken === 'eth'
+                ? await fetchToken(web3Config[account.network].zkUsdcAddress, chain, web3 as any)
+                : getGasToken(chainId)
         console.log('toToken: ', toToken)
 
         await this.approve(fromToken.address, web3Config[account.network].swapAddress, wallet, account.network)
@@ -584,4 +600,6 @@ export class Web3Service {
         await this.accountService.save([account])
         return new Web3Result(account.address, tx.hash, web3Config[account.network], true)
     }
+
+    async randomPercent(account: Account) {}
 }