Skip to content

Commit

Permalink
fix(core): perform byte array <-> b64 conversions w/ native JS (#153)
Browse files Browse the repository at this point in the history
* chore(core): revert noUncheckedIndexedAccess to default

This tsconfig setting adds `| undefined` whenever you access undeclared properties of an object/array. It defaults to false.

Overriding the default and setting this to true forces the use of non-null assertion operators in places where I've already confirmed a property exists. The extra protection it adds isn't worth the trade-off.

See https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess

* fix(core): perform byte array <-> base64 conversions w/ web APIs

* chore(core): remove buffer NPM package
  • Loading branch information
drichar authored Apr 9, 2024
1 parent 1164a51 commit 054947c
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 38 deletions.
3 changes: 1 addition & 2 deletions packages/use-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@
"dependencies": {
"@tanstack/store": "^0.3.1",
"@walletconnect/utils": "^2.10.2",
"algosdk": "^2.7.0",
"buffer": "^6.0.3"
"algosdk": "^2.7.0"
},
"devDependencies": {
"@blockshake/defly-connect": "^1.1.6",
Expand Down
28 changes: 26 additions & 2 deletions packages/use-wallet/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ export function compareAccounts(accounts: WalletAccount[], compareTo: WalletAcco
return true
}

export function base64ToByteArray(blob: string): Uint8Array {
return stringToByteArray(atob(blob))
}

export function byteArrayToBase64(array: Uint8Array): string {
return btoa(byteArrayToString(array))
}

export function stringToByteArray(str: string): Uint8Array {
const array = new Uint8Array(str.length)
for (let i = 0; i < str.length; i++) {
array[i] = str.charCodeAt(i)
}
return array
}

export function byteArrayToString(array: Uint8Array): string {
let result = ''
for (let i = 0; i < array.length; i++) {
result += String.fromCharCode(array[i])
}
return result
}

export function isTransaction(
item: algosdk.Transaction | algosdk.Transaction[] | Uint8Array | Uint8Array[]
): item is algosdk.Transaction | algosdk.Transaction[] {
Expand Down Expand Up @@ -122,7 +146,7 @@ export function mergeSignedTxnsWithGroup(
const signedByUser = signedTxns.shift()
signedByUser && acc.push(signedByUser)
} else if (returnGroup) {
acc.push(txnGroup[i]!)
acc.push(txnGroup[i])
}
return acc
}, [])
Expand Down Expand Up @@ -184,7 +208,7 @@ export function generateUuid(): string {

return (
valueAsNumber ^
(window.crypto.getRandomValues(new Uint8Array(1))[0]! & (15 >> (valueAsNumber / 4)))
(window.crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (valueAsNumber / 4)))
).toString(16)
})
}
4 changes: 2 additions & 2 deletions packages/use-wallet/src/wallets/defly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class DeflyWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -163,7 +163,7 @@ export class DeflyWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)
Expand Down
14 changes: 8 additions & 6 deletions packages/use-wallet/src/wallets/exodus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import algosdk from 'algosdk'
import { addWallet, type State } from 'src/store'
import {
base64ToByteArray,
byteArrayToBase64,
isSignedTxnObject,
mergeSignedTxnsWithGroup,
normalizeTxnGroup,
Expand Down Expand Up @@ -94,7 +96,7 @@ export class ExodusWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -165,12 +167,12 @@ export class ExodusWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)

const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (shouldSign) {
txnsToSign.push({ txn: txnBase64 })
Expand All @@ -187,7 +189,7 @@ export class ExodusWallet extends BaseWallet {
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

// Convert base64 signed transactions to msgpack
const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))

