From a8bfb150b0a40163755fa641c7c678d2868ee83b Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 15:03:41 +0100 Subject: [PATCH 01/46] feat: Add Safe multisig integration and bridge pause/unpause workflow --- .github/workflows/bridge-pause-safe.yml | 90 +++++++++++++++++++++++++ README.md | 14 ++++ 2 files changed, 104 insertions(+) create mode 100644 .github/workflows/bridge-pause-safe.yml diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml new file mode 100644 index 00000000..748afd70 --- /dev/null +++ b/.github/workflows/bridge-pause-safe.yml @@ -0,0 +1,90 @@ +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 + +jobs: + prepare-pause-operation: + 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 pause/unpause transaction calldata + id: prepare + env: + CHAIN: ${{ inputs.network }} + run: | + # Get bridge address from config + BRIDGE_ADDRESS=$(jq -r ".${CHAIN}.iexecLayerZeroBridgeAddress" config/config.json) + echo "bridge-address=$BRIDGE_ADDRESS" >> $GITHUB_OUTPUT + + # Determine the function selector based on operation + case "${{ inputs.operation }}" in + "pause-bridge") + # pause() function selector + SELECTOR="0x8456cb59" + ;; + "unpause-bridge") + # unpause() function selector + SELECTOR="0x3f4ba83a" + ;; + "pause-outbound") + # pauseOutboundTransfers() function selector + SELECTOR="0x47e7ef24" + ;; + "unpause-outbound") + # unpauseOutboundTransfers() function selector + SELECTOR="0x63ba0d00" + ;; + esac + + echo "transaction-data=$SELECTOR" >> $GITHUB_OUTPUT + echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + + propose-to-safe: + needs: prepare-pause-operation + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + secrets: + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} + with: + rpc-url: ${{ secrets.RPC_URL }} + safe-address: ${{ needs.prepare-pause-operation.outputs.safe-address }} + transaction-to: ${{ needs.prepare-pause-operation.outputs.bridge-address }} + transaction-value: '0' + transaction-data: ${{ needs.prepare-pause-operation.outputs.transaction-data }} diff --git a/README.md b/README.md index 898a39e6..9a99df1d 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,20 @@ The scripts automatically calculate these fees and include them in the transacti Note that production GitHub environments `arbitrum` and `ethereum` can only be used with the `main` branch. +## Safe Multisig Integration + +All critical administrative operations are secured using Safe (Gnosis Safe) multisig wallets. This ensures that important actions like contract upgrades, role management, and pause operations require approval from multiple authorized signers. + +### Supported Operations + +- **Pause/Unpause**: Control bridge operations with different pause levels + +### GitHub Actions Workflows + +- `.github/workflows/bridge-pause-safe.yml` - Propose pause/unpause transactions + +All workflows use the reusable Safe multisig workflow from [iExecBlockchainComputing/github-actions-workflows](https://github.com/iExecBlockchainComputing/github-actions-workflows). + ## TODO - Use an enterprise RPC URL for `secrets.SEPOLIA_RPC_URL` in Github environment `ci`. From 8202b6d3e14a628bf58ce27f50a54b8b4cf04a42 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 15:16:15 +0100 Subject: [PATCH 02/46] feat: Enhance bridge pause/unpause workflow with dry run mode and transaction details display --- .github/workflows/bridge-pause-safe.yml | 42 +++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 748afd70..586d09f7 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -22,6 +22,11 @@ on: - 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: false jobs: prepare-pause-operation: @@ -53,31 +58,56 @@ jobs: BRIDGE_ADDRESS=$(jq -r ".${CHAIN}.iexecLayerZeroBridgeAddress" config/config.json) echo "bridge-address=$BRIDGE_ADDRESS" >> $GITHUB_OUTPUT - # Determine the function selector based on operation + # Determine the function selector and name based on operation case "${{ inputs.operation }}" in "pause-bridge") # pause() function selector - SELECTOR="0x8456cb59" + TRANSACTION_DATA=$(cast calldata "pause()") + FUNCTION_NAME="pause()" ;; "unpause-bridge") # unpause() function selector - SELECTOR="0x3f4ba83a" + TRANSACTION_DATA=$(cast calldata "unpause()") + FUNCTION_NAME="unpause()" ;; "pause-outbound") # pauseOutboundTransfers() function selector - SELECTOR="0x47e7ef24" + TRANSACTION_DATA=$(cast calldata "pauseOutboundTransfers()") + FUNCTION_NAME="pauseOutboundTransfers()" ;; "unpause-outbound") # unpauseOutboundTransfers() function selector - SELECTOR="0x63ba0d00" + TRANSACTION_DATA=$(cast calldata "unpauseOutboundTransfers()") + FUNCTION_NAME="unpauseOutboundTransfers()" ;; esac - echo "transaction-data=$SELECTOR" >> $GITHUB_OUTPUT + echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + + # Display transaction details for dry-run or verification + echo "==========================================" + echo "Transaction Details" + echo "==========================================" + echo "Network: ${{ inputs.network }}" + echo "Operation: ${{ inputs.operation }}" + echo "Function: $FUNCTION_NAME" + echo "Target Contract: $BRIDGE_ADDRESS" + echo "Transaction Data: $TRANSACTION_DATA" + echo "Transaction Value: 0" + echo "Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo "Dry Run: ${{ inputs.dry_run }}" + echo "==========================================" + + if [ "${{ inputs.dry_run }}" == "true" ]; then + echo "✅ DRY RUN MODE: Transaction prepared successfully" + echo "ℹ️ This transaction would be proposed to Safe multisig" + echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" + fi propose-to-safe: needs: prepare-pause-operation + if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} From 04ae5163d0d2c122606192e81c7ecc0a37a74d7d Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 15:25:02 +0100 Subject: [PATCH 03/46] fix: Correct path to bridge address in pause/unpause workflow --- .github/workflows/bridge-pause-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 586d09f7..cc45347a 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -55,7 +55,7 @@ jobs: CHAIN: ${{ inputs.network }} run: | # Get bridge address from config - BRIDGE_ADDRESS=$(jq -r ".${CHAIN}.iexecLayerZeroBridgeAddress" config/config.json) + 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 From 4c7715ed69e72189ebcc13a1f3876bec174c77ee Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 15:36:58 +0100 Subject: [PATCH 04/46] fix: Update default dry run mode to true and correct RPC URL reference --- .github/workflows/bridge-pause-safe.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index cc45347a..f5dfbe00 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -26,7 +26,7 @@ on: description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)' required: false type: boolean - default: false + default: true jobs: prepare-pause-operation: @@ -113,7 +113,7 @@ jobs: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: - rpc-url: ${{ secrets.RPC_URL }} + rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-pause-operation.outputs.safe-address }} transaction-to: ${{ needs.prepare-pause-operation.outputs.bridge-address }} transaction-value: '0' From 93f1fe64b3acdd008298661936bf1210f529ef86 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 16:25:45 +0100 Subject: [PATCH 05/46] feat: Add role management workflow for TOKEN_BRIDGE_ROLE in Safe multisig --- .../manage-token-bridge-role-safe.yml | 121 ++++++++++++++++++ README.md | 2 + 2 files changed, 123 insertions(+) create mode 100644 .github/workflows/manage-token-bridge-role-safe.yml diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-token-bridge-role-safe.yml new file mode 100644 index 00000000..a3dc1849 --- /dev/null +++ b/.github/workflows/manage-token-bridge-role-safe.yml @@ -0,0 +1,121 @@ +name: Manage Token Bridge Role via Safe Multisig + +on: + workflow_dispatch: + inputs: + operation: + description: 'Role operation to perform' + required: true + type: choice + options: + - grant + - revoke + network: + description: 'Network to perform operation on' + required: true + type: choice + options: + - ethereum + - arbitrum + - sepolia + - arbitrum_sepolia + default: sepolia + bridge_address: + description: 'Bridge 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-role-operation: + runs-on: ubuntu-latest + environment: ${{ inputs.network }} + outputs: + transaction-data: ${{ steps.prepare.outputs.transaction-data }} + safe-address: ${{ steps.prepare.outputs.safe-address }} + target-contract: ${{ steps.prepare.outputs.target-contract }} + + 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 role management transaction calldata + id: prepare + env: + CHAIN: ${{ inputs.network }} + run: | + # Read config to determine which contract to use + APPROVAL_REQUIRED=$(jq -r ".chains.${CHAIN}.approvalRequired" config/config.json) + + if [ "$APPROVAL_REQUIRED" = "true" ]; then + TARGET_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcLiquidityUnifierAddress" config/config.json) + else + TARGET_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcCrosschainTokenAddress" config/config.json) + fi + + echo "target-contract=$TARGET_CONTRACT" >> $GITHUB_OUTPUT + + # Calculate TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE") + ROLE_HASH=$(cast keccak "TOKEN_BRIDGE_ROLE") + + # Determine the function selector and encode calldata + if [ "${{ inputs.operation }}" = "grant" ]; then + # grantRole(bytes32 role, address account) + TRANSACTION_DATA=$(cast calldata "grantRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") + FUNCTION_NAME="grantRole(bytes32,address)" + else + # revokeRole(bytes32 role, address account) + TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") + FUNCTION_NAME="revokeRole(bytes32,address)" + fi + + echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT + echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + + # Display transaction details for dry-run or verification + echo "==========================================" + echo "Transaction Details" + echo "==========================================" + echo "Network: ${{ inputs.network }}" + echo "Operation: ${{ inputs.operation }}" + echo "Function: $FUNCTION_NAME" + echo "Target Contract: $TARGET_CONTRACT" + echo "Bridge Address: ${{ inputs.bridge_address }}" + echo "Role Hash: $ROLE_HASH" + echo "Transaction Data: $TRANSACTION_DATA" + echo "Transaction Value: 0" + echo "Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo "Dry Run: ${{ inputs.dry_run }}" + echo "==========================================" + + if [ "${{ inputs.dry_run }}" == "true" ]; then + echo "✅ DRY RUN MODE: Transaction prepared successfully" + echo "ℹ️ This transaction would be proposed to Safe multisig" + echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" + fi + + propose-to-safe: + needs: prepare-role-operation + if: ${{ inputs.dry_run == false }} + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + secrets: + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} + with: + rpc-url: ${{ github.secrets.RPC_URL }} + safe-address: ${{ needs.prepare-role-operation.outputs.safe-address }} + transaction-to: ${{ needs.prepare-role-operation.outputs.target-contract }} + transaction-value: '0' + transaction-data: ${{ needs.prepare-role-operation.outputs.transaction-data }} diff --git a/README.md b/README.md index 9a99df1d..b8dcd8d5 100644 --- a/README.md +++ b/README.md @@ -354,10 +354,12 @@ All critical administrative operations are secured using Safe (Gnosis Safe) mult ### Supported Operations - **Pause/Unpause**: Control bridge operations with different pause levels +- **Role Management**: Grant or revoke TOKEN_BRIDGE_ROLE ### GitHub Actions Workflows - `.github/workflows/bridge-pause-safe.yml` - Propose pause/unpause transactions +- `.github/workflows/manage-token-bridge-role-safe.yml` - Propose role management transactions All workflows use the reusable Safe multisig workflow from [iExecBlockchainComputing/github-actions-workflows](https://github.com/iExecBlockchainComputing/github-actions-workflows). From 8c3dd28aacc3f173b71d35f7a1ce86722811d143 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 16:48:17 +0100 Subject: [PATCH 06/46] feat: Add workflow for transferring admin role via Safe multisig --- .../workflows/transfer-admin-role-safe.yml | 163 ++++++++++++++++++ README.md | 2 + 2 files changed, 165 insertions(+) create mode 100644 .github/workflows/transfer-admin-role-safe.yml diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml new file mode 100644 index 00000000..8addd1a0 --- /dev/null +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -0,0 +1,163 @@ +name: Transfer Admin Role via Safe Multisig + +on: + workflow_dispatch: + inputs: + operation: + description: 'Admin role operation' + required: true + type: choice + options: + - begin-transfer + - accept-transfer + network: + description: 'Network to perform operation on' + required: true + type: choice + options: + - ethereum + - arbitrum + - sepolia + - arbitrum_sepolia + default: sepolia + new_admin_address: + description: 'New admin address (required for begin-transfer)' + required: false + 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-admin-transfer: + 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: Validate inputs + if: inputs.operation == 'begin-transfer' && inputs.new_admin_address == '' + run: | + echo "Error: new_admin_address is required for begin-transfer operation" + exit 1 + + - name: Prepare admin transfer transaction calldata + id: prepare + env: + CHAIN: ${{ inputs.network }} + run: | + # Read contract addresses from 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 + + # Prepare transactions array for both contracts + TRANSACTIONS='[]' + + if [ "${{ inputs.operation }}" = "begin-transfer" ]; then + # beginDefaultAdminTransfer(address newAdmin) + TRANSACTION_DATA=$(cast calldata "beginDefaultAdminTransfer(address)" "${{ inputs.new_admin_address }}") + FUNCTION_NAME="beginDefaultAdminTransfer(address)" + + # Create transaction for token contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "value": "0", "data": $data}]') + + # Create transaction for bridge contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "value": "0", "data": $data}]') + else + # acceptDefaultAdminTransfer() + TRANSACTION_DATA=$(cast calldata "acceptDefaultAdminTransfer()") + FUNCTION_NAME="acceptDefaultAdminTransfer()" + + # Create transaction for token contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "value": "0", "data": $data}]') + + # Create transaction for bridge contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "value": "0", "data": $data}]') + + echo "transactions=$TRANSACTIONS" >> $GITHUB_OUTPUT + echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + + # Display transaction details for dry-run or verification + echo "==========================================" + echo "Transaction Details" + echo "==========================================" + echo " • Network: ${{ inputs.network }}" + echo " • Operation: ${{ inputs.operation }}" + echo " • Function: $FUNCTION_NAME" + echo " • New Admin: ${{ inputs.new_admin_address }}" + echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Dry Run: ${{ inputs.dry_run }}" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" + echo "Transaction #1 - Token Contract:" + echo " • Target: $TOKEN_CONTRACT" + echo " • Value: 0 ETH" + echo " • Data: $TRANSACTION_DATA" + echo "" + echo "Transaction #2 - Bridge Contract:" + echo " • Target: $BRIDGE_ADDRESS" + echo " • Value: 0 ETH" + echo " • Data: $TRANSACTION_DATA" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" + + if [ "${{ inputs.dry_run }}" == "true" ]; then + echo "✅ DRY RUN MODE: Transactions prepared successfully" + echo "ℹ️ These transactions would be proposed to Safe multisig" + echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" + fi + + propose-token-contract-to-safe: + needs: prepare-admin-transfer + if: ${{ inputs.dry_run == false }} + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + secrets: + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} + with: + rpc-url: ${{ github.secrets.RPC_URL }} + safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} + transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].to }} + transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].value }} + transaction-data: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].data }} + + propose-bridge-to-safe: + needs: prepare-admin-transfer + if: ${{ inputs.dry_run == false }} + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + secrets: + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} + with: + rpc-url: ${{ github.secrets.RPC_URL }} + safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} + transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].to }} + transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].value }} + transaction-data: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].data }} diff --git a/README.md b/README.md index b8dcd8d5..40a80c8a 100644 --- a/README.md +++ b/README.md @@ -355,11 +355,13 @@ All critical administrative operations are secured using Safe (Gnosis Safe) mult - **Pause/Unpause**: Control bridge operations with different pause levels - **Role Management**: Grant or revoke TOKEN_BRIDGE_ROLE +- **Admin Transfer**: Transfer admin role to new addresses ### GitHub Actions Workflows - `.github/workflows/bridge-pause-safe.yml` - Propose pause/unpause transactions - `.github/workflows/manage-token-bridge-role-safe.yml` - Propose role management transactions +- `.github/workflows/transfer-admin-role-safe.yml` - Propose admin role transfer transactions All workflows use the reusable Safe multisig workflow from [iExecBlockchainComputing/github-actions-workflows](https://github.com/iExecBlockchainComputing/github-actions-workflows). From c2c2c5533d3d0fc43728288fb58e2eb90fd4453c Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 16:54:21 +0100 Subject: [PATCH 07/46] refactor: Clean up import statements and improve code formatting across multiple files --- script/TransferAdminRole.s.sol | 5 ++- script/lib/ConfigLib.sol | 13 ++++---- script/lib/UUPSProxyDeployer.sol | 16 ++++------ src/RLCCrosschainToken.sol | 32 +++++-------------- src/RLCLiquidityUnifier.sol | 5 ++- .../layerZero/IexecLayerZeroBridge.sol | 11 ++----- .../IexecLayerZeroBridgeDeployScript.t.sol | 10 +++--- test/units/RLCCrosschainToken.t.sol | 5 +-- test/units/RLCLiquidityUnifier.t.sol | 5 +-- test/units/TransferAdminRoleScript.t.sol | 5 ++- .../layerZero/IexecLayerZeroBridge.t.sol | 5 ++- .../IexecLayerZeroBridgeConfigureScript.t.sol | 20 ++++++------ .../utils/DualPausableUpgradeable.t.sol | 5 ++- test/units/utils/TestUtils.sol | 10 +++--- 14 files changed, 63 insertions(+), 84 deletions(-) diff --git a/script/TransferAdminRole.s.sol b/script/TransferAdminRole.s.sol index 3e00ab88..e894538f 100644 --- a/script/TransferAdminRole.s.sol +++ b/script/TransferAdminRole.s.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; diff --git a/script/lib/ConfigLib.sol b/script/lib/ConfigLib.sol index dc36cfee..48107e45 100644 --- a/script/lib/ConfigLib.sol +++ b/script/lib/ConfigLib.sol @@ -109,10 +109,10 @@ library ConfigLib { params.createxFactory = config.readAddress(".createxFactory"); params.rlcToken = getRLCTokenAddress(config, prefix); ( - params.rlcCrosschainTokenCreatexSalt, - params.rlcLiquidityUnifierCreatexSalt, - params.iexecLayerZeroBridgeCreatexSalt - ) = getAllCreatexParams(config, prefix); + params.rlcCrosschainTokenCreatexSalt, + params.rlcLiquidityUnifierCreatexSalt, + params.iexecLayerZeroBridgeCreatexSalt + ) = getAllCreatexParams(config, prefix); params.rlcCrosschainTokenAddress = getRLCCrosschainTokenAddress(config, prefix); params.rlcLiquidityUnifierAddress = getLiquidityUnifierAddress(config, prefix); params.approvalRequired = config.readBool(string.concat(prefix, ".approvalRequired")); @@ -161,9 +161,8 @@ library ConfigLib { */ function _validateJsonContent(string memory content) private pure { try vm.parseJson(content) { - // JSON is valid, proceed - } - catch { + // JSON is valid, proceed + } catch { console.log("Invalid JSON in config file"); revert("Invalid JSON format"); } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index aa76afc5..9dfeb5b8 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -32,16 +32,14 @@ library UUPSProxyDeployer { address createxFactory, bytes32 createxSalt ) internal returns (address) { - address implementation = deployImplementationUsingCreateX( - contractName, constructorData, createxFactory, createxSalt + address implementation = + deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); + address proxy = ICreateX(createxFactory).deployCreate2AndInit( + createxSalt, + abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode + initializeData, + ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX ); - address proxy = ICreateX(createxFactory) - .deployCreate2AndInit( - createxSalt, - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode - initializeData, - ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX - ); console.log("UUPS Proxy deployed at:", proxy); return proxy; } diff --git a/src/RLCCrosschainToken.sol b/src/RLCCrosschainToken.sol index 629f0ed5..73b73b82 100644 --- a/src/RLCCrosschainToken.sol +++ b/src/RLCCrosschainToken.sol @@ -4,16 +4,13 @@ pragma solidity ^0.8.22; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { - ERC20PermitUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; -import { - ERC20BridgeableUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; +import {ERC20PermitUpgradeable} from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; +import {ERC20BridgeableUpgradeable} from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; import {ITokenSpender} from "./interfaces/ITokenSpender.sol"; /** @@ -104,13 +101,7 @@ contract RLCCrosschainToken is * @dev Authorizes upgrades of the proxy. It can only be called by * an account with the UPGRADER_ROLE. */ - function _authorizeUpgrade( - address /*newImplementation*/ - ) - internal - override - onlyRole(UPGRADER_ROLE) - {} + function _authorizeUpgrade(address /*newImplementation*/ ) internal override onlyRole(UPGRADER_ROLE) {} /** * Checks if the caller is a trusted token bridge that is allowed by iExec to call @@ -118,12 +109,5 @@ contract RLCCrosschainToken is * @dev This function is called by the modifier `onlyTokenBridge` in the * `ERC20BridgeableUpgradeable` contract. */ - function _checkTokenBridge( - address /*caller*/ - ) - internal - view - override - onlyRole(TOKEN_BRIDGE_ROLE) - {} + function _checkTokenBridge(address /*caller*/ ) internal view override onlyRole(TOKEN_BRIDGE_ROLE) {} } diff --git a/src/RLCLiquidityUnifier.sol b/src/RLCLiquidityUnifier.sol index a9849eed..a32f0702 100644 --- a/src/RLCLiquidityUnifier.sol +++ b/src/RLCLiquidityUnifier.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; diff --git a/src/bridges/layerZero/IexecLayerZeroBridge.sol b/src/bridges/layerZero/IexecLayerZeroBridge.sol index 399ce2ed..ad810ea5 100644 --- a/src/bridges/layerZero/IexecLayerZeroBridge.sol +++ b/src/bridges/layerZero/IexecLayerZeroBridge.sol @@ -7,9 +7,8 @@ import {OFTCoreUpgradeable} from "@layerzerolabs/oft-evm-upgradeable/contracts/o import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {DualPausableUpgradeable} from "../utils/DualPausableUpgradeable.sol"; import {IIexecLayerZeroBridge} from "../../interfaces/IIexecLayerZeroBridge.sol"; @@ -313,11 +312,7 @@ contract IexecLayerZeroBridge is * @param amountLD The amount of tokens to mint (in local decimals) * @return amountReceivedLD The amount of tokens actually minted */ - function _credit( - address to, - uint256 amountLD, - uint32 /*_srcEid*/ - ) + function _credit(address to, uint256 amountLD, uint32 /*_srcEid*/ ) internal override whenNotPaused diff --git a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol index 432368eb..6106159d 100644 --- a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol +++ b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol @@ -41,13 +41,15 @@ contract IexecLayerZeroBridgeDeployScriptTest is Test { // Setup Ethereum Mainnet fork vm.selectFork(sepoliaFork); - rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript() - .deploy(params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt")); + rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript().deploy( + params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt") + ); // Setup Arbitrum Sepolia fork vm.selectFork(arbitrumSepoliaFork); - rlcCrosschainToken = new RLCCrosschainTokenDeployScript() - .deploy("iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt); + rlcCrosschainToken = new RLCCrosschainTokenDeployScript().deploy( + "iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt + ); //Add label to make logs more readable vm.label(address(rlcCrosschainToken), "rlcCrosschainToken"); diff --git a/test/units/RLCCrosschainToken.t.sol b/test/units/RLCCrosschainToken.t.sol index 95892ae0..4da2b9c8 100644 --- a/test/units/RLCCrosschainToken.t.sol +++ b/test/units/RLCCrosschainToken.t.sol @@ -36,8 +36,9 @@ contract RLCCrosschainTokenTest is Test { function setUp() public { rlcCrosschainToken = RLCCrosschainToken( - new RLCCrosschainTokenDeployScript() - .deploy("iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt")) + new RLCCrosschainTokenDeployScript().deploy( + "iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt") + ) ); bridgeTokenRoleId = rlcCrosschainToken.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/RLCLiquidityUnifier.t.sol b/test/units/RLCLiquidityUnifier.t.sol index 30cb2653..835a0bfb 100644 --- a/test/units/RLCLiquidityUnifier.t.sol +++ b/test/units/RLCLiquidityUnifier.t.sol @@ -38,8 +38,9 @@ contract LiquidityUnifierTest is Test { function setUp() public { rlcToken = new RLCMock(); rlcLiquidityUnifier = RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript() - .deploy(address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt")) + new RLCLiquidityUnifierDeployScript().deploy( + address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt") + ) ); rlcLiquidityUnifierAddress = address(rlcLiquidityUnifier); bridgeTokenRoleId = rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/TransferAdminRoleScript.t.sol b/test/units/TransferAdminRoleScript.t.sol index f38426c2..47810683 100644 --- a/test/units/TransferAdminRoleScript.t.sol +++ b/test/units/TransferAdminRoleScript.t.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {BeginTransferAdminRole, AcceptAdminRole} from "../../script/TransferAdminRole.s.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {TestUtils} from "./utils/TestUtils.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol index 5b0d9fdf..23e56b19 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol @@ -7,9 +7,8 @@ import {MessagingFee, SendParam, IOFT} from "@layerzerolabs/oft-evm/contracts/in import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {IERC7802} from "@openzeppelin/contracts/interfaces/draft-IERC7802.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol index 3869333e..265888a1 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol @@ -9,9 +9,8 @@ import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOApp import {IOAppOptionsType3} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; -import { - Configure as IexecLayerZeroBridgeConfigureScript -} from "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; +import {Configure as IexecLayerZeroBridgeConfigureScript} from + "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; import {LayerZeroUtils} from "../../../../script/utils/LayerZeroUtils.sol"; // This test contract inherits from `Configure` script because we need the `msg.sender` to be the admin @@ -179,8 +178,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier - .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), + deployment.rlcLiquidityUnifier.hasRole( + deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress + ), "Expected bridge to have the role" ); // rlcCrosschainToken @@ -193,8 +193,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcCrosschainToken - .hasRole(deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress), + deployment.rlcCrosschainToken.hasRole( + deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress + ), "Expected bridge to have the role" ); vm.stopPrank(); @@ -211,8 +212,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier - .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), + deployment.rlcLiquidityUnifier.hasRole( + deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress + ), "Expected bridge to have the role" ); assertFalse( diff --git a/test/units/bridges/utils/DualPausableUpgradeable.t.sol b/test/units/bridges/utils/DualPausableUpgradeable.t.sol index e25e53ed..e831fde9 100644 --- a/test/units/bridges/utils/DualPausableUpgradeable.t.sol +++ b/test/units/bridges/utils/DualPausableUpgradeable.t.sol @@ -6,9 +6,8 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {DualPausableUpgradeable} from "../../../../src/bridges/utils/DualPausableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; /** * @title DualPausableUpgradeableTest diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 387d7c23..390c7b23 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -77,8 +77,9 @@ library TestUtils { bytes32 salt ) private returns (RLCLiquidityUnifier) { return RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript() - .deploy(address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt) + new RLCLiquidityUnifierDeployScript().deploy( + address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt + ) ); } @@ -120,8 +121,9 @@ library TestUtils { bytes32 salt ) private returns (RLCCrosschainToken) { return RLCCrosschainToken( - new RLCCrosschainTokenDeployScript() - .deploy(name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt) + new RLCCrosschainTokenDeployScript().deploy( + name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt + ) ); } From 4e977b556ae7fd6f91352e52bb574223c21c1879 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 16:56:14 +0100 Subject: [PATCH 08/46] fix: error to trigger --- .../manage-token-bridge-role-safe.yml | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-token-bridge-role-safe.yml index a3dc1849..a956987c 100644 --- a/.github/workflows/manage-token-bridge-role-safe.yml +++ b/.github/workflows/manage-token-bridge-role-safe.yml @@ -79,7 +79,6 @@ jobs: # revokeRole(bytes32 role, address account) TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") FUNCTION_NAME="revokeRole(bytes32,address)" - fi echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT @@ -88,17 +87,24 @@ jobs: echo "==========================================" echo "Transaction Details" echo "==========================================" - echo "Network: ${{ inputs.network }}" - echo "Operation: ${{ inputs.operation }}" - echo "Function: $FUNCTION_NAME" - echo "Target Contract: $TARGET_CONTRACT" - echo "Bridge Address: ${{ inputs.bridge_address }}" - echo "Role Hash: $ROLE_HASH" - echo "Transaction Data: $TRANSACTION_DATA" - echo "Transaction Value: 0" - echo "Safe Address: ${{ secrets.SAFE_ADDRESS }}" - echo "Dry Run: ${{ inputs.dry_run }}" - echo "==========================================" + echo "Workflow Configuration:" + echo " • Network: ${{ inputs.network }}" + echo " • Operation: ${{ inputs.operation }}" + echo " • Function: $FUNCTION_NAME" + echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Dry Run: ${{ inputs.dry_run }}" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" + echo "Transaction Details:" + echo " • Target: $TARGET_CONTRACT" + echo " • Bridge: ${{ inputs.bridge_address }}" + echo " • Role Hash: $ROLE_HASH" + echo " • Value: 0 ETH" + echo " • Data: $TRANSACTION_DATA" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" if [ "${{ inputs.dry_run }}" == "true" ]; then echo "✅ DRY RUN MODE: Transaction prepared successfully" @@ -114,7 +120,7 @@ jobs: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: - rpc-url: ${{ github.secrets.RPC_URL }} + rpc-url: ${{ secrets.RPC_URL }} safe-address: ${{ needs.prepare-role-operation.outputs.safe-address }} transaction-to: ${{ needs.prepare-role-operation.outputs.target-contract }} transaction-value: '0' From 604cadf008030cb77a4f513450876ed955770910 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 16:56:46 +0100 Subject: [PATCH 09/46] fix: Correct RPC URL reference in role management workflow --- .github/workflows/manage-token-bridge-role-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-token-bridge-role-safe.yml index a956987c..e85b05ff 100644 --- a/.github/workflows/manage-token-bridge-role-safe.yml +++ b/.github/workflows/manage-token-bridge-role-safe.yml @@ -120,7 +120,7 @@ jobs: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: - rpc-url: ${{ secrets.RPC_URL }} + rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-role-operation.outputs.safe-address }} transaction-to: ${{ needs.prepare-role-operation.outputs.target-contract }} transaction-value: '0' From 1a038c333ce126618fe54a917007136b822713c5 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:06:41 +0100 Subject: [PATCH 10/46] fix: Add missing fi statement for revokeRole transaction data preparation --- .github/workflows/manage-token-bridge-role-safe.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-token-bridge-role-safe.yml index e85b05ff..a70f29c9 100644 --- a/.github/workflows/manage-token-bridge-role-safe.yml +++ b/.github/workflows/manage-token-bridge-role-safe.yml @@ -79,6 +79,7 @@ jobs: # revokeRole(bytes32 role, address account) TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") FUNCTION_NAME="revokeRole(bytes32,address)" + fi echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT From acd2c81d62b5fcec8b8a6e1448cf457ef783b190 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:08:49 +0100 Subject: [PATCH 11/46] refactor: Enhance transaction details logging format in bridge pause workflow --- .github/workflows/bridge-pause-safe.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index f5dfbe00..fff2f1d5 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -89,15 +89,22 @@ jobs: echo "==========================================" echo "Transaction Details" echo "==========================================" - echo "Network: ${{ inputs.network }}" - echo "Operation: ${{ inputs.operation }}" - echo "Function: $FUNCTION_NAME" - echo "Target Contract: $BRIDGE_ADDRESS" - echo "Transaction Data: $TRANSACTION_DATA" - echo "Transaction Value: 0" - echo "Safe Address: ${{ secrets.SAFE_ADDRESS }}" - echo "Dry Run: ${{ inputs.dry_run }}" - echo "==========================================" + echo "Workflow Configuration:" + echo " • Network: ${{ inputs.network }}" + echo " • Operation: ${{ inputs.operation }}" + echo " • Function: $FUNCTION_NAME" + echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Dry Run: ${{ inputs.dry_run }}" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" + echo "Transaction Details:" + echo " • Target: $BRIDGE_ADDRESS" + echo " • Value: 0 ETH" + echo " • Data: $TRANSACTION_DATA" + echo "" + echo "────────────────────────────────────────────────────────────────────────────────" + echo "" if [ "${{ inputs.dry_run }}" == "true" ]; then echo "✅ DRY RUN MODE: Transaction prepared successfully" From 055a2e12225650e803e1abf5ffbf34e1a9dfb49a Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:12:48 +0100 Subject: [PATCH 12/46] fix: add error to trigger --- .github/workflows/transfer-admin-role-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 8addd1a0..41463841 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -156,7 +156,7 @@ jobs: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: - rpc-url: ${{ github.secrets.RPC_URL }} + rpc-url: ${{ secrets.RPC_URL }} safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].to }} transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].value }} From fb4a89b02a0f2939cea223c0b636bda584dc32cb Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:13:01 +0100 Subject: [PATCH 13/46] fix: Correct reference to RPC URL in propose-bridge-to-safe job --- .github/workflows/transfer-admin-role-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 41463841..8addd1a0 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -156,7 +156,7 @@ jobs: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: - rpc-url: ${{ secrets.RPC_URL }} + rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].to }} transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].value }} From ca0b55c41a7b1a37bb732c3d6f8753507fff4670 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:17:05 +0100 Subject: [PATCH 14/46] refactor: Simplify initialization data encoding in deploy function --- script/RLCCrosschainToken.s.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 261d9f8e..dbc30659 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -56,9 +56,8 @@ contract Deploy is Script { address createxFactory, bytes32 createxSalt ) public returns (address) { - bytes memory initData = abi.encodeWithSelector( - RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader - ); + bytes memory initData = + abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } From 7a1756afb8bf679f743eaf671e4df4a6e18b2b03 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:19:52 +0100 Subject: [PATCH 15/46] fix: Add missing fi statement to complete transaction creation for bridge contract --- .github/workflows/transfer-admin-role-safe.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 8addd1a0..baad547e 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -98,6 +98,7 @@ jobs: # Create transaction for bridge contract TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ '. += [{"to": $to, "value": "0", "data": $data}]') + fi echo "transactions=$TRANSACTIONS" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT From 5e04c921bf2570f2699afef37006cac2a6c15c8f Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:23:23 +0100 Subject: [PATCH 16/46] fix: Use compact JSON output for transaction creation in admin transfer workflow --- .github/workflows/transfer-admin-role-safe.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index baad547e..e769d0c6 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -80,11 +80,11 @@ jobs: FUNCTION_NAME="beginDefaultAdminTransfer(address)" # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ '. += [{"to": $to, "value": "0", "data": $data}]') # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ '. += [{"to": $to, "value": "0", "data": $data}]') else # acceptDefaultAdminTransfer() @@ -92,15 +92,15 @@ jobs: FUNCTION_NAME="acceptDefaultAdminTransfer()" # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ '. += [{"to": $to, "value": "0", "data": $data}]') # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ '. += [{"to": $to, "value": "0", "data": $data}]') fi - echo "transactions=$TRANSACTIONS" >> $GITHUB_OUTPUT + echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification From 7a55eb42c51d5385f7986c78df5ec5fe1c0860da Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:26:31 +0100 Subject: [PATCH 17/46] style: Improve code formatting and readability across multiple files --- script/RLCCrosschainToken.s.sol | 5 ++- script/TransferAdminRole.s.sol | 5 ++- script/lib/ConfigLib.sol | 13 ++++---- script/lib/UUPSProxyDeployer.sol | 16 ++++------ src/RLCCrosschainToken.sol | 32 +++++-------------- src/RLCLiquidityUnifier.sol | 5 ++- .../layerZero/IexecLayerZeroBridge.sol | 11 ++----- .../IexecLayerZeroBridgeDeployScript.t.sol | 10 +++--- test/units/RLCCrosschainToken.t.sol | 5 +-- test/units/RLCLiquidityUnifier.t.sol | 5 +-- test/units/TransferAdminRoleScript.t.sol | 5 ++- .../layerZero/IexecLayerZeroBridge.t.sol | 5 ++- .../IexecLayerZeroBridgeConfigureScript.t.sol | 20 ++++++------ .../utils/DualPausableUpgradeable.t.sol | 5 ++- test/units/utils/TestUtils.sol | 10 +++--- 15 files changed, 65 insertions(+), 87 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 261d9f8e..dbc30659 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -56,9 +56,8 @@ contract Deploy is Script { address createxFactory, bytes32 createxSalt ) public returns (address) { - bytes memory initData = abi.encodeWithSelector( - RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader - ); + bytes memory initData = + abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } diff --git a/script/TransferAdminRole.s.sol b/script/TransferAdminRole.s.sol index 3e00ab88..e894538f 100644 --- a/script/TransferAdminRole.s.sol +++ b/script/TransferAdminRole.s.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; diff --git a/script/lib/ConfigLib.sol b/script/lib/ConfigLib.sol index dc36cfee..48107e45 100644 --- a/script/lib/ConfigLib.sol +++ b/script/lib/ConfigLib.sol @@ -109,10 +109,10 @@ library ConfigLib { params.createxFactory = config.readAddress(".createxFactory"); params.rlcToken = getRLCTokenAddress(config, prefix); ( - params.rlcCrosschainTokenCreatexSalt, - params.rlcLiquidityUnifierCreatexSalt, - params.iexecLayerZeroBridgeCreatexSalt - ) = getAllCreatexParams(config, prefix); + params.rlcCrosschainTokenCreatexSalt, + params.rlcLiquidityUnifierCreatexSalt, + params.iexecLayerZeroBridgeCreatexSalt + ) = getAllCreatexParams(config, prefix); params.rlcCrosschainTokenAddress = getRLCCrosschainTokenAddress(config, prefix); params.rlcLiquidityUnifierAddress = getLiquidityUnifierAddress(config, prefix); params.approvalRequired = config.readBool(string.concat(prefix, ".approvalRequired")); @@ -161,9 +161,8 @@ library ConfigLib { */ function _validateJsonContent(string memory content) private pure { try vm.parseJson(content) { - // JSON is valid, proceed - } - catch { + // JSON is valid, proceed + } catch { console.log("Invalid JSON in config file"); revert("Invalid JSON format"); } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index aa76afc5..9dfeb5b8 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -32,16 +32,14 @@ library UUPSProxyDeployer { address createxFactory, bytes32 createxSalt ) internal returns (address) { - address implementation = deployImplementationUsingCreateX( - contractName, constructorData, createxFactory, createxSalt + address implementation = + deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); + address proxy = ICreateX(createxFactory).deployCreate2AndInit( + createxSalt, + abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode + initializeData, + ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX ); - address proxy = ICreateX(createxFactory) - .deployCreate2AndInit( - createxSalt, - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode - initializeData, - ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX - ); console.log("UUPS Proxy deployed at:", proxy); return proxy; } diff --git a/src/RLCCrosschainToken.sol b/src/RLCCrosschainToken.sol index 629f0ed5..73b73b82 100644 --- a/src/RLCCrosschainToken.sol +++ b/src/RLCCrosschainToken.sol @@ -4,16 +4,13 @@ pragma solidity ^0.8.22; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { - ERC20PermitUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; -import { - ERC20BridgeableUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; +import {ERC20PermitUpgradeable} from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; +import {ERC20BridgeableUpgradeable} from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; import {ITokenSpender} from "./interfaces/ITokenSpender.sol"; /** @@ -104,13 +101,7 @@ contract RLCCrosschainToken is * @dev Authorizes upgrades of the proxy. It can only be called by * an account with the UPGRADER_ROLE. */ - function _authorizeUpgrade( - address /*newImplementation*/ - ) - internal - override - onlyRole(UPGRADER_ROLE) - {} + function _authorizeUpgrade(address /*newImplementation*/ ) internal override onlyRole(UPGRADER_ROLE) {} /** * Checks if the caller is a trusted token bridge that is allowed by iExec to call @@ -118,12 +109,5 @@ contract RLCCrosschainToken is * @dev This function is called by the modifier `onlyTokenBridge` in the * `ERC20BridgeableUpgradeable` contract. */ - function _checkTokenBridge( - address /*caller*/ - ) - internal - view - override - onlyRole(TOKEN_BRIDGE_ROLE) - {} + function _checkTokenBridge(address /*caller*/ ) internal view override onlyRole(TOKEN_BRIDGE_ROLE) {} } diff --git a/src/RLCLiquidityUnifier.sol b/src/RLCLiquidityUnifier.sol index a9849eed..a32f0702 100644 --- a/src/RLCLiquidityUnifier.sol +++ b/src/RLCLiquidityUnifier.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; diff --git a/src/bridges/layerZero/IexecLayerZeroBridge.sol b/src/bridges/layerZero/IexecLayerZeroBridge.sol index 399ce2ed..ad810ea5 100644 --- a/src/bridges/layerZero/IexecLayerZeroBridge.sol +++ b/src/bridges/layerZero/IexecLayerZeroBridge.sol @@ -7,9 +7,8 @@ import {OFTCoreUpgradeable} from "@layerzerolabs/oft-evm-upgradeable/contracts/o import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {DualPausableUpgradeable} from "../utils/DualPausableUpgradeable.sol"; import {IIexecLayerZeroBridge} from "../../interfaces/IIexecLayerZeroBridge.sol"; @@ -313,11 +312,7 @@ contract IexecLayerZeroBridge is * @param amountLD The amount of tokens to mint (in local decimals) * @return amountReceivedLD The amount of tokens actually minted */ - function _credit( - address to, - uint256 amountLD, - uint32 /*_srcEid*/ - ) + function _credit(address to, uint256 amountLD, uint32 /*_srcEid*/ ) internal override whenNotPaused diff --git a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol index 432368eb..6106159d 100644 --- a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol +++ b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol @@ -41,13 +41,15 @@ contract IexecLayerZeroBridgeDeployScriptTest is Test { // Setup Ethereum Mainnet fork vm.selectFork(sepoliaFork); - rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript() - .deploy(params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt")); + rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript().deploy( + params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt") + ); // Setup Arbitrum Sepolia fork vm.selectFork(arbitrumSepoliaFork); - rlcCrosschainToken = new RLCCrosschainTokenDeployScript() - .deploy("iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt); + rlcCrosschainToken = new RLCCrosschainTokenDeployScript().deploy( + "iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt + ); //Add label to make logs more readable vm.label(address(rlcCrosschainToken), "rlcCrosschainToken"); diff --git a/test/units/RLCCrosschainToken.t.sol b/test/units/RLCCrosschainToken.t.sol index 95892ae0..4da2b9c8 100644 --- a/test/units/RLCCrosschainToken.t.sol +++ b/test/units/RLCCrosschainToken.t.sol @@ -36,8 +36,9 @@ contract RLCCrosschainTokenTest is Test { function setUp() public { rlcCrosschainToken = RLCCrosschainToken( - new RLCCrosschainTokenDeployScript() - .deploy("iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt")) + new RLCCrosschainTokenDeployScript().deploy( + "iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt") + ) ); bridgeTokenRoleId = rlcCrosschainToken.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/RLCLiquidityUnifier.t.sol b/test/units/RLCLiquidityUnifier.t.sol index 30cb2653..835a0bfb 100644 --- a/test/units/RLCLiquidityUnifier.t.sol +++ b/test/units/RLCLiquidityUnifier.t.sol @@ -38,8 +38,9 @@ contract LiquidityUnifierTest is Test { function setUp() public { rlcToken = new RLCMock(); rlcLiquidityUnifier = RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript() - .deploy(address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt")) + new RLCLiquidityUnifierDeployScript().deploy( + address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt") + ) ); rlcLiquidityUnifierAddress = address(rlcLiquidityUnifier); bridgeTokenRoleId = rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/TransferAdminRoleScript.t.sol b/test/units/TransferAdminRoleScript.t.sol index f38426c2..47810683 100644 --- a/test/units/TransferAdminRoleScript.t.sol +++ b/test/units/TransferAdminRoleScript.t.sol @@ -5,9 +5,8 @@ pragma solidity ^0.8.22; import {BeginTransferAdminRole, AcceptAdminRole} from "../../script/TransferAdminRole.s.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {TestUtils} from "./utils/TestUtils.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol index 5b0d9fdf..23e56b19 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol @@ -7,9 +7,8 @@ import {MessagingFee, SendParam, IOFT} from "@layerzerolabs/oft-evm/contracts/in import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {IERC7802} from "@openzeppelin/contracts/interfaces/draft-IERC7802.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; -import { - IAccessControlDefaultAdminRules -} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import {IAccessControlDefaultAdminRules} from + "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol index 3869333e..265888a1 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol @@ -9,9 +9,8 @@ import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOApp import {IOAppOptionsType3} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; -import { - Configure as IexecLayerZeroBridgeConfigureScript -} from "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; +import {Configure as IexecLayerZeroBridgeConfigureScript} from + "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; import {LayerZeroUtils} from "../../../../script/utils/LayerZeroUtils.sol"; // This test contract inherits from `Configure` script because we need the `msg.sender` to be the admin @@ -179,8 +178,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier - .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), + deployment.rlcLiquidityUnifier.hasRole( + deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress + ), "Expected bridge to have the role" ); // rlcCrosschainToken @@ -193,8 +193,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcCrosschainToken - .hasRole(deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress), + deployment.rlcCrosschainToken.hasRole( + deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress + ), "Expected bridge to have the role" ); vm.stopPrank(); @@ -211,8 +212,9 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier - .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), + deployment.rlcLiquidityUnifier.hasRole( + deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress + ), "Expected bridge to have the role" ); assertFalse( diff --git a/test/units/bridges/utils/DualPausableUpgradeable.t.sol b/test/units/bridges/utils/DualPausableUpgradeable.t.sol index e25e53ed..e831fde9 100644 --- a/test/units/bridges/utils/DualPausableUpgradeable.t.sol +++ b/test/units/bridges/utils/DualPausableUpgradeable.t.sol @@ -6,9 +6,8 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {DualPausableUpgradeable} from "../../../../src/bridges/utils/DualPausableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import { - AccessControlDefaultAdminRulesUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import {AccessControlDefaultAdminRulesUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; /** * @title DualPausableUpgradeableTest diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 387d7c23..390c7b23 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -77,8 +77,9 @@ library TestUtils { bytes32 salt ) private returns (RLCLiquidityUnifier) { return RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript() - .deploy(address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt) + new RLCLiquidityUnifierDeployScript().deploy( + address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt + ) ); } @@ -120,8 +121,9 @@ library TestUtils { bytes32 salt ) private returns (RLCCrosschainToken) { return RLCCrosschainToken( - new RLCCrosschainTokenDeployScript() - .deploy(name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt) + new RLCCrosschainTokenDeployScript().deploy( + name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt + ) ); } From 3c0f1be4a05098f9424aa78d2a8994cd86769fb4 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:29:27 +0100 Subject: [PATCH 18/46] style: Refactor code for improved readability and formatting across multiple files --- script/RLCCrosschainToken.s.sol | 5 +-- script/TransferAdminRole.s.sol | 5 +-- script/lib/ConfigLib.sol | 5 +-- script/lib/UUPSProxyDeployer.sol | 16 ++++++---- src/RLCCrosschainToken.sol | 32 ++++++++++++++----- src/RLCLiquidityUnifier.sol | 5 +-- .../layerZero/IexecLayerZeroBridge.sol | 11 +++++-- .../IexecLayerZeroBridgeDeployScript.t.sol | 10 +++--- test/units/RLCCrosschainToken.t.sol | 5 ++- test/units/RLCLiquidityUnifier.t.sol | 5 ++- test/units/TransferAdminRoleScript.t.sol | 5 +-- .../layerZero/IexecLayerZeroBridge.t.sol | 5 +-- .../IexecLayerZeroBridgeConfigureScript.t.sol | 20 ++++++------ .../utils/DualPausableUpgradeable.t.sol | 5 +-- test/units/utils/TestUtils.sol | 10 +++--- 15 files changed, 83 insertions(+), 61 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index dbc30659..261d9f8e 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -56,8 +56,9 @@ contract Deploy is Script { address createxFactory, bytes32 createxSalt ) public returns (address) { - bytes memory initData = - abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); + bytes memory initData = abi.encodeWithSelector( + RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader + ); return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } diff --git a/script/TransferAdminRole.s.sol b/script/TransferAdminRole.s.sol index e894538f..3e00ab88 100644 --- a/script/TransferAdminRole.s.sol +++ b/script/TransferAdminRole.s.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; diff --git a/script/lib/ConfigLib.sol b/script/lib/ConfigLib.sol index 48107e45..2139eb17 100644 --- a/script/lib/ConfigLib.sol +++ b/script/lib/ConfigLib.sol @@ -161,8 +161,9 @@ library ConfigLib { */ function _validateJsonContent(string memory content) private pure { try vm.parseJson(content) { - // JSON is valid, proceed - } catch { + // JSON is valid, proceed + } + catch { console.log("Invalid JSON in config file"); revert("Invalid JSON format"); } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index 9dfeb5b8..aa76afc5 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -32,14 +32,16 @@ library UUPSProxyDeployer { address createxFactory, bytes32 createxSalt ) internal returns (address) { - address implementation = - deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); - address proxy = ICreateX(createxFactory).deployCreate2AndInit( - createxSalt, - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode - initializeData, - ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX + address implementation = deployImplementationUsingCreateX( + contractName, constructorData, createxFactory, createxSalt ); + address proxy = ICreateX(createxFactory) + .deployCreate2AndInit( + createxSalt, + abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode + initializeData, + ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX + ); console.log("UUPS Proxy deployed at:", proxy); return proxy; } diff --git a/src/RLCCrosschainToken.sol b/src/RLCCrosschainToken.sol index 73b73b82..629f0ed5 100644 --- a/src/RLCCrosschainToken.sol +++ b/src/RLCCrosschainToken.sol @@ -4,13 +4,16 @@ pragma solidity ^0.8.22; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {ERC20PermitUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; -import {ERC20BridgeableUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; +import { + ERC20PermitUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; +import { + ERC20BridgeableUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; import {ITokenSpender} from "./interfaces/ITokenSpender.sol"; /** @@ -101,7 +104,13 @@ contract RLCCrosschainToken is * @dev Authorizes upgrades of the proxy. It can only be called by * an account with the UPGRADER_ROLE. */ - function _authorizeUpgrade(address /*newImplementation*/ ) internal override onlyRole(UPGRADER_ROLE) {} + function _authorizeUpgrade( + address /*newImplementation*/ + ) + internal + override + onlyRole(UPGRADER_ROLE) + {} /** * Checks if the caller is a trusted token bridge that is allowed by iExec to call @@ -109,5 +118,12 @@ contract RLCCrosschainToken is * @dev This function is called by the modifier `onlyTokenBridge` in the * `ERC20BridgeableUpgradeable` contract. */ - function _checkTokenBridge(address /*caller*/ ) internal view override onlyRole(TOKEN_BRIDGE_ROLE) {} + function _checkTokenBridge( + address /*caller*/ + ) + internal + view + override + onlyRole(TOKEN_BRIDGE_ROLE) + {} } diff --git a/src/RLCLiquidityUnifier.sol b/src/RLCLiquidityUnifier.sol index a32f0702..a9849eed 100644 --- a/src/RLCLiquidityUnifier.sol +++ b/src/RLCLiquidityUnifier.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; diff --git a/src/bridges/layerZero/IexecLayerZeroBridge.sol b/src/bridges/layerZero/IexecLayerZeroBridge.sol index ad810ea5..399ce2ed 100644 --- a/src/bridges/layerZero/IexecLayerZeroBridge.sol +++ b/src/bridges/layerZero/IexecLayerZeroBridge.sol @@ -7,8 +7,9 @@ import {OFTCoreUpgradeable} from "@layerzerolabs/oft-evm-upgradeable/contracts/o import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {DualPausableUpgradeable} from "../utils/DualPausableUpgradeable.sol"; import {IIexecLayerZeroBridge} from "../../interfaces/IIexecLayerZeroBridge.sol"; @@ -312,7 +313,11 @@ contract IexecLayerZeroBridge is * @param amountLD The amount of tokens to mint (in local decimals) * @return amountReceivedLD The amount of tokens actually minted */ - function _credit(address to, uint256 amountLD, uint32 /*_srcEid*/ ) + function _credit( + address to, + uint256 amountLD, + uint32 /*_srcEid*/ + ) internal override whenNotPaused diff --git a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol index 6106159d..432368eb 100644 --- a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol +++ b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol @@ -41,15 +41,13 @@ contract IexecLayerZeroBridgeDeployScriptTest is Test { // Setup Ethereum Mainnet fork vm.selectFork(sepoliaFork); - rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript().deploy( - params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt") - ); + rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript() + .deploy(params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt")); // Setup Arbitrum Sepolia fork vm.selectFork(arbitrumSepoliaFork); - rlcCrosschainToken = new RLCCrosschainTokenDeployScript().deploy( - "iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt - ); + rlcCrosschainToken = new RLCCrosschainTokenDeployScript() + .deploy("iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt); //Add label to make logs more readable vm.label(address(rlcCrosschainToken), "rlcCrosschainToken"); diff --git a/test/units/RLCCrosschainToken.t.sol b/test/units/RLCCrosschainToken.t.sol index 4da2b9c8..95892ae0 100644 --- a/test/units/RLCCrosschainToken.t.sol +++ b/test/units/RLCCrosschainToken.t.sol @@ -36,9 +36,8 @@ contract RLCCrosschainTokenTest is Test { function setUp() public { rlcCrosschainToken = RLCCrosschainToken( - new RLCCrosschainTokenDeployScript().deploy( - "iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt") - ) + new RLCCrosschainTokenDeployScript() + .deploy("iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt")) ); bridgeTokenRoleId = rlcCrosschainToken.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/RLCLiquidityUnifier.t.sol b/test/units/RLCLiquidityUnifier.t.sol index 835a0bfb..30cb2653 100644 --- a/test/units/RLCLiquidityUnifier.t.sol +++ b/test/units/RLCLiquidityUnifier.t.sol @@ -38,9 +38,8 @@ contract LiquidityUnifierTest is Test { function setUp() public { rlcToken = new RLCMock(); rlcLiquidityUnifier = RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript().deploy( - address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt") - ) + new RLCLiquidityUnifierDeployScript() + .deploy(address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt")) ); rlcLiquidityUnifierAddress = address(rlcLiquidityUnifier); bridgeTokenRoleId = rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/TransferAdminRoleScript.t.sol b/test/units/TransferAdminRoleScript.t.sol index 47810683..f38426c2 100644 --- a/test/units/TransferAdminRoleScript.t.sol +++ b/test/units/TransferAdminRoleScript.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {BeginTransferAdminRole, AcceptAdminRole} from "../../script/TransferAdminRole.s.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {TestUtils} from "./utils/TestUtils.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol index 23e56b19..5b0d9fdf 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol @@ -7,8 +7,9 @@ import {MessagingFee, SendParam, IOFT} from "@layerzerolabs/oft-evm/contracts/in import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {IERC7802} from "@openzeppelin/contracts/interfaces/draft-IERC7802.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol index 265888a1..3869333e 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol @@ -9,8 +9,9 @@ import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOApp import {IOAppOptionsType3} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; -import {Configure as IexecLayerZeroBridgeConfigureScript} from - "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; +import { + Configure as IexecLayerZeroBridgeConfigureScript +} from "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; import {LayerZeroUtils} from "../../../../script/utils/LayerZeroUtils.sol"; // This test contract inherits from `Configure` script because we need the `msg.sender` to be the admin @@ -178,9 +179,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier.hasRole( - deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress - ), + deployment.rlcLiquidityUnifier + .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), "Expected bridge to have the role" ); // rlcCrosschainToken @@ -193,9 +193,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcCrosschainToken.hasRole( - deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress - ), + deployment.rlcCrosschainToken + .hasRole(deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress), "Expected bridge to have the role" ); vm.stopPrank(); @@ -212,9 +211,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier.hasRole( - deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress - ), + deployment.rlcLiquidityUnifier + .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), "Expected bridge to have the role" ); assertFalse( diff --git a/test/units/bridges/utils/DualPausableUpgradeable.t.sol b/test/units/bridges/utils/DualPausableUpgradeable.t.sol index e831fde9..e25e53ed 100644 --- a/test/units/bridges/utils/DualPausableUpgradeable.t.sol +++ b/test/units/bridges/utils/DualPausableUpgradeable.t.sol @@ -6,8 +6,9 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {DualPausableUpgradeable} from "../../../../src/bridges/utils/DualPausableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; /** * @title DualPausableUpgradeableTest diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 390c7b23..387d7c23 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -77,9 +77,8 @@ library TestUtils { bytes32 salt ) private returns (RLCLiquidityUnifier) { return RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript().deploy( - address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt - ) + new RLCLiquidityUnifierDeployScript() + .deploy(address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt) ); } @@ -121,9 +120,8 @@ library TestUtils { bytes32 salt ) private returns (RLCCrosschainToken) { return RLCCrosschainToken( - new RLCCrosschainTokenDeployScript().deploy( - name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt - ) + new RLCCrosschainTokenDeployScript() + .deploy(name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt) ); } From 80e472ec0c985e32bc71deb823c11fcfddf225d1 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 4 Nov 2025 17:36:24 +0100 Subject: [PATCH 19/46] style: Improve code formatting and readability across multiple files --- script/RLCCrosschainToken.s.sol | 5 +-- script/TransferAdminRole.s.sol | 5 +-- script/lib/ConfigLib.sol | 5 +-- script/lib/UUPSProxyDeployer.sol | 16 ++++++---- src/RLCCrosschainToken.sol | 32 ++++++++++++++----- src/RLCLiquidityUnifier.sol | 5 +-- .../layerZero/IexecLayerZeroBridge.sol | 11 +++++-- .../IexecLayerZeroBridgeDeployScript.t.sol | 10 +++--- test/units/RLCCrosschainToken.t.sol | 5 ++- test/units/RLCLiquidityUnifier.t.sol | 5 ++- test/units/TransferAdminRoleScript.t.sol | 5 +-- .../layerZero/IexecLayerZeroBridge.t.sol | 5 +-- .../IexecLayerZeroBridgeConfigureScript.t.sol | 20 ++++++------ .../utils/DualPausableUpgradeable.t.sol | 5 +-- test/units/utils/TestUtils.sol | 10 +++--- 15 files changed, 83 insertions(+), 61 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index dbc30659..261d9f8e 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -56,8 +56,9 @@ contract Deploy is Script { address createxFactory, bytes32 createxSalt ) public returns (address) { - bytes memory initData = - abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); + bytes memory initData = abi.encodeWithSelector( + RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader + ); return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } diff --git a/script/TransferAdminRole.s.sol b/script/TransferAdminRole.s.sol index e894538f..3e00ab88 100644 --- a/script/TransferAdminRole.s.sol +++ b/script/TransferAdminRole.s.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; diff --git a/script/lib/ConfigLib.sol b/script/lib/ConfigLib.sol index 48107e45..2139eb17 100644 --- a/script/lib/ConfigLib.sol +++ b/script/lib/ConfigLib.sol @@ -161,8 +161,9 @@ library ConfigLib { */ function _validateJsonContent(string memory content) private pure { try vm.parseJson(content) { - // JSON is valid, proceed - } catch { + // JSON is valid, proceed + } + catch { console.log("Invalid JSON in config file"); revert("Invalid JSON format"); } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index 9dfeb5b8..aa76afc5 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -32,14 +32,16 @@ library UUPSProxyDeployer { address createxFactory, bytes32 createxSalt ) internal returns (address) { - address implementation = - deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); - address proxy = ICreateX(createxFactory).deployCreate2AndInit( - createxSalt, - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode - initializeData, - ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX + address implementation = deployImplementationUsingCreateX( + contractName, constructorData, createxFactory, createxSalt ); + address proxy = ICreateX(createxFactory) + .deployCreate2AndInit( + createxSalt, + abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode + initializeData, + ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX + ); console.log("UUPS Proxy deployed at:", proxy); return proxy; } diff --git a/src/RLCCrosschainToken.sol b/src/RLCCrosschainToken.sol index 73b73b82..629f0ed5 100644 --- a/src/RLCCrosschainToken.sol +++ b/src/RLCCrosschainToken.sol @@ -4,13 +4,16 @@ pragma solidity ^0.8.22; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {ERC20PermitUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; -import {ERC20BridgeableUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; +import { + ERC20PermitUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; +import { + ERC20BridgeableUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20BridgeableUpgradeable.sol"; import {ITokenSpender} from "./interfaces/ITokenSpender.sol"; /** @@ -101,7 +104,13 @@ contract RLCCrosschainToken is * @dev Authorizes upgrades of the proxy. It can only be called by * an account with the UPGRADER_ROLE. */ - function _authorizeUpgrade(address /*newImplementation*/ ) internal override onlyRole(UPGRADER_ROLE) {} + function _authorizeUpgrade( + address /*newImplementation*/ + ) + internal + override + onlyRole(UPGRADER_ROLE) + {} /** * Checks if the caller is a trusted token bridge that is allowed by iExec to call @@ -109,5 +118,12 @@ contract RLCCrosschainToken is * @dev This function is called by the modifier `onlyTokenBridge` in the * `ERC20BridgeableUpgradeable` contract. */ - function _checkTokenBridge(address /*caller*/ ) internal view override onlyRole(TOKEN_BRIDGE_ROLE) {} + function _checkTokenBridge( + address /*caller*/ + ) + internal + view + override + onlyRole(TOKEN_BRIDGE_ROLE) + {} } diff --git a/src/RLCLiquidityUnifier.sol b/src/RLCLiquidityUnifier.sol index a32f0702..a9849eed 100644 --- a/src/RLCLiquidityUnifier.sol +++ b/src/RLCLiquidityUnifier.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; diff --git a/src/bridges/layerZero/IexecLayerZeroBridge.sol b/src/bridges/layerZero/IexecLayerZeroBridge.sol index ad810ea5..399ce2ed 100644 --- a/src/bridges/layerZero/IexecLayerZeroBridge.sol +++ b/src/bridges/layerZero/IexecLayerZeroBridge.sol @@ -7,8 +7,9 @@ import {OFTCoreUpgradeable} from "@layerzerolabs/oft-evm-upgradeable/contracts/o import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {DualPausableUpgradeable} from "../utils/DualPausableUpgradeable.sol"; import {IIexecLayerZeroBridge} from "../../interfaces/IIexecLayerZeroBridge.sol"; @@ -312,7 +313,11 @@ contract IexecLayerZeroBridge is * @param amountLD The amount of tokens to mint (in local decimals) * @return amountReceivedLD The amount of tokens actually minted */ - function _credit(address to, uint256 amountLD, uint32 /*_srcEid*/ ) + function _credit( + address to, + uint256 amountLD, + uint32 /*_srcEid*/ + ) internal override whenNotPaused diff --git a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol index 6106159d..432368eb 100644 --- a/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol +++ b/test/e2e/IexecLayerZeroBridgeDeployScript.t.sol @@ -41,15 +41,13 @@ contract IexecLayerZeroBridgeDeployScriptTest is Test { // Setup Ethereum Mainnet fork vm.selectFork(sepoliaFork); - rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript().deploy( - params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt") - ); + rlcLiquidityUnifier = new RLCLiquidityUnifierDeployScript() + .deploy(params.rlcToken, admin, upgrader, params.createxFactory, keccak256("salt")); // Setup Arbitrum Sepolia fork vm.selectFork(arbitrumSepoliaFork); - rlcCrosschainToken = new RLCCrosschainTokenDeployScript().deploy( - "iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt - ); + rlcCrosschainToken = new RLCCrosschainTokenDeployScript() + .deploy("iEx.ec Network Token", "RLC", admin, admin, params.createxFactory, salt); //Add label to make logs more readable vm.label(address(rlcCrosschainToken), "rlcCrosschainToken"); diff --git a/test/units/RLCCrosschainToken.t.sol b/test/units/RLCCrosschainToken.t.sol index 4da2b9c8..95892ae0 100644 --- a/test/units/RLCCrosschainToken.t.sol +++ b/test/units/RLCCrosschainToken.t.sol @@ -36,9 +36,8 @@ contract RLCCrosschainTokenTest is Test { function setUp() public { rlcCrosschainToken = RLCCrosschainToken( - new RLCCrosschainTokenDeployScript().deploy( - "iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt") - ) + new RLCCrosschainTokenDeployScript() + .deploy("iEx.ec Network Token", "RLC", admin, upgrader, address(new CreateX()), keccak256("salt")) ); bridgeTokenRoleId = rlcCrosschainToken.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/RLCLiquidityUnifier.t.sol b/test/units/RLCLiquidityUnifier.t.sol index 835a0bfb..30cb2653 100644 --- a/test/units/RLCLiquidityUnifier.t.sol +++ b/test/units/RLCLiquidityUnifier.t.sol @@ -38,9 +38,8 @@ contract LiquidityUnifierTest is Test { function setUp() public { rlcToken = new RLCMock(); rlcLiquidityUnifier = RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript().deploy( - address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt") - ) + new RLCLiquidityUnifierDeployScript() + .deploy(address(rlcToken), admin, upgrader, address(new CreateX()), keccak256("salt")) ); rlcLiquidityUnifierAddress = address(rlcLiquidityUnifier); bridgeTokenRoleId = rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(); diff --git a/test/units/TransferAdminRoleScript.t.sol b/test/units/TransferAdminRoleScript.t.sol index 47810683..f38426c2 100644 --- a/test/units/TransferAdminRoleScript.t.sol +++ b/test/units/TransferAdminRoleScript.t.sol @@ -5,8 +5,9 @@ pragma solidity ^0.8.22; import {BeginTransferAdminRole, AcceptAdminRole} from "../../script/TransferAdminRole.s.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {TestUtils} from "./utils/TestUtils.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol index 23e56b19..5b0d9fdf 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol @@ -7,8 +7,9 @@ import {MessagingFee, SendParam, IOFT} from "@layerzerolabs/oft-evm/contracts/in import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import {IERC7802} from "@openzeppelin/contracts/interfaces/draft-IERC7802.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; -import {IAccessControlDefaultAdminRules} from - "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; +import { + IAccessControlDefaultAdminRules +} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol"; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol index 265888a1..3869333e 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeConfigureScript.t.sol @@ -9,8 +9,9 @@ import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOApp import {IOAppOptionsType3} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; -import {Configure as IexecLayerZeroBridgeConfigureScript} from - "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; +import { + Configure as IexecLayerZeroBridgeConfigureScript +} from "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; import {LayerZeroUtils} from "../../../../script/utils/LayerZeroUtils.sol"; // This test contract inherits from `Configure` script because we need the `msg.sender` to be the admin @@ -178,9 +179,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier.hasRole( - deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress - ), + deployment.rlcLiquidityUnifier + .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), "Expected bridge to have the role" ); // rlcCrosschainToken @@ -193,9 +193,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcCrosschainToken.hasRole( - deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress - ), + deployment.rlcCrosschainToken + .hasRole(deployment.rlcCrosschainToken.TOKEN_BRIDGE_ROLE(), targetBridgeAddress), "Expected bridge to have the role" ); vm.stopPrank(); @@ -212,9 +211,8 @@ contract IexecLayerZeroBridgeUpgradeScriptTest is TestHelperOz5, IexecLayerZeroB "Expected authorizeBridgeIfNeeded to return true" ); assertTrue( - deployment.rlcLiquidityUnifier.hasRole( - deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress - ), + deployment.rlcLiquidityUnifier + .hasRole(deployment.rlcLiquidityUnifier.TOKEN_BRIDGE_ROLE(), sourceBridgeAddress), "Expected bridge to have the role" ); assertFalse( diff --git a/test/units/bridges/utils/DualPausableUpgradeable.t.sol b/test/units/bridges/utils/DualPausableUpgradeable.t.sol index e831fde9..e25e53ed 100644 --- a/test/units/bridges/utils/DualPausableUpgradeable.t.sol +++ b/test/units/bridges/utils/DualPausableUpgradeable.t.sol @@ -6,8 +6,9 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {DualPausableUpgradeable} from "../../../../src/bridges/utils/DualPausableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import {AccessControlDefaultAdminRulesUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; +import { + AccessControlDefaultAdminRulesUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol"; /** * @title DualPausableUpgradeableTest diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 390c7b23..387d7c23 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -77,9 +77,8 @@ library TestUtils { bytes32 salt ) private returns (RLCLiquidityUnifier) { return RLCLiquidityUnifier( - new RLCLiquidityUnifierDeployScript().deploy( - address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt - ) + new RLCLiquidityUnifierDeployScript() + .deploy(address(rlcToken), params.initialAdmin, params.initialUpgrader, createXFactory, salt) ); } @@ -121,9 +120,8 @@ library TestUtils { bytes32 salt ) private returns (RLCCrosschainToken) { return RLCCrosschainToken( - new RLCCrosschainTokenDeployScript().deploy( - name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt - ) + new RLCCrosschainTokenDeployScript() + .deploy(name, symbol, params.initialAdmin, params.initialUpgrader, createXFactory, salt) ); } From d8e72cb1bfa291cd03d0def833bdcf680dc380b3 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Wed, 5 Nov 2025 19:03:05 +0100 Subject: [PATCH 20/46] style: Update workflow to manage contract roles via Safe multisig with improved transaction handling --- .../manage-token-bridge-role-safe.yml | 109 +++++++++++++----- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-token-bridge-role-safe.yml index a70f29c9..a7e476af 100644 --- a/.github/workflows/manage-token-bridge-role-safe.yml +++ b/.github/workflows/manage-token-bridge-role-safe.yml @@ -1,4 +1,4 @@ -name: Manage Token Bridge Role via Safe Multisig +name: Manage Contract Roles via Safe Multisig on: workflow_dispatch: @@ -10,6 +10,15 @@ on: 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 @@ -20,8 +29,8 @@ on: - sepolia - arbitrum_sepolia default: sepolia - bridge_address: - description: 'Bridge address to grant/revoke role to/from' + target_address: + description: 'Address to grant/revoke role to/from' required: true type: string dry_run: @@ -35,9 +44,8 @@ jobs: runs-on: ubuntu-latest environment: ${{ inputs.network }} outputs: - transaction-data: ${{ steps.prepare.outputs.transaction-data }} + transactions: ${{ steps.prepare.outputs.transactions }} safe-address: ${{ steps.prepare.outputs.safe-address }} - target-contract: ${{ steps.prepare.outputs.target-contract }} steps: - name: Checkout repository @@ -55,33 +63,60 @@ jobs: id: prepare env: CHAIN: ${{ inputs.network }} + ROLE_NAME: ${{ inputs.role }} run: | - # Read config to determine which contract to use + # 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 - TARGET_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcLiquidityUnifierAddress" config/config.json) + TOKEN_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcLiquidityUnifierAddress" config/config.json) else - TARGET_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcCrosschainTokenAddress" config/config.json) + TOKEN_CONTRACT=$(jq -r ".chains.${CHAIN}.rlcCrosschainTokenAddress" config/config.json) fi - echo "target-contract=$TARGET_CONTRACT" >> $GITHUB_OUTPUT - - # Calculate TOKEN_BRIDGE_ROLE = keccak256("TOKEN_BRIDGE_ROLE") - ROLE_HASH=$(cast keccak "TOKEN_BRIDGE_ROLE") + # Calculate role hash: keccak256("ROLE_NAME") + ROLE_HASH=$(cast keccak "$ROLE_NAME") # Determine the function selector and encode calldata if [ "${{ inputs.operation }}" = "grant" ]; then - # grantRole(bytes32 role, address account) - TRANSACTION_DATA=$(cast calldata "grantRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") + TRANSACTION_DATA=$(cast calldata "grantRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}") FUNCTION_NAME="grantRole(bytes32,address)" else - # revokeRole(bytes32 role, address account) - TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.bridge_address }}") + TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}") FUNCTION_NAME="revokeRole(bytes32,address)" fi - echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT + # 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, "value": "0", "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, "value": "0", "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, "value": "0", "data": $data, "contract": "token"}]') + # Add transaction for bridge contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "value": "0", "data": $data, "contract": "bridge"}]') + ;; + *) + echo "❌ Error: Unknown role $ROLE_NAME" + exit 1 + ;; + esac + + echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification @@ -90,6 +125,7 @@ jobs: echo "==========================================" echo "Workflow Configuration:" echo " • Network: ${{ inputs.network }}" + echo " • Role: $ROLE_NAME" echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" @@ -97,25 +133,40 @@ jobs: echo "" echo "────────────────────────────────────────────────────────────────────────────────" echo "" - echo "Transaction Details:" - echo " • Target: $TARGET_CONTRACT" - echo " • Bridge: ${{ inputs.bridge_address }}" - echo " • Role Hash: $ROLE_HASH" - echo " • Value: 0 ETH" - echo " • Data: $TRANSACTION_DATA" - 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 prepared successfully" - echo "ℹ️ This transaction would be proposed to Safe multisig" + 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: needs: prepare-role-operation if: ${{ inputs.dry_run == false }} + strategy: + matrix: + transaction: ${{ fromJson(needs.prepare-role-operation.outputs.transactions) }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} @@ -123,6 +174,6 @@ jobs: with: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-role-operation.outputs.safe-address }} - transaction-to: ${{ needs.prepare-role-operation.outputs.target-contract }} - transaction-value: '0' - transaction-data: ${{ needs.prepare-role-operation.outputs.transaction-data }} + transaction-to: ${{ matrix.transaction.to }} + transaction-value: ${{ matrix.transaction.value }} + transaction-data: ${{ matrix.transaction.data }} From 4451f86920df6ff23c00aa47b9e66ce180389b1f Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Wed, 5 Nov 2025 19:03:34 +0100 Subject: [PATCH 21/46] feat: Add workflow for managing contract roles via Safe multisig with transaction preparation --- ...-token-bridge-role-safe.yml => manage-contract-roles-safe.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{manage-token-bridge-role-safe.yml => manage-contract-roles-safe.yml} (100%) diff --git a/.github/workflows/manage-token-bridge-role-safe.yml b/.github/workflows/manage-contract-roles-safe.yml similarity index 100% rename from .github/workflows/manage-token-bridge-role-safe.yml rename to .github/workflows/manage-contract-roles-safe.yml From e2d1d4fdd2197177981837fc103360a115f12a07 Mon Sep 17 00:00:00 2001 From: gfournieriExec <100280020+gfournieriExec@users.noreply.github.com> Date: Wed, 5 Nov 2025 19:25:11 +0100 Subject: [PATCH 22/46] fix: Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40a80c8a..89334101 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ All critical administrative operations are secured using Safe (Gnosis Safe) mult ### GitHub Actions Workflows - `.github/workflows/bridge-pause-safe.yml` - Propose pause/unpause transactions -- `.github/workflows/manage-token-bridge-role-safe.yml` - Propose role management transactions +- `.github/workflows/manage-contract-roles-safe.yml` - Propose role management transactions - `.github/workflows/transfer-admin-role-safe.yml` - Propose admin role transfer transactions All workflows use the reusable Safe multisig workflow from [iExecBlockchainComputing/github-actions-workflows](https://github.com/iExecBlockchainComputing/github-actions-workflows). From 3d691cef93906bd298273bc0fc86749d3dd3fbb6 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 12:01:01 +0100 Subject: [PATCH 23/46] refactor: Rename job steps to standardize transaction calldata preparation across workflows --- .github/workflows/bridge-pause-safe.yml | 12 +++++----- .../workflows/manage-contract-roles-safe.yml | 10 ++++---- .../workflows/transfer-admin-role-safe.yml | 24 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index fff2f1d5..42cf1b69 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -29,7 +29,7 @@ on: default: true jobs: - prepare-pause-operation: + prepare-transaction-calldata: runs-on: ubuntu-latest environment: ${{ inputs.network }} outputs: @@ -49,7 +49,7 @@ jobs: version: stable cache: true - - name: Prepare pause/unpause transaction calldata + - name: Prepare transaction calldata id: prepare env: CHAIN: ${{ inputs.network }} @@ -113,7 +113,7 @@ jobs: fi propose-to-safe: - needs: prepare-pause-operation + needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: @@ -121,7 +121,7 @@ jobs: safe-api-key: ${{ secrets.SAFE_API_KEY }} with: rpc-url: ${{ github.secrets.RPC_URL }} - safe-address: ${{ needs.prepare-pause-operation.outputs.safe-address }} - transaction-to: ${{ needs.prepare-pause-operation.outputs.bridge-address }} + safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} + transaction-to: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }} transaction-value: '0' - transaction-data: ${{ needs.prepare-pause-operation.outputs.transaction-data }} + transaction-data: ${{ needs.prepare-transaction-calldata.outputs.transaction-data }} diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index a7e476af..1e4b4bf0 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -40,7 +40,7 @@ on: default: true jobs: - prepare-role-operation: + prepare-transaction-calldata: runs-on: ubuntu-latest environment: ${{ inputs.network }} outputs: @@ -59,7 +59,7 @@ jobs: version: stable cache: true - - name: Prepare role management transaction calldata + - name: Prepare transaction calldata id: prepare env: CHAIN: ${{ inputs.network }} @@ -162,18 +162,18 @@ jobs: fi propose-to-safe: - needs: prepare-role-operation + needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} strategy: matrix: - transaction: ${{ fromJson(needs.prepare-role-operation.outputs.transactions) }} + transaction: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions) }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} safe-api-key: ${{ secrets.SAFE_API_KEY }} with: rpc-url: ${{ github.secrets.RPC_URL }} - safe-address: ${{ needs.prepare-role-operation.outputs.safe-address }} + safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ matrix.transaction.to }} transaction-value: ${{ matrix.transaction.value }} transaction-data: ${{ matrix.transaction.data }} diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index e769d0c6..0d596c8f 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -31,7 +31,7 @@ on: default: true jobs: - prepare-admin-transfer: + prepare-transaction-calldata: runs-on: ubuntu-latest environment: ${{ inputs.network }} outputs: @@ -56,7 +56,7 @@ jobs: echo "Error: new_admin_address is required for begin-transfer operation" exit 1 - - name: Prepare admin transfer transaction calldata + - name: Prepare transaction calldata id: prepare env: CHAIN: ${{ inputs.network }} @@ -136,7 +136,7 @@ jobs: fi propose-token-contract-to-safe: - needs: prepare-admin-transfer + needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: @@ -144,13 +144,13 @@ jobs: safe-api-key: ${{ secrets.SAFE_API_KEY }} with: rpc-url: ${{ github.secrets.RPC_URL }} - safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} - transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].to }} - transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].value }} - transaction-data: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[0].data }} + safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} + transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].to }} + transaction-value: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].value }} + transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].data }} propose-bridge-to-safe: - needs: prepare-admin-transfer + needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: @@ -158,7 +158,7 @@ jobs: safe-api-key: ${{ secrets.SAFE_API_KEY }} with: rpc-url: ${{ github.secrets.RPC_URL }} - safe-address: ${{ needs.prepare-admin-transfer.outputs.safe-address }} - transaction-to: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].to }} - transaction-value: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].value }} - transaction-data: ${{ fromJson(needs.prepare-admin-transfer.outputs.transactions)[1].data }} + safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} + transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].to }} + transaction-value: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].value }} + transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].data }} From beca9f73fe189432c13d616030bde5c7f745361f Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 12:03:17 +0100 Subject: [PATCH 24/46] refactor: Remove transaction value from multisig transaction preparation across workflows --- .github/workflows/bridge-pause-safe.yml | 1 - .github/workflows/manage-contract-roles-safe.yml | 9 ++++----- .github/workflows/transfer-admin-role-safe.yml | 10 ++++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 42cf1b69..bacad9a1 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -123,5 +123,4 @@ jobs: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }} - transaction-value: '0' transaction-data: ${{ needs.prepare-transaction-calldata.outputs.transaction-data }} diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index 1e4b4bf0..d78e6a92 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -94,21 +94,21 @@ jobs: 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, "value": "0", "data": $data, "contract": "token"}]') + '. += [{"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, "value": "0", "data": $data, "contract": "bridge"}]') + '. += [{"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, "value": "0", "data": $data, "contract": "token"}]') + '. += [{"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, "value": "0", "data": $data, "contract": "bridge"}]') + '. += [{"to": $to, "data": $data, "contract": "bridge"}]') ;; *) echo "❌ Error: Unknown role $ROLE_NAME" @@ -175,5 +175,4 @@ jobs: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ matrix.transaction.to }} - transaction-value: ${{ matrix.transaction.value }} transaction-data: ${{ matrix.transaction.data }} diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 0d596c8f..fbe2cfd1 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -81,11 +81,11 @@ jobs: # Create transaction for token contract TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "value": "0", "data": $data}]') + '. += [{"to": $to, "data": $data}]') # Create transaction for bridge contract TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "value": "0", "data": $data}]') + '. += [{"to": $to, "data": $data}]') else # acceptDefaultAdminTransfer() TRANSACTION_DATA=$(cast calldata "acceptDefaultAdminTransfer()") @@ -93,11 +93,11 @@ jobs: # Create transaction for token contract TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "value": "0", "data": $data}]') + '. += [{"to": $to, "data": $data}]') # Create transaction for bridge contract TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "value": "0", "data": $data}]') + '. += [{"to": $to, "data": $data}]') fi echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT @@ -146,7 +146,6 @@ jobs: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].to }} - transaction-value: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].value }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].data }} propose-bridge-to-safe: @@ -160,5 +159,4 @@ jobs: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].to }} - transaction-value: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].value }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].data }} From f0b098a00bc189f9503e86874c5c926e71b659c7 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 12:17:42 +0100 Subject: [PATCH 25/46] refactor: Rename propose-to-safe jobs to propose-to-safe-tx for consistency across workflows --- .github/workflows/bridge-pause-safe.yml | 2 +- .github/workflows/manage-contract-roles-safe.yml | 2 +- .github/workflows/transfer-admin-role-safe.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index bacad9a1..15d66cb7 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -112,7 +112,7 @@ jobs: echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" fi - propose-to-safe: + propose-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index d78e6a92..90b5f5a0 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -161,7 +161,7 @@ jobs: echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" fi - propose-to-safe: + propose-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} strategy: diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index fbe2cfd1..599daed1 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -148,7 +148,7 @@ jobs: transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].to }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].data }} - propose-bridge-to-safe: + propose-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main From 3c3f6dcef7ff118767b5dac451dcf60c7ca3effe Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 12:25:20 +0100 Subject: [PATCH 26/46] refactor: Replace if-else with case statement for operation handling in transaction preparation --- .../workflows/manage-contract-roles-safe.yml | 21 ++++--- .../workflows/transfer-admin-role-safe.yml | 58 ++++++++++--------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index 90b5f5a0..ca8c39ad 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -79,13 +79,20 @@ jobs: ROLE_HASH=$(cast keccak "$ROLE_NAME") # Determine the function selector and encode calldata - if [ "${{ inputs.operation }}" = "grant" ]; then - TRANSACTION_DATA=$(cast calldata "grantRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}") - FUNCTION_NAME="grantRole(bytes32,address)" - else - TRANSACTION_DATA=$(cast calldata "revokeRole(bytes32,address)" "$ROLE_HASH" "${{ inputs.target_address }}") - FUNCTION_NAME="revokeRole(bytes32,address)" - fi + 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='[]' diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 599daed1..a3e76b59 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -73,32 +73,38 @@ jobs: # Prepare transactions array for both contracts TRANSACTIONS='[]' - - if [ "${{ inputs.operation }}" = "begin-transfer" ]; then - # beginDefaultAdminTransfer(address newAdmin) - TRANSACTION_DATA=$(cast calldata "beginDefaultAdminTransfer(address)" "${{ inputs.new_admin_address }}") - FUNCTION_NAME="beginDefaultAdminTransfer(address)" - - # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - - # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - else - # acceptDefaultAdminTransfer() - TRANSACTION_DATA=$(cast calldata "acceptDefaultAdminTransfer()") - FUNCTION_NAME="acceptDefaultAdminTransfer()" - - # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - - # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - fi + case "${{ inputs.operation }}" in + begin-transfer) + # beginDefaultAdminTransfer(address newAdmin) + TRANSACTION_DATA=$(cast calldata "beginDefaultAdminTransfer(address)" "${{ inputs.new_admin_address }}") + FUNCTION_NAME="beginDefaultAdminTransfer(address)" + + # Create transaction for token contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "data": $data}]') + + # Create transaction for bridge contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "data": $data}]') + ;; + accept-transfer) + # acceptDefaultAdminTransfer() + TRANSACTION_DATA=$(cast calldata "acceptDefaultAdminTransfer()") + FUNCTION_NAME="acceptDefaultAdminTransfer()" + + # Create transaction for token contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "data": $data}]') + + # Create transaction for bridge contract + TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ + '. += [{"to": $to, "data": $data}]') + ;; + *) + echo "❌ Error: Unknown operation ${{ inputs.operation }}" + exit 1 + ;; + esac echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT From e37f2cbba492e81a97dfd936dc07d777f3aee206 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 16:00:05 +0100 Subject: [PATCH 27/46] refactor: Rename propose-token-contract-to-safe job to propose-token-to-safe-tx for consistency refactor: Rename propose-to-safe-tx job to propose-bridge-to-safe-tx for clarity --- .github/workflows/transfer-admin-role-safe.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index a3e76b59..4bc05e7d 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -141,7 +141,7 @@ jobs: echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" fi - propose-token-contract-to-safe: + propose-token-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main @@ -154,7 +154,7 @@ jobs: transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].to }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].data }} - propose-to-safe-tx: + propose-bridge-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main From 2f36ceeecfd7a94e630c78e8a5f3c27e83fe5478 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Fri, 7 Nov 2025 16:03:21 +0100 Subject: [PATCH 28/46] refactor: Consolidate propose-to-safe jobs and implement matrix strategy for transaction handling --- .../workflows/transfer-admin-role-safe.yml | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 4bc05e7d..0eecf291 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -141,9 +141,17 @@ jobs: echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" fi - propose-token-to-safe-tx: + propose-to-safe-tx: needs: prepare-transaction-calldata if: ${{ inputs.dry_run == false }} + strategy: + matrix: + transaction-index: [0, 1] + include: + - transaction-index: 0 + contract-name: token + - transaction-index: 1 + contract-name: bridge uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} @@ -151,18 +159,5 @@ jobs: with: rpc-url: ${{ github.secrets.RPC_URL }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} - transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].to }} - transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[0].data }} - - propose-bridge-to-safe-tx: - needs: prepare-transaction-calldata - if: ${{ inputs.dry_run == false }} - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main - secrets: - safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} - safe-api-key: ${{ secrets.SAFE_API_KEY }} - with: - rpc-url: ${{ github.secrets.RPC_URL }} - safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} - transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].to }} - transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[1].data }} + transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].to }} + transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].data }} From af37061b5917b11e4ab0990538e1e818de6a7ebb Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 09:42:40 +0100 Subject: [PATCH 29/46] refactor: Remove dry_run condition from propose-to-safe-tx jobs and pass dry_run input to steps --- .github/workflows/bridge-pause-safe.yml | 2 +- .github/workflows/manage-contract-roles-safe.yml | 2 +- .github/workflows/transfer-admin-role-safe.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 15d66cb7..1ec6f021 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -114,7 +114,6 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata - if: ${{ inputs.dry_run == false }} uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main secrets: safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} @@ -124,3 +123,4 @@ jobs: 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 }} diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index ca8c39ad..ae5e403d 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -170,7 +170,6 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata - if: ${{ inputs.dry_run == false }} strategy: matrix: transaction: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions) }} @@ -183,3 +182,4 @@ jobs: safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ matrix.transaction.to }} transaction-data: ${{ matrix.transaction.data }} + dry-run: ${{ inputs.dry_run }} diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 0eecf291..f9c87386 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -143,7 +143,6 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata - if: ${{ inputs.dry_run == false }} strategy: matrix: transaction-index: [0, 1] @@ -161,3 +160,4 @@ jobs: safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].to }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].data }} + dry-run: ${{ inputs.dry_run }} From 4c8c2dc3269ad4bfb38f91336393ed2da802ac5e Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 10:16:30 +0100 Subject: [PATCH 30/46] refactor: Standardize dry-run input naming across workflow files --- .github/workflows/bridge-pause-safe.yml | 10 +++++----- .github/workflows/manage-contract-roles-safe.yml | 10 +++++----- .github/workflows/transfer-admin-role-safe.yml | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 1ec6f021..355e4c7b 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -22,7 +22,7 @@ on: - sepolia - arbitrum_sepolia default: sepolia - dry_run: + dry-run: description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)' required: false type: boolean @@ -94,7 +94,7 @@ jobs: echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" - echo " • Dry Run: ${{ inputs.dry_run }}" + echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" echo "" @@ -106,10 +106,10 @@ jobs: echo "────────────────────────────────────────────────────────────────────────────────" echo "" - if [ "${{ inputs.dry_run }}" == "true" ]; then + if [ "${{ inputs.dry-run }}" == "true" ]; then echo "✅ DRY RUN MODE: Transaction prepared successfully" echo "ℹ️ This transaction would be proposed to Safe multisig" - echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" + echo "ℹ️ Re-run with dry-run=false to actually propose to Safe" fi propose-to-safe-tx: @@ -123,4 +123,4 @@ jobs: 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 }} + dry-run: ${{ inputs.dry-run }} diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index ae5e403d..9c53f7ca 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -33,7 +33,7 @@ on: description: 'Address to grant/revoke role to/from' required: true type: string - dry_run: + dry-run: description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)' required: false type: boolean @@ -136,7 +136,7 @@ jobs: echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" - echo " • Dry Run: ${{ inputs.dry_run }}" + echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" echo "" @@ -162,10 +162,10 @@ jobs: echo "────────────────────────────────────────────────────────────────────────────────" echo "" - if [ "${{ inputs.dry_run }}" == "true" ]; then + 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" + echo "ℹ️ Re-run with dry-run=false to actually propose to Safe" fi propose-to-safe-tx: @@ -182,4 +182,4 @@ jobs: safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ matrix.transaction.to }} transaction-data: ${{ matrix.transaction.data }} - dry-run: ${{ inputs.dry_run }} + dry-run: ${{ inputs.dry-run }} diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index f9c87386..505f3601 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -24,7 +24,7 @@ on: description: 'New admin address (required for begin-transfer)' required: false type: string - dry_run: + dry-run: description: 'Dry run mode (only prepare and display transaction, do not propose to Safe)' required: false type: boolean @@ -118,7 +118,7 @@ jobs: echo " • Function: $FUNCTION_NAME" echo " • New Admin: ${{ inputs.new_admin_address }}" echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" - echo " • Dry Run: ${{ inputs.dry_run }}" + echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" echo "" @@ -135,10 +135,10 @@ jobs: echo "────────────────────────────────────────────────────────────────────────────────" echo "" - if [ "${{ inputs.dry_run }}" == "true" ]; then + if [ "${{ inputs.dry-run }}" == "true" ]; then echo "✅ DRY RUN MODE: Transactions prepared successfully" echo "ℹ️ These transactions would be proposed to Safe multisig" - echo "ℹ️ Re-run with dry_run=false to actually propose to Safe" + echo "ℹ️ Re-run with dry-run=false to actually propose to Safe" fi propose-to-safe-tx: @@ -160,4 +160,4 @@ jobs: safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].to }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].data }} - dry-run: ${{ inputs.dry_run }} + dry-run: ${{ inputs.dry-run }} From 20fb6ea47c0a2c85213d41202842465b42d1f0bc Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 10:27:05 +0100 Subject: [PATCH 31/46] refactor: Update safe address reference from secrets to vars in transaction output --- .github/workflows/bridge-pause-safe.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 355e4c7b..3c7060a1 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -83,7 +83,7 @@ jobs: esac echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT - echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" @@ -93,7 +93,7 @@ jobs: echo " • Network: ${{ inputs.network }}" echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" - echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}" echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" From 92c5aae586209ebb47b31971c5d17c274a27e16a Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 10:44:08 +0100 Subject: [PATCH 32/46] refactor: Add rpc-url output to prepare-transaction-calldata job and update propose-to-safe-tx job to use it --- .github/workflows/bridge-pause-safe.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 3c7060a1..0db56528 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -36,6 +36,7 @@ jobs: transaction-data: ${{ steps.prepare.outputs.transaction-data }} safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} + rpc-url: ${{ steps.prepare.outputs.rpc-url }} steps: - name: Checkout repository @@ -84,6 +85,7 @@ jobs: echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + echo "rpc-url=${{ secrets.RPC_URL }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" @@ -115,11 +117,9 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main - secrets: - safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} - safe-api-key: ${{ secrets.SAFE_API_KEY }} + secrets: inherit with: - rpc-url: ${{ github.secrets.RPC_URL }} + rpc-url: ${{ needs.prepare-transaction-calldata.outputs.rpc-url }} 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 }} From d6ad7341d9a85a6c4a41c70630b90a164cb35354 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 10:47:27 +0100 Subject: [PATCH 33/46] refactor: Add safe-proposer-private-key and safe-api-key outputs to prepare-transaction-calldata job --- .github/workflows/bridge-pause-safe.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 0db56528..3748429c 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -37,6 +37,8 @@ jobs: safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} rpc-url: ${{ steps.prepare.outputs.rpc-url }} + safe-proposer-private-key: ${{ steps.prepare.outputs.safe-proposer-private-key }} + safe-api-key: ${{ steps.prepare.outputs.safe-api-key }} steps: - name: Checkout repository @@ -86,6 +88,8 @@ jobs: echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT echo "rpc-url=${{ secrets.RPC_URL }}" >> $GITHUB_OUTPUT + echo "safe-proposer-private-key=${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }}" >> $GITHUB_OUTPUT + echo "safe-api-key=${{ secrets.SAFE_API_KEY }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" @@ -117,7 +121,9 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main - secrets: inherit + secrets: + safe-proposer-private-key: ${{ needs.prepare-transaction-calldata.outputs.safe-proposer-private-key }} + safe-api-key: ${{ needs.prepare-transaction-calldata.outputs.safe-api-key }} with: rpc-url: ${{ needs.prepare-transaction-calldata.outputs.rpc-url }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} From 0c4826ea668e081f0904dc13b7779cfdf24b46ea Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 10:50:26 +0100 Subject: [PATCH 34/46] refactor: Update outputs in bridge-pause-safe workflow to use secrets instead of steps --- .github/workflows/bridge-pause-safe.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 3748429c..28a53408 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -36,9 +36,9 @@ jobs: transaction-data: ${{ steps.prepare.outputs.transaction-data }} safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} - rpc-url: ${{ steps.prepare.outputs.rpc-url }} - safe-proposer-private-key: ${{ steps.prepare.outputs.safe-proposer-private-key }} - safe-api-key: ${{ steps.prepare.outputs.safe-api-key }} + rpc-url: ${{ secrets.RPC_URL }} + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} steps: - name: Checkout repository @@ -87,9 +87,6 @@ jobs: echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT - echo "rpc-url=${{ secrets.RPC_URL }}" >> $GITHUB_OUTPUT - echo "safe-proposer-private-key=${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }}" >> $GITHUB_OUTPUT - echo "safe-api-key=${{ secrets.SAFE_API_KEY }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" From 7720b82173f18e87c042a71f726b9acd8bde2f88 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Thu, 13 Nov 2025 11:04:35 +0100 Subject: [PATCH 35/46] refactor: Update safe address reference from outputs to vars in propose-to-safe-tx job --- .github/workflows/bridge-pause-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 28a53408..24968ac7 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -123,7 +123,7 @@ jobs: safe-api-key: ${{ needs.prepare-transaction-calldata.outputs.safe-api-key }} with: rpc-url: ${{ needs.prepare-transaction-calldata.outputs.rpc-url }} - safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} + safe-address: ${{ vars.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 }} From 3a100761505ec3d57e4ea589ac5565e3aaab1602 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 09:11:34 +0100 Subject: [PATCH 36/46] refactor: Update safe address reference in propose-to-safe-tx job to use outputs instead of vars --- .github/workflows/bridge-pause-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 24968ac7..28a53408 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -123,7 +123,7 @@ jobs: safe-api-key: ${{ needs.prepare-transaction-calldata.outputs.safe-api-key }} with: rpc-url: ${{ needs.prepare-transaction-calldata.outputs.rpc-url }} - safe-address: ${{ vars.SAFE_ADDRESS}} + 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 }} From 250ba30191e0529f25d0877cad1b1b47a5271a72 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 09:31:50 +0100 Subject: [PATCH 37/46] refactor: Update propose-to-safe-tx job to use secrets for RPC and API keys --- .github/workflows/bridge-pause-safe.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 28a53408..7cb7aef6 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -36,9 +36,6 @@ jobs: transaction-data: ${{ steps.prepare.outputs.transaction-data }} safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} - rpc-url: ${{ secrets.RPC_URL }} - safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} - safe-api-key: ${{ secrets.SAFE_API_KEY }} steps: - name: Checkout repository @@ -117,12 +114,12 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@fix/multisig-rpc-secrets secrets: - safe-proposer-private-key: ${{ needs.prepare-transaction-calldata.outputs.safe-proposer-private-key }} - safe-api-key: ${{ needs.prepare-transaction-calldata.outputs.safe-api-key }} + safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + safe-api-key: ${{ secrets.SAFE_API_KEY }} + rpc-url: ${{ secrets.RPC_URL }} with: - rpc-url: ${{ needs.prepare-transaction-calldata.outputs.rpc-url }} 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 }} From 4902dc3a5ad43ca30078c1a145a36570d710aa63 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 09:54:18 +0100 Subject: [PATCH 38/46] refactor: Update propose-to-safe-tx job to inherit secrets for improved security --- .github/workflows/bridge-pause-safe.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 7cb7aef6..815c9b8c 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -115,10 +115,7 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@fix/multisig-rpc-secrets - secrets: - safe-proposer-private-key: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} - safe-api-key: ${{ secrets.SAFE_API_KEY }} - rpc-url: ${{ secrets.RPC_URL }} + secrets: inherit with: safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }} From a36165ee2bda864f6b985c11caf3d3a6f224cc83 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 10:09:57 +0100 Subject: [PATCH 39/46] refactor: Enhance propose-to-safe-tx job to inherit environment secrets for improved security --- .github/workflows/bridge-pause-safe.yml | 35 ++++++++++++------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 815c9b8c..a338f130 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -36,6 +36,7 @@ jobs: transaction-data: ${{ steps.prepare.outputs.transaction-data }} safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} + # Note: We'll pass secrets through the next job steps: - name: Checkout repository @@ -61,22 +62,18 @@ jobs: # Determine the function selector and name based on operation case "${{ inputs.operation }}" in "pause-bridge") - # pause() function selector TRANSACTION_DATA=$(cast calldata "pause()") FUNCTION_NAME="pause()" ;; "unpause-bridge") - # unpause() function selector TRANSACTION_DATA=$(cast calldata "unpause()") FUNCTION_NAME="unpause()" ;; "pause-outbound") - # pauseOutboundTransfers() function selector TRANSACTION_DATA=$(cast calldata "pauseOutboundTransfers()") FUNCTION_NAME="pauseOutboundTransfers()" ;; "unpause-outbound") - # unpauseOutboundTransfers() function selector TRANSACTION_DATA=$(cast calldata "unpauseOutboundTransfers()") FUNCTION_NAME="unpauseOutboundTransfers()" ;; @@ -85,7 +82,7 @@ jobs: echo "transaction-data=$TRANSACTION_DATA" >> $GITHUB_OUTPUT echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT - # Display transaction details for dry-run or verification + # Display transaction details echo "==========================================" echo "Transaction Details" echo "==========================================" @@ -96,28 +93,30 @@ jobs: echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}" echo " • Dry Run: ${{ inputs.dry-run }}" echo "" - echo "────────────────────────────────────────────────────────────────────────────────" - echo "" echo "Transaction Details:" echo " • Target: $BRIDGE_ADDRESS" echo " • Value: 0 ETH" echo " • Data: $TRANSACTION_DATA" echo "" - echo "────────────────────────────────────────────────────────────────────────────────" - echo "" if [ "${{ inputs.dry-run }}" == "true" ]; then echo "✅ DRY RUN MODE: Transaction prepared successfully" - echo "ℹ️ This transaction would be proposed to Safe multisig" - echo "ℹ️ Re-run with dry-run=false to actually propose to Safe" fi + # ✅ NEW: Wrapper job that bridges the environment secrets to the reusable workflow propose-to-safe-tx: needs: prepare-transaction-calldata - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@fix/multisig-rpc-secrets - secrets: inherit - with: - 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 }} + runs-on: ubuntu-latest + environment: ${{ inputs.network }} # ✅ This gives access to environment secrets + steps: + - name: Call reusable workflow with secrets + uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@fix/multisig-rpc-secrets + with: + 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 }} + env: + RPC_URL: ${{ secrets.RPC_URL }} + SAFE_PROPOSER_PRIVATE_KEY: ${{ secrets.SAFE_PROPOSER_PRIVATE_KEY }} + SAFE_API_KEY: ${{ secrets.SAFE_API_KEY }} From 03db83245da5d708acd93a56cb27eb6e5a15877d Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 10:21:29 +0100 Subject: [PATCH 40/46] refactor: Update propose-to-safe-tx job to bridge environment secrets and enhance transaction proposal process --- .github/workflows/bridge-pause-safe.yml | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index a338f130..9f11f8df 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -36,7 +36,6 @@ jobs: transaction-data: ${{ steps.prepare.outputs.transaction-data }} safe-address: ${{ steps.prepare.outputs.safe-address }} bridge-address: ${{ steps.prepare.outputs.bridge-address }} - # Note: We'll pass secrets through the next job steps: - name: Checkout repository @@ -103,20 +102,36 @@ jobs: echo "✅ DRY RUN MODE: Transaction prepared successfully" fi - # ✅ NEW: Wrapper job that bridges the environment secrets to the reusable workflow propose-to-safe-tx: needs: prepare-transaction-calldata runs-on: ubuntu-latest - environment: ${{ inputs.network }} # ✅ This gives access to environment secrets + environment: ${{ inputs.network }} steps: - - name: Call reusable workflow with secrets - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@fix/multisig-rpc-secrets + - name: Checkout Safe proposal repository + uses: actions/checkout@v4 + with: + repository: iExecBlockchainComputing/github-actions-workflows + ref: fix/multisig-rpc-secrets + path: .github-actions + + - name: Setup Node.js + uses: actions/setup-node@v4 with: - 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 }} + node-version: '20' + + - name: Install dependencies + working-directory: .github-actions/propose-safe-multisig-tx + run: npm ci + + - name: Propose transaction to Safe + working-directory: .github-actions/propose-safe-multisig-tx env: + SAFE_ADDRESS: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} + TRANSACTION_TO: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }} + TRANSACTION_VALUE: "0" + TRANSACTION_DATA: ${{ needs.prepare-transaction-calldata.outputs.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 From ec71340bb0af1930db6932c023253b730debca3e Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 10:25:40 +0100 Subject: [PATCH 41/46] refactor: Update Node.js version in bridge-pause-safe workflow from 20 to 22 --- .github/workflows/bridge-pause-safe.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 9f11f8df..2aedc0b8 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -117,12 +117,16 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + 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: From e0bcbeaee903f033f47289329389f3772263da5e Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 11:00:17 +0100 Subject: [PATCH 42/46] refactor: Update checkout reference in bridge-pause-safe workflow from fix/multisig-rpc-secrets to main --- .github/workflows/bridge-pause-safe.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 2aedc0b8..82921762 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -111,7 +111,7 @@ jobs: uses: actions/checkout@v4 with: repository: iExecBlockchainComputing/github-actions-workflows - ref: fix/multisig-rpc-secrets + ref: main path: .github-actions - name: Setup Node.js From 05ea315a26f88ee3cd327d74b8711df5b03dc4d5 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 11:02:54 +0100 Subject: [PATCH 43/46] refactor: Update checkout step in bridge-pause-safe workflow to use specific repository path --- .github/workflows/bridge-pause-safe.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 82921762..503f5225 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -111,7 +111,6 @@ jobs: uses: actions/checkout@v4 with: repository: iExecBlockchainComputing/github-actions-workflows - ref: main path: .github-actions - name: Setup Node.js From d118bbc3bdd0f1be754f42285a9f4b5493bbc4ab Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 11:19:22 +0100 Subject: [PATCH 44/46] refactor: Simplify propose-to-safe-tx job by using reusable workflow and passing necessary inputs --- .github/workflows/bridge-pause-safe.yml | 45 +++-------- .../workflows/propose-safe-transaction.yml | 78 +++++++++++++++++++ 2 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/propose-safe-transaction.yml diff --git a/.github/workflows/bridge-pause-safe.yml b/.github/workflows/bridge-pause-safe.yml index 503f5225..d885c27d 100644 --- a/.github/workflows/bridge-pause-safe.yml +++ b/.github/workflows/bridge-pause-safe.yml @@ -104,37 +104,14 @@ jobs: propose-to-safe-tx: needs: prepare-transaction-calldata - 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: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} - TRANSACTION_TO: ${{ needs.prepare-transaction-calldata.outputs.bridge-address }} - TRANSACTION_VALUE: "0" - TRANSACTION_DATA: ${{ needs.prepare-transaction-calldata.outputs.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 + 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 }} diff --git a/.github/workflows/propose-safe-transaction.yml b/.github/workflows/propose-safe-transaction.yml new file mode 100644 index 00000000..f56ed1a4 --- /dev/null +++ b/.github/workflows/propose-safe-transaction.yml @@ -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 From a855f664f7bc25284e13374da5c10b053761c694 Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Tue, 18 Nov 2025 11:24:42 +0100 Subject: [PATCH 45/46] refactor: Update propose-to-safe-tx job to use vars for safe address and streamline transaction handling --- .github/workflows/manage-contract-roles-safe.yml | 11 ++++++----- .github/workflows/transfer-admin-role-safe.yml | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/manage-contract-roles-safe.yml b/.github/workflows/manage-contract-roles-safe.yml index 9c53f7ca..0f000955 100644 --- a/.github/workflows/manage-contract-roles-safe.yml +++ b/.github/workflows/manage-contract-roles-safe.yml @@ -119,12 +119,12 @@ jobs: ;; *) echo "❌ Error: Unknown role $ROLE_NAME" - exit 1 + '. += [{"to": $to, "data": $data, "contract": "bridge"}]') ;; esac echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT - echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" @@ -135,7 +135,7 @@ jobs: echo " • Role: $ROLE_NAME" echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" - echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}" echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" @@ -173,12 +173,13 @@ jobs: strategy: matrix: transaction: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions) }} - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + 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: - rpc-url: ${{ github.secrets.RPC_URL }} + network: ${{ inputs.network }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ matrix.transaction.to }} transaction-data: ${{ matrix.transaction.data }} diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml index 505f3601..d17e1159 100644 --- a/.github/workflows/transfer-admin-role-safe.yml +++ b/.github/workflows/transfer-admin-role-safe.yml @@ -102,12 +102,12 @@ jobs: ;; *) echo "❌ Error: Unknown operation ${{ inputs.operation }}" - exit 1 + '. += [{"to": $to, "data": $data}]') ;; esac echo "transactions=$(echo $TRANSACTIONS | jq -c .)" >> $GITHUB_OUTPUT - echo "safe-address=${{ secrets.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT + echo "safe-address=${{ vars.SAFE_ADDRESS }}" >> $GITHUB_OUTPUT # Display transaction details for dry-run or verification echo "==========================================" @@ -117,7 +117,7 @@ jobs: echo " • Operation: ${{ inputs.operation }}" echo " • Function: $FUNCTION_NAME" echo " • New Admin: ${{ inputs.new_admin_address }}" - echo " • Safe Address: ${{ secrets.SAFE_ADDRESS }}" + echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}" echo " • Dry Run: ${{ inputs.dry-run }}" echo "" echo "────────────────────────────────────────────────────────────────────────────────" @@ -151,12 +151,13 @@ jobs: contract-name: token - transaction-index: 1 contract-name: bridge - uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/propose-safe-multisig-tx.yml@main + 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: - rpc-url: ${{ github.secrets.RPC_URL }} + network: ${{ inputs.network }} safe-address: ${{ needs.prepare-transaction-calldata.outputs.safe-address }} transaction-to: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].to }} transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].data }} From 20f75c71393d6f6e62fc4787d99622cf10b114fd Mon Sep 17 00:00:00 2001 From: gfournieriExec Date: Wed, 10 Dec 2025 10:30:30 +0100 Subject: [PATCH 46/46] refactor: Remove transfer-admin-role-safe workflow as it is no longer needed --- .../workflows/transfer-admin-role-safe.yml | 164 ------------------ 1 file changed, 164 deletions(-) delete mode 100644 .github/workflows/transfer-admin-role-safe.yml diff --git a/.github/workflows/transfer-admin-role-safe.yml b/.github/workflows/transfer-admin-role-safe.yml deleted file mode 100644 index d17e1159..00000000 --- a/.github/workflows/transfer-admin-role-safe.yml +++ /dev/null @@ -1,164 +0,0 @@ -name: Transfer Admin Role via Safe Multisig - -on: - workflow_dispatch: - inputs: - operation: - description: 'Admin role operation' - required: true - type: choice - options: - - begin-transfer - - accept-transfer - network: - description: 'Network to perform operation on' - required: true - type: choice - options: - - ethereum - - arbitrum - - sepolia - - arbitrum_sepolia - default: sepolia - new_admin_address: - description: 'New admin address (required for begin-transfer)' - required: false - 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: Validate inputs - if: inputs.operation == 'begin-transfer' && inputs.new_admin_address == '' - run: | - echo "Error: new_admin_address is required for begin-transfer operation" - exit 1 - - - name: Prepare transaction calldata - id: prepare - env: - CHAIN: ${{ inputs.network }} - run: | - # Read contract addresses from 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 - - # Prepare transactions array for both contracts - TRANSACTIONS='[]' - case "${{ inputs.operation }}" in - begin-transfer) - # beginDefaultAdminTransfer(address newAdmin) - TRANSACTION_DATA=$(cast calldata "beginDefaultAdminTransfer(address)" "${{ inputs.new_admin_address }}") - FUNCTION_NAME="beginDefaultAdminTransfer(address)" - - # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - - # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - ;; - accept-transfer) - # acceptDefaultAdminTransfer() - TRANSACTION_DATA=$(cast calldata "acceptDefaultAdminTransfer()") - FUNCTION_NAME="acceptDefaultAdminTransfer()" - - # Create transaction for token contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$TOKEN_CONTRACT" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - - # Create transaction for bridge contract - TRANSACTIONS=$(echo "$TRANSACTIONS" | jq -c --arg to "$BRIDGE_ADDRESS" --arg data "$TRANSACTION_DATA" \ - '. += [{"to": $to, "data": $data}]') - ;; - *) - echo "❌ Error: Unknown operation ${{ inputs.operation }}" - '. += [{"to": $to, "data": $data}]') - ;; - 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 - echo "==========================================" - echo "Transaction Details" - echo "==========================================" - echo " • Network: ${{ inputs.network }}" - echo " • Operation: ${{ inputs.operation }}" - echo " • Function: $FUNCTION_NAME" - echo " • New Admin: ${{ inputs.new_admin_address }}" - echo " • Safe Address: ${{ vars.SAFE_ADDRESS }}" - echo " • Dry Run: ${{ inputs.dry-run }}" - echo "" - echo "────────────────────────────────────────────────────────────────────────────────" - echo "" - echo "Transaction #1 - Token Contract:" - echo " • Target: $TOKEN_CONTRACT" - echo " • Value: 0 ETH" - echo " • Data: $TRANSACTION_DATA" - echo "" - echo "Transaction #2 - Bridge Contract:" - echo " • Target: $BRIDGE_ADDRESS" - echo " • Value: 0 ETH" - echo " • Data: $TRANSACTION_DATA" - echo "" - echo "────────────────────────────────────────────────────────────────────────────────" - echo "" - - if [ "${{ inputs.dry-run }}" == "true" ]; then - echo "✅ DRY RUN MODE: Transactions prepared successfully" - echo "ℹ️ These transactions 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-index: [0, 1] - include: - - transaction-index: 0 - contract-name: token - - transaction-index: 1 - contract-name: bridge - 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: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].to }} - transaction-data: ${{ fromJson(needs.prepare-transaction-calldata.outputs.transactions)[matrix.transaction-index].data }} - dry-run: ${{ inputs.dry-run }}