forked from dappforce/grillchat
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/dev/channels' into dev/epic
- Loading branch information
Showing
13 changed files
with
424 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}) | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
Oops, something went wrong.