Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions .github/workflows/deploy-testnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
name: Deploy All Contracts to Testnet

on:
push:
branches:
- dev
- staging
pull_request:
branches:
- dev
- staging

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Build contracts
run: forge build

- name: Run tests (with fork)
if: ${{ secrets.TESTNET_RPC_URL != '' }}
run: forge test --fork-url ${{ secrets.TESTNET_RPC_URL }} -vvvv

- name: Run tests (without fork)
if: ${{ secrets.TESTNET_RPC_URL == '' }}
run: forge test -vvvv

- name: Initialize deployment tracking
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
run: |
echo '{"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'", "network": "testnet", "contracts": {}}' > deployment.json

- name: Deploy ButteredBread
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
env:
TESTNET_RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
TESTNET_PRIVATE_KEY: ${{ secrets.TESTNET_PRIVATE_KEY }}
run: |
forge script script/deploy/DeployButteredBread.s.sol:DeployButteredBread --rpc-url "$TESTNET_RPC_URL" --broadcast --private-key "$TESTNET_PRIVATE_KEY" --json > butteredbread_deploy.json
ADDRESS=$(jq -r '.transactions[0].contractAddress // empty' butteredbread_deploy.json)
if [ -n "$ADDRESS" ] && [ "$ADDRESS" != "null" ]; then
jq --arg addr "$ADDRESS" '.contracts.ButteredBread = $addr' deployment.json > temp.json && mv temp.json deployment.json
echo "ButteredBread deployed to: $ADDRESS"
fi

- name: Deploy YieldDistributor
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
env:
TESTNET_RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
TESTNET_PRIVATE_KEY: ${{ secrets.TESTNET_PRIVATE_KEY }}
run: |
forge script script/deploy/DeployYieldDistributor.s.sol:DeployYieldDistributor --rpc-url "$TESTNET_RPC_URL" --broadcast --private-key "$TESTNET_PRIVATE_KEY" --json > yielddistributor_deploy.json
ADDRESS=$(jq -r '.transactions[0].contractAddress // empty' yielddistributor_deploy.json)
if [ -n "$ADDRESS" ] && [ "$ADDRESS" != "null" ]; then
jq --arg addr "$ADDRESS" '.contracts.YieldDistributor = $addr' deployment.json > temp.json && mv temp.json deployment.json
echo "YieldDistributor deployed to: $ADDRESS"
fi

- name: Deploy NFTMultiplier
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
env:
TESTNET_RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
TESTNET_PRIVATE_KEY: ${{ secrets.TESTNET_PRIVATE_KEY }}
run: |
forge script script/deploy/DeployNFTMultiplier.s.sol:DeployNFTMultiplier --rpc-url "$TESTNET_RPC_URL" --broadcast --private-key "$TESTNET_PRIVATE_KEY" --json > nftmultiplier_deploy.json
ADDRESS=$(jq -r '.transactions[0].contractAddress // empty' nftmultiplier_deploy.json)
if [ -n "$ADDRESS" ] && [ "$ADDRESS" != "null" ]; then
jq --arg addr "$ADDRESS" '.contracts.NFTMultiplier = $addr' deployment.json > temp.json && mv temp.json deployment.json
echo "NFTMultiplier deployed to: $ADDRESS"
fi

- name: Deploy VotingStreakMultiplier
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
env:
TESTNET_RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
TESTNET_PRIVATE_KEY: ${{ secrets.TESTNET_PRIVATE_KEY }}
run: |
forge script script/deploy/DeployVotingStreakMultiplier.s.sol:DeployVotingStreakMultiplier --rpc-url "$TESTNET_RPC_URL" --broadcast --private-key "$TESTNET_PRIVATE_KEY" --json > votingstreakMultiplier_deploy.json
ADDRESS=$(jq -r '.transactions[0].contractAddress // empty' votingstreakMultiplier_deploy.json)
if [ -n "$ADDRESS" ] && [ "$ADDRESS" != "null" ]; then
jq --arg addr "$ADDRESS" '.contracts.VotingStreakMultiplier = $addr' deployment.json > temp.json && mv temp.json deployment.json
echo "VotingStreakMultiplier deployed to: $ADDRESS"
fi

