Skip to content

Commit 9ebf9e4

Browse files
authored
feat: Adding support to send ERC-1155 tokens (#20153)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Adding support to send ERC-1155 tokens ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: #18923 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** TODO ## **Pre-merge author checklist** - [X] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
1 parent 174aa4c commit 9ebf9e4

File tree

13 files changed

+240
-34
lines changed

13 files changed

+240
-34
lines changed

app/components/Views/confirmations/components/hero-nft/hero-nft.test.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,42 @@ describe('HeroNft', () => {
9393
collectible: mockNft,
9494
});
9595
});
96+
97+
it('renders NFT image correctly when image is defined in collection.imageUrl', () => {
98+
const { getByText, getByTestId } = renderWithProvider(<HeroNft />, {
99+
state: merge({}, MOCK_STATE_NFT, {
100+
engine: {
101+
backgroundState: {
102+
NftController: {
103+
allNfts: {
104+
[MOCK_ADDRESS_1.toLowerCase()]: {
105+
'0x1': [
106+
{
107+
...mockNft,
108+
image: undefined,
109+
collection: { imageUrl: 'testURI//:333' },
110+
},
111+
],
112+
},
113+
},
114+
},
115+
},
116+
},
117+
}),
118+
});
119+
120+
expect(getByTestId('nft-image')).toBeDefined();
121+
expect(getByTestId('network-avatar-image')).toBeDefined();
122+
expect(getByText('Test Dapp NFTs')).toBeDefined();
123+
expect(getByText('#12345')).toBeDefined();
124+
125+
fireEvent.press(getByTestId('nft-image'));
126+
expect(mockNavigate).toHaveBeenCalledWith('NftDetailsFullImage', {
127+
collectible: {
128+
...mockNft,
129+
image: undefined,
130+
collection: { imageUrl: 'testURI//:333' },
131+
},
132+
});
133+
});
96134
});

