Is possible to allow smart contact users to supply their own token?

Hi:
I'm new to the solidity and smart contract, I know this question may be stupid, but just want to confirm those things, and make sure I understand those concepts crystal.

I'm writing a smart contract that allows vendors/sellers to release tokens as a discount coupons for their consumers. I just wrote this for practicing and learning, my question is, is possible to deploy this contract once, but allows multiple vendors to call this contract, supply their own token without deploying the contract again?
here is my code:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

//the token is used as coupon
contract CPT is ERC20,Ownable,ERC20Burnable{
   constructor(uint256 initialSupply)  ERC20("TCoupon", "CPT") {
        require(initialSupply>=0,"supply token number must bigger than 0!");
        _mint(msg.sender, initialSupply);
    }
    //the consumer who get the CPT token can use it to get a discount
    struct Consumer {
        Product purchasedProdcut; //which product the comsumer purchased
        bool notSpent; //the customer already spent coupon or not
        address id;
    }

    struct Product {
        string name;
        uint id;
        uint256 price;
        uint256 stock; 
        bool _exist;
    }

    mapping(address=>Consumer) consumers;
    mapping(uint=>Product) product;
    uint[] productIdList;
    
    event couponGet(address indexed receipient,uint tokenAmount,uint256 timeStamp);
    event couponUsed(Consumer consumer);
    event productSet(Product product);
    function decimals() public view virtual override returns (uint8) {
        return 1;
    }
   
    //consumer get token,one address is allowed to get one token/coupon only
    function getCoupon(address receipient) external  returns (bool) {
         require(receipient != address(0), "invalid address");
        //check if the receipient is the owner or not
        require(receipient!=owner(),"don't send coupon to the vendor!");
        //check if address already has token or not
        require(balanceOf(receipient)==0,"You already have the coupon!");
        //check if the balance of owner >=1
        require(balanceOf(owner())>=1,"Not enough coupon!");
       (bool sent) =transfer(receipient,1);
        require(sent, "Failed to buy the coupon");
        consumers[receipient].notSpent=true;
        emit couponGet(receipient,1,block.timestamp);
        return sent;
    }

function useCoupon(uint productId) external returns(bool){
    //check if the product exists
    require(product[productId]._exist,"product not found");
    //check if the stock of product bigger than 1
    require(product[productId].stock>=1,"out of stock");
    //check if the msg.sender has 1 token and not spent yet
    require(balanceOf(msg.sender)==1,"invalid balance!");
    //owner should not use coupon
    require(msg.sender!=owner(),"vendor should not use coupon!");
    bool spent= consumers[msg.sender].notSpent;
    require(spent,"you have spent your token");
    product[productId].stock-=1;
    //add product to consumer
    consumers[msg.sender].purchasedProdcut=product[productId];
    consumers[msg.sender].notSpent=false;
    consumers[msg.sender].id=msg.sender;
    //burn the token
    burn(1);
    emit couponUsed(consumers[msg.sender]);
   return true;
}
    //we need to check if the product is already in the contract before we 
    //put a new product in it. 
    function setProduct(string memory name,uint id,uint256 price,uint256 stock) external onlyOwner {
      require(product[id]._exist==false,"This product is already exist!");
      product[id]=Product(name,id,price,stock,true);
      productIdList.push(id);
      emit productSet(product[id]);
    }

    function getProduct(uint id) public view returns(Product memory){
        if(product[id]._exist){
            return product[id];
        }
      revert('Not found');
    }
    
    //we may not need to handle the fetch of all products in contract
    //we can do it in the front end by looping the productIdList.
    function getProductIDList() public view returns(uint[] memory){
        return productIdList;
    }
    function getProducts() public view returns (Product[] memory ){
      Product[] memory Iproducts=new Product[](productIdList.length);
       for (uint i = 0; i < productIdList.length; i++) {
          Product memory Iproduct = product[productIdList[i]];
          Iproducts[i] = Iproduct;
      }
      return Iproducts;
    }
    function getConsumerInfo(address consumerAdrs) public view returns(Consumer memory){
         require(consumerAdrs != address(0), "invalid address");
        return consumers[consumerAdrs];
    }
}

if not, is use the access control to assign a minter role the right method? like allowing a vendor as a minter to mint 1000 tokens as token for purchasing its products. thanks

1 Like

will the token be multiple or just a single erc20 token?
can the system verify that vendor mint specific amount of token?
Lots of code up there and you definitely know what you are doing.

Hi @Boluwatife_Adegbola , thanks for your reply. I'm thinking of supplying multiply ec20 tokens after deploying this contract. It is like each vendor can have its own instance of this smart contract, has its own name and symbol, those tokens can be only used to the products which the vendor set, it is like a class-instance concept, one instance has nothing to do with other instance, in input different parameters to instantiate instances, you get different objects. But I guess, this may not be the case for smart contracts, right? I mean, can I set the token name, symbol and amount in a public function, rather than in the constructor. then the vendor can call the public function to release its own token even after the contract is already deployed to the blockchain.

1 Like

Yes. this is possible. What interface will users interact with this token, a Dapp(a webpage) or from CryptoWallet Interface?

You can set token name, symbol, edit it anytime in a public function after deployment. Constructor code only run once during deployment. Any other action/changes after can be done via public function.

1 Like

Great, thank you. It looks like the Dapp just works like other normal programs.

Yes. It works like normal programs.

What is erc20 token is any smart contract code that uses and satisfies the IERC20 interface. How you implement it is left to you and your code.

It will be difficult for this kind of token to be visible on Crypto Wallet Interface because it is not the normal usual ERC20 kind of token, but won't be problem to a Dapp since the logic and query will be part of the website

Hi @Boluwatife_Adegbola , thanks for your reply. I am going to build a website to interact with this contract.

I still have some unclear about how smart contracts work, I will really appreciate it if you can help me to figure this out.

Question 1: let’s say, if a vendor A creates its ERC20 token name “AAA”, the amount is 1000, this actually means that a message “a ERC20 token name AAA is released by A, now, A has 1000 balance of AAA tokens” is written to the contract address, in Ethereum; and another vendor B creates its ERC20 token name “BBB”, the amount is 2000, then another message “a ERC20 token name BBB is released by B, now, B has 2000 balance of BBB tokens” is written to the contract address. They create token via calling a public token supply function. Is this how supply token work? Do I understand it correctly?

Question 2: if I want to record two lists, one is what kind of products the vendor has, and another is who has its token/coupon, I will have to persist them to contract storage(the state variables that are outside the function), right? I may have to write codes like this:

struct VendorInfo{

Product[] productList;

Consumer[] consumerList;

}

mapping(address=>VendorInfo) vendorInfos;

All vendors share their own data in the same contract storage(address), right? Unlike cookies in browser, each website has its own cookie storage, other websites can not access to it. Do I understand the contact storage thing, right?

Best regards

1 Like

Yes, Question 1 works like that. They add to existing created token by adding more token to their wallet address, each token will have a way to identify it (mint function), all of the various token will use same method though through it identifier.

Question2; yes, that kind of structure.

Great, now I have a better understanding of smart contracts. thanks for your help.