- name: Verify contracts on block explorer
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.ETHERSCAN_API_KEY != '' }}
env:
TESTNET_RPC_URL: ${{ secrets.TESTNET_RPC_URL }}
ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
run: |
echo "Starting contract verification..."
VERIFICATION_FAILED=false

# Verify ButteredBread
BUTTEREDBREAD_ADDR=$(jq -r '.contracts.ButteredBread // empty' deployment.json)
if [ -n "$BUTTEREDBREAD_ADDR" ] && [ "$BUTTEREDBREAD_ADDR" != "null" ]; then
echo "Verifying ButteredBread at $BUTTEREDBREAD_ADDR..."
forge verify-contract "$BUTTEREDBREAD_ADDR" src/ButteredBread.sol:ButteredBread --rpc-url "$TESTNET_RPC_URL" --etherscan-api-key "$ETHERSCAN_API_KEY" || VERIFICATION_FAILED=true
fi

# Verify YieldDistributor
YIELDDISTRIBUTOR_ADDR=$(jq -r '.contracts.YieldDistributor // empty' deployment.json)
if [ -n "$YIELDDISTRIBUTOR_ADDR" ] && [ "$YIELDDISTRIBUTOR_ADDR" != "null" ]; then
echo "Verifying YieldDistributor at $YIELDDISTRIBUTOR_ADDR..."
forge verify-contract "$YIELDDISTRIBUTOR_ADDR" src/YieldDistributor.sol:YieldDistributor --rpc-url "$TESTNET_RPC_URL" --etherscan-api-key "$ETHERSCAN_API_KEY" || VERIFICATION_FAILED=true
fi

# Verify NFTMultiplier
NFTMULTIPLIER_ADDR=$(jq -r '.contracts.NFTMultiplier // empty' deployment.json)
if [ -n "$NFTMULTIPLIER_ADDR" ] && [ "$NFTMULTIPLIER_ADDR" != "null" ]; then
echo "Verifying NFTMultiplier at $NFTMULTIPLIER_ADDR..."
forge verify-contract "$NFTMULTIPLIER_ADDR" src/NFTMultiplier.sol:NFTMultiplier --rpc-url "$TESTNET_RPC_URL" --etherscan-api-key "$ETHERSCAN_API_KEY" || VERIFICATION_FAILED=true
fi

# Verify VotingStreakMultiplier
VOTINGSTREAKMULTIPLIER_ADDR=$(jq -r '.contracts.VotingStreakMultiplier // empty' deployment.json)
if [ -n "$VOTINGSTREAKMULTIPLIER_ADDR" ] && [ "$VOTINGSTREAKMULTIPLIER_ADDR" != "null" ]; then
echo "Verifying VotingStreakMultiplier at $VOTINGSTREAKMULTIPLIER_ADDR..."
forge verify-contract "$VOTINGSTREAKMULTIPLIER_ADDR" src/VotingStreakMultiplier.sol:VotingStreakMultiplier --rpc-url "$TESTNET_RPC_URL" --etherscan-api-key "$ETHERSCAN_API_KEY" || VERIFICATION_FAILED=true
fi

# Update deployment.json with verification status
if [ "$VERIFICATION_FAILED" = "true" ]; then
jq '.verification_status = "partial_failure"' deployment.json > temp.json && mv temp.json deployment.json
echo "⚠️ Some contract verifications failed. Check logs above for details."
else
jq '.verification_status = "success"' deployment.json > temp.json && mv temp.json deployment.json
echo "✅ All contracts verified successfully!"
fi

- name: Upload deployment artifacts
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
uses: actions/upload-artifact@v4
with:
name: deployment-artifacts
path: |
deployment.json
*_deploy.json
retention-days: 30

- name: Display deployment summary
if: ${{ secrets.TESTNET_RPC_URL != '' && secrets.TESTNET_PRIVATE_KEY != '' }}
run: |
echo "📋 Deployment Summary"
echo "===================="
cat deployment.json | jq '.'
echo ""
echo "🔗 Contract Addresses:"
jq -r '.contracts | to_entries[] | "- \(.key): \(.value)"' deployment.json
115 changes: 115 additions & 0 deletions TESTNET_DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Testnet Deployment Setup

This repository includes automated testnet deployment via GitHub Actions. The workflow automatically deploys all smart contracts to testnet when code is pushed to the `dev` or `staging` branches.

