Getting error while calling the function of ContractManager in HeaderMenu
Tags: react, ethereum, blockchain, javascript
Published: 2020-03-22 20:54:48

**CodeSandBox:**https://codesandbox.io/s/funny-moon-krrsj
**HeaderMenu.js**
```
import {Component} from 'react';
import {
    Menu,
    Container,
    Button,
    Label,
    Loader,
    List,
    Image,
    Icon,
    Dropdown
} from 'semantic-ui-react';
import Head from 'next/head';
import web3 from '../ethereum/web3';
import Constant from '../support/Constant';
import Config from '../support/Config';
import appDispatcher from '../core/AppDispatcher';
import contractManager from '../core/ContractManager';

class HeaderMenu extends Component {
    constructor(props) {
        super(props);
        this.account = props.account;
        this.contractManager = contractManager;
        // console.log(contractManager);
        this.transactionDispatcher = props.transactionDispatcher;
        this.state = {address: "", balance: "", name: "", 
            avatarUrl: "", isLoading: true, isJoinButtonLoading: false, 
            isJoined: false, numPendingTx: 0};
        this.reloadCount = 0;
    }
    clearAllData = () => {
        window.localStorage.clear();
    }

    componentDidMount() {
        if (this.account) {
            this.getAccountInfo();
            appDispatcher.register((payload) => {
                if (payload.action == Constant.EVENT.ACCOUNT_BALANCE_UPDATED) {
                    this.setState({balance: this.account.balance});
                } else if (payload.action == Constant.EVENT.ACCOUNT_INFO_UPDATED) {
                    this.setState({name: payload.profile.name, avatarUrl: payload.profile.avatarUrl, isJoined: payload.profile.isJoined});
                } 
            });
            this.transactionDispatcher.register((payload) => {
                if (payload.action == Constant.EVENT.PENDING_TRANSACTION_UPDATED) {
                    this.setState({numPendingTx: payload.numPendingTx});
                }
            });
        }
    }

    getAccountInfo = () => {
        var address = this.account.getAddress();
        if (address) {
            this.setState({address: address, balance: this.account.balance, isLoading: false, isJoined: this.account.isJoined});
        } else {
            if (this.reloadCount == 1) {
                this.setState({isLoading: false});
            } else {
                this.reloadCount++;
                setTimeout(this.getAccountInfo, 800);
            }
        }
    }

    handleDropdownClicked = (event, data) => {
        if (data.name == 'updateProfile') {
            appDispatcher.dispatch({
                action: Constant.ACTION.OPEN_UPDATE_PROFILE
            });
        } else if (data.name == 'logOutItem') {
            this.clearAllData();
            window.location.reload();
        } else if (data.name == 'settingsItem') {
            appDispatcher.dispatch({
                action: Constant.ACTION.OPEN_SETTINGS_MODAL
            })
        } 
         else if (data.name == 'changeEthNetwork') {
            if (data.networkid != Config.ENV.EthNetworkId) {
                Config.ENV.EthNetworkId = data.networkid;
                this.removeNetworkDependentData();
                window.location.reload();
            }
        }
    }

    removeNetworkDependentData = () => {
        this.account.storageManager.removeNetworkDependentData();
    }

    handleJoinClicked = () => {
        var publicKeyBuffer = this.account.getPublicKeyBuffer();
        this.contractManager.joinContract(publicKeyBuffer, (resultEvent) => {
            if (resultEvent == Constant.EVENT.ON_REJECTED || resultEvent == Constant.EVENT.ON_ERROR) {
                this.setState({isJoinButtonLoading: false});
            } else if (resultEvent == Constant.EVENT.ON_RECEIPT) {
                window.location.reload();
            }
        });
        this.setState({isJoinButtonLoading: true});
    }

    handleImportPrivateKeyClicked = () => {
        appDispatcher.dispatch({
            action: Constant.ACTION.OPEN_PRIVATE_KEY_MODAL
        });
    }

    render() {
        var accountInfo = (<div></div>);

        if (this.account) {
            if (this.state.isLoading == false) {
                if (this.state.address) {
                    var addressExplorerUrl = Config.ENV.ExplorerUrl + 'address/' + this.state.address;
                    var dropdownTrigger;

                    if (this.state.avatarUrl) { 
                        dropdownTrigger = (
                            <span><Image src={this.state.avatarUrl} avatar/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
                        );
                    } else {
                        dropdownTrigger = (
                            <span><Icon name='user' size='large'/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
                        );
                    }

                    var networkItems = [];
                    for (var i=0;i<Config.NETWORK_LIST.length;i++) {
                        networkItems.push(
                            <Dropdown.Item key={'networkItem' + i} networkid={Config.NETWORK_LIST[i].id} name='changeEthNetwork' onClick={this.handleDropdownClicked}>
                                {Config.NETWORK_LIST[i].name}
                            </Dropdown.Item>
                        );
                    }

                    var memberInfo;
                    if (this.account.isJoined) {
                        memberInfo = (
                            <Dropdown item trigger={dropdownTrigger}>
                                <Dropdown.Menu>
                                    <Dropdown.Item name='updateProfile' onClick={this.handleDropdownClicked}>
                                        <Icon name='write'/>Update profile
                                    </Dropdown.Item>
                                    <Dropdown.Item name='settingsItem' onClick={this.handleDropdownClicked}>
                                        <Icon name='settings'/>Settings
                                    </Dropdown.Item>
                                    <Dropdown.Item name='logOutItem' onClick={this.handleDropdownClicked}>
                                        <Icon name='log out'/>Log out
                                    </Dropdown.Item>
                                </Dropdown.Menu>
                            </Dropdown>
                        );
                    } else {
                        memberInfo = (
                            <Button color='orange' onClick={this.handleJoinClicked} 
                                loading={this.state.isJoinButtonLoading} 
                                disabled={this.state.isJoinButtonLoading}>Join {Constant.APP_NAME}</Button>
                        );
                    }

                    var pendingTxItem;
                    if (this.state.numPendingTx > 0) {
                        pendingTxItem = (
                            <Label as='a' color='yellow' href={addressExplorerUrl} target='_blank'>
                                <Icon name='spinner' loading/>
                                {this.state.numPendingTx} pending tx
                            </Label>
                        );
                    }

                    accountInfo = (
                        <Menu.Menu position='right'>
                            <Menu.Item>
                            <Dropdown item text={Config.ENV.NetworkName}>
                                    <Dropdown.Menu>
                                        {networkItems}
                                    </Dropdown.Menu>
                                </Dropdown>
                            </Menu.Item>
                            <Menu.Item>
                                <List>
                                <List.Item>
                                    <a href={addressExplorerUrl} target='_blank'>
                                        {this.state.address}
                                    </a>
                                </List.Item>
                                <List.Item>
                                    Balance: <Label as='a' href={addressExplorerUrl} target='_blank' color='orange'>{parseFloat(web3.utils.fromWei("" +this.state.balance, 'ether')).toFixed(8) + ' ETH' }</Label>
                                    {pendingTxItem}
                                </List.Item>
                                </List>
                            </Menu.Item>
                            <Menu.Item>
                                {memberInfo}
                            </Menu.Item>
                        </Menu.Menu>
                    );
                } else {
                    accountInfo = (
                        <Menu.Menu position='right'>
                            <Menu.Item>
                                <Button onClick={this.handleImportPrivateKeyClicked} color='blue'>Import private key</Button>
                            </Menu.Item>
                        </Menu.Menu>
                    );
                }
            } else {
                accountInfo = (<Loader inverted active />);
            }
        }

        return (
            <Menu fixed='top' color='grey' inverted>
                <Head>
                <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.12/semantic.min.css"></link>
                </Head>
                <Container>
                <Menu.Item>
                    <a href='/'><Image src='static/images/blockchat.png' height={55} /></a>
                </Menu.Item>
                    {this.account ? accountInfo: (<div></div>)}
                </Container>
            </Menu>
        );
    }
}

export default HeaderMenu;
```


