How to implement workable (window.ethereum) - javascript

I want to inject a provider (window.ethereum) that can make ethers.js web3.js work into the flutter WebView
Now I have implemented the first version, but it doesn't work and can't be recognized by ethers.js or Web3.js
Here is my code
enum RpcMethods {
eth_requestAccounts = "eth_requestAccounts",
}
interface RequestArguments {
method: RpcMethods;
params?: unknown[] | object;
}
function _request({ method, params }: RequestArguments): Promise<any> {
return _sendToFlutter(method, params);
}
function _connect({ method, params }: RequestArguments): Promise<any> {
return _sendToFlutter(method, params);
}
function _disconnect({ method, params }: RequestArguments): Promise<any> {
return _sendToFlutter(method, params);
}
function _isConnected({ method, params }: RequestArguments): Promise<any> {
return _sendToFlutter(method, params);
}
function _sendToFlutter(rpcMethod: RpcMethods, params?: any) {
return (<any>globalThis).flutter_inappwebview.callHandler(rpcMethod, params);
}
(<any>globalThis).ethereum = {
request: _request,
connect: _connect,
disconnect: _disconnect,
isConnected: _isConnected,
};
Call Contract
const callContract = async () => {
const contract = new ethers.Contract(
"0x27998293A37A0662389487214d1cF37E1B319fe3",
"Contract Abis",
new ethers.providers.Web3Provider((window as any).ethereum, {
name: "ETD",
chainId: 3101,
}).getSigner()
);
const res = await contract.functions.getRecentTokens();
alert(JSON.stringify(res));
}
error message:
Uncaught (in promise) TypeError: Cannot read property 'length' of null

Related

Firesotre error: TypeError: this._delegate.toFirestore is not a function

I'm trying to learn how to use firestore and I can't solve this error when trying to add a client:
(I'm using Next.Js, Typescript and Firebase 8.8.0)
TypeError: this._delegate.toFirestore is not a function
return client;
} else {
const docRef = await this.collection().add(client);
| ^
const doc = await docRef.get();
return doc.data() as Client;
}
I tried to use Firebase V9 but got the same error.
If I add clients collection manually in firebase page I get this error:
TypeError: this._delegate.fromFirestore is not a function
async showAll(): Promise<Client[]> {
const query = await this.collection().get()
return query.docs.map((doc: any) => doc.data()) ?? [];
^
}
My code:
import ClientRepository from "../../core/ClientRepository";
import Client from "../../core/Clients";
import firebase from "../config";
export default class ClientCollection implements ClientRepository {
#converter: any = {
toFireStore(client: Client) {
return {
name: client.name,
taxId: client.taxId,
ie: client.ie,
zipCode: client.zipCode,
state: client.state,
city: client.city,
district: client.district,
street: client.street,
number: client.number,
};
},
fromFireStore(snapshot: firebase.firestore.QueryDocumentSnapshot, options: firebase.firestore.SnapshotOptions): Client {
const data = snapshot.data(options);
return new Client(
data.name,
data.taxId,
data.ie,
data.zipCode,
data.state,
data.city,
data.district,
data.street,
data.number,
snapshot.id
);
},
};
private collection() {
return firebase.firestore().collection("clients").withConverter(this.#converter);
}
async save(client: Client): Promise<Client> {
if (client?.id) {
await this.collection().doc(client.id).set(client);
return client;
} else {
const docRef = await this.collection().add(client);
const doc = await docRef.get();
return doc.data() as Client;
}
}
async delete(client: Client): Promise<void> {
return this.collection().doc(client.id).delete();
}
async showAll(): Promise<Client[]> {
const query = await this.collection().get()
return query.docs.map((doc: any) => doc.data()) ?? [];
}
}

Type error cannot read properties of undefined while trying to access a class property created by a class constructor

