Skip to content

Commit

Permalink
webinar example
Browse files Browse the repository at this point in the history
  • Loading branch information
ClaytonNorthey92 committed Aug 29, 2024
1 parent abce2ac commit 4cb0a9d
Show file tree
Hide file tree
Showing 10 changed files with 2,625 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,5 @@ flycheck_*.el
### Other files
## macOS
.DS_Store

node_modules
4 changes: 2 additions & 2 deletions e2e/deploy-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"cliqueSignerAddress": "0x78697c88847dfbbb40523e42c1f2e28a13a170be",
"l1UseClique": true,
"l1StartingBlockTag": "0x0",
"l2OutputOracleSubmissionInterval": 10,
"l2OutputOracleSubmissionInterval": 60,
"l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleProposer": "0x78697c88847dfbbb40523e42c1f2e28a13a170be",
Expand All @@ -30,7 +30,7 @@
"proxyAdminOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"finalSystemOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"superchainConfigGuardian": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"finalizationPeriodSeconds": 2,
"finalizationPeriodSeconds": 4,
"fundDevAccounts": true,
"l2GenesisBlockBaseFeePerGas": "0x1",
"gasPriceOracleOverhead": 2100,
Expand Down
23 changes: 16 additions & 7 deletions e2e/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ services:
- "-rpcport=18443"
- "-rpcconnect=bitcoind"
- "generatetoaddress"
- "3000" # need to generate a lot for greater chance to not spend coinbase
- "1000" # need to generate a lot for greater chance to not spend coinbase
- "$BTC_ADDRESS"
restart: on-failure

Expand Down Expand Up @@ -181,6 +181,8 @@ services:
command:
- "sh"
- "/tmp/entrypointl2.sh"
ports:
- 8546:8546
volumes:
- "./e2e/keystore:/tmp/keystore:ro"
- "./e2e/passwords.txt:/tmp/passwords.txt:ro"
Expand Down Expand Up @@ -236,22 +238,24 @@ services:
deploy:
restart_policy:
condition: "any"
environment:
LOG_LEVEL: TRACE
command:
- "op-batcher/bin/op-batcher"
- "--l1-eth-rpc=http://op-geth-l1:8545"
- "--l2-eth-rpc=http://op-geth-l2:8546"
- "--rollup-rpc=http://op-node:8547"
- "--poll-interval=1s"
- "--sub-safety-margin=1"
- "--num-confirmations=1"
- "--safe-abort-nonce-too-low-count=3"
- "--resubmission-timeout=30s"
- "--resubmission-timeout=15s"
- "--rpc.addr=0.0.0.0"
- "--rpc.port=8548"
- "--rpc.enable-admin"
- "--max-channel-duration=1"
- "--max-pending-tx=1"
- "--l1-eth-rpc=http://op-geth-l1:8545"
- "--private-key=${ADMIN_PRIVATE_KEY}"
- "--max-channel-duration=5"
- "--log.level=DEBUG"
- "--log.format=terminal"
- "--safe-abort-nonce-too-low-count=1"
depends_on:
op-geth-l1:
condition: "service_started"
Expand Down Expand Up @@ -282,6 +286,11 @@ services:
- "--l2oo-address=${L2OO_ADDRESS}"
- "--private-key=${ADMIN_PRIVATE_KEY}"
- "--l1-eth-rpc=http://op-geth-l1:8545"
- "--log.level=DEBUG"
- "--log.format=terminal"
- "--allow-non-finalized"
- "--resubmission-timeout=15s"
- "--safe-abort-nonce-too-low-count=1"

volumes:
l2configs:
5 changes: 3 additions & 2 deletions e2e/entrypointl2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ sh /tmp/genesisl2.sh
--http.port=8546 \
--http.addr \
'0.0.0.0' \
--http.vhosts \
'*' \
--http.vhosts='*' \
--http.api=debug,eth,txpool,net,engine,personal \
--allow-insecure-unlock \
--ws \
--ws.addr=0.0.0.0 \
--ws.port=28546 \
Expand Down
18 changes: 18 additions & 0 deletions e2e/examples/read-balances/L1ReadBalances.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract L1ReadBalances {
mapping(string => uint256) balances;
mapping(string => uint) lastUpdated;

function setBitcoinAddressBalance(string calldata btcAddress, uint256 balance, uint blockHeight) public {
// if you do this in real life, add some security checks to ensure the call is coming from the expected
// party. this is for example purposes only
balances[btcAddress] = balance;
lastUpdated[btcAddress] = blockHeight;
}

function getBitcoinAddressBalance(string calldata btcAddress) public view returns (uint256, uint) {
return (balances[btcAddress], lastUpdated[btcAddress]);
}
}
40 changes: 40 additions & 0 deletions e2e/examples/read-balances/L2ReadBalances.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface CrossDomainMessenger {
function sendMessage(address _target, bytes calldata _message, uint32 _minGasLimit) external;
}