## Required GitHub Secrets

Before the deployment workflow can run successfully, you must configure the following secrets in your GitHub repository settings:

### 1. TESTNET_RPC_URL
- **Description**: The RPC endpoint URL for your target testnet
- **Example**: `https://rpc.gnosischain.com` (for Gnosis Chain testnet)
- **Format**: Full HTTPS URL including protocol

### 2. TESTNET_PRIVATE_KEY
- **Description**: Private key for the wallet that will deploy the contracts
- **Format**: 64-character hexadecimal string (with or without `0x` prefix)
- **Security**: This wallet should be used ONLY for testnet deployments and contain only testnet tokens

### 3. ETHERSCAN_API_KEY
- **Description**: API key for contract verification on block explorer (e.g., Etherscan, Gnosisscan)
- **Format**: Alphanumeric string provided by the block explorer service
- **Purpose**: Enables automatic contract source code verification after deployment
- **Optional**: Verification will be skipped if this secret is not provided

## Setting Up GitHub Secrets

1. Navigate to your GitHub repository
2. Go to **Settings** > **Secrets and variables** > **Actions**
3. Click **New repository secret**
4. Add each secret with the exact names listed above

## Deployed Contracts

The workflow deploys the following contracts in sequence:

1. **ButteredBread** - `script/deploy/DeployButteredBread.s.sol`
2. **YieldDistributor** - `script/deploy/DeployYieldDistributor.s.sol`
3. **NFTMultiplier** - `script/deploy/DeployNFTMultiplier.s.sol`
4. **VotingStreakMultiplier** - `script/deploy/DeployVotingStreakMultiplier.s.sol`

## Workflow Triggers

The deployment workflow runs automatically on:
- Push to `dev` branch (production testnet deployment)
- Push to `staging` branch (testing/validation deployment)

## Workflow Steps

1. **Build**: Compiles all contracts using `forge build`
2. **Test**: Runs tests with fork testing using the testnet RPC
3. **Deploy**: Executes each deployment script in sequence, capturing contract addresses
4. **Verify**: Attempts to verify each deployed contract on the block explorer
5. **Artifact Generation**: Creates `deployment.json` with all contract addresses and verification status
6. **Logging**: Contract addresses and transaction hashes are logged in the workflow output

## Deployment Artifacts

After each deployment, the workflow generates several artifacts:

### deployment.json
Contains the complete deployment information:
```json
{
"timestamp": "2024-01-01T12:00:00Z",
"network": "testnet",
"contracts": {
"ButteredBread": "0x1234...",
"YieldDistributor": "0x5678...",
"NFTMultiplier": "0x9abc...",
"VotingStreakMultiplier": "0xdef0..."
},
"verification_status": "success"
}
```

### Individual deployment files
- `butteredbread_deploy.json`
- `yielddistributor_deploy.json`
- `nftmultiplier_deploy.json`
- `votingstreakMultiplier_deploy.json`

These artifacts are automatically uploaded and retained for 30 days.

## Monitoring Deployments

- View deployment logs in the **Actions** tab of your GitHub repository
- Each deployment step will show the deployed contract addresses
- Failed deployments will stop the workflow and show error details

## Security Considerations

- Never use mainnet private keys for testnet deployments
- Testnet private keys should be separate from any production keys
- Monitor the testnet wallet balance to ensure sufficient gas funds
- Regularly rotate testnet private keys for security

## Troubleshooting

### Common Issues

1. **Insufficient Gas**: Ensure the deployment wallet has enough testnet tokens
2. **RPC Errors**: Verify the TESTNET_RPC_URL is correct and accessible
3. **Private Key Format**: Ensure the private key is properly formatted (64 hex characters)
4. **Contract Dependencies**: Some contracts may depend on others being deployed first
5. **Verification Failures**:
- Check that ETHERSCAN_API_KEY is valid and has sufficient rate limits
- Ensure the block explorer supports the target network
- Verification may fail if contracts are not yet indexed (try again later)
6. **Missing Contract Addresses**: If deployment.json shows null addresses, check individual deployment logs for errors

### Getting Help

- Check the GitHub Actions logs for detailed error messages
- Verify all secrets are properly set in repository settings
- Ensure the target testnet is operational and accessible