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

Added T&C doc signing functionality #129

Closed
wants to merge 7 commits into from
48 changes: 44 additions & 4 deletions src/app/api/tnc/signUser/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { NextResponse } from 'next/server';

import { SIGNING_DATA } from '@/constants';
import { db } from '@/db';
import { standariseAddress } from '@/utils';
import { Account, RpcProvider } from 'starknet';

export async function POST(req: Request) {
const { address, message } = await req.json();
const { address, signature } = await req.json();

if (!address || !message) {
if (!address || !signature) {
return NextResponse.json({
success: false,
message: 'address or message not found',
message: 'address or signature not found',
user: null,
});
}
Expand All @@ -22,6 +24,44 @@ export async function POST(req: Request) {
throw new Error('Invalid address');
}

const parsedSignature = JSON.parse(signature) as string[];
console.log(address, parsedSignature, 'parsedSignature');

if (!parsedSignature || parsedSignature.length <= 0) {
return NextResponse.json({
success: false,
message: 'parsing of signature failed',
user: null,
});
}

const provider = new RpcProvider({
nodeUrl: process.env.NEXT_PUBLIC_RPC_URL!,
});

const myAccount = new Account(
provider,
address,
process.env.NEXT_PUBLIC_PRIVATE_KEY!,
);

let isValid;

try {
isValid = await myAccount.verifyMessage(SIGNING_DATA, parsedSignature);
console.log('isValid', isValid);
} catch (error) {
console.log('verification failed:', error);
}

if (!isValid) {
return NextResponse.json({
success: false,
message: 'Invalid signature',
user: null,
});
}

const user = await db.user.findFirst({
where: {
address: parsedAddress,
Expand All @@ -41,7 +81,7 @@ export async function POST(req: Request) {
address: parsedAddress,
},
data: {
message,
message: signature,
isTncSigned: true,
},
});
Expand Down
4 changes: 3 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ interface NavbarProps {
}

export default function Navbar(props: NavbarProps) {
const { address, connector } = useAccount();
const { address, connector, account } = useAccount();
const { connect, connectors } = useConnect();
const searchParams = useSearchParams();
const { disconnectAsync } = useDisconnect();
Expand Down Expand Up @@ -92,6 +92,8 @@ export default function Navbar(props: NavbarProps) {
return balance.amount.toEtherToFixedDecimals(6);
};

console.log(account, 'account');

useEffect(() => {
(async () => {
if (address) {
Expand Down
128 changes: 40 additions & 88 deletions src/components/TncModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';

import React from 'react';

import { SIGNING_DATA } from '@/constants';
import {
Button,
Link,
Expand All @@ -12,102 +11,55 @@ import {
ModalOverlay,
Text,
} from '@chakra-ui/react';
import { useAccount, useSignTypedData } from '@starknet-react/core';

import { useAccount } from '@starknet-react/core';
import axios from 'axios';

const exampleData = {
types: {
StarkNetDomain: [
{ name: 'name', type: 'felt' },
{ name: 'version', type: 'felt' },
{ name: 'chainId', type: 'felt' },
],
Person: [
{ name: 'name', type: 'felt' },
{ name: 'wallet', type: 'felt' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'felt' },
],
},
primaryType: 'Mail',
domain: {
name: 'Starknet Mail',
version: '1',
chainId: 1,
},
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
};

const signingData = {
types: {
StarkNetDomain: [
{ name: 'name', type: 'felt' },
{ name: 'version', type: 'felt' },
{ name: 'chainId', type: 'felt' },
],
Person: [
{ name: 'name', type: 'felt' },
{ name: 'wallet', type: 'felt' },
],
Felt: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'felt' },
],
},
primaryType: 'felt',
domain: {
name: 'STRKFarm',
version: '1',
chainId: '0x534e5f4d41494e',
},
message: {
from: {
name: 'Test1',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Test2',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents:
'You confirm that you have read and agree to our Terms & Conditions, which can be found at https://github.com/strkfarm/static-assets/src/tnc.md.\n\nPlease note, this message is solely for verifying your agreement to our T&C and does not authorize any transaction or movement of your assets.',
},
};
import React, { SetStateAction } from 'react';

interface TncModalProps {
isOpen: boolean;
setIsTncSigned: React.Dispatch<SetStateAction<boolean>>;
setIsSigningPending: React.Dispatch<SetStateAction<boolean>>;
onClose: () => void;
}

const TncModal: React.FC<TncModalProps> = ({ isOpen, onClose }) => {
const { signTypedDataAsync } = useSignTypedData(signingData);
const { address } = useAccount();
const TncModal: React.FC<TncModalProps> = ({
isOpen,
onClose,
setIsTncSigned,
setIsSigningPending,
}) => {
// const { signTypedDataAsync } = useSignTypedData(SIGNING_DATA);
const { address, account } = useAccount();

const handleSign = async () => {
const res = await signTypedDataAsync();
if (!address || !account) {
return;
}

setIsSigningPending(true);

const signature = (await account.signMessage(SIGNING_DATA)) as string[];

if (res && res?.toString().length > 0) {
onClose();
await axios.post('/api/tnc/signUser', {
address,
message: res?.toString(),
});
if (signature && signature.length > 0) {
try {
const res2 = await axios.post('/api/tnc/signUser', {
address,
signature: JSON.stringify([signature[1], signature[2]]),
});

if (res2.data?.success) {
onClose();
setIsTncSigned(true);
}
} catch (error) {
console.error(error);
setIsSigningPending(false);
} finally {
setIsSigningPending(false);
}
}

setIsSigningPending(false);
};

return (
Expand Down Expand Up @@ -135,7 +87,7 @@ const TncModal: React.FC<TncModalProps> = ({ isOpen, onClose }) => {
<Text textAlign="center">
You agree to STRKFarm terms and conditions as stated in{' '}
<Link href="#" color="purple" _hover={{ textDecor: 'underline' }}>
githublink
github link
</Link>
</Text>

Expand Down
54 changes: 37 additions & 17 deletions src/components/TxButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import { useAccount, useContractWrite } from '@starknet-react/core';
import axios from 'axios';
import { useAtomValue, useSetAtom } from 'jotai';
import mixpanel from 'mixpanel-browser';
import { usePathname } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { TwitterShareButton } from 'react-share';
import { Call } from 'starknet';
import TncModal from './TncModal';

interface TxButtonProps {
txInfo: StrategyTxProps;
Expand All @@ -42,10 +42,11 @@ export default function TxButton(props: TxButtonProps) {
const { address } = useAccount();
const monitorNewTx = useSetAtom(monitorNewTxAtom);
const { isOpen, onOpen, onClose } = useDisclosure();
const pathname = usePathname();
const referralCode = useAtomValue(referralCodeAtom);

const [showTncModal, setShowTncModal] = useState(false);
const [isTncSigned, setIsTncSigned] = useState(false);
const [isSigningPending, setIsSigningPending] = useState(false);

const disabledStyle = {
bg: 'var(--chakra-colors-disabled_bg)',
Expand Down Expand Up @@ -104,6 +105,21 @@ export default function TxButton(props: TxButtonProps) {
return '';
}, [isMobile, address, props]);

useEffect(() => {
if (isTncSigned) {
writeAsync().then((tx) => {
if (props.buttonText === 'Deposit') onOpen();
mixpanel.track('Submitted tx', {
strategyId: props.txInfo.strategyId,
txHash: tx.transaction_hash,
text: props.text,
address,
buttonText: props.buttonText,
});
});
}
}, [isTncSigned]);

if (disabledText) {
return (
<Button
Expand Down Expand Up @@ -213,12 +229,14 @@ export default function TxButton(props: TxButtonProps) {
</ModalContent>
</Modal>

{/* {showTncModal && (
{showTncModal && (
<TncModal
isOpen={showTncModal}
setIsTncSigned={setIsTncSigned}
setIsSigningPending={setIsSigningPending}
onClose={() => setShowTncModal(false)}
/>
)} */}
)}

<Box width={'100%'} textAlign={'center'}>
<Button
Expand All @@ -239,24 +257,26 @@ export default function TxButton(props: TxButtonProps) {
address,
});

// const res = await getUser();
const res = await getUser();

// if (!res) {
writeAsync().then((tx) => {
if (props.buttonText === 'Deposit') onOpen();
mixpanel.track('Submitted tx', {
strategyId: props.txInfo.strategyId,
txHash: tx.transaction_hash,
text: props.text,
address,
buttonText: props.buttonText,
if (!res) {
writeAsync().then((tx) => {
if (props.buttonText === 'Deposit') onOpen();
mixpanel.track('Submitted tx', {
strategyId: props.txInfo.strategyId,
txHash: tx.transaction_hash,
text: props.text,
address,
buttonText: props.buttonText,
});
});
});
// }
}
}}
{...props.buttonProps}
>
{isPending && <Spinner size={'sm'} marginRight={'5px'} />}{' '}
{(isPending || isSigningPending) && (
<Spinner size={'sm'} marginRight={'5px'} />
)}{' '}
{props.text}
</Button>
</Box>
Expand Down
25 changes: 25 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TypedData } from 'starknet';
import { NFTInfo, TokenInfo } from './strategies/IStrategy';
import MyNumber from './utils/MyNumber';

Expand Down Expand Up @@ -191,6 +192,30 @@ export const NFTS: NFTInfo[] = [
},
];

export const SIGNING_DATA: TypedData = {
types: {
StarkNetDomain: [
{ name: 'name', type: 'felt' },
{ name: 'version', type: 'felt' },
{ name: 'chainId', type: 'felt' },
],
Tnc: [
{ name: 'message', type: 'felt' },
{ name: 'document', type: 'felt' },
],
},
primaryType: 'Tnc',
domain: {
name: 'STRKFarm',
version: '1',
chainId: '0x534e5f4d41494e',
},
message: {
message: 'Read and Agree T&C',
document: 'github.com/strkfarm/tncdoc',
},
};

export const LATEST_TNC_DOC_VERSION = '1.0';

export default CONSTANTS;
Loading