Commit 092b0901 authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: fix deposit helpers

There is a solidity contract for computing the L2 tx hash
so that its easy for devs to compute it with an `eth_call`.
This makes it easy for users that do not have a custom library
written in their language to compute the L2 tx hash.

Also fix the typescript implementation of the deposit tx type
so that the correct tx hash is computed.
parent 28138a34
---
'@eth-optimism/contracts-bedrock': patch
---
Update to new L2 tx hash style for deposits
...@@ -105,7 +105,7 @@ library CrossDomainHashing { ...@@ -105,7 +105,7 @@ library CrossDomainHashing {
raw[6] = Lib_RLPWriter.writeBytes(_data); raw[6] = Lib_RLPWriter.writeBytes(_data);
bytes memory encoded = Lib_RLPWriter.writeList(raw); bytes memory encoded = Lib_RLPWriter.writeList(raw);
return abi.encodePacked(uint8(0x7e), encoded); return abi.encodePacked(uint8(0x7e), uint8(0x0), encoded);
} }
/** /**
......
...@@ -14,10 +14,10 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -14,10 +14,10 @@ contract CrossDomainHashing_Test is CommonTest {
// TODO(tynes): turn this into differential fuzzing // TODO(tynes): turn this into differential fuzzing
// it is very easy to do so with the typescript // it is very easy to do so with the typescript
function test_l2TransactionHash() external { function test_l2TransactionHash() external {
bytes32 l1BlockHash = 0xd1a498e053451fc90bd8a597051a1039010c8e55e2659b940d3070b326e4f4c5; bytes32 l1BlockHash = 0xd25df7858efc1778118fb133ac561b138845361626dfb976699c5287ed0f4959;
uint256 logIndex = 0x0; uint256 logIndex = 0x1;
address from = address(0xDe3829A23DF1479438622a08a116E8Eb3f620BB5); address from = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
address to = address(0xB7e390864a90b7b923C9f9310C6F98aafE43F707); address to = 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244;
bool isCreate = false; bool isCreate = false;
uint256 mint = 0xe043da617250000; uint256 mint = 0xe043da617250000;
uint256 value = 0xde0b6b3a7640000; uint256 value = 0xde0b6b3a7640000;
...@@ -31,7 +31,7 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -31,7 +31,7 @@ contract CrossDomainHashing_Test is CommonTest {
assertEq( assertEq(
sourceHash, sourceHash,
0x77fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd0735 0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc
); );
bytes memory raw = CrossDomainHashing.L2Transaction( bytes memory raw = CrossDomainHashing.L2Transaction(
...@@ -48,11 +48,11 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -48,11 +48,11 @@ contract CrossDomainHashing_Test is CommonTest {
assertEq( assertEq(
raw, raw,
hex"7ef862a077fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd073594de3829a23df1479438622a08a116e8eb3f620bb594b7e390864a90b7b923c9f9310c6f98aafe43f707880e043da617250000880de0b6b3a7640000832dc6c080" hex"7e00f862a0f923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc94f39fd6e51aad88f6f4ce6ab8827279cfffb9226694b79f76ef2c5f0286176833e7b2eee103b1cc3244880e043da617250000880de0b6b3a7640000832dc6c080"
); );
bytes32 digest = CrossDomainHashing.L2TransactionHash( bytes32 digest = CrossDomainHashing.L2TransactionHash(
l1BlockHash, l1BlockHash,
logIndex, logIndex,
from, from,
to, to,
...@@ -65,7 +65,7 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -65,7 +65,7 @@ contract CrossDomainHashing_Test is CommonTest {
assertEq( assertEq(
digest, digest,
0xf5f97d03e8be48a4b20ed70c9d8b11f1c851bf949bf602b7580985705bb09077 0xf58e30138cb01330f6450b9a5e717a63840ad2e21f17340105b388ad3c668749
); );
} }
} }
...@@ -49,7 +49,7 @@ interface DepositTxOpts { ...@@ -49,7 +49,7 @@ interface DepositTxOpts {
sequenceNumber?: BigNumberish sequenceNumber?: BigNumberish
} }
interface DepostTxExtraOpts { interface DepositTxExtraOpts {
domain?: SourceHashDomain domain?: SourceHashDomain
l1BlockHash?: string l1BlockHash?: string
logIndex?: BigNumberish logIndex?: BigNumberish
...@@ -57,7 +57,8 @@ interface DepostTxExtraOpts { ...@@ -57,7 +57,8 @@ interface DepostTxExtraOpts {
} }
export class DepositTx { export class DepositTx {
public type = '0x7E' public type = 0x7e
public version = 0x00
private _sourceHash?: string private _sourceHash?: string
public from: string public from: string
public to: string | null public to: string | null
...@@ -135,13 +136,21 @@ export class DepositTx { ...@@ -135,13 +136,21 @@ export class DepositTx {
this.data || '0x', this.data || '0x',
] ]
return ethers.utils.hexConcat([this.type, ethers.utils.RLP.encode(fields)]) return ethers.utils.hexConcat([
BigNumber.from(this.type).toHexString(),
BigNumber.from(this.version).toHexString(),
ethers.utils.RLP.encode(fields),
])
} }
decode(raw: BytesLike, extra: DepostTxExtraOpts = {}) { decode(raw: BytesLike, extra: DepositTxExtraOpts = {}) {
const payload = ethers.utils.arrayify(raw) const payload = ethers.utils.arrayify(raw)
const transaction = ethers.utils.RLP.decode(payload.slice(1)) if (payload[0] !== this.type) {
throw new Error(`Invalid type ${payload[0]}`)
}
this.version = payload[1]
const transaction = ethers.utils.RLP.decode(payload.slice(2))
this._sourceHash = transaction[0] this._sourceHash = transaction[0]
this.from = handleAddress(transaction[1]) this.from = handleAddress(transaction[1])
this.to = handleAddress(transaction[2]) this.to = handleAddress(transaction[2])
...@@ -165,7 +174,7 @@ export class DepositTx { ...@@ -165,7 +174,7 @@ export class DepositTx {
return this return this
} }
static decode(raw: BytesLike, extra?: DepostTxExtraOpts): DepositTx { static decode(raw: BytesLike, extra?: DepositTxExtraOpts): DepositTx {
return new this().decode(raw, extra) return new this().decode(raw, extra)
} }
......
import { task, types } from 'hardhat/config' import { task, types } from 'hardhat/config'
import { Contract, providers, utils, Wallet, Event } from 'ethers' import { providers, utils, Wallet, Event } from 'ethers'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import 'hardhat-deploy'
import { DepositTx } from '../src' import { DepositTx } from '../src'
...@@ -31,22 +32,14 @@ task('deposit', 'Deposits funds onto L2.') ...@@ -31,22 +32,14 @@ task('deposit', 'Deposits funds onto L2.')
process.env.PRIVATE_KEY, process.env.PRIVATE_KEY,
types.string types.string
) )
.addOptionalParam(
'depositContractAddr',
'Address of deposit contract.',
'deaddeaddeaddeaddeaddeaddeaddeaddead0001',
types.string
)
.setAction(async (args, hre) => { .setAction(async (args, hre) => {
const { const { l1ProviderUrl, l2ProviderUrl, to, amountEth, privateKey } = args
l1ProviderUrl, const proxy = await hre.deployments.get('OptimismPortalProxy')
l2ProviderUrl,
to, const OptimismPortal = await hre.ethers.getContractAt(
amountEth, 'OptimismPortal',
depositContractAddr, proxy.address
privateKey, )
} = args
const depositFeedArtifact = await hre.deployments.get('OptimismPortal')
const l1Provider = new providers.JsonRpcProvider(l1ProviderUrl) const l1Provider = new providers.JsonRpcProvider(l1ProviderUrl)
const l2Provider = new providers.JsonRpcProvider(l2ProviderUrl) const l2Provider = new providers.JsonRpcProvider(l2ProviderUrl)
...@@ -65,16 +58,11 @@ task('deposit', 'Deposits funds onto L2.') ...@@ -65,16 +58,11 @@ task('deposit', 'Deposits funds onto L2.')
throw new Error(`${from} has no balance`) throw new Error(`${from} has no balance`)
} }
const depositFeed = new Contract(
depositContractAddr,
depositFeedArtifact.abi
).connect(l1Wallet)
const amountWei = utils.parseEther(amountEth) const amountWei = utils.parseEther(amountEth)
const value = amountWei.add(utils.parseEther('0.01')) const value = amountWei.add(utils.parseEther('0.01'))
console.log(`Depositing ${amountEth} ETH to ${to}`) console.log(`Depositing ${amountEth} ETH to ${to}`)
// Below adds 0.01 ETH to account for gas. // Below adds 0.01 ETH to account for gas.
const tx = await depositFeed.depositTransaction( const tx = await OptimismPortal.depositTransaction(
to, to,
amountWei, amountWei,
'3000000', '3000000',
...@@ -84,6 +72,9 @@ task('deposit', 'Deposits funds onto L2.') ...@@ -84,6 +72,9 @@ task('deposit', 'Deposits funds onto L2.')
) )
console.log(`Got TX hash ${tx.hash}. Waiting...`) console.log(`Got TX hash ${tx.hash}. Waiting...`)
const receipt = await tx.wait() const receipt = await tx.wait()
console.log(
`Included in block ${receipt.blockHash} with index ${receipt.logIndex}`
)
// find the transaction deposited event and derive // find the transaction deposited event and derive
// the deposit transaction from it // the deposit transaction from it
...@@ -91,6 +82,7 @@ task('deposit', 'Deposits funds onto L2.') ...@@ -91,6 +82,7 @@ task('deposit', 'Deposits funds onto L2.')
(e: Event) => e.event === 'TransactionDeposited' (e: Event) => e.event === 'TransactionDeposited'
) )
const l2tx = DepositTx.fromL1Event(event) const l2tx = DepositTx.fromL1Event(event)
console.log(`Deposit has log index ${event.logIndex}`)
const hash = l2tx.hash() const hash = l2tx.hash()
console.log(`Waiting for L2 TX hash ${hash}`) console.log(`Waiting for L2 TX hash ${hash}`)
......
...@@ -11,26 +11,26 @@ describe('Helpers', () => { ...@@ -11,26 +11,26 @@ describe('Helpers', () => {
// constants serialized using optimistic-geth // constants serialized using optimistic-geth
// TODO(tynes): more tests // TODO(tynes): more tests
const hash = const hash =
'0xf5f97d03e8be48a4b20ed70c9d8b11f1c851bf949bf602b7580985705bb09077' '0xf58e30138cb01330f6450b9a5e717a63840ad2e21f17340105b388ad3c668749'
const raw = const raw =
'0x7ef862a077fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd073594de3829a23df1479438622a08a116e8eb3f620bb594b7e390864a90b7b923c9f9310c6f98aafe43f707880e043da617250000880de0b6b3a7640000832dc6c080' '0x7e00f862a0f923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc94f39fd6e51aad88f6f4ce6ab8827279cfffb9226694b79f76ef2c5f0286176833e7b2eee103b1cc3244880e043da617250000880de0b6b3a7640000832dc6c080'
const tx = new DepositTx({ const tx = new DepositTx({
from: '0xDe3829A23DF1479438622a08a116E8Eb3f620BB5', from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
gas: '0x2dc6c0', gas: '0x2dc6c0',
data: '0x', data: '0x',
to: '0xB7e390864a90b7b923C9f9310C6F98aafE43F707', to: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244',
value: '0xde0b6b3a7640000', value: '0xde0b6b3a7640000',
domain: SourceHashDomain.UserDeposit, domain: SourceHashDomain.UserDeposit,
l1BlockHash: l1BlockHash:
'0xd1a498e053451fc90bd8a597051a1039010c8e55e2659b940d3070b326e4f4c5', '0xd25df7858efc1778118fb133ac561b138845361626dfb976699c5287ed0f4959',
logIndex: 0, logIndex: 1,
mint: '0xe043da617250000', mint: '0xe043da617250000',
}) })
const sourceHash = tx.sourceHash() const sourceHash = tx.sourceHash()
expect(sourceHash).to.deep.eq( expect(sourceHash).to.deep.eq(
'0x77fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd0735' '0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc'
) )
const encoded = tx.encode() const encoded = tx.encode()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment