diff --git a/packages/phone-number-privacy/signer/src/config.ts b/packages/phone-number-privacy/signer/src/config.ts index 0983bf0ef7d..da7251a624d 100644 --- a/packages/phone-number-privacy/signer/src/config.ts +++ b/packages/phone-number-privacy/signer/src/config.ts @@ -39,6 +39,7 @@ interface Config { additionalVerifiedQueryMax: number queryPerTransaction: number minDollarBalance: BigNumber + minEuroBalance: BigNumber minCeloBalance: BigNumber } attestations: { @@ -94,6 +95,8 @@ const config: Config = { queryPerTransaction: toNum(env.QUERY_PER_TRANSACTION) || 2, // Min balance is .01 cUSD minDollarBalance: new BigNumber(env.MIN_DOLLAR_BALANCE || 1e16), + // Min balance is .01 cEUR + minEuroBalance: new BigNumber(env.MIN_DOLLAR_BALANCE || 1e16), // Min balance is .005 CELO minCeloBalance: new BigNumber(env.MIN_DOLLAR_BALANCE || 5e15), }, diff --git a/packages/phone-number-privacy/signer/src/signing/query-quota.ts b/packages/phone-number-privacy/signer/src/signing/query-quota.ts index 7fac37179cb..e0b3973792f 100644 --- a/packages/phone-number-privacy/signer/src/signing/query-quota.ts +++ b/packages/phone-number-privacy/signer/src/signing/query-quota.ts @@ -1,5 +1,5 @@ import { retryAsyncWithBackOffAndTimeout } from '@celo/base' -import { NULL_ADDRESS } from '@celo/contractkit' +import { NULL_ADDRESS, StableToken } from '@celo/contractkit' import { authenticateUser, ErrorMessage, @@ -159,11 +159,15 @@ async function _getQueryQuota(logger: Logger, account: string, hashedPhoneNumber .labels('balances') .startTimer() let cUSDAccountBalance = new BigNumber(0) + let cEURAccountBalance = new BigNumber(0) let celoAccountBalance = new BigNumber(0) await Promise.all([ new Promise((resolve) => { - resolve(getDollarBalance(logger, account, walletAddress)) + resolve(getStableTokenBalance(StableToken.cUSD, logger, account, walletAddress)) + }), + new Promise((resolve) => { + resolve(getStableTokenBalance(StableToken.cEUR, logger, account, walletAddress)) }), new Promise((resolve) => { resolve(getCeloBalance(logger, account, walletAddress)) @@ -171,13 +175,15 @@ async function _getQueryQuota(logger: Logger, account: string, hashedPhoneNumber ]) .then((values) => { cUSDAccountBalance = values[0] as BigNumber - celoAccountBalance = values[1] as BigNumber + cEURAccountBalance = values[1] as BigNumber + celoAccountBalance = values[2] as BigNumber }) .finally(getBalancesMeter) - // Min balance can be in either cUSD or CELO + // Min balance can be in either cUSD, cEUR or CELO if ( cUSDAccountBalance.isGreaterThanOrEqualTo(config.quota.minDollarBalance) || + cEURAccountBalance.isGreaterThanOrEqualTo(config.quota.minEuroBalance) || celoAccountBalance.isGreaterThanOrEqualTo(config.quota.minCeloBalance) ) { Counters.requestsWithUnverifiedAccountWithMinBalance.inc() @@ -185,8 +191,10 @@ async function _getQueryQuota(logger: Logger, account: string, hashedPhoneNumber { account, cUSDAccountBalance, + cEURAccountBalance, celoAccountBalance, minDollarBalance: config.quota.minDollarBalance, + minEuroBalance: config.quota.minEuroBalance, minCeloBalance: config.quota.minCeloBalance, }, 'Account is not verified but meets min balance' @@ -208,8 +216,10 @@ async function _getQueryQuota(logger: Logger, account: string, hashedPhoneNumber logger.trace({ account, cUSDAccountBalance, + cEURAccountBalance, celoAccountBalance, minDollarBalance: config.quota.minDollarBalance, + minEuroBalance: config.quota.minEuroBalance, minCeloBalance: config.quota.minCeloBalance, quota: 0, }) @@ -246,13 +256,18 @@ export async function getTransactionCount(logger: Logger, ...addresses: string[] return res } -export async function getDollarBalance(logger: Logger, ...addresses: string[]): Promise { +export async function getStableTokenBalance( + stableToken: StableToken, + logger: Logger, + ...addresses: string[] +): Promise { return Promise.all( addresses .filter((address) => address !== NULL_ADDRESS) .map((address) => retryAsyncWithBackOffAndTimeout( - async () => (await getContractKit().contracts.getStableToken()).balanceOf(address), + async () => + (await getContractKit().contracts.getStableToken(stableToken)).balanceOf(address), RETRY_COUNT, [], RETRY_DELAY_IN_MS, @@ -266,7 +281,7 @@ export async function getDollarBalance(logger: Logger, ...addresses: string[]): ).then((values) => { logger.trace( { addresses, balances: values.map((bn) => bn.toString()) }, - 'Fetched cusd balances for addresses' + `Fetched ${stableToken} balances for addresses` ) return values.reduce((a, b) => a.plus(b)) }) diff --git a/packages/phone-number-privacy/signer/test/signing/query-quota.test.ts b/packages/phone-number-privacy/signer/test/signing/query-quota.test.ts index bb179b78d03..8a4d557e4bf 100644 --- a/packages/phone-number-privacy/signer/test/signing/query-quota.test.ts +++ b/packages/phone-number-privacy/signer/test/signing/query-quota.test.ts @@ -1,4 +1,5 @@ import { isVerified, rootLogger } from '@celo/phone-number-privacy-common' +import { StableToken } from '@celo/contractkit' import BigNumber from 'bignumber.js' import allSettled from 'promise.allsettled' import { @@ -118,16 +119,48 @@ describe(getRemainingQueryCount, () => { totalQuota: 0, }) }) - it('Calculates remaining query count for unverified account with only cUSD balance', async () => { + it('Calculates remaining query count for unverified account with cUSD balance', async () => { const contractKitVerifiedNoTx = createMockContractKit( { [ContractRetrieval.getAttestations]: createMockAttestation(0, 0), - [ContractRetrieval.getStableToken]: createMockToken(new BigNumber(200000000000000000)), + [ContractRetrieval.getStableToken]: createMockToken(new BigNumber(0)), [ContractRetrieval.getGoldToken]: createMockToken(new BigNumber(0)), [ContractRetrieval.getAccounts]: createMockAccounts('0x0'), }, createMockWeb3(0) ) + contractKitVerifiedNoTx.contracts[ContractRetrieval.getStableToken] = jest.fn( + (stableToken: StableToken) => { + return stableToken === StableToken.cUSD + ? createMockToken(new BigNumber(200000000000000000)) + : createMockToken(new BigNumber(0)) + } + ) + mockPerformedQueryCount.mockImplementation(() => new Promise((resolve) => resolve(1))) + mockIsVerified.mockReturnValue(false) + mockGetContractKit.mockImplementation(() => contractKitVerifiedNoTx) + expect(await getRemainingQueryCount(rootLogger, mockAccount, mockPhoneNumber)).toEqual({ + performedQueryCount: 1, + totalQuota: 10, + }) + }) + it('Calculates remaining query count for unverified account with cEUR balance', async () => { + const contractKitVerifiedNoTx = createMockContractKit( + { + [ContractRetrieval.getAttestations]: createMockAttestation(0, 0), + [ContractRetrieval.getStableToken]: createMockToken(new BigNumber(0)), + [ContractRetrieval.getGoldToken]: createMockToken(new BigNumber(0)), + [ContractRetrieval.getAccounts]: createMockAccounts('0x0'), + }, + createMockWeb3(0) + ) + contractKitVerifiedNoTx.contracts[ContractRetrieval.getStableToken] = jest.fn( + (stableToken: StableToken) => { + return stableToken === StableToken.cEUR + ? createMockToken(new BigNumber(200000000000000000)) + : createMockToken(new BigNumber(0)) + } + ) mockPerformedQueryCount.mockImplementation(() => new Promise((resolve) => resolve(1))) mockIsVerified.mockReturnValue(false) mockGetContractKit.mockImplementation(() => contractKitVerifiedNoTx)