Web3.js – Troubleshooting Issues When Calling Smart Contract Functions Using Web3

metamasksolidityweb3js

Trying to learn smart contracts. I successfully deployed a contract. Before deploying it I used the functions successfully in Remix:

enter image description here

Contract Instance:

enter image description here

But in my Vue app, I get an error:

Uncaught TypeError: this.$store.state.contractInstance(...).deposit is not a function

The app successfully detects Metamask. And Via the console I see the contract instance and the ABI. But, deposit not available as a function, only appears in the ABI arrary.

This seems to the closest to my issue, but when I tried the solution it did not work for me: https://stackoverflow.com/questions/48184969/calling-smart-contracts-methods-using-web3-ethereum.

My code … using web3 version ^0.20.5

Component:

<template>
  <div class="container">
    <div class="row">
      <div class="col">

        <h1>Home</h1>

        <form @submit.prevent="deposit">
          <label>Deposit
            <input
              v-model="amount"
              type="number"
              min=".01">
          </label>

          <button type="submit">Submit</button>
        </form>
      </div>
    </div>
  </div>
</template>

<script>
  import {store} from '../store';

  export default {
    name: 'home',
    data: function () {
      return {
        amount: null
      }
    },
    methods: {
      deposit(event) {
        this.$store.state.contractInstance().deposit(event.target.innerHTML, {
          gas: 300000,
          value: this.$store.state.web3.web3Instance().toWei(this.amount, 'ether'),
          from: this.$store.state.web3.coinbase
        }, (error, result) => {
          if (error) {
            console.log(error)
          }
        })
      }
    },
    mounted () {
      console.log('dispatching getContractInstance')
      this.$store.dispatch('getContractInstance')
    }
  }
</script>

Store:

import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import getWeb3 from '../util/getWeb3';
import pollWeb3 from '../util/pollWeb3';
import getContract from '../util/getContract';

Vue.use(Vuex);
export const store = new Vuex.Store({
  strict: true,
  state,
  getters: {

  },
  mutations: {
    registerWeb3Instance(state, payload) {
      console.log('registerWeb3instance Mutation being executed', payload);
      let result = payload;
      let web3Copy = state.web3;
      web3Copy.coinbase = result.coinbase;
      web3Copy.networkId = result.networkId;
      web3Copy.balance = parseInt(result.balance, 10);
      web3Copy.isInjected = result.injectedWeb3;
      web3Copy.web3Instance = result.web3;
      state.web3 = web3Copy;
      pollWeb3();
    },
    pollWeb3Instance(state, payload) {
      console.log('pollWeb3Instance mutation being executed', payload);
      state.web3.coinbase = payload.coinbase;
      state.web3.balance = parseInt(payload.balance, 10);
    },
    registerContractInstance(state, payload) {
      console.log('Escrow contract instance: ', payload);
      state.contractInstance = () => payload;
    }
  },
  actions: {
    registerWeb3({ commit }) {
      console.log('registerWeb3 Action being executed');
      getWeb3
        .then(result => {
          console.log('committing result to registerWeb3Instance mutation');
          commit('registerWeb3Instance', result);
        })
        .catch(e => {
          console.log('error in action registerWeb3', e);
        });
    },
    pollWeb3({ commit }, payload) {
      console.log('pollWeb3 action being executed');
      commit('pollWeb3Instance', payload);
    },
    getContractInstance({ commit }) {
      getContract
        .then(result => {
          commit('registerContractInstance', result);
        })
        .catch(e => console.log(e));
    }
  }
});

escrow.sol

pragma solidity ^0.4.11;

contract Escrow {
    uint balance;
    address public manager;
    address public worker;
    address private escrow;
    uint private start;
    bool managerOk;
    bool workerOk;

function Escrow(address manager_address, address worker_address) public {
        manager = manager_address;
        worker = worker_address;
        escrow = msg.sender;
        start = now;
    }

    function accept() public {
        if (msg.sender == manager){
            managerOk = true;
        } else if (msg.sender == worker){
            workerOk = true;
        }
        if (managerOk && workerOk){
            payBalance();
        } else if (managerOk && !workerOk && now > start + 30 days) {
            selfdestruct(manager);
        }
    }

    function payBalance() private {
        escrow.transfer(this.balance / 100);
        if (worker.send(this.balance)) {
            balance = 0;
        } else {
            throw;
        }
    }

    function deposit() public payable {
        if (msg.sender == manager) {
            balance += msg.value;
        }
    }

    function cancel() public {
        if (msg.sender == manager){
            managerOk = false;
        } else if (msg.sender == worker){
            workerOk = false;
        }
        if (!managerOk && !workerOk){
            selfdestruct(manager);
        }
    }

    function kill() public constant {
        if (msg.sender == escrow) {
            selfdestruct(manager);
        }
    }
}

escrowContract.js:

const address = '0xd61F9331CE61Eb27E0AB7d9b9bc75402Ea5db7f8';
const ABI = [
  [
    {
      constant: false,
      inputs: [],
      name: 'accept',
      outputs: [],
      payable: false,
      stateMutability: 'nonpayable',
      type: 'function'
    },
    {
      constant: false,
      inputs: [],
      name: 'cancel',
      outputs: [],
      payable: false,
      stateMutability: 'nonpayable',
      type: 'function'
    },
    {
      constant: false,
      inputs: [],
      name: 'deposit',
      outputs: [],
      payable: true,
      stateMutability: 'payable',
      type: 'function'
    },
    {
      inputs: [
        {
          name: 'manager_address',
          type: 'address'
        },
        {
          name: 'worker_address',
          type: 'address'
        }
      ],
      payable: false,
      stateMutability: 'nonpayable',
      type: 'constructor'
    },
    {
      constant: true,
      inputs: [],
      name: 'kill',
      outputs: [],
      payable: false,
      stateMutability: 'view',
      type: 'function'
    },
    {
      constant: true,
      inputs: [],
      name: 'manager',
      outputs: [
        {
          name: '',
          type: 'address'
        }
      ],
      payable: false,
      stateMutability: 'view',
      type: 'function'
    },
    {
      constant: true,
      inputs: [],
      name: 'worker',
      outputs: [
        {
          name: '',
          type: 'address'
        }
      ],
      payable: false,
      stateMutability: 'view',
      type: 'function'
    }
  ]
];

export { address, ABI };

getContract.js

import Web3 from 'web3';
import { address, ABI } from './constants/escrowContract';

let getContract = new Promise(function(resolve, reject) {
  let web3 = new Web3(window.web3.currentProvider);
  let escrowContract = web3.eth.contract(ABI);
  let escrowContractInstance = escrowContract.at(address);
  resolve(escrowContractInstance);
});

export default getContract;

Best Answer

Your ABI is wrapped in an extra array.

const ABI = [
  [
    {
      ...
    },
    ...
  ]
];

Remove one pair of square brackets:

const ABI = [
  {
    ...
  },
  ...
];