Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/poor-avocados-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/core-utils': patch
---

Clean up the L1 => L2 address aliasing utilities
33 changes: 19 additions & 14 deletions packages/core-utils/src/alias.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { ethers } from 'ethers'
import { bnToAddress } from './bn'

// Constant representing the alias to apply to the msg.sender when a contract sends an L1 => L2
// message. We need this aliasing scheme because a contract can be deployed to the same address
// on both L1 and L2 but with different bytecode (address is not dependent on bytecode when using
// the standard CREATE opcode). We want to treat L1 contracts as having a different address while
// still making it possible for L2 contracts to easily reverse the aliasing scheme and figure out
// the real address of the contract that sent the L1 => L2 message.
export const L1_TO_L2_ALIAS_OFFSET =
'0x1111000000000000000000000000000000001111'

export const bnToAddress = (bn: ethers.BigNumber | number): string => {
bn = ethers.BigNumber.from(bn)
if (bn.isNegative()) {
bn = ethers.BigNumber.from('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')
.add(bn)
.add(1)
}

const addr = bn.toHexString().slice(2).padStart(40, '0')
return ethers.utils.getAddress(
'0x' + addr.slice(addr.length - 40, addr.length)
)
}

/**
* Applies the L1 => L2 aliasing scheme to an address.
*
* @param address Address to apply the scheme to.
* @returns Address with the scheme applied.
*/
export const applyL1ToL2Alias = (address: string): string => {
if (!ethers.utils.isAddress(address)) {
throw new Error(`not a valid address: ${address}`)
Expand All @@ -25,6 +24,12 @@ export const applyL1ToL2Alias = (address: string): string => {
return bnToAddress(ethers.BigNumber.from(address).add(L1_TO_L2_ALIAS_OFFSET))
}

/**
* Reverses the L1 => L2 aliasing scheme from an address.
*
* @param address Address to reverse the scheme from.
* @returns Alias with the scheme reversed.
*/
export const undoL1ToL2Alias = (address: string): string => {
if (!ethers.utils.isAddress(address)) {
throw new Error(`not a valid address: ${address}`)
Expand Down
37 changes: 37 additions & 0 deletions packages/core-utils/src/bn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ethers } from 'ethers'
import { remove0x, add0x } from './common/hex-strings'

/**
* Converts an ethers BigNumber into an equivalent Ethereum address representation.
*
* @param bn BigNumber to convert to an address.
* @return BigNumber converted to an address, represented as a hex string.
*/
export const bnToAddress = (bn: ethers.BigNumber | number): string => {
// Coerce numbers into a BigNumber.
bn = ethers.BigNumber.from(bn)

// Negative numbers are converted to addresses by adding MAX_ADDRESS + 1.
// TODO: Explain this in more detail, it's basically just matching the behavior of doing
// addr(uint256(addr) - some_number) in Solidity where some_number > uint256(addr).
if (bn.isNegative()) {
bn = ethers.BigNumber.from('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')
.add(bn)
.add(1)
}

// Convert to a hex string
let addr = bn.toHexString()
// Remove leading 0x so we can mutate the address a bit
addr = remove0x(addr)
// Make sure it's 40 characters (= 20 bytes)
addr = addr.padStart(40, '0')
// Only take the last 40 characters (= 20 bytes)
addr = addr.slice(addr.length - 40, addr.length)
// Add 0x again
addr = add0x(addr)
// Convert into a checksummed address
addr = ethers.utils.getAddress(addr)

return addr
}
1 change: 1 addition & 0 deletions packages/core-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './fees'
export * from './provider'
export * from './alias'
export * from './types'
export * from './bn'