How to combine two ERC20 contracts

Hi, I have a simple ERC20 token contract and I would like to combine the PreciousChickenToken.sol contract in order to be able to create a react front end for my contract that I can interact with.

Here is my simple ERC20 token contract:
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

 // Constructor code is only run when the contract is created
 // "decimals" can be excluded from the contructor parameters
 // strings are reference types which are stored
 // The keyword "public" makes variables accessible from other contracts
 // The variables totalSupply and balances are represented by a _mint function
 // The contract will mint 1000 tokens and transfer too the owner/admin account
contract MyToken is ERC20, Pausable, Ownable {
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) public { 
    _mint(msg.sender, 1000);
}
}

And here is the code for MyPreciousChicken:

    // SPDX-License-Identifier: Unlicensed
    pragma solidity ^0.6.2;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

    contract PreciousChickenToken is ERC20 {

        // In reality these events are not needed as the same information is included
        // in the default ERC20 Transfer event, but they serve as demonstrators
        event PCTBuyEvent (
            address from,
            address to,
            uint256 amount
        );
        
        event PCTSellEvent (
            address from,
            address to,
            uint256 amount
        );

        address private owner;
        mapping (address => uint256) pendingWithdrawals;
       
        // Initialises smart contract with supply of tokens going to the address that
        // deployed the contract.
        constructor(uint256 _initialSupply) public ERC20("PreciousChickenToken", "PCT") {
            _mint(msg.sender, _initialSupply);
            _setupDecimals(0); // Sets PCTs as integers only
            owner = msg.sender;
        }

        // A wallet sends Eth and receives PCT in return
        function buyToken(uint256 _amount) external payable {
            // Ensures that correct amount of Eth sent for PCT
            // 1 ETH is set equal to 1 PCT
            require(_amount == ((msg.value / 1 ether)), "Incorrect amount of Eth.");
            transferFrom(owner, msg.sender, _amount);
            emit PCTBuyEvent(owner, msg.sender, _amount);
        }
        
        // A wallet sends PCT and receives Eth in return
        function sellToken(uint256 _amount) public {
            pendingWithdrawals[msg.sender] = _amount;
            transfer(owner, _amount);
            withdrawEth();
            emit PCTSellEvent(msg.sender, owner, _amount);
        }

        // Using the Withdraw Pattern to remove Eth from contract account when user
        // wants to return PCT
        // https://solidity.readthedocs.io/en/latest/common-patterns.html#withdrawal-from-contracts
         function withdrawEth() public {
            uint256 amount = pendingWithdrawals[msg.sender];
            // Pending refund zerod before to prevent re-entrancy attacks
            pendingWithdrawals[msg.sender] = 0;
            msg.sender.transfer(amount * 1 ether);
        }
    }
    ```
1 Like

Hi @crypto_economics,

I recommend that functionality in a token be required for the life of the token. If functionality isn’t required for the life of the token, then it would be better in a separate contract. Hence I believe that sale functionality should be in a separate contract to the token.

If you want a crowdsale, then you can use https://docs.openzeppelin.com/contracts/2.x/crowdsales

I also recommend considering: Points to consider when creating a fungible token (ERC20, ERC777)

1 Like

@abcoathup Understood. Could you help me separate out the functions that aren’t’ needed and to combine the two contracts? I just need a simple buy/sell function–with metamask login and connected address visible.

1 Like

Hi @crypto_economics,

I suggest looking at this very simple crowdsale: Help me write an erc20 token and a crowdsale contract

1 Like

@abcoathup thank you for this. The reason why I am trying to desperately combine the other contracts is because PreciousChicken has front end code which I need to display/interact with my contract.

1 Like

Hi @crypto_economics,

I assume it isn’t too complex to add two contracts to existing front end code. This would be better in my opinion than adding short lived functionality to a token.

What is the front end that @PreciousChicken created?

2 Likes

@abcoathup thanks for the quick response as always. You don’t’ know how helpful the forum is. So do I understand you correctly that we could combine the two contracts to utilize the existing front end code? I have been trying to run this on my environment for almost two weeks now and finally got it to work. I’d like to try and utilize his contract if possible. Here is the front end created by PreciousChicken.

```
import React, { useState } from 'react';
import './App.css';
import { ethers } from "ethers";
import PreciousChickenToken from "./contracts/PreciousChickenToken.json";
import { Button, Alert } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';