app/components/Views/confirmations/components/hero-nft/hero-nft.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ const NftImageAndNetworkBadge = ({
3030
const { styles } = useStyles(styleSheet, {});
3131
const { networkName, networkImage } = useNetworkInfo(chainId);
3232

33-
const { image, tokenId } = nft ?? {};
34-
const showPlaceholder = !nft || !chainId?.length || !image;
33+
const {
34+
image,
35+
tokenId,
36+
collection: { imageUrl } = { imageUrl: undefined },
37+
} = nft ?? { collection: { imageUrl: undefined } };
38+
const showPlaceholder = !nft || !chainId?.length || (!image && !imageUrl);
3539

3640
const onPress = useCallback(() => {
3741
navigation.navigate('NftDetailsFullImage', {
@@ -67,7 +71,10 @@ const NftImageAndNetworkBadge = ({
6771
/>
6872
}
6973
>
70-
<CollectibleMedia collectible={nft} style={styles.noImagePlaceholder} />
74+
<CollectibleMedia
75+
collectible={{ ...nft, image: image ?? imageUrl ?? '' }}
76+
style={styles.noImagePlaceholder}
77+
/>
7178
</BadgeWrapper>
7279
</TouchableOpacity>
7380
);

app/components/Views/confirmations/components/info-root/info-root.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const ConfirmationInfoComponentMap = {
4747
case TransactionType.simpleSend:
4848
case TransactionType.tokenMethodTransfer:
4949
case TransactionType.tokenMethodTransferFrom:
50+
case TransactionType.tokenMethodSafeTransferFrom:
5051
return Transfer;
5152
case TransactionType.deployContract:
5253
return ContractDeployment;

app/components/Views/confirmations/constants/confirmations.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const REDESIGNED_TRANSACTION_TYPES = [
2626
TransactionType.tokenMethodSetApprovalForAll,
2727
TransactionType.tokenMethodTransfer,
2828
TransactionType.tokenMethodTransferFrom,
29+
TransactionType.tokenMethodSafeTransferFrom,
2930
];
3031

3132
export const REDESIGNED_APPROVE_TYPES = [
@@ -38,6 +39,7 @@ export const REDESIGNED_TRANSFER_TYPES = [
3839
TransactionType.simpleSend,
3940
TransactionType.tokenMethodTransfer,
4041
TransactionType.tokenMethodTransferFrom,
42+
TransactionType.tokenMethodSafeTransferFrom,
4143
];
4244

4345
export const REDESIGNED_CONTRACT_INTERACTION_TYPES = [
@@ -55,6 +57,7 @@ export const FULL_SCREEN_CONFIRMATIONS = [
5557
TransactionType.stakingUnstake,
5658
TransactionType.tokenMethodTransfer,
5759
TransactionType.tokenMethodTransferFrom,
60+
TransactionType.tokenMethodSafeTransferFrom,
5861
];
5962

6063
export const EARN_CONTRACT_INTERACTION_TYPES = [

app/components/Views/confirmations/hooks/metrics/useConfirmationLocation.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ describe('useConfirmationLocation', () => {
136136
[TransactionType.simpleSend],
137137
[TransactionType.tokenMethodTransfer],
138138
[TransactionType.tokenMethodTransferFrom],
139+
[TransactionType.tokenMethodSafeTransferFrom],
139140
])('returns TRANSFER location for %s transactions', (transactionType) => {
140141
mockUseApprovalRequest.mockReturnValue(
141142
createApprovalRequestMock({

app/components/Views/confirmations/hooks/metrics/useConfirmationLocation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const ConfirmationLocationMap = {
3838
case TransactionType.simpleSend:
3939
case TransactionType.tokenMethodTransfer:
4040
case TransactionType.tokenMethodTransferFrom:
41+
case TransactionType.tokenMethodSafeTransferFrom:
4142
return CONFIRMATION_EVENT_LOCATIONS.TRANSFER;
4243
case TransactionType.tokenMethodApprove:
4344
case TransactionType.tokenMethodIncreaseAllowance:

app/components/Views/confirmations/hooks/nft/useNft.test.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { MOCK_STATE_NFT } from '../../../../../util/test/mock-data/root-state/nft';
1+
import {
2+
MOCK_STATE_NFT,
3+
MOCK_TX_NFT_TRANSFER,
4+
} from '../../../../../util/test/mock-data/root-state/nft';
25
import { renderHookWithProvider } from '../../../../../util/test/renderWithProvider';
36
import { useNft } from './useNft';
47
import { useTransactionMetadataRequest } from '../transactions/useTransactionMetadataRequest';
@@ -31,4 +34,78 @@ describe('useNft', () => {
3134
expect(result.current.nft?.name).toBe('Test Dapp NFTs #12345');
3235
expect(result.current.tokenId?.toString()).toBe('12345');
3336
});
37+
38+
it('returns NFT data for ERC1155 send', () => {
39+
(useTransactionMetadataRequest as jest.Mock).mockReturnValue({
40+
txParams: {
41+
...MOCK_TX_NFT_TRANSFER.txParams,
42+
data: '0xf242432a000000000000000000000000dc47789de4ceff0e8fe9d15d728af7f17550c16400000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000',
43+
},
44+
chainId: '0x1',
45+
});
46+
const { result } = renderHookWithProvider(useNft, {
47+
state: {
48+
engine: {
49+
backgroundState: {
50+
...MOCK_STATE_NFT.engine.backgroundState,
51+
TransactionController: {
52+
transactions: [
53+
{
54+
...MOCK_TX_NFT_TRANSFER,
55+
txParams: {
56+
...MOCK_TX_NFT_TRANSFER.txParams,
57+
data: '0xf242432a000000000000000000000000dc47789de4ceff0e8fe9d15d728af7f17550c16400000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000',
58+
},
59+
},
60+
],
61+
},
62+
},
63+
},
64+
},
65+
});
66+
67+
expect(result.current.chainId).toBe('0x1');
68+
expect(result.current.name).toBe('Test Dapp NFTs');
69+
expect(result.current.nft).toBeDefined();
70+
expect(result.current.nft?.tokenId).toBe('1');
71+
expect(result.current.nft?.name).toBe('Test Dapp NFTs #1');
72+
expect(result.current.tokenId?.toString()).toBe('1');
73+
});
74+
75+
it('returns NFT data for ERC721 send', () => {
76+
(useTransactionMetadataRequest as jest.Mock).mockReturnValue({
77+
txParams: {
78+
...MOCK_TX_NFT_TRANSFER.txParams,
79+
data: '0x23b872dd000000000000000000000000dc47789de4ceff0e8fe9d15d728af7f17550c16400000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000003039',
80+
},
81+
chainId: '0x1',
82+
});
83+
const { result } = renderHookWithProvider(useNft, {
84+
state: {
85+
engine: {
86+
backgroundState: {
87+
...MOCK_STATE_NFT.engine.backgroundState,
88+
TransactionController: {
89+
transactions: [
90+
{
91+
...MOCK_TX_NFT_TRANSFER,
92+
txParams: {
93+
...MOCK_TX_NFT_TRANSFER.txParams,
94+
data: '0x23b872dd000000000000000000000000dc47789de4ceff0e8fe9d15d728af7f17550c16400000000000000000000000068d3ad12ea94779cb37262be1c179dbd8e208afe0000000000000000000000000000000000000000000000000000000000008e9a',
95+
},
96+
},
97+
],
98+
},
99+
},
100+
},
101+
},
102+
});
103+
104+
expect(result.current.chainId).toBe('0x1');
105+
expect(result.current.name).toBe('Test Dapp NFTs');
106+
expect(result.current.nft).toBeDefined();
107+
expect(result.current.nft?.tokenId).toBe('12345');
108+
expect(result.current.nft?.name).toBe('Test Dapp NFTs #12345');
109+
expect(result.current.tokenId?.toString()).toBe('12345');
110+
});
34111
});

app/components/Views/confirmations/hooks/nft/useNft.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ export const useNft = (): UseNftResponse => {
3535
const tokenAddress = safeToChecksumAddress(txParams?.to) ?? '';
3636

3737
const transactionData = parseStandardTokenTransactionData(txParams?.data);
38-
const tokenId = transactionData?.args?._value ?? undefined;
38+
const tokenId = (
39+
transactionData?.args?._value ?? transactionData?.args[2]
40+
)?.toString();
3941

4042
const nfts: Nft[] = useSelector(collectiblesSelector);
41-
const nft = tokenId
42-
? nfts.find((c) => c.tokenId === tokenId.toString())
43-
: undefined;
43+
const nft = tokenId ? nfts.find((c) => c.tokenId === tokenId) : undefined;
4444

4545
const nftContract = useNftContract(chainId, tokenAddress);
4646

app/components/Views/confirmations/hooks/send/useNfts.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,7 @@ export function useEVMNfts(): Nft[] {
128128
}
129129
}
130130
}
131-
132-
// Filter ERC1155 NFTs temporarily
133-
// This will be removed with https://github.com/MetaMask/metamask-mobile/issues/18923
134-
const filteredResults = transformedResults.filter(
135-
(nft) => nft.standard !== 'ERC1155',
136-
);
137-
138-
setTransformedNfts(filteredResults);
131+
setTransformedNfts(transformedResults);
139132
};
140133

141134
processNfts();

app/components/Views/confirmations/utils/send.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe('prepareEVMTransaction', () => {
137137
{ from: '0x123', to: '0x456', value: '100' },
138138
),
139139
).toStrictEqual({
140-
data: '0xf242432a0000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000045600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064',
140+
data: '0xf242432a000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000',
141141
from: '0x123',
142142
to: '0x123',
143143
value: '0x0',

0 commit comments

Comments
 (0)