Skip to content

Commit

Permalink
feat: Add control to block mints in Faucet (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelmtzinf authored May 26, 2023
1 parent 1fdd23b commit 19ce4be
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
20 changes: 17 additions & 3 deletions contracts/mocks/testnet-helpers/Faucet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import {IFaucet} from './IFaucet.sol';
* @dev Ownable Faucet Contract
*/
contract Faucet is IFaucet, Ownable {
/// @inheritdoc IFaucet
uint256 public constant MAX_MINT_AMOUNT = 10000;

// Mapping to control mint of assets (allowed by default)
mapping(address => bool) internal _nonMintable;

// If _permissioned is enabled, then only owner can mint Testnet ERC20 tokens
// If disabled, anyone can call mint at the faucet, for PoC environments
bool internal _permissioned;

// Maximum amount of tokens per mint allowed
uint256 public constant MAX_MINT_AMOUNT = 10000;

constructor(address owner, bool permissioned) {
require(owner != address(0));
transferOwnership(owner);
Expand All @@ -39,6 +42,7 @@ contract Faucet is IFaucet, Ownable {
address to,
uint256 amount
) external override onlyOwnerIfPermissioned returns (uint256) {
require(!_nonMintable[token], 'Error: not mintable');
require(
amount <= MAX_MINT_AMOUNT * (10 ** TestnetERC20(token).decimals()),
'Error: Mint limit transaction exceeded'
Expand All @@ -58,6 +62,16 @@ contract Faucet is IFaucet, Ownable {
return _permissioned;
}

/// @inheritdoc IFaucet
function setMintable(address asset, bool active) external override onlyOwner {
_nonMintable[asset] = !active;
}

/// @inheritdoc IFaucet
function isMintable(address asset) external view override returns (bool) {
return !_nonMintable[asset];
}

/// @inheritdoc IFaucet
function transferOwnershipOfChild(
address[] calldata childContracts,
Expand Down
20 changes: 20 additions & 0 deletions contracts/mocks/testnet-helpers/IFaucet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
pragma solidity ^0.8.0;

interface IFaucet {
/**
* @notice Returns the maximum amount of tokens per mint allowed
* @return The maximum amount of tokens per mint allowed
*/
function MAX_MINT_AMOUNT() external pure returns (uint256);

/**
* @notice Function to mint Testnet tokens to the destination address
* @param token The address of the token to perform the mint
Expand All @@ -23,6 +29,20 @@ interface IFaucet {
*/
function isPermissioned() external view returns (bool);

/**
* @notice Enable or disable the minting of the faucet asset
* @param asset The address of the asset
* @param active True to enable, false to disable
*/
function setMintable(address asset, bool active) external;

/**
* @notice Returns whether the asset is mintable
* @param asset The address of the asset
* @return True if the asset is mintable, false otherwise
*/
function isMintable(address asset) external view returns (bool);

/**
* @notice Transfer the ownership of child contracts
* @param childContracts A list of child contract addresses
Expand Down
44 changes: 42 additions & 2 deletions test/faucet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from 'chai';
import { parseEther } from 'ethers/lib/utils';
import { ONE_ADDRESS, evmRevert, evmSnapshot, waitForTx } from '@aave/deploy-v3';
import { makeSuite, TestEnv } from './helpers/make-suite';
import { Ownable__factory, TestnetERC20__factory } from '../types';
import { TestnetERC20__factory } from '../types';

declare let hre: HardhatRuntimeEnvironment;

Expand All @@ -28,6 +28,7 @@ makeSuite('Faucet', (testEnv: TestEnv) => {
// Enforce permissioned mode as disabled for deterministic test suite
await waitForTx(await faucetOwnable.setPermissioned(false));
});

it('Mint can be called by anyone', async () => {
const {
users: [user],
Expand All @@ -54,7 +55,7 @@ makeSuite('Faucet', (testEnv: TestEnv) => {

const threshold = await faucetOwnable.connect(deployer.signer).MAX_MINT_AMOUNT();
const thresholdValue = threshold.toNumber();
const withinLimitThreshold = parseEther((thresholdValue).toString());
const withinLimitThreshold = parseEther(thresholdValue.toString());

await faucetOwnable
.connect(deployer.signer)
Expand All @@ -77,6 +78,45 @@ makeSuite('Faucet', (testEnv: TestEnv) => {
faucetOwnable.connect(deployer.signer).mint(dai.address, user.address, maxLimitThreshold)
).to.be.revertedWith('Error: Mint limit transaction exceeded');
});

it('Non-owner tries to deactivate minting (revert expected)', async () => {
const {
users: [, , user],
dai,
} = testEnv;

await expect(
faucetOwnable.connect(user.signer).setMintable(dai.address, false)
).to.be.revertedWith('Ownable: caller is not the owner');
});

it('Owner deactivates mint', async () => {
const {
deployer,
users: [user],
dai,
} = testEnv;

expect(await faucetOwnable.isMintable(dai.address)).to.be.true;
await waitForTx(await faucetOwnable.connect(deployer.signer).setMintable(dai.address, false));
expect(await faucetOwnable.isMintable(dai.address)).to.be.false;

await expect(
faucetOwnable.connect(user.signer).mint(dai.address, user.address, 1)
).to.be.revertedWith('Error: not mintable');

expect(await faucetOwnable.isMintable(dai.address)).to.be.false;
await waitForTx(await faucetOwnable.connect(deployer.signer).setMintable(dai.address, true));
expect(await faucetOwnable.isMintable(dai.address)).to.be.true;

const balanceBefore = await dai.balanceOf(user.address);
expect(await faucetOwnable.connect(user.signer).mint(dai.address, user.address, 1));
expect(await dai.balanceOf(user.address)).eq(balanceBefore.add(1));
});

it('Getter isPermissioned should return false', async () => {
await expect(await faucetOwnable.isPermissioned()).is.equal(false);
});
});

describe('Permissioned mode: enabled', () => {
Expand Down

0 comments on commit 19ce4be

Please sign in to comment.