Skip to content

Commit

Permalink
[Wallet] Hide CTAs with verification when insufficient balance (#4850)
Browse files Browse the repository at this point in the history
### Description

This hides CTAs with verification when balance is insufficient to retrieve a pepper if it's not cached.

### Tested

iOS simulator

### Related issues

- Fixes #4748
- Fixes #4797

### Backwards compatibility

Yes
  • Loading branch information
i1skn authored Aug 28, 2020
1 parent 5f43ef7 commit 606045b
Show file tree
Hide file tree
Showing 17 changed files with 2,139 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fetchMock from 'fetch-mock'
import {
getPepperFromThresholdSignature,
getPhoneNumberIdentifier,
isBalanceSufficientForSigRetrieval,
} from './phone-number-identifier'
import { AuthenticationMethod, EncryptionKeySigner, ErrorMessages, ServiceContext } from './query'

Expand Down Expand Up @@ -30,6 +31,14 @@ const authSigner: EncryptionKeySigner = {
rawKey: '41e8e8593108eeedcbded883b8af34d2f028710355c57f4c10a056b72486aa04',
}

describe(isBalanceSufficientForSigRetrieval, () => {
it('identifies sufficient balance correctly', () => {
expect(isBalanceSufficientForSigRetrieval(0.009, 0.004)).toBe(false)
expect(isBalanceSufficientForSigRetrieval(0.01, 0)).toBe(true)
expect(isBalanceSufficientForSigRetrieval(0, 0.005)).toBe(true)
})
})

describe(getPhoneNumberIdentifier, () => {
afterEach(() => {
fetchMock.reset()
Expand Down
19 changes: 19 additions & 0 deletions packages/contractkit/src/identity/odis/phone-number-identifier.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getPhoneHash, isE164Number } from '@celo/base/lib/phoneNumbers'
import BigNumber from 'bignumber.js'
import { createHash } from 'crypto'
import debugFactory from 'debug'
import { soliditySha3 } from 'web3-utils'
Expand All @@ -11,6 +12,11 @@ import {
SignMessageResponse,
} from './query'

// ODIS minimum dollar balance for sig retrieval
export const ODIS_MINIMUM_DOLLAR_BALANCE = 0.01
// ODIS minimum celo balance for sig retrieval
export const ODIS_MINIMUM_CELO_BALANCE = 0.005

const debug = debugFactory('kit:odis:phone-number-identifier')
const sha3 = (v: string) => soliditySha3({ type: 'string', value: v })

Expand Down Expand Up @@ -85,3 +91,16 @@ export function getPepperFromThresholdSignature(sigBuf: Buffer) {
.digest('base64')
.slice(0, PEPPER_CHAR_LENGTH)
}

/**
* Check if balance is sufficient for quota retrieval
*/
export function isBalanceSufficientForSigRetrieval(
dollarBalance: BigNumber.Value,
celoBalance: BigNumber.Value
) {
return (
new BigNumber(dollarBalance).isGreaterThanOrEqualTo(ODIS_MINIMUM_DOLLAR_BALANCE) ||
new BigNumber(celoBalance).isGreaterThanOrEqualTo(ODIS_MINIMUM_CELO_BALANCE)
)
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion packages/mobile/src/account/Settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as renderer from 'react-test-renderer'
import Settings from 'src/account/Settings'
import { Screens } from 'src/navigator/Screens'
import { createMockStore, getMockStackScreenProps } from 'test/utils'
import { mockE164Number, mockE164NumberPepper } from 'test/values'

describe('Account', () => {
beforeAll(() => {
Expand All @@ -17,7 +18,16 @@ describe('Account', () => {

it('renders correctly', () => {
const tree = renderer.create(
<Provider store={createMockStore({})}>
<Provider
store={createMockStore({
account: {
e164PhoneNumber: mockE164Number,
},
identity: { e164NumberToSalt: { [mockE164Number]: mockE164NumberPepper } },
stableToken: { balance: '0.00' },
goldToken: { balance: '0.00' },
})}
>
<Settings {...getMockStackScreenProps(Screens.Settings)} />
</Provider>
)
Expand All @@ -28,8 +38,12 @@ describe('Account', () => {
const tree = renderer.create(
<Provider
store={createMockStore({
identity: { e164NumberToSalt: { [mockE164Number]: mockE164NumberPepper } },
stableToken: { balance: '0.00' },
goldToken: { balance: '0.00' },
account: {
devModeActive: true,
e164PhoneNumber: mockE164Number,
},
})}
>
Expand All @@ -38,4 +52,12 @@ describe('Account', () => {
)
expect(tree).toMatchSnapshot()
})
it('renders correctly when verification is not possible', () => {
const tree = renderer.create(
<Provider store={createMockStore({})}>
<Settings {...getMockStackScreenProps(Screens.Settings)} />
</Provider>
)
expect(tree).toMatchSnapshot()
})
})
8 changes: 5 additions & 3 deletions packages/mobile/src/account/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
setRequirePinOnAppOpen,
setSessionId,
} from 'src/app/actions'
import { sessionIdSelector } from 'src/app/selectors'
import { sessionIdSelector, verificationPossibleSelector } from 'src/app/selectors'
import Dialog from 'src/components/Dialog'
import SessionId from 'src/components/SessionId'
import { AVAILABLE_LANGUAGES, TOS_LINK } from 'src/config'
Expand Down Expand Up @@ -70,6 +70,7 @@ interface StateProps {
devModeActive: boolean
analyticsEnabled: boolean
numberVerified: boolean
verificationPossible: boolean
pincodeType: PincodeType
backupCompleted: boolean
requirePinOnAppOpen: boolean
Expand All @@ -91,6 +92,7 @@ const mapStateToProps = (state: RootState): StateProps => {
e164PhoneNumber: state.account.e164PhoneNumber,
analyticsEnabled: state.app.analyticsEnabled,
numberVerified: state.app.numberVerified,
verificationPossible: verificationPossibleSelector(state),
pincodeType: pincodeTypeSelector(state),
requirePinOnAppOpen: state.app.requirePinOnAppOpen,
fornoEnabled: state.web3.fornoMode,
Expand Down Expand Up @@ -303,7 +305,7 @@ export class Account extends React.Component<Props, State> {
}

render() {
const { t, i18n, numberVerified } = this.props
const { t, i18n, numberVerified, verificationPossible } = this.props
const promptFornoModal = this.props.route.params?.promptFornoModal ?? false
const promptConfirmRemovalModal = this.props.route.params?.promptConfirmRemovalModal ?? false
const currentLanguage = AVAILABLE_LANGUAGES.find((l) => l.code === i18n.language)
Expand All @@ -318,7 +320,7 @@ export class Account extends React.Component<Props, State> {
</TouchableWithoutFeedback>
<View style={styles.containerList}>
<SettingsItemTextValue title={t('editProfile')} onPress={this.goToProfile} />
{!numberVerified && (
{!numberVerified && verificationPossible && (
<SettingsItemTextValue title={t('confirmNumber')} onPress={this.goToConfirmNumber} />
)}
<SettingsItemTextValue
Expand Down
Loading

0 comments on commit 606045b

Please sign in to comment.