Skip to content

Commit

Permalink
feat(btc-staking): UTXOGateway and BTC staking test (#132)
Browse files Browse the repository at this point in the history
* feat: UTXOGateway for btc staking and testing

* chore: fix solhint
  • Loading branch information
adu-web3 authored Feb 14, 2025
1 parent 490b3f0 commit 992a94a
Show file tree
Hide file tree
Showing 87 changed files with 7,316 additions and 5,239 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/compare-layouts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ jobs:
runs-on: ubuntu-latest

steps:
# The repository needs to be available for script/deployedContracts.json
# The repository needs to be available for script/deployments/deployedContracts.json
# and script/compareLayouts.js.
- name: Checkout the repository
if: ${{ github.event.workflow_run.conclusion == 'success' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/forge-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ jobs:
uses: actions/checkout@v4
- name: Validate deployedContracts.json and prepare artifact
run: |
data=$(cat script/deployedContracts.json)
data=$(cat script/deployments/deployedContracts.json)
bootstrap=$(echo "$data" | jq -r '.clientChain.bootstrapLogic // empty')
clientGateway=$(echo "$data" | jq -r '.clientChain.clientGatewayLogic // empty')
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ node_modules
.secrets

script/integration/*.json
yarn-error.log
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
[submodule "lib/safe-smart-account"]
path = lib/safe-smart-account
url = https://github.com/safe-global/safe-smart-account
[submodule "lib/create3-factory"]
path = lib/create3-factory
url = https://github.com/zeframlou/create3-factory
127 changes: 127 additions & 0 deletions docs/ClientChainGateway.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# ClientChainGateway Documentation

The ClientChainGateway serves as the primary interface for stakers to interact with the Exocore network. It functions as a LayerZero application capable of cross-chain messaging and manages both Vault and Capsule contracts.

## Overview

ClientChainGateway implements two types of restaking:
1. LST (Liquid Staking Token) Restaking
2. NST (Native Staking Token) Restaking

## LST Restaking Functions

### Deposit Operations
- `deposit(address token, uint256 amount)`
- Deposits whitelisted tokens into Exocore
- Locks tokens in a Vault contract
- Sends cross-chain message to Exocore for accounting
- Always considered successful on Exocore side
- Requires relay fee in ETH for cross-chain message

- `depositThenDelegateTo(address token, uint256 amount, string operator)`
- Combines deposit and delegate operations
- Deposits tokens and immediately delegates to specified operator
- More gas-efficient than separate calls
- Delegation can fail if operator is not registered
- Requires relay fee in ETH for cross-chain message

### Delegation Operations
- `delegateTo(string operator, address token, uint256 amount)`
- Delegates previously deposited tokens to an operator
- Requires prior deposit
- Sends cross-chain message to Exocore
- Updates delegation accounting on Exocore
- Requires relay fee in ETH for cross-chain message

- `undelegateFrom(string operator, address token, uint256 amount)`
- Undelegates tokens from an operator
- Requires prior delegation
- Sends cross-chain message to Exocore
- Initiates unbonding period
- Requires relay fee in ETH for cross-chain message

### Withdrawal Operations
- `claimPrincipalFromExocore(address token, uint256 principalAmount)`
- Initiates withdrawal process from Exocore
- Sends cross-chain message to Exocore
- Awaits response to unlock balance in Vault
- Does not transfer tokens to user
- Requires relay fee in ETH for cross-chain message

- `withdrawPrincipal(address token, uint256 amount, address recipient)`
- Transfers unlocked tokens from Vault to recipient
- Must be called after successful `claimPrincipalFromExocore`
- No cross-chain message required
- Direct transfer from Vault
- No relay fee needed

## NST Restaking Functions

### Setup Operations
- `createExoCapsule()`
- Creates Capsule contract for staker
- Used as withdrawal credentials
- Required before staking to beacon chain
- Returns capsule address
- No relay fee needed

- `stake(bytes pubkey, bytes signature, bytes32 depositDataRoot)`
- Stakes ETH to Ethereum beacon chain
- Creates validator with ExoCapsule as withdrawal credentials
- Preparation step for NST restaking
- Payable function accepting exactly 32 ETH for beacon chain staking
- No relay fee needed

### Deposit Operations
- `verifyAndDepositNativeStake(bytes32[] validatorContainer, BeaconChainProofs.ValidatorContainerProof proof)`
- Verifies beacon chain proof of withdrawal credentials
- Confirms validator is using correct ExoCapsule
- Sends message to Exocore to account for validator balance
- Required for NST restaking activation
- Requires relay fee in ETH for cross-chain message

### Withdrawal Operations
- `processBeaconChainWithdrawal(bytes32[] validatorContainer, BeaconChainProofs.ValidatorContainerProof validatorProof, bytes32[] withdrawalContainer, BeaconChainProofs.WithdrawalProof withdrawalProof)`
- Processes beacon chain withdrawals
- Verifies withdrawal proofs
- Sends message to Exocore to unlock ETH in Capsule
- Similar to `claimPrincipalFromExocore` for LSTs
- Requires relay fee in ETH for cross-chain message

- `withdrawNonBeaconChainETHFromCapsule(address payable recipient, uint256 amountToWithdraw)`
- Withdraws non-beacon chain ETH from Capsule
- For ETH not related to beacon chain staking
- Direct transfer from Capsule
- No relay fee needed

## Common Workflows

### LST Deposit and Delegate
1. Call `deposit` or `depositThenDelegateTo`
2. If using separate calls, wait for deposit confirmation
3. Call `delegateTo` if needed
4. Tokens are now staked and delegated on Exocore

### LST Withdrawal
1. Call `claimPrincipalFromExocore`
2. Wait for cross-chain message confirmation
3. Call `withdrawPrincipal` to receive tokens

### NST Restaking
1. Call `createExoCapsule`
2. Call `stake` to become validator
3. Call `verifyAndDepositNativeStake` with proofs
4. ETH is now restaked on Exocore

### NST Withdrawal
1. Initiate withdrawal on beacon chain
2. Call `processBeaconChainWithdrawal` with proofs
3. ETH is unlocked in Capsule
4. Use `withdrawNonBeaconChainETHFromCapsule` if needed

## Notes
- All cross-chain operations require LayerZero fees in ETH
- `stake` function requires exactly 32 ETH for beacon chain staking
- Other payable functions only accept ETH for relay fees
- NST operations require valid beacon chain proofs
- Delegation requires registered operators on Exocore
153 changes: 153 additions & 0 deletions docs/btc-staking-e2e-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# BTC Staking E2E Testing Guide

This guide walks through setting up and testing the BTC staking functionality in a local development environment.

## Prerequisites

- Docker and Docker Compose
- Node.js (v16+)
- npm or yarn
- Git

## Setup Steps

### Start Bitcoin Regtest Node

1. First, clone and set up the Esplora explorer:

```bash
git clone https://github.com/ExocoreNetwork/esplora.git
cd esplora
```

2. Start the Bitcoin regtest node, esplora, send test token to faucet and activate auto mining by running script:

```bash
./start-regtest.sh -h
Start a Bitcoin regtest node with automatic mining and fund a predefined test faucet

Usage: ./start-regtest.sh [amount_btc] [mining_interval_seconds]

Arguments:
amount_btc Amount of BTC to send to faucet (default: 100)
mining_interval_seconds Block mining interval in seconds (default: 30)

Faucet Information(only for regtest):
Private Key: 0xee01cfc3f08cdb020064f31ff1a993aa9ecc1d38e684665742faa705685532a6
Address: bcrt1qvj7e5av2eqrhhvle56f9aqtjpxgywwnt5tem5y

Example:
./start-regtest.sh 50 60
```

NOTICE: Some amount of test BTC would be sent to the faucet address with known private key

### Start Exocore Node

1. Set up and start the Exocore node:

```bash
# Clone the repository
git clone https://github.com/ExocoreNetwork/exocore.git
cd exocore

# Switch to develop branch
git checkout develop

# Start local node
./local_node.sh
```

### Deploy UTXO Gateway Contract

1. Clone repo and set up the UTXO gateway contract:

```bash
git clone https://github.com/ExocoreNetwork/exocore-contracts.git
cd exocore-contracts
npm run deploy:utxogateway
```

Before running the deployment command, please replace the URL path of `exocore_localnet` in `hardhat.config.js` with the URL path of your set up Exocore node, and also set the private key of the Exocore account in `hardhat.config.js` in your local `.env` file. We need at least 3 accounts (`deployer` == `faucet`, `owner` and `witness1`) to execute the deployment script.

```javascript
exocore_localnet: {
url: "http://127.0.0.1:8545",
chainId: 232,
accounts: [
process.env.LOCAL_EXOCORE_FUNDED_ACCOUNT_PRIVATE_KEY, // Deployer/Faucet: Requires minimum 3 eth balance
process.env.TEST_ACCOUNT_ONE_PRIVATE_KEY, // Owner: the owner of the UTXOGateway contract
process.env.TEST_ACCOUNT_TWO_PRIVATE_KEY, // Witness1: the only witness for current implementation, also needed by bridge
process.env.TEST_ACCOUNT_THREE_PRIVATE_KEY,
process.env.TEST_ACCOUNT_FOUR_PRIVATE_KEY,
process.env.TEST_ACCOUNT_FIVE_PRIVATE_KEY,
process.env.TEST_ACCOUNT_SIX_PRIVATE_KEY,
]
}
```

This would deploy UTXOGateway contract on exocore node and setup it:

- set deployed contract as authorized gateway
- set required proofs count
- set authorized witness and transfer tokens to it as fee
- activate BTC staking by registering Bitcoin chain and token

The final output would be stored under `script/deployments/utxogateway.json`

### Start UTXO Bridge

1. Clone the UTXO bridge service:

```bash
# Clone the repository
git clone https://github.com/ExocoreNetwork/utxo-restaking.git
cd utxo-restaking
git checkout btc-restaking-v2
```

2. Configure the bridge service with address of deployed UTXOGateway contract, witness address and its private key and other required parameters:

```bash
# Copy .env.example to .env and set the required variables
cp .env.example .env
```

```env
BITCOIN_RPC // URL of the esplora API
VAULT_ADDRESS // Address of the BTC vault
MIN_CONFIRMATIONS // Minimum number of confirmations for a Bitcoin transaction
EXOCORE_RPC // URL of the Exocore node
CONTRACT_ADDRESS // Address of the UTXOGateway contract
WITNESS_ADDRESS // Address of the witness
WALLET_PRIV_KEY // Private key of the signing wallet, should be the same as the witness for current implementation
```

NOTICE: We'd better set `MIN_CONFIRMATIONS` to 1 to avoid timeouts

3. Start the bridge service:

```bash
# Start bridge services
docker-compose up
```

### Run E2E Tests

1. Go back to the exocore-contracts directory and run the BTC staking E2E test:

```bash
npx hardhat test test/integration/btc-staking-e2e.test.js
```

Before running the test, please configure the test environment variables in your local `.env` file with required parameters like the secret key of BTC faucet and others.

```env
BITCOIN_FAUCET_PRIVATE_KEY // Private key of the BTC faucet
BITCOIN_ESPLORA_API_URL // URL of the esplora API
BITCOIN_STAKER_PRIVATE_KEY // Private key of the staker
BITCOIN_TX_FEE // Transaction fee for BTC deposit
DUST_THRESHOLD // Dust threshold for BTC deposit
```

This test would simulate the process of building a valid Bitcoin deposit transaction, broadcasting it to the Bitcoin network and waiting for it to be confirmed, and finally checking staker's balance on Exocore.
17 changes: 11 additions & 6 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.19",
version: "0.8.28",
settings: {
optimizer: {
enabled: true,
Expand All @@ -17,12 +17,17 @@ module.exports = {
networks: {
hardhat: {
},
exocore_testnet: {
chainId: 9000,
url: "http://23.162.56.84:8545",
exocore_localnet: {
url: "http://127.0.0.1:8545",
chainId: 232,
accounts: [
// process.env.EXOCORE_DEPLOYER_PRIVATE_KEY,
// process.env.EXOCORE_VALIDATOR_SET_PRIVATE_KEY
process.env.LOCAL_EXOCORE_FUNDED_ACCOUNT_PRIVATE_KEY,
process.env.TEST_ACCOUNT_ONE_PRIVATE_KEY,
process.env.TEST_ACCOUNT_TWO_PRIVATE_KEY,
process.env.TEST_ACCOUNT_THREE_PRIVATE_KEY,
process.env.TEST_ACCOUNT_FOUR_PRIVATE_KEY,
process.env.TEST_ACCOUNT_FIVE_PRIVATE_KEY,
process.env.TEST_ACCOUNT_SIX_PRIVATE_KEY,
]
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/create3-factory
Submodule create3-factory added at 6b834a
Loading

0 comments on commit 992a94a

Please sign in to comment.