151 lines
5.3 KiB
TypeScript
151 lines
5.3 KiB
TypeScript
import { Contract } from 'web3-eth-contract'
|
|
import Web3 from 'web3'
|
|
import { Account } from 'web3-core'
|
|
import { ZERO_BYTES32 } from 'common/Constants'
|
|
import { generateRandomBytes32 } from 'utils/wallet.util'
|
|
const abi = require('../../config/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 {
|
|
private web3: Web3
|
|
private contract: Contract
|
|
private account: Account
|
|
|
|
constructor({ web3, address }: { web3: Web3; address: string }) {
|
|
this.web3 = web3
|
|
this.account = this.web3.eth.accounts.wallet[0]
|
|
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 gas = await this.contract.methods
|
|
.schedule(operation.targets, operation.values, operation.datas, operation.predecessor, operation.salt, seconds)
|
|
.estimateGas({ from: this.account.address })
|
|
let res = await this.contract.methods
|
|
.schedule(operation.targets, operation.values, operation.datas, operation.predecessor, operation.salt, seconds)
|
|
.send({ gas: gas | 0 })
|
|
operation.transactionHash = res.transactionHash
|
|
return operation
|
|
}
|
|
|
|
/**
|
|
* 取消定时, 该方法需要有proposer角色
|
|
* @param {bytes32} id beginSchedule返回的id
|
|
* @returns
|
|
*/
|
|
async cancelSchedule(id) {
|
|
let gas = await this.contract.methods.cancel(id).estimateGas({ from: this.account.address })
|
|
let res = await this.contract.methods.cancel(id).send({ gas: gas | 0 })
|
|
return res
|
|
}
|
|
/**
|
|
* 查询定时
|
|
* @param {bytes32} scheduleId beginSchedule返回的id
|
|
* @returns
|
|
*/
|
|
async querySchedule(scheduleId: string) {
|
|
let instance = this.contract
|
|
return this.makeBatchRequest([
|
|
// 查询的scheduleid是否在合约中, 包含所有状态
|
|
instance.methods.isOperation(scheduleId).call,
|
|
// 查询的scheduleid是否在pending状态
|
|
instance.methods.isOperationPending(scheduleId).call,
|
|
// 查询的scheduleid是否已经到了执行时间
|
|
instance.methods.isOperationReady(scheduleId).call,
|
|
// 查询的scheduleid是否已经执行完成
|
|
instance.methods.isOperationDone(scheduleId).call,
|
|
// 查询的scheduleid是否已经满足可执行的confirm数量
|
|
instance.methods.isConfirmed(scheduleId).call,
|
|
// 查询的scheduleid的可执行时间
|
|
instance.methods.getTimestamp(scheduleId).call,
|
|
])
|
|
}
|
|
/**
|
|
* 执行schedule的任务, 该方法需要有executor的role
|
|
* @returns
|
|
*/
|
|
async executeSchedule(operation: IOperationData) {
|
|
let gas = await this.contract.methods
|
|
.execute(operation.targets, operation.values, operation.datas, operation.predecessor, operation.salt)
|
|
.estimateGas({ from: this.account.address })
|
|
gas = gas | 0
|
|
let res = await this.contract.methods
|
|
.execute(operation.targets, operation.values, operation.datas, operation.predecessor, operation.salt)
|
|
.send({ gas })
|
|
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)
|
|
}
|
|
|
|
async updateRequired(num: number) {
|
|
let contractAddress = [process.env.CHAIN_WALLET_ADDRESS]
|
|
let values = ['0']
|
|
let abi = await this.contract.methods.changeRequirement(num + '').encodeABI()
|
|
let salt = generateRandomBytes32()
|
|
let operation: any = this.genOperation({
|
|
targets: contractAddress,
|
|
values,
|
|
datas: [abi],
|
|
predecessor: ZERO_BYTES32,
|
|
salt,
|
|
})
|
|
operation = await this.beginSchedule(operation, 60)
|
|
return operation
|
|
}
|
|
}
|