Skip to content

Commit

Permalink
Clean up guard contract and add full short test
Browse files Browse the repository at this point in the history
  • Loading branch information
hieuh25 committed Feb 6, 2024
1 parent 12b8654 commit ff4ac4e
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 36 deletions.
59 changes: 36 additions & 23 deletions contracts/guard/src/GuardV0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ contract GuardV0 is IGuard, Ownable {
using Path for bytes;
using BytesLib for bytes;

/// @dev The length of the bytes encoded address
uint256 private constant ADDR_SIZE = 20;
/// @dev The length of the bytes encoded pool fee
uint256 private constant ONEDELTA_FEE_SIZE = 3;
/// @dev The length of the bytes encoded DEX ID
uint256 private constant ONEDELTA_PID_SIZE = 1;
/// @dev The length of the bytes encoded action
uint256 private constant ONEDELTA_ACTION_SIZE = 1;
/// @dev The offset of a single token address, fee, pid and action
uint256 private constant ONEDELTA_NEXT_OFFSET = ADDR_SIZE + ONEDELTA_FEE_SIZE + ONEDELTA_PID_SIZE + ONEDELTA_ACTION_SIZE;
/// @dev The offset of an encoded pool key
uint256 private constant ONEDELTA_POP_OFFSET = ONEDELTA_NEXT_OFFSET + ADDR_SIZE;
/// @dev The minimum length of an encoding that contains 2 or more pools
uint256 private constant ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH = ONEDELTA_POP_OFFSET + ONEDELTA_NEXT_OFFSET;

struct ExactInputParams {
bytes path;
address recipient;
Expand Down Expand Up @@ -277,7 +292,7 @@ contract GuardV0 is IGuard, Ownable {
require(isAllowedReceiver(to), "Receiver address does not match");

address token;
for (uint i = 0; i < path.length; i++) {
for (uint256 i = 0; i < path.length; i++) {
token = path[i];
require(isAllowedAsset(token), "Token not allowed");
}
Expand Down Expand Up @@ -362,10 +377,15 @@ contract GuardV0 is IGuard, Ownable {

function validate_transferERC20In(bytes memory callData) public view {
(address token, ) = abi.decode(callData, (address, uint256));

require(isAllowedAsset(token), "Token not allowed");
}

function validate_transferERC20AllIn(bytes memory callData) public view {}
function validate_transferERC20AllIn(bytes memory callData) public view {
(address token) = abi.decode(callData, (address));

require(isAllowedAsset(token), "Token not allowed");
}

function validate_deposit(bytes memory callData) public view {
(address token, address receiver) = abi.decode(callData, (address, address));
Expand All @@ -374,7 +394,12 @@ contract GuardV0 is IGuard, Ownable {
require(isAllowedReceiver(receiver), "Receiver address does not match");
}

function validate_withdraw(bytes memory callData) public view {}
function validate_withdraw(bytes memory callData) public view {
(address token, address receiver) = abi.decode(callData, (address, address));

require(isAllowedAsset(token), "Token not allowed");
require(isAllowedReceiver(receiver), "Receiver address does not match");
}

function validate_flashSwapExactInt(bytes memory callData) public view {
(, , bytes memory path) = abi.decode(callData, (uint256, uint256, bytes));
Expand All @@ -388,38 +413,26 @@ contract GuardV0 is IGuard, Ownable {
validate1deltaPath(path);
}

function validate_flashSwapAllOut(bytes memory callData) public view {}

/// @dev The length of the bytes encoded address
uint256 private constant ADDR_SIZE = 20;
/// @dev The length of the bytes encoded fee
uint256 private constant FEE_SIZE = 3;
function validate_flashSwapAllOut(bytes memory callData) public view {
(, bytes memory path) = abi.decode(callData, (uint256, bytes));

uint256 private constant ID_SIZE = 1;
uint256 private constant FLAG_SIZE = 1;
uint256 private constant OFFSET_TILL_ID = ADDR_SIZE + FEE_SIZE;

/// @dev The offset of a single token address and pool fee
uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE + ID_SIZE + FLAG_SIZE;
/// @dev The offset of an encoded pool key
uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;
/// @dev The minimum length of an encoding that contains 2 or more pools
uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET;
validate1deltaPath(path);
}

function validate1deltaPath(bytes memory path) public view {
address tokenIn;
address tokenOut;

while (true) {
tokenIn = path.toAddress(0);
tokenOut = path.toAddress(NEXT_OFFSET);
tokenOut = path.toAddress(ONEDELTA_NEXT_OFFSET);

require(isAllowedAsset(tokenIn), "Token not allowed");
require(isAllowedAsset(tokenOut), "Token not allowed");

// get next slice if the path still has multiple pools
if (path.length >= MULTIPLE_POOLS_MIN_LENGTH) {
path = path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);
// iterate to next slice if the path still contains multiple pools
if (path.length >= ONEDELTA_MULTIPLE_POOLS_MIN_LENGTH) {
path = path.slice(ONEDELTA_NEXT_OFFSET, path.length - ONEDELTA_NEXT_OFFSET);
} else {
break;
}
Expand Down
2 changes: 1 addition & 1 deletion eth_defi/abi/guard/GuardV0.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion eth_defi/abi/guard/SimpleVaultV0.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion eth_defi/one_delta/lending.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def _build_withdraw_multicall(
:return: list of encoded ABI calls
"""
if amount == MAX_AMOUNT:
# use MAX_AMOUNT to make sure the whole balane is swept
# use MAX_AMOUNT to make sure the whole balance is swept
call_transfer = one_delta_deployment.flash_aggregator.encodeABI(
fn_name="transferERC20AllIn",
args=[atoken.address],
Expand Down
7 changes: 6 additions & 1 deletion eth_defi/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Currently only works with Anvil (:py:mod:`eth_defi.anvil`) backend
"""

import enum
import logging
from typing import Any, Iterator, Optional, cast
Expand Down Expand Up @@ -247,6 +248,7 @@ def assert_transaction_success_with_explanation(
web3: Web3,
tx_hash: HexBytes,
RaisedException=TransactionAssertionError,
tracing: bool = False,
) -> TxReceipt:
"""Checks if a transaction succeeds and give a verbose explanation why not..
Expand Down Expand Up @@ -288,6 +290,9 @@ def assert_transaction_success_with_explanation(
:param RaisedException:
Raise a custom exception instead of :py:class:`TransactionAssertionError`.
:param tracing:
Force turn on transaction tracing to use in e.g testing.
:raise TransactionAssertionError:
Outputs a verbose AssertionError on what went wrong.
Expand All @@ -300,7 +305,7 @@ def assert_transaction_success_with_explanation(
# Explain why the transaction failed
tx_details = web3.eth.get_transaction(tx_hash)

if web3.eth.chain_id == 31337:
if web3.eth.chain_id == 31337 or tracing:
# Transaction tracing only enabled to anvil
revert_reason = fetch_transaction_revert_reason(web3, tx_hash)
trace_data = trace_evm_transaction(web3, tx_hash, TraceMethod.parity)
Expand Down
Loading

0 comments on commit ff4ac4e

Please sign in to comment.