trufflesuite / drizzle

Reactive Ethereum dapp UI suite
906 stars 235 forks source link

Drizzle cacheSend outputs “cacheSend is not a function” error, but the tx works when using send #87

Open LorenzoZaccagnini opened 4 years ago

LorenzoZaccagnini commented 4 years ago

I'm using Drizzle, react-hooks and the lastest openzeppelin-solidity in my new open source project, but cacheSend outputs always "cacheSend is not a function"

This line of code with send works as expected

 const stackId = contract.methods.createMerchant(value.name, value.category, value.weburl).send({from: drizzleState.accounts[0]})

Instead using cacheSend returns an error "cacheSend is not a function"

    const stackId = contract.methods["createMerchant"].cacheSend(value.name, value.category, value.weburl, {
      from: drizzleState.accounts[0]
    }
)

The issue does not appear using @openzeppelin/contracts@2.5.1 (pragma 0.5.0) instead of the lastest openzeppelin-solidity package (pragma 0.6.0)

Every time I use cacheSend in this project I get the "cacheSend is not a function" error (with the lastest the lastest openzeppelin-solidity )

My index.js where I set the Drizzle options

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

// import drizzle functions and contract artifact
import { Drizzle } from "drizzle";
import Coupoken from "./contracts/Coupoken.json";

// let drizzle know what contracts we want and how to access our test blockchain
const options = {
  contracts: [Coupoken],
  web3: {
    fallback: {
      type: "ws",
      url: "ws://127.0.0.1:9545",
    },
  },
};

// setup drizzle
const drizzle = new Drizzle(options);

ReactDOM.render(<App drizzle={drizzle}/>, document.getElementById('root'));

This is my App.js where I load my components

import React, { useState, useEffect, Fragment } from 'react'
import ReadString from "./ReadString";
import SetString from "./SetString";
import SetMerchant from "./SetMerchant";
import SetCoupon from "./SetCoupon";

const App = props => {
  const [drizzleReadinessState, setDrizzleReadinessState] = useState({drizzleState: null, loading: true})
  const { drizzle } = props

  useEffect(
    () => {
      const unsubscribe = drizzle.store.subscribe( () => {
        // every time the store updates, grab the state from drizzle
        const drizzleState = drizzle.store.getState()
        // check to see if it's ready, if so, update local component state
        if (drizzleState.drizzleStatus.initialized) {
          setDrizzleReadinessState({drizzleState: drizzleState, loading: false})
        }
      })
      return () => {
        unsubscribe()
      }
    }, [drizzle.store, drizzleReadinessState]
  )

  return (
    drizzleReadinessState.loading ?
      "Loading Drizzle..."
      :
      <Fragment>
        <ReadString drizzle={drizzle} drizzleState={drizzleReadinessState.drizzleState} />
        <SetString drizzle={drizzle} drizzleState={drizzleReadinessState.drizzleState} />
        <SetMerchant drizzle={drizzle} drizzleState={drizzleReadinessState.drizzleState} />
        <SetCoupon drizzle={drizzle} drizzleState={drizzleReadinessState.drizzleState} />
      </Fragment>
  )
}

export default App

My component

import React, { useState } from "react"
import { useForm } from "react-hook-form";

const SetMerchant = props => {
  const [stackId, setStackID] = useState(null)
  const { drizzle, drizzleState } = props
  const { Coupoken } = drizzleState.contracts

  const { register, handleSubmit, watch, errors } = useForm();
  const onSubmit = data => {
    setValue(data)
  };

  const setValue = value => {
    const contract = drizzle.contracts.Coupoken
    console.log(contract.methods["createMerchant"]);
    // let drizzle know we want to call the `set` method with `value`
    const stackId = contract.methods["createMerchant"].cacheSend(value.name, value.category, value.weburl, {
      from: drizzleState.accounts[0]
    })
    // save the `stackId` for later reference
    setStackID(stackId)
  }

  const getTxStatus = () => {
    // get the transaction states from the drizzle state
    const { transactions, transactionStack } = drizzleState

    // get the transaction hash using our saved `stackId`
    const txHash = transactionStack[stackId]

    // if transaction hash does not exist, don't display anything
    if (!txHash) return null;

    // otherwise, return the transaction status
    return `Transaction status: ${transactions[txHash] && transactions[txHash].status}`
  }

  return (
    <div>
      <h2>Register Merchant</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="row">
          <div className="six columns">
            <label htmlFor="name">Name</label>
            <input name="name" className="u-full-width" ref={register({ required: true, pattern: /^[A-Za-z]+$/i, maxLength: 40})} />
            {errors.name && <span>Use a valid input</span>}
          </div>
          <div className="six columns">
            <label htmlFor="name">Category</label>
            <input name="category" className="u-full-width" ref={register({ required: true, pattern: /^[A-Za-z]+$/i, maxLength: 40 })} />
            {errors.category && <span>Use a valid input</span>}
          </div>
        </div>
        <div className="row">
          <div className="u-full-width">
            <label htmlFor="weburl">Website</label>
            <input name="weburl" className="u-full-width" ref={register({ required: true, maxLength: 40 })} />
            {errors.weburl && <span>Use a valid input</span>}
            </div>
          </div>
        <input className="button-primary" type="submit" value="Submit" />
      </form>
      <div>{getTxStatus()}</div>
    </div>
  )
}

