0

I'm relatively new to Solidity and currently working on building a DAO using the OpenZeppelin contract wizard. I've generated ERC-20 token and governor contracts using the wizard to kickstart my project. However, I encountered an issue while setting up my unit tests. Specifically, I'm receiving an error message "AccessManagedUnauthorized" when attempting to mint tokens.

Here's how I've set up my test contract:

contract KachalGovTest is Test {
    KachalCoin kcoin;
    TimeLock timelock;

    address public USER = makeAddr("user");
    uint256 public constant INITIAL_SUPPLY = 10 ether;
    function setUp() public {
        vm.startPrank(USER);
        kcoin = new KachalCoin(USER);
        kcoin.mint(USER, INITIAL_SUPPLY);
        kcoin.setAuthority(USER);
        kcoin.delegate(USER);
        timelock = new TimeLock(MIN_DELAY, proposers, executors);
    ```
    I'm utilizing the Foundry framework for testing. Below is the remainder of my test contract along with the ERC-20 token implementation.

I've attempted to dive into the documentation and explore the OpenZeppelin contract code, particularly examining contracts like AccessManaged, but unfortunately, I'm still struggling to grasp the issue. Any guidance or insight on resolving this would be greatly appreciated. Thank you!

test contract:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {KachalBox} from "../src/KachalBox.sol";
import {KachalCoin} from "../src/KachalCoin.sol";
import {KachalGov} from "../src/KachalGov.sol";
import {TimeLock} from "../src/TimeLock.sol";

contract KachalGovTest is Test {
    KachalBox kbox;
    KachalCoin kcoin;
    KachalGov kgov;
    TimeLock timelock;

    address public USER = makeAddr("user");
    uint256 public constant INITIAL_SUPPLY = 10 ether;
    // arguments for TimeLock
    uint256 public constant MIN_DELAY = 3600; // 1 hour after a vote passes
    address[] proposers;
    address[] executors;

    function setUp() public {
        vm.startPrank(USER);
        kcoin = new KachalCoin(USER);
        kcoin.mint(USER, INITIAL_SUPPLY);
        kcoin.setAuthority(USER);
        kcoin.delegate(USER);
        timelock = new TimeLock(MIN_DELAY, proposers, executors);

        // now we can deploy our gov contract

        kgov = new KachalGov(kcoin, timelock);

        // now we should give some rules
        // the way that time locker has roles, it hashes the names of stuff
        bytes32 proposersRole = timelock.PROPOSER_ROLE();
        // there is a function in timelock name PROPOSER_ROLE, and we are going to alow this
        // proposer role to be just the governer, so only the governor can propose stuff to the timelock
        bytes32 executorRole = timelock.EXECUTOR_ROLE();
        // any body can execute by setting it to zero address
        bytes32 adminRole = timelock.DEFAULT_ADMIN_ROLE();
        // time lock start with some default rolls
        // and we need to grant the governor whole bunch of rolls
        timelock.grantRole(proposersRole, address(kgov));
        // as a grant role function, we grant the proposersRole to the governor so only the governor
        // can actually propose stuff to timelock\
        timelock.grantRole(executorRole, address(0));
        // and we need to remove our-selves as the admin of time locker
        timelock.revokeRole(adminRole, address(USER));
        // so the user can no longer be the admin
        vm.stopPrank();

        kbox = new KachalBox();
        kbox.transferOwnership(address(timelock));
    }

    // now we can write a quick test:
    function testCantUpdateBoxWithoutGovernance() public {
        vm.expectRevert();
        kbox.store(1);
    }

    function testWhoIsTheInitialAuthority() public view {
        // kcoin = new KachalCoin(USER);
        assertEq(kcoin.authority(), USER, "The initial Authority should be USER address!");
    }

    function testUSERCanMint() public {
        vm.startPrank(USER);
        kcoin.mint(USER, INITIAL_SUPPLY);
        vm.stopPrank();
        assertEq(kcoin.balanceOf(USER), INITIAL_SUPPLY, "The USER address should have the minted tokens");
    }

    function testMintingPermissions() public {
        vm.startPrank(USER); // Set caller as the governor
        kcoin.mint(USER, 100);
        vm.stopPrank();
        assertEq(kcoin.balanceOf(USER), INITIAL_SUPPLY + 100, "The governor should be able to mint tokens");
    }
}

ERC-20 contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import {ERC20Permit, Nonces} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

contract KachalCoin is ERC20, ERC20Burnable, ERC20Pausable, AccessManaged, ERC20Permit, ERC20Votes {
    constructor(address initialAuthority)
        ERC20("KachalCoin", "KCH")
        AccessManaged(initialAuthority)
        ERC20Permit("KachalCoin")
    {}

    function pause() public restricted {
        _pause();
    }

    function unpause() public restricted {
        _unpause();
    }

    function mint(address to, uint256 amount) public restricted {
        _mint(to, amount);
    }

    function clock() public view override returns (uint48) {
        return uint48(block.timestamp);
    }

    // solhint-disable-next-line func-name-mixedcase
    function CLOCK_MODE() public pure override returns (string memory) {
        return "mode=timestamp";
    }

    // The following functions are overrides required by Solidity.

    function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Pausable, ERC20Votes) {
        super._update(from, to, value);
    }

    function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }
}

here is the error massage:

[FAIL. Reason: setup failed: AccessManagedUnauthorized(0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D)] setUp() (gas: 0)
Traces:
  [2021802] KachalGovTest::setUp()
    ├─ [0] VM::startPrank(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
    │   └─ ← [Return] 
    ├─ [1957628] → new KachalCoin@0x7BD1119CEC127eeCDBa5DCA7d1Bd59986f6d7353
    │   ├─ emit AuthorityUpdated(authority: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
    │   └─ ← [Return] 9428 bytes of code
    ├─ [1900] KachalCoin::mint(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], 10000000000000000000 [1e19])
    │   ├─ [0] user::canCall(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], KachalCoin: [0x7BD1119CEC127eeCDBa5DCA7d1Bd59986f6d7353], 0x40c10f1900000000000000000000000000000000000000000000000000000000) [staticcall]
    │   │   └─ ← [Stop] 
    │   └─ ← [Revert] AccessManagedUnauthorized(0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D)
    └─ ← [Revert] AccessManagedUnauthorized(0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D)

Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 2.01ms (0.00ns CPU time)

Ran 1 test suite in 1.58s (2.01ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)

Failing tests:
Encountered 1 failing test in test/KachalGovTest.t.sol:KachalGovTest
[FAIL. Reason: setup failed: AccessManagedUnauthorized(0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D)] setUp() (gas: 0)

Encountered a total of 1 failing tests, 0 tests succeeded
1
  • I kinda figure out that the USER address does not have permission to mint tokens but I'm struggling to figure out what address has the permission to mint tokens. I couldn't figure out how to find the address that has these permissions through testing. I know that I can set Authority through the functions, but still wasn't able to do so because I thought the address of the test contract had the permission to mint and also transfer Authority but I was wrong. Commented May 7 at 16:46

2 Answers 2

1

You shouldn't use the AccessManaged library but the Ownable one.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
//import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20Permit, Nonces} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

contract KachalCoin is ERC20, ERC20Burnable, ERC20Pausable, Ownable, ERC20Permit, ERC20Votes {
    constructor(address initialAuthority)
        ERC20("KachalCoin", "KCH")
        Ownable(initialAuthority)
        ERC20Permit("KachalCoin")
    {}

    function pause() public onlyOwner {
        _pause();
    }

    ...
1

You may do the following to get it right:

  1. import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

Use this above import instead of Access Managed.

  1. In place of Ownable(initialAuthority), use Ownable(msg.sender).
  2. Modify the constructor in the following manner:
constructor()
        ERC20("KachalCoin", "KCH")
        Ownable(msg.sender)
        ERC20Permit("KachalCoin")
    {}

I hope this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.