interface L1ReadBalances {
function setBitcoinAddressBalance(string calldata btcAddress, uint256 balance, uint timestamp) external;
}

contract L2ReadBalances {
function getBitcoinAddressBalance(string calldata btcAddress) public view returns (uint256 balance) {
bytes memory converted = bytes(btcAddress);
(bool ok, bytes memory out) = address(0x40).staticcall(converted);
require(ok, "Static call to btcBalAddr precompile (0x40) contract failed");

return uint64(bytes8(out));
}

function sendBitcoinAddressBalanceToL1(address l1ReadBalancesAddress, string calldata btcAddress) public {
uint256 balance = getBitcoinAddressBalance(btcAddress);
CrossDomainMessenger cdm = CrossDomainMessenger(0x4200000000000000000000000000000000000007);

uint32 gas = 1000000;

require(gasleft() >= 1000000, "not enough gas left");

cdm.sendMessage(
l1ReadBalancesAddress,
abi.encodeCall(L1ReadBalances.setBitcoinAddressBalance,
(
btcAddress,
balance,
block.number
)),
gas
);
}
}
154 changes: 154 additions & 0 deletions e2e/examples/read-balances/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Example: Read Balances

*Warning: This example is NOT production ready; it is merely to demonstrate
localnet development and btc <-> eth interoperability.*

A short example to read a BTC balance from Hemi L2 and send that value to a
contract on Eth L1. We then read that value from a smart contract and print it.


## Requirements

* Localnet running
* see `heminetwork` README on how to run localnet
* node installed
* I have used and tested with `v21.1.0`


## For the impatient

install deps

```
npm install
```

run example

```
npm run example
```

## Step by step explaination

### Write the contracts

We will need two contracts to achieve our goal: a contract on L2 that has access
to hVM precompiles that reads a bitcoin address balance and a contract on L1
that receives this data and stores it, allowing it to be read.

You can find the L1 contract [here](L1ReadBalances.sol)

You can find the L2 contract [here](L2ReadBalances.sol)

We will be calling `L2ReadBalances.sendBitcoinAddressBalanceToL1` to read the
bitcoin balance of an address then send that balance via the
`CrossDomainMessenger` to L1.

`L1ReadBalances` will store this in a `mapping`, then we will be able to read
that mapping value.

### Bridge some Eth to Hemi Eth

We will need to deploy our L2 contract, and thus will need Hemi Eth. We can
do this by using the `L1StandardBridgeProxy`.

We bridge Eth from the dev account (we run the L1 in dev mode, this is the
account that receives the funds) to our dev account on L2.

```javascript
l1.eth.sendTransaction({
from: devAccount,
to: l1StandardBridgeProxyAddress,
value: 1000000000000000000000000000000000,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
gas: 1000000,
});
```

Now that we have bridged Hemi Eth, we can deploy our L2 contract. There is a
bit too much to paste here, please view the use of the `deployContract` function
in [index.js](index.js).


### Call our L2 contract

We call our L2 contract to read the bitcoin balance of the regtest miner's
address. This can be done like so (where `deployedContract` is the L2 contract)

```javascript
const result = await deployedContract.methods
.sendBitcoinAddressBalanceToL1(
l1DeployedContract.options.address,
btcAddress,
)
.send({
from: devAccount,
});
```

### Prove and relay the transaction

Using the `result` from above, we need to prove the transaction, then finalize
(relay) it to L1.

Again, view [index.js](index.js) for more details.

```javascript
console.log("waiting for message status READY_TO_PROVE");

await waitForStatus(
messenger,
result.transactionHash,
optimism.MessageStatus.READY_TO_PROVE,
5 * 1000,
);

await messenger.proveMessage(result.transactionHash);

console.log("waiting for message status READY_FOR_RELAY");

await waitForStatus(
messenger,
result.transactionHash,
optimism.MessageStatus.READY_FOR_RELAY,
5 * 1000,
);

await messenger.finalizeMessage(result.transactionHash);

console.log("waiting for message status RELAYED");

await waitForStatus(
messenger,
result.transactionHash,
optimism.MessageStatus.RELAYED,
5 * 1000,
);
```

### Read the balances

We can read the L1 and L2 btc balances.

*Note: L2 may be higher than L1, since that btc address has been mining bitcoin
while our script has been running.*

```javascript
const bitcoinBalance = await deployedContract.methods
.getBitcoinAddressBalance(btcAddress)
.call();

console.log(
`bitcoin balance according to the l2 precompile is ${bitcoinBalance}`,
);

const l1BtcBalance = await l1DeployedContract.methods
.getBitcoinAddressBalance(btcAddress)
.call();

console.log(
`l1 btc balance is ${l1BtcBalance} (should be <= than l2 ${bitcoinBalance})`,
);
```
Loading

0 comments on commit 4cb0a9d

Please sign in to comment.