Skip to content

Commit

Permalink
multisig facade (#1206)
Browse files Browse the repository at this point in the history
Co-authored-by: Akshat Mittal <[email protected]>
  • Loading branch information
tbrent and akshatmittal authored Nov 6, 2024
1 parent a5235dd commit cda7d8a
Show file tree
Hide file tree
Showing 25 changed files with 341 additions and 91 deletions.
4 changes: 4 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ interface INetworkConfig {
AAVE_V3_POOL?: string
STARGATE_STAKING_CONTRACT?: string
CURVE_POOL_WETH_FRXETH?: string
DEV_MULTISIG?: string
}

export const networkConfig: { [key: string]: INetworkConfig } = {
Expand Down Expand Up @@ -313,6 +314,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb',
STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b',
CURVE_POOL_WETH_FRXETH: '0x9c3b46c0ceb5b9e304fcd6d88fc50f7dd24b31bc',
DEV_MULTISIG: '0xe63AfB49529E135be5ab2747104fF1D3ceAE0EFf', // same on all networks
},
'3': {
name: 'tenderly',
Expand Down Expand Up @@ -551,6 +553,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
AAVE_V3_POOL: '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5',
AAVE_V3_INCENTIVES_CONTROLLER: '0xf9cc4F0D883F1a1eb2c253bdb46c254Ca51E1F44',
STARGATE_STAKING_CONTRACT: '0x06Eb48763f117c7Be887296CDcdfad2E4092739C',
DEV_MULTISIG: '0xe63AfB49529E135be5ab2747104fF1D3ceAE0EFf', // same on all networks
},
'42161': {
name: 'arbitrum',
Expand Down Expand Up @@ -592,6 +595,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
COMET_EXT: '0x1B2E88cC7365d90e7E81392432482925BD8437E9',
AAVE_V3_POOL: '0x794a61358D6845594F94dc1DB02A252b5b4814aD',
AAVE_V3_INCENTIVES_CONTROLLER: '0x929EC64c34a17401F460460D4B9390518E5B473e',
DEV_MULTISIG: '0xe63AfB49529E135be5ab2747104fF1D3ceAE0EFf', // same on all networks
},
'421614': {
name: 'arbitrum-sepolia',
Expand Down
4 changes: 3 additions & 1 deletion contracts/facade/Facade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ contract Facade is IFacade, Ownable {
mapping(bytes4 => address) public facets;

// solhint-disable-next-line no-empty-blocks
constructor() Ownable() {}
constructor(address owner) Ownable() {
_transferOwnership(owner);
}

// Save new facets to the Facade, forcefully
function save(address facet, bytes4[] memory selectors) external onlyOwner {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
"@openzeppelin/contracts": "4.9.6",
"@openzeppelin/contracts-upgradeable": "4.9.6",
"@openzeppelin/hardhat-upgrades": "^1.23.0",
"@safe-global/api-kit": "^2.4.5",
"@safe-global/protocol-kit": "^4.1.0",
"@tenderly/hardhat-tenderly": "^1.7.7",
"@typechain/ethers-v5": "^7.2.0",
"@typechain/hardhat": "^2.3.1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/addresses/1-tmp-deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"GNOSIS_EASY_AUCTION": "0x0b7fFc1f4AD541A4Ed16b40D8c37f0929158D101"
},
"tradingLib": "0xa54544C6C36C0d776cc4F04EBB847e0BB3A11ea2",
"facade": "0x2C7ca56342177343A2954C250702Fd464f4d0613",
"facade": "0x9d49b363d492725412bf8350bBb525B7a80a7470",
"facets": {
"actFacet": "0xCAB3D3d0d5544145A6BCB47e58F61368BCcAe2dB",
"readFacet": "0x823110a13eB26cB09c4Bb118DBfE4ff5f96D5526",
Expand Down
2 changes: 1 addition & 1 deletion scripts/addresses/42161-tmp-deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"tradingLib": "0x348644F24FA34c40a8E3C4Cf9aF14f8a96aD63fC",
"cvxMiningLib": "",
"facade": "0x387A0C36681A22F728ab54426356F4CAa6bB48a9",
"facade": "0xb48a407e225b4764fD3BCc2a2329fF7745af7e64",
"facets": {
"actFacet": "0xE774CCF1431c3DEe7Fa4c20f67534b61289CAa45",
"readFacet": "0x15175d35F3d88548B49600B4ee8067253A2e4e66",
Expand Down
2 changes: 1 addition & 1 deletion scripts/addresses/8453-tmp-deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"GNOSIS_EASY_AUCTION": "0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02"
},
"tradingLib": "0x6419fe6cf428150e2d8ed38a3316b1bb468f79a7",
"facade": "0xEb2071e9B542555E90E6e4E1F83fa17423583991",
"facade": "0xE41416d8dC94ac1F6d12282d6D46B714F39a87d9",
"facets": {
"actFacet": "0x0eac15B9Fe585432E48Cf175571D75D111861F43",
"readFacet": "0x5Af543D6F95a98200Dd770f39A902Fe793BAeB27",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"tradingLib": "0x348644F24FA34c40a8E3C4Cf9aF14f8a96aD63fC",
"cvxMiningLib": "",
"facade": "0x387A0C36681A22F728ab54426356F4CAa6bB48a9",
"facade": "0xb48a407e225b4764fD3BCc2a2329fF7745af7e64",
"facets": {
"actFacet": "0xE774CCF1431c3DEe7Fa4c20f67534b61289CAa45",
"readFacet": "0x15175d35F3d88548B49600B4ee8067253A2e4e66",
Expand Down
2 changes: 1 addition & 1 deletion scripts/addresses/base-3.4.0/8453-tmp-deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"GNOSIS_EASY_AUCTION": "0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02"
},
"tradingLib": "0x6419fe6cf428150e2d8ed38a3316b1bb468f79a7",
"facade": "0xEb2071e9B542555E90E6e4E1F83fa17423583991",
"facade": "0xE41416d8dC94ac1F6d12282d6D46B714F39a87d9",
"facets": {
"actFacet": "0x0eac15B9Fe585432E48Cf175571D75D111861F43",
"readFacet": "0x5Af543D6F95a98200Dd770f39A902Fe793BAeB27",
Expand Down
2 changes: 1 addition & 1 deletion scripts/addresses/mainnet-3.4.0/1-tmp-deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"GNOSIS_EASY_AUCTION": "0x0b7fFc1f4AD541A4Ed16b40D8c37f0929158D101"
},
"tradingLib": "0xa54544C6C36C0d776cc4F04EBB847e0BB3A11ea2",
"facade": "0x2C7ca56342177343A2954C250702Fd464f4d0613",
"facade": "0x9d49b363d492725412bf8350bBb525B7a80a7470",
"facets": {
"actFacet": "0xCAB3D3d0d5544145A6BCB47e58F61368BCcAe2dB",
"readFacet": "0x823110a13eB26cB09c4Bb118DBfE4ff5f96D5526",
Expand Down
3 changes: 2 additions & 1 deletion scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ async function main() {
'phase1-facade/2_deploy_actFacet.ts',
'phase1-facade/3_deploy_maxIssuable.ts',
'phase1-facade/4_deploy_backingBufferFacet.ts',
'phase1-facade/5_deploy_revenueFacet.ts'
'phase1-facade/5_deploy_revenueFacet.ts',
'phase1-facade/save.ts'
)

// =============================================
Expand Down
6 changes: 5 additions & 1 deletion scripts/deployment/phase1-core/4_deploy_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ async function main() {
throw new Error(`RSR Asset contract not found in network ${hre.network.name}`)
}

if (!networkConfig[chainId].DEV_MULTISIG) {
throw new Error(`Missing DEV_MULTISIG address in network ${hre.network.name}`)
}

// ******************** Deploy Facade ****************************************/

const FacadeFactory = await ethers.getContractFactory('Facade')
facade = <Facade>await FacadeFactory.connect(burner).deploy()
facade = <Facade>await FacadeFactory.connect(burner).deploy(networkConfig[chainId].DEV_MULTISIG)
await facade.deployed()

// Write temporary deployments file
Expand Down
13 changes: 0 additions & 13 deletions scripts/deployment/phase1-facade/1_deploy_readFacet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,6 @@ async function main() {
console.log(`Deployed to ${hre.network.name} (${chainId})
ReadFacet: ${readFacet.address}
Deployment file: ${deploymentFilename}`)

// ******************** Save to Facade ****************************************/

console.log('Configuring with Facade...')

// Save ReadFacet to Facade
const facade = await ethers.getContractAt('Facade', deployments.facade)
await facade.save(
readFacet.address,
Object.entries(readFacet.functions).map(([fn]) => readFacet.interface.getSighash(fn))
)

console.log('Finished saving to Facade')
}

main().catch((error) => {
Expand Down
13 changes: 0 additions & 13 deletions scripts/deployment/phase1-facade/2_deploy_actFacet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,6 @@ async function main() {
console.log(`Deployed to ${hre.network.name} (${chainId})
ActFacet: ${actFacet.address}
Deployment file: ${deploymentFilename}`)

// ******************** Save to Facade ****************************************/

console.log('Configuring with Facade...')

// Save ReadFacet to Facade
const facade = await ethers.getContractAt('Facade', deployments.facade)
await facade.save(
actFacet.address,
Object.entries(actFacet.functions).map(([fn]) => actFacet.interface.getSighash(fn))
)

console.log('Finished saving to Facade')
}

main().catch((error) => {
Expand Down
15 changes: 0 additions & 15 deletions scripts/deployment/phase1-facade/3_deploy_maxIssuable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,6 @@ async function main() {
console.log(`Deployed to ${hre.network.name} (${chainId})
MaxIssuableFacet: ${maxIssuableFacet.address}
Deployment file: ${deploymentFilename}`)

// ******************** Save to Facade ****************************************/

console.log('Configuring with Facade...')

// Save MaxIssuableFacet functions to Facade
const facade = await ethers.getContractAt('Facade', deployments.facade)
await facade.save(
maxIssuableFacet.address,
Object.entries(maxIssuableFacet.functions).map(([fn]) =>
maxIssuableFacet.interface.getSighash(fn)
)
)

console.log('Finished saving to Facade')
}

main().catch((error) => {
Expand Down
15 changes: 0 additions & 15 deletions scripts/deployment/phase1-facade/4_deploy_backingBufferFacet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,6 @@ async function main() {
console.log(`Deployed to ${hre.network.name} (${chainId})
BackingBufferFacet: ${backingBufferFacet.address}
Deployment file: ${deploymentFilename}`)

// ******************** Save to Facade ****************************************/

console.log('Configuring with Facade...')

// Save BackingBufferFacet functions to Facade
const facade = await ethers.getContractAt('Facade', deployments.facade)
await facade.save(
backingBufferFacet.address,
Object.entries(backingBufferFacet.functions).map(([fn]) =>
backingBufferFacet.interface.getSighash(fn)
)
)

console.log('Finished saving to Facade')
}

main().catch((error) => {
Expand Down
13 changes: 0 additions & 13 deletions scripts/deployment/phase1-facade/5_deploy_revenueFacet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,6 @@ async function main() {
console.log(`Deployed to ${hre.network.name} (${chainId})
RevenueFacet: ${revenueFacet.address}
Deployment file: ${deploymentFilename}`)

// ******************** Save to Facade ****************************************/

console.log('Configuring with Facade...')

// Save RevenueFacet functions to Facade
const facade = await ethers.getContractAt('Facade', deployments.facade)
await facade.save(
revenueFacet.address,
Object.entries(revenueFacet.functions).map(([fn]) => revenueFacet.interface.getSighash(fn))
)

console.log('Finished saving to Facade')
}

main().catch((error) => {
Expand Down
60 changes: 60 additions & 0 deletions scripts/deployment/phase1-facade/save.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import hre, { ethers } from 'hardhat'

import { getChainId, isValidContract } from '../../../common/blockchain-utils'
import { getDeploymentFile, getDeploymentFilename, IDeployments } from '../common'
import { initiateMultisigTxs } from '../utils'
import { MetaTransactionData } from '@safe-global/safe-core-sdk-types'

async function main() {
// ==== Read Configuration ====
const [burner] = await hre.ethers.getSigners()
const chainId = await getChainId(hre)

console.log(`Saving Facets to Facade on network ${hre.network.name} (${chainId})
with burner account: ${burner.address}`)

const deploymentFilename = getDeploymentFilename(chainId)
const deployments = <IDeployments>getDeploymentFile(deploymentFilename)

// Check facade exists
if (!deployments.facade) {
throw new Error(`Missing deployed contracts in network ${hre.network.name}`)
} else if (!(await isValidContract(hre, deployments.facade))) {
throw new Error(`Facade contract not found in network ${hre.network.name}`)
}

// ******************** Save Facets to Facade ****************************************/

if (hre.network.name == 'localhost' || hre.network.name == 'hardhat') {
console.log('Skipping saving facets on localhost')
return
}

const facade = await ethers.getContractAt('Facade', deployments.facade)

const facets = [
await ethers.getContractAt('ReadFacet', deployments.facets.readFacet),
await ethers.getContractAt('ActFacet', deployments.facets.actFacet),
await ethers.getContractAt('MaxIssuableFacet', deployments.facets.maxIssuableFacet),
await ethers.getContractAt('BackingBufferFacet', deployments.facets.backingBufferFacet),
await ethers.getContractAt('RevenueFacet', deployments.facets.revenueFacet),
]

const txs = facets.map((facet): MetaTransactionData => {
return {
to: facade.address,
value: '0',
data: facade.interface.encodeFunctionData('save', [
facet.address,
Object.entries(facet.functions).map(([fn]) => facet.interface.getSighash(fn)),
]),
}
})

await initiateMultisigTxs(chainId, txs)
}

main().catch((error) => {
console.error(error)
process.exitCode = 1
})
57 changes: 57 additions & 0 deletions scripts/deployment/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { IComponents, arbitrumL2Chains, baseL2Chains } from '../../common/config
import { isValidContract } from '../../common/blockchain-utils'
import { IDeployments } from './common'
import { useEnv } from '#/utils/env'
import { networkConfig } from '../../common/configuration'

import Safe from '@safe-global/protocol-kit'
import SafeApiKit from '@safe-global/api-kit'
import { HttpNetworkConfig } from 'hardhat/types'
import { MetaTransactionData } from '@safe-global/safe-core-sdk-types'

export const priceTimeout = bn('604800') // 1 week

Expand Down Expand Up @@ -274,3 +280,54 @@ export const getUsdtOracleError = (network: string): BigNumber => {
return fp('0.0025') // 0.25% mainnet
}
}

export const initiateMultisigTx = async (
chainId: string,
tx: MetaTransactionData
): Promise<void> => {
return await initiateMultisigTxs(chainId, [tx])
}

// Note: may end up with conflicting nonces if used naively
export const initiateMultisigTxs = async (
chainId: string,
txs: MetaTransactionData[]
): Promise<void> => {
if (hre.network.name == 'localhost' || hre.network.name == 'hardhat') {
console.log('Skipping multisig tx on localhost')
return
}

const provider = (hre.config.networks[hre.network.name] as HttpNetworkConfig).url
const wallet = hre.ethers.Wallet.fromMnemonic(process.env.MNEMONIC!)
const safeAddress = networkConfig[chainId].DEV_MULTISIG!

const safe = await Safe.init({
provider,
signer: wallet.privateKey,
safeAddress,
})
const safeApi = new SafeApiKit({
chainId: parseInt(chainId) as unknown as bigint,
})
const safeTx = await safe.createTransaction({ transactions: txs })
const safeTxHash = await safe.getTransactionHash(safeTx)
const signature = await safe.signHash(safeTxHash)

// Propose transaction to the service
await safeApi.proposeTransaction({
safeAddress: await safe.getAddress(),
safeTransactionData: safeTx.data,
safeTxHash,
senderAddress: wallet.address,
senderSignature: signature.data,
})

let prefix = 'base'
if (hre.network.name == 'mainnet') prefix = 'eth'
else if (hre.network.name == 'arbitrum') prefix = 'arb'

const hyperlink = `https://app.safe.global/transactions/queue?safe=${prefix}:${networkConfig[chainId].DEV_MULTISIG}`

console.log(`Queued tx, requires confirmation: ${hyperlink}`)
}
7 changes: 6 additions & 1 deletion scripts/verification/4_verify_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ async function main() {
deployments = <IDeployments>getDeploymentFile(getDeploymentFilename(chainId))

/** ******************** Verify Facade ****************************************/
await verifyContract(chainId, deployments.facade, [], 'contracts/facade/Facade.sol:Facade')
await verifyContract(
chainId,
deployments.facade,
[networkConfig[chainId].DEV_MULTISIG],
'contracts/facade/Facade.sol:Facade'
)

/** ******************** Verify ReadFacet ****************************************/
await verifyContract(
Expand Down
7 changes: 6 additions & 1 deletion test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,12 @@ const makeDefaultFixture = async (setBasket: boolean): Promise<DefaultFixture> =

// Deploy Facade
const FacadeFactory: ContractFactory = await ethers.getContractFactory('Facade')
const facade = await ethers.getContractAt('TestIFacade', (await FacadeFactory.deploy()).address)
const facade = await ethers.getContractAt(
'TestIFacade',
(
await FacadeFactory.deploy(owner.address)
).address
)

// Save ReadFacet to Facade
const ReadFacetFactory: ContractFactory = await ethers.getContractFactory('ReadFacet')
Expand Down
Loading

0 comments on commit cda7d8a

Please sign in to comment.