export default SetMerchant

The github repo is available here

Thank you for your time

cds-amal commented 4 years ago

Thanks for the detailed write up and repo @LorenzoZaccagnini!

KLM-GIT commented 4 years ago

Thanks for reporting the problem @LorenzoZaccagnini . I had this same problem and I'm not sure I would have found it but for your issue. It wasn't feasible for me to roll back oz-contracts and, unfortunately, this situation makes react-components ContractForm method calling capabilities unusable (or at least less useful). Therefore, I had to replumb certain components to use .send instead of .cacheSend.

LorenzoZaccagnini commented 4 years ago

Thanks for reporting the problem @LorenzoZaccagnini . I had this same problem and I'm not sure I would have found it but for your issue. It wasn't feasible for me to roll back oz-contracts and, unfortunately, this situation makes react-components ContractForm method calling capabilities unusable (or at least less useful). Therefore, I had to replumb certain components to use .send instead of .cacheSend.

It's such a loss, I would like to use the oz CLI smart contract upgradeability features but I've to do it manually if I'm using Drizzle. I really don't know if the compatibility issue is a problem of drizzle or something the lastest oz.

My 2 cents bet is on the lastest oz ABI json generation, I guess we can solve this issue tweaking how Drizzle loads the ABI or how OZ exports the ABI.

@KLM-GIT did you experienced this issue when using an upgradeable smart contract structure?

KLM-GIT commented 4 years ago

Hi @LorenzoZaccagnini - as you suggested, it looks like a drizzle incompatibility with solc 0.6+ ABI rather than upgradeability in OZ (the latest version of which upgrades to 0.6). It appears that others have submitted issues and that @cds-amal is working on it in #74

cwang25 commented 4 years ago

Hi everyone,

Was wondering if anyone here know if there is a some work-around to mitigate this issue? Our project has requirements from OpenZeppeplin but that dependencies are in 3.0 which is using solc-0.6.0 So we're getting 'cacheSend is not a functio' error too.

Was wondering if there is a workaround to mitigate this issue ? or downgrade solc version back to 0.5.0 is the only option here?

onionpeel commented 4 years ago

@LorenzoZaccagnini Thanks for your description of the issue. I ran into the same thing trying to use Drizzle with an ERC-20 token from OpenZeppelin 3.1.0. I am using drizzleReactHooks, and it leads to an error that says useCacheSend is not a function.

@KLM-GIT I may end up going with your solution of using send instead of cacheSend/useCacheSend, though that kind of seems to defeat the purpose of using Drizzle. It's either that or re-writing some contracts to accommodate OpenZeppelin 2.5.0 (solc 0.5.0).

christianvari commented 4 years ago

@LorenzoZaccagnini Thanks for your description of the issue. I have the same bug in my project.

apuccia commented 3 years ago

Hello! I don't understand if the issue is solved or not. I tried to use solc 0.5.0 and openzeppelin/contracts@2.5.1 but I still get the "cacheSend is not a function" error

LorenzoZaccagnini commented 3 years ago

Hello! I don't understand if the issue is solved or not. I tried to use solc 0.5.0 and openzeppelin/contracts@2.5.1 but I still get the "cacheSend is not a function" error

Not solved, Alessandro tieni duro!

LucasTrecker commented 3 years ago

Still having the error, using "call()" works for me, but I dont know what really changes in usability in that case. Also found nothing in the docs