Skip to content

Commit

Permalink
Add gmail emails import logic
Browse files Browse the repository at this point in the history
  • Loading branch information
wryonik committed May 25, 2024
1 parent 4a4e3c6 commit db6a076
Show file tree
Hide file tree
Showing 22 changed files with 1,965 additions and 1,051 deletions.
2 changes: 0 additions & 2 deletions packages/app/.env

This file was deleted.

1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"@proof-of-twitter/circuits": "workspace:^",
"@proof-of-twitter/contracts": "workspace:^",
"@rainbow-me/rainbowkit": "^1.3.3",
"@react-oauth/google": "^0.12.1",
"@zk-email/helpers": "4.0.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
Expand Down
47 changes: 47 additions & 0 deletions packages/app/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,50 @@ export const Button = styled.button`
cursor: not-allowed;
}
`;

export const OutlinedButton = styled.button`
padding: 0 14px;
border-radius: 4px;
border: none;
display: flex;
align-items: center;
justify-content: center;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: -0.02em;
color: #8272e4;
cursor: pointer;
height: 48px;
width: 100%;
min-width: 32px;
transition: all 0.2s ease-in-out;
background: transparent;
border: 1px solid #8272e4;
&:hover {
background: #9b8df2;
color: white;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;


export const TextButton = styled.button`
width: fit-content;
background: transparent;
border: none;
color: white;
font-weight: 500;
padding: 4px 16px;
border-radius: 4px;
&:hover {
background: #00000020;
color: white;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
28 changes: 28 additions & 0 deletions packages/app/src/components/EmailInputMethod.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useState } from "react";
import { Button, OutlinedButton } from "./Button";

const EmailInputMethod = ({ onClickGoogle, onClickEMLFile }) => {
return (
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 16,
alignItems: "center",
}}
>
<Button onClick={onClickGoogle}>Sign in with Google</Button>
or
<OutlinedButton
onClick={() => {
onClickEMLFile();
}}
>
Upload email .eml file{" "}
</OutlinedButton>
</div>
);
};

export default EmailInputMethod;
75 changes: 75 additions & 0 deletions packages/app/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
export const ZERO = BigInt(0);
export const SECONDS_IN_DAY = BigInt(86400);

export const MAX_USDC_TRANSFER_SIZE_VENMO = 500000000n; // 500 USD
export const MAX_USDC_TRANSFER_SIZE_HDFC = 10000000n; // 10 USD
export const MAX_USDC_TRANSFER_SIZE_GARANTI = 10000000n; // 10 USD
export const MAX_USDC_TRANSFER_SIZE_REVOLUT = 100000000n; // 100 USD

export const DEPOSIT_REFETCH_INTERVAL = 20000; // 0.3 minutes
export const STATE_REFETCH_INTERVAL = 60000; // 1 minute
// export const DEPOSIT_REFETCH_INTERVAL = 2000000; // 30 minutes
// export const STATE_REFETCH_INTERVAL = 6000000; // 100 minute

export const VENMO_EMAIL_FILTER = `from:[email protected] subject:"You paid" after:2024/01/10`;
export const HDFC_EMAIL_FULTER = `from:[email protected] subject:"❗ You have done a UPI txn. Check details!" after:2023/09/01`;
export const GARANTI_EMAIL_FULTER = `from:[email protected] subject:"Para Transferi Bilgilendirmesi" after:2023/09/01`;

export const PRECISION = BigInt(1_000_000_000_000_000_000); // 18
export const USDC_UNITS = BigInt(1_000_000); // 6
export const PENNY_IN_USDC_UNITS = BigInt(10_000); // 6

export const EMPTY_STRING = '';

export const SOCKET_QUOTE_DEFAULT_ADDRESS = '0x18Cc6F90512C6D95ACA0d57F98C727D61873c06a';
export const SOCKET_DEFAULT_SOL_ADDRESS = '8pHKRNF3u8tndkUJ4euAddNWM9EAMWbUiK5GVmtaGY5U';
export const QUOTE_FETCHING_DEBOUNCE_MS = 750;

