site-activity-chain/src/chain/WalletReactor.ts
2024-01-12 19:31:42 +08:00

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
}
}