// Needs to change to reflect current PreciousChickenToken address
const contractAddress ='0xa8dC92bEeF9E5D20B21A5CC01bf8b6a5E0a51888';

let provider;
let signer;
let erc20;
let noProviderAbort = true;

// Ensures metamask or similar installed
if (typeof window.ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) {
	try{
		// Ethers.js set up, gets data from MetaMask and blockchain
		window.ethereum.enable().then(
			provider = new ethers.providers.Web3Provider(window.ethereum)
		);
		signer = provider.getSigner();
		erc20 = new ethers.Contract(contractAddress, PreciousChickenToken.abi, signer);
		noProviderAbort = false;
	} catch(e) {
		noProviderAbort = true;
	}
}

function App() {
	const [walAddress, setWalAddress] = useState('0x00');
	const [pctBal, setPctBal] = useState(0);
	const [ethBal, setEthBal] = useState(0);
	const [coinSymbol, setCoinSymbol] = useState("Nil");
	const [transAmount, setTransAmount] = useState('0');
	const [pendingFrom, setPendingFrom] = useState('0x00');
	const [pendingTo, setPendingTo] = useState('0x00');
	const [pendingAmount, setPendingAmount] = useState('0');
	const [isPending, setIsPending] = useState(false);
	const [errMsg, setErrMsg] = useState("Transaction failed!");
	const [isError, setIsError] = useState(false);

	// Aborts app if metamask etc not present
	if (noProviderAbort) {
		return (
			<div>
			<h1>Error</h1>
			<p><a href="https://metamask.io">Metamask</a> or equivalent required to access this page.</p>
			</div>
		);
	}

	// Notification to user that transaction sent to blockchain
	const PendingAlert = () => {
		if (!isPending) return null;
		return (
			<Alert key="pending" variant="info" 
			style={{position: 'absolute', top: 0}}>
			Blockchain event notification: transaction of {pendingAmount} 
			&#x39e; from <br />
			{pendingFrom} <br /> to <br /> {pendingTo}.
			</Alert>
		);
	};

	// Notification to user of blockchain error
	const ErrorAlert = () => {
		if (!isError) return null;
		return (
			<Alert key="error" variant="danger" 
			style={{position: 'absolute', top: 0}}>
			{errMsg}
			</Alert>
		);
	};

	// Sets current balance of PCT for user
	signer.getAddress().then(response => {
		setWalAddress(response);
		return erc20.balanceOf(response);
	}).then(balance => {
		setPctBal(balance.toString())
	});

	// Sets current balance of Eth for user
	signer.getAddress().then(response => {
		return provider.getBalance(response);
	}).then(balance => {
		let formattedBalance = ethers.utils.formatUnits(balance, 18);
		setEthBal(formattedBalance.toString())
	});

	// Sets symbol of ERC20 token (i.e. PCT)
	async function getSymbol() {
		let symbol = await erc20.symbol();
		return symbol;
	}
	let symbol = getSymbol();
	symbol.then(x => setCoinSymbol(x.toString()));

	// Interacts with smart contract to buy PCT
	async function buyPCT() {
		// Converts integer as Eth to Wei,
		let amount = await ethers.utils.parseEther(transAmount.toString());
		try {
			await erc20.buyToken(transAmount, {value: amount});
			// Listens for event on blockchain
			await erc20.on("PCTBuyEvent", (from, to, amount) => {
				setPendingFrom(from.toString());
				setPendingTo(to.toString());
				setPendingAmount(amount.toString());
				setIsPending(true);
			})
		} catch(err) {
			if(typeof err.data !== 'undefined') {
				setErrMsg("Error: "+ err.data.message);
			} 
			setIsError(true);
		} 	
	}

	// Interacts with smart contract to sell PCT
	async function sellPCT() {
		try {
			await erc20.sellToken(transAmount);
			// Listens for event on blockchain
			await erc20.on("PCTSellEvent", (from, to, amount) => {
				setPendingFrom(from.toString());
				setPendingTo(to.toString());
				setPendingAmount(amount.toString());
				setIsPending(true);
			})
		} catch(err) {
			if(typeof err.data !== 'undefined') {
				setErrMsg("Error: "+ err.data.message);
			} 
			setIsError(true);
		} 
	}

	// Sets state for value to be transacted
	// Clears extant alerts
	function valueChange(value) {
		setTransAmount(value);
		setIsPending(false);
		setIsError(false);
	}

	// Handles user buy form submit
	const handleBuySubmit = (e: React.FormEvent) => {
		e.preventDefault();
		valueChange(e.target.buypct.value);
		buyPCT();
	};

	// Handles user sell form submit
	const handleSellSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		valueChange(e.target.sellpct.value);
		sellPCT();
	};

	return (
		<div className="App">
		<header className="App-header">

		<ErrorAlert />
		<PendingAlert />

		<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Ethereum-icon-purple.svg/512px-Ethereum-icon-purple.svg.png" className="App-logo" alt="Ethereum logo" />

		<h2>{coinSymbol}</h2>

		<p>
		User Wallet address: {walAddress}<br/>
		Eth held: {ethBal}<br />
		PCT held: {pctBal}<br />
		</p>

		<form onSubmit={handleBuySubmit}>
		<p>
		<label htmlFor="buypct">PCT to buy:</label>
		<input type="number" step="1" min="0" id="buypct" 
		name="buypct" onChange={e => valueChange(e.target.value)} required 
		style={{margin:'12px'}}/>	
		<Button type="submit" >Buy PCT</Button>
		</p>
		</form>

		<form onSubmit={handleSellSubmit}>
		<p>
		<label htmlFor="sellpct">PCT to sell:</label>
		<input type="number" step="1" min="0" id="sellpct" 
		name="sellpct" onChange={e => valueChange(e.target.value)} required 
		style={{margin:'12px'}}/>	
		<Button type="submit" >Sell PCT</Button>
		</p>
		</form>

		<a  title="GitR0n1n / CC BY-SA (https://creativecommons.org/licenses/by-sa/4.0)" href="https://commons.wikimedia.org/wiki/File:Ethereum-icon-purple.svg">
		<span style={{fontSize:'12px',color:'grey'}}>
		Ethereum logo by GitRon1n
		</span></a>
		</header>
		</div>
	);
}

