From 21cf9d4a523d3a5caf78a4ecea75881c1b84379c Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 23 Sep 2020 11:27:45 +0200 Subject: [PATCH 1/2] Check bonded ETH for operators We are getting bond creation events to obtain values of bonds created for all the ECDSA operators. --- inspector/scripts/inspect-operators.js | 42 +++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/inspector/scripts/inspect-operators.js b/inspector/scripts/inspect-operators.js index de252cd..2434e4a 100644 --- a/inspector/scripts/inspect-operators.js +++ b/inspector/scripts/inspect-operators.js @@ -1,6 +1,8 @@ const truffleContract = require("@truffle/contract") const clc = require("cli-color") +const contractHelper = require("./lib/contract-helper") + const KeepTokenJson = require("@keep-network/keep-core/artifacts/KeepToken.json") const TokenStakingJson = require("@keep-network/keep-core/artifacts/TokenStaking.json") const KeepBondingJson = require("@keep-network/keep-ecdsa/artifacts/KeepBonding.json") @@ -41,6 +43,11 @@ module.exports = async function () { const keepBonding = await KeepBonding.deployed() const tbtcSystem = await TBTCSystem.deployed() + const deploymentBlock = await contractHelper.getDeploymentBlockNumber( + KeepBondingJson, + web3, + ) + console.log(clc.yellow(`*** Contract Addresses ***`)) console.log(`KeepToken: ${keepToken.address}`) console.log(`TokenStaking: ${tokenStaking.address}`) @@ -102,9 +109,36 @@ module.exports = async function () { ) console.log(``) + const bondCreatedEvents = await keepBonding.getPastEvents("BondCreated", { + fromBlock: deploymentBlock, + toBlock: "latest", + }) + + const totalOperatorBondsAmount = {} + const totalBondedAmount = web3.utils.toBN(0) + + for (let i = 0; i < bondCreatedEvents.length; i++) { + const event = bondCreatedEvents[i] + + const operator = event.args.operator.toLowerCase() + const bondAmount = web3.utils.toBN(event.args.amount) + + totalBondedAmount.iadd(bondAmount) + + if (totalOperatorBondsAmount[operator] === undefined) { + totalOperatorBondsAmount[operator] = bondAmount + } else { + totalOperatorBondsAmount[operator].iadd(bondAmount) + } + } + + console.log( + `Total bonded ETH: ${web3.utils.fromWei(totalBondedAmount).toString()}`, + ) + const ecdsaSummary = [] for (let i = 0; i < ecdsaOperators.length; i++) { - const operator = ecdsaOperators[i] + const operator = ecdsaOperators[i].toLowerCase() const eligibleStake = await tokenStaking.eligibleStake( operator, @@ -133,17 +167,23 @@ module.exports = async function () { isUpToDateInTbtcPool = "N/A" } + const bondsAmountEth = web3.utils.fromWei( + web3.utils.toBN(totalOperatorBondsAmount[operator] || 0), + ) + ecdsaSummary.push({ address: operator, eligibleStakeKeep: eligibleStakeKeep.toString(), operatorBalanceEth: operatorBalanceEth.toString(), unbondedValueEth: unbondedValueEth.toString(), + bondsAmountEth: bondsAmountEth.toString(), isRegisteredInTbtcPool: isRegisteredInTbtcPool, isUpToDateInTbtcPool: isUpToDateInTbtcPool, }) } console.log(clc.yellow(`*** ECDSA Operators ***`)) + if (process.env.OUTPUT_MODE === "text") { ecdsaSummary.forEach((s) => console.log( From a94ccf427d799545572ede752e3d3798ba300836 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 23 Sep 2020 17:47:04 +0200 Subject: [PATCH 2/2] Updated bond amounts calculation scripts We need to respect bonds release and seizure in calculating currently bonded value. What's more we need to respect the fact that value can be deposited as a return from tBTC after seizing the whole bond and keeping there just some part of it. --- inspector/scripts/inspect-operators.js | 260 ++++++++++++++++++++++--- 1 file changed, 237 insertions(+), 23 deletions(-) diff --git a/inspector/scripts/inspect-operators.js b/inspector/scripts/inspect-operators.js index 2434e4a..f57588f 100644 --- a/inspector/scripts/inspect-operators.js +++ b/inspector/scripts/inspect-operators.js @@ -109,31 +109,224 @@ module.exports = async function () { ) console.log(``) - const bondCreatedEvents = await keepBonding.getPastEvents("BondCreated", { - fromBlock: deploymentBlock, - toBlock: "latest", - }) + getDepositedAmount = async function () { + bondSeizedEventSignature = web3.utils.sha3( + "BondSeized(address,uint256,address,uint256)", + ) - const totalOperatorBondsAmount = {} - const totalBondedAmount = web3.utils.toBN(0) + const operatorsDepositedAmount = {} + const totalDepositedAmount = web3.utils.toBN(0) - for (let i = 0; i < bondCreatedEvents.length; i++) { - const event = bondCreatedEvents[i] + const events = await keepBonding.getPastEvents("UnbondedValueDeposited", { + fromBlock: deploymentBlock, + }) - const operator = event.args.operator.toLowerCase() - const bondAmount = web3.utils.toBN(event.args.amount) + eventsLoop: for (let i = 0; i < events.length; i++) { + const event = events[i] - totalBondedAmount.iadd(bondAmount) + const operator = event.args.operator.toLowerCase() + const amount = web3.utils.toBN(event.args.amount || 0) - if (totalOperatorBondsAmount[operator] === undefined) { - totalOperatorBondsAmount[operator] = bondAmount - } else { - totalOperatorBondsAmount[operator].iadd(bondAmount) + // Part of seized bond amount is being returned by tBTC as a unbonded + // value deposit. We want to check if the deposited value came from + // a returned seized amount and if so don't include it in the + // deposited amount. + const transaction = await web3.eth.getTransactionReceipt( + event.transactionHash, + ) + + for (let i = 0; i < transaction.logs.length; i++) { + const log = transaction.logs[i] + + if (log.topics[0] === bondSeizedEventSignature) { + continue eventsLoop + } + } + + if (operatorsDepositedAmount[operator] === undefined) { + operatorsDepositedAmount[operator] = amount + } else { + operatorsDepositedAmount[operator].iadd(amount) + } + + totalDepositedAmount.iadd(amount) + } + + return { + forOperators: operatorsDepositedAmount, + total: totalDepositedAmount, + } + } + + getWithdrawnAmount = async function (eventName) { + const operatorsWithdrawnAmount = {} + const totalWithdrawnAmount = web3.utils.toBN(0) + + const events = await keepBonding.getPastEvents("UnbondedValueWithdrawn", { + fromBlock: deploymentBlock, + }) + + for (let i = 0; i < events.length; i++) { + const event = events[i] + + const operator = event.args.operator.toLowerCase() + const amount = web3.utils.toBN(event.args.amount || 0) + + if (operatorsWithdrawnAmount[operator] === undefined) { + operatorsWithdrawnAmount[operator] = amount + } else { + operatorsWithdrawnAmount[operator].iadd(amount) + } + + totalWithdrawnAmount.iadd(amount) + } + + return { + forOperators: operatorsWithdrawnAmount, + total: totalWithdrawnAmount, + } + } + + getBondedAmount = async function () { + const operatorsBondedAmount = {} + const totalBondedAmount = web3.utils.toBN(0) + + const events = await keepBonding.getPastEvents("BondCreated", { + fromBlock: deploymentBlock, + }) + + for (let i = 0; i < events.length; i++) { + const event = events[i] + + const operator = event.args.operator.toLowerCase() + const bondAmount = web3.utils.toBN(event.args.amount || 0) + const bondReferenceID = event.args.referenceID + + // Check if the bond has been released. If so don't include the bond in + // the result and skip to a next event. + const releasedEvents = await keepBonding.getPastEvents("BondReleased", { + fromBlock: deploymentBlock, + filter: { + operator: operator, + referenceID: bondReferenceID.toString(), + }, + }) + if (releasedEvents.length > 0) { + continue + } + + // Check if the bond has been seized. If so subtract the seized amount from + // the bonded amount. + const seizedEvents = await keepBonding.getPastEvents("BondSeized", { + fromBlock: deploymentBlock, + filter: { + operator: operator, + referenceID: bondReferenceID.toString(), + }, + }) + + for (let i = 0; i < seizedEvents.length; i++) { + const seizedEvent = seizedEvents[i] + const seizedAmount = seizedEvent.args.amount + bondAmount.isub(seizedAmount) + } + + if (operatorsBondedAmount[operator] === undefined) { + operatorsBondedAmount[operator] = bondAmount + } else { + operatorsBondedAmount[operator].iadd(bondAmount) + } + + totalBondedAmount.iadd(bondAmount) + } + + return { forOperators: operatorsBondedAmount, total: totalBondedAmount } + } + + getSeizedAmount = async function () { + depositedEventSignature = web3.utils.sha3( + "UnbondedValueDeposited(address,address,uint256)", + ) + + const operatorsSeizedAmount = {} + const totalSeizedAmount = web3.utils.toBN(0) + + const events = await keepBonding.getPastEvents("BondSeized", { + fromBlock: deploymentBlock, + }) + + eventsLoop: for (let i = 0; i < events.length; i++) { + const event = events[i] + const operator = event.args.operator.toLowerCase() + const grossSeizedAmount = web3.utils.toBN(event.args.amount || 0) + + // Part of seized bond amount is being returned by tBTC as a unbonded + // value deposit. We want to find the value that got returned so we can + // find actual seized amount (net seized amount). + const transaction = await web3.eth.getTransactionReceipt( + event.transactionHash, + ) + + // Check other events emitted in the same transaction that bond has been + // seized. + for (let i = 0; i < transaction.logs.length; i++) { + const log = transaction.logs[i] + + encodedOperator = web3.eth.abi.encodeParameter("address", operator) + + if ( + log.topics[0] === depositedEventSignature && + log.topics[1] === encodedOperator + ) { + const returnedAmount = web3.utils.toBN(log.data) + + const netSeizedAmount = grossSeizedAmount.sub(returnedAmount) + + if (operatorsSeizedAmount[operator] === undefined) { + operatorsSeizedAmount[operator] = netSeizedAmount + } else { + operatorsSeizedAmount[operator].iadd(netSeizedAmount) + } + + totalSeizedAmount.iadd(netSeizedAmount) + } + } } + + return { forOperators: operatorsSeizedAmount, total: totalSeizedAmount } } + // Operators' Deposited Amount + const depositedAmounts = await getDepositedAmount() + + // Operators' Withdrawn Amount + const withdrawnAmounts = await getWithdrawnAmount() + + // Operators' Currently Bonded Amount + const bondedAmounts = await getBondedAmount() + + // Operators' Seized Bonds Amount + const bondsSeizedAmounts = await getSeizedAmount() + + // Total Values + console.log( + `Total Deposited Amount ETH: ` + + `${web3.utils.fromWei(depositedAmounts.total).toString()}`, + ) + console.log( - `Total bonded ETH: ${web3.utils.fromWei(totalBondedAmount).toString()}`, + `Total Withdrawn Amount ETH: ` + + `${web3.utils.fromWei(withdrawnAmounts.total).toString()}`, + ) + + console.log( + `Total Currently Bonded Amount ETH: ` + + `${web3.utils.fromWei(bondedAmounts.total).toString()}`, + ) + + console.log( + `Total Seized Bonds ETH: ` + + `${web3.utils.fromWei(bondsSeizedAmounts.total).toString()}`, ) const ecdsaSummary = [] @@ -152,6 +345,26 @@ module.exports = async function () { const unbondedValue = await keepBonding.unbondedValue(operator) const unbondedValueEth = web3.utils.fromWei(unbondedValue) + const depositedAmount = web3.utils.toBN( + depositedAmounts.forOperators[operator] || 0, + ) + const depositedAmountEth = web3.utils.fromWei(depositedAmount) + + const withdrawnAmount = web3.utils.toBN( + withdrawnAmounts.forOperators[operator] || 0, + ) + const withdrawnAmountEth = web3.utils.fromWei(withdrawnAmount) + + const bondedAmount = web3.utils.toBN( + bondedAmounts.forOperators[operator] || 0, + ) + const bondedAmountEth = web3.utils.fromWei(bondedAmount) + + const bondsSeizedAmount = web3.utils.toBN( + bondsSeizedAmounts.forOperators[operator] || 0, + ) + const bondsSeizedAmountEth = web3.utils.fromWei(bondsSeizedAmount) + const isRegisteredInTbtcPool = await bondedEcdsaKeepFactory.isOperatorRegistered( operator, tbtcSystem.address, @@ -167,16 +380,15 @@ module.exports = async function () { isUpToDateInTbtcPool = "N/A" } - const bondsAmountEth = web3.utils.fromWei( - web3.utils.toBN(totalOperatorBondsAmount[operator] || 0), - ) - ecdsaSummary.push({ address: operator, eligibleStakeKeep: eligibleStakeKeep.toString(), operatorBalanceEth: operatorBalanceEth.toString(), + depositedAmountEth: depositedAmountEth.toString(), + withdrawnAmountEth: withdrawnAmountEth.toString(), unbondedValueEth: unbondedValueEth.toString(), - bondsAmountEth: bondsAmountEth.toString(), + bondsAmountEth: bondedAmountEth.toString(), + seizedAmountEth: bondsSeizedAmountEth.toString(), isRegisteredInTbtcPool: isRegisteredInTbtcPool, isUpToDateInTbtcPool: isUpToDateInTbtcPool, }) @@ -188,7 +400,9 @@ module.exports = async function () { ecdsaSummary.forEach((s) => console.log( `${s.address} ${s.eligibleStakeKeep} ${s.operatorBalanceEth} ` + - `${s.unbondedValueEth} ${s.isRegisteredInTbtcPool} ${s.isUpToDateInTbtcPool}`, + `${s.depositedAmountEth} ${s.withdrawnAmountEth} ${s.unbondedValueEth} ` + + `${s.bondsAmountEth} ${s.seizedAmountEth} ${s.isRegisteredInTbtcPool} ` + + `${s.isUpToDateInTbtcPool}`, ), ) } else { @@ -199,7 +413,7 @@ module.exports = async function () { process.exit(0) } catch (error) { - console.log(`ERROR: ${error}`) + console.trace(error) process.exit(1) } }