Im havving trouble building an express api in TS node.
Im new at express and im learning Node, JS and TS since 2022 so im sorry if the question is not to complex.
The thing is that im tryng to build a class Controller that handles the needed instructions for every route in my express router.
And im passing through the constructor a DAO that ive builded to access the firebase firestore database.
But when i instanciate the object and i try to run it Gives me the cannot read properties of undefined error.
Even when ive found a solution by using a clousure i want to learn how to do this using classes
Here it is the code of the DAO
import { setDoc, doc, getDocs, collection, query, where, deleteDoc } from 'firebase/firestore'
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage'
import { DataResponse, GenericItem } from '../types'
import { DAO } from '../clases/abstractClasses'
import { v4 } from 'uuid'
import db from '../config/firebase'
// import fs from 'fs/promises'
const fs = require('fs').promises
const storage = getStorage()
export class DataResponseClass implements DataResponse {
data: GenericItem[]
status: number
statusText: string
err: string
ok: boolean
constructor (data: GenericItem[], status: number, statusText: string, err: string, ok: boolean) {
this.data = data
this.status = status
this.statusText = statusText
this.err = err
this.ok = ok
}
}
export class DbManager extends DAO {
constructor (collectionRef: string) {
super(collectionRef)
}
async addItem (item: GenericItem): Promise<DataResponse> {
const id = v4()
console.log(id, typeof id)
return await setDoc(doc(db, this.collectionRef, id), { ...item, id }).then(res => {
return new DataResponseClass([{ ...item, id }], 201, 'Item added successfully', '', true)
}).catch(err => {
return new DataResponseClass([item], 400, "Couldn't add item", err.toString(), false)
})
}
async getAll (): Promise<DataResponse> {
return await getDocs(collection(db, this.collectionRef)).then(response => {
const dataArray: any = []
response.forEach(item => dataArray.push(item.data()))
return new DataResponseClass(dataArray, 200, 'Information obtained', '', true)
}).catch(err => new DataResponseClass([], 400, 'Couldnt Retrieve data', err.toString(), false))
}
async getById (passedId: string): Promise<DataResponse> {
const q = query(collection(db, this.collectionRef), where('id', '==', passedId))
return await getDocs(q)
.then(res => {
const dataArray: any[] = []
res.forEach(item => {
dataArray.push(item.data())
})
if (dataArray.length === 0) throw new Error('No data found for the id')
return new DataResponseClass(dataArray, 200, 'Information obtained', '', true)
})
.catch(err => new DataResponseClass([], 400, 'Couldnt Retrieve data', err.toString(), false))
}
async updateById (id: string, item: GenericItem): Promise<DataResponse> {
return await setDoc(doc(db, this.collectionRef, id), item)
.then(() => new DataResponseClass([{ ...item, id }], 200, 'Item succesifuly updated', '', true))
.catch(err => new DataResponseClass([], 400, 'Couldnt update item', err.toString(), false))
}
async deleteByid (id: string): Promise<DataResponse> {
return await deleteDoc(doc(db, this.collectionRef, id))
.then(() => new DataResponseClass([], 200, 'Success deleting a document', '', true))
.catch(err => new DataResponseClass([], 400, 'Couldnt Delete data', err, false))
}
async upLoadFile (file: Express.Multer.File | undefined): Promise<string> {
if (file !== undefined) {
const buffer = await fs.readFile(file.path).then()
const reference = ref(storage, `/${this.collectionRef}/${file.filename}`)
try {
await uploadBytes(reference, buffer)
return await getDownloadURL(reference)
} catch (err: any) {
console.log(err)
return 'There was an error uploading the file'
}
}
return 'No file was uploaded'
}
}
And this is the Controller class code..
import colors from 'colors'
import { Request, Response } from 'express'
import { DbManager, DataResponseClass } from '../services/firebase'
import fs from 'fs/promises'
export class Controller {
protected readonly dbManager: DbManager
constructor (collection: string) {
this.dbManager = new DbManager(collection)
}
async readData (req: Request, res: Response): Promise<void> {
const id: string = req.params.id
if (id !== undefined) {
res.send(await this.dbManager.getById(id))
} else {
res.send(await this.dbManager.getAll())
}
}
async createData (req: Request, res: Response): Promise<void> {
if (req.file !== undefined) {
const uploadedFilePath = await this.dbManager.upLoadFile(req.file)
.then((response: any) => {
console.log(`${response}/${req.file?.filename || ' '}`)
if (req.file?.path !== undefined) {
fs.unlink(req.file.path).then(() => console.log('Upload Complete')).catch(err => console.log(err))
}
return `${response}`
})
.catch((err: any) => {
console.log(err)
res.send(false)
})
const data = { ...req.body, images: uploadedFilePath }
console.log(colors.bgRed.white(data))
res.send(await this.dbManager.addItem({ ...req.body, images: uploadedFilePath }))
} else res.send(new DataResponseClass([], 400, 'Invalid Request no image uploaded', 'Invalid Request no image uploaded', false))
}
async editData (req: Request, res: Response): Promise<void> {
const { id } = req.params
if (req.file !== undefined) {
const uploadedFilePath = await this.dbManager.upLoadFile(req.file)
.then((response: any) => {
if (req.file?.path !== undefined) {
fs.unlink(req.file.path).then(() => console.log('Upload Complete')).catch(err => console.log(err))
}
return `${response}`
})
.catch((err: { toString: () => string }) => {
console.log(err)
res.send(new DataResponseClass([], 400, 'Imposible to upload the file', err.toString(), false))
})
res.send(await this.dbManager.updateById(id, { ...req.body, images: uploadedFilePath }))
} else res.send(new DataResponseClass([], 400, 'Invalid Request no image uploaded', 'Invalid Request no image uploaded', false))
}
async deleteData (req: Request, res: Response): Promise<void> {
const { id } = req.params
if (id !== undefined) {
res.send(await this.dbManager.deleteByid(id))
} else res.send(new DataResponseClass([], 400, 'Invalid Request no id', 'Invalid Request no id', false))
}
}
Thanks for your time Im tryng to learn this beautifull world that is the backend development
Ive tryed to call the constructor outside the class and pass the constant to the constructor
Ive tryed to instanciate the DAO object as a param,Ive even called the dao constructor in a global variable and defined the properti taking value from it .
But the only solution ive found for my issue es transforming the class into a clousure function and calling the constructor in the body of the closure
TypeError: Cannot read properties of undefined (reading 'dbManager')
at /run/media/adrianabadin/code/dcsbackend/src/controllers/controllerClass.ts:20:27
at Generator.next (<anonymous>)
at /run/media/adrianabadin/code/dcsbackend/src/controllers/controllerClass.ts:8:71
at new Promise (<anonymous>)
at __awaiter (/run/media/adrianabadin/code/dcsbackend/src/controllers/controllerClass.ts:4:12)
at readData (/run/media/adrianabadin/code/dcsbackend/src/controllers/controllerClass.ts:24:16)
at Layer.handle [as handle_request] (/run/media/adrianabadin/code/dcsbackend/node_modules/express/lib/router/layer.js:95:5)
at next (/run/media/adrianabadin/code/dcsbackend/node_modules/express/lib/router/route.js:144:13)
at Route.dispatch (/run/media/adrianabadin/code/dcsbackend/node_modules/express/lib/router/route.js:114:3)
at Layer.handle [as handle_request] (/run/media/adrianabadin/code/dcsbackend/node_modules/express/lib/router/layer.js:95:5)
[ERROR] 16:55:20 TypeError: Cannot read properties of undefined (reading 'dbManager')
Routes
Here I call the method readData
import { Router } from 'express'
import { Controller } from '../controllers/controllerClass'
// import { Validation } from '../services/validation'
import { upload } from '../config/multer'
const router = Router()
// const { validate } = new Validation('welcome')
const { readData, createData, editData, deleteData } = new Controller('welcome')
router.get('/', readData)
router.get('/:id', readData)
router.post('/', upload.single('images'), createData)
router.put('/:id', upload.single('images'), editData)
router.delete('/:id', deleteData)
export default router
const { readData, createData, editData, deleteData } = new Controller('welcome')
You can't destructure normally declared instance methods from classes.
I'm going to vastly simplify this to this example:
class Foo {
private data = 123
getData() { return this.data }
}
Now if you call getData like so, it works:
const foo = new Foo()
console.log(foo.getData())
// 123
But if you destructure the method:
const { getData } = new Foo()
console.log(getData())
// Cannot read properties of undefined (reading 'data')
Then it crashes.
The value of this is being lost because you don't call it with a ., which is what provides the class instance to the function.
Now let's try this one:
class Foo {
private data = 123
getData = () => { return this.data }
}
const foo = new Foo()
console.log(foo.getData())
// 123
const { getData } = new Foo()
console.log(getData())
// 123
This works because typescript compiles property assignments in classes to happen in the constructor, and because the arrow function => captures the value of this from when it was declared. So now you can break off the method and it works.
Just note that while the traditional instance method declaration is shared between all instances, this arrow function method will create a new function for every instance. This may hurt performance if you plan to create a very large number of instances. But for backend service classes like this that's probably not a concern.
So in your case just call the method on the instance:
const controller = new Controller('welcome')
router.get('/', (req, res) => controller.readData(req, res))
Or you declare your method as a arrow function.
export class Controller {
//...
readData = async (req: Request, res: Response): Promise<void> => {
const id: string = req.params.id
if (id !== undefined) {
res.send(await this.dbManager.getById(id))
} else {
res.send(await this.dbManager.getAll())
}
}
//...
}
const { readData } = new Controller()
readData() // fine now

