Ethers.js: ReferenceError: utils is not defined - javascript

I'm trying to create a listener for incoming transactions with ethers.js (v5.6). According to the docs, to listen to incoming transactions you need to create this filter:
// List all token transfers *to* myAddress:
filter = {
address: tokenAddress,
topics: [
utils.id("Transfer(address,address,uint256)"),
null,
hexZeroPad(myAddress, 32)
]
};
Having this in my script gives me an error saying utils.id("Transfer(address,address,uint256)"), ReferenceError: utils is not defined. I can't find anything in the docs about importing an utils package. Can anyone sort me out?
My full code:
async function incomingTransactions() {
if (loadedUser) {
console.log("User loaded", loadedUser)
let myAddress = loadedUser.publicKey
let filter = {
address: myAddress,
topics: [
utils.id("Transfer(address,address,uint256)"),
null,
hexZeroPad(myAddress, 32)
]
};
// let foo = await provider.getLogs(filter)
// console.log(foo)
}
console.log("No user loaded")
}
const interval = setInterval(function() {
incomingTransactions();
}, 5000);

Looks like utils is part of the ethers object, and hexZeroPad and id are part of utils so you can use them like so:
const { ethers } = require("ethers"); // assuming commonjs
ethers.utils.id("Transfer(address,address,uint256)");
ethers.utils.hexZeroPad(myAddress, 32);

Related

How can i write a function that extends the firestore-mock npm module adding the firebase startAfter function for query cursor pagination?

I am adding query pagination to my GCF query function and am writing unit tests for the pagination query, I am writing a mock function using Node, and the firestore-mock npm library. Note, I can't use Sinon and no third party modules or libraries are allowed aside from firestore-mock.
I am extending the firestore-mock library to add query pagination to our firestore query. Write now, I have 3 methods that extend Firestore-mock that I need to write. Two of which are completed.
orderBy
limit
startAfter
So far I have successfully written mocks methods for limit and orderBy but am stuck on how to implement startAfter
Ideally this startAfter function would have the same functionality as the firebase built in startAfter function.
The request I am using for the query pagination has the following structure:
const paginatedQuery = await f.getQueryPagination({
customerId: ['62005'],
startDate: '2021-11-04T00:00:00.000Z',
endDate: '2021-11-05T00:00:00.000Z',
next: '1657667147.865000000',
},
'wal-com', ['62005'],
logger
);
I have looked at the docs for the startAfter cursor query and in my request I want the startAfter method to set the cursor to be set to the document that starts with the timestamp that next was converted from.
So for the above example: next: '1657667147.865000000' would be equivelant to the next document having a eventDateTime value of
TimeStamp: {
_seconds: 1657667147
_nanoSeconds: 865000000
}
EDIT: Here is the code I have right now for startAfter but it doesn't work as intended
const FirebaseMock = require('firebase-mock');
const QueryMock = require('firestore-mock/mock_constructors/QueryMock');
QueryMock.prototype._startAfter = function(doc) {
const startAfter = doc;
const startAfterId = startAfter.id;
const startAfterData = startAfter.data();
const startFinder = function(doc) {
const docId = doc.id;
const docData = doc.data();
if (docId === startAfterId) {
return true;
}
if (docData === startAfterData) {
return true;
}
return false;
};
const buildStartFinder = function() {
return startFinder;
};
return buildStartFinder;
};
QueryMock.prototype.startAfter = function(value) {
// if value is passed as a parameter
// set the cursor for the query to the document with the timestamp equal to value
if (value) {
if (value.constructor.name === 'DocumentSnapshot') {
this._docs = this.firestore._startAfter(value, this._docs, this.id);
} else if (value.constructor.name === 'Timestamp') {
this._docs = this.firestore._startAfter(
new TimestampMock(value.seconds, value.nanoseconds),
this._docs,
this.id
);
} else {
throw new Error(
'startAfter() only accepts a DocumentSnapshot or a Timestamp'
);
}
}
};
module.exports = {
FirestoreMock
};

