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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions soljson-latest.js

Large diffs are not rendered by default.

13 changes: 3 additions & 10 deletions src/UniversalBridgeProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ contract UniversalBridgeProxy {
bytes32 private constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

constructor(
address _implementation,
address _owner,
address _operator,
address payable _protocolFeeRecipient,
uint256 _protocolFeeBps
) {
constructor(address _implementation, address _owner, address _operator, address payable _protocolFeeRecipient) {
if (_implementation == address(0)) {
revert ImplementationZeroAddress();
}
Expand All @@ -34,11 +28,10 @@ contract UniversalBridgeProxy {
}

bytes memory data = abi.encodeWithSignature(
"initialize(address,address,address,uint256)",
"initialize(address,address,address)",
_owner,
_operator,
_protocolFeeRecipient,
_protocolFeeBps
_protocolFeeRecipient
);
(bool success, ) = _implementation.delegatecall(data);

Expand Down
73 changes: 34 additions & 39 deletions src/UniversalBridgeV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ library UniversalBridgeStorage {
mapping(bytes32 => bool) processed;
/// @dev Mapping from forward address or token address => whether restricted.
mapping(address => bool) isRestricted;
/// @dev protocol fee bps, capped at 300 bps (3%)
uint256 protocolFeeBps;
/// @dev protocol fee recipient address
address protocolFeeRecipient;
/// @dev whether the bridge is paused
Expand Down Expand Up @@ -56,6 +54,7 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
address payable forwardAddress;
address payable spenderAddress;
uint256 expirationTimestamp;
uint256 protocolFeeBps;
address payable developerFeeRecipient;
uint256 developerFeeBps;
bytes callData;
Expand All @@ -64,7 +63,7 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol

bytes32 private constant TRANSACTION_REQUEST_TYPEHASH =
keccak256(
"TransactionRequest(bytes32 transactionId,address tokenAddress,uint256 tokenAmount,address forwardAddress,address spenderAddress,uint256 expirationTimestamp,address developerFeeRecipient,uint256 developerFeeBps,bytes callData,bytes extraData)"
"TransactionRequest(bytes32 transactionId,address tokenAddress,uint256 tokenAmount,address forwardAddress,address spenderAddress,uint256 expirationTimestamp,uint256 protocolFeeBps,address developerFeeRecipient,uint256 developerFeeBps,bytes callData,bytes extraData)"
);

/*///////////////////////////////////////////////////////////////
Expand All @@ -76,10 +75,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
bytes32 indexed transactionId,
address tokenAddress,
uint256 tokenAmount,
uint256 protocolFee,
address developerFeeRecipient,
uint256 developerFeeBps,
uint256 developerFee,
uint256 protocolFee,
bytes extraData
);

Expand All @@ -96,22 +95,17 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
error UniversalBridgePaused(); // 0x46ecd2c9
error UniversalBridgeRestrictedAddress(); // 0xec7d39b3
error UniversalBridgeVerificationFailed(); // 0x1573f645
error UniversalBridgeRequestExpired(uint256 expirationTimestamp); // 0xb4ebecd8
error UniversalBridgeTransactionAlreadyProcessed(); // 0x7d21ae4b
error UniversalBridgeRequestExpired(uint256 expirationTimestamp); // 0xb4ebecd8

constructor() {
_disableInitializers();
}

function initialize(
address _owner,
address _operator,
address payable _protocolFeeRecipient,
uint256 _protocolFeeBps
) external initializer {
function initialize(address _owner, address _operator, address payable _protocolFeeRecipient) external initializer {
_initializeOwner(_owner);
_grantRoles(_operator, _OPERATOR_ROLE);
_setProtocolFeeInfo(_protocolFeeRecipient, _protocolFeeBps);
_setProtocolFeeInfo(_protocolFeeRecipient);
}

/*///////////////////////////////////////////////////////////////
Expand All @@ -136,8 +130,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
}
}

function setProtocolFeeInfo(address payable feeRecipient, uint256 feeBps) external onlyOwner {
_setProtocolFeeInfo(feeRecipient, feeBps);
function setProtocolFeeInfo(address payable feeRecipient) external onlyOwner {
_setProtocolFeeInfo(feeRecipient);
}

function pause(bool _pause) external onlyOwner {
Expand All @@ -148,9 +142,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
_universalBridgeStorage().isRestricted[_target] = _restrict;
}

function getProtocolFeeInfo() external view returns (address feeRecipient, uint256 feeBps) {
function getProtocolFeeInfo() external view returns (address feeRecipient) {
feeRecipient = _universalBridgeStorage().protocolFeeRecipient;
feeBps = _universalBridgeStorage().protocolFeeBps;
}

function isPaused() external view returns (bool) {
Expand Down Expand Up @@ -188,6 +181,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
revert UniversalBridgeRestrictedAddress();
}

if (req.protocolFeeBps > MAX_PROTOCOL_FEE_BPS) {
revert UniversalBridgeInvalidFeeBps();
}

// verify amount
if (req.tokenAmount == 0) {
revert UniversalBridgeInvalidAmount(req.tokenAmount);
Expand All @@ -200,7 +197,8 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
req.tokenAddress,
req.tokenAmount,
req.developerFeeRecipient,
req.developerFeeBps
req.developerFeeBps,
req.protocolFeeBps
);

if (_isNativeToken(req.tokenAddress)) {
Expand Down Expand Up @@ -251,10 +249,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
req.transactionId,
req.tokenAddress,
req.tokenAmount,
protocolFee,
req.developerFeeRecipient,
req.developerFeeBps,
developerFee,
protocolFee,
req.extraData
);
}
Expand Down Expand Up @@ -293,18 +291,20 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
}

bytes32 structHash = keccak256(
abi.encode(
TRANSACTION_REQUEST_TYPEHASH,
req.transactionId,
req.tokenAddress,
req.tokenAmount,
req.forwardAddress,
req.spenderAddress,
req.expirationTimestamp,
req.developerFeeRecipient,
req.developerFeeBps,
keccak256(req.callData),
keccak256(req.extraData)
bytes.concat(
abi.encode(
TRANSACTION_REQUEST_TYPEHASH,
req.transactionId,
req.tokenAddress,
req.tokenAmount,
req.forwardAddress,
req.spenderAddress,
req.expirationTimestamp,
req.protocolFeeBps,
req.developerFeeRecipient,
req.developerFeeBps
),
abi.encode(keccak256(req.callData), keccak256(req.extraData))
)
);

Expand All @@ -319,10 +319,10 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
address tokenAddress,
uint256 tokenAmount,
address developerFeeRecipient,
uint256 developerFeeBps
uint256 developerFeeBps,
uint256 protocolFeeBps
) private returns (uint256, uint256) {
address protocolFeeRecipient = _universalBridgeStorage().protocolFeeRecipient;
uint256 protocolFeeBps = _universalBridgeStorage().protocolFeeBps;

uint256 protocolFee = (tokenAmount * protocolFeeBps) / 10_000;
uint256 developerFee = (tokenAmount * developerFeeBps) / 10_000;
Expand Down Expand Up @@ -353,17 +353,12 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol
version = "1";
}

function _setProtocolFeeInfo(address payable feeRecipient, uint256 feeBps) internal {
function _setProtocolFeeInfo(address payable feeRecipient) internal {
if (feeRecipient == address(0)) {
revert UniversalBridgeZeroAddress();
}

if (feeBps > MAX_PROTOCOL_FEE_BPS) {
revert UniversalBridgeInvalidFeeBps();
}

_universalBridgeStorage().protocolFeeRecipient = feeRecipient;
_universalBridgeStorage().protocolFeeBps = feeBps;
}

function _isNativeToken(address tokenAddress) private pure returns (bool) {
Expand All @@ -372,7 +367,7 @@ contract UniversalBridgeV1 is EIP712, Initializable, UUPSUpgradeable, OwnableRol

function _authorizeUpgrade(address) internal override onlyOwner {}

function _universalBridgeStorage() internal view returns (UniversalBridgeStorage.Data storage) {
function _universalBridgeStorage() internal pure returns (UniversalBridgeStorage.Data storage) {
return UniversalBridgeStorage.data();
}
}
23 changes: 16 additions & 7 deletions test/UniversalBridgeV1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ contract UniversalBridgeTest is Test {
bytes32 indexed transactionId,
address tokenAddress,
uint256 tokenAmount,
uint256 protocolFee,
address developerFeeRecipient,
uint256 developerFeeBps,
uint256 developerFee,
uint256 protocolFee,
bytes extraData
);

Expand Down Expand Up @@ -73,9 +73,7 @@ contract UniversalBridgeTest is Test {

// deploy impl and proxy
address impl = address(new UniversalBridgeV1());
bridge = UniversalBridgeV1(
address(new UniversalBridgeProxy(impl, owner, operator, protocolFeeRecipient, protocolFeeBps))
);
bridge = UniversalBridgeV1(address(new UniversalBridgeProxy(impl, owner, operator, protocolFeeRecipient)));

mockERC20 = new MockERC20("Token", "TKN");
mockTarget = new MockTarget();
Expand All @@ -89,7 +87,7 @@ contract UniversalBridgeTest is Test {

// EIP712
typehashTransactionRequest = keccak256(
"TransactionRequest(bytes32 transactionId,address tokenAddress,uint256 tokenAmount,address forwardAddress,address spenderAddress,uint256 expirationTimestamp,address developerFeeRecipient,uint256 developerFeeBps,bytes callData,bytes extraData)"
"TransactionRequest(bytes32 transactionId,address tokenAddress,uint256 tokenAmount,address forwardAddress,address spenderAddress,uint256 expirationTimestamp,uint256 protocolFeeBps,address developerFeeRecipient,uint256 developerFeeBps,bytes callData,bytes extraData)"
);
nameHash = keccak256(bytes("UniversalBridgeV1"));
versionHash = keccak256(bytes("1"));
Expand Down Expand Up @@ -138,6 +136,7 @@ contract UniversalBridgeTest is Test {
req.forwardAddress,
req.spenderAddress,
req.expirationTimestamp,
req.protocolFeeBps,
req.developerFeeRecipient,
req.developerFeeBps,
keccak256(req.callData),
Expand Down Expand Up @@ -177,6 +176,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -221,6 +221,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -263,6 +264,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;

// generate signature
bytes memory _signature = _prepareAndSignData(
Expand Down Expand Up @@ -308,6 +310,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -354,6 +357,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -394,6 +398,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -438,6 +443,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand All @@ -454,10 +460,10 @@ contract UniversalBridgeTest is Test {
_transactionId,
address(mockERC20),
sendValue,
expectedProtocolFee,
developer,
developerFeeBps,
expectedDeveloperFee,
expectedProtocolFee,
""
);
bridge.initiateTransaction(req, _signature);
Expand Down Expand Up @@ -500,6 +506,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -533,6 +540,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = "";

// generate signature
Expand Down Expand Up @@ -643,6 +651,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down Expand Up @@ -684,7 +693,6 @@ contract UniversalBridgeTest is Test {
vm.prank(sender);
mockERC20.approve(address(bridge), sendValueWithFees);


// create transaction request
UniversalBridgeV1.TransactionRequest memory req;
bytes32 _transactionId = keccak256("erc20 refund transaction ID");
Expand All @@ -697,6 +705,7 @@ contract UniversalBridgeTest is Test {
req.expirationTimestamp = 1000;
req.developerFeeRecipient = developer;
req.developerFeeBps = developerFeeBps;
req.protocolFeeBps = protocolFeeBps;
req.callData = targetCalldata;

// generate signature
Expand Down