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

Arbitrum L1 to L2 messaging proposal #13516

Merged
merged 39 commits into from Apr 4, 2024

Conversation

blahkheart
Copy link
Contributor

Description

This PR adds an example of how to create a DAO proposal for execution of Arbitrum's L1-to-L2 message passing system (aka "retryable tickets")

Issues

Fixes #13515
Refs #13515

Checklist:

  • 1 PR, 1 purpose: my Pull Request applies to a single purpose
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the docs to reflect my changes if applicable
  • I have added tests (and stories for frontend components) that prove my fix is effective or that my feature works
  • I have performed a self-review of my own code
  • If my code involves visual changes, I am adding applicable screenshots to this thread

Release Note Draft Snippet

Copy link

cla-bot bot commented Mar 25, 2024

Thank you for your pull request and welcome to Unlock! We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @blahkheart on file.
In order for us to review and merge your code, please open another pull request with a single modification: your github username added to the file .clabot.
Thank you!

Copy link

cla-bot bot commented Mar 25, 2024

Thank you for your pull request and welcome to Unlock! We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @blahkheart on file.
In order for us to review and merge your code, please open another pull request with a single modification: your github username added to the file .clabot.
Thank you!

Copy link
Member

@clemsos clemsos left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Good start. A few comments added !

.clabot Outdated Show resolved Hide resolved
governance/.env Outdated Show resolved Hide resolved
governance/.env Outdated
# Duplicate this file as .env

# Your Private key
DEVNET_PRIVKEY="0x14e1a3103ce06d0348acf19df3df1965ee55f9cdb16708c0bb8cd3d843ab8805"
Copy link
Member

Choose a reason for hiding this comment

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

You should never check a private key into a git repo...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah totally agree with that, I didn't know .env wasn't included in the .gitignore file, especially because I saw a .env.copy file already present in the /governance/ folder, which I copied and renamed to .env. For what it's worth it's a fresh wallet I created specifically for the tests.

governance/.env Outdated Show resolved Hide resolved
governance/proposals/010-arbitrum-l1-l2-messaging.js Outdated Show resolved Hide resolved
Copy link

cla-bot bot commented Mar 26, 2024

Thank you for your pull request and welcome to Unlock! We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @blahkheart on file.
In order for us to review and merge your code, please open another pull request with a single modification: your github username added to the file .clabot.
Thank you!

const l2Wallet = new ethers.Wallet(walletPrivateKey, l2Provider)

module.exports = async () => {
await arbLog('Cross-chain Proposer')
Copy link
Member

Choose a reason for hiding this comment

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

what is arbLog used for ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It came with the arbitrum sdk, its more or less a branding/description of the script.

Copy link
Member

Choose a reason for hiding this comment

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

Can we just use a regular console.log please?

gasPriceBid,
transfer_calldata,
],
value: L1ToL2MessageGasParams.deposit.toNumber() * 10, // I Multiply by 10 to add extra in case gas changes due to proposal delay
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure you need to pass the value again here? It is already declared above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, from my understanding of the existing format, and correct me if I'm wrong but this value property is responsible for passing the eth amount to a payable function, and in this case that's the eth deposit to be sent to arbitrum's inbox contract.
If you were referring to this line const values = [L1ToL2MessageGasParams.deposit.toNumber() * 10] that was declared to show how the proposal can be created from an interface like etherscan or remix, but if its not necessary or confusing I can remove it as well.

@cla-bot cla-bot bot added the cla-signed label Mar 26, 2024
Copy link
Member

@julien51 julien51 left a comment

Choose a reason for hiding this comment

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

Can you please use #13509 to run tests for your proposal, then import the values in Tenderly to check that it passes?

export GNOSISSCAN_API_KEY=

# This is a sample .env file for use in local development.
Copy link
Member

Choose a reason for hiding this comment

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

Please remeove these changes...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay got it

Comment on lines 6 to 7
"@arbitrum/nitro-contracts": "v1.0.2",
"@arbitrum/sdk": "^v3.1.9",
Copy link
Member

Choose a reason for hiding this comment

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

Please use fixed version numbers (no v or ^)

