|
| 1 | +import consola from 'consola' |
| 2 | +import { task } from 'hardhat/config' |
| 3 | +import { HardhatRuntimeEnvironment } from 'hardhat/types' |
| 4 | + |
| 5 | +import { askConfirm, getTokenLockManagerOrFail, prettyEnv, waitTransaction } from './create' |
| 6 | +import { TxBuilder } from './tx-builder' |
| 7 | + |
| 8 | +const logger = consola.create({}) |
| 9 | + |
| 10 | +task('update-auth-functions-horizon', 'Update authorized functions for Horizon upgrade') |
| 11 | + .addParam('horizonStakingAddress', 'Address of the HorizonStaking contract') |
| 12 | + .addParam('subgraphServiceAddress', 'Address of the SubgraphService contract') |
| 13 | + .addParam('managerName', 'Name of the token lock manager deployment', 'GraphTokenLockManager') |
| 14 | + .addFlag('txBuilder', 'Output transaction batch in JSON format for Safe multisig') |
| 15 | + .addOptionalParam('txBuilderTemplate', 'File to use as a template for the transaction builder') |
| 16 | + .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => { |
| 17 | + const manager = await getTokenLockManagerOrFail(hre, taskArgs.managerName) |
| 18 | + |
| 19 | + logger.info('Updating authorized functions for Horizon upgrade...') |
| 20 | + logger.log(`> GraphTokenLockManager: ${manager.address}`) |
| 21 | + logger.log(`> HorizonStaking: ${taskArgs.horizonStakingAddress}`) |
| 22 | + logger.log(`> SubgraphService: ${taskArgs.subgraphServiceAddress}`) |
| 23 | + |
| 24 | + logger.log(await prettyEnv(hre)) |
| 25 | + |
| 26 | + // Functions to ADD for HorizonStaking |
| 27 | + const horizonStakingFunctionsToAdd = [ |
| 28 | + 'provisionLocked(address,address,uint256,uint32,uint64)', |
| 29 | + 'thaw(address,address,uint256)', |
| 30 | + 'deprovision(address,address,uint256)', |
| 31 | + 'setDelegationFeeCut(address,address,uint8,uint256)', |
| 32 | + 'setOperatorLocked(address,address,bool)', |
| 33 | + 'withdrawDelegated(address,address,uint256)', |
| 34 | + ] |
| 35 | + |
| 36 | + // Functions to ADD for SubgraphService |
| 37 | + const subgraphServiceFunctionsToAdd = ['setPaymentsDestination(address)'] |
| 38 | + |
| 39 | + // Functions to REMOVE for old Staking contract |
| 40 | + const functionsToRemove = [ |
| 41 | + 'setDelegationParameters(uint32,uint32,uint32)', |
| 42 | + 'setOperator(address,bool)', |
| 43 | + 'setRewardsDestination(address)', |
| 44 | + ] |
| 45 | + |
| 46 | + logger.info('\n=== Functions to be ADDED ===') |
| 47 | + logger.info('For HorizonStaking:') |
| 48 | + horizonStakingFunctionsToAdd.forEach((sig) => logger.log(` + ${sig}`)) |
| 49 | + logger.info('\nFor SubgraphService:') |
| 50 | + subgraphServiceFunctionsToAdd.forEach((sig) => logger.log(` + ${sig}`)) |
| 51 | + |
| 52 | + logger.info('\n=== Functions to be REMOVED ===') |
| 53 | + functionsToRemove.forEach((sig) => logger.log(` - ${sig}`)) |
| 54 | + |
| 55 | + // Check if not using tx-builder that deployer is the manager owner |
| 56 | + if (!taskArgs.txBuilder) { |
| 57 | + const tokenLockManagerOwner = await manager.owner() |
| 58 | + const { deployer } = await hre.getNamedAccounts() |
| 59 | + if (tokenLockManagerOwner !== deployer) { |
| 60 | + logger.error('Only the owner can update authorized functions') |
| 61 | + process.exit(1) |
| 62 | + } |
| 63 | + logger.success(`\nDeployer is the manager owner: ${deployer}`) |
| 64 | + } |
| 65 | + |
| 66 | + // Confirm before proceeding |
| 67 | + logger.info('\n=== Summary ===') |
| 68 | + logger.info(`Functions to remove: ${functionsToRemove.length}`) |
| 69 | + logger.info(`Functions to add for HorizonStaking: ${horizonStakingFunctionsToAdd.length}`) |
| 70 | + logger.info(`Functions to add for SubgraphService: ${subgraphServiceFunctionsToAdd.length}`) |
| 71 | + |
| 72 | + if (!(await askConfirm())) { |
| 73 | + logger.log('Cancelled') |
| 74 | + process.exit(1) |
| 75 | + } |
| 76 | + |
| 77 | + if (taskArgs.txBuilder) { |
| 78 | + // Generate tx-builder JSON for Safe multisig |
| 79 | + logger.info('\nCreating transaction builder JSON file for Safe multisig...') |
| 80 | + const chainId = (await hre.ethers.provider.getNetwork()).chainId.toString() |
| 81 | + const txBuilder = new TxBuilder(chainId, taskArgs.txBuilderTemplate) |
| 82 | + |
| 83 | + // Add transactions to remove old functions |
| 84 | + logger.log('\nBuilding transactions to remove old functions...') |
| 85 | + for (const signature of functionsToRemove) { |
| 86 | + const tx = await manager.populateTransaction.unsetAuthFunctionCall(signature) |
| 87 | + txBuilder.addTx({ |
| 88 | + to: manager.address, |
| 89 | + value: '0', |
| 90 | + data: tx.data, |
| 91 | + }) |
| 92 | + logger.log(` - Remove: ${signature}`) |
| 93 | + } |
| 94 | + |
| 95 | + // Add transactions to add HorizonStaking functions |
| 96 | + logger.log('\nBuilding transactions to add HorizonStaking functions...') |
| 97 | + const horizonTargets = Array(horizonStakingFunctionsToAdd.length).fill(taskArgs.horizonStakingAddress) |
| 98 | + const tx1 = await manager.populateTransaction.setAuthFunctionCallMany( |
| 99 | + horizonStakingFunctionsToAdd, |
| 100 | + horizonTargets, |
| 101 | + ) |
| 102 | + txBuilder.addTx({ |
| 103 | + to: manager.address, |
| 104 | + value: '0', |
| 105 | + data: tx1.data, |
| 106 | + }) |
| 107 | + logger.log(` + Added ${horizonStakingFunctionsToAdd.length} functions for HorizonStaking`) |
| 108 | + |
| 109 | + // Add transactions to add SubgraphService functions |
| 110 | + logger.log('\nBuilding transactions to add SubgraphService functions...') |
| 111 | + const subgraphTargets = Array(subgraphServiceFunctionsToAdd.length).fill(taskArgs.subgraphServiceAddress) |
| 112 | + const tx2 = await manager.populateTransaction.setAuthFunctionCallMany( |
| 113 | + subgraphServiceFunctionsToAdd, |
| 114 | + subgraphTargets, |
| 115 | + ) |
| 116 | + txBuilder.addTx({ |
| 117 | + to: manager.address, |
| 118 | + value: '0', |
| 119 | + data: tx2.data, |
| 120 | + }) |
| 121 | + logger.log(` + Added ${subgraphServiceFunctionsToAdd.length} functions for SubgraphService`) |
| 122 | + |
| 123 | + // Add token destinations if needed |
| 124 | + logger.log('\nChecking and adding token destinations if needed...') |
| 125 | + |
| 126 | + // Check if HorizonStaking is already a token destination |
| 127 | + const isHorizonStakingDestination = await manager.isTokenDestination(taskArgs.horizonStakingAddress) |
| 128 | + if (!isHorizonStakingDestination) { |
| 129 | + const tx3 = await manager.populateTransaction.addTokenDestination(taskArgs.horizonStakingAddress) |
| 130 | + txBuilder.addTx({ |
| 131 | + to: manager.address, |
| 132 | + value: '0', |
| 133 | + data: tx3.data, |
| 134 | + }) |
| 135 | + logger.log(` + Add HorizonStaking as token destination`) |
| 136 | + } else { |
| 137 | + logger.log(` ✓ HorizonStaking already added as token destination`) |
| 138 | + } |
| 139 | + |
| 140 | + // Check if SubgraphService is already a token destination |
| 141 | + const isSubgraphServiceDestination = await manager.isTokenDestination(taskArgs.subgraphServiceAddress) |
| 142 | + if (!isSubgraphServiceDestination) { |
| 143 | + const tx4 = await manager.populateTransaction.addTokenDestination(taskArgs.subgraphServiceAddress) |
| 144 | + txBuilder.addTx({ |
| 145 | + to: manager.address, |
| 146 | + value: '0', |
| 147 | + data: tx4.data, |
| 148 | + }) |
| 149 | + logger.log(` + Add SubgraphService as token destination`) |
| 150 | + } else { |
| 151 | + logger.log(` ✓ SubgraphService already added as token destination`) |
| 152 | + } |
| 153 | + |
| 154 | + // Save result into json file |
| 155 | + const outputFile = txBuilder.saveToFile() |
| 156 | + logger.success(`\nTransaction batch saved to ${outputFile}`) |
| 157 | + logger.info('\nUpload this file to your Safe multisig to execute the transactions.') |
| 158 | + |
| 159 | + // Summary |
| 160 | + logger.info('\n=== SUMMARY ===') |
| 161 | + logger.info(`Total transactions: ${txBuilder.contents.transactions.length}`) |
| 162 | + logger.info(` - Remove functions: ${functionsToRemove.length}`) |
| 163 | + logger.info(` - Add HorizonStaking functions: 1 batch (${horizonStakingFunctionsToAdd.length} functions)`) |
| 164 | + logger.info(` - Add SubgraphService functions: 1 batch (${subgraphServiceFunctionsToAdd.length} functions)`) |
| 165 | + const destinationsAdded = (!isHorizonStakingDestination ? 1 : 0) + (!isSubgraphServiceDestination ? 1 : 0) |
| 166 | + logger.info(` - Add token destinations: ${destinationsAdded}`) |
| 167 | + } else { |
| 168 | + // Execute transactions |
| 169 | + logger.info('\nExecuting transactions...') |
| 170 | + |
| 171 | + // Remove old functions |
| 172 | + logger.info('\nRemoving old functions...') |
| 173 | + for (const signature of functionsToRemove) { |
| 174 | + try { |
| 175 | + logger.log(` Removing: ${signature}`) |
| 176 | + const tx = await manager.unsetAuthFunctionCall(signature) |
| 177 | + await waitTransaction(tx) |
| 178 | + logger.success(` ✓ Removed: ${signature}`) |
| 179 | + } catch (error) { |
| 180 | + logger.error(` ✗ Failed to remove ${signature}: ${error.message}`) |
| 181 | + process.exit(1) |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + // Add HorizonStaking functions |
| 186 | + logger.info('\nAdding HorizonStaking functions...') |
| 187 | + try { |
| 188 | + const horizonTargets = Array(horizonStakingFunctionsToAdd.length).fill(taskArgs.horizonStakingAddress) |
| 189 | + const tx = await manager.setAuthFunctionCallMany(horizonStakingFunctionsToAdd, horizonTargets) |
| 190 | + await waitTransaction(tx) |
| 191 | + logger.success(` ✓ Added ${horizonStakingFunctionsToAdd.length} functions for HorizonStaking`) |
| 192 | + } catch (error) { |
| 193 | + logger.error(` ✗ Failed to add HorizonStaking functions: ${error.message}`) |
| 194 | + process.exit(1) |
| 195 | + } |
| 196 | + |
| 197 | + // Add SubgraphService functions |
| 198 | + logger.info('\nAdding SubgraphService functions...') |
| 199 | + try { |
| 200 | + const subgraphTargets = Array(subgraphServiceFunctionsToAdd.length).fill(taskArgs.subgraphServiceAddress) |
| 201 | + const tx = await manager.setAuthFunctionCallMany(subgraphServiceFunctionsToAdd, subgraphTargets) |
| 202 | + await waitTransaction(tx) |
| 203 | + logger.success(` ✓ Added ${subgraphServiceFunctionsToAdd.length} functions for SubgraphService`) |
| 204 | + } catch (error) { |
| 205 | + logger.error(` ✗ Failed to add SubgraphService functions: ${error.message}`) |
| 206 | + process.exit(1) |
| 207 | + } |
| 208 | + |
| 209 | + // Add token destinations if needed |
| 210 | + logger.info('\nChecking and adding token destinations if needed...') |
| 211 | + |
| 212 | + // Check if HorizonStaking is already a token destination |
| 213 | + const isHorizonStakingDestination = await manager.isTokenDestination(taskArgs.horizonStakingAddress) |
| 214 | + if (!isHorizonStakingDestination) { |
| 215 | + try { |
| 216 | + logger.log(` Adding HorizonStaking as token destination...`) |
| 217 | + const tx = await manager.addTokenDestination(taskArgs.horizonStakingAddress) |
| 218 | + await waitTransaction(tx) |
| 219 | + logger.success(` ✓ Added HorizonStaking as token destination`) |
| 220 | + } catch (error) { |
| 221 | + logger.error(` ✗ Failed to add HorizonStaking as token destination: ${error.message}`) |
| 222 | + process.exit(1) |
| 223 | + } |
| 224 | + } else { |
| 225 | + logger.success(` ✓ HorizonStaking already a token destination`) |
| 226 | + } |
| 227 | + |
| 228 | + // Check if SubgraphService is already a token destination |
| 229 | + const isSubgraphServiceDestination = await manager.isTokenDestination(taskArgs.subgraphServiceAddress) |
| 230 | + if (!isSubgraphServiceDestination) { |
| 231 | + try { |
| 232 | + logger.log(` Adding SubgraphService as token destination...`) |
| 233 | + const tx = await manager.addTokenDestination(taskArgs.subgraphServiceAddress) |
| 234 | + await waitTransaction(tx) |
| 235 | + logger.success(` ✓ Added SubgraphService as token destination`) |
| 236 | + } catch (error) { |
| 237 | + logger.error(` ✗ Failed to add SubgraphService as token destination: ${error.message}`) |
| 238 | + process.exit(1) |
| 239 | + } |
| 240 | + } else { |
| 241 | + logger.success(` ✓ SubgraphService already a token destination`) |
| 242 | + } |
| 243 | + |
| 244 | + // Summary |
| 245 | + logger.info('\n=== COMPLETED SUCCESSFULLY ===') |
| 246 | + logger.info(`Removed ${functionsToRemove.length} old functions`) |
| 247 | + logger.info(`Added ${horizonStakingFunctionsToAdd.length} functions for HorizonStaking`) |
| 248 | + logger.info(`Added ${subgraphServiceFunctionsToAdd.length} functions for SubgraphService`) |
| 249 | + const destinationsAdded = (!isHorizonStakingDestination ? 1 : 0) + (!isSubgraphServiceDestination ? 1 : 0) |
| 250 | + logger.info(`Added ${destinationsAdded} token destinations`) |
| 251 | + } |
| 252 | + }) |
0 commit comments