-
-
Notifications
You must be signed in to change notification settings - Fork 12
fix: add dynamic nft and erc20 check #506
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ae8c396
fix: add dynamic nft and erc20 check
montelaidev c26e419
chore: update pr number
montelaidev 1fa71bd
fix: changelog
montelaidev a7eb11d
Apply suggestions from code review
montelaidev f979cba
feat: add `KeyringClientV2` support (#408)
ccharly 62146e2
release: 99.0.0 (#509)
ccharly bede212
Merge remote-tracking branch 'origin/main' into fix/mul-1593
montelaidev 6dfcb37
fix: lint
montelaidev 51e56bb
fix: update jest
montelaidev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { keccak256 } from 'ethereum-cryptography/keccak'; | ||
|
|
||
| import { ERC20_WRITE_SELECTORS, NFT_ONLY_SELECTORS } from './constants'; | ||
|
|
||
| /** | ||
| * Computes the four-byte function selector for a canonical Solidity signature. | ||
| * | ||
| * @param signature - Canonical Solidity ABI function signature. | ||
| * @returns Lowercase `0x` + 8-hex-digit selector. | ||
| */ | ||
| function selectorFromSignature(signature: string): string { | ||
| const hash = keccak256(Buffer.from(signature, 'utf8')); | ||
| return `0x${Buffer.from(hash).subarray(0, 4).toString('hex')}`; | ||
| } | ||
|
|
||
| describe('NFT_ONLY_SELECTORS', () => { | ||
| const signatures: readonly string[] = [ | ||
| 'setApprovalForAll(address,bool)', | ||
| 'safeTransferFrom(address,address,uint256)', | ||
| 'safeTransferFrom(address,address,uint256,bytes)', | ||
| 'safeTransferFrom(address,address,uint256,uint256,bytes)', | ||
| 'safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)', | ||
| ]; | ||
|
|
||
| it('contains exactly one entry per canonical NFT-related signature', () => { | ||
| expect(NFT_ONLY_SELECTORS.size).toBe(signatures.length); | ||
| for (const signature of signatures) { | ||
| expect(NFT_ONLY_SELECTORS.has(selectorFromSignature(signature))).toBe( | ||
| true, | ||
| ); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe('ERC20_WRITE_SELECTORS', () => { | ||
| const signatures: readonly string[] = [ | ||
| 'transfer(address,uint256)', | ||
| 'transferFrom(address,address,uint256)', | ||
| 'approve(address,uint256)', | ||
| ]; | ||
|
|
||
| it('contains exactly the three EIP-20 write function selectors', () => { | ||
| expect(ERC20_WRITE_SELECTORS.size).toBe(signatures.length); | ||
| for (const signature of signatures) { | ||
| expect(ERC20_WRITE_SELECTORS.has(selectorFromSignature(signature))).toBe( | ||
| true, | ||
| ); | ||
| } | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /** | ||
| * Selectors that are used only by NFT standards (ERC721/ERC1155), not by ERC20. | ||
| * When the tx uses one of these, we enable Ledger NFT clear signing. | ||
| * approve(0x095ea7b3) is shared by ERC20 and ERC721 so we do NOT include it here. | ||
| */ | ||
| export const NFT_ONLY_SELECTORS = new Set([ | ||
| '0xa22cb465', // setApprovalForAll (ERC721 + ERC1155) | ||
| '0x42842e0e', // safeTransferFrom (ERC721) | ||
| '0xb88d4fde', // safeTransferFrom(address,address,uint256,bytes) (ERC721) | ||
| '0xf242432a', // safeTransferFrom (ERC1155) | ||
| '0x2eb2c2d6', // safeBatchTransferFrom (ERC1155) | ||
| ]); | ||
|
|
||
| /** | ||
| * Four-byte selectors for the three state-changing functions defined in EIP-20. | ||
| * | ||
| * @see https://eips.ethereum.org/EIPS/eip-20 | ||
| */ | ||
| export const ERC20_WRITE_SELECTORS = new Set([ | ||
| '0xa9059cbb', // transfer(address,uint256) | ||
| '0x23b872dd', // transferFrom(address,address,uint256) | ||
| '0x095ea7b3', // approve(address,uint256) | ||
| ]); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| import { Common, Chain, Hardfork } from '@ethereumjs/common'; | ||
| import { TransactionFactory } from '@ethereumjs/tx'; | ||
| import { bytesToHex } from '@ethereumjs/util'; | ||
| import { remove0x } from '@metamask/utils'; | ||
|
|
||
| import { getTransactionSelector } from './utils'; | ||
|
|
||
| const TRANSFER_SELECTOR = '0xa9059cbb'; | ||
| const TRANSFER_FROM_SELECTOR = '0x23b872dd'; | ||
| const APPROVE_SELECTOR = '0x095ea7b3'; | ||
|
|
||
| describe('getTransactionSelector', () => { | ||
| const commonLegacy = new Common({ | ||
| chain: Chain.Mainnet, | ||
| hardfork: Hardfork.Berlin, | ||
| }); | ||
| const common1559 = new Common({ | ||
| chain: Chain.Mainnet, | ||
| hardfork: Hardfork.London, | ||
| }); | ||
|
|
||
| it('returns the first four bytes of calldata for a legacy serialized tx', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: `${TRANSFER_SELECTOR}00`, | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| const serializedHex = bytesToHex(tx.serialize()); | ||
| expect(getTransactionSelector(serializedHex)).toBe(TRANSFER_SELECTOR); | ||
| }); | ||
|
|
||
| it('returns the selector for an EIP-1559 serialized tx', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| type: 2, | ||
| nonce: '0x00', | ||
| maxFeePerGas: '0x01', | ||
| maxPriorityFeePerGas: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: `${TRANSFER_SELECTOR}deadbeef`, | ||
| }, | ||
| { common: common1559 }, | ||
| ); | ||
| const serializedHex = bytesToHex(tx.serialize()); | ||
| expect(getTransactionSelector(serializedHex)).toBe(TRANSFER_SELECTOR); | ||
| }); | ||
|
|
||
| it('accepts hex without a 0x prefix', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: `${TRANSFER_SELECTOR}00`, | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| const serializedHex = remove0x(bytesToHex(tx.serialize())); | ||
| expect(getTransactionSelector(serializedHex)).toBe(TRANSFER_SELECTOR); | ||
| }); | ||
|
|
||
| it('returns undefined when calldata is empty', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: '0x', | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| expect(getTransactionSelector(bytesToHex(tx.serialize()))).toBeUndefined(); | ||
| }); | ||
|
|
||
| it('returns undefined for invalid serialized hex', () => { | ||
| expect(getTransactionSelector('0xnothex')).toBeUndefined(); | ||
| }); | ||
|
|
||
| it('returns transferFrom selector when calldata uses transferFrom', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: `${TRANSFER_FROM_SELECTOR}00`, | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| expect(getTransactionSelector(bytesToHex(tx.serialize()))).toBe( | ||
| TRANSFER_FROM_SELECTOR, | ||
| ); | ||
| }); | ||
|
|
||
| it('returns approve selector when calldata uses approve', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: `${APPROVE_SELECTOR}00`, | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| expect(getTransactionSelector(bytesToHex(tx.serialize()))).toBe( | ||
| APPROVE_SELECTOR, | ||
| ); | ||
| }); | ||
|
|
||
| it('returns undefined when calldata is shorter than four bytes', () => { | ||
| const tx = TransactionFactory.fromTxData( | ||
| { | ||
| nonce: '0x00', | ||
| gasPrice: '0x01', | ||
| gasLimit: '0x5208', | ||
| to: '0x0000000000000000000000000000000000000000', | ||
| value: '0x00', | ||
| data: '0xabcd', | ||
| }, | ||
| { common: commonLegacy }, | ||
| ); | ||
| expect(getTransactionSelector(bytesToHex(tx.serialize()))).toBeUndefined(); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.