Skip to content

Commit

Permalink
chore: added script to migrate protocol contracts to Horizon
Browse files Browse the repository at this point in the history
  • Loading branch information
Maikol committed Jan 9, 2025
1 parent 1038cf4 commit e1d173d
Show file tree
Hide file tree
Showing 22 changed files with 929 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,12 @@ interface IHorizonStakingMain {
* @param beneficiary The address where the tokens will be withdrawn after thawing
* @return The ID of the thaw request
*/
function undelegateWithBeneficiary(
address serviceProvider,
address verifier,
uint256 shares,
address beneficiary
) external returns (bytes32);
// function undelegateWithBeneficiary(
// address serviceProvider,
// address verifier,
// uint256 shares,
// address beneficiary
// ) external returns (bytes32);

/**
* @notice Withdraw undelegated tokens from a provision after thawing.
Expand Down Expand Up @@ -815,11 +815,11 @@ interface IHorizonStakingMain {
* @param verifier The verifier address
* @param nThawRequests The number of thaw requests to fulfill. Set to 0 to fulfill all thaw requests.
*/
function withdrawDelegatedWithBeneficiary(
address serviceProvider,
address verifier,
uint256 nThawRequests
) external;
// function withdrawDelegatedWithBeneficiary(
// address serviceProvider,
// address verifier,
// uint256 nThawRequests
// ) external;

/**
* @notice Re-delegate undelegated tokens from a provision after thawing to a `newServiceProvider` and `newVerifier`.
Expand Down
48 changes: 24 additions & 24 deletions packages/horizon/contracts/staking/HorizonStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,15 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
/**
* @notice See {IHorizonStakingMain-undelegate}.
*/
function undelegateWithBeneficiary(
address serviceProvider,
address verifier,
uint256 shares,
address beneficiary
) external override notPaused returns (bytes32) {
require(beneficiary != address(0), HorizonStakingInvalidBeneficiaryZeroAddress());
return _undelegate(ThawRequestType.DelegationWithBeneficiary, serviceProvider, verifier, shares, beneficiary);
}
// function undelegateWithBeneficiary(
// address serviceProvider,
// address verifier,
// uint256 shares,
// address beneficiary
// ) external override notPaused returns (bytes32) {
// require(beneficiary != address(0), HorizonStakingInvalidBeneficiaryZeroAddress());
// return _undelegate(ThawRequestType.DelegationWithBeneficiary, serviceProvider, verifier, shares, beneficiary);
// }

/**
* @notice See {IHorizonStakingMain-withdrawDelegated}.
Expand All @@ -342,21 +342,21 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
/**
* @notice See {IHorizonStakingMain-withdrawDelegatedWithBeneficiary}.
*/
function withdrawDelegatedWithBeneficiary(
address serviceProvider,
address verifier,
uint256 nThawRequests
) external override notPaused {
_withdrawDelegated(
ThawRequestType.DelegationWithBeneficiary,
serviceProvider,
verifier,
address(0),
address(0),
0,
nThawRequests
);
}
// function withdrawDelegatedWithBeneficiary(
// address serviceProvider,
// address verifier,
// uint256 nThawRequests
// ) external override notPaused {
// _withdrawDelegated(
// ThawRequestType.DelegationWithBeneficiary,
// serviceProvider,
// verifier,
// address(0),
// address(0),
// 0,
// nThawRequests
// );
// }

/**
* @notice See {IHorizonStakingMain-redelegate}.
Expand Down
25 changes: 24 additions & 1 deletion packages/horizon/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import '@nomicfoundation/hardhat-foundry'
import '@nomicfoundation/hardhat-toolbox'
import '@nomicfoundation/hardhat-ignition-ethers'
import '@tenderly/hardhat-tenderly'
import 'hardhat-storage-layout'
import 'hardhat-contract-sizer'
import 'hardhat-secure-accounts'
import * as dotenv from 'dotenv'

import type { HardhatUserConfig } from 'hardhat/config'

dotenv.config()

const getNetworkAccounts = () => {
const accounts: string[] = []
if (process.env.DEPLOYER_PRIVATE_KEY) accounts.push(process.env.DEPLOYER_PRIVATE_KEY)
if (process.env.GOVERNOR_PRIVATE_KEY) accounts.push(process.env.GOVERNOR_PRIVATE_KEY)
return accounts.length > 0 ? accounts : undefined
}

if (process.env.BUILD_RUN !== 'true') {
require('hardhat-graph-protocol')
}
Expand All @@ -17,7 +28,7 @@ const config: HardhatUserConfig = {
settings: {
optimizer: {
enabled: true,
runs: 200,
runs: 1,
},
},
},
Expand All @@ -41,6 +52,18 @@ const config: HardhatUserConfig = {
chainId: 421614,
url: 'https://sepolia-rollup.arbitrum.io/rpc',
},
arbitrumVirtualTestnet: {
secureAccounts: {
enabled: false,
},
chainId: 421615,
url: process.env.ARBITRUM_VIRTUAL_TESTNET_URL || '',
accounts: getNetworkAccounts(),
},
},
tenderly: {
project: 'graph-network',
username: 'graphprotocol',
},
graph: {
deployments: {
Expand Down
34 changes: 34 additions & 0 deletions packages/horizon/ignition/configs/horizon-migrate.hardhat.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$global": {
"isMigrate": true,
"graphProxyAdminAddress": "0x7474a6cc5fAeDEc620Db0fa8E4da6eD58477042C",
"controllerAddress": "0x9DB3ee191681f092607035d9BDA6e59FbEaCa695",
"horizonStakingProxyAddress": "0x865365C425f3A593Ffe698D9c4E6707D14d51e08",
"bridgeEscrowProxyAddress": "0x428Ab6E9EeF41Dc5098a34a6993Cdd5Be5BA24a6",
"epochManagerProxyAddress": "0x88b3C7f37253bAA1A9b95feAd69bD5320585826D",
"graphTokenProxyAddress": "0xf8c05dCF59E8B28BFD5eed176C562bEbcfc7Ac04",
"graphTokenGatewayProxyAddress": "0xB24Ce0f8c18c4DdDa584A7EeC132F49C966813bb",
// Placeholder address for a standalone Horizon deployment, see README.md for more details
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000"
},
"RewardsManager": {
"rewardsManagerProxyAddress": "0x1F49caE7669086c8ba53CC35d1E9f80176d67E79"
},
"Curation": {
"graphCurationProxyAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5"
},
"HorizonStaking": {
"maxThawingPeriod": 2419200
},
"GraphPayments": {
"protocolPaymentCut": 10000
},
"PaymentsEscrow": {
"withdrawEscrowThawingPeriod": 10000
},
"TAPCollector": {
"eip712Name": "TAPCollector",
"eip712Version": "1",
"revokeSignerThawingPeriod": 10000
}
}
1 change: 1 addition & 0 deletions packages/horizon/ignition/configs/horizon.hardhat.json5
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"$global": {
"isMigrate": false,
"governor": "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0",
"pauseGuardian": "0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC",
"subgraphAvailabilityOracle": "0xd03ea8624C8C5987235048901fB614fDcA89b117",
Expand Down
2 changes: 1 addition & 1 deletion packages/horizon/ignition/modules/core/GraphPayments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ export default buildModule('GraphPayments', (m) => {
// Load contract with implementation ABI
const GraphPayments = m.contractAt('GraphPayments', GraphPaymentsArtifact, GraphPaymentsProxy, { id: 'GraphPayments_Instance' })

return { GraphPayments }
return { GraphPayments, GraphPaymentsImplementation }
})
19 changes: 15 additions & 4 deletions packages/horizon/ignition/modules/core/HorizonProxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,28 @@ export default buildModule('HorizonProxies', (m) => {
const { Controller, PeripheryRegistered } = m.useModule(GraphPeripheryModule)
const { GraphProxyAdmin } = m.useModule(GraphProxyAdminModule)

const isMigrate = m.getParameter('isMigrate', false)

// Deploy HorizonStaking proxy without an implementation
const HorizonStakingProxy = m.contract('GraphProxy', GraphProxyArtifact, [ZERO_ADDRESS, GraphProxyAdmin], { after: [PeripheryRegistered], id: 'GraphProxy_HorizonStaking' })
let HorizonStakingProxy, setProxyHorizonStaking
if (isMigrate) {
const horizonStakingProxyAddress = m.getParameter('horizonStakingProxyAddress')
HorizonStakingProxy = m.contractAt('GraphProxy', GraphProxyArtifact, horizonStakingProxyAddress, { id: 'GraphProxy_HorizonStaking' })
setProxyHorizonStaking = HorizonStakingProxy
} else {
HorizonStakingProxy = m.contract('GraphProxy', GraphProxyArtifact, [ZERO_ADDRESS, GraphProxyAdmin], { after: [PeripheryRegistered], id: 'GraphProxy_HorizonStaking' })
setProxyHorizonStaking = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('Staking')), HorizonStakingProxy], { id: 'setContractProxy_HorizonStaking' })
}

// Deploy proxies for payments contracts using OZ TransparentUpgradeableProxy
const { Proxy: GraphPaymentsProxy, ProxyAdmin: GraphPaymentsProxyAdmin } = deployWithOZProxy(m, 'GraphPayments')
const { Proxy: PaymentsEscrowProxy, ProxyAdmin: PaymentsEscrowProxyAdmin } = deployWithOZProxy(m, 'PaymentsEscrow')

// Register the proxies in the controller
const setProxyHorizonStaking = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('Staking')), HorizonStakingProxy], { id: 'setContractProxy_HorizonStaking' })
const setProxyGraphPayments = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphPayments')), GraphPaymentsProxy], { id: 'setContractProxy_GraphPayments' })
const setProxyPaymentsEscrow = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('PaymentsEscrow')), PaymentsEscrowProxy], { id: 'setContractProxy_PaymentsEscrow' })
// if isMigrate then use from: governor
const options = isMigrate ? { from: m.getAccount(1) } : {}
const setProxyGraphPayments = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphPayments')), GraphPaymentsProxy], { ...options, id: 'setContractProxy_GraphPayments' })
const setProxyPaymentsEscrow = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('PaymentsEscrow')), PaymentsEscrowProxy], { ...options, id: 'setContractProxy_PaymentsEscrow' })

// Deploy dummy contract to signal that all periphery contracts are registered
const HorizonRegistered = m.contract('Dummy', DummyArtifact, [], {
Expand Down
9 changes: 6 additions & 3 deletions packages/horizon/ignition/modules/core/HorizonStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ export default buildModule('HorizonStaking', (m) => {
)

// Upgrade proxy to implementation contract
const upgradeCall = m.call(GraphProxyAdmin, 'upgrade', [HorizonStakingProxy, HorizonStakingImplementation])
const acceptCall = m.call(GraphProxyAdmin, 'acceptProxy', [HorizonStakingImplementation, HorizonStakingProxy], { after: [upgradeCall] })
const isMigrate = m.getParameter('isMigrate')
const governor = m.getAccount(1)
const options = isMigrate ? { from: governor } : {}
const upgradeCall = m.call(GraphProxyAdmin, 'upgrade', [HorizonStakingProxy, HorizonStakingImplementation], options)
const acceptCall = m.call(GraphProxyAdmin, 'acceptProxy', [HorizonStakingImplementation, HorizonStakingProxy], { ...options, after: [upgradeCall] })

// Load contract with implementation ABI
const HorizonStaking = m.contractAt('HorizonStaking', HorizonStakingArtifact, HorizonStakingProxy, { after: [acceptCall], id: 'HorizonStaking_Instance' })
m.call(HorizonStaking, 'setMaxThawingPeriod', [m.getParameter('maxThawingPeriod')])
m.call(HorizonStaking, 'setMaxThawingPeriod', [m.getParameter('maxThawingPeriod')], options)

return { HorizonStakingProxy, HorizonStakingImplementation, HorizonStaking }
})
2 changes: 1 addition & 1 deletion packages/horizon/ignition/modules/core/PaymentsEscrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ export default buildModule('PaymentsEscrow', (m) => {
// Load contract with implementation ABI
const PaymentsEscrow = m.contractAt('PaymentsEscrow', PaymentsEscrowArtifact, PaymentsEscrowProxy, { id: 'PaymentsEscrow_Instance' })

return { PaymentsEscrow }
return { PaymentsEscrow, PaymentsEscrowImplementation }
})
58 changes: 35 additions & 23 deletions packages/horizon/ignition/modules/periphery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,42 @@ export default buildModule('GraphHorizon_Periphery', (m) => {
const { EpochManager } = m.useModule(EpochManagerModule)
const { GraphProxyAdmin } = m.useModule(GraphProxyAdminModule)
const { GraphTokenGateway } = m.useModule(GraphTokenGatewayModule)
const { RewardsManager } = m.useModule(RewardsManagerModule)
const { GraphToken } = m.useModule(GraphTokenModule)
const { Curation } = m.useModule(CurationModule)

// Register contracts in the Controller
const setProxyEpochManager = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('EpochManager')), EpochManager], { id: 'setContractProxy_EpochManager' })
const setProxyRewardsManager = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('RewardsManager')), RewardsManager], { id: 'setContractProxy_RewardsManager' })
const setProxyGraphToken = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphToken')), GraphToken], { id: 'setContractProxy_GraphToken' })
const setProxyGraphTokenGateway = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphTokenGateway')), GraphTokenGateway], { id: 'setContractProxy_GraphTokenGateway' })
// eslint-disable-next-line no-secrets/no-secrets
const setProxyGraphProxyAdmin = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphProxyAdmin')), GraphProxyAdmin], { id: 'setContractProxy_GraphProxyAdmin' })
const setProxyCuration = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('Curation')), Curation], { id: 'setContractProxy_Curation' })

// Deploy dummy contract to signal that all periphery contracts are registered
const PeripheryRegistered = m.contract('Dummy', DummyArtifact, [], {
after: [
setProxyEpochManager,
setProxyRewardsManager,
setProxyGraphToken,
setProxyGraphTokenGateway,
setProxyGraphProxyAdmin,
setProxyCuration,
],
})

const { instance: RewardsManager } = m.useModule(RewardsManagerModule)
const { instance: Curation } = m.useModule(CurationModule)

const isMigrate = m.getParameter('isMigrate', false)

let PeripheryRegistered
if (!isMigrate) {
// Register contracts in the Controller
const setProxyEpochManager = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('EpochManager')), EpochManager], { id: 'setContractProxy_EpochManager' })
const setProxyRewardsManager = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('RewardsManager')), RewardsManager], { id: 'setContractProxy_RewardsManager' })
const setProxyGraphToken = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphToken')), GraphToken], { id: 'setContractProxy_GraphToken' })
const setProxyGraphTokenGateway = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphTokenGateway')), GraphTokenGateway], { id: 'setContractProxy_GraphTokenGateway' })
// eslint-disable-next-line no-secrets/no-secrets
const setProxyGraphProxyAdmin = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphProxyAdmin')), GraphProxyAdmin], { id: 'setContractProxy_GraphProxyAdmin' })
const setProxyCuration = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('Curation')), Curation], { id: 'setContractProxy_Curation' })

// Deploy dummy contract to signal that all periphery contracts are registered
PeripheryRegistered = m.contract('Dummy', DummyArtifact, [], {
after: [
setProxyEpochManager,
setProxyRewardsManager,
setProxyGraphToken,
setProxyGraphTokenGateway,
setProxyGraphProxyAdmin,
setProxyCuration,
],
})
} else {
// TODO: Remove if not needed
const governor = m.getAccount(1)
// eslint-disable-next-line no-secrets/no-secrets
const setProxyGraphProxyAdmin = m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('GraphProxyAdmin')), GraphProxyAdmin], { id: 'setContractProxy_GraphProxyAdmin', from: governor })
PeripheryRegistered = m.contract('Dummy', DummyArtifact, [], { after: [setProxyGraphProxyAdmin] })
}

return {
BridgeEscrow,
Expand Down
20 changes: 14 additions & 6 deletions packages/horizon/ignition/modules/periphery/BridgeEscrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ import BridgeEscrowArtifact from '@graphprotocol/contracts/build/contracts/contr
import ControllerModule from '../periphery/Controller'

export default buildModule('BridgeEscrow', (m) => {
const { Controller } = m.useModule(ControllerModule)
const isMigrate = m.getParameter('isMigrate', false)

const { instance: BridgeEscrow } = deployWithGraphProxy(m, {
name: 'BridgeEscrow',
artifact: BridgeEscrowArtifact,
args: [Controller],
})
let BridgeEscrow
if (isMigrate) {
const bridgeEscrowProxyAddress = m.getParameter('bridgeEscrowProxyAddress')
BridgeEscrow = m.contractAt('BridgeEscrow', BridgeEscrowArtifact, bridgeEscrowProxyAddress)
} else {
const { Controller } = m.useModule(ControllerModule)

BridgeEscrow = deployWithGraphProxy(m, {
name: 'BridgeEscrow',
artifact: BridgeEscrowArtifact,
args: [Controller],
}).instance
}

return { BridgeEscrow }
})
20 changes: 14 additions & 6 deletions packages/horizon/ignition/modules/periphery/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ import ControllerArtifact from '@graphprotocol/contracts/build/contracts/contrac

// TODO: Ownership transfer is a two step process, the new owner needs to accept it by calling acceptOwnership
export default buildModule('Controller', (m) => {
const governor = m.getParameter('governor')
const pauseGuardian = m.getParameter('pauseGuardian')
const isMigrate = m.getParameter('isMigrate', false)

const Controller = m.contract('Controller', ControllerArtifact)
let Controller
if (isMigrate) {
const controllerAddress = m.getParameter('controllerAddress')
Controller = m.contractAt('Controller', ControllerArtifact, controllerAddress)
} else {
const governor = m.getParameter('governor')
const pauseGuardian = m.getParameter('pauseGuardian')

m.call(Controller, 'setPauseGuardian', [pauseGuardian])
m.call(Controller, 'transferOwnership', [governor])
m.call(Controller, 'setPaused', [false])
Controller = m.contract('Controller', ControllerArtifact)

m.call(Controller, 'setPauseGuardian', [pauseGuardian])
m.call(Controller, 'transferOwnership', [governor])
m.call(Controller, 'setPaused', [false])
}

return { Controller }
})
Loading

0 comments on commit e1d173d

Please sign in to comment.