// the numeric form of the payload1 passed into the primitive
// corresponds to the openssh signature produced by the following command:
// echo "E PLURIBUS UNUM; DO NOT SHARE" | ssh-keygen -Y sign -n double-blind.xyz -f ~/.ssh/id_rsa | pbcopy
export const MAGIC_DOUBLE_BLIND_BASE_MESSAGE =
14447023197094784173331616578829287000074783130802912942914027114823662617007553911501158244718575362051758829289159984830457466395841150324770159971462582912755545324694933673046215187947905307019469n;
// Length in bits
export const MAGIC_DOUBLE_BLIND_BASE_MESSAGE_LEN = 672;

export const CIRCOM_FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
export const MAX_HEADER_PADDED_BYTES = 1024; // NOTE: this must be the same as the first arg in the email in main args circom
export const MAX_BODY_PADDED_BYTES = 6400; // NOTE: this must be the same as the arg to sha the remainder number of bytes in the email in main args circom


export const CLIENT_VERSION = '0.3.2';

// circom constants from main.circom / https://zkrepl.dev/?gist=30d21c7a7285b1b14f608325f172417b
// template RSAGroupSigVerify(n, k, levels) {
// component main { public [ modulus ] } = RSAVerify(121, 17);
// component main { public [ root, payload1 ] } = RSAGroupSigVerify(121, 17, 30);
export const CIRCOM_BIGINT_N = 121;
export const CIRCOM_BIGINT_K = 17;
export const CIRCOM_LEVELS = 30;


// This is the string that comes right before the target string in the email. Ideally as close to the end of the email as possible.
export const STRING_PRESELECTOR = "<!-- recipient name -->";


// Misc smart contract values
export const UINT256_MAX = "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000000000000000000000000000";
export const CALLER_ACCOUNT = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
export const DEFAULT_NETWORK = "base";


// Proving key paths
export const HOSTED_FILES_PATH = "https://s3.amazonaws.com/zk-p2p/v2/v0.0.10/";
export const REGISTRATION_KEY_FILE_NAME = "venmo_registration/venmo_registration";
export const SEND_KEY_FILE_NAME = "venmo_send/venmo_send";

export const RemoteProofGenEmailTypes = {
REGISTRATION: "registration",
SEND: "send",
};

const ENABLE_STATE_LOGGING = false;
export const esl = ENABLE_STATE_LOGGING;
30 changes: 30 additions & 0 deletions packages/app/src/contexts/Account/AccountContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createContext } from 'react';


interface AccountValues {
isLoggedIn: boolean;
loggedInEthereumAddress: string | null;
loginStatus: string | null;
authenticatedLogout: (() => void) | null;
authenticatedLogin: (() => void) | null;
accountDisplay: string | null;
network: string | null;
connectStatus: string | null;
exportAuthenticatedWallet: (() => void) | null;
};

const defaultValues: AccountValues = {
isLoggedIn: false,
loggedInEthereumAddress: null,
loginStatus: null,
authenticatedLogout: null,
authenticatedLogin: null,
accountDisplay: null,
network: null,
connectStatus: null,
exportAuthenticatedWallet: null
};

const AccountContext = createContext<AccountValues>(defaultValues);

export default AccountContext;
172 changes: 172 additions & 0 deletions packages/app/src/contexts/Account/AccountProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { useEffect, useState, ReactNode } from 'react';
import { useAccount, useConnect, useDisconnect, useNetwork } from 'wagmi';
import { useWallets } from '@privy-io/react-auth';
import { usePrivyWagmi } from '@privy-io/wagmi-connector';
import { usePrivy } from '@privy-io/react-auth';

import AccountContext from './AccountContext';
import { esl } from '../../constants';
import { formatAddress } from '../../helpers/addressFormat';
import { LoginStatus, LoginStatusType } from '../../helpers/types/loginStatus';

interface ProvidersProps {
children: ReactNode;
}

