Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Claimables [PR #1]: updates to query, types, utils, wallet screen rendering logic + wallet screen components #6140

Merged
merged 3 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 const Claimable = 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 @@ -12,7 +12,7 @@ const AnimatedImgixImage = Animated.createAnimatedComponent(Image);
const TokenFamilyHeaderAnimationDuration = 200;
const TokenFamilyHeaderHeight = 48;

const ClaimablesListHeader = ({ total }: { total: string }) => {
export const ClaimablesListHeader = React.memo(function ClaimablesListHeader({ total }: { total: string }) {
const { colors } = useTheme();
const { isClaimablesOpen, toggleOpenClaimables } = useOpenClaimables();

Expand Down Expand Up @@ -84,10 +84,4 @@ const ClaimablesListHeader = ({ total }: { total: string }) => {
</Box>
</ButtonPressAnimation>
);
};

ClaimablesListHeader.animationDuration = TokenFamilyHeaderAnimationDuration;

ClaimablesListHeader.height = TokenFamilyHeaderHeight;

export default ClaimablesListHeader;
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import { RemoteCardCarousel } from '@/components/cards/remote-cards';
import WrappedCollectiblesHeader from '../WrappedCollectiblesHeader';
import NFTLoadingSkeleton from '../NFTLoadingSkeleton';
import { NFTEmptyState } from '../NFTEmptyState';
import Claimable from '../Claimable';
import ClaimablesListHeader from '../ClaimablesListHeader';
import { ClaimablesListHeader } from '../ClaimablesListHeader';
import { Claimable } from '../Claimable';

function rowRenderer(type: CellType, { uid }: { uid: string }, _: unknown, extendedState: ExtendedState) {
const data = extendedState.additionalData[uid];
Expand Down 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;
}
8 changes: 5 additions & 3 deletions src/resources/addys/claimables/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { CLAIMABLES, useExperimentalFlag } from '@/config';
import { IS_TEST } from '@/env';
import { SUPPORTED_CHAIN_IDS } from '@/chains';

const addysHttp = new RainbowFetchClient({
baseURL: 'https://addys.p.rainbow.me/v3',
export const ADDYS_BASE_URL = 'https://addys.p.rainbow.me/v3';

export const addysHttp = new RainbowFetchClient({
baseURL: ADDYS_BASE_URL,
headers: {
Authorization: `Bearer ${ADDYS_API_KEY}`,
},
Expand All @@ -30,7 +32,7 @@ export type ClaimablesArgs = {
// Query Key

export const claimablesQueryKey = ({ address, currency }: ClaimablesArgs) =>
createQueryKey('claimables', { address, currency }, { persisterVersion: 1 });
createQueryKey('claimables', { address, currency }, { persisterVersion: 2 });

type ClaimablesQueryKey = ReturnType<typeof claimablesQueryKey>;

Expand Down
72 changes: 64 additions & 8 deletions src/resources/addys/claimables/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId } from '@rainbow-me/swaps';
import { Address } from 'viem';
import { AddysAsset, AddysConsolidatedError, AddysResponseStatus } from '../types';
import { ChainId } from '@/chains/types';

interface Colors {
primary: string;
Expand Down Expand Up @@ -28,20 +28,33 @@ interface DApp {
colors: Colors;
}

type ClaimableType = 'transaction' | 'sponsored';

export interface AddysClaimable {
interface AddysBaseClaimable {
name: string;
unique_id: string;
type: ClaimableType;
type: string;
network: ChainId;
asset: AddysAsset;
amount: string;
dapp: DApp;
claim_action_type?: string | null;
}

interface AddysTransactionClaimable extends AddysBaseClaimable {
claim_action_type: 'transaction';
claim_action: ClaimActionTransaction[];
}

interface AddysSponsoredClaimable extends AddysBaseClaimable {
claim_action_type: 'sponsored';
claim_action: ClaimActionSponsored[];
}

interface AddysUnsupportedClaimable extends AddysBaseClaimable {
claim_action_type?: 'unknown' | null;
claim_action?: ClaimAction[];
}

export type AddysClaimable = AddysTransactionClaimable | AddysSponsoredClaimable | AddysUnsupportedClaimable;

interface ConsolidatedClaimablesPayloadResponse {
claimables: AddysClaimable[];
}
Expand All @@ -61,8 +74,13 @@ export interface ConsolidatedClaimablesResponse {
payload: ConsolidatedClaimablesPayloadResponse;
}

// will add more attributes as needed
export interface Claimable {
interface BaseClaimable {
asset: {
iconUrl: string;
name: string;
symbol: string;
};
chainId: ChainId;
name: string;
uniqueId: string;
iconUrl: string;
Expand All @@ -71,3 +89,41 @@ export interface Claimable {
nativeAsset: { amount: string; display: string };
};
}

export interface TransactionClaimable extends BaseClaimable {
type: 'transaction';
action: { to: Address; data: string };
}

export interface SponsoredClaimable extends BaseClaimable {
type: 'sponsored';
action: { url: string; method: string };
}

export type Claimable = TransactionClaimable | SponsoredClaimable;

interface ClaimTransactionStatus {
network: ChainId;
transaction_hash: string;
explorer_url: string;
sponsored_status: string;
}

interface ClaimPayloadResponse {
success: boolean;
claimable: Claimable | null;
claim_transaction_status: ClaimTransactionStatus | null;
}

interface ClaimMetadataResponse {
address: string;
chain_id: ChainId;
currency: string;
claim_type: string;
error: string;
}

export interface ClaimResponse {
metadata: ClaimMetadataResponse;
payload: ClaimPayloadResponse;
}
53 changes: 43 additions & 10 deletions src/resources/addys/claimables/utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
import { NativeCurrencyKey } from '@/entities';
import { AddysClaimable, Claimable } from './types';
import { convertRawAmountToBalance, convertRawAmountToNativeDisplay, greaterThan, lessThan } from '@/helpers/utilities';
import { convertRawAmountToBalance, convertRawAmountToNativeDisplay, greaterThan } from '@/helpers/utilities';

export const parseClaimables = (claimables: AddysClaimable[], currency: NativeCurrencyKey): Claimable[] => {
return claimables
.map(claimable => ({
name: claimable.name,
uniqueId: claimable.unique_id,
iconUrl: claimable.dapp.icon_url,
value: {
claimAsset: convertRawAmountToBalance(claimable.amount, claimable.asset),
nativeAsset: convertRawAmountToNativeDisplay(claimable.amount, claimable.asset.decimals, claimable.asset.price.value, currency),
},
}))
.map(claimable => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be more efficient to do one for...of loop where you sort, filter, and parse instead of mapping, filtering, and then sorting all in different iterations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true but users will only have a handful of claimables so doesn't matter too much

if (
!(claimable.claim_action_type === 'transaction' || claimable.claim_action_type === 'sponsored') ||
!claimable.claim_action?.length
) {
return undefined;
}

const baseClaimable = {
asset: {
iconUrl: claimable.asset.icon_url,
name: claimable.asset.name,
symbol: claimable.asset.symbol,
},
chainId: claimable.network,
name: claimable.name,
uniqueId: claimable.unique_id,
iconUrl: claimable.dapp.icon_url,
value: {
claimAsset: convertRawAmountToBalance(claimable.amount, claimable.asset),
nativeAsset: convertRawAmountToNativeDisplay(claimable.amount, claimable.asset.decimals, claimable.asset.price.value, currency),
},
};

if (claimable.claim_action_type === 'transaction') {
return {
...baseClaimable,
type: 'transaction' as const,
action: {
to: claimable.claim_action[0].address_to,
data: claimable.claim_action[0].calldata,
},
};
} else if (claimable.claim_action_type === 'sponsored') {
return {
...baseClaimable,
type: 'sponsored' as const,
action: { method: claimable.claim_action[0].method, url: claimable.claim_action[0].url },
};
}
})
.filter((c): c is Claimable => !!c)
.sort((a, b) => (greaterThan(a.value.claimAsset.amount ?? '0', b.value.claimAsset.amount ?? '0') ? -1 : 1));
};
Loading
Loading