Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev/channels' into dev/epic
Browse files Browse the repository at this point in the history
  • Loading branch information
samchuk-vlad committed Sep 4, 2024
2 parents 5811f6d + 8cac9c1 commit f6b19b0
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const env = createEnv({
GOOGLE_CLIENT_SECRET: z.string().default(''),
FRAMES_SECRET: z.string().default(''),
NEYNAR_API_KEY: z.string().default(''),
DAPP_SECRET_KEY: z.string().default(''),
},
client: {
NEXT_PUBLIC_BASE_URL: z.string().default(''),
Expand Down Expand Up @@ -81,6 +82,8 @@ export const env = createEnv({
NEXT_PUBLIC_DATAHUB_QUERY_URL: z.string().default(''),
NEXT_PUBLIC_DATAHUB_SUBSCRIPTION_URL: z.string().default(''),
NEXT_PUBLIC_NEYNAR_CLIENT_ID: z.string().default(''),

NEXT_PUBLIC_DAPP_PUBLIC_KEY: z.string().default(''),
},
experimental__runtimeEnv: {
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
Expand Down Expand Up @@ -130,5 +133,6 @@ export const env = createEnv({
process.env.NEXT_PUBLIC_DATAHUB_SUBSCRIPTION_URL,
NEXT_PUBLIC_NEYNAR_CLIENT_ID: process.env.NEXT_PUBLIC_NEYNAR_CLIENT_ID,
NEXT_PUBLIC_PROPOSALS_HUB: process.env.NEXT_PUBLIC_PROPOSALS_HUB,
NEXT_PUBLIC_DAPP_PUBLIC_KEY: process.env.NEXT_PUBLIC_DAPP_PUBLIC_KEY,
},
})
34 changes: 34 additions & 0 deletions src/modules/telegram/AirdropPage/solana.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import LinkText from '@/components/LinkText'
import { env } from '@/env.mjs'
import { useEncryptData } from '@/services/api/mutation'
import { useMyAccount, useMyMainAddress } from '@/stores/my-account'
import { useQuery } from '@tanstack/react-query'

export default function SolanaButton() {
const myAddress = useMyMainAddress()
const { mutateAsync: encrypt } = useEncryptData()
const encodedSecretKey = useMyAccount((state) => state.encodedSecretKey)

const { data: encryptedData, isLoading } = useQuery({
queryKey: ['solana-connect', encodedSecretKey],
queryFn: () => encrypt(encodedSecretKey!),
enabled: !!encodedSecretKey,
})

const { encrypted, nonce } = encryptedData || {}
const params = {
cluster: 'mainnet-beta',
app_url: `${env.NEXT_PUBLIC_BASE_URL}/tg`,
dapp_encryption_public_key: env.NEXT_PUBLIC_DAPP_PUBLIC_KEY,
redirect_link: `${env.NEXT_PUBLIC_BASE_URL}/solana/sign?signer=${encrypted}&signer_nonce=${nonce}&address=${myAddress}`,
}
const url = `https://phantom.app/ul/v1/connect?${new URLSearchParams(
params
).toString()}`

return (
<LinkText href={url} className='break-all'>
Open Solana
</LinkText>
)
}
10 changes: 5 additions & 5 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ export type AppCommonProps = {
}

export default function App(props: AppProps<AppCommonProps>) {
// useEffect(() => {
// import('eruda').then(({ default: eruda }) => {
// eruda.init()
// })
// }, [])
useEffect(() => {
import('eruda').then(({ default: eruda }) => {
eruda.init()
})
}, [])

return (
<SessionProvider
Expand Down
35 changes: 35 additions & 0 deletions src/pages/api/encrypt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { env } from '@/env.mjs'
import { ApiResponse, handlerWrapper } from '@/server/common'
import bs58 from 'bs58'
import nacl from 'tweetnacl'
import { z } from 'zod'

type ResponseData = { encrypted: string; nonce: string }
export type ApiEncryptResponseData = ApiResponse<ResponseData>

export default handlerWrapper({
dataGetter: (req) => req.body,
inputSchema: z.object({
data: z.string(),
}),
})<ResponseData>({
errorLabel: 'encrypt',
allowedMethods: ['POST'],
handler: async ({ data }, _, res) => {
const nonce = nacl.randomBytes(nacl.box.nonceLength)
const encrypted = Buffer.from(
nacl.secretbox(
Buffer.from(data) as unknown as Uint8Array,
nonce,
bs58.decode(env.DAPP_SECRET_KEY)
)
).toString('hex')

res.json({
encrypted,
nonce: bs58.encode(nonce),
message: 'OK',
success: true,
})
},
})
108 changes: 108 additions & 0 deletions src/pages/solana/connect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Button from '@/components/Button'
import Container from '@/components/Container'
import { env } from '@/env.mjs'
import { addExternalProviderToIdentity } from '@/server/datahub-queue/identity'
import { createSignedSocialDataEvent } from '@/services/datahub/utils'
import { decryptPayload } from '@/stores/encryption'
import { decodeSecretKey, loginWithSecretKey } from '@/utils/account'
import { getCommonServerSideProps } from '@/utils/page'
import { IdentityProvider } from '@subsocial/data-hub-sdk'
import bs58 from 'bs58'
import nacl from 'tweetnacl'

export const getServerSideProps = getCommonServerSideProps(
{},
async (context) => {
const message = context.query.message as string
const solanaAddress = context.query.solana_address as string
const address = context.query.address as string
const signerKey = context.query.signer as string
const signerNonce = context.query.signer_nonce as string
const nonce = context.query.nonce as string
const data = context.query.data as string
const phantomEncryptionPublicKey = context.query
.phantom_encryption_public_key as string

const sharedSecretDapp = nacl.box.before(
bs58.decode(phantomEncryptionPublicKey),
bs58.decode(env.DAPP_SECRET_KEY)
)
const decryptedSigData = decryptPayload(data, nonce, sharedSecretDapp) as {
signature: string
}
const decryptRes = nacl.secretbox.open(
Buffer.from(signerKey, 'hex') as unknown as Uint8Array,
bs58.decode(signerNonce),
bs58.decode(env.DAPP_SECRET_KEY)
)

if (!decryptRes) {
return {
props: {
solanaAddress,
address,
error: 'Bad Request',
},
}
}

const secretKey = decodeSecretKey(Buffer.from(decryptRes).toString())
const signer = await loginWithSecretKey(secretKey)
await addExternalProviderToIdentity(
await createSignedSocialDataEvent(
'synth_add_linked_identity_external_provider',
{
address: signer.address,
proxyToAddress: address,
timestamp: Date.now(),
signer,
},
{
externalProvider: {
id: solanaAddress,
provider: IdentityProvider.SOLANA,
solProofMsg: message,
solProofMsgSig: decryptedSigData.signature,
},
}
)
)

return {
props: {
solanaAddress,
address,
error: '',
},
}
}
)

export default function SolanaConnectPage({
solanaAddress,
error,
}: {
solanaAddress: string
error: string
}) {
return (
<Container className='flex h-screen w-full flex-col items-center justify-center text-center'>
<h1 className='text-2xl font-bold'>
{error
? 'Uh-oh! 🚫 Something went wrong with the connection process. Give it another try!'
: 'Your Solana Address Successfully Linked 🎉'}
</h1>
<p className='mt-3 text-text-muted'>
{error ? error : `Your Solana address: ${solanaAddress}`}
</p>

<Button
className='mt-6'
size='lg'
href={`tg://resolve?domain=${env.NEXT_PUBLIC_TELEGRAM_BOT_USERNAME}`}
>
{error ? 'Retry Connecting' : 'Back to Epic'}
</Button>
</Container>
)
}
Loading

0 comments on commit f6b19b0

Please sign in to comment.