const AccountProvider = ({ children }: ProvidersProps) => {
const { address } = useAccount();
const { chain } = useNetwork();
const { disconnect } = useDisconnect();
const { status: connectStatus } = useConnect();

const { wallets } = useWallets();
const { wallet: activeWallet, setActiveWallet } = usePrivyWagmi();
const {
authenticated,
logout: authenticatedLogout,
user,
login: authenticatedLogin,
exportWallet: exportAuthenticatedWallet
} = usePrivy();

/*
* State
*/

const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const [loginStatus, setLoginStatus] = useState<LoginStatusType>(LoginStatus.LOGGED_OUT);
const [loggedInEthereumAddress, setLoggedInEthereumAddress] = useState<string | null>(null);
const [accountDisplay, setAccountDisplay] = useState<string | null>(null);
const [network, setNetwork] = useState<string | null>(null);

/*
* Hooks
*/

useEffect(() => {
esl && console.log('activeWallet_1');
esl && console.log('checking wallets: ', wallets);
esl && console.log('checking activeWallet: ', activeWallet);

if (wallets[0] && !activeWallet) {
esl && console.log('activeWallet_2');
setActiveWallet(wallets[0]);
}
}, [activeWallet, wallets, setActiveWallet]);

useEffect(() => {
esl && console.log('loginStatus_1');
esl && console.log('user: ', user);

if (authenticated && user?.wallet?.connectorType) {
const connectorType = user.wallet.connectorType;
if (connectorType === 'embedded') {
esl && console.log('loginStatus_2');

setLoginStatus(LoginStatus.AUTHENTICATED);
} else if (
connectorType === 'injected' ||
connectorType === 'coinbase_wallet' ||
connectorType === 'wallet_connect' ||
connectorType === 'wallet_connect_v2'
) {
esl && console.log('loginStatus_3');

setLoginStatus(LoginStatus.EOA);
} else {
esl && console.log('loginStatus_4');

setLoginStatus(LoginStatus.LOGGED_OUT);
}
} else {
esl && console.log('loginStatus_5');

setLoginStatus(LoginStatus.LOGGED_OUT);
}
}, [authenticated, user]);

useEffect(() => {
esl && console.log('isLoggedIn_1');
esl && console.log('checking loginStatus: ', loginStatus);
esl && console.log('user: ', user);
esl && console.log('address: ', address);

switch (loginStatus) {
case LoginStatus.AUTHENTICATED:
if (address && user) {
esl && console.log('isLoggedIn_2');

if (user.email && user.email.address) {
setAccountDisplay(user.email.address);
} else if (user.google && user.google.email) {
setAccountDisplay(user.google.email);
} else if (user.twitter && user.twitter.username) {
setAccountDisplay(user.twitter.username);
} else if (user.farcaster && user.farcaster.displayName) {
setAccountDisplay(user.farcaster.displayName);
} else {
setAccountDisplay('Account');
}

setIsLoggedIn(true);
setLoggedInEthereumAddress(address);
}
break;

case LoginStatus.EOA:
if (address) {
esl && console.log('isLoggedIn_3');

const formattedAddress = formatAddress(address);

setAccountDisplay(formattedAddress);
setIsLoggedIn(true);
setLoggedInEthereumAddress(address);
}
break;

case LoginStatus.LOGGED_OUT:
default:
esl && console.log('isLoggedIn_4');

setAccountDisplay(null);
setIsLoggedIn(false);
setLoggedInEthereumAddress(null);
}
}, [user, address, loginStatus, disconnect]);

useEffect(() => {
esl && console.log('networkRaw_1');
esl && console.log('checking chain: ', chain);

if (chain) {
esl && console.log('networkRaw_2');

setNetwork(chain.network);
} else {
esl && console.log('networkRaw_3');

setNetwork(null);
}
}, [chain]);

return (
<AccountContext.Provider
value={{
isLoggedIn,
loggedInEthereumAddress,
loginStatus,
authenticatedLogin,
authenticatedLogout,
accountDisplay,
network,
connectStatus,
exportAuthenticatedWallet
}}
>
{children}
</AccountContext.Provider>
);
};

export default AccountProvider;
2 changes: 2 additions & 0 deletions packages/app/src/contexts/Account/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as AccountContext } from './AccountContext'
export { default as AccountProvider } from './AccountProvider'
24 changes: 24 additions & 0 deletions packages/app/src/contexts/GoogleAuth/GoogleAuthContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createContext } from 'react'


interface GoogleAuthValues {
googleAuthToken: any | null;
isGoogleAuthed: boolean;
loggedInGmail: string | null;
scopesApproved: boolean;
googleLogIn: () => void;
googleLogOut: () => void;
}

const defaultValues: GoogleAuthValues = {
googleAuthToken: null,
isGoogleAuthed: false,
loggedInGmail: null,
scopesApproved: false,
googleLogIn: () => {},
googleLogOut: () => {},
};

const GoogleAuthContext = createContext<GoogleAuthValues>(defaultValues)

export default GoogleAuthContext
Loading

0 comments on commit db6a076

Please sign in to comment.