Skip to content

Commit c57aae8

Browse files
Merge pull request #448 from HathorNetwork/chore/v0.27.0-rc2
release: v0.27.0-rc.2
2 parents d68334f + d40d1c7 commit c57aae8

File tree

5 files changed

+84
-7
lines changed

5 files changed

+84
-7
lines changed

src/actions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const types = {
135135
NETWORKSETTINGS_UPDATE_INVALID: 'NETWORKSETTINGS_UPDATE_INVALID',
136136
/* It indicates the update request has failed. */
137137
NETWORKSETTINGS_UPDATE_FAILURE: 'NETWORK_SETTINGS_UPDATE_FAILURE',
138-
/* It updates the redux state of network settings status */
138+
/* It updates the redux state of network settings status. */
139139
NETWORKSETTINGS_UPDATE_READY: 'NETWORK_SETTINGS_UPDATE_READY',
140140
};
141141

src/constants.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ export const WALLET_SERVICE_FEATURE_TOGGLE = 'wallet-service-mobile.rollout';
158158
export const PUSH_NOTIFICATION_FEATURE_TOGGLE = 'push-notification.rollout';
159159
export const WALLET_CONNECT_FEATURE_TOGGLE = 'wallet-connect-mobile.rollout';
160160
export const NETWORK_SETTINGS_FEATURE_TOGGLE = 'network-settings.rollout';
161+
export const NANO_CONTRACT_FEATURE_TOGGLE = 'nano-contract.rollout';
161162

162163
/**
163164
* Default feature toggle values.
@@ -172,6 +173,7 @@ export const FEATURE_TOGGLE_DEFAULTS = {
172173
[PUSH_NOTIFICATION_FEATURE_TOGGLE]: false,
173174
[WALLET_CONNECT_FEATURE_TOGGLE]: false,
174175
[NETWORK_SETTINGS_FEATURE_TOGGLE]: false,
176+
[NANO_CONTRACT_FEATURE_TOGGLE]: false,
175177
};
176178

177179
// Project id configured in https://walletconnect.com
@@ -244,3 +246,11 @@ export const HTTP_REQUEST_TIMEOUT = 3000;
244246
* Any network that is not mainnet or testnet should be a privatenet.
245247
*/
246248
export const NETWORK_PRIVATENET = 'privatenet';
249+
250+
/**
251+
* The following constants are used on a progressive retry mechanism.
252+
* @see `src/sagas/helper.js@progressiveRetryRequest`
253+
*/
254+
export const MAX_RETRIES = 8;
255+
export const INITIAL_RETRY_LATENCY = 300; // ms
256+
export const LATENCY_MULTIPLIER = 30; // multiplier per iteration

src/sagas/helpers.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
take,
1414
call,
1515
select,
16+
delay,
1617
} from 'redux-saga/effects';
1718
import { t } from 'ttag';
1819
import axiosWrapperCreateRequestInstance from '@hathor/wallet-lib/lib/api/axiosWrapper';
@@ -28,6 +29,9 @@ import {
2829
WALLET_SERVICE_FEATURE_TOGGLE,
2930
WALLET_SERVICE_REQUEST_TIMEOUT,
3031
networkSettingsKeyMap,
32+
MAX_RETRIES,
33+
INITIAL_RETRY_LATENCY,
34+
LATENCY_MULTIPLIER,
3135
} from '../constants';
3236
import { STORE } from '../store';
3337

@@ -275,3 +279,53 @@ export function getNetworkSettings(state) {
275279
// has precedence, once it indicates a custom network.
276280
return STORE.getItem(networkSettingsKeyMap.networkSettings) ?? state.networkSettings;
277281
}
282+
283+
/**
284+
* A request abstraction that applies a progressive retry strategy.
285+
* One can define how many retries it should make or use the default value.
286+
*
287+
* @param {Promise<any>} request The async callback function to be executed.
288+
* @param {number} maxRetries The max retries allowed, with default value.
289+
* Notice this param should be at least 1 to make sense.
290+
* @returns {any} A success object from the request.
291+
* @throws An error after retries exhausted.
292+
*
293+
* @example
294+
* yield call(progressiveRetryRequest, async () => asyncFn());
295+
* // use default maxRetries
296+
*
297+
* @example
298+
* yield call(progressiveRetryRequest, async () => asyncFn(), 3);
299+
* // use custom maxRetries equal to 3
300+
*/
301+
export function* progressiveRetryRequest(request, maxRetries = MAX_RETRIES) {
302+
let lastError = null;
303+
304+
// eslint-disable-next-line no-plusplus
305+
for (let i = 0; i <= maxRetries; i++) {
306+
try {
307+
// return if success
308+
return yield call(request)
309+
} catch (error) {
310+
lastError = error;
311+
}
312+
313+
// skip delay for last call
314+
if (i === maxRetries) {
315+
continue;
316+
}
317+
318+
// attempt 0: 300ms
319+
// attempt 1: 330ms
320+
// attempt 2: 420ms
321+
// attempt 3: 570ms
322+
// attempt 4: 780ms
323+
// attempt 5: 1050ms
324+
// attempt 6: 1380ms
325+
// attempt 7: 1770ms
326+
yield delay(INITIAL_RETRY_LATENCY + LATENCY_MULTIPLIER * (i * i));
327+
}
328+
329+
// throw last error after retries exhausted
330+
throw lastError;
331+
}