@@ -21,8 +23,9 @@
"@unlock-protocol/hardhat-helpers": "workspace:^",
"@unlock-protocol/hardhat-plugin": "workspace:^",
"@unlock-protocol/networks": "workspace:./packages/networks",
"arb-shared-dependencies": "^1.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

Same here!

/**
* Set up: instantiate L1 / L2 wallets connected to providers
*/
const walletPrivateKey = 'Your_Private_key'
Copy link
Member

Choose a reason for hiding this comment

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

Please use env variables here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@clemsos earlier informed me .env is not supported, how else would I use env variables?

Copy link
Member

Choose a reason for hiding this comment

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

Process.env.PRIVATE_KEY for example

* Set up: instantiate L1 / L2 wallets connected to providers
*/
const walletPrivateKey = 'Your_Private_key'
const L1RPC = 'https://mainnet.infura.io/v3/<your_infura_api_key>'
Copy link
Member

Choose a reason for hiding this comment

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

Please use https://rpc.unlock-protocol.com/1 so that this does not break in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it

const ARBTokenAddressOnL2 = '0x912CE59144191C1204E64559FE8253a0e49E6548' // ARB TOKEN ADDRESS ON ARBITRUM ONE
const grantsContractAddress = '0x00D5E0d31d37cc13C645D86410aB4cB7Cb428ccA'
const timelockL2Alias = '0x28ffDfB0A6e6E06E95B3A1f928Dc4024240bD87c' // Timelock Alias Address on L2
const L1TimelockContract = '0x17EEDFb0a6E6e06E95B3A1F928dc4024240BC76B' // Timelock Address mainnet
Copy link
Member

Choose a reason for hiding this comment

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

We should probably move that to a config file in this folder.

from: L1TimelockContract,
to: ARBTokenAddressOnL2,
l2CallValue: 0,
excessFeeRefundAddress: l2Wallet.address,
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain why l2Wallet would recive the excess here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's following the format from arbitrum's example, but I didn't need to change it because the L1 and L2 wallets are the same. Ideally you want to specify the wallet address you want the excess eth refund to be sent to if it's different.

Copy link
Member

Choose a reason for hiding this comment

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

Where is the excess ETH coming from? An funds should go back to the DAO and not to some external address.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, it has now been changed to the DAO's address. The reason for having it that way in the first place is due to the nature of the transaction. The createRetryable function forces the caller to pay the value sent to the Delayed Inbox contract, Therefore ETH is paid by the person calling the execute() function and not the Timelock. So it was set up so whoever creates the proposal and provides the private key gets any excess ETH refund sent to that address. But that was flawed by the assumption that the proposer will always be the executor. Ideally, the DAO will fund/reimburse whoever executes the proposal so excess should return to the DAO, thank you for pointing that out.

to: ARBTokenAddressOnL2,
l2CallValue: 0,
excessFeeRefundAddress: l2Wallet.address,
callValueRefundAddress: l2Wallet.address,
Copy link
Member

Choose a reason for hiding this comment

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

Same here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same reason as before

const inbox_calldata = iface_inbox.encodeFunctionData(
'createRetryableTicket',
[
ARBTokenAddressOnL2,
Copy link
Member

Choose a reason for hiding this comment

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

Please add comments in front of each of these params to provide their names

]
)

const proposalName = `# Test Transaction before 7k ARB Transfer To Fund Unlock Protocol’s Ecosystem via Grants Stack This proposal requests to use 1 ARB from the tokens given to Unlock Protocol DAO by ArbitrumDAO to run a test transaction to de-risk the transfer of 7k ARB tokens to fund the retroQF round on Grants Stack.`
Copy link
Member

Choose a reason for hiding this comment

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

Please format this correctly.

This would be

Test Transaction before 7k ARB Transfer To Fund Unlock Protocol’s Ecosystem via Grants Stack This proposal requests to use 1 ARB from the tokens given to Unlock Protocol DAO by ArbitrumDAO to run a test transaction to de-risk the transfer of 7k ARB tokens to fund the retroQF round on Grants Stack.