Nextjs cannot destructure property 'user' of 'props' as it is undefined

i'm getting this error when i run npm run build.
Error occurred prerendering page "/components/comments". Read more:
https://nextjs.org/docs/messages/prerender-error TypeError: Cannot
destructure property 'user' of 'props' as it is undefined.
This is how i'm passing it from parent to child
<CommentComponent
props={{
user,
myComments,
currentComments,
value,
setMsg,
text,
setText,
}}
/>
And this is how i'm receiving it
interface CommentComp {
props: {
user: User;
myComments: MyComments[];
currentComments: (data: User) => void;
value: Date;
setMsg: (data: string) => void;
text: string;
setText: (data: string) => void;
};
}
export default function CommentComponent({ props }: CommentComp): JSX.Element {
const { user, myComments, currentComments, value, setMsg, text, setText } =
props;
}
It works fine when running it locally.
I'm getting the props as
export const getServerSideProps: GetServerSideProps = async (context) => {
const { email } = context.query as { email: string };
const fetchData = await fetch(`http://localhost:3000/api/user/${email}`);
const fetchResult = await fetchData.json();
try {
const userData = fetchResult as {
currUser: User;
notes: MyNotes[];
tasks: MyTasks[];
comments: MyComments[];
reminders: MyReminder[];
};
return {
props: {
user: userData.currUser,
notes: userData.notes,
tasks: userData.tasks,
comments: userData.comments,
reminders: userData.reminders,
},
};
} catch (error) {
console.log(error);
}
};
After the user signed in it redirects to the [email].tsx page

