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

Connect user account methods to PG #298

Merged
merged 14 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/web/src/composables/auth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { LoginCredentials } from '@casimir/types'
import useEnvironment from '@/composables/environment'
import { LoginCredentials } from '@casimir/types'
import { ProviderString } from '@casimir/types'

const { usersBaseURL } = useEnvironment()

export default function useAuth() {
/**
* Gets a message from the server to sign
*
* @param {ProviderString} provider - The provider the user is using to sign in
* @param {string} address - The user's address
* @returns {Promise<Response>} - The response from the message request
*/
async function getMessage(provider: ProviderString, address: string) {
const requestOptions = {
method: 'GET',
Expand Down
30 changes: 17 additions & 13 deletions apps/web/src/composables/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,30 @@ export default function useEthers() {
async function loginWithEthers(provider: ProviderString, address: string, currency: Currency) {
const browserProvider = availableProviders.value[provider as keyof BrowserProviders]
const web3Provider: ethers.providers.Web3Provider = new ethers.providers.Web3Provider(browserProvider as EthersProvider)
const messageJson = await getMessage(provider, address)
const { message } = await messageJson.json()
const signer = web3Provider.getSigner()
const signature = await signer.signMessage(message)
const response = await login({
provider,
address,
message: message.toString(),
signedMessage: signature,
currency
})
return await response.json()
try {
const { message } = await (await getMessage(provider, address)).json()
const signer = web3Provider.getSigner()
const signature = await signer.signMessage(message)
const ethersLoginResponse = await login({
provider,
address,
message: message.toString(),
signedMessage: signature,
currency
})
return await ethersLoginResponse.json()
} catch (err) {
console.log('Error logging in: ', err)
return err
}
}

async function getEthersBrowserProviderSelectedCurrency(providerString: ProviderString) {
// IOTEX Smart Contract Address: 0x6fb3e0a217407efff7ca062d46c26e5d60a14d69
const browserProvider = availableProviders.value[providerString as keyof BrowserProviders]
const web3Provider: ethers.providers.Web3Provider = new ethers.providers.Web3Provider(browserProvider as EthersProvider)
const network = await web3Provider.getNetwork()
console.log('network.chainId :>> ', network.chainId)
// console.log('network.chainId :>> ', network.chainId)
const { currency } = currenciesByChainId[network.chainId.toString() as keyof typeof currenciesByChainId]
return currency
}
Expand Down
66 changes: 21 additions & 45 deletions apps/web/src/composables/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import useSSV from '@/composables/ssv'
import useWallet from '@/composables/wallet'
import { onMounted, ref } from 'vue'
import { User } from '@casimir/types'
ccali11 marked this conversation as resolved.
Show resolved Hide resolved
import { Account } from '@casimir/types'
import { Currency } from '@casimir/types'
import { AddAccountOptions } from '@casimir/types'
import { RemoveAccountOptions } from '@casimir/types'
import { ProviderString } from '@casimir/types'

const { usersBaseURL, ethereumURL } = useEnvironment()
Expand Down Expand Up @@ -60,62 +60,38 @@ export default function useUsers () {
// subscribeToUserEvents()
// })

async function addAccount(provider: ProviderString, address: string, currency: Currency): Promise<{ error: boolean, message: string, data: User | null }> {
address = address.toLowerCase()
const account = user.value?.accounts?.find((account: Account) => {
const accountAddress = account.address.toLowerCase()
const accountProvider = account.walletProvider
const accountCurrency = account.currency
const addressIsEqual = accountAddress === address
const providerIsEqual = accountProvider === provider
const currencyIsEqual = accountCurrency === currency
const isEqual = addressIsEqual && providerIsEqual && currencyIsEqual
return isEqual
}) as Account
if (account) {
return { error: false, message: `Account already exists on user: ${account}`, data: user.value }
} else {
const accountToAdd = {
address,
currency,
balance: '0', // TODO: Decide how we want to handle this
balanceSnapshots: [],
roi: 0,
walletProvider: provider
}
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: user.value?.address,
account: accountToAdd
})
}
const response = await fetch(`${usersBaseURL}/user/add-sub-account`, requestOptions)
const { data: userAccount } = await response.json()
user.value = userAccount
return { error: false, message: `Account added to user: ${userAccount}`, data: userAccount }
async function addAccount(account: AddAccountOptions): Promise<{ error: boolean, message: string, data: User | null }> {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ account })
}
const response = await fetch(`${usersBaseURL}/user/add-sub-account`, requestOptions)
const { data: userAccount } = await response.json()
user.value = userAccount
return { error: false, message: `Account added to user: ${userAccount}`, data: userAccount }
}

// TODO: Refactor this next. 2/14
async function removeAccount(provider: ProviderString, address: string, currency: Currency) {
async function removeAccount({ address, currency, ownerAddress, walletProvider }: RemoveAccountOptions) {
address = address.toLowerCase()
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
primaryAddress: user.value?.address,
provider,
address,
currency
currency,
ownerAddress,
walletProvider,
})
}
return await fetch(`${usersBaseURL}/user/remove-sub-account`, requestOptions)
const response = await fetch(`${usersBaseURL}/user/remove-sub-account`, requestOptions)
const { data: userAccount } = await response.json()
user.value = userAccount
return { error: false, message: `Account removed from user: ${userAccount}`, data: userAccount }
}

