From d9d021c2bc089dee20ff491b08f7a16919f16682 Mon Sep 17 00:00:00 2001 From: Heiko Burkhardt Date: Fri, 13 Jan 2023 15:47:04 +0100 Subject: [PATCH] feat: replace address with ens name as id --- .eslintignore | 4 +- .prettierignore | 7 +- packages/backend/src/auth.test.ts | 32 +- packages/backend/src/auth.ts | 37 +- packages/backend/src/delivery.test.ts | 105 ++--- packages/backend/src/delivery.ts | 59 +-- packages/backend/src/messaging.test.ts | 24 +- packages/backend/src/messaging.ts | 40 +- .../backend/src/persistance/getDatabase.ts | 17 +- .../src/persistance/session/getSession.ts | 6 +- .../src/persistance/session/setSession.ts | 7 +- packages/backend/src/profile.test.ts | 46 +-- packages/backend/src/profile.ts | 35 +- packages/backend/src/redis.ts | 78 ++++ .../methods/handleResolveProfileExtension.ts | 17 +- packages/backend/src/rpc/rpc-proxy.test.ts | 56 +-- packages/backend/src/storage.test.ts | 96 ++--- packages/backend/src/storage.ts | 14 +- packages/backend/src/types.ts | 2 +- packages/backend/src/utils.test.ts | 24 +- packages/backend/src/utils.ts | 45 ++- packages/lib/src/account/Account.test.ts | 368 ++++++++---------- packages/lib/src/account/Account.ts | 198 ++++++---- packages/lib/src/account/index.ts | 17 +- packages/lib/src/delivery/Keys.ts | 19 +- packages/lib/src/delivery/Messages.test.ts | 80 ++-- packages/lib/src/delivery/Messages.ts | 16 +- packages/lib/src/delivery/Session.test.ts | 48 ++- packages/lib/src/delivery/Session.ts | 16 +- packages/lib/src/delivery/UserProfile.test.ts | 72 +--- packages/lib/src/delivery/UserProfile.ts | 27 +- .../lib/src/external-apis/BackendAPI.test.ts | 19 +- packages/lib/src/external-apis/BackendAPI.ts | 41 +- .../lib/src/external-apis/InjectedWeb3API.ts | 4 +- .../src/external-apis/OffchainResolverApi.ts | 8 +- packages/lib/src/messaging/Envelop.test.ts | 8 +- packages/lib/src/messaging/Envelop.ts | 4 +- packages/lib/src/messaging/Message.test.ts | 20 +- packages/lib/src/session/Connect.ts | 5 +- packages/lib/src/session/SignIn/signIn.ts | 37 +- .../lib/src/session/SignIn/signProfile.ts | 4 +- .../src/session/SignIn/signProfileKeyPair.ts | 8 +- packages/lib/src/storage/Storage.test.ts | 58 ++- packages/lib/src/storage/Storage.ts | 17 +- .../lib/src/storage/location/Dm3Storage.ts | 9 +- .../deployments/dev-add_dm3_eth-2.json | 41 ++ packages/offchain-resolver/package.json | 2 +- packages/offchain-resolver/scripts/deploy.ts | 9 +- .../offchain-resolver/src/http/ccipGateway.ts | 1 + .../offchain-resolver/src/http/profile.ts | 12 +- packages/offchain-resolver/src/index.ts | 9 + .../offchain-resolver/src/utils/getSigner.ts | 3 +- .../src/utils/getWeb3Provider.ts | 2 +- packages/react/src/Dm3.tsx | 9 - packages/react/src/chat/Chat.tsx | 21 +- packages/react/src/chat/ChatHeader.tsx | 10 +- .../react/src/contacts/AccountNameHeader.tsx | 7 +- packages/react/src/contacts/ContactList.tsx | 2 +- .../react/src/contacts/ContractListEntry.tsx | 15 +- packages/react/src/reducers/Accounts.ts | 17 +- packages/react/src/reducers/Auth.ts | 6 +- packages/react/src/reducers/Cache.ts | 29 +- packages/react/src/reducers/Connection.ts | 2 +- packages/react/src/reducers/UserDB.ts | 10 +- packages/react/src/reducers/shared.ts | 1 - packages/react/src/sign-in/Connectors.ts | 44 +-- packages/react/src/sign-in/SignIn.tsx | 2 +- packages/react/src/sign-in/getDatabase.ts | 19 +- packages/react/src/storage/StorageView.tsx | 5 +- packages/react/src/ui-shared/Avatar.tsx | 36 +- .../react/src/ui-shared/RequestContacts.ts | 49 +-- packages/react/src/user-info/UserInfo.tsx | 21 +- test-data/encrypted-envelops.test.ts | 28 +- yarn.lock | 21 +- 74 files changed, 1105 insertions(+), 1182 deletions(-) create mode 100644 packages/backend/src/redis.ts create mode 100644 packages/offchain-resolver/deployments/dev-add_dm3_eth-2.json diff --git a/.eslintignore b/.eslintignore index b8fa252d0..0e2ed52c2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,6 @@ package.json **/dist **/build -**/*.json \ No newline at end of file +**/*.json +**/coverage +**/dist.* diff --git a/.prettierignore b/.prettierignore index 19f6a96e2..2732d077d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,7 @@ **/dist -**/build \ No newline at end of file +**/build +**/coverage +**/dist.* +**/typechain +**/artifacts +**/*.json \ No newline at end of file diff --git a/packages/backend/src/auth.test.ts b/packages/backend/src/auth.test.ts index 98509e4b1..f6d5b9753 100644 --- a/packages/backend/src/auth.test.ts +++ b/packages/backend/src/auth.test.ts @@ -20,35 +20,13 @@ describe('Auth', () => { describe('getChallenge', () => { describe('schema', () => { - it('Returns 400 if schema is invalid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(auth()); - - app.locals.db = { - getSession: async (accountAddress: string) => ({ - challenge: '123', - }), - }; - - app.locals.storeSession = async ( - accountAddress: string, - session: any, - ) => { - return (_: any, __: any, ___: any) => {}; - }; - - const { status } = await request(app).get('/890').send(); - - expect(status).toBe(400); - }); it('Returns 200 if schema is valid', async () => { const app = express(); app.use(bodyParser.json()); app.use(auth()); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', }), setSession: async (_: string, __: any) => { @@ -73,7 +51,7 @@ describe('Auth', () => { app.use(auth()); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', }), setSession: async (_: string, __: any) => { @@ -88,7 +66,7 @@ describe('Auth', () => { const signature = await wallet.signMessage('123'); const { status } = await request(app).post(`/1234`).send({ - signature, + signature: 123, }); expect(status).toBe(400); @@ -99,7 +77,7 @@ describe('Auth', () => { app.use(auth()); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', }), setSession: async (_: string, __: any) => { @@ -127,7 +105,7 @@ describe('Auth', () => { app.use(auth()); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: 'my-Challenge', signedUserProfile: { profile: { diff --git a/packages/backend/src/auth.ts b/packages/backend/src/auth.ts index 132dbc2c9..c0b872784 100644 --- a/packages/backend/src/auth.ts +++ b/packages/backend/src/auth.ts @@ -1,24 +1,23 @@ import * as Lib from 'dm3-lib/dist.backend'; import express from 'express'; import cors from 'cors'; -import { ethers } from 'ethers'; import { WithLocals } from './types'; const getChallengeSchema = { type: 'object', properties: { - address: { type: 'string' }, + ensName: { type: 'string' }, }, - required: ['address'], + required: ['ensName'], additionalProperties: false, }; const createNewSessionTokenParamsSchema = { type: 'object', properties: { - address: { type: 'string' }, + ensName: { type: 'string' }, }, - required: ['address'], + required: ['ensName'], additionalProperties: false, }; @@ -38,27 +37,25 @@ export default () => { router.use(cors()); router.get( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, + ); const schemaIsValid = Lib.validateSchema( getChallengeSchema, req.params, ); - if ( - !schemaIsValid || - !ethers.utils.isAddress(req.params.address) - ) { + if (!schemaIsValid) { return res.send(400); } - const account = Lib.external.formatAddress(req.params.address); - const challenge = await Lib.delivery.createChallenge( req.app.locals.db.getSession, req.app.locals.db.setSession, - account, + ensName, ); res.json({ @@ -71,9 +68,12 @@ export default () => { ); router.post( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, + ); const paramsAreValid = Lib.validateSchema( createNewSessionTokenParamsSchema, req.params, @@ -86,20 +86,15 @@ export default () => { const schemaIsValid = paramsAreValid && bodyIsValid; - if ( - !schemaIsValid || - !ethers.utils.isAddress(req.params.address) - ) { + if (!schemaIsValid) { return res.send(400); } - const account = Lib.external.formatAddress(req.params.address); - const token = await Lib.delivery.createNewSessionToken( req.app.locals.db.getSession, req.app.locals.db.setSession, req.body.signature, - account, + ensName, ); res.json({ diff --git a/packages/backend/src/delivery.test.ts b/packages/backend/src/delivery.test.ts index 66772c6ff..2882ced23 100644 --- a/packages/backend/src/delivery.test.ts +++ b/packages/backend/src/delivery.test.ts @@ -1,5 +1,4 @@ import bodyParser from 'body-parser'; -import { ethers } from 'ethers'; import express from 'express'; import request from 'supertest'; import auth from './auth'; @@ -25,10 +24,14 @@ describe('Delivery', () => { const app = express(); app.use(bodyParser.json()); app.use(delivery()); - (app.locals.keys = { - signing: keysA.signingKeyPair, - encryption: keysA.encryptionKeyPair, + (app.locals.web3Provider = { + resolveName: async () => + '0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', }), + (app.locals.keys = { + signing: keysA.signingKeyPair, + encryption: keysA.encryptionKeyPair, + }), (app.locals.redisClient = { exists: (_: any) => false, }); @@ -36,7 +39,7 @@ describe('Delivery', () => { const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => + getSession: async (ensName: string) => Promise.resolve({ challenge: 'my-Challenge', signedUserProfile: { @@ -54,10 +57,7 @@ describe('Delivery', () => { }; const { status } = await request(app) - .get( - // eslint-disable-next-line max-len - '/messages/0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870/contact/0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', - ) + .get('/messages/alice.eth/contact/bob.eth') .set({ authorization: `Bearer ${token}`, }) @@ -66,34 +66,6 @@ describe('Delivery', () => { expect(status).toBe(200); }); - it('Returns 400 if schema is invalid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(delivery()); - - const token = await createAuthToken(); - - app.locals.db = { - getSession: async (accountAddress: string) => ({ - challenge: '123', - token, - }), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; - }, - getPending: (_: any) => [], - }; - - const { status } = await request(app) - .get('/messages/01234/contact/5679') - .set({ - authorization: `Bearer ${token}`, - }) - - .send(); - - expect(status).toBe(400); - }); }); describe('getPendingMessages', () => { @@ -107,11 +79,15 @@ describe('Delivery', () => { sMembers: (_: any) => [], del: (_: any) => {}, }; + app.locals.web3Provider = { + resolveName: async () => + '0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', + }; const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', token, }), @@ -134,39 +110,6 @@ describe('Delivery', () => { expect(status).toBe(200); }); - it('Returns 400 if schema is invalid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(delivery()); - - app.locals.redisClient = { - exists: (_: any) => false, - sMembers: (_: any) => [], - del: (_: any) => {}, - }; - - const token = await createAuthToken(); - - app.locals.db = { - getSession: async (accountAddress: string) => ({ - challenge: '123', - token, - }), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; - }, - }; - - const { status } = await request(app) - .post('/messages/1234/pending') - .set({ - authorization: `Bearer ${token}`, - }) - - .send(); - - expect(status).toBe(400); - }); }); describe('syncAcknoledgment', () => { @@ -183,11 +126,15 @@ describe('Delivery', () => { hGetAll: () => ['123', '456'], zRemRangeByScore: (_: any, __: any, ___: any) => 0, }; + app.locals.web3Provider = { + resolveName: async () => + '0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', + }; const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', token, }), @@ -226,11 +173,15 @@ describe('Delivery', () => { sMembers: (_: any) => [], del: (_: any) => {}, }; + app.locals.web3Provider = { + resolveName: async () => + '0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', + }; const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', token, }), @@ -263,11 +214,15 @@ describe('Delivery', () => { sMembers: (_: any) => [], del: (_: any) => {}, }; + app.locals.web3Provider = { + resolveName: async () => + '0x99C19AB10b9EC8aC6fcda9586E81f6B73a298870', + }; const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: '123', token, }), @@ -303,7 +258,7 @@ const createAuthToken = async () => { }; app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ challenge: 'my-Challenge', signedUserProfile: { profile: { diff --git a/packages/backend/src/delivery.ts b/packages/backend/src/delivery.ts index b4235b011..57a1aa237 100644 --- a/packages/backend/src/delivery.ts +++ b/packages/backend/src/delivery.ts @@ -1,28 +1,17 @@ import cors from 'cors'; import * as Lib from 'dm3-lib/dist.backend'; -import { isAddress } from 'ethers/lib/utils'; import express from 'express'; import { RedisPrefix } from './persistance/getDatabase'; import { WithLocals } from './types'; import { auth } from './utils'; -const getMessagesSchema = { - type: 'object', - properties: { - address: { type: 'string' }, - contact_address: { type: 'string' }, - }, - required: ['address', 'contact_address'], - additionalProperties: false, -}; - const syncAcknoledgmentParamsSchema = { type: 'object', properties: { - address: { type: 'string' }, + ensName: { type: 'string' }, last_message_pull: { type: 'string' }, }, - required: ['address', 'last_message_pull'], + required: ['ensName', 'last_message_pull'], additionalProperties: false, }; const syncAcknoledgmentBodySchema = { @@ -41,38 +30,24 @@ export default () => { const router = express.Router(); //TODO remove router.use(cors()); - router.param('address', auth); + router.param('ensName', auth); router.get( - '/messages/:address/contact/:contact_address', + '/messages/:ensName/contact/:contactEnsName', async (req: express.Request & { app: WithLocals }, res, next) => { try { - const { address, contact_address } = req.params; - - const isSchemaValid = Lib.validateSchema( - getMessagesSchema, - req.params, + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, ); - if ( - !( - isSchemaValid && - isAddress(address) && - isAddress(contact_address) - ) - ) { - return res.send(400); - } - - const account = Lib.external.formatAddress(address); - const contact = Lib.external.formatAddress( - req.params.contact_address, + const contactEnsName = Lib.account.normalizeEnsName( + req.params.contactEnsName, ); const newMessages = await Lib.delivery.getMessages( req.app.locals.db.getMessages, req.app.locals.keys.encryption, - account, - contact, + ensName, + contactEnsName, ); res.json(newMessages); @@ -82,9 +57,9 @@ export default () => { }, ); - router.post('/messages/:address/pending', async (req, res, next) => { + router.post('/messages/:ensName/pending', async (req, res, next) => { try { - const account = Lib.external.formatAddress(req.params.address); + const account = Lib.account.normalizeEnsName(req.params.ensName); const pending = await req.app.locals.db.getPending( account, @@ -101,7 +76,7 @@ export default () => { }); router.post( - '/messages/:address/syncAcknoledgment/:last_message_pull', + '/messages/:ensName/syncAcknoledgment/:last_message_pull', async (req, res, next) => { const hasValidParams = Lib.validateSchema( syncAcknoledgmentParamsSchema, @@ -124,13 +99,15 @@ export default () => { } try { - const account = Lib.external.formatAddress(req.params.address); + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, + ); await Promise.all( req.body.acknoledgments.map( async (ack: Lib.delivery.Acknoledgment) => { const conversationId = Lib.storage.getConversationId( - account, + ensName, ack.contactAddress, ); @@ -142,7 +119,7 @@ export default () => { await req.app.locals.redisClient.hSet( redisKey, - account, + ensName, req.params.last_message_pull, ); diff --git a/packages/backend/src/messaging.test.ts b/packages/backend/src/messaging.test.ts index aeacbe227..d147aa0fc 100644 --- a/packages/backend/src/messaging.test.ts +++ b/packages/backend/src/messaging.test.ts @@ -1,7 +1,6 @@ import express from 'express'; import { Socket } from 'socket.io'; import { onConnection } from './messaging'; -import { socketAuth } from './utils'; import { testData } from '../../../test-data/encrypted-envelops.test'; import * as Lib from 'dm3-lib/dist.backend'; import { WithLocals } from './types'; @@ -34,22 +33,6 @@ const keysA = { const keyPair = Lib.crypto.createKeyPair(); describe('Messaging', () => { - describe('socketAuth', () => { - it('throws error if address is not a valid ethereum address', async () => { - const app = {} as express.Express & WithLocals; - - const getSocketMock = jest.fn(() => { - return { - handshake: { auth: { account: { address: 'foo' } } }, - } as unknown as Socket; - }); - const next = jest.fn((e: any) => { - expect(e.message).toBe('Invalid address'); - }); - - await socketAuth(app)(getSocketMock(), next); - }); - }); describe('submitMessage', () => { it('returns success if schema is valid', (done: any) => { //We expect the callback functions called once witht he value 'success' @@ -73,6 +56,10 @@ describe('Messaging', () => { }, deliveryServiceProperties: { sizeLimit: 2 ** 14 }, + web3Provider: { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }, db: { getSession, createMessage: () => {} }, redisClient: { @@ -126,6 +113,7 @@ describe('Messaging', () => { } as Lib.delivery.Session; }; //We provide an mocked express app with all needes locals vars + const app = { locals: { logger, @@ -139,6 +127,8 @@ describe('Messaging', () => { db: { getSession: session, createMessage: () => {} }, web3Provider: { getTransactionCount: (_: string) => Promise.resolve(0), + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', }, redisClient: { zAdd: () => {}, diff --git a/packages/backend/src/messaging.ts b/packages/backend/src/messaging.ts index 7c0ff49aa..333687a3c 100644 --- a/packages/backend/src/messaging.ts +++ b/packages/backend/src/messaging.ts @@ -7,11 +7,11 @@ import { WithLocals } from './types'; const pendingMessageSchema = { type: 'object', properties: { - accountAddress: { type: 'string' }, - contactAddress: { type: 'string' }, + ensName: { type: 'string' }, + contactEnsName: { type: 'string' }, token: { type: 'string' }, }, - required: ['accountAddress', 'contactAddress', 'token'], + required: ['ensName', 'contactEnsName', 'token'], additionalProperties: false, }; @@ -88,11 +88,7 @@ export function onConnection(app: express.Application & WithLocals) { data, ); - const addressesAreValid = - isAddress(data.accountAddress) && - isAddress(data.contactAddress); - - if (!isSchemaValid || !addressesAreValid) { + if (!isSchemaValid) { const error = 'invalid schema'; app.locals.logger.warn({ @@ -103,18 +99,34 @@ export function onConnection(app: express.Application & WithLocals) { return callback({ error }); } - const account = Lib.external.formatAddress(data.accountAddress); - const contact = Lib.external.formatAddress(data.contactAddress); + let ensName: string; + let contactEnsName: string; + + try { + ensName = Lib.account.normalizeEnsName(data.ensName); + contactEnsName = Lib.account.normalizeEnsName( + data.contactEnsName, + ); + } catch (error) { + app.locals.logger.warn({ + method: 'WS PENDING MESSAGE', + error, + }); + + return callback({ error }); + } + app.locals.logger.info({ method: 'WS PENDING MESSAGE', - account, - contact, + ensName, + contactEnsName, }); try { if ( !(await Lib.delivery.checkToken( + app.locals.web3Provider, app.locals.db.getSession, - account, + ensName, data.token, )) ) { @@ -126,7 +138,7 @@ export function onConnection(app: express.Application & WithLocals) { return callback({ error }); } - await app.locals.db.addPending(account, contact); + await app.locals.db.addPending(ensName, contactEnsName); callback({ response: 'success' }); } catch (error) { diff --git a/packages/backend/src/persistance/getDatabase.ts b/packages/backend/src/persistance/getDatabase.ts index 58f737160..025872230 100644 --- a/packages/backend/src/persistance/getDatabase.ts +++ b/packages/backend/src/persistance/getDatabase.ts @@ -73,21 +73,18 @@ export interface IDatabase { deleteExpiredMessages: (time: number) => Promise; setSession: ( - account: string, + ensName: string, session: Lib.delivery.Session, ) => Promise; - getSession: (account: string) => Promise; + getSession: (ensName: string) => Promise; getUserStorage: ( - accountAddress: string, + ensName: string, ) => Promise; - setUserStorage: (accountAddress: string, data: string) => Promise; - addPending: ( - accountAddress: string, - contactAddress: string, - ) => Promise; - getPending: (accountAddress: string) => Promise; - deletePending: (accountAddress: string) => Promise; + setUserStorage: (ensName: string, data: string) => Promise; + addPending: (ensName: string, contactEnsName: string) => Promise; + getPending: (ensName: string) => Promise; + deletePending: (ensName: string) => Promise; } export type Redis = Awaited>; diff --git a/packages/backend/src/persistance/session/getSession.ts b/packages/backend/src/persistance/session/getSession.ts index a7e1f08d3..59e5ae595 100644 --- a/packages/backend/src/persistance/session/getSession.ts +++ b/packages/backend/src/persistance/session/getSession.ts @@ -2,10 +2,8 @@ import { Redis, RedisPrefix } from '../getDatabase'; import * as Lib from 'dm3-lib/dist.backend'; export function getSession(redis: Redis) { - return async (account: string) => { - const session = await redis.get( - RedisPrefix.Session + Lib.external.formatAddress(account), - ); + return async (ensName: string) => { + const session = await redis.get(RedisPrefix.Session + ensName); if (!session) { return null; } diff --git a/packages/backend/src/persistance/session/setSession.ts b/packages/backend/src/persistance/session/setSession.ts index b4df76e51..5944e0124 100644 --- a/packages/backend/src/persistance/session/setSession.ts +++ b/packages/backend/src/persistance/session/setSession.ts @@ -2,7 +2,7 @@ import { Redis, RedisPrefix } from '../getDatabase'; import * as Lib from 'dm3-lib/dist.backend'; export function setSession(redis: Redis) { - return async (account: string, session: Lib.delivery.Session) => { + return async (ensName: string, session: Lib.delivery.Session) => { const isValid = Lib.validateSchema( Lib.delivery.schema.SessionSchema, session, @@ -11,9 +11,6 @@ export function setSession(redis: Redis) { if (!isValid) { throw Error('Invalid session'); } - await redis.set( - RedisPrefix.Session + Lib.external.formatAddress(account), - Lib.stringify(session), - ); + await redis.set(RedisPrefix.Session + ensName, Lib.stringify(session)); }; } diff --git a/packages/backend/src/profile.test.ts b/packages/backend/src/profile.test.ts index 2c169a46f..25d3ecc94 100644 --- a/packages/backend/src/profile.test.ts +++ b/packages/backend/src/profile.test.ts @@ -14,7 +14,7 @@ describe('Profile', () => { app.use(profile()); app.locals.db = { - getSession: async (accountAddress: string) => ({ + getSession: async (ensName: string) => ({ signedUserProfile: {}, }), setSession: async (_: string, __: any) => { @@ -28,23 +28,6 @@ describe('Profile', () => { expect(status).toBe(200); }); - it('Returns 400 if schema is invalid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(profile()); - - app.locals.db = { - getSession: async (accountAddress: string) => ({ - signedUserProfile: {}, - }), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; - }, - }; - const { status, body } = await request(app).get('/12345').send(); - - expect(status).toBe(400); - }); }); describe('submitUserProfile', () => { @@ -53,13 +36,21 @@ describe('Profile', () => { app.use(bodyParser.json()); app.use(profile()); - app.locals.db = { - getSession: async (accountAddress: string) => - Promise.resolve(null), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; + const mnemonic = + 'announce room limb pattern dry unit scale effort smooth jazz weasel alcohol'; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + app.locals = { + web3Provider: { resolveName: async () => wallet.address }, + db: { + getSession: async (ensName: string) => + Promise.resolve(null), + setSession: async (_: string, __: any) => { + return (_: any, __: any, ___: any) => {}; + }, + getPending: (_: any) => [], }, - getPending: (_: any) => [], }; const userProfile: UserProfile = { @@ -68,11 +59,6 @@ describe('Profile', () => { deliveryServices: [], }; - const mnemonic = - 'announce room limb pattern dry unit scale effort smooth jazz weasel alcohol'; - - const wallet = ethers.Wallet.fromMnemonic(mnemonic); - const createUserProfileMessage = Lib.account.getProfileCreationMessage( Lib.stringify(userProfile), @@ -123,7 +109,7 @@ describe('Profile', () => { const signedUserProfile = { profile: userProfile, - signature, + signature: null, }; const { status } = await request(app) diff --git a/packages/backend/src/profile.ts b/packages/backend/src/profile.ts index 48f869757..c2b7bfed0 100644 --- a/packages/backend/src/profile.ts +++ b/packages/backend/src/profile.ts @@ -7,9 +7,9 @@ import { WithLocals } from './types'; const getProfileSchema = { type: 'object', properties: { - address: { type: 'string' }, + ensName: { type: 'string' }, }, - required: ['address'], + required: ['ensName'], additionalProperties: false, }; @@ -17,20 +17,16 @@ export default () => { const router = express.Router(); router.get( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { - const schemaIsValid = Lib.validateSchema( - getProfileSchema, - req.params, + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, ); - if (!schemaIsValid || !isAddress(req.params.address)) { - return res.status(400).send({ error: 'invalid schema' }); - } const profile = await Lib.delivery.getUserProfile( req.app.locals.db.getSession, - req.params.address, + ensName, ); if (profile) { res.json(profile); @@ -44,26 +40,29 @@ export default () => { ); router.post( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { const schemaIsValid = Lib.validateSchema( - getProfileSchema, - req.params, + Lib.account.schema.SignedUserProfile, + req.body, ); - if (!schemaIsValid || !isAddress(req.params.address)) { + if (!schemaIsValid) { return res.status(400).send({ error: 'invalid schema' }); } - const account = Lib.external.formatAddress(req.params.address); + const ensName = Lib.account.normalizeEnsName( + req.params.ensName, + ); res.json( await Lib.delivery.submitUserProfile( + req.app.locals.web3Provider, req.app.locals.db.getSession, req.app.locals.db.setSession, - account, + ensName, req.body, - (accountAddress: string) => - req.app.locals.db.getPending(accountAddress), + (ensName: string) => + req.app.locals.db.getPending(ensName), (socketId: string) => req.app.locals.io.sockets .to(socketId) diff --git a/packages/backend/src/redis.ts b/packages/backend/src/redis.ts new file mode 100644 index 000000000..3dea34217 --- /dev/null +++ b/packages/backend/src/redis.ts @@ -0,0 +1,78 @@ +import 'dotenv/config'; +import { createClient } from 'redis'; +import * as Lib from 'dm3-lib/dist.backend'; +import { Express } from 'express'; +import { stringify } from 'safe-stable-stringify'; + +const endpointUrl = process.env.REDIS_URL || 'redis://127.0.0.1:6379'; + +export enum RedisPrefix { + Conversation = 'conversation:', + Sync = 'sync:', + Session = 'session:', + UserStorage = 'user.storage:', + Pending = 'pending:', +} + +export async function createRedisClient(app: Express) { + const socketConf = { + socket: { + tls: true, + rejectUnauthorized: false, + }, + }; + /* const client = createClient({ + url: endpointUrl, + ...(process.env.NODE_ENV == 'development' ? {} : socketConf), + }); */ + + const client = createClient(); + client.on('error', (error) => { + app.locals.logger.error({ + method: 'REDIS CLIENT', + error, + }); + }); + await client.connect(); + return client; +} + +export async function getUserStorage( + ensName: string, + redisClient: Awaited>, +): Promise { + const userStorage = await redisClient.get( + RedisPrefix.UserStorage + ensName, + ); + return userStorage ? JSON.parse(userStorage) : null; +} + +export async function setUserStorage( + ensName: string, + data: string, + redisClient: Awaited>, +): Promise { + await redisClient.set(RedisPrefix.UserStorage + ensName, stringify(data)); +} + +export async function addPending( + ensName: string, + contactEnsName: string, + redisClient: Awaited>, +): Promise { + await redisClient.sAdd(RedisPrefix.Pending + contactEnsName, ensName); +} + +export async function getPending( + ensName: string, + redisClient: Awaited>, +): Promise { + return redisClient.sMembers(RedisPrefix.Pending + ensName); +} + +export async function deletePending( + ensName: string, + redisClient: Awaited>, +): Promise { + await redisClient.del(RedisPrefix.Pending + ensName); +} diff --git a/packages/backend/src/rpc/methods/handleResolveProfileExtension.ts b/packages/backend/src/rpc/methods/handleResolveProfileExtension.ts index 440a98575..eebcce791 100644 --- a/packages/backend/src/rpc/methods/handleResolveProfileExtension.ts +++ b/packages/backend/src/rpc/methods/handleResolveProfileExtension.ts @@ -10,23 +10,14 @@ export function handleResolveProfileExtension(axios: Axios) { next: express.NextFunction, ) => { const { - params: [name], + params: [ensName], } = req.body; - //Resolve the address of the provided ENS name - const address = await req.app.locals.web3Provider.resolveName(name); - if (!address) { - const error = 'unknown ens-name'; - - req.app.locals.logger.warn({ - method: 'RPC - RESOLVE PROFILE', - error, - }); - return res.status(400).send({ error }); - } + const normalizedEnsName = Lib.account.normalizeEnsName(ensName); //Get the Session to retrive profileExtension - const session = await req.app.locals.db.getSession(address); + + const session = await req.app.locals.db.getSession(normalizedEnsName); //The requesito ens-name it not known to the delivery service if (!session) { diff --git a/packages/backend/src/rpc/rpc-proxy.test.ts b/packages/backend/src/rpc/rpc-proxy.test.ts index 3c0789192..cfdf504e2 100644 --- a/packages/backend/src/rpc/rpc-proxy.test.ts +++ b/packages/backend/src/rpc/rpc-proxy.test.ts @@ -16,6 +16,9 @@ const logger = { error: log, }; +const SENDER_NAME = 'alice.eth'; +const RECEIVER_NAME = 'bob.eth'; + const SENDER_ADDRESS = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; const RECEIVER_ADDRESS = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; @@ -90,7 +93,10 @@ describe('rpc-Proxy', () => { encryption: keysA.encryptionKeyPair, }, deliveryServiceProperties: { sizeLimit: 2 ** 14 }, - + web3Provider: { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }, loadSession: getSession, redisClient: { zAdd: () => {}, @@ -190,39 +196,6 @@ describe('rpc-Proxy', () => { }); describe('resolveProfileExtension', () => { - it('return 400 if ens-name is not linked to an address', async () => { - const mockPost = jest.fn((url: string, body: any) => { - return Promise.reject('Should not have been invoked'); - }); - const axiosMock = { - post: mockPost, - } as Partial; - - const app = express(); - app.use(bodyParser.json()); - app.use(RpcProxy(axiosMock as Axios)); - - app.locals = { - logger, - web3Provider: { - resolveName: (_: string) => Promise.resolve(null), - }, - }; - - const { status, body } = await request(app) - .post('/') - .send({ - jsonrpc: '2.0', - method: 'dm3_getProfileExtension', - params: ['unknown.eth'], - }); - - expect(mockPost).not.toBeCalled(); - expect(status).toBe(400); - expect(body).toStrictEqual({ - error: 'unknown ens-name', - }); - }); it('return 400 if user is unknown', async () => { const mockPost = jest.fn((url: string, body: any) => { return Promise.reject('Should not have been invoked'); @@ -238,8 +211,7 @@ describe('rpc-Proxy', () => { app.locals = { logger, web3Provider: { - resolveName: (_: string) => - Promise.resolve(RECEIVER_ADDRESS), + resolveName: (_: string) => Promise.resolve(RECEIVER_NAME), }, db: { getSession: (_: string) => Promise.resolve(null), @@ -276,8 +248,7 @@ describe('rpc-Proxy', () => { app.locals = { logger, web3Provider: { - resolveName: (_: string) => - Promise.resolve(RECEIVER_ADDRESS), + resolveName: (_: string) => Promise.resolve(RECEIVER_NAME), }, db: { getSession: (_: string) => @@ -323,14 +294,15 @@ describe('rpc-Proxy', () => { }); }); -const getSession = async (address: string) => { +const getSession = async (ensName: string) => { const emptyProfile: Lib.account.UserProfile = { publicSigningKey: '', publicEncryptionKey: '', deliveryServices: [''], }; - const isSender = Lib.external.formatAddress(address) === SENDER_ADDRESS; - const isReceiver = Lib.external.formatAddress(address) === RECEIVER_ADDRESS; + + const isSender = Lib.account.normalizeEnsName(ensName) === SENDER_NAME; + const isReceiver = Lib.account.normalizeEnsName(ensName) === RECEIVER_NAME; const session = ( account: string, @@ -350,7 +322,7 @@ const getSession = async (address: string) => { } if (isReceiver) { - return session(RECEIVER_ADDRESS, 'abc', { + return session(RECEIVER_NAME, 'abc', { ...emptyProfile, publicEncryptionKey: (await keyPair).publicKey, }); diff --git a/packages/backend/src/storage.test.ts b/packages/backend/src/storage.test.ts index cc1af8438..1cf26e99f 100644 --- a/packages/backend/src/storage.test.ts +++ b/packages/backend/src/storage.test.ts @@ -28,7 +28,7 @@ describe('Storage', () => { const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => + getSession: async (ensName: string) => Promise.resolve({ challenge: '123', token, @@ -46,47 +46,20 @@ describe('Storage', () => { return {}; }, }; - const { status } = await request(app) - .get(`/0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1`) - .set({ - authorization: `Bearer ${token}`, - }) - .send(); - - expect(status).toBe(200); - }); - it('Returns 200 if schema is valid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(storage()); - - app.locals.redisClient = { - get: (_: any) => JSON.stringify({}), + app.locals.web3Provider = { + resolveName: async () => + '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1', }; - - const token = await createAuthToken(); - - app.locals.db = { - getSession: async (accountAddress: string) => - Promise.resolve({ - challenge: '123', - token, - }), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; - }, - }; - const { status } = await request(app) - .get(`/12345`) + .get(`/bob.eth`) .set({ authorization: `Bearer ${token}`, }) .send(); - expect(status).toBe(400); + expect(status).toBe(200); }); }); @@ -99,7 +72,7 @@ describe('Storage', () => { const token = await createAuthToken(); app.locals.db = { - getSession: async (accountAddress: string) => + getSession: async (ensName: string) => Promise.resolve({ challenge: '123', token, @@ -109,46 +82,19 @@ describe('Storage', () => { }, setUserStorage: (_: string, __: string) => {}, }; - - const { status } = await request(app) - .post(`/0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1`) - .set({ - authorization: `Bearer ${token}`, - }) - .send(); - - expect(status).toBe(200); - }); - it('Returns 400 if schema is invalid', async () => { - const app = express(); - app.use(bodyParser.json()); - app.use(storage()); - - app.locals.redisClient = { - set: (_: any) => {}, - }; - - const token = await createAuthToken(); - - app.locals.db = { - getSession: async (accountAddress: string) => - Promise.resolve({ - challenge: '123', - token, - }), - setSession: async (_: string, __: any) => { - return (_: any, __: any, ___: any) => {}; - }, + app.locals.web3Provider = { + resolveName: async () => + '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1', }; const { status } = await request(app) - .post(`/1234`) + .post(`/bob.eth`) .set({ authorization: `Bearer ${token}`, }) .send(); - expect(status).toBe(400); + expect(status).toBe(200); }); }); }); @@ -158,8 +104,14 @@ const createAuthToken = async () => { app.use(bodyParser.json()); app.use(auth()); - app.locals.redisClient = { - exists: (_: any) => false, + app.locals = { + web3Provider: { + resolveName: async () => + '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1', + }, + redisClient: { + exists: (_: any) => false, + }, }; app.locals.db = { @@ -181,11 +133,9 @@ const createAuthToken = async () => { '3A893rTBPEa3g9FL2vgDreY3vvXnOiYCOoJURNyctncwH' + '0En/mcwo/t2v2jtQx/pcnOpTzuJwLuZviTQjd9vBQ=='; - const { body } = await request(app) - .post(`/0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1`) - .send({ - signature, - }); + const { body } = await request(app).post(`/bob.eth`).send({ + signature, + }); return body.token; }; diff --git a/packages/backend/src/storage.ts b/packages/backend/src/storage.ts index 3335da933..31b75129a 100644 --- a/packages/backend/src/storage.ts +++ b/packages/backend/src/storage.ts @@ -10,13 +10,15 @@ export default () => { //TODO remove router.use(cors()); - router.param('address', auth); + router.param('ensName', auth); router.get( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { - const account = Lib.external.formatAddress(req.params.address); + const account = Lib.account.normalizeEnsName( + req.params.ensName, + ); const userStorage = await req.app.locals.db.getUserStorage( account, ); @@ -28,10 +30,12 @@ export default () => { ); router.post( - '/:address', + '/:ensName', async (req: express.Request & { app: WithLocals }, res, next) => { try { - const account = Lib.external.formatAddress(req.params.address); + const account = Lib.account.normalizeEnsName( + req.params.ensName, + ); await req.app.locals.db.setUserStorage( account, diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 09e62d6fc..041c89609 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -10,5 +10,5 @@ export interface WithLocals { 'deliveryServiceProperties', Lib.delivery.DeliveryServiceProperties > & - Record<'web3Provider', ethers.providers.BaseProvider>; + Record<'web3Provider', ethers.providers.JsonRpcProvider>; } diff --git a/packages/backend/src/utils.test.ts b/packages/backend/src/utils.test.ts index db9fa687e..42cdd702d 100644 --- a/packages/backend/src/utils.test.ts +++ b/packages/backend/src/utils.test.ts @@ -30,12 +30,17 @@ describe('Utils', () => { }, }; + app.locals.web3Provider = { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }; + app.locals.logger = { warn: (_: string) => {}, }; const { status, body } = await request(app) - .get('/0x25A643B6e52864d0eD816F1E43c0CF49C83B8292') + .get('/alice.eth') .set({ authorization: `Bearer foo` }) .send(); @@ -61,6 +66,12 @@ describe('Utils', () => { return (_: any, __: any, ___: any) => {}; }, }; + + app.locals.web3Provider = { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }; + app.locals.logger = { warn: (_: string) => {}, }; @@ -96,6 +107,11 @@ describe('Utils', () => { }, }; + app.locals.web3Provider = { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }; + app.locals.logger = { warn: (_: string) => {}, }; @@ -131,6 +147,12 @@ describe('Utils', () => { return (_: any, __: any, ___: any) => {}; }, }; + + app.locals.web3Provider = { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + }; + app.locals.logger = { warn: (_: string) => {}, }; diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index ddd6fcf2f..220648606 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -1,6 +1,5 @@ import * as Lib from 'dm3-lib/dist.backend'; import { ethers } from 'ethers'; -import { isAddress } from 'ethers/lib/utils'; import { Express, NextFunction, Request, Response } from 'express'; import { Socket } from 'socket.io'; import { ExtendedError } from 'socket.io/dist/namespace'; @@ -10,22 +9,28 @@ export async function auth( req: Request, res: Response, next: NextFunction, - address: string, + ensName: string, ) { - //Address has to be a valid ethereum addresss - if (!isAddress(address)) { - return res.sendStatus(400); - } - - const account = Lib.external.formatAddress(address); + const normalizedEnsName = Lib.account.normalizeEnsName(ensName); const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; + const address = await req.app.locals.web3Provider.resolveName(ensName); + if (!address) { + req.app.locals.logger.warn({ + method: 'AUTH', + error: 'Token check failed: Could not resolve ENS name', + normalizedEnsName, + }); + res.sendStatus(401); + } + if ( token && (await Lib.delivery.checkToken( + req.app.locals.web3Provider, req.app.locals.db.getSession, - account, + address, token, )) ) { @@ -34,7 +39,7 @@ export async function auth( req.app.locals.logger.warn({ method: 'AUTH', error: 'Token check failed', - account, + normalizedEnsName, }); res.sendStatus(401); } @@ -45,32 +50,32 @@ export function socketAuth(app: Express & WithLocals) { socket: Socket, next: (err?: ExtendedError | undefined) => void, ) => { - const address = socket.handshake.auth.account.address; - if (!isAddress(address)) { - return next(new Error('Invalid address')); - } - const account = Lib.external.formatAddress(address as string); + const ensName = Lib.account.normalizeEnsName( + socket.handshake.auth.account.ensName, + ); + app.locals.logger.info({ method: 'WS CONNECT', - account, + ensName, socketId: socket.id, }); if ( !(await Lib.delivery.checkToken( + app.locals.web3Provider, app.locals.db.getSession, - account, + ensName, socket.handshake.auth.token as string, )) ) { return next(new Error('invalid username')); } - const session = await app.locals.db.getSession(account); + const session = await app.locals.db.getSession(ensName); if (!session) { throw Error('Could not get session'); } - await app.locals.db.setSession(account, { + await app.locals.db.setSession(ensName, { ...session, socketId: socket.id, }); @@ -140,7 +145,7 @@ export function readKeysFromEnv(env: NodeJS.ProcessEnv): { export function getWeb3Provider( env: NodeJS.ProcessEnv, -): ethers.providers.BaseProvider { +): ethers.providers.JsonRpcProvider { const readKey = (keyName: string) => { const key = env[keyName]; if (!key) { diff --git a/packages/lib/src/account/Account.test.ts b/packages/lib/src/account/Account.test.ts index 2cd079b85..328ea848d 100644 --- a/packages/lib/src/account/Account.test.ts +++ b/packages/lib/src/account/Account.test.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { ethers, providers } from 'ethers'; import { stringify } from '../shared/stringify'; import { sha256 } from 'ethers/lib/utils'; @@ -26,6 +26,9 @@ import { publishProfileOnchain, SignedUserProfile, getProfileCreationMessage, + normalizeNamehash, + getNamehash, + normalizeEnsName, } from './Account'; const connection: Connection = { @@ -33,7 +36,7 @@ const connection: Connection = { storageLocation: StorageLocation.File, defaultServiceUrl: '', account: { - address: '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', + ensName: 'alice.eth', }, provider: {} as any, }; @@ -52,6 +55,7 @@ const keys = { }; const getProfileData = async (): Promise<{ + address: string; signedUserProfile: SignedUserProfile; account: Account; }> => { @@ -72,8 +76,9 @@ const getProfileData = async (): Promise<{ const signature = await wallet.signMessage(createUserProfileMessage); return { + address: wallet.address, account: { - address: wallet.address, + ensName: 'bob.eth', profile, }, signedUserProfile: { @@ -86,48 +91,32 @@ const getProfileData = async (): Promise<{ describe('Account', () => { describe('getAccountDisplayName', () => { test('get correct account display name', async () => { - const ensNames = new Map(); - ensNames.set('0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', 'test1'); - expect( - getAccountDisplayName( - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ensNames, - ), - ).toStrictEqual('test1'); + expect(getAccountDisplayName('alice.eth')).toStrictEqual( + 'alice.eth', + ); - expect( - getAccountDisplayName( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - ensNames, - ), - ).toStrictEqual('0x25...8292'); + expect(getAccountDisplayName('Alice.eth')).toStrictEqual( + 'alice.eth', + ); + + expect(getAccountDisplayName('0x25a6....eth')).toStrictEqual( + '0x25a6....eth', + ); }); test('get correct account display name for file', async () => { - const ensNames = new Map(); - expect( getAccountDisplayName( - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ensNames, + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292.addr.dm3.eth', + true, ), - ).toStrictEqual('0xDd-7855'); + ).toStrictEqual('0x25a6-.eth'); }); test('get correct account display name for short account', async () => { - const ensNames = new Map(); - - expect( - getAccountDisplayName('0xDd55', ensNames, true), - ).toStrictEqual('0xDd55'); - }); - - test('get correct account display name if account is undefined', async () => { - const ensNames = new Map(); - ensNames.set('0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', 'test1'); - expect(getAccountDisplayName(undefined, ensNames)).toStrictEqual( - '', + expect(getAccountDisplayName('alice.eth', true)).toStrictEqual( + 'alice.eth', ); }); }); @@ -142,8 +131,49 @@ describe('Account', () => { }); }); + describe('normalizeNamehash', () => { + test('should normalize a namehash', async () => { + expect( + normalizeNamehash( + '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeF', + ), + ).toStrictEqual( + '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', + ); + }); + test('should reject a 31 bytes hash value ', async () => { + expect(() => + normalizeNamehash( + '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd', + ), + ).toThrowError('Namehash must be a 32 bytes hex value'); + }); + }); + + describe('getNamehash', () => { + test('should get the correct namehash for an ENS name', async () => { + expect(getNamehash({ ensName: 'alice.eth' })).toStrictEqual( + '0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec', + ); + }); + }); + + describe('normalizeEnsName', () => { + test('should normalize an ENS name', async () => { + expect(normalizeEnsName('Alice.eth')).toStrictEqual('alice.eth'); + }); + }); + + describe('getNamehash', () => { + test('should get the correct namehash for an ENS name', async () => { + expect(getNamehash({ ensName: 'alice.eth' })).toStrictEqual( + '0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec', + ); + }); + }); + describe('checkProfileHash', () => { - test('should accept a correct hash ', async () => { + test('should accept a correct hash', async () => { const profile: UserProfile = { publicSigningKey: '3', publicEncryptionKey: '2', @@ -164,7 +194,7 @@ describe('Account', () => { expect(checkProfileHash(signedProfile, uri)).toStrictEqual(true); }); - test('should reject an invalid hash ', async () => { + test('should reject an invalid hash', async () => { const profile: UserProfile = { publicSigningKey: '3', publicEncryptionKey: '2', @@ -206,48 +236,40 @@ describe('Account', () => { describe('checkUserProfile', () => { test('checkUserProfile should accept a correct signature ', async () => { - const profile: UserProfile = { - publicSigningKey: '3', - publicEncryptionKey: '2', - deliveryServices: [''], - }; - - const wallet = ethers.Wallet.createRandom(); - const createUserProfileMessage = getProfileCreationMessage( - stringify(profile), - ); - const signature = await wallet.signMessage( - createUserProfileMessage, - ); + const profile = await getProfileData(); expect( - checkUserProfile( + await checkUserProfile( { - profile: profile, - signature, + resolveName: async () => profile.address, + } as any, + + { + profile: profile.account.profile!, + signature: profile.signedUserProfile.signature, }, - wallet.address, + + 'alice.eth', ), ).toStrictEqual(true); }); test('checkUserProfile should reject an invalid signature ', async () => { - const profile: UserProfile = { - publicSigningKey: '3', - publicEncryptionKey: '2', - deliveryServices: [''], - }; - - const wallet = ethers.Wallet.createRandom(); - - const signature = await wallet.signMessage( - stringify(profile.publicEncryptionKey), - ); + const profile = await getProfileData(); expect( - checkUserProfile( - { profile: profile, signature }, - wallet.address, + await checkUserProfile( + { + resolveName: async () => profile.address, + } as any, + { + profile: { + ...profile.account.profile!, + deliveryServices: ['test.test'], + }, + signature: profile.signedUserProfile.signature, + }, + 'alice.eth', ), ).toStrictEqual(false); }); @@ -264,24 +286,23 @@ describe('Account', () => { synced: true, }; - userDb.conversations.set( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - [], - ); + userDb.conversations.set('alice.eth,bob.eth', []); expect( await getContacts( - connection, + { + ...connection, + provider: { resolveName: async () => '' } as any, + }, '', async () => undefined, async () => [], - async () => '', userDb, () => {}, ), ).toStrictEqual([ { - address: '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + ensName: 'bob.eth', profile: undefined, }, ]); @@ -297,30 +318,25 @@ describe('Account', () => { synced: true, }; - userDb.conversations.set( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - [], - ); + userDb.conversations.set('alice.eth,bob.eth', []); expect( await getContacts( { ...connection, account: { - address: - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + ensName: 'bob.eth', }, }, '', async () => undefined, async () => [], - async () => '', userDb, () => {}, ), ).toStrictEqual([ { - address: '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', + ensName: 'alice.eth', profile: undefined, }, ]); @@ -338,26 +354,27 @@ describe('Account', () => { const profile = await getProfileData(); userDb.conversations.set( - getConversationId( - profile.account.address, - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ), + getConversationId(profile.account.ensName, 'bob.eth'), [], ); expect( await getContacts( - connection, + { + ...connection, + provider: { + resolveName: async () => profile.address, + } as any, + }, '', async () => profile.signedUserProfile, async () => [], - async () => '', userDb, () => {}, ), ).toStrictEqual([ { - address: profile.account.address, + ensName: profile.account.ensName, profile: profile.signedUserProfile.profile, }, ]); @@ -373,11 +390,7 @@ describe('Account', () => { synced: true, }; - userDb.conversations.set( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,' + - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - [], - ); + userDb.conversations.set('alice.eth,' + 'bob.eth', []); const conversations: string[] = []; @@ -385,15 +398,14 @@ describe('Account', () => { connection, '', async () => undefined, - async () => ['0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'], - async () => '', + async () => ['bob.eth'], userDb, (id) => { conversations.push(id); }, ); expect(Array.from(userDb.conversations.keys())).toStrictEqual([ - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', + 'alice.eth,bob.eth', ]); }); @@ -413,16 +425,13 @@ describe('Account', () => { connection, '', async () => undefined, - async () => ['0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'], - async () => '', + async () => ['bob.eth'], userDb, (id) => { conversations.push(id); }, ); - expect(conversations).toStrictEqual([ - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ]); + expect(conversations).toStrictEqual(['alice.eth,bob.eth']); }); test('should throw if provider is undefined', async () => { @@ -433,7 +442,6 @@ describe('Account', () => { '', async () => undefined, async () => [], - async () => '', {} as any, () => {}, ), @@ -452,19 +460,11 @@ describe('Account', () => { synced: true, }; - addContact( - connection, - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - async () => null, - userDb, - (id: string) => { - expect(id).toStrictEqual( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ); - done(); - return true; - }, - ); + addContact(connection, 'bob.eth', userDb, (id: string) => { + expect(id).toStrictEqual('alice.eth,bob.eth'); + done(); + return true; + }); }); test('Should create an empty conversation for a new contact after resolving the ENS name', (done) => { @@ -480,42 +480,15 @@ describe('Account', () => { addContact( connection, 'test.eth', - async () => '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + userDb, (id: string) => { - expect(id).toStrictEqual( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ); + expect(id).toStrictEqual('alice.eth,test.eth'); done(); }, ); }); - test('Should throw if name could not be resolved', async () => { - const userDb: UserDB = { - conversations: new Map(), - conversationsCount: 0, - keys, - lastChangeTimestamp: 0, - syncProcessState: SyncProcessState.Idle, - synced: true, - }; - - await expect( - addContact( - connection, - 'test.eth', - async () => null, - userDb, - (id: string) => { - expect(id).toStrictEqual( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ); - }, - ), - ).rejects.toEqual(Error(`Couldn't resolve name`)); - }); - test('Should reject to add a contact if the contact was already added', async () => { const userDb: UserDB = { conversations: new Map(), @@ -526,38 +499,20 @@ describe('Account', () => { synced: true, }; - userDb.conversations.set( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292,0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - [], - ); + userDb.conversations.set('alice.eth,bob.eth', []); userDb.conversationsCount = 1; expect.assertions(1); await expect( - addContact( - connection, - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - async () => null, - userDb, - () => false, - ), + addContact(connection, 'bob.eth', userDb, () => false), ).rejects.toEqual(Error('Contact exists already.')); }); }); describe('getBrowserStorageKey', () => { test('should return the correct storage key', async () => { - expect( - getBrowserStorageKey( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - ), - ).toStrictEqual( - 'userStorageSnapshot0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - ); - }); - test('should return the correct storage key', async () => { - expect(() => getBrowserStorageKey(null as any)).toThrowError( - 'No address provided', + expect(getBrowserStorageKey('alice.eth')).toStrictEqual( + 'userStorageSnapshot:alice.eth', ); }); }); @@ -584,27 +539,38 @@ describe('Account', () => { describe('checkUserProfile', () => { test('Should accept a valid profile signature', async () => { + const profile = await getProfileData(); expect( - checkUserProfile( - (await getProfileData()).signedUserProfile, - (await getProfileData()).account.address, + await checkUserProfile( + { + resolveName: async () => profile.address, + } as any, + profile.signedUserProfile, + profile.account.ensName, ), ).toStrictEqual(true); }); test('Should reject an invalid profile signature', async () => { - const userProfile = (await getProfileData()).account.profile!; + const profile = await getProfileData(); expect( - checkUserProfile( + await checkUserProfile( + { + resolveName: async () => profile.address, + } as any, { profile: { - ...userProfile, + ...profile.account.profile!, deliveryServices: ['http://1'], }, - signature: (await getProfileData()).signedUserProfile - .signature, + signature: ( + await getProfileData() + ).signedUserProfile.signature, }, - (await getProfileData()).account.address, + + ( + await getProfileData() + ).account.ensName, ), ).toStrictEqual(false); }); @@ -630,7 +596,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => undefined, async () => 'http://123?' + createHashUrlParam(signedUserProfile), @@ -649,7 +615,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => ({ test: 'test' } as any), async () => 'test', @@ -677,7 +643,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => undefined, async () => 'ipfs://QmZwrAZFDprTo2h3Gbdc4hS2vEVSP1q9j7vDTq8TS1Z137', @@ -711,7 +677,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => undefined, async () => 'data:application/json,' + @@ -739,7 +705,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => signedUserProfile, async () => undefined, @@ -771,7 +737,7 @@ describe('Account', () => { await expect( getUserProfile( { provider: {} } as any, - '0x8101b0729eb9708a344c820fce80f12a90a7c1fa', + 'bob.eth', async () => signedUserProfile2, async () => 'http://123?' + createHashUrlParam(signedUserProfile), @@ -793,10 +759,13 @@ describe('Account', () => { const tx = await publishProfileOnchain( { ...connection, + provider: { + resolveName: async () => profile.address, + } as any, account: profile.account, }, 'http://bla', - async () => '0x1', + () => { return { address: '0x2' } as any; }, @@ -809,7 +778,7 @@ describe('Account', () => { ); expect(tx?.args).toStrictEqual([ - '0xca7a0eadca1ba3745db7065063294b717422bd1c70995cba8f5adcd094fdae1d', + '0xbe11069ec59144113f438b6ef59dd30497769fc2dce8e2b52e3ae71ac18e47c9', 'dm3.profile', 'http://bla?dm3Hash=0x352942c3b35370f5424b2a4d263aeca1158a5c6e3c1d0a866c23f9d80e6ea426', ]); @@ -824,7 +793,6 @@ describe('Account', () => { publishProfileOnchain( { ...connection, provider: undefined }, 'http://bla', - async () => '0x1', () => { return { address: '0x2' } as any; }, @@ -845,7 +813,6 @@ describe('Account', () => { publishProfileOnchain( { ...connection, account: undefined }, 'http://bla', - async () => '0x1', () => { return { address: '0x2' } as any; }, @@ -866,7 +833,7 @@ describe('Account', () => { publishProfileOnchain( { ...connection }, 'http://bla', - async () => '0x1', + () => ({ address: '0x2' } as any), () => ({ setText: () => 'success' } as any), async () => undefined, @@ -883,9 +850,11 @@ describe('Account', () => { { ...connection, account: profile.account, + provider: { + resolveName: async () => profile.address, + } as any, }, 'http://bla', - async () => '0x1', () => ({ address: '0x2' } as any), () => ({ setText: () => 'success' } as any), async () => ({ @@ -898,27 +867,6 @@ describe('Account', () => { ).rejects.toEqual(Error('account profile check failed')); }); - test('Should throw if ENS name could not be found', async () => { - expect.assertions(1); - - await expect( - publishProfileOnchain( - { ...connection }, - 'http://bla', - async () => null, - () => { - return { address: '0x2' } as any; - }, - () => { - return { setText: () => 'success' } as any; - }, - async () => { - return undefined; - }, - ), - ).rejects.toEqual(Error('No ENS name found')); - }); - test('Should throw if the ENS could not be obtained ', async () => { expect.assertions(1); @@ -926,7 +874,7 @@ describe('Account', () => { publishProfileOnchain( { ...connection }, 'http://bla', - async () => '0x1', + async () => { return null; }, diff --git a/packages/lib/src/account/Account.ts b/packages/lib/src/account/Account.ts index 7e9d0a7a9..56cfc4bdb 100644 --- a/packages/lib/src/account/Account.ts +++ b/packages/lib/src/account/Account.ts @@ -12,8 +12,6 @@ import { GetConractInstance, GetEnsTextRecord, GetResolver, - LookupAddress, - ResolveName, } from '../external-apis/InjectedWeb3API'; import { log } from '../shared/log'; import { sha256 } from '../shared/sha256'; @@ -53,7 +51,7 @@ export interface PrivateKeys { } export interface Account { - address: string; + ensName: string; profile?: UserProfile; } @@ -65,12 +63,29 @@ export function getProfileCreationMessage(stringifiedProfile: string) { return `Hearby your dm3 profile is linked with your Ethereum account\n\n ${stringifiedProfile}`; } +/** + * calculate the namehash of a given ENS name + * @param account Account with ENS name + */ +export function getNamehash(account: Account): string { + return normalizeNamehash( + ethers.utils.namehash(ethers.utils.nameprep(account.ensName)), + ); +} + +/** + * normalizes an ENS name + * @param ensName name that should be normalized + */ +export function normalizeEnsName(ensName: string): string { + return ethers.utils.nameprep(ensName); +} + export async function getContacts( connection: Connection, deliveryServiceToken: string, getUserProfile: GetUserProfile, getPendingConversations: GetPendingConversations, - resolveName: ResolveName, userDb: UserDB, createEmptyConversationEntry: (id: string) => void, ): Promise { @@ -86,7 +101,7 @@ export async function getContacts( if ( !userDb.conversations.has( getConversationId( - connection.account!.address, + normalizeEnsName(connection.account!.ensName), pendingConversation, ), ) @@ -94,7 +109,6 @@ export async function getContacts( await addContact( connection, pendingConversation, - resolveName, userDb, createEmptyConversationEntry, ); @@ -105,16 +119,16 @@ export async function getContacts( const uncheckedProfiles = await Promise.all( Array.from(userDb.conversations.keys()) .map((conversationId) => conversationId.split(',')) - .map((addresses) => - formatAddress(connection.account!.address) === - formatAddress(addresses[0]) - ? formatAddress(addresses[1]) - : formatAddress(addresses[0]), + .map((ensNames) => + normalizeEnsName(connection.account!.ensName) === + normalizeEnsName(ensNames[0]) + ? normalizeEnsName(ensNames[1]) + : normalizeEnsName(ensNames[0]), ) - .map(async (address) => { - const profile = await getUserProfile(connection, address); + .map(async (ensName) => { + const profile = await getUserProfile(connection, ensName); return { - address, + ensName, profile: profile, }; }), @@ -122,94 +136,97 @@ export async function getContacts( // accept if account has a profile and a valid signature // accept if there is no profile and no signature - return uncheckedProfiles - .filter( - (uncheckedProfile) => - (uncheckedProfile.profile && - checkUserProfile( + return ( + await Promise.all( + uncheckedProfiles.map(async (uncheckedProfile) => ({ + valid: + !uncheckedProfile.profile || + (await checkUserProfile( + connection.provider!, uncheckedProfile.profile, - uncheckedProfile.address, - )) || - !uncheckedProfile.profile, + + uncheckedProfile.ensName, + )), + container: uncheckedProfile, + })), ) + ) + .filter((checkedProfile) => checkedProfile.valid) .map((profileContainer) => ({ - address: profileContainer.address, - profile: profileContainer.profile?.profile, + ensName: profileContainer.container.ensName, + profile: profileContainer.container.profile?.profile, })); } /** * make too long names shorter - * @param accountAddress ethereum account address - * @param ensNames ENS name cache + * @param ensName The ENS name * @param forFile Use shortend name for a file name */ export function getAccountDisplayName( - accountAddress: string | undefined, - ensNames: Map, + ensName: string, forFile?: boolean, ): string { - if (!accountAddress) { - return ''; - } - if (ensNames.get(accountAddress)) { - return ensNames.get(accountAddress) as string; - } - return accountAddress.length > 10 - ? accountAddress.substring(0, 4) + + const normalizedEnsName = normalizeEnsName(ensName); + + return normalizedEnsName.length > 10 + ? normalizedEnsName.substring(0, 6) + (forFile ? '-' : '...') + - accountAddress.substring(accountAddress.length - 4) - : accountAddress; + normalizedEnsName.substring(normalizedEnsName.length - 4) + : normalizedEnsName; } export async function addContact( connection: Connection, - accountInput: string, - resolveName: ResolveName, + ensName: string, userDb: UserDB, createEmptyConversationEntry: (id: string) => void, ) { - if (ethers.utils.isAddress(accountInput)) { - if ( - !createEmptyConversation( - connection, - accountInput, - userDb, - createEmptyConversationEntry, - ) - ) { - throw Error('Contact exists already.'); - } - } else { - const address = await resolveName( - connection.provider as ethers.providers.JsonRpcProvider, - accountInput, - ); - if (address) { - createEmptyConversation( - connection, - address, - userDb, - createEmptyConversationEntry, - ); - } else { - throw Error(`Couldn't resolve name`); - } + if ( + !createEmptyConversation( + connection, + ensName, + userDb, + createEmptyConversationEntry, + ) + ) { + throw Error('Contact exists already.'); } } /** * check the signature of the fetched user profile + * @param provider Eth rpc provider + * @param signedUserProfile The profile to check + * @param ensName The ENS domain name + */ +export async function checkUserProfile( + provider: ethers.providers.JsonRpcProvider, + { profile, signature }: SignedUserProfile, + ensName: string, +): Promise { + const accountAddress = await provider.resolveName(ensName); + + if (!accountAddress) { + throw Error(`Couldn't resolve name`); + } + + return checkUserProfileWithAddress({ profile, signature }, accountAddress); +} + +/** + * check the signature of the fetched user profile using the eth address * @param signedUserProfile The profile to check - * @param accountAddress The Etehereum account address of the profile owner + * @param accountAddress The ENS domain name */ -export function checkUserProfile( +export function checkUserProfileWithAddress( { profile, signature }: SignedUserProfile, accountAddress: string, ): boolean { const createUserProfileMessage = getProfileCreationMessage( stringify(profile), ); + return ( ethers.utils.recoverAddress( ethers.utils.hashMessage(createUserProfileMessage), @@ -239,13 +256,10 @@ export function checkStringSignature( /** * create the string used to create the browser storage key - * @param accountAddress The Ethereum account address + * @param ensName The ENS name */ -export function getBrowserStorageKey(accountAddress: string) { - if (!accountAddress) { - throw Error('No address provided'); - } - return 'userStorageSnapshot' + formatAddress(accountAddress); +export function getBrowserStorageKey(ensName: string) { + return 'userStorageSnapshot:' + normalizeEnsName(ensName); } export type GetResource = (uri: string) => Promise; @@ -318,10 +332,21 @@ export function createHashUrlParam(profile: SignedUserProfile): string { return `dm3Hash=${sha256(stringify(profile))}`; } +/** + * normalizes a namehash + * @param namehash the namehash that should be checked + */ +export function normalizeNamehash(namehash: string): string { + if (!/^(0x)?[a-fA-F0-9]{64}$/.test(namehash)) { + throw Error('Namehash must be a 32 bytes hex value'); + } + return namehash.toLocaleLowerCase(); +} + export async function publishProfileOnchain( connection: Connection, url: string, - lookupAddress: LookupAddress, + getResolver: GetResolver, getConractInstance: GetConractInstance, getProfileOffChain: GetUserProfileOffChain, @@ -332,20 +357,16 @@ export async function publishProfileOnchain( if (!connection.account) { throw Error('No account'); } - const ensName = await lookupAddress( + + const ethersResolver = await getResolver( connection.provider, - connection.account.address, + connection.account.ensName, ); - if (!ensName) { - throw Error('No ENS name found'); - } - - const ethersResolver = await getResolver(connection.provider, ensName); if (!ethersResolver) { throw Error('No resolver found'); } - const node = ethers.utils.namehash(ensName); + const node = ethers.utils.namehash(connection.account.ensName); const resolver = getConractInstance( ethersResolver.address, @@ -358,14 +379,21 @@ export async function publishProfileOnchain( const ownProfile = await getProfileOffChain( connection, connection.account, - connection.account.address, + connection.account.ensName, ); if (!ownProfile) { throw Error('could not load account profile'); } - if (!checkUserProfile(ownProfile, connection.account.address)) { + if ( + !(await checkUserProfile( + connection.provider, + ownProfile, + + connection.account.ensName, + )) + ) { throw Error('account profile check failed'); } diff --git a/packages/lib/src/account/index.ts b/packages/lib/src/account/index.ts index 67754fee3..34d62dbfb 100644 --- a/packages/lib/src/account/index.ts +++ b/packages/lib/src/account/index.ts @@ -6,8 +6,6 @@ import { getConractInstance, getEnsTextRecord, getResolver, - lookupAddress, - resolveName, } from '../external-apis/InjectedWeb3API'; import { UserDB } from '../storage'; import { Connection } from '../web3-provider/Web3Provider'; @@ -25,10 +23,12 @@ export { getBrowserStorageKey, checkStringSignature, getProfileCreationMessage, + normalizeNamehash, + normalizeEnsName, + checkUserProfile, + checkUserProfileWithAddress, } from './Account'; -export { checkUserProfile } from './Account'; - export type { Account, ProfileKeys, UserProfile } from './Account'; export type { ProfileExtension } from './profileExtension'; @@ -37,20 +37,19 @@ export type { ProfileExtension } from './profileExtension'; * add a contact by creating an empty converation with that contact * * @param connection dm3 connection object - * @param accountInput Contact Etehereum account address + * @param ensName The ENS name of the contact * @param userDb User storage database * @param createEmptyConversationEntry Function to create an empty conversation */ export async function addContact( connection: Connection, - accountInput: string, + ensName: string, userDb: UserDB, createEmptyConversationEntry: (id: string) => void, ) { return execAddContact( connection, - accountInput, - resolveName, + ensName, userDb, createEmptyConversationEntry, ); @@ -75,7 +74,6 @@ export async function getContacts( deliveryServiceToken, getUserProfile, getPendingConversations, - resolveName, userDb, createEmptyConversationEntry, ); @@ -91,7 +89,6 @@ export function publishProfileOnchain(connection: Connection, url: string) { return execPublishProfileOnchain( connection, url, - lookupAddress, getResolver, getConractInstance, getUserProfileOffChain, diff --git a/packages/lib/src/delivery/Keys.ts b/packages/lib/src/delivery/Keys.ts index 69aa37fce..32009a988 100644 --- a/packages/lib/src/delivery/Keys.ts +++ b/packages/lib/src/delivery/Keys.ts @@ -1,16 +1,14 @@ -import { formatAddress } from '../external-apis/InjectedWeb3API'; - import { v4 as uuidv4 } from 'uuid'; - import { Session } from './Session'; import { checkSignature } from '../crypto'; +import { normalizeEnsName } from '../account'; export async function createChallenge( getSession: (accountAddress: string) => Promise, setSession: (accountAddress: string, session: Session) => Promise, - accountAddress: string, + ensName: string, ) { - const account = formatAddress(accountAddress); + const account = normalizeEnsName(ensName); const session = await getSession(account); if (!session) { @@ -26,13 +24,12 @@ export async function createChallenge( } export async function createNewSessionToken( - getSession: (accountAddress: string) => Promise, - setSession: (accountAddress: string, session: Session) => Promise, + getSession: (ensName: string) => Promise, + setSession: (ensName: string, session: Session) => Promise, signature: string, - accountAddress: string, + ensName: string, ): Promise { - const account = formatAddress(accountAddress); - const session = await getSession(account); + const session = await getSession(ensName); if (!session) { throw Error('Session not found'); @@ -53,6 +50,6 @@ export async function createNewSessionToken( } const token = uuidv4(); - await setSession(account, { ...session, challenge: undefined, token }); + await setSession(ensName, { ...session, challenge: undefined, token }); return token; } diff --git a/packages/lib/src/delivery/Messages.test.ts b/packages/lib/src/delivery/Messages.test.ts index 3647b418b..c73845806 100644 --- a/packages/lib/src/delivery/Messages.test.ts +++ b/packages/lib/src/delivery/Messages.test.ts @@ -1,15 +1,15 @@ -import { assert } from 'console'; import { BigNumber, ethers } from 'ethers'; -import { UserProfile } from '../account/Account'; -import { decryptAsymmetric } from '../crypto'; -import { formatAddress } from '../external-apis/InjectedWeb3API'; -import { EncryptionEnvelop } from '../messaging/Envelop'; +import { normalizeEnsName, UserProfile } from '../account/Account'; +import { decryptAsymmetric, encryptAsymmetric } from '../crypto'; +import { DeliveryInformation, EncryptionEnvelop } from '../messaging/Envelop'; import { stringify } from '../shared/stringify'; import { getConversationId } from '../storage/Storage'; import { getMessages, incomingMessage } from './Messages'; import { testData } from '../../../../test-data/encrypted-envelops.test'; import { Session } from './Session'; +const SENDER_NAME = 'alice.eth'; +const RECEIVER_NAME = 'bob.eth'; const SENDER_ADDRESS = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; const RECEIVER_ADDRESS = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; @@ -41,14 +41,14 @@ const keysB = { storageEncryptionNonce: 0, }; -const getSession = async (address: string, socketId?: string) => { +const getSession = async (ensName: string, socketId?: string) => { const emptyProfile: UserProfile = { publicSigningKey: '', publicEncryptionKey: '', deliveryServices: [''], }; - const isSender = formatAddress(address) === SENDER_ADDRESS; - const isReceiver = formatAddress(address) === RECEIVER_ADDRESS; + const isSender = normalizeEnsName(ensName) === SENDER_NAME; + const isReceiver = normalizeEnsName(ensName) === RECEIVER_NAME; const session = ( account: string, @@ -70,11 +70,11 @@ const getSession = async (address: string, socketId?: string) => { }); if (isSender) { - return session(SENDER_ADDRESS, '123', emptyProfile); + return session(SENDER_NAME, '123', emptyProfile); } if (isReceiver) { - return session(RECEIVER_ADDRESS, 'abc', { + return session(RECEIVER_NAME, 'abc', { ...emptyProfile, publicEncryptionKey: keysB.encryptionKeyPair.publicKey, }); @@ -116,10 +116,14 @@ describe('Messages', () => { getSession, storeNewMessage, () => {}, - {} as ethers.providers.BaseProvider, + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, ), ).rejects.toEqual(Error('Token check failed')); }); + it('rejects an incoming message if it is to large', async () => { const storeNewMessage = async ( conversationId: string, @@ -149,7 +153,7 @@ describe('Messages', () => { getSession, storeNewMessage, () => {}, - {} as ethers.providers.BaseProvider, + {} as ethers.providers.JsonRpcProvider, ), ).rejects.toEqual(Error('Message is too large')); }); @@ -176,7 +180,7 @@ describe('Messages', () => { ), }, }, - token: 'abc', + token: '123', }, keysA.signingKeyPair, keysA.encryptionKeyPair, @@ -184,10 +188,14 @@ describe('Messages', () => { getSession, storeNewMessage, () => {}, - {} as ethers.providers.BaseProvider, + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, ), ).rejects.toEqual(Error('unknown session')); }); + it('rejects message if the senders nonce is below the threshold', async () => { //Mock the time so we can test the message with the incomming timestamp jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); @@ -205,7 +213,9 @@ describe('Messages', () => { const provider = { getTransactionCount: async (_: string) => Promise.resolve(0), - } as ethers.providers.BaseProvider; + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any; const storeNewMessage = async ( conversationId: string, @@ -246,6 +256,7 @@ describe('Messages', () => { ); } }); + it('rejects message if the senders eth balance is below the threshold', async () => { //Mock the time so we can test the message with the incomming timestamp jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); @@ -264,7 +275,9 @@ describe('Messages', () => { const provider = { getBalance: async (_: string) => Promise.resolve(BigNumber.from(5)), - } as ethers.providers.BaseProvider; + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any; const storeNewMessage = async ( conversationId: string, @@ -305,6 +318,7 @@ describe('Messages', () => { ); } }); + it('rejects message if the senders token balance is below the threshold', async () => { //Mock the time so we can test the message with the incomming timestamp jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); @@ -329,7 +343,9 @@ describe('Messages', () => { const provider = { _isProvider: true, call: () => Promise.resolve(BigNumber.from(0).toHexString()), - } as unknown as ethers.providers.BaseProvider; + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as unknown as ethers.providers.JsonRpcProvider; const storeNewMessage = async ( conversationId: string, @@ -371,6 +387,7 @@ describe('Messages', () => { ); } }); + it('stores proper incoming message', async () => { //Mock the time so we can test the message with the incomming timestamp jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); @@ -411,13 +428,13 @@ describe('Messages', () => { getSession, storeNewMessage, sendMock, - {} as ethers.providers.BaseProvider, + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, ); - const conversationId = getConversationId( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ); + const conversationId = getConversationId('alice.eth', 'bob.eth'); const actualPostmark = await decryptAsymmetric( keysB.encryptionKeyPair, @@ -453,6 +470,7 @@ describe('Messages', () => { //Check if the message was submitted to the socket expect(sendMock).not.toBeCalled(); }); + it('stores proper incoming message and submit it if receiver is connected to a socket', async () => { //Mock the time so we can test the message with the incomming timestamp jest.useFakeTimers().setSystemTime(new Date('2020-01-01')); @@ -495,13 +513,13 @@ describe('Messages', () => { _getSession, storeNewMessage, sendMock, - {} as ethers.providers.BaseProvider, + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, ); - const conversationId = getConversationId( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - ); + const conversationId = getConversationId('alice.eth', 'bob.eth'); const actualPostmark = await decryptAsymmetric( keysB.encryptionKeyPair, @@ -542,8 +560,8 @@ describe('Messages', () => { describe('GetMessages', () => { it('returns all messages of the user', async () => { const conversationIdToUse = getConversationId( - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', + 'alice.eth', + 'bob.eth', ); const loadMessages = async ( @@ -597,8 +615,8 @@ describe('Messages', () => { await getMessages( loadMessages, keysA.encryptionKeyPair, - '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855', - '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + 'bob.eth', + 'alice.eth', ), ).toStrictEqual([ { diff --git a/packages/lib/src/delivery/Messages.ts b/packages/lib/src/delivery/Messages.ts index 15c817941..ceed81780 100644 --- a/packages/lib/src/delivery/Messages.ts +++ b/packages/lib/src/delivery/Messages.ts @@ -1,5 +1,6 @@ import { ethers } from 'ethers'; import stringify from 'safe-stable-stringify'; +import { normalizeEnsName } from '../account'; import { decryptAsymmetric, @@ -8,7 +9,6 @@ import { KeyPair, sign, } from '../crypto'; -import { formatAddress } from '../external-apis/InjectedWeb3API'; import { DeliveryInformation, EncryptionEnvelop, Postmark } from '../messaging'; import { sha256 } from '../shared/sha256'; import { isSpam } from '../spam-filter'; @@ -33,11 +33,11 @@ export async function getMessages( size: number, ) => Promise, encryptionKeyPair: KeyPair, - accountAddress: string, - contactAddress: string, + ensName: string, + contactEnsName: string, ) { - const account = formatAddress(accountAddress); - const contact = formatAddress(contactAddress); + const account = normalizeEnsName(ensName); + const contact = normalizeEnsName(contactEnsName); const conversationId = getConversationId(contact, account); const receivedMessages: EncryptionEnvelop[] = await loadMessages( @@ -48,7 +48,7 @@ export async function getMessages( const envelopContainers = await Promise.all( receivedMessages.map(async (envelop) => ({ - to: formatAddress( + to: normalizeEnsName( JSON.parse( await decryptAsymmetric( encryptionKeyPair, @@ -87,7 +87,7 @@ export async function incomingMessage( envelop: EncryptionEnvelop, ) => Promise, send: (socketId: string, envelop: EncryptionEnvelop) => void, - provider: ethers.providers.BaseProvider, + provider: ethers.providers.JsonRpcProvider, ): Promise { //Checks the size of the incoming message if (messageIsToLarge(envelop, sizeLimit)) { @@ -105,8 +105,10 @@ export async function incomingMessage( deliveryInformation.from, deliveryInformation.to, ); + //Checks if the sender is authenticated const tokenIsValid = await checkToken( + provider, getSession, deliveryInformation.from, token, diff --git a/packages/lib/src/delivery/Session.test.ts b/packages/lib/src/delivery/Session.test.ts index 8bfc870cf..737bfc917 100644 --- a/packages/lib/src/delivery/Session.test.ts +++ b/packages/lib/src/delivery/Session.test.ts @@ -8,34 +8,66 @@ describe('Session', () => { token: 'foo', createdAt: new Date().getTime(), } as Session); - const address = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; - const isValid = await checkToken(getSession, address, 'foo'); + + const isValid = await checkToken( + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, + getSession, + 'alice.eth', + 'foo', + ); expect(isValid).toBe(true); }); + it('Should return false if no session exists for the account ', async () => { const getSession = (_: string) => Promise.resolve(null); - const address = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; - const isValid = await checkToken(getSession, address, 'foo'); + const isValid = await checkToken( + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, + getSession, + 'alice.eth', + 'foo', + ); expect(isValid).toBe(false); }); + it('Should return false if a session exists but the token is wrong ', async () => { const getSession = (_: string) => Promise.resolve({ token: 'bar' } as Session); - const address = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; - const isValid = await checkToken(getSession, address, 'foo'); + const isValid = await checkToken( + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, + getSession, + 'alice.eth', + 'foo', + ); expect(isValid).toBe(false); }); + it('Should return false if a session exists but the token is expired ', async () => { const getSession = (_: string) => Promise.resolve({ token: 'foo', createdAt: 1 } as Session); - const address = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; - const isValid = await checkToken(getSession, address, 'foo'); + const isValid = await checkToken( + { + resolveName: async () => + '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292', + } as any, + getSession, + 'alice.eth', + 'foo', + ); expect(isValid).toBe(false); }); diff --git a/packages/lib/src/delivery/Session.ts b/packages/lib/src/delivery/Session.ts index d8658c721..6e13e2a1e 100644 --- a/packages/lib/src/delivery/Session.ts +++ b/packages/lib/src/delivery/Session.ts @@ -1,6 +1,6 @@ +import { ethers } from 'ethers'; import { ProfileExtension } from '../account'; import { SignedUserProfile } from '../account/Account'; -import { formatAddress } from '../external-apis/InjectedWeb3API'; import { SpamFilterRules } from '../spam-filter/SpamFilterRules'; //1Year @@ -19,12 +19,18 @@ export interface Session { } export async function checkToken( - getSession: (accountAddress: string) => Promise, - accountAddress: string, + provider: ethers.providers.JsonRpcProvider, + getSession: (ensName: string) => Promise, + ensName: string, token: string, ): Promise { - const account = formatAddress(accountAddress); - const session = await getSession(account); + const address = await provider.resolveName(ensName); + + if (!address) { + throw Error(`Couln't resolve ENS name`); + } + + const session = await getSession(ensName); //There is now account for the requesting accoung if (!session) { return false; diff --git a/packages/lib/src/delivery/UserProfile.test.ts b/packages/lib/src/delivery/UserProfile.test.ts index 1849e583a..67eb331e9 100644 --- a/packages/lib/src/delivery/UserProfile.test.ts +++ b/packages/lib/src/delivery/UserProfile.test.ts @@ -4,47 +4,11 @@ import { stringify } from '../shared/stringify'; import { Session } from './Session'; import { getUserProfile, submitUserProfile } from './UserProfile'; +const SENDER_NAME = 'alice.eth'; +const RANDO_NAME = 'bob.eth'; const SENDER_ADDRESS = '0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1'; const RANDO_ADDRESS = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; -const keysA = { - encryptionKeyPair: { - publicKey: - '0x78798cab6f457a23ca7cd3e449cb4fb99197bd5d2c29e3bf299917da75ef320c', - privateKey: - '0xa4c23bec5db0dc62bea2664207803ad560ea21238e9d61974767ff3132dba9b6', - }, - signingKeyPair: { - publicKey: - '0xfad90341665fbfd8b106639bb1ff2d8131d365a8f0004f66b93b450148f67bd2', - privateKey: - '0xf83a5e0630b32021688bbe37ff8ebac89ba7b07479e4186bdc69ea712e1cb895' + - 'fad90341665fbfd8b106639bb1ff2d8131d365a8f0004f66b93b450148f67bd2', - }, - storageEncryptionKey: - '0xf83a5e0630b32021688bbe37ff8ebac89ba7b07479e4186bdc69ea712e1cb895', - storageEncryptionNonce: 0, -}; - -const keysB = { - encryptionKeyPair: { - publicKey: - '0x19867565066fc86c876f6f027006f64edabe435155fffa5269746eac00142608', - privateKey: - '0x395643aa80723066c4ce1c5d1dc4eeaf3a44c31c4fdbfd44322354c7e493e60e', - }, - signingKeyPair: { - publicKey: - '0xee64c50eb6b097cee37b534d9d1858128578b465578479533dc69d968aa2be6d', - privateKey: - '0xf83a5e0630b32021688bbe37ff8ebac89ba7b07479e4186bdc69ea712e1cb89' + - '6ee64c50eb6b097cee37b534d9d1858128578b465578479533dc69d968aa2be6d', - }, - storageEncryptionKey: - '0xf83a5e0630b32021688bbe37ff8ebac89ba7b07479e4186bdc69ea712e1cb896', - storageEncryptionNonce: 0, -}; - const emptyProfile: UserProfile = { publicSigningKey: '', publicEncryptionKey: '', @@ -83,9 +47,10 @@ describe('UserProfile', () => { await expect(async () => { await submitUserProfile( + { resolveName: () => RANDO_ADDRESS } as any, getSession, setSession, - RANDO_ADDRESS, + RANDO_NAME, singedUserProfile, getPendingConversations, send, @@ -115,7 +80,7 @@ describe('UserProfile', () => { }; }; - return session(SENDER_ADDRESS, '123', emptyProfile); + return session(SENDER_NAME, '123', emptyProfile); }; const getPendingConversations = () => Promise.resolve([]); const send = () => {}; @@ -124,28 +89,30 @@ describe('UserProfile', () => { await expect(async () => { await submitUserProfile( + { resolveName: () => SENDER_ADDRESS } as any, getSession, setSession, - SENDER_ADDRESS, + SENDER_NAME, singedUserProfile, getPendingConversations, send, ); }).rejects.toEqual(Error('Profile exists already')); }); + it('skips pending contact without a session', async () => { const setSession = jest.fn(); const getSession = (address: string) => Promise.resolve(null); - const getPendingConversations = () => - Promise.resolve([RANDO_ADDRESS]); + const getPendingConversations = () => Promise.resolve([RANDO_NAME]); const send = jest.fn(); const singedUserProfile = await signProfile(emptyProfile); await submitUserProfile( + { resolveName: () => SENDER_ADDRESS } as any, getSession, setSession, - SENDER_ADDRESS, + SENDER_NAME, singedUserProfile, getPendingConversations, send, @@ -154,26 +121,27 @@ describe('UserProfile', () => { expect(setSession).toBeCalled(); expect(send).not.toBeCalled(); }); + it('notifies pending contact with a socketId', async () => { const setSession = jest.fn(); const getSession = (address: string) => { - if (address === RANDO_ADDRESS) { + if (address === RANDO_NAME) { return Promise.resolve({ socketId: 'foo', } as Session); } return Promise.resolve(null); }; - const getPendingConversations = () => - Promise.resolve([RANDO_ADDRESS]); + const getPendingConversations = () => Promise.resolve([RANDO_NAME]); const send = jest.fn(); const singedUserProfile = await signProfile(emptyProfile); await submitUserProfile( + { resolveName: () => SENDER_ADDRESS } as any, getSession, setSession, - SENDER_ADDRESS, + SENDER_NAME, singedUserProfile, getPendingConversations, send, @@ -182,6 +150,7 @@ describe('UserProfile', () => { expect(setSession).toBeCalled(); expect(send).toBeCalled(); }); + it('stores a newly created user profile', async () => { const setSession = jest.fn(); const getSession = () => Promise.resolve(null); @@ -191,9 +160,10 @@ describe('UserProfile', () => { const singedUserProfile = await signProfile(emptyProfile); await submitUserProfile( + { resolveName: () => SENDER_ADDRESS } as any, getSession, setSession, - SENDER_ADDRESS, + SENDER_NAME, singedUserProfile, getPendingConversations, send, @@ -206,7 +176,7 @@ describe('UserProfile', () => { it('Returns undefined if address has no session', async () => { const getSession = () => Promise.resolve(null); - const profile = await getUserProfile(getSession, RANDO_ADDRESS); + const profile = await getUserProfile(getSession, RANDO_NAME); expect(profile).toBeUndefined(); }); @@ -214,7 +184,7 @@ describe('UserProfile', () => { const getSession = () => Promise.resolve({ signedUserProfile: {} } as Session); - const profile = await getUserProfile(getSession, RANDO_ADDRESS); + const profile = await getUserProfile(getSession, RANDO_NAME); expect(profile).not.toBeUndefined(); }); diff --git a/packages/lib/src/delivery/UserProfile.ts b/packages/lib/src/delivery/UserProfile.ts index 06ffca291..d7043a48b 100644 --- a/packages/lib/src/delivery/UserProfile.ts +++ b/packages/lib/src/delivery/UserProfile.ts @@ -1,19 +1,23 @@ -import { SignedUserProfile, checkUserProfile } from '../account/Account'; +import { + SignedUserProfile, + checkUserProfile, + normalizeEnsName, +} from '../account/Account'; import { getDefaultProfileExtension } from '../account/profileExtension/ProfileExtension'; -import { formatAddress } from '../external-apis'; import { Session } from './Session'; import { v4 as uuidv4 } from 'uuid'; +import { ethers } from 'ethers'; const handlePendingConversations = async ( - account: string, + ensName: string, getSession: (accountAddress: string) => Promise, getPendingConversations: (accountAddress: string) => Promise, send: (socketId: string) => void, ) => { - const pending = await getPendingConversations(account); + const pending = await getPendingConversations(ensName); await Promise.all( pending.map(async (pendingEntry) => { - const contact = formatAddress(pendingEntry); + const contact = normalizeEnsName(pendingEntry); const contactSession = await getSession(contact); if (contactSession?.socketId) { @@ -24,16 +28,17 @@ const handlePendingConversations = async ( }; export async function submitUserProfile( + provider: ethers.providers.JsonRpcProvider, getSession: (accountAddress: string) => Promise, setSession: (accountAddress: string, session: Session) => Promise, - accountAddress: string, + ensName: string, signedUserProfile: SignedUserProfile, getPendingConversations: (accountAddress: string) => Promise, send: (socketId: string) => void, ): Promise { - const account = formatAddress(accountAddress); + const account = normalizeEnsName(ensName); - if (!checkUserProfile(signedUserProfile, account)) { + if (!(await checkUserProfile(provider, signedUserProfile, account))) { throw Error('Signature invalid.'); } if (await getSession(account)) { @@ -51,7 +56,7 @@ export async function submitUserProfile( await setSession(account, session); await handlePendingConversations( - accountAddress, + account, getSession, getPendingConversations, send, @@ -61,9 +66,9 @@ export async function submitUserProfile( export async function getUserProfile( getSession: (accountAddress: string) => Promise, - accountAddress: string, + ensName: string, ): Promise { - const account = formatAddress(accountAddress); + const account = normalizeEnsName(ensName); const session = await getSession(account); return session?.signedUserProfile; } diff --git a/packages/lib/src/external-apis/BackendAPI.test.ts b/packages/lib/src/external-apis/BackendAPI.test.ts index 874bc27bd..3f72ee033 100644 --- a/packages/lib/src/external-apis/BackendAPI.test.ts +++ b/packages/lib/src/external-apis/BackendAPI.test.ts @@ -15,8 +15,8 @@ import { syncAcknoledgment, } from './BackendAPI'; -const SENDER_ADDRESS = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; -const RECEIVER_ADDRESS = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; +const SENDER_NAME = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; +const RECEIVER_NAME = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; jest.mock('../delivery/Delivery', () => ({ getDeliveryServiceClient: jest.fn(() => ({ @@ -66,7 +66,7 @@ describe('BackendAPI', () => { describe('getChallenge', () => { it('Returns a challenge if the user has a deliveryService specified ', async () => { const account = { - address: SENDER_ADDRESS, + ensName: SENDER_NAME, profile: { deliveryServices: ['foo.eth'], }, @@ -97,7 +97,7 @@ describe('BackendAPI', () => { describe('getNewToken', () => { it('Returns a token if a valid signature was provided', async () => { const account = { - address: SENDER_ADDRESS, + ensName: SENDER_NAME, profile: { deliveryServices: ['foo.eth'], }, @@ -112,7 +112,7 @@ describe('BackendAPI', () => { describe('submitUserProfile', () => { it('Returns a userToken if a valid profile was provided', async () => { const account = { - address: SENDER_ADDRESS, + ensName: SENDER_NAME, profile: { deliveryServices: ['foo.eth'], }, @@ -186,6 +186,7 @@ describe('BackendAPI', () => { it('Returns the acknoledgment ', async () => { const connection = { account: { + ensName: SENDER_NAME, profile: {}, }, } as Connection; @@ -263,7 +264,7 @@ describe('BackendAPI', () => { it('Returns new messages', async () => { const connection = { account: { - address: SENDER_ADDRESS, + ensName: SENDER_NAME, profile: {}, }, } as Connection; @@ -285,7 +286,7 @@ describe('BackendAPI', () => { it('returns pending conversations', async () => { const connection = { account: { - address: SENDER_ADDRESS, + ensName: SENDER_NAME, profile: {}, }, } as Connection; @@ -298,7 +299,7 @@ describe('BackendAPI', () => { it('returns profile from given url', async () => { const connection = {} as Connection; const account = {} as Account; - const contact = RECEIVER_ADDRESS; + const contact = RECEIVER_NAME; const url = 'dm3.io'; const profile = await getUserProfileOffChain( @@ -315,7 +316,7 @@ describe('BackendAPI', () => { const account = { profile: {}, } as Account; - const contact = RECEIVER_ADDRESS; + const contact = RECEIVER_NAME; const url = undefined; const profile = await getUserProfileOffChain( diff --git a/packages/lib/src/external-apis/BackendAPI.ts b/packages/lib/src/external-apis/BackendAPI.ts index fd3436ece..3e6638cb0 100644 --- a/packages/lib/src/external-apis/BackendAPI.ts +++ b/packages/lib/src/external-apis/BackendAPI.ts @@ -1,15 +1,19 @@ import axios from 'axios'; -import { Account, SignedUserProfile } from '../account/Account'; +import { + Account, + getNamehash, + normalizeEnsName, + SignedUserProfile, +} from '../account/Account'; import { Acknoledgment } from '../delivery'; import { getDeliveryServiceClient } from '../delivery/Delivery'; import { EncryptionEnvelop, Envelop } from '../messaging'; import { log } from '../shared/log'; import { Connection } from '../web3-provider/Web3Provider'; -import { formatAddress } from './InjectedWeb3API'; -const PROFILE_PATH = '/profile'; -const DELIVERY_PATH = '/delivery'; -const AUTH_SERVICE_PATH = '/auth'; +const PROFILE_PATH = process.env.REACT_APP_BACKEND + '/profile'; +const DELIVERY_PATH = process.env.REACT_APP_BACKEND + '/delivery'; +const AUTH_SERVICE_PATH = process.env.REACT_APP_BACKEND + '/auth'; function getAxiosConfig(token: string) { return { @@ -33,9 +37,9 @@ export async function getChallenge( account: Account, connection: Connection, ): Promise { - const { profile, address } = checkAccount(account); + const { profile, ensName } = checkAccount(account); - const url = `${AUTH_SERVICE_PATH}/${formatAddress(address)}`; + const url = `${AUTH_SERVICE_PATH}/${normalizeEnsName(ensName)}`; const { data } = await getDeliveryServiceClient( profile, @@ -52,9 +56,9 @@ export async function getNewToken( connection: Connection, signature: string, ): Promise { - const { profile, address } = checkAccount(account); + const { profile, ensName } = checkAccount(account); - const url = `${AUTH_SERVICE_PATH}/${formatAddress(address)}`; + const url = `${AUTH_SERVICE_PATH}/${normalizeEnsName(ensName)}`; const { data } = await getDeliveryServiceClient( profile, @@ -73,9 +77,9 @@ export async function submitUserProfile( connection: Connection, signedUserProfile: SignedUserProfile, ): Promise { - const { profile, address } = checkAccount(account); + const { profile, ensName } = checkAccount(account); - const url = `${PROFILE_PATH}/${formatAddress(address)}`; + const url = `${PROFILE_PATH}/${normalizeEnsName(ensName)}`; const { data } = await getDeliveryServiceClient( profile, @@ -85,7 +89,6 @@ export async function submitUserProfile( return data; } - export type SubmitUserProfile = typeof submitUserProfile; export async function submitMessage( @@ -127,9 +130,9 @@ export async function syncAcknoledgment( const { account } = connection; const { profile } = checkAccount(account); - const url = `${DELIVERY_PATH}/messages/${ - account!.address - }/syncAcknoledgment/${lastMessagePull}`; + const url = `${DELIVERY_PATH}/messages/${normalizeEnsName( + account!.ensName, + )}/syncAcknoledgment/${lastMessagePull}`; return getDeliveryServiceClient( profile, @@ -179,9 +182,9 @@ export async function getNewMessages( const { account } = connection; const { profile } = checkAccount(account); - const url = `${DELIVERY_PATH}/messages/${ - account!.address - }/contact/${contactAddress}`; + const url = `${DELIVERY_PATH}/messages/${normalizeEnsName( + account!.ensName, + )}/contact/${contactAddress}`; const { data } = await getDeliveryServiceClient( profile, @@ -200,7 +203,7 @@ export async function getPendingConversations( const { account } = connection; const { profile } = checkAccount(account); - const url = `${DELIVERY_PATH}/messages/${account!.address}/pending/`; + const url = `${DELIVERY_PATH}/messages/${getNamehash(account!)}/pending/`; const { data } = await getDeliveryServiceClient( profile, diff --git a/packages/lib/src/external-apis/InjectedWeb3API.ts b/packages/lib/src/external-apis/InjectedWeb3API.ts index 9f5300b6f..d3d577e8d 100644 --- a/packages/lib/src/external-apis/InjectedWeb3API.ts +++ b/packages/lib/src/external-apis/InjectedWeb3API.ts @@ -7,10 +7,10 @@ export interface Executable { export async function prersonalSign( provider: ethers.providers.JsonRpcProvider, - account: string, + address: string, message: string, ): Promise { - return provider.send('personal_sign', [message, account]); + return provider.send('personal_sign', [message, address]); } export type PersonalSign = typeof prersonalSign; diff --git a/packages/lib/src/external-apis/OffchainResolverApi.ts b/packages/lib/src/external-apis/OffchainResolverApi.ts index 944e07088..e6cbea5a6 100644 --- a/packages/lib/src/external-apis/OffchainResolverApi.ts +++ b/packages/lib/src/external-apis/OffchainResolverApi.ts @@ -16,13 +16,13 @@ export async function claimSubdomain( name: string, signedUserProfile: SignedUserProfile, ): Promise { - const { address } = checkAccount(account); + const { ensName } = checkAccount(account); const url = `${offchainResolverUrl}/name`; const data = { signedUserProfile, name, - address, + ensName, }; const { status } = await axios.post(url, data); @@ -30,12 +30,10 @@ export async function claimSubdomain( } export async function claimAddress( - account: Account, + address: string, offchainResolverUrl: string, signedUserProfile: SignedUserProfile, ) { - const { address } = checkAccount(account); - const url = `${offchainResolverUrl}/address`; const data = { signedUserProfile, diff --git a/packages/lib/src/messaging/Envelop.test.ts b/packages/lib/src/messaging/Envelop.test.ts index f6e52044e..8fb25cd41 100644 --- a/packages/lib/src/messaging/Envelop.test.ts +++ b/packages/lib/src/messaging/Envelop.test.ts @@ -52,7 +52,7 @@ describe('Envelope', () => { deliveryServiceEncryptionPubKey: '', keys: await getMockProfileKeys(), from: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -60,7 +60,7 @@ describe('Envelope', () => { }, }, to: { - address: '', + ensName: '', }, }; expect(async () => { @@ -93,7 +93,7 @@ describe('Envelope', () => { deliveryServiceEncryptionPubKey: '', keys: await getMockProfileKeys(), from: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -101,7 +101,7 @@ describe('Envelope', () => { }, }, to: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', diff --git a/packages/lib/src/messaging/Envelop.ts b/packages/lib/src/messaging/Envelop.ts index 7c92800f8..e08e6571e 100644 --- a/packages/lib/src/messaging/Envelop.ts +++ b/packages/lib/src/messaging/Envelop.ts @@ -49,8 +49,8 @@ export async function buildEnvelop( ); const deliveryInformation: DeliveryInformation = { - to: to.address, - from: from.address, + to: to.ensName, + from: from.ensName, }; /** * Builds the {@see EnvelopMetadata} for the message diff --git a/packages/lib/src/messaging/Message.test.ts b/packages/lib/src/messaging/Message.test.ts index b2028ba31..699eb3013 100644 --- a/packages/lib/src/messaging/Message.test.ts +++ b/packages/lib/src/messaging/Message.test.ts @@ -13,7 +13,9 @@ import { StorageLocation, UserDB } from '../storage'; import { Connection, ConnectionState } from '../web3-provider/Web3Provider'; import { getMessages, submitMessage } from './Message'; +const USER_NAME_1 = 'alice.eth'; const USER_ADDRESS_1 = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; +const USER_NAME_2 = 'bob.eth'; const USER_ADDRESS_2 = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; const getMockProfileKeys = async () => { @@ -39,7 +41,7 @@ describe('Message', () => { const message: Message = { metadata: { to: '', - from: USER_ADDRESS_1, + from: USER_NAME_1, timestamp: 123, type: 'NEW', }, @@ -50,7 +52,7 @@ describe('Message', () => { deliveryServiceEncryptionPubKey: '', keys: await getMockProfileKeys(), from: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -58,7 +60,7 @@ describe('Message', () => { }, }, to: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -103,7 +105,7 @@ describe('Message', () => { const message: Message = { metadata: { to: '', - from: USER_ADDRESS_1, + from: USER_NAME_1, timestamp: 123, type: 'NEW', }, @@ -114,7 +116,7 @@ describe('Message', () => { deliveryServiceEncryptionPubKey: '', keys: await getMockProfileKeys(), from: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -122,7 +124,7 @@ describe('Message', () => { }, }, to: { - address: '', + ensName: '', profile: { deliveryServices: [], publicEncryptionKey: '', @@ -178,7 +180,7 @@ describe('Message', () => { it('Throws Exception if connection has no account', async () => { const connection = {} as Connection; const deliveryServiceToken = ''; - const contact = USER_ADDRESS_2; + const contact = USER_NAME_2; const getNewMessages = jest.fn(); const storeMessages = () => {}; const userDb = {} as UserDB; @@ -208,7 +210,7 @@ describe('Message', () => { publicEncryptionKey: '', publicSigningKey: '', }, - address: USER_ADDRESS_1, + ensName: USER_NAME_1, }, provider: { getResolver: () => { @@ -220,7 +222,7 @@ describe('Message', () => { } as Connection; const deliveryServiceToken = ''; - const contact = USER_ADDRESS_2; + const contact = USER_NAME_2; const keys = await getMockProfileKeys(); diff --git a/packages/lib/src/session/Connect.ts b/packages/lib/src/session/Connect.ts index 46e44939a..af913552b 100644 --- a/packages/lib/src/session/Connect.ts +++ b/packages/lib/src/session/Connect.ts @@ -26,7 +26,10 @@ export async function connectAccount( account, connection.defaultServiceUrl + '/profile/' + account, ); - if (profile && !checkUserProfile(profile, account)) { + if ( + profile && + !(await checkUserProfile(connection.provider, profile, account)) + ) { throw Error('Profile signature is invalid'); } //Todo is this the right way to do it diff --git a/packages/lib/src/session/SignIn/signIn.ts b/packages/lib/src/session/SignIn/signIn.ts index 74ab0c90e..d58fa33f2 100644 --- a/packages/lib/src/session/SignIn/signIn.ts +++ b/packages/lib/src/session/SignIn/signIn.ts @@ -1,4 +1,4 @@ -import { UserProfile } from '../../account'; +import { Account, UserProfile } from '../../account'; import { SubmitUserProfile } from '../../external-apis/BackendAPI'; import { PersonalSign } from '../../external-apis/InjectedWeb3API'; import { stringify } from '../../shared/stringify'; @@ -7,6 +7,8 @@ import { ConnectionState } from '../../web3-provider'; import { Connection } from '../../web3-provider/Web3Provider'; import { createKeyPairsFromSig } from './signProfileKeyPair'; import { signProfile as signProfile } from './signProfile'; +import { claimAddress } from '../../external-apis'; +import { SignedUserProfile } from '../../account/Account'; const DEFAULT_NONCE = 0; @@ -18,6 +20,9 @@ const DEFAULT_NONCE = 0; //1 -> Use reAuth to create a new deliverySerivceToken. User has to sign a challenge //2-> Decrypt storageFile. Sign message with getMessage(nonce) to get the storage encryption key +const ADDR_SUBDOMAIN = '.dev-addr.dm3.eth'; +const OFFCHAIN_RESOLVER_URL = 'http://localhost:8081/profile'; + export async function signIn( connection: Partial, personalSign: PersonalSign, @@ -26,8 +31,9 @@ export async function signIn( connectionState: ConnectionState; db: UserDB; deliveryServiceToken: string; + account: Account; }> { - const { provider, account } = connection; + const { provider } = connection; const nonce = DEFAULT_NONCE; @@ -45,22 +51,33 @@ export async function signIn( deliveryServices: ['dev-ds.dm3.eth'], }; + const address = (await provider!.listAccounts())[0]; + const ensName = address + ADDR_SUBDOMAIN; + //Create signed user profile const signature = await signProfile( provider!, personalSign, - account?.address!, + address, stringify(profile), ); + const signedUserProfile: SignedUserProfile = { + profile, + signature, + }; + + if ( + !(await claimAddress(address, OFFCHAIN_RESOLVER_URL, signedUserProfile)) + ) { + throw Error(`Couldn't claim address subdomain`); + } + //Submit newely created UserProfile const deliveryServiceToken = await submitUserProfile( - { address: account?.address!, profile }, + { ensName: ensName, profile }, connection as Connection, - { - profile, - signature, - }, + signedUserProfile, ); return { @@ -69,5 +86,9 @@ export async function signIn( ...createDB(profileKeys), }, deliveryServiceToken, + account: { + ensName, + profile, + }, }; } diff --git a/packages/lib/src/session/SignIn/signProfile.ts b/packages/lib/src/session/SignIn/signProfile.ts index 28fa3dddd..5e4a49b67 100644 --- a/packages/lib/src/session/SignIn/signProfile.ts +++ b/packages/lib/src/session/SignIn/signProfile.ts @@ -5,13 +5,13 @@ import { PersonalSign } from '../../external-apis/InjectedWeb3API'; export async function signProfile( provider: ethers.providers.JsonRpcProvider, personalSign: PersonalSign, - account: string, + address: string, stringifiedProfile: string, ): Promise { try { const profileCreationMessage = getProfileCreationMessage(stringifiedProfile); - return await personalSign(provider, account, profileCreationMessage); + return await personalSign(provider, address, profileCreationMessage); } catch (e) { throw Error("Can't signIn profile"); } diff --git a/packages/lib/src/session/SignIn/signProfileKeyPair.ts b/packages/lib/src/session/SignIn/signProfileKeyPair.ts index 2dc6bb29e..84b4b31ba 100644 --- a/packages/lib/src/session/SignIn/signProfileKeyPair.ts +++ b/packages/lib/src/session/SignIn/signProfileKeyPair.ts @@ -21,9 +21,15 @@ export async function createKeyPairsFromSig( const storageKeyCreationMessage = getStorageKeyCreationMessage(nonce); + const address = await connection.provider?.resolveName(account.ensName); + + if (!address) { + throw Error(`Couldn't resolve ENS name to eth address`); + } + const signature = await personalSign( provider, - account.address, + address, storageKeyCreationMessage, ); diff --git a/packages/lib/src/storage/Storage.test.ts b/packages/lib/src/storage/Storage.test.ts index 7f699d7e0..dac2e3b83 100644 --- a/packages/lib/src/storage/Storage.test.ts +++ b/packages/lib/src/storage/Storage.test.ts @@ -16,15 +16,15 @@ import { sync, } from './Storage'; -const USER_ADDRESS_1 = '0x25A643B6e52864d0eD816F1E43c0CF49C83B8292'; -const USER_ADDRESS_2 = '0xDd36ae7F9a8E34FACf1e110c6e9d37D0dc917855'; -const USER_ADDRESS_3 = '0x09c3d8547020a044c4879cD0546D448D362124Ae'; +const USER_1 = 'alice.eth'; +const USER_2 = 'bob.eth'; +const USER_3 = 'joe.eth'; const getStorageEnvelopeContainer = (timestamp: number = 0) => { const message: Message = { metadata: { to: '', - from: USER_ADDRESS_1, + from: USER_1, timestamp, type: 'NEW', }, @@ -71,8 +71,8 @@ const getMockProfileKeys = async () => { describe('Storage', () => { describe('Serialize Conversations', () => { it('Should serialize a conversation properly', () => { - const conversion1ID = USER_ADDRESS_1 + USER_ADDRESS_2; - const conversion2ID = USER_ADDRESS_2 + USER_ADDRESS_3; + const conversion1ID = USER_1 + ',' + USER_2; + const conversion2ID = USER_2 + ',' + USER_3; const conversations = new Map(); @@ -107,41 +107,30 @@ describe('Storage', () => { const profileKeys = await getMockProfileKeys(); const connection = { - account: { address: USER_ADDRESS_2 }, + account: { ensName: USER_2 }, } as Connection; const db = createDB(profileKeys); - const conversations = getConversation( - USER_ADDRESS_1, - connection, - db, - ); + const conversations = getConversation(USER_1, connection, db); expect(conversations).toStrictEqual([]); }); it('Returns the conversation between the account specified in the connection and the contact ', async () => { const connection = { - account: { address: USER_ADDRESS_2 }, + account: { ensName: USER_2 }, } as Connection; const profileKeys = await getMockProfileKeys(); const db = createDB(profileKeys); - const conversationId = getConversationId( - USER_ADDRESS_1, - USER_ADDRESS_2, - ); + const conversationId = getConversationId(USER_1, USER_2); const expectedConversation = [getStorageEnvelopeContainer()]; db.conversations.set(conversationId, expectedConversation); - const actualConversation = getConversation( - USER_ADDRESS_1, - connection, - db, - ); + const actualConversation = getConversation(USER_1, connection, db); expect(actualConversation).toStrictEqual(expectedConversation); }); }); @@ -181,7 +170,7 @@ describe('Storage', () => { getStorageEnvelopeContainer(), ]; - const conversationId = USER_ADDRESS_1 + USER_ADDRESS_2; + const conversationId = USER_1 + USER_2; db.conversations.set(conversationId, conversation); @@ -190,7 +179,7 @@ describe('Storage', () => { expect(acknoledgments.length).toBe(1); expect(acknoledgments).toStrictEqual([ { - contactAddress: USER_ADDRESS_1, + contactAddress: USER_1, messageDeliveryServiceTimestamp: 123, }, ]); @@ -211,8 +200,8 @@ describe('Storage', () => { const conversation = [getStorageEnvelopeContainer()]; - const conversationId = USER_ADDRESS_1 + USER_ADDRESS_2; - const emptyConversion = USER_ADDRESS_1 + USER_ADDRESS_3; + const conversationId = USER_1 + ',' + USER_2; + const emptyConversion = USER_1 + ',' + USER_3; db.conversations.set(conversationId, conversation); db.conversations.set(emptyConversion, []); @@ -222,7 +211,7 @@ describe('Storage', () => { expect(acknoledgments.length).toBe(1); expect(acknoledgments).toStrictEqual([ { - contactAddress: USER_ADDRESS_1, + contactAddress: USER_1, messageDeliveryServiceTimestamp: 123, }, ]); @@ -258,7 +247,7 @@ describe('Storage', () => { describe('createEmptyConversation', () => { it('Returns true and creates a new conversation if the conversionId was not used so far', async () => { const connection = { - account: { address: USER_ADDRESS_2 }, + account: { ensName: USER_2 }, } as Connection; const keys = await getMockProfileKeys(); @@ -269,25 +258,22 @@ describe('Storage', () => { const createdNewConversation = createEmptyConversation( connection, - USER_ADDRESS_1, + USER_1, db, createEmptyConversationMock, ); expect(createdNewConversation).toBe(true); expect(createEmptyConversationMock).toBeCalledWith( - getConversationId(USER_ADDRESS_2, USER_ADDRESS_1), + getConversationId(USER_2, USER_1), ); }); it('Returns false and conversionId was used before', async () => { const connection = { - account: { address: USER_ADDRESS_2 }, + account: { ensName: USER_2 }, } as Connection; - const conversionId = getConversationId( - USER_ADDRESS_2, - USER_ADDRESS_1, - ); + const conversionId = getConversationId(USER_2, USER_1); const keys = await getMockProfileKeys(); const db = createDB(keys); @@ -298,7 +284,7 @@ describe('Storage', () => { const createdNewConversation = createEmptyConversation( connection, - USER_ADDRESS_1, + USER_1, db, createEmptyConversationMock, ); diff --git a/packages/lib/src/storage/Storage.ts b/packages/lib/src/storage/Storage.ts index 0c5941a91..061b0f5ea 100644 --- a/packages/lib/src/storage/Storage.ts +++ b/packages/lib/src/storage/Storage.ts @@ -1,7 +1,6 @@ -import { ProfileKeys } from '../account/Account'; +import { normalizeEnsName, ProfileKeys } from '../account/Account'; import { decrypt, encrypt, EncryptedPayload } from '../crypto'; import { Acknoledgment } from '../delivery'; -import { formatAddress } from '../external-apis/InjectedWeb3API'; import { Envelop } from '../messaging'; import { MessageState } from '../messaging/Message'; import { log } from '../shared/log'; @@ -116,7 +115,7 @@ export function getConversation( ): StorageEnvelopContainer[] { const conversationId = getConversationId( contact, - connection.account!.address, + connection.account!.ensName, ); const envelops = db.conversations.get(conversationId); return envelops ?? []; @@ -239,8 +238,10 @@ export async function load( }; } -export function getConversationId(accountA: string, accountB: string): string { - return [formatAddress(accountA), formatAddress(accountB)].sort().join(); +export function getConversationId(ensNameA: string, ensNameB: string): string { + return [normalizeEnsName(ensNameA), normalizeEnsName(ensNameB)] + .sort() + .join(); } /** * Creates a new conversation entry if the conversationId not yet known. @@ -249,13 +250,13 @@ export function getConversationId(accountA: string, accountB: string): string { */ export function createEmptyConversation( connection: Connection, - accountAddress: string, + ensName: string, userDb: UserDB, createEmptyConversationEntry: (id: string) => void, ): boolean { const conversationId = getConversationId( - connection.account!.address, - accountAddress, + connection.account!.ensName, + ensName, ); const conversationIsAlreadyKnown = userDb.conversations.has(conversationId); diff --git a/packages/lib/src/storage/location/Dm3Storage.ts b/packages/lib/src/storage/location/Dm3Storage.ts index 48c7bc829..a097156aa 100644 --- a/packages/lib/src/storage/location/Dm3Storage.ts +++ b/packages/lib/src/storage/location/Dm3Storage.ts @@ -1,5 +1,6 @@ import axios from 'axios'; import { UserDB } from '..'; +import { normalizeEnsName } from '../../account'; import { Acknoledgment } from '../../delivery'; import { getDeliveryServiceClient } from '../../delivery/Delivery'; import { log } from '../../shared/log'; @@ -25,9 +26,9 @@ export async function useDm3Storage( log(`[dm3 Storage] Saving user storage`); const { account } = connection; - const { profile, address } = account!; + const { profile, ensName } = account!; - const url = `${STORAGE_SERVICE}/${address}`; + const url = `${STORAGE_SERVICE}/${normalizeEnsName(ensName)}`; await await getDeliveryServiceClient( profile!, @@ -44,9 +45,9 @@ export async function getDm3Storage( log(`[dm3 Storage] Get user storage`); const { account } = connection; - const { profile, address } = account!; + const { profile, ensName } = account!; - const url = `${STORAGE_SERVICE}/${address}`; + const url = `${STORAGE_SERVICE}/${normalizeEnsName(ensName)}`; const { data } = await getDeliveryServiceClient( profile!, connection, diff --git a/packages/offchain-resolver/deployments/dev-add_dm3_eth-2.json b/packages/offchain-resolver/deployments/dev-add_dm3_eth-2.json new file mode 100644 index 000000000..065b1b775 --- /dev/null +++ b/packages/offchain-resolver/deployments/dev-add_dm3_eth-2.json @@ -0,0 +1,41 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n" + }, + "contracts/IExtendedResolver.sol": { + "content": "pragma solidity ^0.8.4;\n\ninterface IExtendedResolver {\n function resolve(bytes memory name, bytes memory data)\n external\n view\n returns (bytes memory);\n}\n" + }, + "contracts/OffchainResolver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport './IExtendedResolver.sol';\nimport './SignatureVerifier.sol';\n\nimport 'hardhat/console.sol';\n\ninterface ISupportsInterface {\n function supportsInterface(bytes4 interfaceID) external pure returns (bool);\n}\n\nabstract contract SupportsInterface is ISupportsInterface {\n function supportsInterface(bytes4 interfaceID)\n public\n pure\n virtual\n override\n returns (bool)\n {\n return interfaceID == type(ISupportsInterface).interfaceId;\n }\n}\n\ninterface IResolverService {\n function resolve(bytes calldata name, bytes calldata data)\n external\n view\n returns (\n bytes memory result,\n uint64 expires,\n bytes memory sig\n );\n}\n\n/**\n * Implements an ENS resolver that directs all queries to a CCIP read gateway.\n * Callers must implement EIP 3668 and ENSIP 10.\n */\ncontract OffchainResolver is IExtendedResolver, SupportsInterface {\n address public owner;\n string public url;\n mapping(address => bool) public signers;\n\n event NewSigners(address[] signers);\n event NewOwner(address newOwner);\n event SignerRemoved(address removedSinger);\n\n error OffchainLookup(\n address sender,\n string[] urls,\n bytes callData,\n bytes4 callbackFunction,\n bytes extraData\n );\n\n constructor(\n string memory _url,\n address _owner,\n address[] memory _signers\n ) {\n url = _url;\n owner = _owner;\n for (uint256 i = 0; i < _signers.length; i++) {\n signers[_signers[i]] = true;\n }\n emit NewSigners(_signers);\n }\n\n modifier onlyOwner() {\n require(msg.sender == owner, 'only owner');\n _;\n }\n\n function setOwner(address _newOwner) external onlyOwner {\n owner = _newOwner;\n emit NewOwner(owner);\n }\n\n //This function only exists during develoopent. Will be removed before releasing prod\n function setUrl(string memory _url) external onlyOwner {\n url = _url;\n }\n\n function addSigners(address[] memory _signers) external onlyOwner {\n for (uint256 i = 0; i < _signers.length; i++) {\n signers[_signers[i]] = true;\n }\n emit NewSigners(_signers);\n }\n\n function removeSigners(address[] memory _signers) external onlyOwner {\n for (uint256 i = 0; i < _signers.length; i++) {\n //Without this if check it's possible to add a signer to the SignerRemoved Event that never was a signer in the first place. This may cause failures at indexing services that are trying to delete a non-existing signer...\n if (signers[_signers[i]]) {\n signers[_signers[i]] = false;\n emit SignerRemoved((_signers[i]));\n }\n }\n }\n\n function makeSignatureHash(\n address target,\n uint64 expires,\n bytes memory request,\n bytes memory result\n ) external pure returns (bytes32) {\n return\n SignatureVerifier.makeSignatureHash(\n target,\n expires,\n request,\n result\n );\n }\n\n /**\n * Resolves a name, as specified by ENSIP 10.\n * @param name The DNS-encoded name to resolve.\n * @param data The ABI encoded data for the underlying resolution function (Eg, addr(bytes32), text(bytes32,string), etc).\n * @return The return data, ABI encoded identically to the underlying function.\n */\n function resolve(bytes calldata name, bytes calldata data)\n external\n view\n override\n returns (bytes memory)\n {\n bytes memory callData = abi.encodeWithSelector(\n IResolverService.resolve.selector,\n name,\n data\n );\n string[] memory urls = new string[](1);\n urls[0] = url;\n revert OffchainLookup(\n address(this),\n urls,\n callData,\n OffchainResolver.resolveWithProof.selector,\n callData\n );\n }\n\n /**\n * Callback used by CCIP read compatible clients to verify and parse the response.\n * extraData -> the original call data\n */\n function resolveWithProof(bytes calldata response, bytes calldata extraData)\n external\n view\n returns (bytes memory)\n {\n (address signer, bytes memory result) = SignatureVerifier.verify(\n extraData,\n response\n );\n require(signers[signer], 'SignatureVerifier: Invalid sigature');\n return result;\n }\n\n function supportsInterface(bytes4 interfaceID)\n public\n pure\n override\n returns (bool)\n {\n return\n interfaceID == type(IExtendedResolver).interfaceId ||\n super.supportsInterface(interfaceID);\n }\n}\n" + }, + "contracts/SignatureVerifier.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';\n\nlibrary SignatureVerifier {\n /**\n * @dev Generates a hash for signing/verifying.\n * @param target: The address the signature is for.(RESOLVER)\n * @param request: The original request that was sent.\n * @param result: The `result` field of the response (not including the signature part).\n */\n function makeSignatureHash(\n address target,\n uint64 expires,\n bytes memory request,\n bytes memory result\n ) internal pure returns (bytes32) {\n return\n keccak256(\n abi.encodePacked(\n hex'1900',\n target,\n expires,\n keccak256(request),\n keccak256(result)\n )\n );\n }\n\n /**\n * @dev Verifies a signed message returned from a callback.\n * @param request: The original request that was sent.\n * @param response: An ABI encoded tuple of `(bytes result, uint64 expires, bytes sig)`, where `result` is the data to return\n * to the caller, and `sig` is the (r,s,v) encoded message signature.\n * @return signer: The address that signed this message.\n * @return result: The `result` decoded from `response`.\n */\n function verify(bytes calldata request, bytes calldata response)\n internal\n view\n returns (address, bytes memory)\n {\n (bytes memory result, uint64 expires, bytes memory sig) = abi.decode(\n response,\n (bytes, uint64, bytes)\n );\n address signer = ECDSA.recover(\n ECDSA.toEthSignedMessageHash(\n makeSignatureHash(address(this), expires, request, result)\n ),\n sig\n );\n require(\n expires >= block.timestamp,\n 'SignatureVerifier: Signature expired'\n );\n return (signer, result);\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >= 0.4.22 <0.9.0;\n\nlibrary console {\n\taddress constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);\n\n\tfunction _sendLogPayload(bytes memory payload) private view {\n\t\tuint256 payloadLength = payload.length;\n\t\taddress consoleAddress = CONSOLE_ADDRESS;\n\t\tassembly {\n\t\t\tlet payloadStart := add(payload, 32)\n\t\t\tlet r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)\n\t\t}\n\t}\n\n\tfunction log() internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log()\"));\n\t}\n\n\tfunction logInt(int256 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n\t}\n\n\tfunction logUint(uint256 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n\t}\n\n\tfunction logString(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction logBool(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction logAddress(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction logBytes(bytes memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n\t}\n\n\tfunction logBytes1(bytes1 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n\t}\n\n\tfunction logBytes2(bytes2 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n\t}\n\n\tfunction logBytes3(bytes3 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n\t}\n\n\tfunction logBytes4(bytes4 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n\t}\n\n\tfunction logBytes5(bytes5 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n\t}\n\n\tfunction logBytes6(bytes6 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n\t}\n\n\tfunction logBytes7(bytes7 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n\t}\n\n\tfunction logBytes8(bytes8 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n\t}\n\n\tfunction logBytes9(bytes9 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n\t}\n\n\tfunction logBytes10(bytes10 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n\t}\n\n\tfunction logBytes11(bytes11 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n\t}\n\n\tfunction logBytes12(bytes12 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n\t}\n\n\tfunction logBytes13(bytes13 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n\t}\n\n\tfunction logBytes14(bytes14 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n\t}\n\n\tfunction logBytes15(bytes15 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n\t}\n\n\tfunction logBytes16(bytes16 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n\t}\n\n\tfunction logBytes17(bytes17 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n\t}\n\n\tfunction logBytes18(bytes18 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n\t}\n\n\tfunction logBytes19(bytes19 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n\t}\n\n\tfunction logBytes20(bytes20 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n\t}\n\n\tfunction logBytes21(bytes21 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n\t}\n\n\tfunction logBytes22(bytes22 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n\t}\n\n\tfunction logBytes23(bytes23 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n\t}\n\n\tfunction logBytes24(bytes24 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n\t}\n\n\tfunction logBytes25(bytes25 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n\t}\n\n\tfunction logBytes26(bytes26 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n\t}\n\n\tfunction logBytes27(bytes27 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n\t}\n\n\tfunction logBytes28(bytes28 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n\t}\n\n\tfunction logBytes29(bytes29 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n\t}\n\n\tfunction logBytes30(bytes30 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n\t}\n\n\tfunction logBytes31(bytes31 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n\t}\n\n\tfunction logBytes32(bytes32 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n\t}\n\n\tfunction log(uint256 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n\t}\n\n\tfunction log(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction log(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction log(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n\t}\n\n\tfunction log(uint256 p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n\t}\n\n\tfunction log(uint256 p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n\t}\n\n\tfunction log(uint256 p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, uint256 p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, uint256 p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n\t}\n\n\tfunction log(address p0, uint256 p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n\t}\n\n\tfunction log(address p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n\t}\n\n\tfunction log(address p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n\t}\n\n\tfunction log(address p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, bool p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, address p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint256 p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint256 p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint256 p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint256 p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint256 p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint256 p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint256 p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint256 p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, uint256 p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, uint256 p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint256 p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint256 p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint256 p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint256 p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint256 p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint256 p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint256 p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint256 p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, uint256 p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n}\n" + } + }, + "settings": { + "optimizer": { "enabled": true, "runs": 1 }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata" + ], + "": ["ast"] + } + } + } +} \ No newline at end of file diff --git a/packages/offchain-resolver/package.json b/packages/offchain-resolver/package.json index f31247738..90c7b9be5 100644 --- a/packages/offchain-resolver/package.json +++ b/packages/offchain-resolver/package.json @@ -24,7 +24,7 @@ "docker:up": "docker-compose up -d", "deploy:goerli": " hardhat run scripts/deploy.ts --network goerli", "deploy:mainnet": " hardhat run scripts/deploy.ts --network mainnet", - "start": "node dist", + "start": "node dist/src/", "test": " npm run docker:up && nyc hardhat test test/**test.ts src/**/**.test.ts ", "build": "tsc && cp ./config.yml ./dist/config.yml | true" }, diff --git a/packages/offchain-resolver/scripts/deploy.ts b/packages/offchain-resolver/scripts/deploy.ts index 4d504ae8b..d756f8100 100644 --- a/packages/offchain-resolver/scripts/deploy.ts +++ b/packages/offchain-resolver/scripts/deploy.ts @@ -9,15 +9,10 @@ async function main() { 'OffchainResolver', ); - const signer = ethers.Wallet.createRandom(); - - console.log(`Signer address: ${signer.address}`); - console.log(`SIGNER PK: ${signer.privateKey}`); - const offchainResolver = await OffchainResolver.deploy( - 'http://localhost:8081', + 'http://localhost:8081/{sender}/{data}.json', accounts[0].address, - [signer.getAddress()], + ['0x300AdE3DF46e3531004F9c0E19EdEa62Be3f67f2'], ); await offchainResolver.deployed(); diff --git a/packages/offchain-resolver/src/http/ccipGateway.ts b/packages/offchain-resolver/src/http/ccipGateway.ts index 6c7ffec3e..230ee3388 100644 --- a/packages/offchain-resolver/src/http/ccipGateway.ts +++ b/packages/offchain-resolver/src/http/ccipGateway.ts @@ -32,6 +32,7 @@ export function ccipGateway(signer: Signer, resolverAddr: string) { calldata, 'text(bytes32,string)', ); + Lib.log(`GET ${resolverAddr} ${calldata}`); return res.send({ data }); } catch (e) { diff --git a/packages/offchain-resolver/src/http/profile.ts b/packages/offchain-resolver/src/http/profile.ts index 2e56e3ceb..9a972bccb 100644 --- a/packages/offchain-resolver/src/http/profile.ts +++ b/packages/offchain-resolver/src/http/profile.ts @@ -20,7 +20,7 @@ export function profile(web3Provider: ethers.providers.BaseProvider) { return res.status(400).send({ error: 'invalid schema' }); } - const profileIsValid = Lib.account.checkUserProfile( + const profileIsValid = Lib.account.checkUserProfileWithAddress( signedUserProfile, address, ); @@ -71,7 +71,7 @@ export function profile(web3Provider: ethers.providers.BaseProvider) { address, ); - return res.send(200); + return res.sendStatus(200); }, ); router.post( @@ -88,7 +88,7 @@ export function profile(web3Provider: ethers.providers.BaseProvider) { return res.status(400).send({ error: 'invalid schema' }); } - const profileIsValid = Lib.account.checkUserProfile( + const profileIsValid = Lib.account.checkUserProfileWithAddress( signedUserProfile, address, ); @@ -109,7 +109,7 @@ export function profile(web3Provider: ethers.providers.BaseProvider) { .send({ error: 'address has already claimed a subdomain' }); } - const name = `${address}.dm3.eth`; + const name = `${address}.dev-addr.dm3.eth`; const profileExists = await req.app.locals.db.getUserProfile(name); @@ -125,7 +125,9 @@ export function profile(web3Provider: ethers.providers.BaseProvider) { address, ); - return res.send(200); + Lib.log(`Registered ${name}`); + + return res.sendStatus(200); }, ); diff --git a/packages/offchain-resolver/src/index.ts b/packages/offchain-resolver/src/index.ts index 65c356e30..0b4faf624 100644 --- a/packages/offchain-resolver/src/index.ts +++ b/packages/offchain-resolver/src/index.ts @@ -9,6 +9,9 @@ import { getWeb3Provider } from './utils/getWeb3Provider'; import { getSigner } from './utils/getSigner'; import { readKeyFromEnv } from './utils/readKeyEnv'; import { profile } from './http/profile'; +import * as dotenv from 'dotenv'; + +dotenv.config(); const app = express(); app.use(express.json({ limit: '50mb' })); @@ -32,3 +35,9 @@ app.use(bodyParser.json()); app.use('/', ccipGateway(signer, resolverAddress)); app.use('/profile', profile(getWeb3Provider())); })(); +const port = process.env.PORT || '8081'; +server.listen(port, () => { + app.locals.logger.log( + '[Server] listening at port ' + port + ' and dir ' + __dirname, + ); +}); diff --git a/packages/offchain-resolver/src/utils/getSigner.ts b/packages/offchain-resolver/src/utils/getSigner.ts index 9d9e6fbc7..a83ea5612 100644 --- a/packages/offchain-resolver/src/utils/getSigner.ts +++ b/packages/offchain-resolver/src/utils/getSigner.ts @@ -1,5 +1,6 @@ -import ethers from 'ethers'; +import { ethers } from 'ethers'; import { readKeyFromEnv } from './readKeyEnv'; + export function getSigner() { const privateKey = readKeyFromEnv('SIGNER_PRIVATE_KEY'); return new ethers.Wallet(privateKey); diff --git a/packages/offchain-resolver/src/utils/getWeb3Provider.ts b/packages/offchain-resolver/src/utils/getWeb3Provider.ts index f784a9250..edde67384 100644 --- a/packages/offchain-resolver/src/utils/getWeb3Provider.ts +++ b/packages/offchain-resolver/src/utils/getWeb3Provider.ts @@ -1,4 +1,4 @@ -import ethers from 'ethers'; +import { ethers } from 'ethers'; import { readKeyFromEnv } from './readKeyEnv'; export function getWeb3Provider(): ethers.providers.BaseProvider { diff --git a/packages/react/src/Dm3.tsx b/packages/react/src/Dm3.tsx index ba167969d..74c3fcd96 100644 --- a/packages/react/src/Dm3.tsx +++ b/packages/react/src/Dm3.tsx @@ -13,7 +13,6 @@ import { UserDbType } from './reducers/UserDB'; import { ConnectionType } from './reducers/Connection'; import { showSignIn } from './sign-in/Phases'; import SignIn from './sign-in/SignIn'; -import { CacheType } from './reducers/Cache'; import { UiStateType } from './reducers/UiState'; import Start from './start/Start'; import 'bootstrap/dist/css/bootstrap.min.css'; @@ -190,14 +189,6 @@ function dm3(props: dm3Props) { }), (contacts: Contact[]) => dispatch({ type: AccountsType.SetContacts, payload: contacts }), - (address: string, name: string) => - dispatch({ - type: CacheType.AddEnsName, - payload: { - address, - name, - }, - }), state.userDb, (id: string) => dispatch({ diff --git a/packages/react/src/chat/Chat.tsx b/packages/react/src/chat/Chat.tsx index c7e827987..ab22988ae 100644 --- a/packages/react/src/chat/Chat.tsx +++ b/packages/react/src/chat/Chat.tsx @@ -47,7 +47,7 @@ function Chat() { await Lib.messaging.getMessages( state.connection, state.auth.currentSession?.token!, - state.accounts.selectedContact.account.address, + state.accounts.selectedContact.account.ensName, state.userDb as Lib.storage.UserDB, (envelops) => envelops.forEach((envelop) => @@ -76,12 +76,9 @@ function Chat() { } const account = - Lib.external.formatAddress( + Lib.account.normalizeEnsName( container.envelop.message.metadata.from, - ) === - Lib.external.formatAddress( - state.accounts.selectedContact.account.address, - ) + ) === state.accounts.selectedContact.account.ensName ? state.accounts.selectedContact.account : state.connection.account!; @@ -89,7 +86,7 @@ function Chat() { ? checkSignature( container.envelop.message, account.profile?.publicSigningKey, - account.address, + account.ensName, container.envelop.message.signature, ) : false; @@ -154,7 +151,7 @@ function Chat() { messageContainers.forEach((container) => { if ( container.envelop.message.metadata.from === - state.connection.account!.address + state.connection.account!.ensName ) { addUserMessage( container.envelop.message.message, @@ -183,7 +180,7 @@ function Chat() { time={container.envelop.message.metadata.timestamp} ownMessage={ container.envelop.message.metadata.from === - state.connection.account!.address + state.connection.account!.ensName } /> ), @@ -204,7 +201,7 @@ function Chat() { if (state.accounts.selectedContact && state.userDb) { handleMessages( Lib.storage.getConversation( - state.accounts.selectedContact.account.address, + state.accounts.selectedContact.account.ensName, state.connection, state.userDb, ), @@ -234,8 +231,8 @@ function Chat() { : true; const messageData = await Lib.messaging.createMessage( - state.accounts.selectedContact.account.address, - state.connection.account!.address, + state.accounts.selectedContact.account.ensName, + state.connection.account!.ensName, message, userDb, ); diff --git a/packages/react/src/chat/ChatHeader.tsx b/packages/react/src/chat/ChatHeader.tsx index a59ccf989..8494eba9c 100644 --- a/packages/react/src/chat/ChatHeader.tsx +++ b/packages/react/src/chat/ChatHeader.tsx @@ -54,8 +54,7 @@ function ChatHeader(props: ChatHeaderProps) { state.accounts.accountInfoView === AccountInfo.Contact ? Lib.account.getAccountDisplayName( - props.account.address, - state.cache.ensNames, + props.account.ensName, ) : 'Account Info'} @@ -138,16 +137,13 @@ function ChatHeader(props: ChatHeaderProps) { } > {Lib.account.getAccountDisplayName( - props.account.address, - state.cache.ensNames, + props.account.ensName, )}
- +
diff --git a/packages/react/src/contacts/AccountNameHeader.tsx b/packages/react/src/contacts/AccountNameHeader.tsx index 58595bc6a..cde898439 100644 --- a/packages/react/src/contacts/AccountNameHeader.tsx +++ b/packages/react/src/contacts/AccountNameHeader.tsx @@ -50,16 +50,13 @@ function AccountNameHeader(props: AccountNameHeaderProps) { }} > {Lib.account.getAccountDisplayName( - props.account.address, - state.cache.ensNames, + props.account.ensName, )}
- +
diff --git a/packages/react/src/contacts/ContactList.tsx b/packages/react/src/contacts/ContactList.tsx index b870dc91b..f5521ce28 100644 --- a/packages/react/src/contacts/ContactList.tsx +++ b/packages/react/src/contacts/ContactList.tsx @@ -10,7 +10,7 @@ function ContactList() { const contactsList = state.accounts.contacts ? state.accounts.contacts.map((contact) => ( diff --git a/packages/react/src/contacts/ContractListEntry.tsx b/packages/react/src/contacts/ContractListEntry.tsx index fa7dda0ca..fcfeeeeeb 100644 --- a/packages/react/src/contacts/ContractListEntry.tsx +++ b/packages/react/src/contacts/ContractListEntry.tsx @@ -18,7 +18,7 @@ function ContactListEntry(props: ContactListProps) { const { state, dispatch } = useContext(GlobalContext); useEffect(() => { const messages = Lib.storage.getConversation( - props.contact.account.address, + props.contact.account.ensName, state.connection, state.userDb as Lib.storage.UserDB, ); @@ -44,9 +44,9 @@ function ContactListEntry(props: ContactListProps) { const selected = state.accounts.selectedContact && - Lib.external.formatAddress(props.contact.account.address) === - Lib.external.formatAddress( - state.accounts.selectedContact?.account.address, + Lib.account.normalizeEnsName(props.contact.account.ensName) === + Lib.account.normalizeEnsName( + state.accounts.selectedContact?.account.ensName, ); return ( @@ -54,7 +54,7 @@ function ContactListEntry(props: ContactListProps) { className={`list-group-item list-group-item-action contact-entry d-flex justify-content-between ${ selected ? 'contract-entry-selected' : '' }`} - key={props.contact.account.address} + key={props.contact.account.ensName} onClick={() => { dispatch({ type: AccountsType.SetSelectedContact, @@ -68,7 +68,7 @@ function ContactListEntry(props: ContactListProps) { >
- +
@@ -76,8 +76,7 @@ function ContactListEntry(props: ContactListProps) {
{Lib.account.getAccountDisplayName( - props.contact.account.address, - state.cache.ensNames, + props.contact.account.ensName, )}
diff --git a/packages/react/src/reducers/Accounts.ts b/packages/react/src/reducers/Accounts.ts index fcfafb9af..f561c0a83 100644 --- a/packages/react/src/reducers/Accounts.ts +++ b/packages/react/src/reducers/Accounts.ts @@ -19,16 +19,27 @@ export type AccountsActions = export function accountsReducer(state: Accounts, action: AccountsActions) { switch (action.type) { case AccountsType.SetSelectedContact: - if (state.selectedContact === action.payload?.account.address) { + if ( + state.selectedContact === action.payload?.account.ensName || + !action.payload + ) { return state; } else { Lib.log( - `[Accounts] Set selected account to ${action.payload?.account.address}`, + `[Accounts] Set selected account to ${action.payload?.account.ensName}`, ); return { ...state, - selectedContact: action.payload, + selectedContact: { + ...action.payload, + account: { + ...action.payload.account, + ensName: Lib.account.normalizeEnsName( + action.payload.account.ensName, + ), + }, + }, }; } diff --git a/packages/react/src/reducers/Auth.ts b/packages/react/src/reducers/Auth.ts index 1e4712910..cbac31d29 100644 --- a/packages/react/src/reducers/Auth.ts +++ b/packages/react/src/reducers/Auth.ts @@ -8,7 +8,7 @@ export interface AuthState { export interface AuthSession { storage: string; token: string; - address: string; + ensName: string; storageEncryptionKey?: string; } @@ -31,13 +31,13 @@ export function authReducer( case AuthStateType.AddNewSession: const allSessions = { ...state.allSessions, - [payload.address]: payload, + [payload.ensName]: payload, }; return { ...state, currentSession: payload, allSessions, - recentlyUsedSession: payload.address, + recentlyUsedSession: payload.ensName, }; default: diff --git a/packages/react/src/reducers/Cache.ts b/packages/react/src/reducers/Cache.ts index e6defa497..a0dc6bb9b 100644 --- a/packages/react/src/reducers/Cache.ts +++ b/packages/react/src/reducers/Cache.ts @@ -2,7 +2,6 @@ import * as Lib from 'dm3-lib'; import { ActionMap, GlobalState } from './shared'; export interface Cache { - ensNames: Map; abis: Map; avatarUrls: Map; } @@ -14,9 +13,8 @@ export enum CacheType { } export type CachePayload = { - [CacheType.AddEnsName]: { address: string; name: string }; - [CacheType.AddAbis]: { address: string; abi: string }[]; - [CacheType.AddAvatarUrl]: { address: string; url: string }; + [CacheType.AddAbis]: { ensName: string; abi: string }[]; + [CacheType.AddAvatarUrl]: { ensName: string; url: string }; }; export type CacheActions = @@ -24,32 +22,17 @@ export type CacheActions = export function cacheReducer(state: Cache, action: CacheActions): Cache { switch (action.type) { - case CacheType.AddEnsName: - if (state.ensNames.has(action.payload.address)) { - return state; - } - - Lib.log( - `[Cache] New ens name ${action.payload.name} for ${action.payload.address}`, - ); - const ensNames = new Map(state.ensNames); - ensNames.set(action.payload.address, action.payload.name); - return { - ...state, - ensNames, - }; - case CacheType.AddAvatarUrl: - if (state.avatarUrls.has(action.payload.address)) { + if (state.avatarUrls.has(action.payload.ensName)) { return state; } Lib.log( - `[Cache] Add avatar url ${action.payload.url} for ${action.payload.address}`, + `[Cache] Add avatar url ${action.payload.url} for ${action.payload.ensName}`, ); const avatarUrls = new Map(state.avatarUrls); - avatarUrls.set(action.payload.address, action.payload.url); + avatarUrls.set(action.payload.ensName, action.payload.url); return { ...state, avatarUrls, @@ -60,7 +43,7 @@ export function cacheReducer(state: Cache, action: CacheActions): Cache { action.payload.forEach((abiContainer) => { const address = Lib.external.formatAddress( - abiContainer.address, + abiContainer.ensName, ); if (state.abis.has(address)) { Lib.log(`[Cache] ABI for ${address} already in cache`); diff --git a/packages/react/src/reducers/Connection.ts b/packages/react/src/reducers/Connection.ts index 7021ba98a..6d4a24274 100644 --- a/packages/react/src/reducers/Connection.ts +++ b/packages/react/src/reducers/Connection.ts @@ -54,7 +54,7 @@ export function connectionReducer( }; case ConnectionType.ChangeAccount: - Lib.log(`[Connection] Set account ${action.payload.address}`); + Lib.log(`[Connection] Set account ${action.payload.ensName}`); return { ...state, account: action.payload, diff --git a/packages/react/src/reducers/UserDB.ts b/packages/react/src/reducers/UserDB.ts index 74b18bf59..994b2abd1 100644 --- a/packages/react/src/reducers/UserDB.ts +++ b/packages/react/src/reducers/UserDB.ts @@ -43,17 +43,17 @@ export function userDbReducer( let hasChanged = false; - const contactAddress = + const contactEnsName = container.envelop.message.metadata.from === - connection.account!.address + connection.account!.ensName ? container.envelop.message.metadata.to : container.envelop.message.metadata.from; const conversationId = Lib.storage.getConversationId( - contactAddress, - connection.account!.address, + contactEnsName, + connection.account!.ensName, ); const prevContainers = Lib.storage.getConversation( - contactAddress, + contactEnsName, connection, state, ); diff --git a/packages/react/src/reducers/shared.ts b/packages/react/src/reducers/shared.ts index 39a01a99a..0c7e5598f 100644 --- a/packages/react/src/reducers/shared.ts +++ b/packages/react/src/reducers/shared.ts @@ -52,7 +52,6 @@ export const initialState: GlobalState = { accountInfoView: AccountInfo.None, }, cache: { - ensNames: new Map(), abis: new Map(), avatarUrls: new Map(), }, diff --git a/packages/react/src/sign-in/Connectors.ts b/packages/react/src/sign-in/Connectors.ts index 4f763411e..2ff93aa92 100644 --- a/packages/react/src/sign-in/Connectors.ts +++ b/packages/react/src/sign-in/Connectors.ts @@ -101,24 +101,10 @@ export async function connectAccount( dispatch({ type: ConnectionType.ChangeAccount, payload: { - address: accountConnection.account, + ensName: accountConnection.account, profile: accountConnection.profile?.profile, }, }); - - const ensName = await Lib.external.lookupAddress( - state.connection.provider as ethers.providers.JsonRpcProvider, - accountConnection.account, - ); - if (ensName) { - dispatch({ - type: CacheType.AddEnsName, - payload: { - address: accountConnection.account, - name: ensName, - }, - }); - } } else { dispatch({ type: ConnectionType.ChangeConnectionState, @@ -141,29 +127,25 @@ export async function signIn( // Get the users DB. Based wether the profile already exits the db // will either be created by decrypting the exisitng storge file // or by by creating a enitre new profile - const { db, connectionState, deliveryServiceToken } = await getDatabase( - state.uiState.proflieExists, - storageLocation, - storageToken, - state, - dispatch, - ); + const { db, connectionState, deliveryServiceToken, account } = + await getDatabase( + state.uiState.proflieExists, + storageLocation, + storageToken, + state, + dispatch, + ); - const address = state.connection.account!.address; + const ensName = state.connection.account!.ensName; //Fetching the profile from the Delivery Service const profile = ( await Lib.account.getUserProfile( state.connection, - address, - state.connection.defaultServiceUrl + '/profile/' + address, + ensName, + state.connection.defaultServiceUrl + '/profile/' + ensName, ) )?.profile; - const account: Lib.account.Account = { - address: state.connection.account!.address, - profile, - }; - dispatch({ type: ConnectionType.ChangeAccount, payload: account, @@ -186,7 +168,7 @@ export async function signIn( type: AuthStateType.AddNewSession, payload: { token: deliveryServiceToken, - address: account.address, + ensName: account.ensName, storage: storageLocation, }, }); diff --git a/packages/react/src/sign-in/SignIn.tsx b/packages/react/src/sign-in/SignIn.tsx index 55d5fa4d3..93e57df27 100644 --- a/packages/react/src/sign-in/SignIn.tsx +++ b/packages/react/src/sign-in/SignIn.tsx @@ -70,7 +70,7 @@ function SignIn(props: SignInProps) { state.connection.account && state.uiState.browserStorageBackup ? await localforage.getItem( Lib.account.getBrowserStorageKey( - state.connection.account.address, + state.connection.account.ensName, ), ) : null; diff --git a/packages/react/src/sign-in/getDatabase.ts b/packages/react/src/sign-in/getDatabase.ts index aa3a6df68..70b70bedc 100644 --- a/packages/react/src/sign-in/getDatabase.ts +++ b/packages/react/src/sign-in/getDatabase.ts @@ -3,6 +3,7 @@ import { ConnectionType } from '../reducers/Connection'; import { GlobalState } from '../reducers/shared'; import { getStorageFile } from './getStorageFile'; import * as Lib from 'dm3-lib'; +import { AccountsType } from '../reducers/Accounts'; export async function getDatabase( profileExists: boolean, @@ -11,9 +12,10 @@ export async function getDatabase( state: GlobalState, dispatch: React.Dispatch, ) { - return profileExists - ? getExistingDatebase(storageLocation, storageToken, state, dispatch) - : createNewDatabase(state); + // return profileExists + // ? getExistingDatebase(storageLocation, storageToken, state, dispatch) + // : createNewDatabase(state); + return createNewDatabase(state); } async function getExistingDatebase( @@ -53,6 +55,13 @@ async function getExistingDatebase( return { deliveryServiceToken, db, connectionState }; } -async function createNewDatabase(state: GlobalState) { - return await Lib.session.signIn(state.connection); +async function createNewDatabase(state: GlobalState): Promise<{ + connectionState: Lib.web3provider.ConnectionState; + db: Lib.storage.UserDB; + deliveryServiceToken: string; + account: Lib.account.Account; +}> { + const signInData = await Lib.session.signIn(state.connection); + + return signInData; } diff --git a/packages/react/src/storage/StorageView.tsx b/packages/react/src/storage/StorageView.tsx index 4a653a19c..9b873e582 100644 --- a/packages/react/src/storage/StorageView.tsx +++ b/packages/react/src/storage/StorageView.tsx @@ -68,8 +68,7 @@ function StorageView() { const a = document.createElement('a'); a.download = `${Lib.account.getAccountDisplayName( - state.connection.account!.address, - state.cache.ensNames, + state.connection.account!.ensName, true, )}-${Date.now()}.json`; a.href = window.URL.createObjectURL(blob); @@ -152,7 +151,7 @@ function StorageView() { const setBroserStorage = async () => { localforage.setItem( Lib.account.getBrowserStorageKey( - state.connection.account!.address, + state.connection.account!.ensName, ), ( await Lib.storage.sync( diff --git a/packages/react/src/ui-shared/Avatar.tsx b/packages/react/src/ui-shared/Avatar.tsx index 4a64f5527..78e1c9c22 100644 --- a/packages/react/src/ui-shared/Avatar.tsx +++ b/packages/react/src/ui-shared/Avatar.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useState } from 'react'; import { GlobalContext } from '../GlobalContextProvider'; import makeBlockie from 'ethereum-blockies-base64'; import './Avatar.css'; @@ -13,7 +13,7 @@ export enum SpecialSize { } interface AvatarProps { - accountAddress: string; + ensName: string; specialSize?: SpecialSize; } @@ -22,28 +22,24 @@ function Avatar(props: AvatarProps) { const { state, dispatch } = useContext(GlobalContext); const getAvatar = async () => { - const address = Lib.external.formatAddress(props.accountAddress); + const ensName = Lib.account.normalizeEnsName(props.ensName); - let url = state.cache.avatarUrls.get(address); + let url = state.cache.avatarUrls.get(ensName); if (url) { return url; } - const ensName = state.cache.ensNames.get(address); + const urlResponse = await state.connection.provider!.getAvatar( + props.ensName, + ); - if (ensName) { - const urlResponse = await state.connection.provider!.getAvatar( - ensName!, - ); - - if (urlResponse) { - dispatch({ - type: CacheType.AddAvatarUrl, - payload: { address, url: urlResponse }, - }); - return url; - } + if (urlResponse) { + dispatch({ + type: CacheType.AddAvatarUrl, + payload: { ensName, url: urlResponse }, + }); + return url; } return undefined; @@ -54,10 +50,10 @@ function Avatar(props: AvatarProps) { (avatarUrl: unknown) => { setAvatar(avatarUrl as string | undefined); }, - [props.accountAddress, state.cache.ensNames, state.cache.avatarUrls], + [props.ensName, state.cache.avatarUrls], ); - if (!props.accountAddress) { + if (!props.ensName) { return null; } @@ -97,7 +93,7 @@ function Avatar(props: AvatarProps) { Avatar ); diff --git a/packages/react/src/ui-shared/RequestContacts.ts b/packages/react/src/ui-shared/RequestContacts.ts index 8aff9a383..cc4d82bb9 100644 --- a/packages/react/src/ui-shared/RequestContacts.ts +++ b/packages/react/src/ui-shared/RequestContacts.ts @@ -1,6 +1,5 @@ import axios from 'axios'; import * as Lib from 'dm3-lib'; -import { ethers } from 'ethers'; import { Contact } from '../reducers/shared'; function fetchDeliveryServiceProfile(connection: Lib.Connection) { @@ -36,11 +35,10 @@ export async function requestContacts( selectedContact: Contact | undefined, setSelectedContact: (contact: Contact | undefined) => void, setContacts: (constacts: Contact[]) => void, - addEnsName: (address: string, name: string) => void, userDb: Lib.storage.UserDB, createEmptyConversationEntry: (id: string) => void, storeMessages: (envelops: Lib.storage.StorageEnvelopContainer[]) => void, - defaultContact?: string, + defaultContactEnsName?: string, ) { let retrievedContacts = await Lib.account.getContacts( connection, @@ -50,16 +48,16 @@ export async function requestContacts( ); if ( - defaultContact && + defaultContactEnsName && !retrievedContacts.find( (accounts) => - Lib.external.formatAddress(accounts.address) === - Lib.external.formatAddress(defaultContact), + Lib.account.normalizeEnsName(accounts.ensName) === + Lib.account.normalizeEnsName(defaultContactEnsName), ) ) { await Lib.account.addContact( connection, - defaultContact, + defaultContactEnsName, userDb, createEmptyConversationEntry, ); @@ -82,50 +80,33 @@ export async function requestContacts( !selectedContact?.account.profile?.publicEncryptionKey && retrievedContacts.find( (contact: Lib.account.Account) => - Lib.external.formatAddress(contact.address) === - Lib.external.formatAddress(selectedContact.account.address), + Lib.account.normalizeEnsName(contact.ensName) === + Lib.account.normalizeEnsName(selectedContact.account.ensName), )?.profile?.publicSigningKey ) { setSelectedContact( contacts.find( (contact) => - Lib.external.formatAddress(contact.account.address) === - Lib.external.formatAddress(selectedContact.account.address), + Lib.account.normalizeEnsName(contact.account.ensName) === + Lib.account.normalizeEnsName( + selectedContact.account.ensName, + ), ), ); - } else if (!selectedContact && defaultContact) { + } else if (!selectedContact && defaultContactEnsName) { const contactToSelect = contacts.find( (accounts) => - Lib.external.formatAddress(accounts.account.address) === - Lib.external.formatAddress(defaultContact), + Lib.account.normalizeEnsName(accounts.account.ensName) === + Lib.account.normalizeEnsName(defaultContactEnsName), ); setSelectedContact(contactToSelect); } - ( - await Promise.all( - retrievedContacts.map(async (contact: Lib.account.Account) => ({ - address: contact.address, - ens: await Lib.external.lookupAddress( - connection.provider as ethers.providers.JsonRpcProvider, - contact.address, - ), - })), - ) - ) - .filter( - (lookup: { address: string; ens: string | null }) => - lookup.ens !== null, - ) - .forEach((lookup: { address: string; ens: string | null }) => - addEnsName(lookup.address, lookup.ens as string), - ); - contacts.forEach((contact) => { if (contact.deliveryServiceProfile) { Lib.storage - .getConversation(contact.account.address, connection, userDb) + .getConversation(contact.account.ensName, connection, userDb) .filter( (message) => message.messageState === diff --git a/packages/react/src/user-info/UserInfo.tsx b/packages/react/src/user-info/UserInfo.tsx index 995f8c55e..9e8a4f747 100644 --- a/packages/react/src/user-info/UserInfo.tsx +++ b/packages/react/src/user-info/UserInfo.tsx @@ -55,18 +55,15 @@ function UserInfo(props: UserInfoProps) { getDeliveryServiceUrl(); }, [state.connection.account?.profile]); - const ensName = state.cache.ensNames.get(props.account.address); - const getTextRecords = async (): Promise => { if ( (state.accounts.accountInfoView === AccountInfo.Account || state.accounts.accountInfoView === AccountInfo.Contact) && - ensName && state.connection.provider ) { return Lib.external.getDefaultEnsTextRecord( state.connection.provider, - ensName, + props.account.ensName, ); } else { return; @@ -92,7 +89,7 @@ function UserInfo(props: UserInfoProps) { state.connection, state.connection.defaultServiceUrl + '/profile/' + - state.connection.account!.address, + state.connection.account!.ensName, ); if (tx) { @@ -113,7 +110,7 @@ function UserInfo(props: UserInfoProps) {
@@ -135,11 +132,11 @@ function UserInfo(props: UserInfoProps) { className="text-decoration-none " href={ 'https://etherscan.io/address/' + - props.account.address + props.account.ensName } target="_blank" > - {props.account.address} + {props.account.ensName}
@@ -265,23 +262,23 @@ function UserInfo(props: UserInfoProps) { href={ deliveryServiceUrl + '/profile/' + - props.account.address + props.account.ensName } target="_blank" > {deliveryServiceUrl + '/profile/' + - props.account.address} + props.account.ensName} )} - {ensName && ( + {props.account.ensName && (