How a guard can be implemented to a graphql resolver

I'm new in graphql and I am trying to integrate an authentication/authorization system in my project. I found an example on Medium, but I do not understand how a guard communicates with a resolver. If someone knows, I will be very grateful.
import { ApolloServer } from 'apollo-server';
import gql from 'graphql-tag';
import { tradeTokenForUser } from './auth-helpers';
const HEADER_NAME = 'authorization';
const typeDefs = gql`
type Query {
me: User
serverTime: String
}
type User {
id: ID!
username: String!
}
`;
const resolvers = {
Query: {
me: authenticated((root, args, context) => context.currentUser),
serverTime: () => new Date(),
},
User: {
id: user => user._id,
username: user => user.username,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
let authToken = null;
let currentUser = null;
try {
authToken = req.headers[HEADER_NAME];
if (authToken) {
currentUser = await tradeTokenForUser(authToken);
}
} catch (e) {
console.warn(`Unable to authenticate using auth token: ${authToken}`);
}
return {
authToken,
currentUser,
};
},
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
export const authenticated = next => (root, args, context, info) => {
if (!context.currentUser) {
throw new Error(`Unauthenticated!`);
}
return next(root, args, context, info);
};
I do not understand what "next" parameter does and why as an argument when this guard is called I have to return a value?
authenticated is higher-order function that makes the code DRY. next is a callback that is used as a predicate.
It's a DRYer way to write:
...
me: (root, args, context) => {
if (!context.currentUser) {
throw new Error(`Unauthenticated!`);
}
return context.currentUser;
)
...

Decorator to return a 404 in a Nest controller

I'm working on a backend using NestJS, (which is amazing btw). I have a 'standard get a single instance of an entity situation' similar to this example below.
#Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
..
..
..
#Get(':id')
async findOneById(#Param() params): Promise<User> {
return userService.findOneById(params.id);
}
This is incredibly simple and works - however, if the user does not exist, the service returns undefined and the controller returns a 200 status code and an empty response.
In order to make the controller return a 404, I came up with the following:
#Get(':id')
async findOneById(#Res() res, #Param() params): Promise<User> {
const user: User = await this.userService.findOneById(params.id);
if (user === undefined) {
res.status(HttpStatus.NOT_FOUND).send();
}
else {
res.status(HttpStatus.OK).json(user).send();
}
}
..
..
This works, but is a lot more code-y (yes it can be refactored).
This could really use a decorator to handle this situation:
#Get(':id')
#OnUndefined(404)
async findOneById(#Param() params): Promise<User> {
return userService.findOneById(params.id);
}
Anyone aware of a decorator that does this, or a better solution than the one above?
The shortest way to do this would be
#Get(':id')
async findOneById(#Param() params): Promise<User> {
const user: User = await this.userService.findOneById(params.id);
if (user === undefined) {
throw new BadRequestException('Invalid user');
}
return user;
}
There is no point in decorator here because it would have the same code.
Note: BadRequestException is imported from #nestjs/common;
Edit
After some time with, I came with another solution, which is a decorator in the DTO:
import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint } from 'class-validator';
import { createQueryBuilder } from 'typeorm';
#ValidatorConstraint({ async: true })
export class IsValidIdConstraint {
validate(id: number, args: ValidationArguments) {
const tableName = args.constraints[0];
return createQueryBuilder(tableName)
.where({ id })
.getOne()
.then(record => {
return record ? true : false;
});
}
}
export function IsValidId(tableName: string, validationOptions?: ValidationOptions) {
return (object, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [tableName],
validator: IsValidIdConstraint,
});
};
}
Then in your DTO:
export class GetUserParams {
#IsValidId('user', { message: 'Invalid User' })
id: number;
}
Hope it helps someone.
There is no built-in decorator for this, but you can create an interceptor that checks the return value and throws a NotFoundException on undefined:
Interceptor
#Injectable()
export class NotFoundInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle()
.pipe(tap(data => {
if (data === undefined) throw new NotFoundException();
}));
}
}
Then you can use the Interceptor by adding it to either a single endpoint:
#Get(':id')
#UseInterceptors(NotFoundInterceptor)
findUserById(#Param() params): Promise<User> {
return this.userService.findOneById(params.id);
}
or all endpoints of your Controller:
#Controller('user')
#UseInterceptors(NotFoundInterceptor)
export class UserController {
Dynamic Interceptor
You can also pass values to your interceptor to customize its behavior per endpoint.
Pass the parameters in the constructor:
#Injectable()
export class NotFoundInterceptor implements NestInterceptor {
constructor(private errorMessage: string) {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
return stream$
.pipe(tap(data => {
if (data === undefined) throw new NotFoundException(this.errorMessage);
^^^^^^^^^^^^^^^^^
}));
}
}
and then create the interceptor with new:
#Get(':id')
#UseInterceptors(new NotFoundInterceptor('No user found for given userId'))
findUserById(#Param() params): Promise<User> {
return this.userService.findOneById(params.id);
}
Updated version of the #Kim Kern's answer for latests Nestjs versions:
As said on the Nestjs docs:
The interceptors API has also been simplified. In addition, the change was required due to this issue which was reported by the community.
Updated code:
import { Injectable, NestInterceptor, ExecutionContext, NotFoundException, CallHandler } from '#nestjs/common';
import { Observable, pipe } from 'rxjs';
import { tap } from 'rxjs/operators';
#Injectable()
export class NotFoundInterceptor implements NestInterceptor {
constructor(private errorMessage: string) { }
intercept(context: ExecutionContext, stream$: CallHandler): Observable<any> {
return stream$
.handle()
.pipe(tap(data => {
if (data === undefined) { throw new NotFoundException(this.errorMessage); }
}));
}
}
If it's a simple case, I usually do it this lazy way without adding extra fluff:
import {NotFoundException} from '#nestjs/common'
...
#Get(':id')
async findOneById(#Param() params): Promise<User> {
const user: User = await this.userService.findOneById(params.id)
if (!user) throw new NotFoundException('User Not Found')
return user
}
You could just use following to send your desired response in conjunction with correct status codes inside the header.
Inside your route handler in the controller class:
this.whateverService.getYourEntity(
params.id
)
.then(result => {
return res.status(HttpStatus.OK).json(result)
})
.catch(err => {
return res.status(HttpStatus.NOT_FOUND).json(err)
})
For that to work you have to reject the Promise inside your service method like following:
const entity = await this.otherService
.getEntityById(id)
if (!entity) {
return Promise.reject({
statusCode: 404,
message: 'Entity not found'
})
}
return Promise.resolve(entity)
Here I just used another service inside the service class. You could of course just directly fetch your database or do whatever is needed to get your entity.
export const OnUndefined = (
Error: new () => HttpException = NotFoundException,
) => {
return (
_target: unknown,
_propKey: string,
descriptor: PropertyDescriptor,
) => {
const original = descriptor.value;
const mayThrow = (r: unknown) => {
if (undefined === r) throw new Error();
return r;
};
descriptor.value = function (...args: unknown[]) {
const r = Reflect.apply(original, this, args);
if ('function' === typeof r?.then) return r.then(mayThrow);
return mayThrow(r);
};
};
};
Then use like this
#Get(':id')
#OnUndefined()
async findOneById(#Param() params): Promise<User> {
return userService.findOneById(params.id);
}
The OnUndefined function create e decorator that must be used as decribed above.
If the service return a undefined response (the searched id not exist) the controller return a 404 (NotFoundException) or any other excepion passed as parameter to the #OnUndefined decorator
The simplest solution I guess is to edit your UserService that way:
findOneById(id): Promise<User> {
return new Promise<User>((resolve, reject) => {
const user: User = await this.userService.findOneById(id);
user ?
resolve(user) :
reject(new NotFoundException())
}
}
No changes are needed on your controller.
Regards

Categories