Can't get signer - solidity

Trying to read a transfer event from the ENS contract.
const listenToEvent = () => {
const contract = new ethers.Contract(
"0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
erc721Abi,
signer
);
contract.on("Transfer", (from, to, tokenId, event) => {
let data = {
from,
to,
tokenId: tokenId.toString(),
event,
};
console.log(data);
});
};
const balance = async (tokenAddress) => {
const contract = new ethers.Contract(tokenAddress, erc721Abi, signer);
const balance = await contract.balanceOf(account);
console.log(balance.toString());
};
const connect = async () => {
if (typeof window.ethereum !== "undefined") {
const accounts = await window.ethereum.request({
method: "eth_requestAccounts",
});
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
setSigner(signer);
listenToEvent();
setAccount(accounts[0]);
} else {
console.log("Please install metamask.");
}
};
In my console it gives me this error:
Uncaught (in promise) Error: events require a provider or a signer with a provider (operation="once", code=UNSUPPORTED_OPERATION, version=contracts/5.7.0)

Related

Mock, jest and time

I read some tips on how to mock your request/response in Express framework in the blog:
https://codewithhugo.com/express-request-response-mocking/. However, I have no clue how to mock the controller below.
export const healthCheck = async (req, res, next) => {
log("debug", "healthCheck controller called");
const healthcheck = {
uptime: process.uptime(),
message: "Server is running!",
now_timestamp: Date.now()
};
try {
res.send(healthcheck);
} catch (error) {
healthcheck.message = error;
res.status(503).send();
}
};
I am glad to share my efforts below. My suspicion is that I must mock class Date as well.
import {
healthCheck
} from "../healthcheck.js";
const mockRequest = () => {
const req = {}
req.body = jest.fn().mockReturnValue(req)
req.params = jest.fn().mockReturnValue(req)
return req
};
const mockResponse = () => {
const res = {}
res.get = jest.fn().mockReturnValue(res)
res.send = jest.fn().mockReturnValue(res)
res.status = jest.fn().mockReturnValue(res)
res.json = jest.fn().mockReturnValue(res)
return res
};
const mockNext = () => {
return jest.fn()
};
describe("healthcheck", () => {
afterEach(() => {
// restore the spy created with spyOn
jest.restoreAllMocks();
});
it("should call mocked log for invalid from scaler", async () => {
let req = mockRequest();
let res = mockResponse();
let next = mockNext();
await healthCheck(req, res, next);
expect(res.send).toHaveBeenCalledTimes(1)
expect(res.send.mock.calls.length).toBe(1);
});
});

Where is module './get-username'?

