Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a8bfb15
feat: Add Safe multisig integration and bridge pause/unpause workflow
gfournierPro Nov 4, 2025
8202b6d
feat: Enhance bridge pause/unpause workflow with dry run mode and tra…
gfournierPro Nov 4, 2025
04ae516
fix: Correct path to bridge address in pause/unpause workflow
gfournierPro Nov 4, 2025
4c7715e
fix: Update default dry run mode to true and correct RPC URL reference
gfournierPro Nov 4, 2025
93f1fe6
feat: Add role management workflow for TOKEN_BRIDGE_ROLE in Safe mult…
gfournierPro Nov 4, 2025
8c3dd28
feat: Add workflow for transferring admin role via Safe multisig
gfournierPro Nov 4, 2025
c2c2c55
refactor: Clean up import statements and improve code formatting acro…
gfournierPro Nov 4, 2025
4e977b5
fix: error to trigger
gfournierPro Nov 4, 2025
604cadf
fix: Correct RPC URL reference in role management workflow
gfournierPro Nov 4, 2025
1a038c3
fix: Add missing fi statement for revokeRole transaction data prepara…
gfournierPro Nov 4, 2025
acd2c81
refactor: Enhance transaction details logging format in bridge pause …
gfournierPro Nov 4, 2025
055a2e1
fix: add error to trigger
gfournierPro Nov 4, 2025
fb4a89b
fix: Correct reference to RPC URL in propose-bridge-to-safe job
gfournierPro Nov 4, 2025
ca0b55c
refactor: Simplify initialization data encoding in deploy function
gfournierPro Nov 4, 2025
7a1756a
fix: Add missing fi statement to complete transaction creation for br…
gfournierPro Nov 4, 2025
5e04c92
fix: Use compact JSON output for transaction creation in admin transf…
gfournierPro Nov 4, 2025
7a55eb4
style: Improve code formatting and readability across multiple files
gfournierPro Nov 4, 2025
3c0f1be
style: Refactor code for improved readability and formatting across m…
gfournierPro Nov 4, 2025
4a62de4
Merge branch 'fix/formating' into feat/add-gha-multisig
gfournierPro Nov 4, 2025
80e472e
style: Improve code formatting and readability across multiple files
gfournierPro Nov 4, 2025
773ba33
Merge branch 'main' into feat/add-gha-multisig
gfournierPro Nov 5, 2025
d8e72cb
style: Update workflow to manage contract roles via Safe multisig wit…
gfournierPro Nov 5, 2025
4451f86
feat: Add workflow for managing contract roles via Safe multisig with…
gfournierPro Nov 5, 2025
e2d1d4f
fix: Apply suggestion from @Copilot
gfournierPro Nov 5, 2025
88221d1
Merge branch 'main' into feat/add-gha-multisig
gfournierPro Nov 7, 2025
3d691ce
refactor: Rename job steps to standardize transaction calldata prepar…
gfournierPro Nov 7, 2025
beca9f7
refactor: Remove transaction value from multisig transaction preparat…
gfournierPro Nov 7, 2025
f0b098a
refactor: Rename propose-to-safe jobs to propose-to-safe-tx for consi…
gfournierPro Nov 7, 2025
3c3f6dc
refactor: Replace if-else with case statement for operation handling …
gfournierPro Nov 7, 2025
e37f2cb
refactor: Rename propose-token-contract-to-safe job to propose-token-…
gfournierPro Nov 7, 2025
2f36cee
refactor: Consolidate propose-to-safe jobs and implement matrix strat…
gfournierPro Nov 7, 2025
af37061
refactor: Remove dry_run condition from propose-to-safe-tx jobs and p…
gfournierPro Nov 13, 2025
4c8c2dc
refactor: Standardize dry-run input naming across workflow files
gfournierPro Nov 13, 2025
20fb6ea
refactor: Update safe address reference from secrets to vars in trans…
gfournierPro Nov 13, 2025
92c5aae
refactor: Add rpc-url output to prepare-transaction-calldata job and …
gfournierPro Nov 13, 2025
d6ad734
refactor: Add safe-proposer-private-key and safe-api-key outputs to p…
gfournierPro Nov 13, 2025
0c4826e
refactor: Update outputs in bridge-pause-safe workflow to use secrets…
gfournierPro Nov 13, 2025
7720b82
refactor: Update safe address reference from outputs to vars in propo…
gfournierPro Nov 13, 2025
3a10076
refactor: Update safe address reference in propose-to-safe-tx job to …
gfournierPro Nov 18, 2025
250ba30
refactor: Update propose-to-safe-tx job to use secrets for RPC and AP…
gfournierPro Nov 18, 2025
4902dc3
refactor: Update propose-to-safe-tx job to inherit secrets for improv…
gfournierPro Nov 18, 2025
a36165e
refactor: Enhance propose-to-safe-tx job to inherit environment secre…
gfournierPro Nov 18, 2025
03db832
refactor: Update propose-to-safe-tx job to bridge environment secrets…
gfournierPro Nov 18, 2025
ec71340
refactor: Update Node.js version in bridge-pause-safe workflow from 2…
gfournierPro Nov 18, 2025
e0bcbea
refactor: Update checkout reference in bridge-pause-safe workflow fro…
gfournierPro Nov 18, 2025
05ea315
refactor: Update checkout step in bridge-pause-safe workflow to use s…
gfournierPro Nov 18, 2025
d118bbc
refactor: Simplify propose-to-safe-tx job by using reusable workflow …
gfournierPro Nov 18, 2025
a855f66
refactor: Update propose-to-safe-tx job to use vars for safe address …
gfournierPro Nov 18, 2025
20f75c7
refactor: Remove transfer-admin-role-safe workflow as it is no longer…
gfournierPro Dec 10, 2025
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
117 changes: 117 additions & 0 deletions .github/workflows/bridge-pause-safe.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Bridge Pause/Unpause via Safe Multisig

