how do I decode an anonymous event? - events

I'm trying to decode a simple anonymous event I wrote in my contract and instead of result I'm getting an empty array, does anyone know what I'm doing wrong?
I'm using hardhat.
contract:
pragma solidity ^0.8.9;
contract Lock {
event DataStored(address admin, uint256 indexed data) anonymous;
uint256 data;
function storeData(uint256 data) external {
data = data;
emit DataStored(msg.sender, data);
}
}
deploy.js
const hre = require("hardhat");
const contract = require("../artifacts/contracts/Lock.sol/Lock.json");
const abiDecoder = require('abi-decoder');
async function main() {
const Lock = await hre.ethers.getContractFactory("Lock");
const lock = await Lock.deploy();
await lock.deployed();
console.log(
` deployed to ${lock.address}`
);
tx = await lock.storeData(10);
const transactionReceipt = await tx.wait()
abiDecoder.addABI(contract.abi);
const decodedLogs = abiDecoder.decodeLogs(transactionReceipt.events);
console.log(transactionReceipt.events)
console.log(decodedLogs);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
and the result I'm getting from running the deploy.js
deployed to 0x21dF544947ba3E8b3c32561399E88B52Dc8b2823
[]

So I think you should try to learn the different between non-anonymous and anonymous event by this example
smart contract:
pragma solidity ^0.8.9;
contract Lock {
event DataStoredNonAnonymous(address admin, uint256 indexed data);
event DataStoredAnonymous(address admin, uint256 indexed data) anonymous;
uint256 x;
uint256 y;
function storeDataNonAnonymous(uint256 _data) external {
x = _data;
emit DataStoredNonAnonymous(msg.sender, _data);
}
function storeDataAnonymous(uint256 _data) external {
y = _data;
emit DataStoredAnonymous(msg.sender, _data);
}
}
deploy.js or deploy.ts depending on your preference
import { ethers } from "hardhat";
async function main() {
const Lock = await ethers.getContractFactory("Lock");
const lock = await Lock.deploy();
await lock.deployed();
console.log(`deployed to ${lock.address}`);
const tx1 = await lock.storeDataNonAnonymous(10);
const txReceipt1 = await tx1.wait()
console.log("event non-anonymous",txReceipt1.events)
const tx2 = await lock.storeDataAnonymous(12345);
const txReceipt2 = await tx2.wait()
console.log("event anonymous",txReceipt2.events)
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The result after deployed on the terminal the topic[0] which is reserved for specify the event signature will not showing up
Compiled 1 Solidity file successfully
deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
event non-anonymous [
{
transactionIndex: 0,
blockNumber: 2,
transactionHash: '0x22e2b2c6274a83e0c6ff9e2733e7875940e038f3faf5641d77ede41958657fa0',
address: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
topics: [
'0xe70463dc16bf49899544f11b2caa7874683dbf886102edb6cbc82d728dc425d4',
'0x000000000000000000000000000000000000000000000000000000000000000a'
],
data: '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266',
logIndex: 0,
blockHash: '0x1491b08df4abb9ac8aaeb3be796ab0635957668224470944fd215400a02ce276',
args: [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
BigNumber { value: "10" },
admin: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
data: BigNumber { value: "10" }
],
decode: [Function (anonymous)],
event: 'DataStoredNonAnonymous',
eventSignature: 'DataStoredNonAnonymous(address,uint256)',
removeListener: [Function (anonymous)],
getBlock: [Function (anonymous)],
getTransaction: [Function (anonymous)],
getTransactionReceipt: [Function (anonymous)]
}
]
event anonymous [
{
transactionIndex: 0,
blockNumber: 3,
transactionHash: '0x76ec1eef5693031441fd0446ab681d3f703cca3a016167b343ad8d75fdfd478f',
address: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
topics: [
'0x0000000000000000000000000000000000000000000000000000000000003039'
],
data: '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266',
logIndex: 0,
blockHash: '0xd4f64019225eb982522f8f41a1cc2d6869a7ac41f5d93e5b6f7fb39607914e78',
removeListener: [Function (anonymous)],
getBlock: [Function (anonymous)],
getTransaction: [Function (anonymous)],
getTransactionReceipt: [Function (anonymous)]
}
]

Related

Unable to subscribe to web3js event due to provider

I'm trying to subscribe to an event so I can have it appear in my terminal.
Firstly, I do this to have the deployed contract stored in the app variable.
ChainList.deployed().then(function(instance){app=instance})
Then I do,
app.LogSellArticle({filter:{}, fromBlock:0})
.on('data', event => console.log(event))
.on('changed', changed => console.log(changed))
.on('error', err => console.log(err))
.on('connected', str => console.log(str))
which gives me this output,
EventEmitter {
_events: [Object: null prototype] {
data: [Function (anonymous)],
changed: [Function (anonymous)],
error: [Function (anonymous)],
connected: [Function (anonymous)]
},
_eventsCount: 4,
_maxListeners: undefined,
[Symbol(kCapture)]: false
}
truffle(ganache)> Error: The current provider doesn't support subscriptions: HttpProvider
at Timeout._onTimeout (C:\Users\Yiannis\AppData\Roaming\npm\node_modules\truffle\build\webpack:\node_modules\web3-eth\node_modules\web3-core-subscriptions\lib\subscription.js:176:1)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7)
I'm not using HttpProvider, I'm using websockets and I also have them enabled in my truffle-config.js. I am using Ganache as a test node.
truffle-config.js:
networks: {
ganache: {
host: "localhost", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
websocket: true // Enable EventEmitter interface for web3 (default: false)
},
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: "0.8.11", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
settings: { // See the solidity docs for advice about optimization and evmVersion
optimizer: {
enabled: true,
runs: 200
},
// evmVersion: "byzantium"
}
}
},
};
ChainList.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.11;
contract ChainList{
//state vars
address seller;
string name;
string description;
uint256 price;
//events
event LogSellArticle(
address indexed _seller,
string _name,
uint256 _price
);
//sell an article
function sellArticle(string memory _name, string memory _desc, uint256 _price) public{
seller = msg.sender;
name = _name;
description = _desc;
price = _price;
emit LogSellArticle(seller, name, price);
}
//get an article
function getArticle() public view returns (
address _seller,
string memory _name,
string memory _desc,
uint256 _price
){
return(seller, name, description, price);
}
}
app.js
App = {
web3Provider: null,
contracts: {},
account: 0x0,
init: function() {
return App.initWeb3();
},
initWeb3: function() {
//initialise web3
if(typeof web3 != 'undefined'){
App.web3Provider = web3.currentProvider;
}else{
//create a new provider and plug it directly into our local node
App.web3Provider = new Web3.providers.WebsocketProvider('ws://localhost:7545');
}
web3 = new Web3(App.web3Provider);
App.displayAccountInfo();
return App.initContract();
},
displayAccountInfo: function(){
web3.eth.getCoinbase(function(err, account){
if(err == null){
App.account = account;
$('#account').text(account);
web3.eth.getBalance(account, function(err, balance){
if(err == null){
$('#accountBalance').text(web3.fromWei(balance, "ether") + ' ETH');
}
})
}
})
},
initContract: function() {
$.getJSON('ChainList.json', function(chainListArtifact){
//get the contract artifact file and use it to instantiate a truffle contract abstraction
App.contracts.ChainList = TruffleContract(chainListArtifact);
//set the provider for our contract
App.contracts.ChainList.setProvider(App.web3Provider);
//retrieve article from contract
return App.reloadArticles();
})
},
reloadArticles: function(){
//refresh account information because the balance might have changed
App.displayAccountInfo();
//retrieve article placeholder and clear it
$('#articlesRow').empty();
App.contracts.ChainList.deployed().then(function(instance){
return instance.getArticle();
}).then(function(article){
if(article[0] == 0x0){
//no article to display
return;
}
//retrieve the article template and fill it with data
var articleTemplate = $('#articleTemplate');
articleTemplate.find('.panel-title').text(article[1]);
articleTemplate.find('.article-description').text(article[2]);
articleTemplate.find('.article-price').text(web3.fromWei(article[3] , "ether"));
var seller = article[0];
if(seller == App.account){
seller = "You";
}
articleTemplate.find('.article-seller').text(seller);
//add this article
$('#articlesRow').append(articleTemplate.html());
}).catch(function(err){
console.error(err.message);
})
},
sellArticle: function(){
//retrieve detail of article
var _article_name = $('#article_name').val();
var _description = $('#article_description').val();
var _price = web3.toWei(parseFloat($('#article_price').val() || 0,), "ether" );
if((_article_name.trim() == '') || (_price == 0)){
//nothing to sell
return false;
}
App.contracts.ChainList.deployed().then(function(instance){
return instance.sellArticle(_article_name,_description,_price, {
from: App.account,
gas: 500000
}).then(function(result){
App.reloadArticles();
}).catch(function(err){
console.error(err);
})
})
}
};
$(function() {
$(window).load(function() {
App.init();
});
});

