packages/contracts-rfq/script/ConfigureFastBridge.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {FastBridge} from "../contracts/FastBridge.sol";
import {SynapseScript, stdJson} from "@synapsecns/solidity-devops/src/SynapseScript.sol";
// solhint-disable no-empty-blocks
contract ConfigureFastBridge is SynapseScript {
using stdJson for string;
string public constant NAME = "FastBridge";
FastBridge public fastBridge;
string public config;
/// @notice We include an empty "test" function so that this contract does not appear in the coverage report.
function testConfigureFastBridge() external {}
function run() external broadcastWithHooks {
configureFB(ENVIRONMENT_PROD);
}
function runTestnet() external broadcastWithHooks {
configureFB("testnet");
}
function configureFB(string memory environment) internal {
loadConfig(environment);
syncRole("governor", fastBridge.GOVERNOR_ROLE());
setChainGasAmount();
setProtocolFeeRate();
syncRole("guard", fastBridge.GUARD_ROLE());
syncRole("relayer", fastBridge.RELAYER_ROLE());
syncRole("refunder", fastBridge.REFUNDER_ROLE());
}
function loadConfig(string memory environment) internal {
config = readGlobalDeployConfig({contractName: NAME, environment: environment, revertIfNotFound: true});
fastBridge = FastBridge(getDeploymentAddress({contractName: NAME, revertIfNotFound: true}));
}
function setChainGasAmount() internal {
printLog("Setting chain gas amount");
uint256 chainGasAmount = config.readUint(".chainGasAmount");
string memory action = string.concat("set to ", vm.toString(chainGasAmount));
if (fastBridge.chainGasAmount() != chainGasAmount) {
fastBridge.setChainGasAmount(chainGasAmount);
printSuccessWithIndent(string.concat("Chain gas amount ", action));
} else {
printSkipWithIndent(string.concat("already ", action));
}
}
function setProtocolFeeRate() internal {
printLog("Setting protocol fee rate");
uint256 protocolFeeRate = config.readUint(".protocolFeeRate");
string memory action = string.concat("set to ", vm.toString(protocolFeeRate));
if (fastBridge.protocolFeeRate() != protocolFeeRate) {
fastBridge.setProtocolFeeRate(protocolFeeRate);
printSuccessWithIndent(string.concat("Protocol fee rate ", action));
} else {
printSkipWithIndent(string.concat("already ", action));
}
}
function syncRole(string memory roleName, bytes32 role) internal {
string memory roleNamePlural = string.concat(roleName, "s");
printLog(string.concat("Syncing ", roleNamePlural));
address[] memory members = config.readAddressArray(string.concat(".accounts.", roleNamePlural));
address[] memory existingMembers = getMembers(role);
// Remove members that are not in the config
uint256 removed = 0;
for (uint256 i = 0; i < existingMembers.length; i++) {
if (!contains(members, existingMembers[i])) {
fastBridge.revokeRole(role, existingMembers[i]);
printSuccessWithIndent(string.concat("Removed ", roleName, " ", vm.toString(existingMembers[i])));
++removed;
}
}
// Add members that are in the config but not in the contract
uint256 added = 0;
for (uint256 i = 0; i < members.length; i++) {
if (!contains(existingMembers, members[i])) {
fastBridge.grantRole(role, members[i]);
printSuccessWithIndent(string.concat("Added ", roleName, " ", vm.toString(members[i])));
++added;
}
}
if (added + removed == 0) {
printSkipWithIndent(string.concat(roleNamePlural, " are up to date"));
} else {
printLog(
string.concat("Added ", vm.toString(added), " and removed ", vm.toString(removed), " ", roleNamePlural)
);
}
}
function getMembers(bytes32 role) internal view returns (address[] memory members) {
uint256 count = fastBridge.getRoleMemberCount(role);
members = new address[](count);
for (uint256 i = 0; i < count; i++) {
members[i] = fastBridge.getRoleMember(role, i);
}
}
function contains(address[] memory array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) return true;
}
return false;
}
}