-
Notifications
You must be signed in to change notification settings - Fork 75
Optimism L2 integration [Hardhat - v2] #29
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
Conversation
5f686cf to
5e69a49
Compare
abd35d9 to
8c31861
Compare
f338602 to
371770a
Compare
371770a to
a6cadd5
Compare
32dba9a to
83054d9
Compare
83054d9 to
48a0863
Compare
b865a95 to
188a322
Compare
f94550b to
b063cb4
Compare
| onlyInitialized() | ||
| { | ||
| // Unless explicitly unsafe op, stop deposits to contracts (avoid accidentally lost tokens) | ||
| require(_isUnsafe() || !Address.isContract(_to), "Unsafe deposit to contract"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused by how this line functions. Since this contract exists on L1, but the _to address is pointing at an L2 address. Can Address.isContract(_to) check if it's a contract on L2 from L1?
(this might have to do with my lack of understanding of how Optimism fully works...)
Also shows up in the L2 contract. so also wondering the flip of the statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait...I think I figured it out. Correct me if I am wrong:
- Since OVM is a roll up solution where it is completely dependent on L1, the addresses line up directly with what is seen on L1
- So if the address on L1 is a smart contract, funds sent to that address on L2 would only be accessible if the smart contract also had integrated with OVM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The questions are great! :)
This specific line is a little bit confusing, mostly because we could not extend deposit/depositTo & withdraw/withdrawTo (they are not virtual). So I added an unsafe marker to the third function depositToUnsafe/withdrawToUnsafe to be able to set up this check.
We want to block the bridge transfers to (local) contracts, unless explicit.
This is because, when sending to a contract it's easy to get tokens stuck, as you need to have a contract be available at the same address on L2. The "only way" to have the same contract address on L1 & L2 is to use CREATE with the same nonce. The CREATE2 opcode will not work, as the OVM compiled bytecode will be different than EVM (for contracts of any relevant complexity), so they will get different addresses on L1 & L2 networks.
This actually happened to a Synthetix user with an Argent SC wallet, that used the bridge from his smart wallet and locked his funds. The wallet sent to itself on L2, and there is no way (except luck) to recreate the same address on L2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great explanation! And yes blocking token transfers to contracts makes sense.
If I understand your explanation correctly, then on L1 there could be a smart contract deployed at a certain address, but not on L2 (and vice versa).
So I think I still have my original question: how can a contract deployed on L1 as the gateway detect that an address on L2 is a smart contract or not? it seems like !Address.isContract(_to) would only check on L1... (or on the layer it is deployed on)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we only check for local contracts, L1 gateway checks for L1 contracts, and similarly on L2.
If an L1 gateway receives a transfer request to L2 address, it checks if that address is a contract on L1. If it is, the gateway makes sure the user is explicit in its intention. If not, the gateway rejects to avoid accidental mistakes, as the contract address space, at least for the CREATE2 opcode, is vastly different for the two networks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, that makes sense! thanks :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, the logic is looking pretty good! Left a few comments, but most of it is me trying to understand how Optimism works with L1 (Eth) at the smart contract level. Feel free to correct me if you think I am misunderstanding :)
I have only taken a look at the bridge contracts so far. I will also take a look at the other components (mocks, scripts, etc) at another point. (lots to go through!)
b063cb4 to
556d5cb
Compare
556d5cb to
aef60f6
Compare
af21a76 to
add2c60
Compare
90c6a28 to
486f675
Compare
486f675 to
0a984b2
Compare
| * As the OVM_ProxyEOA.sol contract source could potentially change in the future (i.e., due to a fork), | ||
| * here we actually track a set of possible EOA proxy contracts. | ||
| */ | ||
| abstract contract OVM_EOACodeHashSet is ConfirmedOwner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like something we should pull into our repo and give you a nice library for :)
93152a0 to
d874349
Compare
|
Closing in favor of: |
LinkTokenChildcontract, an access-controlled mintable & burnable LinkToken, for use on sidechains and L2 networksdeployGatewaysto deploy the L1<->L2 bridge that consumers might find useful.TODO:
OVM_L2DepositedLinkToken.solinto a proxiedOVM_L2ERC20Gateway.soland a non-proxiedLinkTokenChild.soltoken contractERC677ReceiversotransferAndCallcan be used to avoid approving tokens before deposit/withdrawOpUnsafehelper contract when/if upstreamdeposit/depositTo&withdraw/withdrawToare made virtual (PR #31)