Deno oak websocket must have a method that returns an async iterator

I am trying to build up a WebSocket with oak (not the native deno one).
The following code is how I build the server.
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onclose = () => { console.log('Disconnected from the client!');};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket) => {
console.log('new websocket, ws: ',ws);
const uid = globalThis.crypto.randomUUID();
sockets.set(uid,ws);
console.log('socket: ',sockets);
for await (const ev of ws){
console.log('ev: ', ev);
}
}
export const wsRoutes = new Router()
.get('/ws', runWS);
But in the for loop (at the end), for ws it says Type 'WebSocket' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.. What's the deal with this and how to fix it?
The error message is providing you with useful information: the WebSocket is not AsyncIterable, which means that it cannot be used with a for await...of loop.
Here is the type documentation for WebSocket in Deno. It is (for the most part) the same as the WHATWG standard WebSocket that is documented on MDN.
If your intention is to respond to incoming message events, you'll need to attach an event listener:
webSocket.addEventListener("message", (messageEvent) => {
// Do something in response to each message event
});
Additional:
Here's an observation based on the code you've shown, but not in response to your question:
It's probably more ergonomic to store the sockets as the keys of your map, and the associated state data in the values. (This is the inverse of what you've shown). Here's an example of why:
import {
Router,
type RouterMiddleware,
} from "https://deno.land/x/oak#v10.6.0/mod.ts";
// You seem to want to log data to the console.
// This function will help you easily log only certain properties of objects:
/**
* Functional implementation of the type utility
* [`Pick<Type, Keys>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)
*/
function pick<T, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) result[key] = obj[key];
return result;
}
type SocketData = { id: string };
const socketMap = new Map<WebSocket, SocketData>();
// Do something when a connection is opened
function handleOpen(ev: Event, ws: WebSocket) {
const socketData: SocketData = { id: window.crypto.randomUUID() };
socketMap.set(ws, socketData);
console.log({
event: pick(ev, ["type"]),
socketData,
});
}
// Do something when an error occurs
function handleError(ev: Event, ws: WebSocket) {
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a connection is closed
function handleClose(ev: CloseEvent, ws: WebSocket) {
ev.code; // number
ev.reason; // string
ev.wasClean; // boolean
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type", "code", "reason", "wasClean"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a message is received
// Change `unknown` to the type of message payloads used in your application.
// (for example, JSON messages are `string`)
function handleMessage(ev: MessageEvent<unknown>, ws: WebSocket) {
ev.data; // unknown
ev.lastEventId; // string
ev.ports; // readonly MessagePort[]
const socketData = socketMap.get(ws);
if (socketData) {
socketData.id; // string
}
console.log({
event: pick(ev, ["type", "data", "lastEventId", "ports"]),
socketData,
});
}
const webSocketMiddleware: RouterMiddleware<"/ws"> = async (ctx, next) => {
const ws = ctx.upgrade();
ws.addEventListener("open", (ev) => handleOpen(ev, ws));
ws.addEventListener("error", (ev) => handleError(ev, ws));
ws.addEventListener("close", (ev) => handleClose(ev, ws));
ws.addEventListener("message", (ev) => handleMessage(ev, ws));
await next();
};
export const router = new Router();
router.get("/ws", webSocketMiddleware);
This is my updated code. It avoids the problem entirely
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
interface BroadcastObj{
name: string,
mssg: string
}
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
if(!ctx.isUpgradable){
ctx.throw(501);
}
const uid = globalThis.crypto.randomUUID();
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onmessage = (m) => {
let mssg = m.data as string;
if(typeof(mssg) === 'string'){
chatMessage(JSON.parse(mssg));
}
};
ws.onerror = (e) => {console.log('error occured: ', e);};
ws.onclose = () => { chatDisconnect(uid);};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket, uid: string) => {
await sockets.set(uid,ws);
}
const chatMessage = async (msg: BroadcastObj) => {
await sockets.forEach((ws: WebSocket) => {
ws.send(JSON.stringify(msg));
});
}
const chatDisconnect = async (uid: string) => {
await sockets.delete(uid);
}
export const wsRoutes = new Router()
.get('/ws', runWS);

Handle unsubscribe GraphQL subscription

I have an issue with subscription can't be unsubscribe.
Before we start, this is my setup: Apollo Client(graphql-ws) <-> Apollo Server(graphql-ws). On the server, I build a custom PubSub instead of using the one provided.
As you can see here, the client has sent a complete request to server with the id. However, the server is still sending more data to it. I have read somewhere that you have to send GQL_STOP, aka STOP instead. However, this is what Apollo Client is sending.
A bit of code:
Client subscription:
export const useGetDataThroughSubscription = (
resourceIds: number[],
startDate?: Date,
endDate?: Date
) => {
const variables = {
startTime: startDate?.toISOString() ?? '',
endTime: endDate?.toISOString() ?? '',
resourceIds,
};
return useGetDataSubscription({
variables,
...
})
}
Server pubsub:
const createPubSub = <TopicPayload extends { [key: string]: unknown }>(
emitter: EventEmitter = new EventEmitter()
) => ({
publish: <Topic extends Extract<keyof TopicPayload, string>>(
topic: Topic,
payload: TopicPayload[Topic]
) => {
emitter.emit(topic as string, payload);
},
async *subscribe<Topic extends Extract<keyof TopicPayload, string>>(
topic: Topic,
retrievalFunc: (value: TopicPayload[Topic]) => Promise<any>
): AsyncIterableIterator<TopicPayload[Topic]> {
const asyncIterator = on(emitter, topic);
for await (const [value] of asyncIterator) {
const data = await retrievalFunc(value);
yield data;
}
},
Server subscribe to event:
const resolver: Resolvers = {
Subscription: {
[onGetAllLocationsEvent]: {
async *subscribe(_a, _b, ctx) {
const locations = await ...;
yield locations;
const iterator = ctx.pubsub.subscribe(
onGetAllLocationsEvent,
async (id: number) => {
const location = ...;
return location;
}
);
for await (const data of iterator) {
if (data) {
yield [data];
}
}
},
resolve: (payload) => payload,
},
},
};
In this one, if instead of the for loop, I return iterator instead, then the server will send back a complete and stop the subscription all together. That's great, but I want to keep the connection open until client stop listening.
And server publish
ctx.pubsub.publish(onGetAllResourcesEvent, resource.id);
So how should I deal with this?

Nodejs: axios request parallel way

I have a code which execute request sequentially
try {
const results = [];
for await (let tc of testCases) {
const { data } = await axios.post("URL",
{
command_line_arguments: "",
compiler_options: "",
redirect_stderr_to_stdout: true,
source_code: source_code,
language_id,
stdin: tc.input,
expected_output: tc.output,
}
);
results.push({ text: tc.text, input: tc.input, output: tc.output, testType: tc.testType, ...data });
}
it works, but it is very slow.
I am looking to request all of them in parallel way.
Note: I have tries Promise all,Promise.allsettled, and Axios.all. Some how it didnt worked.
My solution:
const runTestCases = async (testCases: ITestCase[], source_code, language_id) => {
try {
const requests = createRequests(testCases, source_code, language_id);
const result = await Promise.all(requests.map(async (response) => {
const { data }:any = await response;
return data;
}));
return result;
} catch (error) {
throw new BadRequestError(error?.message);
}
};
/**
* Create array of request to run test cases in parallel
* #param testCases
* #param advance
* #returns
*/
const createRequests = (testCases: ITestCase[], source_code: string, language_id: string) => {
const requests = testCases.map((tc) => {
return () => axios.post(process.env.JUDGE0_HOST,{
redirect_stderr_to_stdout: true,
source_code: source_code,
language_id,stdin: tc.input,
expected_output: tc.output
})})
return requests;
};
output:
[ undefined, undefined ]
Am I doing wrong ?
Thanks in advance!

sendResponse in Port.postmessage()

I have the following code
browser.runtime.onConnect.addListener(function (externalPort) {
externalPort.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(42);
}
});
However, it seems that listeners for Port.onMessage do not get called with a sendResponse as listeners for browser.runtime.onMessage.
Any idea how to send responses for messages to ports?
Port-based messaging doesn't use sendResponse. Simply post another message to the port.
Here's a very simplified example of a port-based messaging system. It doesn't transfer errors or exceptions, doesn't have a timeout. The idea is to pass an id, save the callback for the id in a map, and use the same id in the response to call that saved callback.
Unlike browser.runtime.sendMessage that creates a new port each time (a relatively expensive operation in case you send a lot of messages), we reuse the same port.
sender:
const port = browser.runtime.connect({name: 'foo'});
const portMap = new Map();
let portMessageId = 0;
port.onMessage.addListener(msg => {
const {id, data} = msg;
const resolve = portMap.get(id);
portMap.delete(id);
resolve(data);
});
function send(data) {
return new Promise(resolve => {
const id = ++portMessageId;
portMap.set(id, resolve);
port.postMessage({id, data});
});
}
usage:
(async () => {
const response = await send({foo: 'whatever'});
console.log(response);
})();
receiver:
/** #param {chrome.runtime.Port} port */
browser.runtime.onConnect.addListener(port => {
if (port.name === 'foo') {
port.onMessage.addListener(msg => {
const {id, data} = msg;
port.postMessage({id, data: processMessage(data)});
});
}
});
The Port.postMessage() is a push-only messaging method, so you need to use regular runtime.sendMessage() method in parallel. Here is an example:
manifest.json:
{
"name": "panel example",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "panel",
"default_popup": "panel.html"
},
"permissions": [
"tabs"
]
}
background.js:
browser.runtime.onConnect.addListener(port => {
let tabId;
const listenerForPort = (message, sender) => {
if (message &&
typeof message == 'object' &&
message.portName == port.name) {
switch (message.type) {
case 'get-tabId':
return Promise.resolve(tabId);
}
}
};
browser.runtime.onMessage.addListener(listenerForPort);
port.onMessage.addListener(message => {
if (message &&
typeof message == 'object' &&
message.tabId)
tabId = message.tabId;
});
port.onDisconnect.addListener(port => {
browser.runtime.onMessage.removeListener(listenerForPort);
if (tabId)
browser.tabs.remove(tabId);
});
});
panel.html:
<!DOCTYPE html>
<script type="application/javascript" src="panel.js"></script>
<button id="button">Click Me</button>
panel.js:
browser.windows.getCurrent({ populate: true }).then(win => {
const portName = `port for window ${win.id}`;
const activeTab = win.tabs.find(tab => tab.active);
const port = browser.runtime.connect({
name: portName
});
port.postMessage({ tabId: activeTab.id });
const button = document.getElementById('button');
button.addEventListener('click', async event => {
const tabIdFromBackground = await browser.runtime.sendMessage({
type: 'get-tabId',
portName
});
button.textContent = tabIdFromBackground;
});
});
In this example, there is a listener corresponding to a connection and it is designed to respond only to messages sent with the corresponding port name.

Resources