Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added support for handling lowercase addresses #233

Merged
merged 2 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions example/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -869,3 +869,26 @@ test('returns keyMaterial for conversations', async () => {

return true
})

test('correctly handles lowercase addresses', async () => {
const bob = await Client.createRandom({ env: 'local' })
await delayToPropogate()
const alice = await Client.createRandom({ env: 'local' })
await delayToPropogate()
if (bob.address === alice.address) {
throw new Error('bob and alice should be different')
}

const bobConversation = await bob.conversations.newConversation(
alice.address.toLocaleLowerCase()
)
await delayToPropogate()
if (!bobConversation) {
throw new Error('bobConversation should exist')
}
const aliceConversation = (await alice.conversations.list())[0]
if (!aliceConversation) {
throw new Error('aliceConversation should exist')
}
return true
})
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
"dependencies": {
"@ethersproject/bytes": "^5.7.0",
"@msgpack/msgpack": "^3.0.0-beta2",
"@noble/hashes": "^1.3.3",
"@xmtp/proto": "^3.25.0",
"buffer": "^6.0.3"
"buffer": "^6.0.3",
"text-encoding": "^0.7.0"
},
"devDependencies": {
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
Expand Down
11 changes: 8 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { Conversation } from './lib/Conversation'
import { DecodedMessage } from './lib/DecodedMessage'
import type { Query } from './lib/Query'
import { getAddress } from './utils/address'

export { ReactionCodec } from './lib/NativeCodecs/ReactionCodec'
export { ReplyCodec } from './lib/NativeCodecs/ReplyCodec'
Expand Down Expand Up @@ -105,15 +106,19 @@ export async function canMessage(
clientAddress: string,
peerAddress: string
): Promise<boolean> {
return await XMTPModule.canMessage(clientAddress, peerAddress)
return await XMTPModule.canMessage(clientAddress, getAddress(peerAddress))
}

export async function staticCanMessage(
peerAddress: string,
environment: 'local' | 'dev' | 'production',
appVersion?: string | undefined
): Promise<boolean> {
return await XMTPModule.staticCanMessage(peerAddress, environment, appVersion)
return await XMTPModule.staticCanMessage(
getAddress(peerAddress),
environment,
appVersion
)
}

export async function encryptAttachment(
Expand Down Expand Up @@ -212,7 +217,7 @@ export async function createConversation<ContentTypes>(
JSON.parse(
await XMTPModule.createConversation(
client.address,
peerAddress,
getAddress(peerAddress),
JSON.stringify(context || {})
)
)
Expand Down
4 changes: 3 additions & 1 deletion src/lib/Conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Conversation } from './Conversation'
import { DecodedMessage } from './DecodedMessage'
import { ConversationContext } from '../XMTP.types'
import * as XMTPModule from '../index'
import { getAddress } from '../utils/address'

export default class Conversations<ContentTypes> {
client: Client<ContentTypes>
Expand Down Expand Up @@ -50,9 +51,10 @@ export default class Conversations<ContentTypes> {
peerAddress: string,
context?: ConversationContext
): Promise<Conversation<ContentTypes>> {
const checksumAddress = getAddress(peerAddress)
return await XMTPModule.createConversation(
this.client,
peerAddress,
checksumAddress,
context
)
}
Expand Down
50 changes: 50 additions & 0 deletions src/utils/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { keccak_256 } from '@noble/hashes/sha3'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Port from viem getAddress

import { TextEncoder } from 'text-encoding'

const addressRegex = /^0x[a-fA-F0-9]{40}$/
const encoder = new TextEncoder()

export function stringToBytes(value: string): Uint8Array {
const bytes = encoder.encode(value)
return bytes
}

export function keccak256(value: Uint8Array): Uint8Array {
const bytes = keccak_256(value)
return bytes
}

export function isAddress(address: string): boolean {
return addressRegex.test(address)
}

export function checksumAddress(address_: string, chainId?: number): string {
const hexAddress = chainId
? `${chainId}${address_.toLowerCase()}`
: address_.substring(2).toLowerCase()
const hash = keccak256(stringToBytes(hexAddress))

const address = (
chainId ? hexAddress.substring(`${chainId}0x`.length) : hexAddress
).split('')
for (let i = 0; i < 40; i += 2) {
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
address[i] = address[i].toUpperCase()
}
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
address[i + 1] = address[i + 1].toUpperCase()
}
}

return `0x${address.join('')}`
}

const addressCache = new Map<string, string>()

export function getAddress(address: string, chainId?: number): string {
if (addressCache.has(address)) return addressCache.get(address) as string
if (!isAddress(address)) throw new Error('Invalid address' + address)
const checksumedAddress = checksumAddress(address, chainId)
addressCache.set(address, checksumedAddress)
return checksumedAddress
}
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,7 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==

"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2":
"@noble/hashes@^1.3.3", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
Expand Down Expand Up @@ -7958,6 +7958,11 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"

text-encoding@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.7.0.tgz#f895e836e45990624086601798ea98e8f36ee643"
integrity sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==

text-extensions@^2.0.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-2.4.0.tgz#a1cfcc50cf34da41bfd047cc744f804d1680ea34"
Expand Down
Loading