Skip to content
Open
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
2 changes: 1 addition & 1 deletion foundry.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
"rev": "0844d7e1fc5e60d77b68e469bff60265f236c398"
}
}
}
}
2 changes: 1 addition & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ aave-v3-origin/=lib/aave-address-book/lib/aave-v3-origin/src/
aave-v3-origin-tests/=lib/aave-address-book/lib/aave-v3-origin/tests
forge-std/=lib/forge-std/src/
solidity-utils/=lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src/
openzeppelin-contracts/=lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/
openzeppelin-contracts/=lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

pragma solidity ^0.8.0;

import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol';
import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol';
import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol';
import {GovernanceV3Plasma} from 'aave-address-book/GovernanceV3Plasma.sol';
import {ArbitrumScript, EthereumScript, OptimismScript, PolygonScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol';
import {AaveArbEthERC20Bridge} from 'src/bridges/arbitrum/AaveArbEthERC20Bridge.sol';
import {AavePolEthERC20Bridge} from 'src/bridges/polygon/AavePolEthERC20Bridge.sol';
import {AavePolEthPlasmaBridge} from 'src/bridges/polygon/AavePolEthPlasmaBridge.sol';
import {AaveOpEthERC20Bridge} from 'src/bridges/optimism/AaveOpEthERC20Bridge.sol';

address constant TOKEN_LOGIC = 0x3765A685a401622C060E5D700D9ad89413363a91;

contract DeployEthereum is EthereumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Bridge';
Expand All @@ -29,34 +32,34 @@ contract DeployPolygon is PolygonScript {
contract DeployPlasmaEthereum is EthereumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Plasma Bridge';
new AavePolEthPlasmaBridge{salt: salt}(0x3765A685a401622C060E5D700D9ad89413363a91);
new AavePolEthPlasmaBridge{salt: salt}(TOKEN_LOGIC);
}
}

contract DeployPlasmaPolygon is PolygonScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Plasma Bridge';
new AavePolEthPlasmaBridge{salt: salt}(0x3765A685a401622C060E5D700D9ad89413363a91);
new AavePolEthPlasmaBridge{salt: salt}(TOKEN_LOGIC);
}
}

contract DeployOptimism is OptimismScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Optimism Bridge';
new AaveOpEthERC20Bridge{salt: salt}(0x3765A685a401622C060E5D700D9ad89413363a91);
new AaveOpEthERC20Bridge{salt: salt}(TOKEN_LOGIC);
}
}

contract DeployArbBridgeEthereum is EthereumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Bridge';
new AaveArbEthERC20Bridge{salt: salt}(0x3765A685a401622C060E5D700D9ad89413363a91);
new AaveArbEthERC20Bridge{salt: salt}(TOKEN_LOGIC);
}
}

contract DeployArbBridgeArbitrum is ArbitrumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury Bridge';
new AaveArbEthERC20Bridge{salt: salt}(0x3765A685a401622C060E5D700D9ad89413363a91);
new AaveArbEthERC20Bridge{salt: salt}(TOKEN_LOGIC);
}
}
77 changes: 77 additions & 0 deletions scripts/bridges/DeployOFTBridge.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol';
import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol';
import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol';
import {GovernanceV3Plasma} from 'aave-address-book/GovernanceV3Plasma.sol';
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol';
import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol';
import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol';
import {AaveV3Plasma} from 'aave-address-book/AaveV3Plasma.sol';
import {ArbitrumScript, EthereumScript, OptimismScript, PolygonScript, PlasmaScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol';
import {AaveOFTBridgeSteward} from 'src/bridges/oft/AaveOFTBridgeSteward.sol';
import {TOKEN_LOGIC} from './DeployBridges.s.sol';

contract DeployOFTEthereum is EthereumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury OFT Bridge';
new AaveOFTBridgeSteward{salt: salt}(
0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee, // USDT0 OFT (OAdapterUpgradeable)
TOKEN_LOGIC, // owner
GovernanceV3Ethereum.EXECUTOR_LVL_1, // guardian
address(AaveV3Ethereum.COLLECTOR) // collector
);
}
}

contract DeployOFTArbitrum is ArbitrumScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury OFT Bridge';
new AaveOFTBridgeSteward{salt: salt}(
0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92, // USDT0 OFT (OUpgradeable)
TOKEN_LOGIC, // owner
GovernanceV3Arbitrum.EXECUTOR_LVL_1, // guardian
address(AaveV3Arbitrum.COLLECTOR) // collector
);
}
}

contract DeployOFTPolygon is PolygonScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury OFT Bridge';
new AaveOFTBridgeSteward{salt: salt}(
0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13, // USDT0 OFT (OUpgradeable)
TOKEN_LOGIC, // owner
GovernanceV3Polygon.EXECUTOR_LVL_1, // guardian
address(AaveV3Polygon.COLLECTOR) // collector
);
}
}

contract DeployOFTOptimism is OptimismScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury OFT Bridge';
new AaveOFTBridgeSteward{salt: salt}(
0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD, // USDT0 OFT (OUpgradeable)
TOKEN_LOGIC, // owner
GovernanceV3Optimism.EXECUTOR_LVL_1, // guardian
address(AaveV3Optimism.COLLECTOR) // collector
);
}
}

