How to prevent ERC20 transfers less than the average of the last 20 and return array transfer records?

I’m making a simple contract and trying to improve myself but I need help.

In short, what I want to do is take the average of the last 20 transfers and compare it with the smart contract without making a transfer. not allowing the transfer if it is less than the average of the last 20 transfers.

I also want to return the transfer records as public as an array. please review the codes and help me.

function transfer(address recipient, uint256 amount) public override returns (bool) {
  require(amount <= _getMid(), "Require the amount must be less than mid");
  _transfer(_msgSender(), recipient, amount);
  _allAmountArray.push(amount);
  return true;
}

function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
  require(amount <= _getMid(), "Require the amount must be less than mid");
  _transfer(sender, recipient, amount);
  _allAmountArray.push(amount);
  return true;
}

function _getMid() public view returns(uint256, uint256){
  (uint256 mid, ) = findAmountAverage(_allAmountArray);
  return mid;
}

function findAmountAverage(uint256[] storage array) internal view returns (uint256, uint256) {
  if (array.length == 0 || _low == 0) {
      return (0, 0);
  }

  uint256 high = array.length;
  uint256 low = array.length.sub(_low);
  uint256 totalAmount = 0;
  uint256 mid = 0;

  while (low < high) {
      uint256 lowAmount = array[low];
      totalAmount = totalAmount.add(lowAmount);
      low += 1;
  } 
  mid = totalAmount.div(_low);
  return (mid, totalAmount);
}

function _getMid() public view returns(uint256, uint256){
  (uint256 mid, uint256 tAmountAvarage) = findAmountAverage(_allAmountArray);
  return (mid, tAmountAvarage);
}

function _getAllAmountArray(_allAmountArray[] memory) public view returns(uint256) {
  return _allAmountArray;
}
1 Like

This would probably be close, but I think there could be issues with the first 20 TXs, a little tweaking is needed.

uint256 private constant max = 20;

function transfer(address recipient, uint256 amount) public override returns (bool) {
  require(checkAvg(amount), 'The amount is too high');
  _transfer(_msgSender(), recipient, amount);
  // if array is full, forget the oldest amount
  if (_allAmountArray.length == max) {
    _allAmountArray.shift();
  }
  _allAmountArray.push(amount);
  return true;
}

function transferFrom(
  address sender,
  address recipient,
  uint256 amount
) public override returns (bool) {
  require(checkAvg(amount), 'The amount is too high');
  _transfer(sender, recipient, amount);
  // if array is full, forget the oldest amount
  if (_allAmountArray.length == max) {
    _allAmountArray.shift();
  }
  _allAmountArray.push(amount);
  return true;
}

function checkAvg(uint256 thisAmount) internal view returns (bool) {
    uint256 i = 0;
    uint256 totalAmount = 0;
    uint256 avg = 0;

    while (i < _allAmountArray.length) {
        uint256 pastAmt = _allAmountArray[i];
        totalAmount = totalAmount.add(pastAmt);
        i++;
    }
    avg = totalAmount.div(max);
    // do not send TX
    if(avg < thisAmount) {
        return false;
    }
    return true;
}
1 Like

Hi @donita,

Welcome to the community :wave:

Taking a step back, you may want to consider the behavior that you are trying to incentivize or prevent. Also how any mechanism you put in place might be gamed, including by a whale/using flash loans.

I would be cautious about the gas costs for the calculation, so would recommending thorough testing this to see what the impact is.

If you are using OpenZeppelin Contracts ERC20 implementation you could use hooks to add in any functionality that you want to apply, see: https://docs.openzeppelin.com/contracts/3.x/extending-contracts#using-hooks