on:
workflow_dispatch:
inputs:
operation:
description: 'Pause operation to perform'
required: true
type: choice
options:
- pause-bridge
- unpause-bridge
- pause-outbound
- unpause-outbound
network:
description: 'Network to perform operation on'
required: true
type: choice
options:
- ethereum
- arbitrum
- sepolia
- arbitrum_sepolia
default: sepolia
dry-run:
description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)'
required: false
type: boolean
default: true

jobs:
prepare-transaction-calldata:
runs-on: ubuntu-latest
environment: ${{ inputs.network }}
outputs:
transaction-data: ${{ steps.prepare.outputs.transaction-data }}
safe-address: ${{ steps.prepare.outputs.safe-address }}
bridge-address: ${{ steps.prepare.outputs.bridge-address }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: stable
cache: true

- name: Prepare transaction calldata
id: prepare
env:
CHAIN: ${{ inputs.network }}
run: |
# Get bridge address from config
BRIDGE_ADDRESS=$(jq -r ".chains.${CHAIN}.iexecLayerZeroBridgeAddress" config/config.json)
echo "bridge-address=$BRIDGE_ADDRESS" >> $GITHUB_OUTPUT

# Determine the function selector and name based on operation
case "${{ inputs.operation }}" in
"pause-bridge")
TRANSACTION_DATA=$(cast calldata "pause()")
FUNCTION_NAME="pause()"
;;
"unpause-bridge")
TRANSACTION_DATA=$(cast calldata "unpause()")
FUNCTION_NAME="unpause()"
;;
"pause-outbound")
TRANSACTION_DATA=$(cast calldata "pauseOutboundTransfers()")
FUNCTION_NAME="pauseOutboundTransfers()"
;;
"unpause-outbound")
TRANSACTION_DATA=$(cast calldata "unpauseOutboundTransfers()")
FUNCTION_NAME="unpauseOutboundTransfers()"
;;
esac

echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT
echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT

# Display transaction details
echo "=========================================="
echo "Transaction Details"
echo "=========================================="
echo "Workflow Configuration:"
echo " • Network: ${{ inputs.network }}"
echo " • Operation: ${{ inputs.operation }}"
echo " • Function: $FUNCTION_NAME"
echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}"
echo " • Dry Run: ${{ inputs.dry-run }}"
echo ""
echo "Transaction Details:"
echo " • Target: $BRIDGE_ADDRESS"
echo " • Value: 0 ETH"
echo " • Data: $TRANSACTION_DATA"
echo ""