Make sure there is a title, one (or more) distinct paragraphs, as well as links to provide clarity (links to the snapshot, to the UI on which people are voting... etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it

blahkheart and others added 16 commits March 30, 2024 18:32
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rk-parse to v11 (unlock-protocol#13531)

* Updated unified & related dependencies

* Updated remark
…15, remark-parse to v11" (unlock-protocol#13532)

Revert "fix(deps): update dependency unified to v11, remark-html to v15, rema…"

This reverts commit 80969a6.
unlock-protocol#13528)

* changed the UI for refunds per Gnosis

* refactored receipt number
* Update Referrals.tsx

microcopy fix

* Update CancellationForm.tsx

microcopy update

* Update UpdateReferralFee.tsx

microcopy update
…-protocol#13509)

* allow test execution of proposal from tx id

* parse and pass correctly tx args

* add unlock addresses in whales

* allow proposal id only for votes

* pass odwn tx receipt

* herlpes for gov contract

* log events after proposal execution

* Update governance/scripts/gov/index.js

---------

Co-authored-by: Julien Genestoux <julien.genestoux@gmail.com>
*/
const walletPrivateKey = process.env.PRIVATE_KEY
const L1RPC = 'https://rpc.unlock-protocol.com/1' // mainnet RPC
const L2RPC = 'https://rpc.unlock-protocol.com/42161' // Arbitrum RPC
Copy link
Member

Choose a reason for hiding this comment

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

same

const l2Wallet = new ethers.Wallet(walletPrivateKey, l2Provider)

module.exports = async () => {
console.log('Cross-chain Proposer')
Copy link
Member

Choose a reason for hiding this comment

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

This is misleading. Can you use a better description please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what you mean here, what are you referring to please?

const tokenAmount = ethers.parseEther('1')

// Create an instance of the Interface from the ABIs
const iface_erc20 = new ethers.Interface(ERC20_ABI)
Copy link
Member

Choose a reason for hiding this comment

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

Please respect the styleguide if you can:

Suggested change
const iface_erc20 = new ethers.Interface(ERC20_ABI)
const ifaceErc20 = new ethers.Interface(ERC20_ABI)

const iface_inbox = new ethers.Interface(INBOX_ABI)

// Encode the ERC20 Token transfer calldata
const transfer_calldata = iface_erc20.encodeFunctionData('transfer', [
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const transfer_calldata = iface_erc20.encodeFunctionData('transfer', [
const transferCalldata = iface_erc20.encodeFunctionData('transfer', [

l1Provider
)
const gasPriceBid = await l2Provider.getGasPrice()
const ETHDeposit = L1ToL2MessageGasParams.deposit.toNumber() * 10 // I Multiply by 10 to add extra in case gas changes due to proposal delay
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain what this ETHDeposit is?

Copy link
Contributor Author

@blahkheart blahkheart Apr 3, 2024

Choose a reason for hiding this comment

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

ETHDeposit is the amount ETH value that must be sent to the Delayed Inbox Contract for a successful retryable ticket creation. It is estimated by calling the l1ToL2MessageGasEstimate.estimateAll({params}) function provided by the Arbitrum sdk. When the createRetryableTicket function is called, it forces the caller to pay this amount else reverts, this is why the excessFeeRefundAddress you asked about earlier is needed, to send any excess back. Here I multiply the actual value by 10 as a buffer because in the time it takes to execute a proposal that value may change.

Note that, the function createRetryableTicket forces the sender to provide a reasonable amount of funds (at least enough for submitting, and attempting to execute the ticket), but that doesn't guarantee a successful auto-redemption. Checkout arbitrum docs for more info..

Copy link
Member

Choose a reason for hiding this comment

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

ok that makes sense. Please add this nside the proposal. Because this comment won't be easy to find for people who are reviewing the proposal!

* Used providers, and addresses from networks package
* Renamed config.js to constants.js
* Used suggested style guide for variable names
* More detailed description
@@ -2,6 +2,7 @@
cache
contracts
artifacts
.env
Copy link
Member

Choose a reason for hiding this comment

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

same here

).connect(l2Wallet)

const balanceOf = await L2TokenContract.balanceOf(TIMELOCK_L2_ALIAS)
const tokenAmount = ethers.parseEther('1')
Copy link
Member

Choose a reason for hiding this comment

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

Please let's use the real anount. It does not make sense to send 1 ARB if this has been tested correctly (as I am sure you have done by now!)

Also, please use ethers.parseUnits as this is an ERC20, not Ethers.

This proposal requests to use 1 ARB from the tokens given to Unlock Protocol DAO by ArbitrumDAO to run a test transaction to de-risk the transfer of 7k ARB tokens to fund the retroQF round on Grants Stack.

#### Current situation of DAO's ARB Tokens
- total: ${ethers.formatEther(balanceOf).toString()} ARB.
Copy link
Member

Choose a reason for hiding this comment

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

same here. do not use formatEther when it is an ERC20.


#### About the proposal
The proposal contains a single call to the Arbitrum Delayed Inbox Contract's \`createRetryableTicket\` function on mainnet to create a \`Retryable Ticket\` that will attempt to execute an L2 request to the ARB token contract to transfer ${ethers
.formatEther(tokenAmount)
Copy link
Member

Choose a reason for hiding this comment

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

same

The proposal contains a single call to the Arbitrum Delayed Inbox Contract's \`createRetryableTicket\` function on mainnet to create a \`Retryable Ticket\` that will attempt to execute an L2 request to the ARB token contract to transfer ${ethers
.formatEther(tokenAmount)
.toString()} of token from the Timelock L2 Alias address \`${TIMELOCK_L2_ALIAS}\` to the [grants contract](https://arbiscan.io/address/0x00d5e0d31d37cc13c645d86410ab4cb7cb428cca) - \`transfer(${GRANTS_CONTRACT_ADDRESS},${ethers
.formatEther(tokenAmount)
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Member

@julien51 julien51 left a comment

Choose a reason for hiding this comment

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

since the DAO does not hold any ETH and yet it needs to send ETH to the mailbox contract (if I understand your proposal correctly), how will this work?

@clemsos
Copy link
Member

clemsos commented Apr 3, 2024

@blahkheart I refactored the proposal script a bit to make it clearer. Almost there !

"eslint": "8.54.0",
"ethers": "6.10.0",
"ethers5": "npm:ethers@5",
Copy link
Member

@clemsos clemsos Apr 3, 2024

Choose a reason for hiding this comment

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

We have to add ethers5 here, as it is required by the arbitrum sdk

@blahkheart
Copy link
Contributor Author

@blahkheart I refactored the proposal script a bit to make it clearer. Almost there !

Great! Is any further action required on my part?

@blahkheart
Copy link
Contributor Author

since the DAO does not hold any ETH and yet it needs to send ETH to the mailbox contract (if I understand your proposal correctly), how will this work?

I think I mentioned this earlier, one way I feel this could work is whoever calls the execute function on the governor has to provide the ETH to be sent. So, the DAO then funds/refunds the person, anyone can do this (I committed to do that on the test one, and I'm happy to do so again ) or the DAO can elect someone to do that and fund/refund them to do it.

* Use actual amount of ARB tokens required
* Use correct proposal amount
* Revert .gitignore, and .env.copy changes
@blahkheart blahkheart requested a review from julien51 April 3, 2024 19:59
# Transfer 8200 ARB To Fund Unlock Protocol’s Ecosystem via Grants Stack

### Goal of the proposal
This proposal requests to use 8200 ARB from the tokens given to Unlock Protocol DAO by ArbitrumDAO to fund the retroQF round on Grants Stack for projects building on Unlock Protocol.
Copy link
Member

Choose a reason for hiding this comment

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

This is UP from what the snapshot proposal initially asked...

Asking for 7k in ARB to help Fund Unlock Protocol’s Ecosystem via Grants Stack

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is an explanation for why this is up from the initial 7k as well as a reference to the snapshot proposal included in the description

@julien51
Copy link
Member

julien51 commented Apr 3, 2024

Please @blahkheart merge the conflicts on yarn.lock!

@julien51 julien51 merged commit a5c3404 into unlock-protocol:master Apr 4, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Proposal example for Arbitrum L1 to L2 Messaging (aka Retryable tickets)
6 participants