Open d0na opened 5 years ago
@d0na That is not expected behavior. Thanks for reporting this! I'll update this thread with my findings.
@dona ~I think this a web3 1.0 issue and wanted to upgrade from web3 1.0.0-beta.35 to the latest version to test, but there are breaking changes in the beta releases. I'll code up a sample outside of drizzle to test this theory against the latest beta and take it from there.~
Edit: Seems to be an issue with MetaMask's provider
I created a vanilla JS test that loads two deployed contracts via web3 and registered as a listener for their events. I then updated a contract state via the drizzle-event-demo and saw the same issue (without the drizzle framework) for web3@1.0.0-beta.35, the version drizzle is pinned at, as well as beta.55, the latest.
This could be an issue with MetaMask provider and/or web3. I'll continue investigating.
Anyone interested can view the sample/gist here
It may be related to MetaMask. I wrote a test app to see the behavior of interacting with web3 with MetaMask (regular chrome browser) and sans Metamask (incognito chrome browser). With metaMask, you can see the duplicate events fired.
The test loads two contracts, SimpleStorage and DimpleStorage (don't judge!) where a contract event is emitted whenever the storage value is set; events SimpleSet and DimpleNumber2 respectively.
The test repo is here with instructions in its readme.
This Video demonstrates the issue using repo and steps above
EDITED: linked to reproduction repository
@cds-amal many thanks for your work.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Same problem in Drizzle suite version 1.5.0, here is my SimpleStorage sample:
pragma solidity ^0.5.8;
contract SimpleStorage {
uint storedData;
event Change(string message, uint indexed newVal);
event Stored(uint newVal);
event Message(string message);
constructor (uint s) public {
storedData = s;
}
function set(uint x) public {
require(x < 5000, "Should be less than 5000");
storedData = x;
emit Change("set", x);
}
function get() public view returns (uint) {
return storedData;
}
}
drizzleOptions has 3 events.
const options = {
web3: {
block: false,
fallback: {
type: "ws",
url: "ws://127.0.0.1:8545",
},
},
contracts: [SimpleStorage],
events: {
SimpleStorage: ["Change", "Stored", "Message"],
},
polls: {
accounts: 3000,
},
};
Main.js for Provider
import React, { Component } from 'react'
import { Drizzle, generateStore, EventActions } from "@drizzle/store";
import { DrizzleContext } from "@drizzle/react-plugin";
import options from "./drizzleOptions";
import MyStorage from "./MyStorage";
import EventNotifier from "./EventNotifier";
const drizzleStore = generateStore({drizzleOptions: options, appMiddlewares: [EventNotifier]});
const drizzle = new Drizzle(options, drizzleStore);
//const drizzle = new Drizzle(options);
class Main extends Component {
render () {
return (
<DrizzleContext.Provider drizzle={drizzle}>
<MyStorage />
</DrizzleContext.Provider>
)
}
}
export default Main;
MyStorage.js for Consumer
import React, { useState, useEffect, useContext } from 'react';
import { DrizzleContext } from "@drizzle/react-plugin";
const MyStorage = () => {
const drizzleContext = useContext(DrizzleContext.Context);
const { drizzle, drizzleState, initialized } = drizzleContext;
return (
<MyData drizzle={drizzle} drizzleState={drizzleState} />
)
}
export default MyStorage;
function MyData(props) {
const [web3, setWeb3] = useState(null);
const [contracts, setContracts] = useState(null);
const [val, setVal] = useState(0);
const [storedData, setStoredData] = useState(0);
useEffect(() => {
setContracts(props.drizzle.contracts);
setWeb3(props.drizzle.web3);
})
const handleSet = () => {
if (val && !isNaN(val)) {
contracts.SimpleStorage.methods.set.cacheSend(val);
}
}
const handleChange = (e) => {
if (e.target.value !== "") {
setVal(e.target.value);
}
}
return (
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<input type={"text"} onChange={handleChange}/>
<button onClick={handleSet}>Set</button>
<p>{storedData}</p>
</div>
)
}
EventNotifier.js
import {EventActions} from "@drizzle/store";
const EventNotifier = store => next => action => {
if (action.type === EventActions.EVENT_FIRED) {
//console.log(action);
const contract = action.name;
const eventName = action.event.event;
const newVal = action.event.returnValues.newVal;
const display = `${contract}(${eventName}): ${newVal}`
console.log(display);
}
return next(action);
}
export default EventNotifier;
Emitted only 1 event but EventNotifier says 3 Change events.
Hi @swkim109, Thank you for your detailed writeup, however this is a MetaMask issue, perhaps you can comment there and give it a +1
One workaround is to use a middleware to filter out consecutive duplicate error events until this is resolved upstream.
import { generateStore, EventActions } from '@drizzle/store'
import drizzleOptions from '../drizzleOptions'
// lastSeenEventId used as a work around for known MM events issue
const contractEventNotifier = lastSeenEventId => _ => next => action => {
if (action.type === EventActions.EVENT_FIRED) {
if (action.event.id !== lastSeenEventId) {
lastSeenEventId = action.event.id
const caller = action.event.returnValues.caller
const value = action.event.returnValues.value
const blockNumber = action.event.blockNumber
const message = `value=[${value}] set by [${caller}] in block [${blockNumber}]`
console.group('Event fired')
console.log(action)
console.log(message)
console.groupEnd()
}
}
return next(action)
}
// see issue:
// https://github.com/MetaMask/metamask-extension/issues/6668
const appMiddlewares = [ contractEventNotifier("lastSeenEventIdToGetAroundMetaMaskIssue6668") ]
export default generateStore({
drizzleOptions,
appMiddlewares,
disableReduxDevTools: false // enable ReduxDevTools!
})
Using the code described in the tutorial DRIZZLE AND CONTRACT EVENTS I verified a strange behavior if I set up more than one contract (with related events to watch) inside the
drizzleOptions.js
.It's seems in fact that the EVENT_FIRED by Drizzle are replicated one per each contracts set up in the
drizzle_options.js
file.The steps below are the test case used to reproduce and show the behavior:
start from this repo drizzle-event-demo
change the drizzle version in the package.json
modify the drizzleOptions.js
compile , migrate and then run the DApp.
submit a new value
The behavior: after submitted a new value will appears two toasts as shown in the figure below while I was expecting just one Toast corresponding to a single event triggered.
Actually drizzle fired the same event twice.
Is this behavior correct? If yes why?