if [ "${{ inputs.dry-run }}" == "true" ]; then
echo "✅ DRY RUN MODE: Transaction prepared successfully"
fi

propose-to-safe-tx:
needs: prepare-transaction-calldata
uses: ./.github/workflows/propose-safe-transaction.yml
secrets:
rpc-url: ${{ secrets.RPC_URL }}
safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }}
safe-api-key: ${{ secrets.SAFE_API_KEY }}
with:
network: ${{ inputs.network }}
safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }}
transaction-to: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }}
transaction-data: ${{ needs.prepare-transaction-calldata.outputs.transaction-data }}
dry-run: ${{ inputs.dry-run }}
186 changes: 186 additions & 0 deletions .github/workflows/manage-contract-roles-safe.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
name: Manage Contract Roles via Safe Multisig

on:
workflow_dispatch:
inputs:
operation:
description: 'Role operation to perform'
required: true
type: choice
options:
- grant
- revoke
role:
description: 'Role to grant or revoke'
required: true
type: choice
options:
- TOKEN_BRIDGE_ROLE
- PAUSER_ROLE
- UPGRADER_ROLE
default: TOKEN_BRIDGE_ROLE
network:
description: 'Network to perform operation on'
required: true
type: choice
options:
- ethereum
- arbitrum
- sepolia
- arbitrum_sepolia
default: sepolia
target_address:
description: 'Address to grant/revoke role to/from'
required: true
type: string
dry-run:
description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)'
required: false
type: boolean
default: true

jobs:
prepare-transaction-calldata:
runs-on: ubuntu-latest
environment: ${{ inputs.network }}
outputs:
transactions: ${{ steps.prepare.outputs.transactions }}
safe-address: ${{ steps.prepare.outputs.safe-address }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: stable
cache: true

- name: Prepare transaction calldata
id: prepare
env:
CHAIN: ${{ inputs.network }}
ROLE_NAME: ${{ inputs.role }}
run: |
# Determine which contract to use based on role and network config
APPROVAL_REQUIRED=$(jq -r ".chains.${CHAIN}.approvalRequired" config/config.json)
BRIDGE_ADDRESS=$(jq -r ".chains.${CHAIN}.iexecLayerZeroBridgeAddress" config/config.json)

if [ "$APPROVAL_REQUIRED" = "true" ]; then
TOKEN_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcLiquidityUnifierAddress" config/config.json)
else
TOKEN_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcCrosschainTokenAddress" config/config.json)
fi

# Calculate role hash: keccak256("ROLE_NAME")
ROLE_HASH=$(cast keccak "$ROLE_NAME")

# Determine the function selector and encode calldata
case "${{ inputs.operation }}" in
grant)
TRANSACTION_DATA=$(cast calldata "grantRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}")
FUNCTION_NAME="grantRole(bytes32,address)"
;;
revoke)
TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}")
FUNCTION_NAME="revokeRole(bytes32,address)"
;;
*)
echo "❌ Error: Unknown operation ${{ inputs.operation }}"
exit 1
;;
esac

# Prepare transactions array based on role type
TRANSACTIONS='[]'

case "$ROLE_NAME" in
TOKEN_BRIDGE_ROLE)
# TOKEN_BRIDGE_ROLE is only on token contract (RLCLiquidityUnifier or RLCCrosschainToken)
TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \
'. += [{"to": $to, "data": $data, "contract": "token"}]')
;;
PAUSER_ROLE)
# PAUSER_ROLE is only on bridge contract
TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \
'. += [{"to": $to, "data": $data, "contract": "bridge"}]')
;;
UPGRADER_ROLE)
# UPGRADER_ROLE is on both bridge and token contracts
# Add transaction for token contract
TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \
'. += [{"to": $to, "data": $data, "contract": "token"}]')
# Add transaction for bridge contract
TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \
'. += [{"to": $to, "data": $data, "contract": "bridge"}]')
;;
*)
echo "❌ Error: Unknown role $ROLE_NAME"
'. += [{"to": $to, "data": $data, "contract": "bridge"}]')
;;
esac

echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT
echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT

# Display transaction details for dry-run or verification
Copy link
Contributor

Choose a reason for hiding this comment

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

same