contract DeployOFTPlasma is PlasmaScript {
function run() external broadcast {
bytes32 salt = 'Aave Treasury OFT Bridge';
new AaveOFTBridgeSteward{salt: salt}(
0x02ca37966753bDdDf11216B73B16C1dE756A7CF9, // USDT0 OFT (OUpgradeable)
TOKEN_LOGIC, // owner
GovernanceV3Plasma.EXECUTOR_LVL_1, // guardian
address(AaveV3Plasma.COLLECTOR) // collector
);
}
}
145 changes: 145 additions & 0 deletions src/bridges/oft/AaveOFTBridgeSteward.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from 'openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol';
import {ICollector} from 'aave-v3-origin/contracts/treasury/ICollector.sol';
import {OwnableWithGuardian} from 'solidity-utils/contracts/access-control/OwnableWithGuardian.sol';
import {RescuableBase, IRescuableBase} from 'solidity-utils/contracts/utils/RescuableBase.sol';

import {IAaveOFTBridgeSteward} from './interfaces/IAaveOFTBridgeSteward.sol';
import {IOFT, SendParam, MessagingFee, OFTReceipt} from './interfaces/IOFT.sol';

/// @title AaveOFTBridge
/// @author @stevyhacker (TokenLogic)
/// @notice Helper contract to bridge USDT using OFT V2 (LayerZero OFT)
contract AaveOFTBridgeSteward is OwnableWithGuardian, RescuableBase, IAaveOFTBridgeSteward {
using SafeERC20 for IERC20;

/// @inheritdoc IAaveOFTBridgeSteward
address public immutable OFT_USDT;

/// @inheritdoc IAaveOFTBridgeSteward
address public immutable USDT;

/// @inheritdoc IAaveOFTBridgeSteward
address public immutable COLLECTOR;

mapping(address receiver => bool allowed) public isAllowedReciever;

/// @param oftUsdt The OFT address for USDT on this chain
/// @param initialOwner The initial owner of the contract
/// @param initialGuardian The initial guardian of the contract
/// @param collector The address to collect fees
constructor(
address oftUsdt,
address initialOwner,
address initialGuardian,
address collector
) OwnableWithGuardian(initialOwner, initialGuardian) {
if (initialGuardian == address(0)) revert InvalidZeroAddress();
if (collector == address(0)) revert InvalidZeroAddress();
if (oftUsdt == address(0)) revert InvalidZeroAddress();

COLLECTOR = collector;
OFT_USDT = oftUsdt;
USDT = IOFT(OFT_USDT).token();
}

/// @dev Default receive function enabling the contract to accept native tokens for gas fees
receive() external payable {}

/// @inheritdoc IAaveOFTBridgeSteward
function bridge(
uint32 dstEid,
uint256 amount,
address receiver,
uint256 minAmountLD,
uint256 maxFee
) external payable onlyOwnerOrGuardian {
require(amount > 0, InvalidZeroAmount());
require(isAllowedReciever[receiver], OnlyAllowedRecipients());

ICollector(COLLECTOR).transfer(IERC20(USDT), address(this), amount);

SendParam memory sendParam = _buildSendParam(dstEid, amount, receiver, minAmountLD);

MessagingFee memory messagingFee = IOFT(OFT_USDT).quoteSend(sendParam, false);

require(messagingFee.nativeFee <= maxFee, ExceedsMaxFee());

IERC20(USDT).forceApprove(OFT_USDT, amount);

IOFT(OFT_USDT).send{value: messagingFee.nativeFee}(sendParam, messagingFee, COLLECTOR);

emit Bridge(USDT, dstEid, receiver, amount, minAmountLD);
}

/// @inheritdoc IAaveOFTBridgeSteward
function setAllowedReceiver(address receiver, bool allowed) external onlyOwner {
require(receiver != address(0), InvalidZeroAddress());
isAllowedReciever[receiver] = allowed;
}

/// @inheritdoc IAaveOFTBridgeSteward
function rescueToken(address token) external onlyOwnerOrGuardian {
_emergencyTokenTransfer(token, COLLECTOR, type(uint256).max);
}

/// @inheritdoc IAaveOFTBridgeSteward
function rescueEth() external onlyOwnerOrGuardian {
_emergencyEtherTransfer(COLLECTOR, address(this).balance);
}

/// @inheritdoc IAaveOFTBridgeSteward
function quoteBridge(
uint32 dstEid,
uint256 amount,
address receiver,
uint256 minAmountLD
) external view returns (uint256) {
SendParam memory sendParam = _buildSendParam(dstEid, amount, receiver, minAmountLD);
MessagingFee memory messagingFee = IOFT(OFT_USDT).quoteSend(sendParam, false);
return messagingFee.nativeFee;
}

/// @inheritdoc IAaveOFTBridgeSteward
function quoteOFT(
uint32 dstEid,
uint256 amount,
address receiver
) external view returns (uint256) {
SendParam memory sendParam = _buildSendParam(dstEid, amount, receiver, 0);
(, , OFTReceipt memory receipt) = IOFT(OFT_USDT).quoteOFT(sendParam);
return receipt.amountReceivedLD;
}

/// @inheritdoc IRescuableBase
function maxRescue(address token) public view override(RescuableBase) returns (uint256) {
return IERC20(token).balanceOf(address(this));
}

/// @dev Builds the SendParam struct for OFT transfer
/// @param dstEid The destination LayerZero endpoint ID
/// @param amount The amount to send
/// @param receiver The receiver address on destination
/// @param minAmountLD The minimum amount to receive
/// @return The constructed SendParam struct
function _buildSendParam(
uint32 dstEid,
uint256 amount,
address receiver,
uint256 minAmountLD
) internal pure returns (SendParam memory) {
return
SendParam({
dstEid: dstEid,
to: bytes32(uint256(uint160(receiver))),
amountLD: amount,
minAmountLD: minAmountLD,
extraOptions: new bytes(0),
composeMsg: new bytes(0),
oftCmd: new bytes(0)
});
}
}
Loading