Skip to content

Commit

Permalink
Merge pull request #2423 from fluidity-money/develop-add-blacklist-fe…
Browse files Browse the repository at this point in the history
…ature

Add blacklist feature
  • Loading branch information
af-afk authored Nov 23, 2023
2 parents d0340e9 + 80cc9e5 commit 996fd65
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 7 deletions.
49 changes: 49 additions & 0 deletions contracts/ethereum/contracts/Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ contract Token is
/// @notice burnFee_ that's paid by the user when they mint
uint256 private mintFee_;

/* ~~~~~~~~~~ ADDRESS BLACKLISTING ~~~~~~~~~~ */

/// @notice blacklist_ that's used to prevent certain accounts from
/// moving and unwrapping/wrapping funds.
mapping(address => bool) private blacklist_;

/* ~~~~~~~~~~ EVENTS ~~~~~~~~~~ */

/// @dev BlacklistEnabled activated for a specific address
event BlacklistEnabled(address indexed spender, bool status);

/* ~~~~~~~~~~ SETUP FUNCTIONS ~~~~~~~~~~ */

/**
Expand Down Expand Up @@ -266,6 +277,7 @@ contract Token is
uint256 _amount
) internal returns (uint256) {
require(noEmergencyMode_, "emergency mode!");
require(isAddressAllowed(_spender), "address blacklisted");

// take underlying tokens from the user

Expand Down Expand Up @@ -315,6 +327,9 @@ contract Token is
address _beneficiary,
uint256 _amount
) internal returns (uint256) {
// check if the account isn't blacklisted
require(isAddressAllowed(_sender), "address blacklisted");

// take the user's fluid tokens

// if the fee amount > 0 and the burn fee is greater than 0, then
Expand Down Expand Up @@ -387,6 +402,7 @@ contract Token is
}

/// @dev _transfer is implemented by OpenZeppelin
/// @dev also checks the blacklist and responds accordingly
function _transfer(
address from,
address to,
Expand All @@ -398,6 +414,8 @@ contract Token is
// solhint-disable-next-line reason-string
require(to != address(0), "ERC20: transfer to the zero address");

require(isAddressAllowed(from), "address blacklisted");

uint256 fromBalance = balances_[from];

// solhint-disable-next-line reason-string
Expand Down Expand Up @@ -810,11 +828,19 @@ contract Token is
return true;
}

/**
* @dev transferFrom the address with the sender, if they're allowed.
* @param _from address to send from
* @param _to recipient of the amount
* @param _amount to send
* @dev note that this enforces a blacklist on the sender and _from
*/
function transferFrom(
address _from,
address _to,
uint256 _amount
) public returns (bool) {
require(isAddressAllowed(msg.sender), "address blacklisted");
_spendAllowance(_from, msg.sender, _amount);
_transfer(_from, _to, _amount);
return true;
Expand Down Expand Up @@ -869,4 +895,27 @@ contract Token is
mintFee_ = _mintFee;
burnFee_ = _burnFee;
}

/**
* @dev blacklistAddress, only callable by the operator.
* @param _spender to ban using the blacklisting feature
* @param _status of whether or not it's enabled
*/
function blacklistAddress(address _spender, bool _status) public {
require(
msg.sender == operator_ || msg.sender == emergencyCouncil_,
"only operator/emergency council"
);
require(_spender != address(0), "no zero address");
emit BlacklistEnabled(_spender, _status);
blacklist_[_spender] = _status;
}

/**
* @notice isAddressAllowed (are they not blacklisted?)
* @param _account to test
*/
function isAddressAllowed(address _account) public view returns (bool) {
return !blacklist_[_account];
}
}
2 changes: 1 addition & 1 deletion contracts/ethereum/mainnet-constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import type { Token } from "../types";
import type { Token } from "./types";