echo "=========================================="
echo "Transaction Details"
echo "=========================================="
echo "Workflow Configuration:"
echo " • Network: ${{ inputs.network }}"
echo " • Role: $ROLE_NAME"
echo " • Operation: ${{ inputs.operation }}"
echo " • Function: $FUNCTION_NAME"
echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}"
echo " • Dry Run: ${{ inputs.dry-run }}"
echo ""
echo "────────────────────────────────────────────────────────────────────────────────"
echo ""

# Display each transaction
TX_COUNT=$(echo $TRANSACTIONS | jq 'length')
for i in $(seq 0 $(($TX_COUNT - 1))); do
TX=$(echo $TRANSACTIONS | jq -r ".[$i]")
TX_TO=$(echo $TX | jq -r '.to')
TX_DATA=$(echo $TX | jq -r '.data')
TX_CONTRACT=$(echo $TX | jq -r '.contract')

echo "Transaction #$((i + 1)) - ${TX_CONTRACT^} Contract:"
echo " • Target: $TX_TO"
echo " • Address: ${{ inputs.target_address }}"
echo " • Role Name: $ROLE_NAME"
echo " • Role Hash: $ROLE_HASH"
echo " • Value: 0 ETH"
echo " • Data: $TX_DATA"
echo ""
done

echo "────────────────────────────────────────────────────────────────────────────────"
echo ""

if [ "${{ inputs.dry-run }}" == "true" ]; then
echo "✅ DRY RUN MODE: Transaction(s) prepared successfully"
echo "ℹ️ These transaction(s) would be proposed to Safe multisig"
echo "ℹ️ Re-run with dry-run=false to actually propose to Safe"
fi

propose-to-safe-tx:
needs: prepare-transaction-calldata
strategy:
matrix:
transaction: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions) }}
uses: ./.github/workflows/propose-safe-transaction.yml
secrets:
rpc-url: ${{ secrets.RPC_URL }}
safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }}
safe-api-key: ${{ secrets.SAFE_API_KEY }}
with:
network: ${{ inputs.network }}
safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }}
transaction-to: ${{ matrix.transaction.to }}
transaction-data: ${{ matrix.transaction.data }}
dry-run: ${{ inputs.dry-run }}
78 changes: 78 additions & 0 deletions .github/workflows/propose-safe-transaction.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Propose Safe Transaction (Reusable)

on:
workflow_call:
inputs:
network:
description: 'Network environment'
required: true
type: string
safe-address:
description: 'Safe multisig address'
required: true
type: string
transaction-to:
description: 'Transaction target address'
required: true
type: string
transaction-data:
description: 'Transaction calldata'
required: true
type: string
transaction-value:
description: 'Transaction value in wei'
required: false
type: string
default: "0"
dry-run:
description: 'Dry run mode'
required: false
type: boolean
default: true
secrets:
rpc-url:
description: 'RPC URL for the network'
required: true
safe-proposer-private-key:
description: 'Private key of the Safe proposer'
required: true
safe-api-key:
description: 'Safe API key'
required: true

jobs:
propose-transaction:
runs-on: ubuntu-latest
environment: ${{ inputs.network }}
steps:
- name: Checkout Safe proposal repository
uses: actions/checkout@v4
with:
repository: iExecBlockchainComputing/github-actions-workflows
path: .github-actions

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'

- name: Install dependencies
working-directory: .github-actions/propose-safe-multisig-tx
run: npm ci

- name: Build the action
working-directory: .github-actions/propose-safe-multisig-tx
run: npm run build

- name: Propose transaction to Safe
working-directory: .github-actions/propose-safe-multisig-tx
env:
SAFE_ADDRESS: ${{ inputs.safe-address }}
TRANSACTION_TO: ${{ inputs.transaction-to }}
TRANSACTION_VALUE: ${{ inputs.transaction-value }}
TRANSACTION_DATA: ${{ inputs.transaction-data }}
RPC_URL: ${{ secrets.rpc-url }}
SAFE_PROPOSER_PRIVATE_KEY: ${{ secrets.safe-proposer-private-key }}
SAFE_API_KEY: ${{ secrets.safe-api-key }}
DRY_RUN: ${{ inputs.dry-run }}
run: npm run propose
Loading
Loading