// Merge signed transactions back into original group
const txnGroupSigned = mergeSignedTxnsWithGroup(
Expand All @@ -209,7 +211,7 @@ export class ExodusWallet extends BaseWallet {
}

const txnsToSign = txnGroup.reduce<WalletTransaction[]>((acc, txn, idx) => {
const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (indexesToSign.includes(idx)) {
acc.push({ txn: txnBase64 })
Expand All @@ -222,7 +224,7 @@ export class ExodusWallet extends BaseWallet {
const signTxnsResult = await this.client.signTxns(txnsToSign)
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))
return signedTxns
}
}
14 changes: 8 additions & 6 deletions packages/use-wallet/src/wallets/kibisis.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import algosdk from 'algosdk'
import { addWallet, type State } from 'src/store'
import {
base64ToByteArray,
byteArrayToBase64,
generateUuid,
isSignedTxnObject,
mergeSignedTxnsWithGroup,
Expand Down Expand Up @@ -400,7 +402,7 @@ export class KibisisWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -449,12 +451,12 @@ export class KibisisWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)

const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (shouldSign) {
txnsToSign.push({ txn: txnBase64 })
Expand All @@ -471,7 +473,7 @@ export class KibisisWallet extends BaseWallet {
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

// Convert base64 signed transactions to msgpack
const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))

// Merge signed transactions back into original group
const txnGroupSigned = mergeSignedTxnsWithGroup(
Expand All @@ -497,7 +499,7 @@ export class KibisisWallet extends BaseWallet {
): Promise<Uint8Array[]> => {
try {
const txnsToSign = txnGroup.reduce<Arc0001SignTxns[]>((acc, txn, idx) => {
const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (indexesToSign.includes(idx)) {
acc.push({ txn: txnBase64 })
Expand All @@ -510,7 +512,7 @@ export class KibisisWallet extends BaseWallet {
const signTxnsResult = await this.signTxns(txnsToSign)
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))
return signedTxns
} catch (error: any) {
console.error(
Expand Down
4 changes: 2 additions & 2 deletions packages/use-wallet/src/wallets/kmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class KmdWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -186,7 +186,7 @@ export class KmdWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)
Expand Down
9 changes: 5 additions & 4 deletions packages/use-wallet/src/wallets/lute.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import algosdk from 'algosdk'
import { addWallet, type State } from 'src/store'
import {
byteArrayToBase64,
isSignedTxnObject,
mergeSignedTxnsWithGroup,
normalizeTxnGroup,
Expand Down Expand Up @@ -87,7 +88,7 @@ export class LuteWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -151,12 +152,12 @@ export class LuteWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)

const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (shouldSign) {
txnsToSign.push({ txn: txnBase64 })
Expand Down Expand Up @@ -200,7 +201,7 @@ export class LuteWallet extends BaseWallet {
}

const txnsToSign = txnGroup.reduce<WalletTransaction[]>((acc, txn, idx) => {
const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (indexesToSign.includes(idx)) {
acc.push({ txn: txnBase64 })
Expand Down
2 changes: 1 addition & 1 deletion packages/use-wallet/src/wallets/mnemonic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class MnemonicWallet extends BaseWallet {
const signedTxn = txn.signTxn(this.account!.sk)
txnGroupSigned.push(signedTxn)
} else if (returnGroup) {
txnGroupSigned.push(msgpackTxnGroup[idx]!)
txnGroupSigned.push(msgpackTxnGroup[idx])
}
})

Expand Down
4 changes: 2 additions & 2 deletions packages/use-wallet/src/wallets/pera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class PeraWallet extends BaseWallet {
address
}))

const activeAccount = walletAccounts[0]!
const activeAccount = walletAccounts[0]

addWallet(this.store, {
walletId: this.id,
Expand Down Expand Up @@ -164,7 +164,7 @@ export class PeraWallet extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)
Expand Down
16 changes: 9 additions & 7 deletions packages/use-wallet/src/wallets/walletconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import algosdk from 'algosdk'
import { NetworkId, caipChainId } from 'src/network'
import { addWallet, setAccounts, type State } from 'src/store'
import {
base64ToByteArray,
byteArrayToBase64,
compareAccounts,
formatJsonRpcRequest,
isSignedTxnObject,
Expand Down Expand Up @@ -148,7 +150,7 @@ export class WalletConnect extends BaseWallet {
walletId: this.id,
wallet: {
accounts: walletAccounts,
activeAccount: walletAccounts[0]!
activeAccount: walletAccounts[0]
}
})
} else {
Expand Down Expand Up @@ -232,7 +234,7 @@ export class WalletConnect extends BaseWallet {

if (client.session.length) {
const lastKeyIndex = client.session.keys.length - 1
const restoredSession = client.session.get(client.session.keys[lastKeyIndex]!)
const restoredSession = client.session.get(client.session.keys[lastKeyIndex])
this.onSessionConnected(restoredSession)
}
} catch (error: any) {
Expand Down Expand Up @@ -271,12 +273,12 @@ export class WalletConnect extends BaseWallet {
const isSigned = isSignedTxnObject(txnObject)
const shouldSign = shouldSignTxnObject(txnObject, this.addresses, indexesToSign, idx)

const txnBuffer: Uint8Array = msgpackTxnGroup[idx]!
const txnBuffer: Uint8Array = msgpackTxnGroup[idx]
const txn: algosdk.Transaction = isSigned
? algosdk.decodeSignedTransaction(txnBuffer).txn
: algosdk.decodeUnsignedTransaction(txnBuffer)

const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (shouldSign) {
txnsToSign.push({ txn: txnBase64 })
Expand All @@ -300,7 +302,7 @@ export class WalletConnect extends BaseWallet {
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

// Convert base64 signed transactions to msgpack
const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))

// Merge signed transactions back into original group
const txnGroupSigned = mergeSignedTxnsWithGroup(
Expand Down Expand Up @@ -328,7 +330,7 @@ export class WalletConnect extends BaseWallet {
}

const txnsToSign = txnGroup.reduce<WalletTransaction[]>((acc, txn, idx) => {
const txnBase64 = Buffer.from(txn.toByte()).toString('base64')
const txnBase64 = byteArrayToBase64(txn.toByte())

if (indexesToSign.includes(idx)) {
acc.push({ txn: txnBase64 })
Expand All @@ -352,7 +354,7 @@ export class WalletConnect extends BaseWallet {
const signedTxnsBase64 = signTxnsResult.filter(Boolean) as string[]

// Convert base64 signed transactions to msgpack
const signedTxns = signedTxnsBase64.map((txn) => new Uint8Array(Buffer.from(txn, 'base64')))
const signedTxns = signedTxnsBase64.map((txn) => base64ToByteArray(txn))

return signedTxns
}
Expand Down
1 change: 0 additions & 1 deletion packages/use-wallet/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"moduleResolution": "node",
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"noEmit": true,
Expand Down
3 changes: 0 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 054947c

Please sign in to comment.