Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate HardHat tests to DS-tests (Solidity tests) #601

Merged
merged 24 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6bb15f7
Migrate HardHat tests to DS-Test
richardpringle Apr 11, 2023
93d1571
Remove solidity 0.8.5 dependency
richardpringle May 2, 2023
3d0dc81
Add comments to example-deployer contract
richardpringle May 2, 2023
11c8d0b
Merge remote-tracking branch 'origin/master' into migrate-hardhat-to-…
richardpringle May 4, 2023
84a27c4
Move AllowListTest into test directory
richardpringle May 16, 2023
ba3cde5
Move ERC20NativeMinter test
richardpringle May 16, 2023
04b6924
Move ExampleDeployerList test
richardpringle May 16, 2023
926bd97
Change test_ to step_ for ds-tests
richardpringle May 16, 2023
d9dd232
Move ExampleFeeManagerTest
richardpringle May 16, 2023
1e1044e
Move ExampleRewardManagerTest
richardpringle May 16, 2023
59b1c79
Move ExampleTxAllowListTest
richardpringle May 16, 2023
82c47ae
Rename tests with Test suffix
richardpringle May 17, 2023
37a2737
Remove redundant native-minter variable
richardpringle May 17, 2023
6770605
Initialize interfaces as contract variables
richardpringle May 18, 2023
b315b50
Merge remote-tracking branch 'origin/master' into migrate-hardhat-to-…
richardpringle May 18, 2023
aaf472c
Move comments
richardpringle May 23, 2023
9bc25bf
Remove unused constant from example contract
richardpringle May 23, 2023
9f2e2e9
Fix allow-list role check in test
richardpringle May 23, 2023
83312f8
Check balances instead of roles in native-minter test
richardpringle May 23, 2023
b1e193a
Update contract-examples/test/contract_native_minter.ts
richardpringle May 23, 2023
db2cde0
Merge remote-tracking branch 'origin/master' into migrate-hardhat-to-…
richardpringle May 23, 2023
0b13065
Try to speed up e2e tests
richardpringle May 23, 2023
e049a0d
Make e2e-test block-rate 0
richardpringle May 23, 2023
f3e7fde
Revert "Make e2e-test block-rate 0"
richardpringle May 23, 2023
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
6 changes: 6 additions & 0 deletions contract-examples/contracts/AllowList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ contract AllowList is Ownable {
uint256 constant STATUS_ENABLED = 1;
uint256 constant STATUS_ADMIN = 2;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove these in favor of the new enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do that in a new PR... Otherwise, this PR will never get over the line.

Originally, I didn't want some weird intermediate state with half JS tests and half Solidity tests, but in hindsight, that was a mistake.


enum Role {
None,
Enabled,
Admin
}

constructor(address precompileAddr) Ownable() {
allowList = IAllowList(precompileAddr);
}
Expand Down
9 changes: 5 additions & 4 deletions contract-examples/contracts/ERC20NativeMinter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./AllowList.sol";
import "./INativeMinter.sol";

// Precompiled Native Minter Contract Address
address constant MINTER_ADDRESS = 0x0200000000000000000000000000000000000001;
// Designated Blackhole Address
address constant BLACKHOLE_ADDRESS = 0x0100000000000000000000000000000000000000;

contract ERC20NativeMinter is ERC20, AllowList {
// Precompiled Native Minter Contract Address
address constant MINTER_ADDRESS = 0x0200000000000000000000000000000000000001;
// Designated Blackhole Address
address constant BLACKHOLE_ADDRESS = 0x0100000000000000000000000000000000000000;
string private constant TOKEN_NAME = "ERC20NativeMinterToken";
string private constant TOKEN_SYMBOL = "XMPL";

Expand Down
13 changes: 11 additions & 2 deletions contract-examples/contracts/ExampleDeployerList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ import "@openzeppelin/contracts/access/Ownable.sol";
import "./IAllowList.sol";
import "./AllowList.sol";

address constant DEPLOYER_LIST = 0x0200000000000000000000000000000000000000;
address constant OTHER_ADDRESS = 0x0Fa8EA536Be85F32724D57A37758761B86416123;
richardpringle marked this conversation as resolved.
Show resolved Hide resolved

// ExampleDeployerList shows how ContractDeployerAllowList precompile can be used in a smart contract
// All methods of [allowList] can be directly called. There are example calls as tasks in hardhat.config.ts file.
contract ExampleDeployerList is AllowList {
// Precompiled Allow List Contract Address
address constant DEPLOYER_LIST = 0x0200000000000000000000000000000000000000;

constructor() AllowList(DEPLOYER_LIST) {}

function deployContract() public {
new Example();
}
}

// This is an empty contract that can be used to test contract deployment
contract Example {}
richardpringle marked this conversation as resolved.
Show resolved Hide resolved

76 changes: 47 additions & 29 deletions contract-examples/contracts/ExampleFeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,67 @@ import "@openzeppelin/contracts/access/Ownable.sol";
import "./AllowList.sol";
import "./IFeeManager.sol";

address constant FEE_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000003;

uint constant WAGMI_GAS_LIMIT = 20_000_000;
uint constant WAGMI_TARGET_BLOCK_RATE = 2;
uint constant WAGMI_MIN_BASE_FEE = 1_000_000_000;
uint constant WAGMI_TARGET_GAS = 100_000_000;
uint constant WAGMI_BASE_FEE_CHANGE_DENOMINATOR = 48;
uint constant WAGMI_MIN_BLOCK_GAS_COST = 0;
uint constant WAGMI_MAX_BLOCK_GAS_COST = 10_000_000;
uint constant WAGMI_BLOCK_GAS_COST_STEP = 500_000;

uint constant CCHAIN_GAS_LIMIT = 8_000_000;
uint constant CCHAIN_TARGET_BLOCK_RATE = 2;
uint constant CCHAIN_MIN_BASE_FEE = 25_000_000_000;
uint constant CCHAIN_TARGET_GAS = 15_000_000;
uint constant CCHAIN_BASE_FEE_CHANGE_DENOMINATOR = 36;
uint constant CCHAIN_MIN_BLOCK_GAS_COST = 0;
uint constant CCHAIN_MAX_BLOCK_GAS_COST = 1_000_000;
uint constant CCHAIN_BLOCK_GAS_COST_STEP = 100_000;

struct FeeConfig {
uint256 gasLimit;
uint256 targetBlockRate;
uint256 minBaseFee;
uint256 targetGas;
uint256 baseFeeChangeDenominator;
uint256 minBlockGasCost;
uint256 maxBlockGasCost;
uint256 blockGasCostStep;
}

// ExampleFeeManager shows how FeeManager precompile can be used in a smart contract
// All methods of [allowList] can be directly called. There are example calls as tasks in hardhat.config.ts file.
contract ExampleFeeManager is AllowList {
// Precompiled Fee Manager Contract Address
address constant FEE_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000003;
IFeeManager feeManager = IFeeManager(FEE_MANAGER_ADDRESS);

struct FeeConfig {
uint256 gasLimit;
uint256 targetBlockRate;
uint256 minBaseFee;
uint256 targetGas;
uint256 baseFeeChangeDenominator;
uint256 minBlockGasCost;
uint256 maxBlockGasCost;
uint256 blockGasCostStep;
}

constructor() AllowList(FEE_MANAGER_ADDRESS) {}

function enableWAGMIFees() public onlyEnabled {
feeManager.setFeeConfig(
20_000_000, // gasLimit
2, // targetBlockRate
1_000_000_000, // minBaseFee
100_000_000, // targetGas
48, // baseFeeChangeDenominator
0, // minBlockGasCost
10_000_000, // maxBlockGasCost
500_000 // blockGasCostStep
WAGMI_GAS_LIMIT,
WAGMI_TARGET_BLOCK_RATE,
WAGMI_MIN_BASE_FEE,
WAGMI_TARGET_GAS,
WAGMI_BASE_FEE_CHANGE_DENOMINATOR,
WAGMI_MIN_BLOCK_GAS_COST,
WAGMI_MAX_BLOCK_GAS_COST,
WAGMI_BLOCK_GAS_COST_STEP
);
}

function enableCChainFees() public onlyEnabled {
feeManager.setFeeConfig(
8_000_000, // gasLimit
2, // targetBlockRate
25_000_000_000, // minBaseFee
15_000_000, // targetGas
36, // baseFeeChangeDenominator
0, // minBlockGasCost
1_000_000, // maxBlockGasCost
200_000 // blockGasCostStep
CCHAIN_GAS_LIMIT,
CCHAIN_TARGET_BLOCK_RATE,
CCHAIN_MIN_BASE_FEE,
CCHAIN_TARGET_GAS,
CCHAIN_BASE_FEE_CHANGE_DENOMINATOR,
CCHAIN_MIN_BLOCK_GAS_COST,
CCHAIN_MAX_BLOCK_GAS_COST,
CCHAIN_BLOCK_GAS_COST_STEP
);
}

Expand Down
3 changes: 2 additions & 1 deletion contract-examples/contracts/ExampleRewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ pragma solidity ^0.8.0;
import "./IRewardManager.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

address constant REWARD_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000004;

// ExampleRewardManager is a sample wrapper contract for RewardManager precompile.
contract ExampleRewardManager is Ownable {
address constant REWARD_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000004;
IRewardManager rewardManager = IRewardManager(REWARD_MANAGER_ADDRESS);

constructor() Ownable() {}
Expand Down
15 changes: 11 additions & 4 deletions contract-examples/contracts/ExampleTxAllowList.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./AllowList.sol";
import "./IAllowList.sol";

// Precompiled Allow List Contract Address
address constant TX_ALLOW_LIST = 0x0200000000000000000000000000000000000002;
address constant OTHER_ADDRESS = 0x0Fa8EA536Be85F32724D57A37758761B86416123;

// ExampleTxAllowList shows how TxAllowList precompile can be used in a smart contract
// All methods of [allowList] can be directly called. There are example calls as tasks in hardhat.config.ts file.
contract ExampleTxAllowList is AllowList {
// Precompiled Allow List Contract Address
address constant TX_ALLOW_LIST = 0x0200000000000000000000000000000000000002;

constructor() AllowList(TX_ALLOW_LIST) {}

function deployContract() public {
new Example();
}
}

contract Example {}
11 changes: 11 additions & 0 deletions contract-examples/contracts/test/AllowListTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../AllowList.sol";
import "ds-test/src/test.sol";

contract AllowListTest is DSTest {
function assertRole(uint result, AllowList.Role role) internal {
assertEq(result, uint(role));
}
}
144 changes: 144 additions & 0 deletions contract-examples/contracts/test/ERC20NativeMinterTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../ERC20NativeMinter.sol";
import "../INativeMinter.sol";
import "./AllowListTest.sol";

// TODO:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for stripping this down to only the testing logic

// this contract adds another (unwanted) layer of indirection
// but it's the easiest way to match the previous HardHat testing functionality.
// Once we completely migrate to DS-test, we can simplify this set of tests.
contract Minter {
ERC20NativeMinter token;

constructor(address tokenAddress) {
token = ERC20NativeMinter(tokenAddress);
}

function mintdraw(uint amount) external {
token.mintdraw(amount);
}

function deposit(uint value) external {
token.deposit{value: value}();
}
}

contract ERC20NativeMinterTest is AllowListTest {
INativeMinter nativeMinter = INativeMinter(MINTER_ADDRESS);

function setUp() public {
// noop
}

function step_mintdrawFailure() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

assertRole(nativeMinter.readAllowList(tokenAddress), AllowList.Role.None);

try token.mintdraw(100) {
assertTrue(false, "mintdraw should fail");
} catch {} // TODO should match on an error to make sure that this is failing in the way that's expected
}

function step_addMinter() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

assertRole(nativeMinter.readAllowList(tokenAddress), AllowList.Role.None);

nativeMinter.setEnabled(tokenAddress);

assertRole(nativeMinter.readAllowList(tokenAddress), AllowList.Role.Enabled);
}

function step_adminMintdraw() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

address testAddress = address(this);

nativeMinter.setEnabled(tokenAddress);

uint initialTokenBalance = token.balanceOf(testAddress);
uint initialNativeBalance = testAddress.balance;

uint amount = 100;

token.mintdraw(amount);

assertEq(token.balanceOf(testAddress), initialTokenBalance - amount);
assertEq(testAddress.balance, initialNativeBalance + amount);
}

function step_minterMintdrawFailure() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

Minter minter = new Minter(tokenAddress);
address minterAddress = address(minter);

nativeMinter.setEnabled(tokenAddress);

uint initialTokenBalance = token.balanceOf(minterAddress);
uint initialNativeBalance = minterAddress.balance;

assertEq(initialTokenBalance, 0);

try minter.mintdraw(100) {
assertTrue(false, "mintdraw should fail");
} catch {} // TODO should match on an error to make sure that this is failing in the way that's expected

assertEq(token.balanceOf(minterAddress), initialTokenBalance);
assertEq(minterAddress.balance, initialNativeBalance);
}

function step_minterDeposit() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

Minter minter = new Minter(tokenAddress);
address minterAddress = address(minter);

nativeMinter.setEnabled(tokenAddress);

uint amount = 100;

nativeMinter.mintNativeCoin(minterAddress, amount);

uint initialTokenBalance = token.balanceOf(minterAddress);
uint initialNativeBalance = minterAddress.balance;

minter.deposit(amount);

assertEq(token.balanceOf(minterAddress), initialTokenBalance + amount);
assertEq(minterAddress.balance, initialNativeBalance - amount);
}

function step_mintdraw() public {
ERC20NativeMinter token = new ERC20NativeMinter(1000);
address tokenAddress = address(token);

Minter minter = new Minter(tokenAddress);
address minterAddress = address(minter);

nativeMinter.setEnabled(tokenAddress);

uint amount = 100;

uint initialNativeBalance = minterAddress.balance;
assertEq(initialNativeBalance, 0);

token.mint(minterAddress, amount);

uint initialTokenBalance = token.balanceOf(minterAddress);
assertEq(initialTokenBalance, amount);

minter.mintdraw(amount);

assertEq(token.balanceOf(minterAddress), 0);
assertEq(minterAddress.balance, amount);
}
}
Loading