abi.map not a function error trying to connect to uniswap

I'm new to working with blockchain and I'm having a problem trying to get the contract from Uniswap. I've been following their docs on V3 but I can't get past this "abi.map is not a function" error. When I output the ABI to the console, it looks like I get the ABI back correctly but when I try to use it to initialize the contract I get this error.
import { ethers } from 'ethers'
const ABI = require('#uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json')
console.log(ABI)
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/Your Address Here;p')
const poolAddress = '0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8'
const poolContract = new ethers.Contract(poolAddress, ABI, provider)
interface Immutables {
factory: string
token0: string
token1: string
fee: number
tickSpacing: number
maxLiquidityPerTick: number
}
async function getPoolImmutables() {
const [factory, token0, token1, fee, tickSpacing, maxLiquidityPerTick] = await Promise.all([
poolContract.factory(),
poolContract.token0(),
poolContract.token1(),
poolContract.fee(),
poolContract.tickSpacing(),
poolContract.maxLiquidityPerTick(),
])
const immutables: Immutables = {
factory,
token0,
token1,
fee,
tickSpacing,
maxLiquidityPerTick,
}
return immutables
}
getPoolImmutables().then((result) => {
console.log(result)
})
This error will go away if you delete everything in the abi up to the first square bracket after "abi". So for IUniswapV3Pool.json, instead of:
{
"_format": "hh-sol-artifact-1",
"contractName": "IUniswapV3Pool",
"sourceName": "contracts/interfaces/IUniswapV3Pool.sol",
"abi": [
{
"anonymous": false,
"inputs": [
...
It should be:
[
{
"anonymous": false,
"inputs": [
And don't forget to delete everything up to the last square bracket at the end of the file as well.
To build on #jaspers answer you don't actually need to delete anything from the original file. you just need to pass the abi property from the original.
const poolContract = new ethers.Contract(poolAddress, ABI.abi, provider)

jest test - TypeError: Cannot read property 'filter' of null

I'm quite new with jest testing and I'm having trouble to understand how Jest deals with the functions I'm trying to test. Here's my problem:
I'm trying to test the following, quite simple function, which will receive a bookId and will find the object within an array that containa such id. All is vanilla js, no react.
function catchSelectedBook(bookId) {
const objectSearchAsString = localStorage.getItem('objecttransfer');
const booksObject = JSON.parse(objectSearchAsString);
const currentBook = booksObject.filter((book) => book.id === bookId);
return currentBook;
}
The unit test Jest code is the following:
describe('Given a function that is given as argument the id', () => {
test('When invoked, it finds the book that matches such id', () => {
const returnMokObject = {
kind: 'books#volumes',
totalItems: 1080,
items: [
{
kind: 'books#volume',
id: 'HN1dzQEACAAJ',
}],
};
mockLocalstorageJest();
const answer = catchSelectedBook('HN1dzQEACAAJ');
expect(answer.id).toBe('HN1dzQEACAAJ');
});
});
The function mockLocalstorageJest sends to the local storage an object so that it can be get when the function catchSelectedBook is tested
I export my function like this:
module.exports = {
catchSelectedBook,mockLocalstorageJest,
};
And I import the function into the test file like this:
const { catchSelectedBook, mockLocalstorageJest} = require('./book-details.js');
Whenever I run the test, I got the following error message:
enter image description here
Does that mean that Jest doesn't have the array method "filter" defined?
Thanks!
I think that this instruction is returning null
const objectSearchAsString = localStorage.getItem('objecttransfer');
JSON.parse(null) returns null so bookObject is also null.
I believe that your problem is that you are not setting up the local storage correctly in mockLocalstorageJest().

Jest Mock returns undefined instead of data

I'm trying to mock a function and not sure what i'm doing wrong here. I have this function "getGroups"
getGroups:
export const getGroups = async () => {
try {
const groupApiUrl = getDashboardPath(GROUPS_TAB_INDEX);
const data = await fetch(groupApiUrl, { cache: 'force-cache' });
const userData = await data.json();
return userData;
} catch (error) {
throw Error(error);
}
};
___mocks___/getGroups.js:
export default async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
getGroups.test.js:
jest.mock('./getGroups.js');
// eslint-disable-next-line import/first
import { getGroups } from './getGroups';
const fakeRespose = {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
describe('getGroups', () => {
it('returns data', async () => {
const data = await getGroups();
console.log('DATA', data); <---- UNDEFINED?
expect(data).toBeDefined();
expect(data).toMatchObject(fakeRespose);
});
it('handles error', async () => {
// const data = await getGroups();
await getGroups().toThrow('Failed');
});
});
What are you doing wrong here?
Default export in your mock instead of named as in the implementation
In your implementation you're using named export and you're importing { getGroups } so to make it work you need to change your mock like this
__mocks__\getGroups.js
export const getGroups = async () => {
return {
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
};
};
working example
TL;DR
Testing mock
There is no point at all to test mock function. This does not proves your implementation is working. Even if you change your implementation your tests will still pass.
Use mocks only for the dependencies of your implementation
Use jest.genMockFromModule
It will create jest.fn() for each of the module's exported methods and will preserve the constants, allowing you to change the return value/implementation for some test cases and will also be able to write assertions if the function have been called
__mocks__\getGroups.js
const mock = jest.genMockFromModule('../getGroups');
mock.getGroups.mockResolvedValue({
groups: [
{ id: 1, name: 'Data1' },
{ id: 2, name: 'Data2' }
]
})
module.exports = mock;
Jest will automatically hoist jest.mock calls (read more...)
So you can safely leave the import statements first and then call jest.mock
From Jest Docs, here's an example of a Mock.
jest.mock('../moduleName', () => {
return jest.fn(() => 42);
});
// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';
In your case data is undefined, because you haven't actually supplied a mocked implementation for the function or the mock hasn't worked and you're still calling the original function.
Example Reference: https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options
However, in your simple case you could also solve this with a spy, either jest.spyOn or jest.fn(). Here are two solutions to what you're trying to achieve. You can look at the code and run it here: https://repl.it/repls/FairUnsungMice
UPDATE after comment:
Manual mocks are defined by writing a module in a __mocks__/
subdirectory immediately adjacent to the module. For example, to mock
a module called user in the models directory, create a file called
user.js and put it in the models/__mocks__ directory. Note that the
__mocks__ folder is case-sensitive, so naming the directory __MOCKS__ will break on some systems.
Double check the naming, directory structure & type of exports you've setup - they should match. Also, it's worth checking this out: https://github.com/facebook/jest/issues/6127 - looks like an open issue with jest. If you need a solution, look at using a different approach as I mentioned.
Reference: https://jestjs.io/docs/en/manual-mocks

Mongoose infinite loop on Model creation

Background
I have Mongoose Schema about Surveys, that needs to check if the Survey belongs to a set of countries that is in another collection.
Code
To check this, I have a surveySchema, a countrySchema, and a file where I create the models and connect to the DB.
To perform the check that a survey belongs to a valid country, I am using Mongoose async validators in surveySchema like the following:
surveySchema.js:
"use strict";
const mongoose = require("mongoose");
const surveySchema = {
subject: { type: String, required: true },
country: {
type: String,
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {
const {
Country
} = require("./models.js").getModels();
const countriesNum = await Country.find({"isoCodes.alpha2": val}).count();
callback(countriesNum === 1);
}
},
message: "The country {VALUE} is not available in the DB at the moment."
}
}
};
module.exports = new mongoose.Schema(surveySchema);
module.exports.surveySchema = surveySchema;
countrySchema.js:
"use strict";
const mongoose = require("mongoose");
const countrySchema = {
name: { type: String, required: true },
isoCodes:{
alpha2: { type: String, required: true }
}
}
};
module.exports = new mongoose.Schema(countrySchema);
module.exports.countrySchema = countrySchema;
models.js:
"use strict";
const mongoose = require("mongoose");
const fs = require("fs");
const DB_CONFIG = "./config/dbConfig.json";
/**
* Module responsible for initializing the Models. Should be a Singleton.
*/
module.exports = (function() {
let models;
const initialize = () => {
//Connect to DB
const {
dbConnectionURL
} = JSON.parse(fs.readFileSync(DB_CONFIG, "utf8"));
mongoose.connect(dbConnectionURL);
mongoose.Promise = global.Promise;
//Build Models Object
models = {
Country: mongoose.model('Country', require("./countrySchema.js")),
Survey: mongoose.model('Survey', require("./surveySchema.js"))
};
};
const getModels = () => {
if (models === undefined)
initialize();
return models;
};
return Object.freeze({
getModels
});
}());
The idea here is that I am using the models.js file in other places as well. Because this file is also responsible for connecting to the DB, I decided to make it a Singleton. This way, I should only connect once, and all further requests will always return the same Models, which would be ideal.
Problem
The problem here is that I have a circular dependency that results in:
RangeError: Maximum call stack size exceeded at exports.isMongooseObject (/home/ubuntu/workspace/server/node_modules/mongoose/lib/utils.js:537:12)
...
The flow of code leading to this error is:
Code runs getModels()`
getModels() checks that models is undefined and runs initialize()
initialize() tries to create the models.
When creating the Survey model Survey: mongoose.model('Survey', require("./surveySchema.js")) it runs into the validator function, which again requires models.js
Infinite loop begins
Questions
Is there any other way to check if a Survey's country is part of the county's collection without making a async validation?
How can I structure/change my code so this doesn't happen?
As said in the comments, I think you are a bit confused about how you are using your models.js module. I think this is what is happening:
You are exporting a single function from models.js:
models.js
module.exports = function() { ... };
Therefore, when you require it, you just get that single function:
surveySchema.js
const models = require("./models.js");
models is now a function. Which means every time you call it, you run through the code in models.js and create a new variable let models;, and also new functions initialize() and getModels().
You could move the let models out of the anonymous function into the global scope which would probably fix it, but for my money you only want to run the anonymous function in models.js once, so I would invoke it immediately and set the exports of the module to its result:
models.js
module.exports = (function() {
// codez here
return Object.freeze({
getModels
});
})(); // immediately invoke the function.
Use it:
// models is now the frozen object returned
const { Survey } = models.getModels();
As for options to validation, there's no reason why you can't add your own middleware validation code if normal async validation doesn't do it for you using serial or parallel mechanisms as described in the docs.
Update after comments
The second problem as you point out is that during first execution of getModels() -> initialize() you call require('./surveySchema.js'), but this calls getModels() which is still in the process of being called and hasn't yet initialized models, so initialize() is re-entered.
I think what you're trying to achieve is fine (survey schema depends on customer model), because you can still draw an object graph without any circular dependencies, and it's just the way you've implemented it that you've ended up with one. The simplest way to deal with this I think is actually to keep the circular reference, but defer the point at which you call getModels() in surveySchema.js:
"use strict";
const mongoose = require("mongoose");
const models = require("./models.js");
const surveySchema = {
subject: { type: String, required: true },
country: {
type: String,
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {
// !! moved from the global scope to here, where we actually use it
const {
Country
} = models.getModels();
const countries = await Country.find({"isoCodes.alpha2": val});
callback(countries.length === 1);
}
},
message: "The country {VALUE} is not available in the DB at the moment."
}
}
};
module.exports = new mongoose.Schema(surveySchema);
module.exports.surveySchema = surveySchema;
A neater and probably more extensible way of approaching it, though, might be to separate out the connection code from the models code, since it's a different concept altogether.
Update #2 after more comments
The infinite stack you're seeing is because you have not used the API correctly. You have:
const surveySchema = {
country: {
validate: {
validator: {
isAsync: true,
validator: async function(val, callback) {...}
},
},
...
}
};
You should have:
const surveySchema = {
country: {
validate: {
isAsync: true,
validator: async function(val, callback) {...}
},
...
}
};
As per the docs.

Categories