**ContractManager.js**
```
import web3 from '../ethereum/web3';
import compiledContract from '../ethereum/build/EtherChat.json';
import TransactionsManager from './TransactionManager';
import appDispatcher from './AppDispatcher';
import Config from '../support/Config';
import Constant from '../support/Constant';
import utils from '../support/Utils';
import crypto from 'crypto';

/**
 * Responsible for interacting with the Ethereum smart contract
 */

export class ContractManager {
    constructor(accountManager, storageManager) {
        this.getContract();
        this.accountManager = accountManager;
        this.storageManager = storageManager;
        this.transactionManager = new TransactionsManager(accountManager);
    }

    // Create a web3 contract object that represent the ethereum smart contract
    getContract = async () => {
        this.contract = await new web3.eth.Contract(JSON.parse(compiledContract.interface), 
                Config.ENV.ContractAddress);
        appDispatcher.dispatch({
            action: Constant.EVENT.CONTRACT_READY
        })
    }

    // Get current account profile from EtherChat contract's storage
    getProfile = async (address) => {
        var result = await this.contract.methods.members(this.accountManager.getAddress()).call();
        var profile = {};
        if (result.isMember == 1) {
            profile.isJoined = true;
            profile.avatarUrl = utils.hexStringToAsciiString(result.avatarUrl);
            profile.name = utils.hexStringToAsciiString(result.name);

            this.storageManager.setJoinedStatus(true);
            this.storageManager.setName(this.name);
            this.storageManager.setAvatarUrl(this.avatarUrl);
            
            appDispatcher.dispatch({
                action: Constant.EVENT.ACCOUNT_INFO_UPDATED,
                profile: profile
            })
        }
        return profile;
    }

    getMemberInfo = async (address, relationship) => {
        var memberInfo = await this.contract.methods.members(address).call();
        if (memberInfo.isMember) {
            var publicKey = '04' + memberInfo.publicKeyLeft.substr(2) + memberInfo.publicKeyRight.substr(2);
            var name = utils.hexStringToAsciiString(memberInfo.name);
            var avatarUrl = utils.hexStringToAsciiString(memberInfo.avatarUrl);
            this.storageManager.updateContact(address, publicKey, name, avatarUrl, relationship);
        }
    }

    getPastEvents = async (eventName, filters) => {
        return await this.contract.getPastEvents(eventName, filters);
    }

    joinContract = async(publicKeyBuffer, callback) => {
        var publicKeyLeft = '0x' + publicKeyBuffer.toString('hex', 0, 32);
        var publicKeyRight = '0x' + publicKeyBuffer.toString('hex', 32, 64);

        this.transactionManager.executeMethod(this.contract.methods.join(publicKeyLeft, publicKeyRight))
            .on(Constant.EVENT.ON_APPROVED, (txHash) => {
                if (callback) callback(Constant.EVENT.ON_APPROVED);
            })
            .on(Constant.EVENT.ON_REJECTED, (txHash) => {
                if (callback) callback(Constant.EVENT.ON_REJECTED);
            })
            .on(Constant.EVENT.ON_RECEIPT, (receipt) => {
                if (callback) callback(Constant.EVENT.ON_RECEIPT);
            })
            .on(Constant.EVENT.ON_ERROR, (error, txHash) => {
                appDispatcher.dispatch({
                    action: Constant.EVENT.ENCOUNTERED_ERROR,
                    message: error.message,
                    title: "Error"
                });
                if (callback) callback(Constant.EVENT.ON_ERROR);
            });
    }
    // joinContract = async (publicKeyBuffer, callback) => {

    addContact = async (address, callback) => {
        console.log(address);

        var method = this.contract.methods.addContact(address);
        this.transactionManager.executeMethod(method)
            .on(Constant.EVENT.ON_APPROVED, (txHash) => {
                if (callback) callback(Constant.EVENT.ON_APPROVED);
            })
            .on(Constant.EVENT.ON_RECEIPT, (receipt) => {
                if (callback) callback(Constant.EVENT.ON_RECEIPT);
            })
            .on(Constant.EVENT.ON_ERROR, (error, txHash) => {
                appDispatcher.dispatch({
                    action: Constant.EVENT.ENCOUNTERED_ERROR,
                    message: error.message,
                    title: "Error"
                });
                if (callback) callback(Constant.EVENT.ON_ERROR);
            });
    }

    acceptContactRequest = async (address, callback) => {
        var method = this.contract.methods.acceptContactRequest(address);
        this.transactionManager.executeMethod(method)
            .on(Constant.EVENT.ON_APPROVED, (txHash) => {
                if (callback) callback(Constant.EVENT.ON_APPROVED);
            })
            .on(Constant.EVENT.ON_RECEIPT, (receipt) => {
                if (callback) callback(Constant.EVENT.ON_RECEIPT);
            })
            .on(Constant.EVENT.ON_ERROR, (error, txHash) => {
                appDispatcher.dispatch({
                    action: Constant.EVENT.ENCOUNTERED_ERROR,
                    message: error.message,
                    title: "Error"
                });
                if (callback) callback(Constant.EVENT.ON_ERROR);
            });
    }

    updateProfile = async (name, avatarUrl, callback) => {
        var nameHex = '0x' + Buffer.from(name, 'ascii').toString('hex');
        var avatarUrlHex = '0x' + Buffer.from(avatarUrl, 'ascii').toString('hex');
        var method = this.contract.methods.updateProfile(nameHex, avatarUrlHex);
        this.transactionManager.executeMethod(method)
            .on(Constant.EVENT.ON_APPROVED, (txHash) => {
                if (callback) callback(Constant.EVENT.ON_APPROVED);
            })
            .on(Constant.EVENT.ON_RECEIPT, (receipt) => {
                if (callback) callback(Constant.EVENT.ON_RECEIPT);
            })
            .on(Constant.EVENT.ON_ERROR, (error, txHash) => {
                appDispatcher.dispatch({
                    action: Constant.EVENT.ENCOUNTERED_ERROR,
                    message: error.message,
                    title: "Error"
                });
                if (callback) callback(Constant.EVENT.ON_ERROR);
            });
    }

    // A message will be encrypted locally before sending to the smart contract
    sendMessage = async (toAddress, publicKey, message) => {
        var publicKeyBuffer = Buffer.from(publicKey, 'hex');
        var encryptedRaw = utils.encrypt(message, this.accountManager.computeSecret(publicKeyBuffer));
        var encryptedMessage = '0x' + encryptedRaw.toString('hex');
        var method = this.contract.methods.sendMessage(toAddress, encryptedMessage, utils.getEncryptAlgorithmInHex());

        this.transactionManager.executeMethod(method)
            .on(Constant.EVENT.ON_APPROVED, (txHash) => {
                this.storageManager.addMyLocalMessage(encryptedMessage, toAddress, utils.getEncryptAlgorithm(), txHash);
                appDispatcher.dispatch({
                    action: Constant.EVENT.MESSAGES_UPDATED,
                    data: toAddress
                });
            })
            .on(Constant.EVENT.ON_REJECTED, (data) => {
                // do nothing
            })
            .on(Constant.EVENT.ON_RECEIPT, (receipt, ) => {
                this.storageManager.updateLocalMessage(toAddress, receipt.transactionHash, Constant.SENT_STATUS.SUCCESS);
                appDispatcher.dispatch({
                    action: Constant.EVENT.MESSAGES_UPDATED,
                    data: toAddress
                });
            })
            .on(Constant.EVENT.ON_ERROR, (error, txHash) => {
                this.storageManager.updateLocalMessage(toAddress, txHash, Constant.SENT_STATUS.FAILED);
                appDispatcher.dispatch({
                    action: Constant.EVENT.MESSAGES_UPDATED,
                    data: toAddress
                });
            });
    }
}

export default ContractManager;
```

Here, I'm trying to call the function 'joinContract' {the function of ContractManager class} in HeaderMenu.js using the function handleJoinClicked(). And,boom... code is getting crash after clicking the join button. It's showing the error. This[coontractManager.joinContract] is not a function. Please help.
Please help.