-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add BridgeProxy contract and state variables * add deposit endpoint * add logic for deposit funds in Bridge contract * moved structs to SharedStructs * add counter for transactions * add execute endpoint * fixed BridgeMock bug * add executeCallback private method * add finishExecuteGracefully private method * add refund method and view functions * removed token payments mapping and refactored * removed token payment * fixed logic after removing token payments * add setter for Bridge address * refactored tests for Bridge, MintBurnERC20 and ERC20Safe contracts * refactored deposit endpoint * removed state variable bridgeAddress * small gas optimization for getPendingTransaction view function * lowestIndexId and calldata encoding fixes * add unit tests for BridgeProxy contract * refactored execute endpoint * add logic for deleting pending txns * add test for txns with endpoints with no args * made BridgeProxy contract upgradable * test fixes after merge * add test for BridgeProxy contract upgrade * add isPendingTransaction private function * temporar fixes * fixes after review * renamed BridgeProxy to BridgeExecutor * more fixes * refactor execute function * renamed variables * - fixes after merge --------- Co-authored-by: cosmatudor <[email protected]> Co-authored-by: Rebegea Dragos-Alexandru <[email protected]> Co-authored-by: Cosma Tudor-Mihai <[email protected]>
- Loading branch information
1 parent
ac815b3
commit 06bc10c
Showing
13 changed files
with
922 additions
and
330 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import "./lib/Pausable.sol"; | ||
import "./SharedStructs.sol"; | ||
import "./lib/BoolTokenTransfer.sol"; | ||
import "./access/AdminRole.sol"; | ||
import "./access/BridgeRole.sol"; | ||
|
||
contract BridgeExecutor is Initializable, Pausable, BridgeRole { | ||
using BoolTokenTransfer for IERC20; | ||
|
||
/*========================= CONTRACT STATE =========================*/ | ||
uint128 public constant MIN_GAS_LIMIT_FOR_SC_CALL = 10_000_000; | ||
uint128 public constant DEFAULT_GAS_LIMIT_FOR_REFUND_CALLBACK = 20_000_000; | ||
uint256 private lowestTxId; | ||
uint256 private currentTxId; | ||
|
||
mapping(uint256 => MvxTransaction) private pendingTransactions; | ||
|
||
/*========================= PUBLIC API =========================*/ | ||
function initialize() public initializer { | ||
__BridgeRole_init(); | ||
__Pausable_init(); | ||
} | ||
|
||
function __BridgeExecutor__init_unchained() internal onlyInitializing { | ||
lowestTxId = 0; | ||
currentTxId = 0; | ||
} | ||
|
||
function deposit(MvxTransaction calldata txn) external payable whenNotPaused onlyBridge returns (bool) { | ||
pendingTransactions[currentTxId] = txn; | ||
currentTxId++; | ||
|
||
IERC20 token = IERC20(txn.token); | ||
bool approvalSuccess = token.approve(txn.recipient, txn.amount); | ||
|
||
return approvalSuccess; | ||
} | ||
|
||
function execute(uint256 txId) external whenNotPaused { | ||
require(txId < currentTxId, "BridgeExecutor: Invalid transaction ID"); | ||
MvxTransaction memory txn = pendingTransactions[txId]; | ||
|
||
require(txn.recipient != address(0), "BridgeExecutor: Transaction does not exist"); | ||
|
||
if (txn.callData.length == 0) { | ||
_refundAndDeleteTxn(txId); | ||
return; | ||
} | ||
|
||
(bytes memory selector, uint256 gasLimit, bytes memory args) = abi.decode( | ||
txn.callData, | ||
(bytes, uint256, bytes) | ||
); | ||
|
||
if (selector.length == 0 || gasLimit == 0 || gasLimit < MIN_GAS_LIMIT_FOR_SC_CALL) { | ||
_refundAndDeleteTxn(txId); | ||
return; | ||
} | ||
|
||
bytes memory data; | ||
if (args.length > 0) { | ||
data = abi.encodePacked(selector, args); | ||
} else { | ||
data = selector; | ||
} | ||
|
||
_updateLowestTxId(); | ||
|
||
delete pendingTransactions[txId]; | ||
|
||
(bool success, ) = txn.recipient.call{ gas: gasLimit }(data); | ||
|
||
if (!success) { | ||
_refundTransaction(txn.token, txn.amount); | ||
return; | ||
} | ||
} | ||
|
||
/*========================= PRIVATE API =========================*/ | ||
function _refundAndDeleteTxn(uint256 txId) private { | ||
MvxTransaction memory txn = pendingTransactions[txId]; | ||
_refundTransaction(txn.token, txn.amount); | ||
|
||
_updateLowestTxId(); | ||
|
||
delete pendingTransactions[txId]; | ||
} | ||
|
||
function _refundTransaction(address token, uint256 amount) private { | ||
IERC20 erc20 = IERC20(token); | ||
bool transferExecuted = erc20.boolTransfer(this.bridge(), amount); | ||
require(transferExecuted, "BridgeExecutor: Refund failed"); | ||
} | ||
|
||
function _updateLowestTxId() private { | ||
uint256 newLowestTxId = lowestTxId; | ||
|
||
while (newLowestTxId < currentTxId && pendingTransactions[newLowestTxId].amount == 0) { | ||
newLowestTxId++; | ||
} | ||
|
||
lowestTxId = newLowestTxId; | ||
} | ||
|
||
function _isPendingTransaction(uint256 txId) private view returns (bool) { | ||
return pendingTransactions[txId].amount != 0; | ||
} | ||
|
||
/*========================= VIEW FUNCTIONS =========================*/ | ||
function getPendingTransactionById(uint256 txId) public view returns (MvxTransaction memory) { | ||
return pendingTransactions[txId]; | ||
} | ||
|
||
function getPendingTransactions() public view returns (MvxTransaction[] memory) { | ||
uint256 pendingTransactionsCount = currentTxId - lowestTxId; | ||
MvxTransaction[] memory txns = new MvxTransaction[](pendingTransactionsCount); | ||
uint256 index = 0; | ||
|
||
for (uint256 i = lowestTxId; i < currentTxId; i++) { | ||
if (_isPendingTransaction(i)) { | ||
txns[index] = pendingTransactions[i]; | ||
index++; | ||
} | ||
} | ||
|
||
// Resize the array to the actual number of pending transactions | ||
assembly { | ||
mstore(txns, index) | ||
} | ||
|
||
return txns; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
interface IERC20 { | ||
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | ||
} | ||
|
||
contract BridgeExecutorTestContract { | ||
uint256 public count; | ||
address public bridgeExecutor; | ||
|
||
constructor(address _bridgeExecutor) { | ||
count = 0; | ||
bridgeExecutor = _bridgeExecutor; | ||
} | ||
|
||
function increment() public { | ||
count += 1; | ||
} | ||
|
||
function withdraw(address tokenAddress, uint256 amount) external { | ||
IERC20 token = IERC20(tokenAddress); | ||
// The test contract should be approved to spend the tokens on behalf of the bridgeExecutor | ||
require(token.transferFrom(bridgeExecutor, address(this), amount), "TransferFrom failed"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
|
||
import "../BridgeExecutor.sol"; | ||
|
||
contract BridgeExecutorUpgrade is Initializable, BridgeExecutor { | ||
uint256 public someValue; | ||
// New initialization function for the upgrade | ||
function initializeV2(uint256 val) public reinitializer(2) { | ||
someValue = val; | ||
} | ||
|
||
function afterUpgrade() public view returns (uint256) { | ||
return someValue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.