export default App;

```
2 Likes

Hi @crypto_economics,

I recommend not combining sale and token functionality. I think it is better to keep them separate as the sale functionality is only short lived and not for the life of the token.

I assume that you could modify the code from @PreciousChicken to add a crowdsale contract.

Unfortunately the front end code isn’t something that I have time to dig into to help you at the moment.

I remember my experience of trying to find how to create a smart contract, unit test and deploy to public networks when I started.

I would have given almost anything for the Learn guides: https://docs.openzeppelin.com/learn/ or for the community forum.

2 Likes

@abcoathup thank you for the explanation. Is it possible to help me sligthly modify the PreciousChicken contract so that it would be a clear separation of functions? I would want to import the PreciousChicken.sol ““purchase”’ and “'sell”” functionality into MyToken.sol and run the front end from MyToken.sol. Is that do-able?

1 Like

I would of course recommend @abcoathup’s recommendation! My demo was just for fun.

I think I might have provided advice to @crypto_economics elsewhere as to possible strategies for what he is looking to achieve. Though I’m sure he would benefit from hearing from those with more expertise.

2 Likes

Hi @crypto_economics,

I still think you should have a Crowdsale and a token contracts, but yes you can add the purchase and sell functionality into your token (I don’t recommend it).

1 Like

@abcoathup thank you for your response. Okay so i took a stab at combining the contracts, if you could please take a look and let me know what i did wrong?

SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract MyToken is ERC20, Pausable, Ownable {

event MTKBuyEvent (
    address from,
    address to,
    uint256 amount
);
        
event MTKSellEvent (
    address from,
    address to,
    uint256 amount
);

mapping (address => uint256) pendingWithdrawals;

constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) public { 
    _mint(msg.sender, 1000);
   
      }
            require(_amount == ((msg.value / 1 ether)), "Incorrect amount of Eth.");
            transferFrom(owner, msg.sender, _amount);
            emit MTKBuyEvent(owner, msg.sender, _amount);
        }
        
            function sellToken(uint256 _amount) public {
            pendingWithdrawals[msg.sender] = _amount;
            transfer(owner, _amount);
            withdrawEth();
            emit MTKSellEvent(msg.sender, owner, _amount);
        }

            function withdrawEth() public {
            uint256 amount = pendingWithdrawals[msg.sender];
           
            pendingWithdrawals[msg.sender] = 0;
            msg.sender.transfer(amount * 1 ether);
        }
    }

and here is the the error message on compile:

CompileError: /Users/lukeshim/Desktop/final-6/contracts/MyToken.sol:43:26: TypeError: Invalid type for argument in function call. Invalid implicit conversion from function () view returns (address) to address requested.
            transferFrom(owner, msg.sender, _amount);
                         ^---^
,/Users/lukeshim/Desktop/final-6/contracts/MyToken.sol:44:30: TypeError: Invalid type for argument in function call. Invalid implicit conversion from function () view returns (address) to address requested.
            emit MTKBuyEvent(owner, msg.sender, _amount);
                             ^---^
,/Users/lukeshim/Desktop/final-6/contracts/MyToken.sol:50:22: TypeError: Invalid type for argument in function call. Invalid implicit conversion from function () view returns (address) to address requested.
            transfer(owner, _amount);
                     ^---^
,/Users/lukeshim/Desktop/final-6/contracts/MyToken.sol:52:43: TypeError: Invalid type for argument in function call. Invalid implicit conversion from function () view returns (address) to address requested.
            emit MTKSellEvent(msg.sender, owner, _amount);
                                          ^---^
1 Like

Hi @crypto_economics,

Sorry, unfortunately I won’t have a chance to dig into what is wrong with your contract.

I really suggest using a crowdsale and token from OpenZeppelin Contracts (it is also much easier for me to look at).

1 Like

@abcoathup Thank you. I will take your advice and try it with that way from the beginning. So, the crowdsale contract willl allow for simple buy and sell of my tokens? It seems like a lot of contract to do these simple tasks.

1 Like

Hi @crypto_economics,

I suggest looking at the following example. You need to deploy both contracts and then transfer the amount you want to sell of SimpleToken to the Crowdsale.

You should do appropriate testing, and auditing. You should also get appropriate regulatory advice for any token. (see: Points to consider when creating a fungible token (ERC20, ERC777))

SimpleToken

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is Context, ERC20, ERC20Detailed {

    /**
     * @dev Constructor that gives _msgSender() all of existing tokens.
     */
    constructor () public ERC20Detailed("SimpleToken", "SIM", 18) {
        _mint(_msgSender(), 1000000 * (10 ** uint256(decimals())));
    }
}

SimpleCrowdsale

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/crowdsale/Crowdsale.sol";


/**
 * @title SimpleCrowdsale
 * @dev This is an example of a fully fledged crowdsale.
 */
contract SimpleCrowdsale is Crowdsale {
    constructor (
        uint256 rate,
        address payable wallet,
        IERC20 token
    )
        public
        Crowdsale(rate, wallet, token)
    {
    }
}
1 Like

@abcoathup I had to install @openzeppelin 2.5 locally instead of the latest version 3.x in order to run the crowdsale.sol contract. I also changed my pragmas on my two contracts to ^0.5.0 and truffle-config
However, i am getting a number of errors now which I didn’t have before:

 @openzeppelin/contracts/utils/Address.sol:31:32: Warning: The "extcodehash" instruction is not supported by the VM version "byzantium" you are currently compiling for. It will be interpreted as an invalid instruction on this VM.
        assembly { codehash := extcodehash(account) }
                               ^------------------^

CompileError: /Users/me/Desktop/final-7/contracts/MyToken.sol:18:61: TypeError: Wrong argument count for modifier invocation: 2 arguments given but expected 0.
    constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) public { 
                                                            ^-------------------^