From b17c940eb2209a4f9d32559eea140389ccbed470 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Fri, 4 Sep 2020 11:54:59 +0200 Subject: [PATCH 1/5] Remove unused functions from the tbtc-auth.service Removed the unused functions that sending transactions. Since we migrated to redux, we handle sending trnasaction via redux. --- .../services/tbtc-authorization.service.js | 89 ------------------- 1 file changed, 89 deletions(-) diff --git a/solidity/dashboard/src/services/tbtc-authorization.service.js b/solidity/dashboard/src/services/tbtc-authorization.service.js index 6d114c5a54..9ff63e8745 100644 --- a/solidity/dashboard/src/services/tbtc-authorization.service.js +++ b/solidity/dashboard/src/services/tbtc-authorization.service.js @@ -93,76 +93,6 @@ const isTbtcSystemAuthorized = async (web3Context, operatorAddress) => { } } -const authorizeBondedECDSAKeepFactory = async ( - web3Context, - operatorAddress, - onTransactionHashCallback -) => { - const { stakingContract, yourAddress } = web3Context - try { - await stakingContract.methods - .authorizeOperatorContract(operatorAddress, bondedECDSAKeepFactoryAddress) - .send({ from: yourAddress }) - .on("transactionHash", onTransactionHashCallback) - } catch (error) { - throw error - } -} - -const authorizeTBTCSystem = async ( - web3Context, - operatorAddress, - onTransactionHashCallback -) => { - const { keepBondingContract, yourAddress } = web3Context - try { - const sortitionPoolAddress = await fetchSortitionPoolForTbtc(web3Context) - - await keepBondingContract.methods - .authorizeSortitionPoolContract(operatorAddress, sortitionPoolAddress) - .send({ from: yourAddress }) - .on("transactionHash", onTransactionHashCallback) - } catch (error) { - throw error - } -} - -const depositEthForOperator = async ( - web3Context, - data, - onTransactionHashCallback -) => { - const { keepBondingContract, yourAddress } = web3Context - const { operatorAddress, ethAmount } = data - const weiToAdd = web3Utils.toWei(ethAmount.toString(), "ether") - - await keepBondingContract.methods - .deposit(operatorAddress) - .send({ from: yourAddress, value: weiToAdd }) - .on("transactionHash", onTransactionHashCallback) -} - -const withdrawUnbondedEth = async ( - web3Context, - data, - onTransactionHashCallback -) => { - const { keepBondingContract, yourAddress } = web3Context - const { operatorAddress, ethAmount, managedGrantAddress } = data - const weiToWithdraw = web3Utils.toWei(ethAmount.toString(), "ether") - const contractSendMethod = managedGrantAddress - ? keepBondingContract.methods.withdrawAsManagedGrantee( - weiToWithdraw, - operatorAddress, - managedGrantAddress - ) - : keepBondingContract.methods.withdraw(weiToWithdraw, operatorAddress) - - await contractSendMethod - .send({ from: yourAddress }) - .on("transactionHash", onTransactionHashCallback) -} - const fetchBondingData = async (web3Context) => { const { yourAddress } = web3Context const bondingData = [] @@ -400,27 +330,8 @@ const fetchAvailableAmount = async (web3Context, operator) => { ) } -const deauthorizeTBTCSystem = async ( - web3Context, - operatorAddress, - onTransactionHashCallback -) => { - const { keepBondingContract, yourAddress } = web3Context - const poolAddress = await fetchSortitionPoolForTbtc(web3Context) - - await keepBondingContract.methods - .deauthorizeSortitionPoolContract(operatorAddress, poolAddress) - .send({ from: yourAddress }) - .on("transactionHash", onTransactionHashCallback) -} - export const tbtcAuthorizationService = { fetchTBTCAuthorizationData, - authorizeBondedECDSAKeepFactory, - authorizeTBTCSystem, fetchBondingData, - depositEthForOperator, - withdrawUnbondedEth, - deauthorizeTBTCSystem, fetchSortitionPoolForTbtc, } From 1980e1a59402a24baeaafbea8af3cb21482a5b6c Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Tue, 8 Sep 2020 15:45:34 +0200 Subject: [PATCH 2/5] Display copied delegations in bonding section-dapp The owner of copied delegation need to be able to add eth to bonding. --- .../services/tbtc-authorization.service.js | 200 ++++++++++++++---- 1 file changed, 153 insertions(+), 47 deletions(-) diff --git a/solidity/dashboard/src/services/tbtc-authorization.service.js b/solidity/dashboard/src/services/tbtc-authorization.service.js index 9ff63e8745..dda8ccf092 100644 --- a/solidity/dashboard/src/services/tbtc-authorization.service.js +++ b/solidity/dashboard/src/services/tbtc-authorization.service.js @@ -1,10 +1,8 @@ import { contractService } from "./contracts.service" import { TOKEN_STAKING_CONTRACT_NAME, - TOKEN_GRANT_CONTRACT_NAME, BONDED_ECDSA_KEEP_FACTORY_CONTRACT_NAME, KEEP_BONDING_CONTRACT_NAME, - MANAGED_GRANT_FACTORY_CONTRACT_NAME, } from "../constants/constants" import { add } from "../utils/arithmetics.utils" import { isEmptyArray } from "../utils/array.utils" @@ -13,12 +11,14 @@ import { getBondedECDSAKeepFactoryAddress, getTBTCSystemAddress, ContractsLoaded, + Web3Loaded, } from "../contracts" import web3Utils from "web3-utils" import { getOperatorsOfAuthorizer, getOperatorsOfOwner, } from "./token-staking.service" +import { tokenGrantsService } from "./token-grants.service" const bondedECDSAKeepFactoryAddress = getBondedECDSAKeepFactoryAddress() const tBTCSystemAddress = getTBTCSystemAddress() @@ -210,32 +210,32 @@ const fetchCreatedBonds = async ( return createdBonds } -const fetchManagedGrantAddresses = async (web3Context, lookupAddress) => { - return ( - await contractService.getPastEvents( - web3Context, - MANAGED_GRANT_FACTORY_CONTRACT_NAME, - "ManagedGrantCreated", - { - fromBlock: - CONTRACT_DEPLOY_BLOCK_NUMBER[MANAGED_GRANT_FACTORY_CONTRACT_NAME], - filter: { grantee: lookupAddress }, - } - ) - ).map((_) => _.returnValues.grantAddress) -} - const fetchOperatorsOf = async (web3Context, yourAddress) => { - // operatorAddress -> { managedGrantInfo: { address }, isWithdrawableForOperator: true } + const { + eth: { defaultAccount }, + } = await Web3Loaded + const { grantContract } = await ContractsLoaded + /** + * Operator address to details. + * @type {Map} + */ const operators = new Map() - // operators of grantee (yourAddress) - const operatorsOfGrantee = await contractService.makeCall( + // operators of authorizer + const operatorsOfAuthorizer = await getOperatorsOfAuthorizer( web3Context, - TOKEN_GRANT_CONTRACT_NAME, - "getGranteeOperators", - yourAddress + web3Context.yourAddress ) + for (let i = 0; i < operatorsOfAuthorizer.length; i++) { + operators.set(web3Utils.toChecksumAddress(operatorsOfAuthorizer[i]), { + managedGrantInfo: {}, + isWithdrawableForOperator: false, + }) + } + + // operators of grantee (yourAddress) + const operatorsOfGrantee = await getGranteeOperators() + for (let i = 0; i < operatorsOfGrantee.length; i++) { operators.set(web3Utils.toChecksumAddress(operatorsOfGrantee[i]), { managedGrantInfo: {}, @@ -243,19 +243,17 @@ const fetchOperatorsOf = async (web3Context, yourAddress) => { }) } - const managedGrantAddresses = await fetchManagedGrantAddresses( - web3Context, - yourAddress - ) - for (const managedGrantAddress of managedGrantAddresses) { + const managedGrants = await tokenGrantsService.fetchManagedGrants() + + for (const managedGrant of managedGrants) { + const managedGrantAddress = + managedGrant.managedGrantContractInstance.options.address // operators of grantee (managedGrantAddress) - const operatorsOfManagedGrant = await contractService.makeCall( - web3Context, - TOKEN_GRANT_CONTRACT_NAME, - "getGranteeOperators", - managedGrantAddress - ) - for (const operatorOfManagedGrant of operatorsOfManagedGrant) { + const operatorsOfManagedGrant = await grantContract.methods + .getGranteeOperators(managedGrantAddress) + .call() + const allOperators = await getAllGranteeOperators(operatorsOfManagedGrant) + for (const operatorOfManagedGrant of allOperators) { operators.set(web3Utils.toChecksumAddress(operatorOfManagedGrant), { managedGrantInfo: { address: managedGrantAddress }, isWithdrawableForOperator: true, @@ -263,18 +261,6 @@ const fetchOperatorsOf = async (web3Context, yourAddress) => { } } - // operators of authorizer - const operatorsOfAuthorizer = await getOperatorsOfAuthorizer( - web3Context, - web3Context.yourAddress - ) - for (let i = 0; i < operatorsOfAuthorizer.length; i++) { - operators.set(web3Utils.toChecksumAddress(operatorsOfAuthorizer[i]), { - managedGrantInfo: {}, - isWithdrawableForOperator: false, - }) - } - // operators of owner (yourAddress as owner) const operatorsOfOwner = await getOperatorsOfOwner(yourAddress) @@ -285,6 +271,20 @@ const fetchOperatorsOf = async (web3Context, yourAddress) => { }) } + const copiedOperatorsFromLiquidTokens = await getCopiedOperatorsFromLiquidTokens( + defaultAccount, + Array.from(operators.keys()) + ) + for (let i = 0; i < copiedOperatorsFromLiquidTokens.length; i++) { + operators.set( + web3Utils.toChecksumAddress(copiedOperatorsFromLiquidTokens[i]), + { + managedGrantInfo: {}, + isWithdrawableForOperator: true, + } + ) + } + const ownerAddress = await contractService.makeCall( web3Context, TOKEN_STAKING_CONTRACT_NAME, @@ -330,6 +330,112 @@ const fetchAvailableAmount = async (web3Context, operator) => { ) } +const getGranteeOperators = async () => { + const web3 = await Web3Loaded + const { defaultAccount } = web3.eth + const { grantContract } = await ContractsLoaded + + // Fetch all grantee operators. These are not all grantee operators, + // since `TokenGrant` contract does not know about escrow redelagtion. + const operatorsOfGrantee = await grantContract.methods + .getGranteeOperators(defaultAccount) + .call() + + return await getAllGranteeOperators(operatorsOfGrantee) +} + +const getAllGranteeOperators = async (operatorsOfGrantee) => { + const { stakingContract, tokenStakingEscrow } = await ContractsLoaded + + if (isEmptyArray(operatorsOfGrantee)) { + return [] + } + + // We need to take into account that the delegation from a grant can be redelegated to a new operator. + const grantIdsToScan = ( + await tokenStakingEscrow.getPastEvents("DepositRedelegated", { + fromBlock: CONTRACT_DEPLOY_BLOCK_NUMBER.tokenStakingEscrow, + filter: { + previousOperator: operatorsOfGrantee, + }, + }) + ).map((_) => _.returnValues.grantId) + + let activeOperators = [] + const newOperators = [] + const previousOperators = [] + const redelagations = isEmptyArray(grantIdsToScan) + ? [] + : await tokenStakingEscrow.getPastEvents("DepositRedelegated", { + fromBlock: CONTRACT_DEPLOY_BLOCK_NUMBER.tokenStakingEscrow, + filter: { + grantId: grantIdsToScan, + }, + }) + + redelagations.forEach((redelegation) => { + newOperators.push(redelegation.returnValues.newOperator) + previousOperators.push(redelegation.returnValues.previousOperator) + }) + + newOperators.forEach((operator) => { + const indexOf = previousOperators.indexOf(operator) + if (indexOf > -1) { + previousOperators.splice(indexOf, 1) + } else { + activeOperators.push(operator) + } + }) + + // Filter out obsolete operators from `TokenGrant::getGranteeOperators`. + const activeOperatorsFromTokenGrant = operatorsOfGrantee.filter( + (operator) => !previousOperators.includes(operator) + ) + activeOperators = activeOperators.concat(activeOperatorsFromTokenGrant) + + return isEmptyArray(activeOperators) + ? activeOperators + : // Scan `OperatorStaked` events to make sure we only return operators, from a "new" `TokenStaking`, + // since `TokenGrant` stores the old grantee-operator relationship. + ( + await stakingContract.getPastEvents("OperatorStaked", { + fromBlock: CONTRACT_DEPLOY_BLOCK_NUMBER.stakingContract, + filter: { operator: activeOperators }, + }) + ).map((_) => _.returnValues.operator) +} + +const getCopiedOperatorsFromLiquidTokens = async ( + ownerOrGrantee, + operatorsToFilterOut +) => { + const { stakingPortBackerContract } = await ContractsLoaded + + const operatorsToCheck = ( + await stakingPortBackerContract.getPastEvents("StakeCopied", { + fromBlock: CONTRACT_DEPLOY_BLOCK_NUMBER.stakingPortBackerContract, + filter: { owner: ownerOrGrantee }, + }) + ) + .map((_) => _.returnValues.operator) + .filter( + (operatorAddress) => !operatorsToFilterOut.includes(operatorAddress) + ) + + // No operators. + if (isEmptyArray(operatorsToCheck)) { + return [] + } + + // We only want operators for whom the delegation has not been paid back. + const operatorsOfPortBacker = await getOperatorsOfOwner( + stakingPortBackerContract.options.address, + operatorsToCheck + ) + + return operatorsOfPortBacker +} + export const tbtcAuthorizationService = { fetchTBTCAuthorizationData, fetchBondingData, From 91e6fdb9ac4de47e2550c7e66f529587a30c18ab Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Wed, 9 Sep 2020 14:22:03 +0200 Subject: [PATCH 3/5] Fix typo redelagation -> redelegation --- solidity/dashboard/src/services/tbtc-authorization.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/dashboard/src/services/tbtc-authorization.service.js b/solidity/dashboard/src/services/tbtc-authorization.service.js index dda8ccf092..a4997378eb 100644 --- a/solidity/dashboard/src/services/tbtc-authorization.service.js +++ b/solidity/dashboard/src/services/tbtc-authorization.service.js @@ -336,7 +336,7 @@ const getGranteeOperators = async () => { const { grantContract } = await ContractsLoaded // Fetch all grantee operators. These are not all grantee operators, - // since `TokenGrant` contract does not know about escrow redelagtion. + // since `TokenGrant` contract does not know about escrow redelegtion. const operatorsOfGrantee = await grantContract.methods .getGranteeOperators(defaultAccount) .call() From 78469b852ec6d67c9d7891955c8dc13e99629019 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Wed, 9 Sep 2020 14:22:41 +0200 Subject: [PATCH 4/5] Disable a withdraw btn in bonding section Disable withdraw btn for copied delegations from liquid tokens. From `TokenStaking` contract's perspective, the `StakingPortBacker` is an owner of the copied delegation, so the "real" owner cannot withdraw bond. --- .../dashboard/src/services/tbtc-authorization.service.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/solidity/dashboard/src/services/tbtc-authorization.service.js b/solidity/dashboard/src/services/tbtc-authorization.service.js index a4997378eb..ec098262f0 100644 --- a/solidity/dashboard/src/services/tbtc-authorization.service.js +++ b/solidity/dashboard/src/services/tbtc-authorization.service.js @@ -280,7 +280,10 @@ const fetchOperatorsOf = async (web3Context, yourAddress) => { web3Utils.toChecksumAddress(copiedOperatorsFromLiquidTokens[i]), { managedGrantInfo: {}, - isWithdrawableForOperator: true, + // From the `TokenStaking` contract's perspective, + // the `StakingPortBacker` contract is an owner of the copied delegation, + // so the "real" owner cannot withdraw bond. + isWithdrawableForOperator: false, } ) } From c5f1222989fcd1e1f510f275146d37284b1aef25 Mon Sep 17 00:00:00 2001 From: Rafal Czajkowski Date: Wed, 9 Sep 2020 15:25:57 +0200 Subject: [PATCH 5/5] Fix fixed typo :facepalm: redelegtion -> redelegation. --- solidity/dashboard/src/services/tbtc-authorization.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/dashboard/src/services/tbtc-authorization.service.js b/solidity/dashboard/src/services/tbtc-authorization.service.js index ec098262f0..7c9eb81ba1 100644 --- a/solidity/dashboard/src/services/tbtc-authorization.service.js +++ b/solidity/dashboard/src/services/tbtc-authorization.service.js @@ -339,7 +339,7 @@ const getGranteeOperators = async () => { const { grantContract } = await ContractsLoaded // Fetch all grantee operators. These are not all grantee operators, - // since `TokenGrant` contract does not know about escrow redelegtion. + // since `TokenGrant` contract does not know about escrow redelegation. const operatorsOfGrantee = await grantContract.methods .getGranteeOperators(defaultAccount) .call()