src/sagas/tokens.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export function* fetchTokenMetadata({ tokenId }) {
243243
}
244244
}
245245

246-
export function* fetchTokenData(tokenId) {
246+
export function* fetchTokenData(tokenId, force = false) {
247247
const fetchBalanceResponse = yield call(
248248
dispatchAndWait,
249249
tokenFetchBalanceRequested(tokenId),
@@ -256,7 +256,7 @@ export function* fetchTokenData(tokenId) {
256256
);
257257
const fetchHistoryResponse = yield call(
258258
dispatchAndWait,
259-
tokenFetchHistoryRequested(tokenId),
259+
tokenFetchHistoryRequested(tokenId, force),
260260
specificTypeAndPayload(types.TOKEN_FETCH_HISTORY_SUCCESS, {
261261
tokenId,
262262
}),

src/sagas/wallet.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import {
7373
getRegisteredTokens,
7474
getNetworkSettings,
7575
getRegisteredTokenUids,
76+
progressiveRetryRequest,
7677
} from './helpers';
7778
import { setKeychainPin } from '../utils';
7879

@@ -287,7 +288,7 @@ export function* startWallet(action) {
287288

288289
/**
289290
* This saga will load both HTR and DEFAULT_TOKEN (if they are different)
290-
* and dispatch actions to asynchronously load all registered tokens.
291+
* and dispatch actions to asynchronously load all registered tokens forcefully.
291292
*
292293
* Will throw an error if the download fails for any token.
293294
* @returns {string[]} Array of token uid
@@ -298,9 +299,12 @@ export function* loadTokens() {
298299

299300
// fetchTokenData will throw an error if the download failed, we should just
300301
// let it crash as throwing an error is the default behavior for loadTokens
301-
yield call(fetchTokenData, htrUid);
302+
yield call(fetchTokenData, htrUid, true);
302303

303304
if (customTokenUid !== htrUid) {
305+
// custom tokens doesn't need to be forced to download because its history status
306+
// will be marked as invalidated, and history will get requested the next time a user
307+
// enters the history screen.
304308
yield call(fetchTokenData, customTokenUid);
305309
}
306310

@@ -485,9 +489,17 @@ export function* handleTx(action) {
485489
return acc;
486490
}, [{}, new Set([])],);
487491

488-
const txWalletAddresses = yield call(wallet.checkAddressesMine.bind(wallet), [...txAddresses]);
489-
const tokensToDownload = [];
492+
let txWalletAddresses = null;
493+
try {
494+
const request = async () => wallet.checkAddressesMine.bind(wallet)([...txAddresses]);
495+
txWalletAddresses = yield call(progressiveRetryRequest, request);
496+
} catch (error) {
497+
// Emmit a fatal error feedback to user and halts tx processing.
498+
yield put(onExceptionCaptured(error, true));
499+
return;
500+
}
490501

502+
const tokensToDownload = [];
491503
for (const [tokenUid, addresses] of Object.entries(tokenAddressesMap)) {
492504
for (const [address] of addresses.entries()) {
493505
// txWalletAddresses should always have the address we requested, but we should double check
@@ -628,6 +640,7 @@ export function* onWalletReloadData() {
628640
}
629641

630642
try {
643+
// Here we force the download of tokens history
631644
const registeredTokens = yield call(loadTokens);
632645

633646
const customTokenUid = DEFAULT_TOKEN.uid;

0 commit comments

Comments
 (0)