Skip to content

Commit

Permalink
Merge 2226d95 into 9a3aafb
Browse files Browse the repository at this point in the history
  • Loading branch information
benisgold authored Sep 25, 2024
2 parents 9a3aafb + 2226d95 commit 8f92d46
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 46 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/)

## [1.9.40] (https://github.com/rainbow-me/rainbow/releases/tag/v1.9.40)

### Fixed
### Fixed

- Fixed a bug with speed up and cancel (#6133)

Expand Down
9 changes: 7 additions & 2 deletions src/components/asset-list/RecyclerAssetList2/Claimable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import { useClaimables } from '@/resources/addys/claimables/query';
import { FasterImageView } from '@candlefinance/faster-image';
import { ButtonPressAnimation } from '@/components/animations';
import { deviceUtils } from '@/utils';
import Routes from '@/navigation/routesNames';
import { ExtendedState } from './core/RawRecyclerList';

export default function Claimable({ uniqueId }: { uniqueId: string }) {
export default React.memo(function Claimable({ uniqueId, extendedState }: { uniqueId: string; extendedState: ExtendedState }) {
const { accountAddress, nativeCurrency } = useAccountSettings();
const { navigate } = extendedState;

const { data = [] } = useClaimables(
{
address: accountAddress,
Expand All @@ -25,6 +29,7 @@ export default function Claimable({ uniqueId }: { uniqueId: string }) {
return (
<Box
as={ButtonPressAnimation}
// onPress={() => navigate(Routes.CLAIM_CLAIMABLE_PANEL, { claimable })}
scaleTo={0.96}
paddingHorizontal="20px"
justifyContent="space-between"
Expand Down Expand Up @@ -68,4 +73,4 @@ export default function Claimable({ uniqueId }: { uniqueId: string }) {
</Box>
</Box>
);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ ClaimablesListHeader.animationDuration = TokenFamilyHeaderAnimationDuration;

ClaimablesListHeader.height = TokenFamilyHeaderHeight;

export default ClaimablesListHeader;
export default React.memo(ClaimablesListHeader);
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function rowRenderer(type: CellType, { uid }: { uid: string }, _: unknown, exten
case CellType.CLAIMABLE: {
const { uniqueId } = data as ClaimableExtraData;

return <Claimable uniqueId={uniqueId} />;
return <Claimable uniqueId={uniqueId} extendedState={extendedState} />;
}

case CellType.LOADING_ASSETS:
Expand Down
32 changes: 18 additions & 14 deletions src/helpers/buildWalletSections.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { createSelector } from 'reselect';
import { buildBriefCoinsList, buildBriefUniqueTokenList } from './assets';
import { NativeCurrencyKey, ParsedAddressAsset } from '@/entities';
import { queryClient } from '@/react-query';
import { positionsQueryKey } from '@/resources/defi/PositionsQuery';
import store from '@/redux/store';
import { ClaimableExtraData, PositionExtraData } from '@/components/asset-list/RecyclerAssetList2/core/ViewTypes';
import { DEFI_POSITIONS, CLAIMABLES, ExperimentalValue } from '@/config/experimental';
import { RainbowPositions } from '@/resources/defi/types';
import { claimablesQueryKey } from '@/resources/addys/claimables/query';
import { Claimable } from '@/resources/addys/claimables/types';
import { add, convertAmountToNativeDisplay } from './utilities';
import { RainbowConfig } from '@/model/remoteConfig';
Expand Down Expand Up @@ -60,20 +57,24 @@ const isFetchingNftsSelector = (state: any) => state.isFetchingNfts;
const listTypeSelector = (state: any) => state.listType;
const remoteConfigSelector = (state: any) => state.remoteConfig;
const experimentalConfigSelector = (state: any) => state.experimentalConfig;
const positionsSelector = (state: any) => state.positions;
const claimablesSelector = (state: any) => state.claimables;

const buildBriefWalletSections = (
balanceSectionData: any,
uniqueTokenFamiliesSection: any,
remoteConfig: RainbowConfig,
experimentalConfig: Record<string, ExperimentalValue>
experimentalConfig: Record<string, ExperimentalValue>,
positions: RainbowPositions | undefined,
claimables: Claimable[] | undefined
) => {
const { balanceSection, isEmpty, isLoadingUserAssets } = balanceSectionData;

const positionsEnabled = experimentalConfig[DEFI_POSITIONS] && !IS_TEST;
const claimablesEnabled = (remoteConfig.claimables || experimentalConfig[CLAIMABLES]) && !IS_TEST;

const positionSection = positionsEnabled ? withPositionsSection(isLoadingUserAssets) : [];
const claimablesSection = claimablesEnabled ? withClaimablesSection(isLoadingUserAssets) : [];
const positionSection = positionsEnabled ? withPositionsSection(positions, isLoadingUserAssets) : [];
const claimablesSection = claimablesEnabled ? withClaimablesSection(claimables, isLoadingUserAssets) : [];
const sections = [balanceSection, claimablesSection, positionSection, uniqueTokenFamiliesSection];

const filteredSections = sections.filter(section => section.length !== 0).flat(1);
Expand All @@ -84,10 +85,7 @@ const buildBriefWalletSections = (
};
};

const withPositionsSection = (isLoadingUserAssets: boolean) => {
const { accountAddress: address, nativeCurrency: currency } = store.getState().settings;
const positionsObj: RainbowPositions | undefined = queryClient.getQueryData(positionsQueryKey({ address, currency }));

const withPositionsSection = (positionsObj: RainbowPositions | undefined, isLoadingUserAssets: boolean) => {
const result: PositionExtraData[] = [];
const sortedPositions = positionsObj?.positions?.sort((a, b) => (a.totals.totals.amount > b.totals.totals.amount ? -1 : 1));
sortedPositions?.forEach((position, index) => {
Expand Down Expand Up @@ -118,9 +116,8 @@ const withPositionsSection = (isLoadingUserAssets: boolean) => {
return [];
};

const withClaimablesSection = (isLoadingUserAssets: boolean) => {
const { accountAddress: address, nativeCurrency: currency } = store.getState().settings;
const claimables: Claimable[] | undefined = queryClient.getQueryData(claimablesQueryKey({ address, currency }));
const withClaimablesSection = (claimables: Claimable[] | undefined, isLoadingUserAssets: boolean) => {
const { nativeCurrency: currency } = store.getState().settings;

const result: ClaimableExtraData[] = [];
let totalNativeValue = '0';
Expand Down Expand Up @@ -285,6 +282,13 @@ const briefBalanceSectionSelector = createSelector(
);

export const buildBriefWalletSectionsSelector = createSelector(
[briefBalanceSectionSelector, (state: any) => briefUniqueTokenDataSelector(state), remoteConfigSelector, experimentalConfigSelector],
[
briefBalanceSectionSelector,
(state: any) => briefUniqueTokenDataSelector(state),
remoteConfigSelector,
experimentalConfigSelector,
positionsSelector,
claimablesSelector,
],
buildBriefWalletSections
);
8 changes: 8 additions & 0 deletions src/hooks/useWalletSectionsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import useNftSort from './useNFTsSortBy';
import useWalletsWithBalancesAndNames from './useWalletsWithBalancesAndNames';
import { useRemoteConfig } from '@/model/remoteConfig';
import { RainbowContext } from '@/helpers/RainbowContext';
import { usePositions } from '@/resources/defi/PositionsQuery';
import { useClaimables } from '@/resources/addys/claimables/query';

export default function useWalletSectionsData({
type,
Expand All @@ -35,6 +37,8 @@ export default function useWalletSectionsData({
address: accountAddress,
sortBy: nftSort,
});
const { data: positions } = usePositions({ address: accountAddress, currency: nativeCurrency });
const { data: claimables } = useClaimables({ address: accountAddress, currency: nativeCurrency });

const walletsWithBalancesAndNames = useWalletsWithBalancesAndNames();

Expand Down Expand Up @@ -76,6 +80,8 @@ export default function useWalletSectionsData({
nftSort,
remoteConfig,
experimentalConfig,
positions,
claimables,
};

const { briefSectionsData, isEmpty } = buildBriefWalletSectionsSelector(accountInfo);
Expand Down Expand Up @@ -110,6 +116,8 @@ export default function useWalletSectionsData({
nftSort,
remoteConfig,
experimentalConfig,
positions,
claimables,
]);
return walletSections;
}
45 changes: 45 additions & 0 deletions src/rapsV2/actions/claimTransactionClaimableAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ActionProps } from '../references';
import { sendTransaction } from '@/model/wallet';
import { getProvider } from '@/handlers/web3';
import { RainbowError } from '@/logger';
import { addNewTransaction } from '@/state/pendingTransactions';
import { NewTransaction } from '@/entities';
import { chainsName } from '@/chains';

export async function claimTransactionClaimable({ parameters, wallet }: ActionProps<'claimTransactionClaimableAction'>) {
// will uncomment actual logic in follow-up PR, don't worry about it for now
return { nonce: undefined, hash: undefined };

// const { claimTx } = parameters;

// const provider = getProvider({ chainId: claimTx.chainId });
// const result = await sendTransaction({ transaction: claimTx, existingWallet: wallet, provider });

// if (!result?.result || !!result.error || !result.result.hash) {
// throw new RainbowError('[CLAIM-TRANSACTION-CLAIMABLE]: failed to execute claim transaction');
// }

// const transaction = {
// amount: result.result.value.toString(),
// gasLimit: result.result.gasLimit,
// from: result.result.from ?? null,
// to: result.result.to ?? null,
// chainId: result.result.chainId,
// hash: result.result.hash,
// network: chainsName[result.result.chainId],
// status: 'pending',
// type: 'send',
// nonce: result.result.nonce,
// } satisfies NewTransaction;

// addNewTransaction({
// address: claimTx.from,
// chainId: claimTx.chainId,
// transaction,
// });

// return {
// nonce: result.result.nonce,
// hash: result.result.hash,
// };
}
1 change: 1 addition & 0 deletions src/rapsV2/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { claimTransactionClaimable } from './claimTransactionClaimableAction';
13 changes: 13 additions & 0 deletions src/rapsV2/claimTransactionClaimableRap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createNewAction, createNewRap } from './common';
import { RapAction, RapParameters } from './references';

export async function createClaimTransactionClaimableRap(parameters: Extract<RapParameters, { type: 'claimTransactionClaimableRap' }>) {
let actions: RapAction<'claimTransactionClaimableAction'>[] = [];

const claim = createNewAction('claimTransactionClaimableAction', parameters.claimTransactionClaimableActionParameters);
actions = actions.concat(claim);

// create the overall rap
const newRap = createNewRap(actions);
return newRap;
}
20 changes: 20 additions & 0 deletions src/rapsV2/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { RapAction, RapActionParameterMap, RapActionTypes } from './references';

export interface RapActionTransaction {
hash: string | null;
}

export function createNewAction<T extends RapActionTypes>(type: T, parameters: RapActionParameterMap[T]): RapAction<T> {
const newAction = {
parameters,
transaction: { confirmed: null, hash: null },
type,
};
return newAction;
}

export function createNewRap<T extends RapActionTypes>(actions: RapAction<T>[]) {
return {
actions,
};
}
121 changes: 121 additions & 0 deletions src/rapsV2/execute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-async-promise-executor */
/* eslint-disable no-promise-executor-return */
import { Signer } from '@ethersproject/abstract-signer';

import { RainbowError, logger } from '@/logger';

import { ActionProps, RapResponse, Rap, RapAction, RapActionResponse, RapActionTypes, RapParameters } from './references';
import { createClaimTransactionClaimableRap } from './claimTransactionClaimableRap';
import { claimTransactionClaimable } from './actions/claimTransactionClaimableAction';
import { delay } from '@/utils/delay';

// get the rap by type
export function createRap(parameters: RapParameters): Promise<{ actions: RapAction<RapActionTypes>[] }> {
switch (parameters.type) {
case 'claimTransactionClaimableRap':
return createClaimTransactionClaimableRap(parameters);
default:
return Promise.resolve({ actions: [] });
}
}

// get the action executable by type
function getActionExecutableByType<T extends RapActionTypes>(type: T, props: ActionProps<T>) {
switch (type) {
case 'claimTransactionClaimableAction':
return () => claimTransactionClaimable(props);
default:
throw new RainbowError(`[rapsV2/execute]: T - unknown action type ${type}`);
}
}

// executes a single action in the rap
// if the action executes a tx on-chain, it will return the nonce it used
// if an error occurs, we return the error message
export async function executeAction<T extends RapActionTypes>({
action,
wallet,
rap,
nonceToUse,
rapName,
}: {
action: RapAction<T>;
wallet: Signer;
rap: Rap;
nonceToUse: number | undefined;
rapName: string;
}): Promise<RapActionResponse> {
const { type, parameters } = action;
try {
const actionProps = {
wallet,
currentRap: rap,
parameters,
nonceToUse,
};
const { nonce, hash } = await getActionExecutableByType<T>(type, actionProps)();
return { nonce, errorMessage: null, hash };
} catch (error) {
logger.error(new RainbowError(`[rapsV2/execute]: ${rapName} - error execute action`), {
message: (error as Error)?.message,
});
return { nonce: null, errorMessage: String(error), hash: null };
}
}

function getRapFullName<T extends RapActionTypes>(actions: RapAction<T>[]) {
const actionTypes = actions.map(action => action.type);
return actionTypes.join(' + ');
}

const waitForNodeAck = async (hash: string, provider: Signer['provider']): Promise<void> => {
return new Promise(async resolve => {
const tx = await provider?.getTransaction(hash);
// This means the node is aware of the tx, we're good to go
if ((tx && tx.blockNumber === null) || (tx && tx?.blockNumber && tx?.blockNumber > 0)) {
resolve();
} else {
// Wait for 1 second and try again
await delay(1000);
return waitForNodeAck(hash, provider);
}
});
};

// goes through each action in the rap and executes it
// if an action executes a tx on-chain, increment the nonceToUse for the next tx
// if an action fails, it will return the error message
const executeRap = async (wallet: Signer, rap: Rap): Promise<RapResponse> => {
const { actions } = rap;
const rapName = getRapFullName(rap.actions);
let nonceToUse: number | undefined;

while (actions.length) {
const action = actions.shift();

if (!action) break;

const { nonce, errorMessage, hash } = await executeAction({
action,
wallet,
rap,
nonceToUse,
rapName,
});

if (errorMessage) return { errorMessage };

if (typeof nonce === 'number') {
actions.length >= 1 && hash && (await waitForNodeAck(hash, wallet.provider));
nonceToUse = nonce + 1;
}
}

return { errorMessage: null };
};

export async function walletExecuteRap(wallet: Signer, rapParameters: RapParameters): Promise<RapResponse> {
const rap = await createRap(rapParameters);
return executeRap(wallet, rap);
}
Loading

0 comments on commit 8f92d46

Please sign in to comment.