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

feat:add verification of usdc contracts #99

Draft
wants to merge 1 commit into
base: main
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
16 changes: 12 additions & 4 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ const config = {
},
},
},
{
version: '0.6.12',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to verify IFiatTokenProxy

settings: {
optimizer: {
enabled: true,
runs: 100,
},
},
},
{
version: '0.8.16',
settings: {
Expand Down Expand Up @@ -191,7 +200,7 @@ const config = {
arbitrumTestnet: process.env['ARBISCAN_API_KEY'],
nova: process.env['NOVA_ARBISCAN_API_KEY'],
arbGoerliRollup: process.env['ARBISCAN_API_KEY'],
arbSepoliaRollup: 'x',
arbSepoliaRollup: process.env['ARBISCAN_API_KEY'],
orbit: 'x',
},
customChains: [
Expand All @@ -215,9 +224,8 @@ const config = {
network: 'arbSepoliaRollup',
chainId: 421614,
urls: {
apiURL:
'https://sepolia-explorer.arbitrum.io/api?module=contract&action=verify',
browserURL: 'https://sepolia-explorer.arbitrum.io/',
apiURL: 'https://api-sepolia.arbiscan.io/api',
browserURL: 'https://sepolia.arbiscan.io/',
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"test:creation-code": "hardhat test test-e2e/creationCodeTest.ts",
"blockscout:verify": "hardhat run ./scripts/orbitVerifyOnBlockscout.ts",
"deploy:usdc-token-bridge": "hardhat run scripts/usdc-bridge-deployment/deployUsdcBridge.ts",
"verify:usdc-token-bridge": "hardhat run scripts/usdc-bridge-deployment/verifyUsdcBridge.ts",
"verify:l1-usdc-token-bridge": "hardhat run scripts/usdc-bridge-deployment/verifyParentUsdcBridge.ts",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to verify the contracts on the parent chain although they are likely to be already verified by bytecode match

"typechain": "hardhat typechain",
"deploy:tokenbridge": "hardhat run scripts/deploy_token_bridge_l1.ts --network mainnet",
"gen:uml": "sol2uml ./contracts/tokenbridge/arbitrum,./contracts/tokenbridge/ethereum,./contracts/tokenbridge/libraries -o ./gatewayUML.svg",
Expand Down
32 changes: 20 additions & 12 deletions scripts/usdc-bridge-deployment/deployUsdcBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,22 +384,30 @@ async function _initializeGateways(

///// verify initialization
if (
(await l1UsdcGateway.router()) != l1Router ||
(await l1UsdcGateway.inbox()) != inbox ||
(await l1UsdcGateway.l1USDC()) != l1Usdc ||
(await l1UsdcGateway.l2USDC()) != l2Usdc ||
(await l1UsdcGateway.owner()) != _owner ||
(await l1UsdcGateway.counterpartGateway()) != _l2CounterPart
(await l1UsdcGateway.counterpartGateway()).toLowerCase() !=
_l2CounterPart.toLowerCase()
) {
console.log('_l2CounterPart')
}
if (
(await l1UsdcGateway.router()).toLowerCase() != l1Router.toLowerCase() ||
(await l1UsdcGateway.inbox()).toLowerCase() != inbox.toLowerCase() ||
(await l1UsdcGateway.l1USDC()).toLowerCase() != l1Usdc.toLowerCase() ||
(await l1UsdcGateway.l2USDC()).toLowerCase() != l2Usdc.toLowerCase() ||
(await l1UsdcGateway.owner()).toLowerCase() != _owner.toLowerCase() ||
(await l1UsdcGateway.counterpartGateway()).toLowerCase() !=
_l2CounterPart.toLowerCase()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases, these addresses don't match because of casing issues

) {
throw new Error('L1 USDC gateway initialization failed')
}

if (
(await l2UsdcGateway.counterpartGateway()) != _l1Counterpart ||
(await l2UsdcGateway.router()) != l2Router ||
(await l2UsdcGateway.l1USDC()) != l1Usdc ||
(await l2UsdcGateway.l2USDC()) != l2Usdc ||
(await l2UsdcGateway.owner()) != ownerL2
(await l2UsdcGateway.counterpartGateway()).toLowerCase() !=
_l1Counterpart.toLowerCase() ||
(await l2UsdcGateway.router()).toLowerCase() != l2Router.toLowerCase() ||
(await l2UsdcGateway.l1USDC()).toLowerCase() != l1Usdc.toLowerCase() ||
(await l2UsdcGateway.l2USDC()).toLowerCase() != l2Usdc.toLowerCase() ||
(await l2UsdcGateway.owner()).toLowerCase() != ownerL2.toLowerCase()
) {
throw new Error('L2 USDC gateway initialization failed')
}
Expand Down Expand Up @@ -459,7 +467,7 @@ async function _registerGateway(

const maxGas = retryableParams.gasLimit
const gasPriceBid = retryableParams.maxFeePerGas.mul(3)
let maxSubmissionCost = retryableParams.maxSubmissionCost
const maxSubmissionCost = retryableParams.maxSubmissionCost
let totalFee = maxGas.mul(gasPriceBid).add(maxSubmissionCost)
if (isFeeToken) {
totalFee = await _getPrescaledAmount(
Expand Down
213 changes: 213 additions & 0 deletions scripts/usdc-bridge-deployment/verifyParentUsdcBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { ethers } from 'hardhat'
import { run } from 'hardhat'
import {
IERC20Bridge__factory,
IInboxBase__factory,
L1GatewayRouter__factory,
L1OrbitGatewayRouter__factory,
} from '../../build/types'
import { JsonRpcProvider, Provider } from '@ethersproject/providers'

main().then(() => console.log('Done.'))

/**
* USDC bridge verification script for the parent chain.
* Script will verify the following contracts:
* - L1 proxy admin
* - L1 USDC gateway (behind a TUP with L1ProxyAdmin as admin)
*/
async function main() {
console.log('Starting USDC bridge contract verification on the parent chain')
_checkEnvVars()

//
// Loading chain information
//
const parentRpc = process.env['PARENT_RPC'] as string
const parentProvider = new JsonRpcProvider(parentRpc)

const inboxAddress = process.env['INBOX'] as string
const parentGatewayRouterAddress = process.env['L1_ROUTER'] as string
const parentUsdcAddress = process.env['L1_USDC'] as string

//
// Getting deployed contract addresses
//
const isFeeToken =
(await _getFeeToken(inboxAddress, parentProvider)) !=
ethers.constants.AddressZero

// Parent chain gateway router
const parentGatewayRouter = isFeeToken
? L1OrbitGatewayRouter__factory.connect(
parentGatewayRouterAddress,
parentProvider
)
: L1GatewayRouter__factory.connect(
parentGatewayRouterAddress,
parentProvider
)

// Parent chain USDC gateway
const parentUsdcGatewayAddress = await parentGatewayRouter.l1TokenToGateway(
parentUsdcAddress
)
if (parentUsdcGatewayAddress === ethers.constants.AddressZero) {
throw new Error(
'It looks like the new Usdc custom gateway is not registered in the GatewayRouter'
)
}
const parentUsdcGatewayLogicAddress = await _getLogicAddress(
parentUsdcGatewayAddress,
parentProvider
)

// Proxy admin
const parentProxyAdminAddress = await _getAdminAddress(
parentUsdcGatewayAddress,
parentProvider
)

// Verify single TUP, others TUPs will be verified by bytecode match
await _verifyContract(
'TransparentUpgradeableProxy',
parentUsdcGatewayAddress,
[parentUsdcGatewayLogicAddress, parentProxyAdminAddress, '0x']
)

// Verify Parent chain USDC gateway
await _verifyContract(
isFeeToken ? 'L1OrbitUSDCGateway' : 'L1USDCGateway',
parentUsdcGatewayLogicAddress,
[]
)

// Verify Parent chain ProxyAdmin
await _verifyContract('ProxyAdmin', parentProxyAdminAddress, [])
}

async function _verifyContract(
contractName: string,
contractAddress: string,
constructorArguments: any[] = [],
contractPathAndName?: string // optional
): Promise<void> {
try {
// Define the verification options with possible 'contract' property
const verificationOptions: {
contract?: string
address: string
constructorArguments: any[]
} = {
address: contractAddress,
constructorArguments: constructorArguments,
}

// if contractPathAndName is provided, add it to the verification options
if (contractPathAndName) {
verificationOptions.contract = contractPathAndName
}

await run('verify:verify', verificationOptions)
console.log(`Verified contract ${contractName} successfully.`)
} catch (error: any) {
if (error.message.includes('Already Verified')) {
console.log(`Contract ${contractName} is already verified.`)
} else {
console.error(
`Verification for ${contractName} failed with the following error: ${error.message}`
)
}
}
}

async function _getLogicAddress(
contractAddress: string,
provider: Provider
): Promise<string> {
return (
await _getAddressAtStorageSlot(
contractAddress,
provider,
'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
)
).toLowerCase()
}

async function _getAdminAddress(
contractAddress: string,
provider: Provider
): Promise<string> {
return (
await _getAddressAtStorageSlot(
contractAddress,
provider,
'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103'
)
).toLowerCase()
}

async function _getAddressAtStorageSlot(
contractAddress: string,
provider: Provider,
storageSlotBytes: string
): Promise<string> {
const storageValue = await provider.getStorageAt(
contractAddress,
storageSlotBytes
)

if (!storageValue) {
return ''
}

// remove excess bytes
const formatAddress =
storageValue.substring(0, 2) + storageValue.substring(26)

// return address as checksum address
return ethers.utils.getAddress(formatAddress)
}

/**
* Fetch fee token if it exists or return zero address
*/
async function _getFeeToken(
inbox: string,
provider: Provider
): Promise<string> {
const bridge = await IInboxBase__factory.connect(inbox, provider).bridge()

let feeToken = ethers.constants.AddressZero

try {
feeToken = await IERC20Bridge__factory.connect(
bridge,
provider
).nativeToken()
} catch {
// ignore
}

return feeToken
}

/**
* Check if all required env vars are set
*/
function _checkEnvVars() {
const requiredEnvVars = [
'PARENT_RPC',
'CHILD_RPC',
'L1_ROUTER',
'L2_ROUTER',
'INBOX',
'L1_USDC',
]

for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing env var ${envVar}`)
}
}
}
Loading
Loading