Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Horizon chore: added script to migrate protocol contracts to Horizon #1084

Draft
wants to merge 1 commit into
base: horizon
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading