Skip to content

Commit

Permalink
Merge pull request #155 from xuhcc/network-change
Browse files Browse the repository at this point in the history
Fixing login, balance check, better cart management
  • Loading branch information
xuhcc authored Oct 22, 2020
2 parents 4432a9d + c81997d commit 0c5f72f
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 111 deletions.
12 changes: 7 additions & 5 deletions vue-app/src/api/storage.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
import { sha256, encrypt, decrypt } from '@/utils/crypto'
import { factory } from './core'

function getFullStorageKey(
accountId: string,
storageId: string,
storageKey: string,
): string {
const factoryAddress = factory.address.toLowerCase()
storageId = storageId.toLowerCase()
accountId = accountId.toLowerCase()
return sha256(`clrfund-${factoryAddress}-${accountId}-${storageKey}`)
return sha256(`clrfund-${storageId}-${accountId}-${storageKey}`)
}

function setItem(
accountId: string,
encryptionKey: string,
storageId: string,
storageKey: string,
value: string,
): void {
const encryptedValue = encrypt(value, encryptionKey)
window.localStorage.setItem(
getFullStorageKey(accountId, storageKey),
getFullStorageKey(accountId, storageId, storageKey),
encryptedValue,
)
}

function getItem(
accountId: string,
encryptionKey: string,
storageId: string,
storageKey: string,
): string | null {
const encryptedValue = window.localStorage.getItem(
getFullStorageKey(accountId, storageKey),
getFullStorageKey(accountId, storageId, storageKey),
)
const value = encryptedValue ? decrypt(encryptedValue, encryptionKey) : null
return value
Expand Down
16 changes: 13 additions & 3 deletions vue-app/src/api/user.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { getProfile } from '3box/lib/api'
import makeBlockie from 'ethereum-blockies-base64'
import { Contract } from 'ethers'
import { BigNumber, Contract } from 'ethers'
import { Web3Provider } from '@ethersproject/providers'

import { FundingRound, VerifiedUserRegistry } from './abi'
import { FundingRound, VerifiedUserRegistry, ERC20 } from './abi'
import { ipfsGatewayUrl, provider } from './core'

export interface User {
walletAddress: string;
walletProvider: Web3Provider;
isVerified: boolean | null;
encryptionKey: string;
isVerified: boolean | null;
balance: BigNumber | null;
contribution: BigNumber | null;
}

export async function getProfileImageUrl(walletAddress: string): Promise<string | null> {
Expand All @@ -37,3 +39,11 @@ export async function isVerifiedUser(
const registry = new Contract(registryAddress, VerifiedUserRegistry, provider)
return await registry.isVerifiedUser(walletAddress)
}

export async function getTokenBalance(
tokenAddress: string,
walletAddress: string,
): Promise<BigNumber> {
const token = new Contract(tokenAddress, ERC20, provider)
return await token.balanceOf(walletAddress)
}
89 changes: 68 additions & 21 deletions vue-app/src/components/Cart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ import ContributionModal from '@/components/ContributionModal.vue'
import ReallocationModal from '@/components/ReallocationModal.vue'
import { MAX_CONTRIBUTION_AMOUNT, CartItem, Contributor } from '@/api/contributions'
import { RoundStatus } from '@/api/round'
import { storage } from '@/api/storage'
import { User } from '@/api/user'
import { CHECK_VERIFICATION } from '@/store/action-types'
import { LOAD_USER_INFO } from '@/store/action-types'
import {
SET_CONTRIBUTOR,
ADD_CART_ITEM,
Expand All @@ -72,10 +73,14 @@ import {
const CART_STORAGE_KEY = 'cart'
const CONTRIBUTOR_INFO_STORAGE_KEY = 'contributor-info'
function loadContributorInfo(user: User): Contributor | null {
function loadContributorInfo(
fundingRoundAddress: string,
user: User,
): Contributor | null {
const serializedData = storage.getItem(
user.walletAddress,
user.encryptionKey,
fundingRoundAddress,
CONTRIBUTOR_INFO_STORAGE_KEY,
)
if (serializedData) {
Expand All @@ -90,11 +95,16 @@ function loadContributorInfo(user: User): Contributor | null {
@Component({
watch: {
cart(items: CartItem[]) {
// Save cart to local storage on changes
const currentUser = this.$store.state.currentUser
const currentRound = this.$store.state.currentRound
if (!currentUser || !currentRound) {
return
}
// Save cart to local storage on changes
storage.setItem(
currentUser.walletAddress,
currentUser.encryptionKey,
currentRound.fundingRoundAddress,
CART_STORAGE_KEY,
JSON.stringify(items),
)
Expand All @@ -104,35 +114,54 @@ function loadContributorInfo(user: User): Contributor | null {
export default class Cart extends Vue {
mounted() {
// Restore cart from local storage
// Reload cart when wallet account or round changes
this.$store.watch(
(state) => state.currentUser?.walletAddress,
this.restoreCart,
(state) => {
return (
state.currentUser?.walletAddress +
state.currentRound?.fundingRoundAddress
)
},
this.refreshCart,
)
this.restoreCart()
this.refreshCart()
// Restore contributor info from local storage
// Reload contributor info if wallet account of round changes
this.$store.watch(
(state) => state.currentUser?.walletAddress,
this.restoreContributor,
(state) => {
return (
state.currentUser?.walletAddress +
state.currentRound?.fundingRoundAddress
)
},
this.refreshContributor,
)
this.restoreContributor()
this.refreshContributor()
// Check verification every minute
setInterval(async () => {
this.$store.dispatch(CHECK_VERIFICATION)
// Check verification and token balance every minute
setInterval(() => {
this.$store.dispatch(LOAD_USER_INFO)
}, 60 * 1000)
}
private restoreCart() {
private refreshCart() {
const currentUser = this.$store.state.currentUser
if (!currentUser) {
// Restore cart only if user has logged in
// Clear the cart on log out / when not logged in
this.cart.slice().forEach((item) => {
this.$store.commit(REMOVE_CART_ITEM, item)
})
return
}
const currentRound = this.$store.state.currentRound
if (!currentRound) {
return
}
// Load cart from local storage
const serializedCart = storage.getItem(
currentUser.walletAddress,
currentUser.encryptionKey,
currentRound.fundingRoundAddress,
CART_STORAGE_KEY,
)
if (serializedCart) {
Expand All @@ -142,13 +171,22 @@ export default class Cart extends Vue {
}
}
private restoreContributor() {
private refreshContributor() {
const currentUser = this.$store.state.currentUser
if (!currentUser) {
// Restore contributor info only if user has logged in
// Reset contributor no log out / when not logged in
this.$store.commit(SET_CONTRIBUTOR, null)
return
}
const contributor = loadContributorInfo(currentUser)
const currentRound = this.$store.state.currentRound
if (!currentRound) {
return
}
// Load contributor info from local storage
const contributor = loadContributorInfo(
currentRound.fundingRoundAddress,
currentUser,
)
if (contributor) {
this.$store.commit(SET_CONTRIBUTOR, contributor)
}
Expand All @@ -160,7 +198,7 @@ export default class Cart extends Vue {
}
get contribution(): BigNumber {
return this.$store.state.contribution
return this.$store.state.currentUser?.contribution || BigNumber.from(0)
}
get cart(): CartItem[] {
Expand Down Expand Up @@ -198,7 +236,7 @@ export default class Cart extends Vue {
canRemoveItem(): boolean {
// The number of MACI messages can't go down after initial submission
return !this.$store.state.contributor
return this.contribution.isZero()
}
removeItem(item: CartItem) {
Expand Down Expand Up @@ -252,11 +290,20 @@ export default class Cart extends Vue {
return 'Your account is not verified'
} else if (!this.isFormValid()) {
return 'Please enter correct amounts'
} else if (currentRound.status === RoundStatus.Cancelled) {
return 'Funding round has been cancelled'
} else {
const total = this.getTotal()
if (this.contribution.isZero()) {
// Contributing
if (DateTime.local() >= currentRound.signUpDeadline) {
return 'The contribution period has ended'
} else if (total.eq(BigNumber.from(0))) {
return 'Contribution amount must be greater then zero'
} else if (currentUser.balance === null) {
return '' // No error: waiting for balance
} else if (total.gt(currentUser.balance)) {
return `Insufficient ${currentRound.nativeTokenSymbol} balance`
} else if (this.isGreaterThanMax()) {
return 'Contribution amount is too large'
} else {
Expand Down
22 changes: 17 additions & 5 deletions vue-app/src/components/ContributionModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,27 @@ import { RoundInfo } from '@/api/round'
import { storage } from '@/api/storage'
import { User } from '@/api/user'
import { LOAD_ROUND_INFO } from '@/store/action-types'
import { SET_CONTRIBUTOR, SET_CONTRIBUTION } from '@/store/mutation-types'
import { SET_CURRENT_USER, SET_CONTRIBUTOR } from '@/store/mutation-types'
import { getEventArg } from '@/utils/contracts'
import { createMessage } from '@/utils/maci'
import { FundingRound, ERC20, MACI } from '@/api/abi'
const CONTRIBUTOR_INFO_STORAGE_KEY = 'contributor-info'
function saveContributorInfo(user: User, contributor: Contributor) {
function saveContributorInfo(
fundingRoundAddress: string,
user: User,
contributor: Contributor,
) {
const serializedData = JSON.stringify({
privateKey: contributor.keypair.privKey.serialize(),
stateIndex: contributor.stateIndex,
})
storage.setItem(
user.walletAddress,
user.encryptionKey,
fundingRoundAddress,
CONTRIBUTOR_INFO_STORAGE_KEY,
serializedData,
)
Expand Down Expand Up @@ -125,10 +130,17 @@ export default class ContributionModal extends Vue {
stateIndex: stateIndex.toNumber(),
}
// Save contributor info to storage
saveContributorInfo(this.$store.state.currentUser, contributor)
saveContributorInfo(
fundingRoundAddress,
this.$store.state.currentUser,
contributor,
)
// Set contribution and update round info
this.$store.commit(SET_CONTRIBUTOR, contributor)
this.$store.commit(SET_CONTRIBUTION, total)
this.$store.commit(SET_CURRENT_USER, {
...this.$store.state.currentUser,
contribution: total,
})
this.$store.dispatch(LOAD_ROUND_INFO)
// Vote (step 3)
const messages: Message[] = []
Expand Down Expand Up @@ -157,7 +169,7 @@ export default class ContributionModal extends Vue {
get contribution(): FixedNumber {
return FixedNumber.fromValue(
this.$store.state.contribution,
this.$store.state.currentUser.contribution,
this.currentRound.nativeTokenDecimals,
)
}
Expand Down
Loading

0 comments on commit 0c5f72f

Please sign in to comment.