export const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7";
export const CUSDT_ADDR = "0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9";
Expand Down
11 changes: 7 additions & 4 deletions contracts/ethereum/scripts/deploy-token-beacon-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ const main = async () => {

let pool: ethers.Contract;

let aToken: string;
let aavePool: string;

switch (backend) {
case "compound":
const cToken = mustEnv(ENV_COMPOUND_CTOKEN);
Expand All @@ -72,8 +75,8 @@ const main = async () => {
break;

case "aaveV2":
const aToken = mustEnv(ENV_AAVE_V2_ATOKEN);
const aavePool = mustEnv(ENV_AAVE_V2_ADDRESS_PROVIDER);
aToken = mustEnv(ENV_AAVE_V2_ATOKEN);
aavePool = mustEnv(ENV_AAVE_V2_ADDRESS_PROVIDER);
console.log(`deploying aave v2 pool with beacon ${poolAddress}, atoken ${aToken}, aave pool ${aavePool}`);
pool = await hre.upgrades.deployBeaconProxy(
poolAddress,
Expand All @@ -84,8 +87,8 @@ const main = async () => {
break;

case "aaveV3":
const aToken = mustEnv(ENV_AAVE_V3_ATOKEN);
const aavePool = mustEnv(ENV_AAVE_V3_ADDRESS_PROVIDER);
aToken = mustEnv(ENV_AAVE_V3_ATOKEN);
aavePool = mustEnv(ENV_AAVE_V3_ADDRESS_PROVIDER);
console.log(`deploying aave v3 pool with beacon ${poolAddress}, atoken ${aToken}, aave pool ${aavePool}`);
pool = await hre.upgrades.deployBeaconProxy(
poolAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const main = async () => {
to: impersonatedAddr
});

const [ fusdcRedeemed, usdcRedeemed, wethRedeemed ] = await redeem(staking);
const [ fusdcRedeemed, usdcRedeemed, wethRedeemed ] = await redeem(staking, 0, 0, 0);

console.log(
`redeemed for addr ${impersonatedAddr}, fusdc redeemed ${fusdcRedeemed}, usdc redeemed ${usdcRedeemed}, weth redeemed ${wethRedeemed}`
Expand Down
2 changes: 1 addition & 1 deletion contracts/ethereum/test/lootbox-staking-deployed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe("LootboxStaking deployed infra", async () => {
method: "hardhat_setBalance",
params: [
stakingOperatorAddr,
ethers.constants.MaxUint256.toHexString()
MaxUint256.toHexString()
]
});

Expand Down
2 changes: 2 additions & 0 deletions contracts/ethereum/test/setup-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ before(async function () {
fwEthAccountSigner,
veGovSigner,
registrySigner,
blacklistedSigner
] = await hre.ethers.getSigners();

const councilAddress = await operatorCouncilSigner.getAddress();
Expand Down Expand Up @@ -146,6 +147,7 @@ before(async function () {
emergencyCouncil: tokenCouncilSigner,
externalOperator: tokenOperatorSigner,
externalOracle: externalOracleSigner,
blacklistedSigner: blacklistedSigner,
},

operator: {
Expand Down
2 changes: 2 additions & 0 deletions contracts/ethereum/test/setup-mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export let bindings: typeof commonBindings & {
oracleBoundOperator: ethers.Contract,
externalOperator: ethers.Contract,
emergencyCouncil: ethers.Contract,
blacklistedSpender: ethers.Contract
},
fei: {
base: ethers.Contract,
Expand Down Expand Up @@ -168,6 +169,7 @@ before(async function () {
fluidAccount2: contracts.usdt.deployedToken.connect(signers.userAccount2),
externalOperator: contracts.usdt.deployedToken.connect(signers.token.externalOperator),
emergencyCouncil: contracts.usdt.deployedToken.connect(signers.token.emergencyCouncil),
blacklistedSpender: contracts.usdt.deployedToken.connect(signers.token.blacklistedSigner),
oracleBoundOperator: contracts.operator.connect(signers.token.externalOracle),
},
fei: {
Expand Down
51 changes: 51 additions & 0 deletions contracts/ethereum/test/token.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import * as hre from "hardhat";

import * as ethers from 'ethers';

import { BigNumber } from 'ethers';
Expand All @@ -20,11 +22,15 @@ function fluidityReward(...winners: [string, number][]) {
]];
}

const MaxUint256 = ethers.constants.MaxUint256;

describe("Token", async function () {
let fUsdtOperator: ethers.Contract;
let fUsdtAccount: ethers.Contract;
let fUsdtOracle: ethers.Contract;
let fUsdtCouncil: ethers.Contract;
let fUsdtBlacklistedSpender: ethers.Contract;

let fluidToken: string;
let accountAddr: string;

Expand All @@ -38,6 +44,7 @@ describe("Token", async function () {
fluidAccount1: fUsdtAccount,
oracleBoundOperator: fUsdtOracle,
emergencyCouncil: fUsdtCouncil,
blacklistedSpender: fUsdtBlacklistedSpender,
},
} = bindings);
accountAddr = await signers.userAccount1.getAddress();
Expand Down Expand Up @@ -180,4 +187,48 @@ describe("Token", async function () {

it("does approvals correctly using eip2612", async () => {
});

it("ensures that the address is blacklisted and enforced correctly", async () => {
const blacklistedAddr = await signers.token.blacklistedSigner.getAddress();
const fUsdtOperatorAddr = await fUsdtOperator.signer.getAddress();

await hre.network.provider.request({
method: "hardhat_setBalance",
params: [blacklistedAddr, MaxUint256.toHexString()]
});

await fUsdtOperator.blacklistAddress(blacklistedAddr, true);

await expect(fUsdtBlacklistedSpender.transfer(fUsdtOperatorAddr, MaxUint256))
.to.be.revertedWith("address blacklisted");

fUsdtBlacklistedSpender.approve(fUsdtOperatorAddr, MaxUint256);

await expect(fUsdtOperator.transferFrom(blacklistedAddr, fUsdtOperatorAddr, MaxUint256))
.to.be.revertedWith("address blacklisted");

await expect(fUsdtBlacklistedSpender.transferFrom(fUsdtOperatorAddr, blacklistedAddr, MaxUint256))
.to.be.revertedWith("address blacklisted");

await expect(fUsdtBlacklistedSpender.erc20In(MaxUint256))
.to.be.revertedWith("address blacklisted");

await expect(fUsdtBlacklistedSpender.erc20Out(MaxUint256))
.to.be.revertedWith("address blacklisted");

await fUsdtOperator.blacklistAddress(blacklistedAddr, false);

await expect(fUsdtOperator.transferFrom(blacklistedAddr, fUsdtOperatorAddr, MaxUint256))
.to.be.revertedWith("ERC20: transfer amount exceeds balance");

await expect(fUsdtBlacklistedSpender.transferFrom(fUsdtOperatorAddr, blacklistedAddr, MaxUint256))
.to.be.revertedWith("insufficient allowance");

await expect(fUsdtBlacklistedSpender.erc20In(MaxUint256))
.to.be.revertedWith("SafeERC20: low-level call failed");

await expect(fUsdtBlacklistedSpender.erc20Out(MaxUint256))
.to.be.revertedWith("ERC20: burn amount exceeds balance");

});
});
1 change: 1 addition & 0 deletions contracts/ethereum/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export type FluiditySigners = {
emergencyCouncil: ethers.Signer,
externalOperator: ethers.Signer,
externalOracle: ethers.Signer,
blacklistedSigner: ethers.Signer,
},

operator: {
Expand Down

0 comments on commit 996fd65

Please sign in to comment.