mango-v4-ui/utils/notifications.ts

231 lines
6.1 KiB
TypeScript
Raw Normal View History

2022-11-24 18:39:14 -08:00
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
2022-09-12 08:53:57 -07:00
import mangoStore from '@store/mangoStore'
2022-11-23 04:40:38 -08:00
import { Howl } from 'howler'
import { NOTIFICATION_API, SOUND_SETTINGS_KEY } from './constants'
import { Payload, SIWS } from '@web3auth/sign-in-with-solana'
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'
import { WalletContextState } from '@solana/wallet-adapter-react'
import {
PublicKey,
Connection,
Transaction,
TransactionInstruction,
} from '@solana/web3.js'
import { IDL } from '@blockworks-foundation/mango-v4'
2022-07-05 20:37:49 -07:00
export type TransactionNotification = {
2022-07-05 20:37:49 -07:00
type: 'success' | 'info' | 'error' | 'confirm'
title: string
description?: null | string
txid?: string
show: boolean
id: number
}
2022-12-07 21:05:36 -08:00
const successSound = new Howl({
src: ['/sounds/transaction-success.mp3'],
volume: 0.5,
})
const failSound = new Howl({
src: ['/sounds/transaction-fail.mp3'],
volume: 0.5,
})
2022-11-23 04:40:38 -08:00
2022-07-05 20:37:49 -07:00
export function notify(newNotification: {
type?: 'success' | 'info' | 'error' | 'confirm'
title: string
description?: string
txid?: string
2022-11-23 04:40:38 -08:00
noSound?: boolean
2024-02-08 11:33:49 -08:00
noMangoIdlEnrichment?: boolean
2022-07-05 20:37:49 -07:00
}) {
const setMangoStore = mangoStore.getState().set
const notifications = mangoStore.getState().transactionNotifications
const lastId = mangoStore.getState().transactionNotificationIdCounter
2022-07-05 20:37:49 -07:00
const newId = lastId + 1
2022-11-23 04:40:38 -08:00
const savedSoundSettings = localStorage.getItem(SOUND_SETTINGS_KEY)
const soundSettings = savedSoundSettings
? JSON.parse(savedSoundSettings)
: INITIAL_SOUND_SETTINGS
if (newNotification.type && !newNotification.noSound) {
switch (newNotification.type) {
case 'success': {
if (soundSettings['transaction-success']) {
successSound.play()
}
break
}
case 'error': {
if (soundSettings['transaction-fail']) {
failSound.play()
}
}
}
}
2022-07-05 20:37:49 -07:00
const newNotif: TransactionNotification = {
2022-07-05 20:37:49 -07:00
id: newId,
type: 'success',
show: true,
description: null,
...newNotification,
}
2024-02-08 11:33:49 -08:00
const parsedNotif = !newNotification.noMangoIdlEnrichment
? enrichError(newNotif)
: newNotif
2023-12-05 18:53:52 -08:00
if (
!parsedNotif.txid ||
2023-12-05 18:53:52 -08:00
!notifications.find(
(n) => n.txid == parsedNotif.txid && n.type == parsedNotif.type,
2023-12-05 18:53:52 -08:00
)
) {
setMangoStore((state) => {
state.transactionNotificationIdCounter = newId
state.transactionNotifications = [...notifications, parsedNotif]
2023-12-05 18:53:52 -08:00
})
}
2022-07-05 20:37:49 -07:00
}
const MEMO_PROGRAM_ID = new PublicKey(
'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',
)
const PAYLOAD_STATEMENT = 'Login to Mango Notifications'
const PAYLOAD_VERSION = '1'
const PAYLOAD_CHAIN_ID = 1
export const createSolanaMessage = async (
wallet: WalletContextState,
setCookie: (wallet: string, token: string) => void,
) => {
const payload = new Payload()
payload.domain = window.location.host
payload.address = wallet.publicKey!.toBase58()
payload.uri = window.location.origin
payload.statement = PAYLOAD_STATEMENT
payload.version = PAYLOAD_VERSION
payload.chainId = PAYLOAD_CHAIN_ID
const message = new SIWS({ payload })
const messageText = message.prepareMessage()
const messageEncoded = new TextEncoder().encode(messageText)
wallet.signMessage!(messageEncoded)
.then(async (resp) => {
const tokenResp = await fetch(`${NOTIFICATION_API}auth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...payload,
signatureString: bs58.encode(resp),
}),
})
const body = await tokenResp.json()
const token = body.token
const error = body.error
if (error) {
notify({
type: 'error',
title: 'Error',
description: error,
})
return
}
setCookie(payload.address, token)
})
.catch((e) => {
notify({
type: 'error',
title: 'Error',
description: e.message ? e.message : `${e}`,
})
})
}
export const createLedgerMessage = async (
wallet: WalletContextState,
setCookie: (wallet: string, token: string) => void,
connection: Connection,
) => {
const payload = new Payload()
payload.domain = window.location.host
payload.address = wallet.publicKey!.toBase58()
payload.uri = window.location.origin
payload.statement = PAYLOAD_STATEMENT
payload.version = PAYLOAD_VERSION
payload.chainId = PAYLOAD_CHAIN_ID
const message = new SIWS({ payload })
const messageText = message.prepareMessage()
const tx = new Transaction()
tx.add(
new TransactionInstruction({
programId: MEMO_PROGRAM_ID,
keys: [],
data: Buffer.from(messageText),
}),
)
tx.feePayer = wallet.publicKey!
2024-01-06 12:44:22 -08:00
tx.recentBlockhash = (
await connection.getLatestBlockhash('processed')
).blockhash
const signedTx = await wallet.signTransaction!(tx)
const serializedTx = signedTx.serialize()
const tokenResp = await fetch(`${NOTIFICATION_API}auth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...payload,
isLedger: true,
serializedTx: Array.from(serializedTx),
}),
})
const body = await tokenResp.json()
const token = body.token
const error = body.error
if (error) {
notify({
type: 'error',
title: 'Error',
description: error,
})
return
}
setCookie(payload.address, token)
}
function enrichError(
unparsedNotification: TransactionNotification,
): TransactionNotification {
const notification = { ...unparsedNotification }
if (
2024-02-08 11:33:49 -08:00
notification.txid &&
notification.type == 'error' &&
notification.description
) {
try {
const errorObject = JSON.parse(notification.description)
const errorCode = errorObject.value.err.InstructionError[1].Custom
if (errorCode - 6000 >= 0) {
const idlError = IDL.errors[errorCode - 6000]
notification.description = `Error Code ${idlError.code}: ${idlError.name} (${idlError.msg})`
} else if (errorCode === 1) {
notification.description = 'Error Code 0x1: Insufficient funds'
}
} catch (e) {
console.log('error while parsing txn error: ', e)
}
}
return notification
}