async function getMessage(address: string) {
Expand Down
141 changes: 99 additions & 42 deletions apps/web/src/composables/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import useEthers from '@/composables/ethers'
import useWalletConnect from '@/composables/walletConnect'
import useSolana from '@/composables/solana'
import useUsers from '@/composables/users'
import { Account, ProviderString } from '@casimir/types'
import { Account, ProviderString, User } from '@casimir/types'
import { TransactionInit } from '@/interfaces/TransactionInit'
import { MessageInit } from '@/interfaces/MessageInit'
import { Currency } from '@casimir/types'
Expand Down Expand Up @@ -80,51 +80,94 @@ export default function useWallet() {
selectedCurrency.value = currency
}

/**
* Checks if session exists and, if so:
* Gets the user's account via the API
* Sets the user's account locally
*/
async function getUserAccount() {
session.value = await Session.doesSessionExist()
if (session.value) {
const user = await getUser()
setUser(user)
try {
session.value = await Session.doesSessionExist()
if (session.value) {
const user = await getUser()
setUser(user)
return user
}
} catch (error) {
console.log('Error in getUserAccount in wallet.ts :>> ', error)
return null
}
}

/**
* Runs the login method for the selected provider
* Checks if the user is logged in, if not, it will sign up or login
* @param provider
* @param currency
* @returns
*/
async function connectWallet(provider: ProviderString, currency?: Currency) {
let response
try { // Sign Up or Login
if (!loggedIn.value) {
loadingUserWallets.value = true
const connectedAddress = await getConnectedAddress(provider, currency)
const connectedCurrency = await detectCurrency(provider) as Currency
response = await loginWithWallet(provider, connectedAddress, connectedCurrency)
loadingUserWallets.value = false
if (!response?.error) {
await getUserAccount()
const connectedAddress = await getConnectedAddressFromProvider(provider, currency)
const connectedCurrency = await detectCurrencyInProvider(provider) as Currency
const loginResponse = await loginWithWallet(provider, connectedAddress, connectedCurrency)
if (!loginResponse?.error) {
// STEP 2
const user = await getUserAccount() // Queries the API for the user's account
setSelectedProvider(provider)
setSelectedAddress(connectedAddress)
setSelectedCurrency(connectedCurrency)
loggedIn.value = true
primaryAddress.value = user.value?.address as string
primaryAddress.value = user?.Address
}
loadingUserWallets.value = false
} else { // Add account
console.log('already logged in!')
const connectedAddress = await getConnectedAddress(provider, currency) // TODO: Remove currency from here? Maybe not.
const connectedCurrency = await detectCurrency(provider, currency) as Currency
response = await addAccount(provider, connectedAddress, connectedCurrency)
if (!response?.error) {
console.log('already logged in')
console.log('checking if account exists on user')
const connectedAddress = await getConnectedAddressFromProvider(provider, currency) // TODO: Remove currency from here? Maybe not.
const connectedCurrency = await detectCurrencyInProvider(provider, currency) as Currency
const accountExists = user.value?.Accounts?.some((account: Account) => account.address === connectedAddress && account.wallet_provider === provider)
console.log('accountExists :>> ', accountExists)
if (accountExists) {
alert('Account already exists; setting provider, address, and currency')
setSelectedProvider(provider)
setSelectedAddress(connectedAddress)
setSelectedCurrency(connectedCurrency)
primaryAddress.value = response.data?.address as string
} else {
// If no, add account using users api
console.log('adding sub account')
const account = {
address: connectedAddress.toLowerCase(),
currency: connectedCurrency,
ownerAddress: user?.value?.Address.toLowerCase(),
walletProvider: provider
}
const response = await addAccount(account)
// If api query is successful, set the user.value = to the response data (which should be the user)
if (!response?.error) {
setSelectedProvider(provider)
setSelectedAddress(connectedAddress)
setSelectedCurrency(connectedCurrency)
primaryAddress.value = response.data?.Address as string
}
}
}
console.log('user.value on connect wallet :>> ', user.value)
console.log('user.value after connecting wallet :>> ', user.value)
return user.value
} catch (error) {
console.error(error)
console.error('There was an error in connectWallet :>> ', error)
}
console.log(response)
return response
}
async function getConnectedAddress(provider: ProviderString, currency?: Currency) {

/**
* Retrieve the address from the selected provider
* @param provider - MetaMask, CoinbaseWallet, Ledger, Trezor, WalletConnect, etc.
* @param currency - ETH, BTC, IOTX, SOL, etc.
* @returns
*/
async function getConnectedAddressFromProvider(provider: ProviderString, currency?: Currency) {
try {
let address
setSelectedProvider(provider)
Expand All @@ -150,14 +193,16 @@ export default function useWallet() {
}
}

/**
* Uses appropriate provider composable to login or sign up
* @param provider
* @param address
* @param currency
* @returns
*/
async function loginWithWallet(provider: ProviderString, address: string, currency: Currency) {
if (ethersProviderList.includes(provider)) {
const result = await loginWithEthers(provider, address, currency)
if (result.error) {
console.log('result in loginWithWallet in wallet.ts :>> ', result)
alert('There was an error signing up. Please try again.')
}
return result
return await loginWithEthers(provider, address, currency)
} else {
// TODO: Implement this for other providers
console.log('Sign up not yet supported for this wallet provider')
Expand All @@ -177,12 +222,12 @@ export default function useWallet() {
loadingUserWallets.value = false
}

// TODO: Implement this for other providers
async function setPrimaryWalletAccount() {
if (!loggedIn.value) {
alert('Please login first')
}

// TODO: Implement this for other providers
return alert('Not yet implemented for this wallet provider')
if (ethersProviderList.includes(selectedProvider.value)) {
const result = await updatePrimaryAddress(primaryAddress.value, selectedProvider.value, selectedAddress.value)
const { data } = await result.json()
Expand All @@ -199,11 +244,17 @@ export default function useWallet() {
if (selectedAddress.value === primaryAddress.value) {
return alert('Cannot remove primary account')
} else if (ethersProviderList.includes(selectedProvider.value)) {
const result = await removeAccount(selectedProvider.value, selectedAddress.value, selectedCurrency.value)
const json = await result.json()
if (!json.error) {
setSelectedAddress(json.data.address)
json.data.accounts.forEach((account: Account) => {
const opts = {
address: selectedAddress.value,
currency: selectedCurrency.value,
ownerAddress: primaryAddress.value,
walletProvider: selectedProvider.value
}
const result = await removeAccount(opts)
console.log('result :>> ', result)
if (!result.error) {
setSelectedAddress(result.data.address)
result.data.Accounts.forEach((account: Account) => {
if (account.address === selectedAddress.value) {
setSelectedProvider(account.walletProvider as ProviderString)
setSelectedCurrency(account.currency as Currency)
Expand All @@ -213,7 +264,13 @@ export default function useWallet() {
}
}

async function detectCurrency(provider: ProviderString, currency?: Currency) {
/**
* Detects the currency of the connected wallet provider and account
* @param provider
* @param currency
* @returns
*/
async function detectCurrencyInProvider(provider: ProviderString, currency?: Currency) {
// TODO: Implement this for other providers
if (ethersProviderList.includes(provider)){
return await getEthersBrowserProviderSelectedCurrency(provider) as Currency
Expand Down Expand Up @@ -305,9 +362,9 @@ export default function useWallet() {
// This is the old method; currently used in users.ts so may want to still keep it
async function getUserBalance(userAddress: string): Promise<ethers.BigNumber> {
const provider = new ethers.providers.JsonRpcProvider(ethereumURL)
const result = await provider.getBalance(userAddress)
console.log('result :>> ', result)
return result
const userBalance = await provider.getBalance(userAddress)
console.log('userBalance :>> ', userBalance)
return userBalance
}

return {
Expand All @@ -324,7 +381,7 @@ export default function useWallet() {
logout,
setPrimaryWalletAccount,
removeConnectedAccount,
detectCurrency,
detectCurrencyInProvider,
getCurrentBalance,
sendTransaction,
signMessage,
Expand Down
Loading