I have custom ERC20 and ERC721 Contract, now my aim is to publish ERC20 token but have a function that only accepts my ERC721 contract address as msg.sender to mint the token and send it to its address, i also need ERC20 token address in my ERC721 contract to make transactions with it, currently i have something like this

customERC20Token = new CustomERC20Token(address(this));

in the constructor of my ERC721 contract

and in my ERC20 contract i initialize ERC721 token address like so

constructor(address erc721Address) ERC20("CUSTOM ERC 20 TOKEN", "CustomToken") {
    globalERC721Address = erc721Address;

i use this globalERC721Address in modifier to restrict function accessibility to ERC721 address.

Correct me if i am wrong but i believe everyone who will inherit this contract in their code and initialize with their desired address, will be able to act as an "owner" and mint the token to any address they want, how do i fix it?

I thought of adding a function in my ERC20 that would allow me to initialize the ERC 721 contract address manually rather than doing it in the constructor, then deploy the ERC 20, copy its address, paste it in my ERC721 constructor and deploy ERC721 contract. but it seems bit hacky. isn't there a better way to do this?

1 Answer 1


In short terms you want to "Only allow" a specific contract to call a specific function? if it is.. then:

Its time to implement Access Control contracts from openzeppelin; With this you could create a new role and limit function calls to addresses who had that role assigned


// SPDX-License-Identifier: MIT
// Creator: @casareafer
// Proof of Wololo

pragma solidity ^0.8.17;

import "./openzeppelin/ERC1155.sol";
import "./openzeppelin/AccessControl.sol";

contract Wololo is ERC1155, AccessControl {
    address public yourContract;
    bytes32 public constant adminControl = keccak256("Margaret_Hamilton");
    bytes32 public constant withdrawControl = keccak256("Marie_Curie");

    constructor(string memory _baseURI, string memory _contractURI) {

        //DEFAULT_ADMIN_ROLE is the Super Role that allow you to grant roles
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

        //this is my special role
        _grantRole(adminControl, msg.sender);
    //give the role to your contract address to be able to call the function

    function setYourMagicContract(address foo) external onlyRole(DEFAULT_ADMIN_ROLE){
    //burn control only callable by admins

    function burn(
        address from,
        uint256 id,
        uint256 amount
    ) external onlyRole(adminControl) {
        _burn(from, id, amount);

    //withdraw function only callable by withdrawControl dudes

    function withdraw() external onlyRole(withdrawControl) {

  • with this you can deploy contract separately and deal with the roles later;
    – Casareafer
    Nov 25, 2022 at 4:51

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.