Skip to content
Draft
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
209 changes: 209 additions & 0 deletions contracts/dependencies/hexagate-gator/Gator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: PRIVATE
// all rights reserved to Hexagate

pragma solidity 0.8.12;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

interface IGator {
function initialize(address owner) external;

function enter(bytes4 signature) external;
function exit(bytes4 signature) external;

function approveClient(address client) external;
function approveClients(address[] calldata clients) external;

function denyClient(address client) external;
function denyClients(address[] calldata clients) external;

function approveFlow(bytes32 flow) external;
function approveFlows(bytes32[] calldata flows) external;

function denyFlow(bytes32 flow) external;
function denyFlows(bytes32[] calldata flows) external;

function enable() external;
function disable() external;
}

error ClientNotApproved(address client);
error FlowNotApproved(bytes32 flow);
error AlreadyEnabled();
error AlreadyDisabled();

contract Gator is Ownable, Initializable, UUPSUpgradeable {
// == Events ==
event Enabled();
event Disabled();
event ClientApproved(address indexed client);
event ClientDenied(address indexed client);
event FlowApproved(bytes32 indexed flow);
event FlowDenied(bytes32 indexed flow);

// == Constants ==
uint256 private constant FALSE = 1;
uint256 private constant TRUE = 2;

// == Storage data ==
uint256 public $enabled;

bytes32 private $flow;
mapping(bytes32 => uint256) private $allowedFlows;
mapping(address => uint256) private $gatedClients;

bytes32 private $txIdentifier;

address[] $depthStack;


// This contract is to be used behind a proxy, so the constructor should not be used
constructor() {
_disableInitializers();
}

// Allow upgrades to this contract by the owner
function _authorizeUpgrade(address) internal override onlyOwner {}

// This initializer is used instead of the constructor
function initialize(address owner) external initializer {
_transferOwnership(owner);
$enabled = TRUE;
}

// == Modifiers ==

modifier onlyGated() {
if ($gatedClients[msg.sender] != TRUE) {
revert ClientNotApproved(msg.sender);
}

_;
}

// == Gator client called functions ==

function _checkFlow(bytes4 selector, bool isEnter) internal {
if ($enabled == FALSE) {
return;
}

// Calculate the the new flow identifier according to the current state
bytes32 flow = keccak256(abi.encodePacked($flow, msg.sender, selector, isEnter));
if ($allowedFlows[flow] != TRUE) {
revert FlowNotApproved(flow);
}

$flow = flow;
}

function _clearFlow() internal {
$flow = bytes32(0);
}

function _txIdentifier() internal view returns (bytes32) {
uint256 id = uint256(uint160(tx.origin));
id |= (block.number << 160);
return bytes32(id);
}

function enter(bytes4 selector) external onlyGated {
// Clear on new tx
bytes32 txId = _txIdentifier();
if ($txIdentifier != txId) {
$txIdentifier = txId;
_clearFlow();
}

$depthStack.push(msg.sender);

if ($depthStack.length >= 2) {
address prev = $depthStack[$depthStack.length - 2];
if (prev == msg.sender) {
// Internal call - no need to check flow
return;
}
}

_checkFlow(selector, true);
}

function exit(bytes4 selector) external onlyGated {
address last = $depthStack[$depthStack.length - 1];
$depthStack.pop();

if ($depthStack.length > 0) {
address prev = $depthStack[$depthStack.length - 1];
if (prev == last) {
// Internal call - no need to check flow
return;
}
}

_checkFlow(selector, false);
}

// == Admin functions ==

function approveFlow(bytes32 flow) public onlyOwner {
$allowedFlows[flow] = TRUE;
emit FlowApproved(flow);
}

function approveFlows(bytes32[] calldata flows) external onlyOwner {
for (uint256 i = 0; i < flows.length; i++) {
approveFlow(flows[i]);
}
}

function denyFlow(bytes32 flow) public onlyOwner {
$allowedFlows[flow] = FALSE;
emit FlowDenied(flow);
}

function denyFlows(bytes32[] calldata flows) external onlyOwner {
for (uint256 i = 0; i < flows.length; i++) {
denyFlow(flows[i]);
}
}

function approveClient(address client) public onlyOwner {
$gatedClients[client] = TRUE;
emit ClientApproved(client);
}

function approveClients(address[] calldata clients) external onlyOwner {
for (uint256 i = 0; i < clients.length; i++) {
approveClient(clients[i]);
}
}

function denyClient(address client) public onlyOwner {
$gatedClients[client] = FALSE;
emit ClientDenied(client);
}

function denyClients(address[] calldata clients) external onlyOwner {
for (uint256 i = 0; i < clients.length; i++) {
denyClient(clients[i]);
}
}

function enable() public onlyOwner {
if ($enabled == TRUE) {
revert AlreadyEnabled();
}
$enabled = TRUE;
emit Enabled();
}

function disable() public onlyOwner {
if ($enabled == FALSE) {
revert AlreadyDisabled();
}
$enabled = FALSE;
emit Disabled();
}
}
53 changes: 53 additions & 0 deletions contracts/dependencies/hexagate-gator/GatorClient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: PRIVATE
// all rights reserved to Hexagate