I am attempting to write a script using the puppeteer library for node and I'm running it on the microtask platform rapidworkers.com
I have run into a challenge when writing part of the script that solves the captcha on the rapidworkers login page, when I try to run my script with node index.js I get the error: Cannot find module './get-username'
here is my code: (copied from https://jsoverson.medium.com/bypassing-captchas-with-headless-chrome-93f294518337)
const puppeteer = require('puppeteer');
const request = require('request-promise-native');
const poll = require('promise-poller').default;
const siteDetails = {
sitekey: '6LeEP20UAAAAAPYQsP7kIhE7XUT_LsDYjzO46uG3',
pageurl: 'https://rapidworkers.com/Login'
}
const getUsername = require('./get-username');
const getPassword = require('./get-password');
const apiKey = require('./api-key');
const chromeOptions = {
executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google
Chrome',
headless:false,
slowMo:10,
defaultViewport: null
};
(async function main() {
const browser = await puppeteer.launch(chromeOptions);
const page = await browser.newPage();
await page.goto('https://rapidworkers.com/Login');
const requestId = await initiateCaptchaRequest(apiKey);
await page.type('#username', getUsername());
const password = getPassword();
await page.type('#password', password);
const response = await pollForRequestResults(apiKey, requestId);
await page.evaluate(`document.getElementById("g-recaptcha-
response").innerHTML="${response}";`);
page.click('#register-form button[type=submit]');
})()
async function initiateCaptchaRequest(apiKey) {
const formData = {
method: 'userrecaptcha',
googlekey: siteDetails.sitekey,
key: apiKey,
pageurl: siteDetails.pageurl,
json: 1
};
const response = await request.post('http://2captcha.com/in.php', {form:
formData});
return JSON.parse(response).request;
}
async function pollForRequestResults(key, id, retries = 30, interval = 1500,
delay = 15000) {
await timeout(delay);
return poll({
taskFn: requestCaptchaResults(key, id),
interval,
retries
});
}
function requestCaptchaResults(apiKey, requestId) {
const url = `http://2captcha.com/res.php?
key=${apiKey}&action=get&id=${requestId}&json=1`;
return async function() {
return new Promise(async function(resolve, reject){
const rawResponse = await request.get(url);
const resp = JSON.parse(rawResponse);
if (resp.status === 0) return reject(resp.request);
resolve(resp.request);
});
}
}
const timeout = millis => new Promise(resolve => setTimeout(resolve,
millis))

Jest integration test Express REST API with Mongoose

everybody. I'm new to unit/integration testing and I'm having trouble with testing one of my API routes which involves file system operations and Mongoose model method calls. I need to be able mock mongoose model method as well as router's post method. Let me share you my router's post method.
documents.js
const { User } = require('../models/user');
const { Document } = require('../models/document');
const isValidObjectId = require('./../helpers/isValidObjectId');
const createError = require('./../helpers/createError');
const path = require('path');
const fs = require('fs');
const auth = require('./../middlewares/auth');
const uploadFile = require('./../middlewares/uploadFile');
const express = require('express');
const router = express.Router();
.
.
.
router.post('/mine', [auth, uploadFile], async (req, res) => {
const user = await User.findById(req.user._id);
user.leftDiskSpace(function(err, leftSpace) {
if(err) {
return res.status(400).send(createError(err.message, 400));
} else {
if(leftSpace < 0) {
fs.access(req.file.path, (err) => {
if(err) {
res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
} else {
fs.unlink(req.file.path, (err) => {
if(err) res.status(500).send('Silinmek istenen doküman diskten silinemedi.');
else res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
});
}
});
} else {
let document = new Document({
filename: req.file.filename,
path: `/uploads/${req.user.username}/${req.file.filename}`,
size: req.file.size
});
document.save()
.then((savedDocument) => {
user.documents.push(savedDocument._id);
user.save()
.then(() => res.send(savedDocument));
});
}
}
});
});
.
.
.
module.exports = router;
documents.test.js
const request = require('supertest');
const { Document } = require('../../../models/document');
const { User } = require('../../../models/user');
const mongoose = require('mongoose');
const fs = require('fs');
const path = require('path');
const config = require('config');
let server;
describe('/api/documents', () => {
beforeEach(() => { server = require('../../../bin/www'); });
afterEach(async () => {
let pathToTestFolder = path.join(process.cwd(), config.get('diskStorage.destination'), 'user');
await fs.promises.access(pathToTestFolder)
.then(() => fs.promises.rm(pathToTestFolder, { recursive: true }))
.catch((err) => { return; });
await User.deleteMany({});
await Document.deleteMany({});
server.close();
});
.
.
.
describe('POST /mine', () => {
let user;
let token;
let file;
const exec = async () => {
return await request(server)
.post('/api/documents/mine')
.set('x-auth-token', token)
.attach('document', file);
}
beforeEach(async () => {
user = new User({
username: 'user',
password: '1234'
});
user = await user.save();
user.leftDiskSpace(function(err, size) { console.log(size); });
token = user.generateAuthToken();
file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.json');
});
.
.
.
it('should return 400 if an error occurs during calculation of authorized user\'s left disk space', async () => {
jest.mock('../../../routes/documents');
let documentsRouter = require('../../../routes/documents');
let mockReq = {};
let mockRes = {}
let mockPostRouter = jest.fn();
mockPostRouter.mockImplementation((path, callback) => {
if('path' === '/mine') callback(mockReq, mockRes);
});
documentsRouter.post = mockPostRouter;
let error = new Error('Something went wrong...');
const res = await exec();
console.log(res.body);
expect(res.status).toBe(400);
expect(res.body.error).toHaveProperty('message', 'Something went wrong...');
});
.
.
.
});
});
What I want to do is, I need to be able call a mock user.leftDiskSpace(function(err, leftSpace)) user model method inside router.post('/mine', ...) route handler. I need to be able to get inside the if and else brances by callback function of user.leftDiskSpace(). How can I do that?
Thanks in advance.

Apollo subscriptions JWT authentication

I am using Robin Wieruch's fullstack boilerplate but it is missing authentication for subscriptions. It uses JWT token for sessions and it is working fine for http but for ws auth is completely missing.
I need to pass user trough context for subscriptions as well, I need session info in subscriptions resolver to be able to decide weather I should fire subscription or not.
I did search Apollo docs, I saw I should use onConnect: (connectionParams, webSocket, context) function, but there is no fully functional fullstack example, I am not sure how to pass JWT from client to be able to get it in webSocket object.
Here is what I have so far:
Server:
import express from 'express';
import {
ApolloServer,
AuthenticationError,
} from 'apollo-server-express';
const app = express();
app.use(cors());
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, process.env.SECRET);
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
const server = new ApolloServer({
introspection: true,
typeDefs: schema,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket, context) => {
console.log(webSocket);
},
},
context: async ({ req, connection }) => {
// subscriptions
if (connection) {
return {
// how to pass me here as well?
models,
};
}
// mutations and queries
if (req) {
const me = await getMe(req);
return {
models,
me,
secret: process.env.SECRET,
};
}
},
});
server.applyMiddleware({ app, path: '/graphql' });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
const isTest = !!process.env.TEST_DATABASE_URL;
const isProduction = process.env.NODE_ENV === 'production';
const port = process.env.PORT || 8000;
httpServer.listen({ port }, () => {
console.log(`Apollo Server on http://localhost:${port}/graphql`);
});
Client:
const httpLink = createUploadLink({
uri: 'http://localhost:8000/graphql',
fetch: customFetch,
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:8000/graphql`,
options: {
reconnect: true,
},
});
const terminatingLink = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return (
kind === 'OperationDefinition' && operation === 'subscription'
);
},
wsLink,
httpLink,
);
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => {
const token = localStorage.getItem('token');
if (token) {
headers = { ...headers, 'x-token': token };
}
return { headers };
});
return forward(operation);
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.log('GraphQL error', message);
if (message === 'UNAUTHENTICATED') {
signOut(client);
}
});
}
if (networkError) {
console.log('Network error', networkError);
if (networkError.statusCode === 401) {
signOut(client);
}
}
});
const link = ApolloLink.from([authLink, errorLink, terminatingLink]);
const cache = new InMemoryCache();
const client = new ApolloClient({
link,
cache,
resolvers,
typeDefs,
});
You need to use connectionParams to set the JWT from the client-side. Below is the code snippet using the angular framework:
const WS_URI = `wss://${environment.HOST}:${environment.PORT}${
environment.WS_PATH
}`;
const wsClient = subscriptionService.getWSClient(WS_URI, {
lazy: true,
// When connectionParams is a function, it gets evaluated before each connection.
connectionParams: () => {
return {
token: `Bearer ${authService.getJwt()}`
};
},
reconnect: true,
reconnectionAttempts: 5,
connectionCallback: (error: Error[]) => {
if (error) {
console.log(error);
}
console.log('connectionCallback');
},
inactivityTimeout: 1000
});
const wsLink = new WebSocketLink(wsClient);
In your server-side, you are correct, using onConnect event handler to handle the JWT. E.g.
const server = new ApolloServer({
typeDefs,
resolvers,
context: contextFunction,
introspection: true,
subscriptions: {
onConnect: (
connectionParams: IWebSocketConnectionParams,
webSocket: WebSocket,
connectionContext: ConnectionContext,
) => {
console.log('websocket connect');
console.log('connectionParams: ', connectionParams);
if (connectionParams.token) {
const token: string = validateToken(connectionParams.token);
const userConnector = new UserConnector<IMemoryDB>(memoryDB);
let user: IUser | undefined;
try {
const userType: UserType = UserType[token];
user = userConnector.findUserByUserType(userType);
} catch (error) {
throw error;
}
const context: ISubscriptionContext = {
// pubsub: postgresPubSub,
pubsub,
subscribeUser: user,
userConnector,
locationConnector: new LocationConnector<IMemoryDB>(memoryDB),
};
return context;
}
throw new Error('Missing auth token!');
},
onDisconnect: (webSocket: WebSocket, connectionContext: ConnectionContext) => {
console.log('websocket disconnect');
},
},
});
server-side: https://github.com/mrdulin/apollo-graphql-tutorial/blob/master/src/subscriptions/server.ts#L72
client-side: https://github.com/mrdulin/angular-apollo-starter/blob/master/src/app/graphql/graphql.module.ts#L38

Jest testing of async middleware for authentication

I'm using a static array to scaffold a user table, prior to refactoring with actual postgres db and some fetch()-ing code. At present, the tests work, but obviously they are working synchronously. Here's the placeholder API code:
// UserAPI.js
let findUserById = (credentials = {}) => {
const { userId } = credentials
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
And an example test as follows:
// excerpt from TokenAuth.test.js
const UserAPI = require('../lib/UserAPI')
describe('With TokenAuth middleware', () => {
beforeEach(() => {
setStatus(0)
})
it('should add user to req on authorised requests', () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})
})
This runs fine, and along with other tests gives me the coverage I want. However, I now want to check that the tests will deal with the async/await nature of the fetch() code I'm going to use for the proper UserAPI.js file. So I re-write the placeholder code as:
// UserAPI.js with added async/await pauses ;-)
let findUserById = async (credentials = {}) => {
const { userId } = credentials
// simulate url resolution
await new Promise(resolve => setTimeout(() => resolve(), 100)) // avoid jest open handle error
if (userId) {
const foundUser = users.find(user => user.id === userId)
if (foundUser !== undefined) {
const { password: storedpassword, ...user } = foundUser
return user
}
}
return null
}
exports.byId = findUserById
... at which point I start getting some lovely failures, due I think it's returning unresolved promises.
My problem is two-fold:
How should I alter the UserAPI.test.js tests to deal with the new async nature of findUserByCredentials() ?
Am I ok in my assumption that ExpressJS is happy with async functions as request handlers? Specifically, due to the async nature ofUserAPI.findUserByCredentials is this ok?
Main App.js uses curried UserAPI.byId() for the findUserById.
// App.js (massively simplified)
const express = require('express')
const TokenAuth = require('./middleware/TokenAuth')
const RequireAuth = require('./middleware/RequireAuth')
const UserAPI = require('./lib/UserAPI')
let router = express.Router()
const app = express()
app.use(TokenAuth(UserAPI.byId))
app.use(RequireAuth)
app.use('/users', UserRouter)
module.exports = app
My TokenAuth middleware would now run along these lines:
// TokenAuth.js (simplified)
const jwt = require('jsonwebtoken')
require('dotenv').config()
const signature = process.env.SIGNATURE
let TokenAuth = findUserById => async (req, res, next) => {
let header = req.headers.authorization || ''
let [type, token] = header.split(' ')
if (type === 'Bearer') {
let payload
try {
payload = jwt.verify(token, signature)
} catch (err) {
res.sendStatus(401)
return
}
let user = await findUserById(payload)
if (user) {
req.user = user
} else {
res.sendStatus(401)
return
}
}
next()
}
module.exports = TokenAuth
A partial answer us simply to add an async/await on the middleware call:
it('should add user to req on authorised requests', async () => {
const token = createToken(fakeUser)
const authReq = { headers: { authorization: 'Bearer ' + token } }
const myMiddleware = TokenAuth(UserAPI.byId)
await myMiddleware(authReq, fakeRes, fakeNext)
// expect(authReq.user).toStrictEqual({ id: 1, username: 'smith#example.com' });
expect(authReq.user.username).toStrictEqual('smith#example.com')
expect(authReq.user.id).toStrictEqual(1)
})