功能: 添加任务调度和批量Web3请求。
- 添加用于调度和取消时间锁定任务的方法 - 添加batchMint方法,以一次性铸造多个代币 - 在Constants.ts中添加常量“ZERO_BYTES32”和“MAX_BATCH_REQ_COUNT” - 更新RequestTask以正确生成多个请求的数据和值数组 - 在RequestTask.ts中将“ReqTaskStatus.WAIT_CONFIRM”更改为“ReqTaskStatus.WAIT_EXEC”。
This commit is contained in:
parent
0a6b7b69ff
commit
5475acdf80
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,11 +1,13 @@
|
|||||||
import { singleton } from 'decorators/singleton'
|
import { singleton } from 'decorators/singleton'
|
||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
|
import { TaskType } from 'models/RequestTask'
|
||||||
import { ConfirmQueue } from 'queue/confirm.queue'
|
import { ConfirmQueue } from 'queue/confirm.queue'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { TransactionReceipt, AddedAccount } from 'web3-core'
|
import { TransactionReceipt, AddedAccount } from 'web3-core'
|
||||||
import { ERC20Reactor } from './ERC20Reactor'
|
import { ERC20Reactor } from './ERC20Reactor'
|
||||||
import { ERC721Reactor } from './ERC721Reactor'
|
import { ERC721Reactor } from './ERC721Reactor'
|
||||||
import { HttpRetryProvider } from './HttpRetryProvider'
|
import { HttpRetryProvider } from './HttpRetryProvider'
|
||||||
|
import { WalletReactor } from './WalletReactor'
|
||||||
|
|
||||||
@singleton
|
@singleton
|
||||||
export class BlockChain {
|
export class BlockChain {
|
||||||
@ -14,6 +16,7 @@ export class BlockChain {
|
|||||||
private accountMaster: AddedAccount
|
private accountMaster: AddedAccount
|
||||||
public erc20Reactor: ERC20Reactor
|
public erc20Reactor: ERC20Reactor
|
||||||
public erc721Reactor: ERC721Reactor
|
public erc721Reactor: ERC721Reactor
|
||||||
|
public walletReactor: WalletReactor
|
||||||
public confirmQueue: ConfirmQueue
|
public confirmQueue: ConfirmQueue
|
||||||
public currentBlockNum: number = 0
|
public currentBlockNum: number = 0
|
||||||
|
|
||||||
@ -32,6 +35,10 @@ export class BlockChain {
|
|||||||
web3: this.web3,
|
web3: this.web3,
|
||||||
address: process.env.CHAIN_NFT_ADDRESS,
|
address: process.env.CHAIN_NFT_ADDRESS,
|
||||||
})
|
})
|
||||||
|
this.walletReactor = new WalletReactor({
|
||||||
|
web3: this.web3,
|
||||||
|
address: process.env.CHAIN_WALLET_ADDRESS,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getContractInstance(address: string, abi: any) {
|
public async getContractInstance(address: string, abi: any) {
|
||||||
@ -56,4 +63,27 @@ export class BlockChain {
|
|||||||
this.currentBlockNum = await this.web3.eth.getBlockNumber()
|
this.currentBlockNum = await this.web3.eth.getBlockNumber()
|
||||||
logger.debug(`update block num: ${this.currentBlockNum}`)
|
logger.debug(`update block num: ${this.currentBlockNum}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async generateFunAbi(reqData: any) {
|
||||||
|
let taskType = reqData.type
|
||||||
|
let abi
|
||||||
|
switch (taskType) {
|
||||||
|
case TaskType.MINT_FT:
|
||||||
|
abi = await this.erc20Reactor.mint(reqData)
|
||||||
|
break
|
||||||
|
case TaskType.MINT_NFT:
|
||||||
|
abi = await this.erc721Reactor.batchMint(reqData)
|
||||||
|
break
|
||||||
|
case TaskType.TRANSFER_FT:
|
||||||
|
abi = await this.erc20Reactor.transfer(reqData)
|
||||||
|
break
|
||||||
|
case TaskType.TRANSFER_NFT:
|
||||||
|
abi = await this.erc721Reactor.transfer(reqData)
|
||||||
|
break
|
||||||
|
case TaskType.MINT_PRESALE_BOX:
|
||||||
|
abi = await this.erc721Reactor.mint(reqData)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return abi
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ export class ERC20Reactor {
|
|||||||
amount,
|
amount,
|
||||||
account,
|
account,
|
||||||
gas,
|
gas,
|
||||||
|
encodeABI = false,
|
||||||
}: {
|
}: {
|
||||||
address: string
|
address: string
|
||||||
from: string
|
from: string
|
||||||
@ -122,19 +123,38 @@ export class ERC20Reactor {
|
|||||||
account?: string
|
account?: string
|
||||||
amount: number | string
|
amount: number | string
|
||||||
gas?: number
|
gas?: number
|
||||||
|
encodeABI: boolean
|
||||||
}) {
|
}) {
|
||||||
const contract = new this.web3.eth.Contract(abiFt, address, { from: account || this.account.address })
|
const contract = new this.web3.eth.Contract(abiFt, address, { from: account || this.account.address })
|
||||||
const amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + ''))
|
const amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + ''))
|
||||||
|
if (encodeABI) {
|
||||||
|
return contract.methods.transfer(to, amountBN).encodeABI()
|
||||||
|
}
|
||||||
return contract.methods.transferFrom(from, to, amountBN).send({
|
return contract.methods.transferFrom(from, to, amountBN).send({
|
||||||
gas: gas || 1000000,
|
gas: gas || 1000000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async mint({ address, to, amount, account }: { account?: string; address?: string; to: string; amount: string }) {
|
async mint({
|
||||||
|
address,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
account,
|
||||||
|
encodeABI = false,
|
||||||
|
}: {
|
||||||
|
account?: string
|
||||||
|
address?: string
|
||||||
|
to: string
|
||||||
|
amount: string
|
||||||
|
encodeABI: boolean
|
||||||
|
}) {
|
||||||
const contract = address
|
const contract = address
|
||||||
? new this.web3.eth.Contract(abiFt, address, { from: account || this.account.address })
|
? new this.web3.eth.Contract(abiFt, address, { from: account || this.account.address })
|
||||||
: this.contract
|
: this.contract
|
||||||
let amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + ''))
|
let amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + ''))
|
||||||
|
if (encodeABI) {
|
||||||
|
return contract.methods.mint(to, amountBN).encodeABI()
|
||||||
|
}
|
||||||
let gas = await contract.methods.mint(to, amountBN).estimateGas({ gas: 1000000 })
|
let gas = await contract.methods.mint(to, amountBN).estimateGas({ gas: 1000000 })
|
||||||
return contract.methods.mint(to, amountBN).send({ gas: (gas * 1.1) | 0 })
|
return contract.methods.mint(to, amountBN).send({ gas: (gas * 1.1) | 0 })
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@ export class ERC721Reactor {
|
|||||||
tokenId,
|
tokenId,
|
||||||
account,
|
account,
|
||||||
gas,
|
gas,
|
||||||
|
encodeABI = false,
|
||||||
}: {
|
}: {
|
||||||
address?: string
|
address?: string
|
||||||
from: string
|
from: string
|
||||||
@ -307,20 +308,37 @@ export class ERC721Reactor {
|
|||||||
tokenId: string
|
tokenId: string
|
||||||
account?: string
|
account?: string
|
||||||
gas?: number
|
gas?: number
|
||||||
|
encodeABI?: boolean
|
||||||
}) {
|
}) {
|
||||||
const contract = address
|
const contract = address
|
||||||
? new this.web3.eth.Contract(abiNft, address, { from: account || this.account.address })
|
? new this.web3.eth.Contract(abiNft, address, { from: account || this.account.address })
|
||||||
: this.contract
|
: this.contract
|
||||||
|
if (encodeABI) {
|
||||||
|
return contract.methods.safeTransferFrom(from, to, tokenId).encodeABI()
|
||||||
|
}
|
||||||
return contract.methods.safeTransferFrom(from, to, tokenId).send({
|
return contract.methods.safeTransferFrom(from, to, tokenId).send({
|
||||||
from,
|
from,
|
||||||
gas: gas || 1000000,
|
gas: gas || 1000000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async mint({ address, to, tokenId }: { address?: string; to: string; tokenId: string }) {
|
async mint({
|
||||||
|
address,
|
||||||
|
to,
|
||||||
|
tokenId,
|
||||||
|
encodeABI = false,
|
||||||
|
}: {
|
||||||
|
address?: string
|
||||||
|
to: string
|
||||||
|
tokenId: string
|
||||||
|
encodeABI?: boolean
|
||||||
|
}) {
|
||||||
const contract = address
|
const contract = address
|
||||||
? new this.web3.eth.Contract(abiNft, address, { from: this.account.address })
|
? new this.web3.eth.Contract(abiNft, address, { from: this.account.address })
|
||||||
: this.contract
|
: this.contract
|
||||||
|
if (encodeABI) {
|
||||||
|
return contract.methods.mint(to, tokenId).encodeABI()
|
||||||
|
}
|
||||||
let gas = await contract.methods.mint(to, tokenId).estimateGas({ gas: 1000000 })
|
let gas = await contract.methods.mint(to, tokenId).estimateGas({ gas: 1000000 })
|
||||||
return contract.methods.mint(to, tokenId).send({ gas: (gas * 1.1) | 0 })
|
return contract.methods.mint(to, tokenId).send({ gas: (gas * 1.1) | 0 })
|
||||||
}
|
}
|
||||||
@ -330,16 +348,21 @@ export class ERC721Reactor {
|
|||||||
address,
|
address,
|
||||||
to,
|
to,
|
||||||
tokenIds,
|
tokenIds,
|
||||||
|
encodeABI = false,
|
||||||
}: {
|
}: {
|
||||||
account?: string
|
account?: string
|
||||||
address?: string
|
address?: string
|
||||||
to: string
|
to: string
|
||||||
tokenIds: string[]
|
tokenIds: string[]
|
||||||
|
encodeABI?: boolean
|
||||||
}) {
|
}) {
|
||||||
const contract = address
|
const contract = address
|
||||||
? new this.web3.eth.Contract(abiNft, address, { from: account || this.account.address })
|
? new this.web3.eth.Contract(abiNft, address, { from: account || this.account.address })
|
||||||
: this.contract
|
: this.contract
|
||||||
// let gas = await contract.methods.batchMint(to, tokenIds, configIds).estimateGas({ gas: 1000000 })
|
// let gas = await contract.methods.batchMint(to, tokenIds, configIds).estimateGas({ gas: 1000000 })
|
||||||
|
if (encodeABI) {
|
||||||
|
return contract.methods.batchMint(to, tokenIds).encodeABI()
|
||||||
|
}
|
||||||
return contract.methods.batchMint(to, tokenIds).send({ gas: 1000000 })
|
return contract.methods.batchMint(to, tokenIds).send({ gas: 1000000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
import { Contract } from 'web3-eth-contract'
|
import { Contract } from 'web3-eth-contract'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { Account } from 'web3-core'
|
import { Account } from 'web3-core'
|
||||||
|
import { ZERO_BYTES32 } from 'common/Constants'
|
||||||
const abi = require('abis/BEMultiSigWallet.json').abi
|
const abi = require('abis/BEMultiSigWallet.json').abi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ["address", "uint256", "bytes", "uint256", "bytes32"],
|
||||||
|
* [target, value, data, predecessor, salt]
|
||||||
|
* data: method.encodeABI
|
||||||
|
* salt: generateRandomBytes32();
|
||||||
|
*/
|
||||||
|
export interface IOperationData {
|
||||||
|
scheduleId?: string
|
||||||
|
targets: string[]
|
||||||
|
values: string[]
|
||||||
|
datas: string[]
|
||||||
|
predecessor: string
|
||||||
|
salt: string
|
||||||
|
transactionHash?: string
|
||||||
|
}
|
||||||
export class WalletReactor {
|
export class WalletReactor {
|
||||||
private web3: Web3
|
private web3: Web3
|
||||||
private contract: Contract
|
private contract: Contract
|
||||||
@ -13,4 +29,97 @@ export class WalletReactor {
|
|||||||
this.account = this.web3.eth.accounts.wallet[0]
|
this.account = this.web3.eth.accounts.wallet[0]
|
||||||
this.contract = new this.web3.eth.Contract(abi, address, { from: this.account.address })
|
this.contract = new this.web3.eth.Contract(abi, address, { from: this.account.address })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启执行一个timelock的任务, 该方法需要有proposer角色
|
||||||
|
* @param {*} seconds delay的秒数
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async beginSchedule(operation: IOperationData, seconds: number) {
|
||||||
|
// let operation: any = this.genOperation(contractAddress, 0, data, ZERO_BYTES32, salt)
|
||||||
|
let res = await this.contract.methods
|
||||||
|
.scheduleBatch(
|
||||||
|
operation.targets,
|
||||||
|
operation.values,
|
||||||
|
operation.datas,
|
||||||
|
operation.predecessor,
|
||||||
|
operation.salt,
|
||||||
|
seconds,
|
||||||
|
)
|
||||||
|
.send({ gas: 1000000 })
|
||||||
|
operation.transactionHash = res.transactionHash
|
||||||
|
return operation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消定时, 该方法需要有proposer角色
|
||||||
|
* @param {bytes32} id beginSchedule返回的id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async cancelSchedule(id) {
|
||||||
|
let res = await this.contract.methods.cancel(id).send({ gas: 1000000 })
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 查询定时
|
||||||
|
* @param {bytes32} scheduleId beginSchedule返回的id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async querySchedule(scheduleId: string) {
|
||||||
|
let instance = this.contract
|
||||||
|
return this.makeBatchRequest([
|
||||||
|
instance.methods.isOperation(scheduleId).call(),
|
||||||
|
instance.methods.isOperationPending(scheduleId).call(),
|
||||||
|
instance.methods.isOperationReady(scheduleId).call(),
|
||||||
|
instance.methods.isOperationDone(scheduleId).call(),
|
||||||
|
instance.methods.getTimestamp(scheduleId).call(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 执行schedule的任务, 该方法需要有executor的role
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async executeSchedule(operation: IOperationData) {
|
||||||
|
let res = await this.contract.methods
|
||||||
|
.executeBatch(operation.targets, operation.values, operation.datas, operation.predecessor, operation.salt)
|
||||||
|
.send({ gas: 1000000 })
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装schedule的请求数据
|
||||||
|
* @param {address} targets 方法所在contract的address
|
||||||
|
* @param {uint256} values 如果调用的方法涉及到转账, 这里表示转账金额
|
||||||
|
* @param {bytes} datas 通过contract.methods.methodName(params).encodeABI()获取
|
||||||
|
* @param {bytes32} predecessor that specifies a dependency between operations, 如果没有, 则传bytes32(0)
|
||||||
|
* @param {bytes32} salt 随机串, 用于区分不同的执行任务
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
genOperation({ targets, values, datas, predecessor, salt }: IOperationData): IOperationData {
|
||||||
|
const scheduleId = this.web3.utils.keccak256(
|
||||||
|
this.web3.eth.abi.encodeParameters(
|
||||||
|
['[address]', '[uint256]', '[bytes]', 'uint256', 'bytes32'],
|
||||||
|
[targets, values, datas, predecessor, salt],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return { scheduleId, targets, values, datas, predecessor, salt }
|
||||||
|
}
|
||||||
|
|
||||||
|
makeBatchRequest(calls: any[], callFrom?: any) {
|
||||||
|
let batch = new this.web3.BatchRequest()
|
||||||
|
let promises = calls.map(call => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let request = call.request({ from: callFrom }, (error, data) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
resolve(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
batch.add(request)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
batch.execute()
|
||||||
|
return Promise.all(promises)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,5 @@
|
|||||||
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||||
|
|
||||||
|
export const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'
|
||||||
|
|
||||||
|
export const MAX_BATCH_REQ_COUNT = 50
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { getModelForClass, index, modelOptions, mongoose, pre, prop, Ref, Severity } from '@typegoose/typegoose'
|
import { getModelForClass, index, modelOptions, mongoose, pre, prop, Ref, Severity } from '@typegoose/typegoose'
|
||||||
|
import { BlockChain } from 'chain/BlockChain'
|
||||||
|
import { MAX_BATCH_REQ_COUNT, ZERO_BYTES32 } from 'common/Constants'
|
||||||
import { dbconn } from 'decorators/dbconn'
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { generateRandomBytes32 } from 'utils/wallet.util'
|
||||||
|
|
||||||
import { BaseModule } from './Base'
|
import { BaseModule } from './Base'
|
||||||
import { ReqTaskStatus, RequestTask, RequestTaskClass } from './RequestTask'
|
import { ReqTaskStatus, RequestTask, RequestTaskClass } from './RequestTask'
|
||||||
@ -13,6 +16,7 @@ export enum TaskStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
|
@index({ taskId: 1 }, { unique: false })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'chain_task', timestamps: true },
|
schemaOptions: { collection: 'chain_task', timestamps: true },
|
||||||
options: { allowMixed: Severity.ALLOW },
|
options: { allowMixed: Severity.ALLOW },
|
||||||
@ -110,20 +114,32 @@ export class ChainTaskClass extends BaseModule {
|
|||||||
let chainTask = await ChainTask.insertOrUpdate({ taskId }, { name, desc, starter, data })
|
let chainTask = await ChainTask.insertOrUpdate({ taskId }, { name, desc, starter, data })
|
||||||
let subTasks: any = []
|
let subTasks: any = []
|
||||||
if (chainTask.newRecord) {
|
if (chainTask.newRecord) {
|
||||||
|
let subTask
|
||||||
|
let index = 0
|
||||||
for (let sub of data) {
|
for (let sub of data) {
|
||||||
let subType = sub.type
|
if (!subTask || subTask.reqDatas.length >= MAX_BATCH_REQ_COUNT) {
|
||||||
let subTask = new RequestTask({
|
index += 1
|
||||||
|
subTask = new RequestTask({
|
||||||
taskId,
|
taskId,
|
||||||
|
index,
|
||||||
chainTaskId: chainTask.id,
|
chainTaskId: chainTask.id,
|
||||||
taskType: subType,
|
reqDatas: [],
|
||||||
reqData: sub,
|
|
||||||
maxTryCount,
|
maxTryCount,
|
||||||
|
predecessor: ZERO_BYTES32,
|
||||||
|
salt: generateRandomBytes32(),
|
||||||
})
|
})
|
||||||
|
subTasks.push(subTasks)
|
||||||
|
}
|
||||||
|
subTask.reqDatas.push(sub)
|
||||||
|
subTask.targets.push(sub.address)
|
||||||
|
let abi = await new BlockChain().generateFunAbi(sub)
|
||||||
|
subTask.datas.push(abi)
|
||||||
|
subTask.values.push('0')
|
||||||
await subTask.save()
|
await subTask.save()
|
||||||
chainTask.tasks.pushOnce(subTask.id)
|
chainTask.tasks.pushOnce(subTask.id)
|
||||||
subTasks.push(subTask)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chainTask.newRecord = false
|
||||||
await chainTask.save()
|
await chainTask.save()
|
||||||
return subTasks
|
return subTasks
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ export enum ReqTaskStatus {
|
|||||||
NOTSTART = 0,
|
NOTSTART = 0,
|
||||||
PEDING = 1,
|
PEDING = 1,
|
||||||
WAIT_CONFIRM = 2,
|
WAIT_CONFIRM = 2,
|
||||||
SUCCESS = 3,
|
WAIT_EXEC = 3,
|
||||||
|
SUCCESS = 4,
|
||||||
REVERT = 8,
|
REVERT = 8,
|
||||||
ERROR = 9,
|
ERROR = 9,
|
||||||
}
|
}
|
||||||
@ -34,17 +35,15 @@ export class RequestTaskClass extends BaseModule {
|
|||||||
@prop({ required: true })
|
@prop({ required: true })
|
||||||
public chainTaskId!: string
|
public chainTaskId!: string
|
||||||
|
|
||||||
@prop({ enum: TaskType, default: TaskType.UNKNOW })
|
@prop({ default: 0 })
|
||||||
public taskType: TaskType
|
public index: number
|
||||||
|
|
||||||
@prop({ required: true, default: 0 })
|
@prop({ required: true, default: 0 })
|
||||||
public tryCount: number
|
public tryCount: number
|
||||||
|
|
||||||
@prop({ required: true, default: 0 })
|
@prop({ required: true, default: 0 })
|
||||||
public maxTryCount: number
|
public maxTryCount: number
|
||||||
/**
|
|
||||||
* address: string
|
|
||||||
*/
|
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
public reqData: any
|
public reqData: any
|
||||||
|
|
||||||
@ -60,6 +59,26 @@ export class RequestTaskClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public endTime: number
|
public endTime: number
|
||||||
|
|
||||||
|
//begin for schedule
|
||||||
|
@prop()
|
||||||
|
public scheduleId: string
|
||||||
|
|
||||||
|
@prop({ type: () => [String] })
|
||||||
|
public targets: string[]
|
||||||
|
|
||||||
|
@prop({ type: () => [String] })
|
||||||
|
public datas: string[]
|
||||||
|
|
||||||
|
@prop({ type: () => [String] })
|
||||||
|
public values: string[]
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public predecessor: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public salt: string
|
||||||
|
//end for schedule
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public txHash: string
|
public txHash: string
|
||||||
/**
|
/**
|
||||||
@ -77,28 +96,10 @@ export class RequestTaskClass extends BaseModule {
|
|||||||
public errMsg: any[]
|
public errMsg: any[]
|
||||||
|
|
||||||
public async requestChain(this: DocumentType<RequestTaskClass>) {
|
public async requestChain(this: DocumentType<RequestTaskClass>) {
|
||||||
let result
|
|
||||||
let self = this
|
let self = this
|
||||||
self.blockReq = new BlockChain().currentBlockNum
|
self.blockReq = new BlockChain().currentBlockNum
|
||||||
await self.save()
|
await self.save()
|
||||||
|
let result = await new BlockChain().walletReactor.beginSchedule(self, 60)
|
||||||
switch (self.taskType) {
|
|
||||||
case TaskType.MINT_FT:
|
|
||||||
result = await new BlockChain().erc20Reactor.mint(self.reqData)
|
|
||||||
break
|
|
||||||
case TaskType.MINT_NFT:
|
|
||||||
result = await new BlockChain().erc721Reactor.batchMint(self.reqData)
|
|
||||||
break
|
|
||||||
case TaskType.TRANSFER_FT:
|
|
||||||
result = await new BlockChain().erc20Reactor.transfer(self.reqData)
|
|
||||||
break
|
|
||||||
case TaskType.TRANSFER_NFT:
|
|
||||||
result = await new BlockChain().erc721Reactor.transfer(self.reqData)
|
|
||||||
break
|
|
||||||
case TaskType.MINT_PRESALE_BOX:
|
|
||||||
result = await new BlockChain().erc721Reactor.mint(self.reqData)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
logger.info(result)
|
logger.info(result)
|
||||||
let { transactionHash } = result
|
let { transactionHash } = result
|
||||||
self.txHash = transactionHash
|
self.txHash = transactionHash
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { BlockChain } from 'chain/BlockChain'
|
||||||
import { singleton } from 'decorators/singleton'
|
import { singleton } from 'decorators/singleton'
|
||||||
import { ChainTask } from 'models/ChainTask'
|
import { ChainTask } from 'models/ChainTask'
|
||||||
import { ChainQueue } from 'queue/chain.queue'
|
import { ChainQueue } from 'queue/chain.queue'
|
||||||
@ -9,6 +10,9 @@ export class TaskSvr {
|
|||||||
let data = await new WechatWorkService().parseOneTask(spNo)
|
let data = await new WechatWorkService().parseOneTask(spNo)
|
||||||
let subTasks = await ChainTask.parseWxApprovalInfo(data)
|
let subTasks = await ChainTask.parseWxApprovalInfo(data)
|
||||||
for (let subTask of subTasks) {
|
for (let subTask of subTasks) {
|
||||||
|
let { scheduleId } = new BlockChain().walletReactor.genOperation(subTask)
|
||||||
|
subTask.scheduleId = scheduleId
|
||||||
|
await subTask.save()
|
||||||
new ChainQueue().addTaskToQueue(subTask)
|
new ChainQueue().addTaskToQueue(subTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { renderFromTokenMinimalUnit } from "./number.util";
|
import { renderFromTokenMinimalUnit } from './number.util'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes IPFS protocol prefix from input string.
|
* Removes IPFS protocol prefix from input string.
|
||||||
@ -8,13 +8,13 @@ import { renderFromTokenMinimalUnit } from "./number.util";
|
|||||||
* @throws Will throw if the url passed is not IPFS.
|
* @throws Will throw if the url passed is not IPFS.
|
||||||
*/
|
*/
|
||||||
export function removeIpfsProtocolPrefix(ipfsUrl: string) {
|
export function removeIpfsProtocolPrefix(ipfsUrl: string) {
|
||||||
if (ipfsUrl.startsWith("ipfs://ipfs/")) {
|
if (ipfsUrl.startsWith('ipfs://ipfs/')) {
|
||||||
return ipfsUrl.replace("ipfs://ipfs/", "");
|
return ipfsUrl.replace('ipfs://ipfs/', '')
|
||||||
} else if (ipfsUrl.startsWith("ipfs://")) {
|
} else if (ipfsUrl.startsWith('ipfs://')) {
|
||||||
return ipfsUrl.replace("ipfs://", "");
|
return ipfsUrl.replace('ipfs://', '')
|
||||||
}
|
}
|
||||||
// this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)
|
// this method should not be used with non-ipfs urls (i.e. startsWith('ipfs://') === true)
|
||||||
throw new Error("this method should not be used with non ipfs urls");
|
throw new Error('this method should not be used with non ipfs urls')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,16 +25,16 @@ export function removeIpfsProtocolPrefix(ipfsUrl: string) {
|
|||||||
* @throws Will throw if the url passed is not ipfs.
|
* @throws Will throw if the url passed is not ipfs.
|
||||||
*/
|
*/
|
||||||
export function getIpfsCIDv1AndPath(ipfsUrl: string): {
|
export function getIpfsCIDv1AndPath(ipfsUrl: string): {
|
||||||
cid: string;
|
cid: string
|
||||||
path?: string;
|
path?: string
|
||||||
} {
|
} {
|
||||||
const url = removeIpfsProtocolPrefix(ipfsUrl);
|
const url = removeIpfsProtocolPrefix(ipfsUrl)
|
||||||
|
|
||||||
// check if there is a path
|
// check if there is a path
|
||||||
// (CID is everything preceding first forward slash, path is everything after)
|
// (CID is everything preceding first forward slash, path is everything after)
|
||||||
const index = url.indexOf("/");
|
const index = url.indexOf('/')
|
||||||
const cid = index !== -1 ? url.substring(0, index) : url;
|
const cid = index !== -1 ? url.substring(0, index) : url
|
||||||
const path = index !== -1 ? url.substring(index) : undefined;
|
const path = index !== -1 ? url.substring(index) : undefined
|
||||||
//TODO:
|
//TODO:
|
||||||
// We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)
|
// We want to ensure that the CID is v1 (https://docs.ipfs.io/concepts/content-addressing/#identifier-formats)
|
||||||
// because most cid v0s appear to be incompatible with IPFS subdomains
|
// because most cid v0s appear to be incompatible with IPFS subdomains
|
||||||
@ -45,7 +45,7 @@ export function getIpfsCIDv1AndPath(ipfsUrl: string): {
|
|||||||
return {
|
return {
|
||||||
cid,
|
cid,
|
||||||
path,
|
path,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,9 +56,9 @@ export function getIpfsCIDv1AndPath(ipfsUrl: string): {
|
|||||||
*/
|
*/
|
||||||
export function addUrlProtocolPrefix(urlString: string): string {
|
export function addUrlProtocolPrefix(urlString: string): string {
|
||||||
if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
|
if (!urlString.match(/(^http:\/\/)|(^https:\/\/)/u)) {
|
||||||
return `https://${urlString}`;
|
return `https://${urlString}`
|
||||||
}
|
}
|
||||||
return urlString;
|
return urlString
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,21 +69,16 @@ export function addUrlProtocolPrefix(urlString: string): string {
|
|||||||
* @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.
|
* @param subdomainSupported - Boolean indicating whether the URL should be formatted with subdomains or not.
|
||||||
* @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.
|
* @returns A formatted URL, with the user's preferred IPFS gateway and format (subdomain or not), pointing to an asset hosted on IPFS.
|
||||||
*/
|
*/
|
||||||
export function getFormattedIpfsUrl(
|
export function getFormattedIpfsUrl(ipfsGateway: string, ipfsUrl: string, subdomainSupported: boolean): string {
|
||||||
ipfsGateway: string,
|
const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway))
|
||||||
ipfsUrl: string,
|
|
||||||
subdomainSupported: boolean
|
|
||||||
): string {
|
|
||||||
const { host, protocol, origin } = new URL(addUrlProtocolPrefix(ipfsGateway));
|
|
||||||
if (subdomainSupported) {
|
if (subdomainSupported) {
|
||||||
const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl);
|
const { cid, path } = getIpfsCIDv1AndPath(ipfsUrl)
|
||||||
return `${protocol}//${cid}.ipfs.${host}${path || ""}`;
|
return `${protocol}//${cid}.ipfs.${host}${path || ''}`
|
||||||
}
|
}
|
||||||
const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl);
|
const cidAndPath = removeIpfsProtocolPrefix(ipfsUrl)
|
||||||
return `${origin}/ipfs/${cidAndPath}`;
|
return `${origin}/ipfs/${cidAndPath}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given code corresponds to a smart contract.
|
* Returns whether the given code corresponds to a smart contract.
|
||||||
*
|
*
|
||||||
@ -93,11 +88,11 @@ export function getFormattedIpfsUrl(
|
|||||||
export function isSmartContractCode(code: string) {
|
export function isSmartContractCode(code: string) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
// Geth will return '0x', and ganache-core v2.2.1 will return '0x0'
|
// Geth will return '0x', and ganache-core v2.2.1 will return '0x0'
|
||||||
const smartContractCode = code !== '0x' && code !== '0x0';
|
const smartContractCode = code !== '0x' && code !== '0x0'
|
||||||
return smartContractCode;
|
return smartContractCode
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatAddress(address: string) {
|
export function formatAddress(address: string) {
|
||||||
@ -112,8 +107,18 @@ export function formatAddress(address: string) {
|
|||||||
|
|
||||||
export function formatMoney(balance: number | string, symbol: string) {
|
export function formatMoney(balance: number | string, symbol: string) {
|
||||||
if (balance === '-') {
|
if (balance === '-') {
|
||||||
return `- ${symbol}`;
|
return `- ${symbol}`
|
||||||
}
|
}
|
||||||
let money = renderFromTokenMinimalUnit(balance, 18, 4)
|
let money = renderFromTokenMinimalUnit(balance, 18, 4)
|
||||||
return `${money} ${symbol}`;
|
return `${money} ${symbol}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机的bytes32的字符串
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function generateRandomBytes32() {
|
||||||
|
const v1 = (Math.random() * 9000000 + 1000000) | 0
|
||||||
|
const v2 = (Math.random() * 900000 + 100000) | 0
|
||||||
|
return this.web3.utils.asciiToHex(v1 + '' + v2)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user