pragma solidity ^0.8.12;

import {IGator} from "./Gator.sol";

abstract contract GatorClient {
// keccak32("gatorclient.gator")
bytes32 private constant GATOR_SLOT = 0xa53f6f8ec545fba22c6f5161ad4c3981dee87a9993a6055a4c0ede893f9496e3;

/**
* @dev Reads the gator address from `GATOR_SLOT` storage slot
*/
function gator() public view returns (IGator gatorAddress) {
assembly {
gatorAddress := sload(GATOR_SLOT)
}
}

/**
* @dev Sets the gator address stored in `GATOR_SLOT` storage slot
*/
function _setGator(address _gator) internal {
assembly {
sstore(GATOR_SLOT, _gator)
}
}

/**
* @dev Calls gator with the current function selector
* to check if the flow is approved
*/
modifier gated() {
IGator _gator = gator();
if (address(_gator) == address(0)) {
_;
return;
}

// calldataload(0) loads the first 32 bytes of calldata,
// and the assignment to `selector` which is a bytes4 variable
// truncates it to the first 4 bytes of the calldata
bytes4 selector;
assembly {
selector := calldataload(0)
}

_gator.enter(selector);
_;
_gator.exit(selector);
}
}
111 changes: 111 additions & 0 deletions contracts/protocol/pool/PoolGator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.12;
import {Pool, IPool} from 'contracts/protocol/pool/Pool.sol';
import {GatorClient} from 'contracts/dependencies/hexagate-gator/GatorClient.sol';
import {IPoolAddressesProvider} from 'contracts/interfaces/IPoolAddressesProvider.sol';
import {SupplyLogic} from 'contracts/protocol/libraries/logic/SupplyLogic.sol';
import {BorrowLogic} from 'contracts/protocol/libraries/logic/BorrowLogic.sol';
import {DataTypes} from 'contracts/protocol/libraries/types/DataTypes.sol';

contract PoolGator is Pool, GatorClient {
constructor(IPoolAddressesProvider provider) Pool(provider) {}

function init(address _gatorAddress) external initializer {
_setGator(_gatorAddress);
}

/// @inheritdoc IPool
function supply(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) public override gated {
SupplyLogic.executeSupply(
_reserves,
_reservesList,
_usersConfig[onBehalfOf],
DataTypes.ExecuteSupplyParams({
asset: asset,
amount: amount,
onBehalfOf: onBehalfOf,
referralCode: referralCode
})
);
}

/// @inheritdoc IPool
function withdraw(
address asset,
uint256 amount,
address to
) public override gated returns (uint256) {
return
SupplyLogic.executeWithdraw(
_reserves,
_reservesList,
_eModeCategories,
_usersConfig[msg.sender],
DataTypes.ExecuteWithdrawParams({
asset: asset,
amount: amount,
to: to,
reservesCount: _reservesCount,
oracle: ADDRESSES_PROVIDER.getPriceOracle(),
userEModeCategory: _usersEModeCategory[msg.sender]
})
);
}

/// @inheritdoc IPool
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) public override gated {
BorrowLogic.executeBorrow(
_reserves,
_reservesList,
_eModeCategories,
_usersConfig[onBehalfOf],
DataTypes.ExecuteBorrowParams({
asset: asset,
user: msg.sender,
onBehalfOf: onBehalfOf,
amount: amount,
interestRateMode: DataTypes.InterestRateMode(interestRateMode),
referralCode: referralCode,
releaseUnderlying: true,
maxStableRateBorrowSizePercent: _maxStableRateBorrowSizePercent,
reservesCount: _reservesCount,
oracle: ADDRESSES_PROVIDER.getPriceOracle(),
userEModeCategory: _usersEModeCategory[onBehalfOf],
priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
})
);
}

/// @inheritdoc IPool
function repay(
address asset,
uint256 amount,
uint256 interestRateMode,
address onBehalfOf
) public override gated returns (uint256) {
return
BorrowLogic.executeRepay(
_reserves,
_reservesList,
_usersConfig[onBehalfOf],
DataTypes.ExecuteRepayParams({
asset: asset,
amount: amount,
interestRateMode: DataTypes.InterestRateMode(interestRateMode),
onBehalfOf: onBehalfOf,
useATokens: false
})
);
}
}
Loading