-
-
Notifications
You must be signed in to change notification settings - Fork 10
feat: integrate the 'signRewardsMessage' flow #566
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
11 commits
Select commit
Hold shift + click to select a range
6a5a542
feat: add the parse validation part of the RewardsMessage
Battambang 78cca2f
feat: use atob for base64 decoding
Battambang 381cce2
test: add unit tests related to parseRewardsMessage validation
Battambang a58ebb1
feat: add validation of address
Battambang 14299ec
feat: add signature
Battambang e51dfc9
chore: remove unused variable
Battambang 9d492e2
test: add unit test for signMessageDirect
Battambang eabcbf8
fix: update snap manifest version
Battambang c84273b
feat: refactor using accountUseCases.signMessage
Battambang c5dd1bb
chore: update signMessage skipConfirmation flag to have an option vis…
Battambang afc2401
add metamask as origin for signMessage from signRewardsMessage
Battambang 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| import { parseRewardsMessage } from './validation'; | ||
|
|
||
| /* eslint-disable @typescript-eslint/naming-convention */ | ||
| jest.mock('@metamask/bitcoindevkit', () => ({ | ||
| Address: { | ||
| from_string: jest.fn(), | ||
| }, | ||
| Amount: { | ||
| from_btc: jest.fn(), | ||
| }, | ||
| })); | ||
|
|
||
| describe('validation', () => { | ||
| describe('parseRewardsMessage', () => { | ||
| const validBitcoinMainnetAddress = | ||
| 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'; | ||
| const currentTimestamp = Math.floor(Date.now() / 1000); | ||
|
|
||
| const toBase64 = (utf8: string): string => btoa(utf8); | ||
|
|
||
| describe('valid parsing', () => { | ||
| it('correctly extracts address and timestamp from valid message', () => { | ||
| const expectedAddress = validBitcoinMainnetAddress; | ||
| const expectedTimestamp = 1736660000; | ||
| const utf8Message = `rewards,${expectedAddress},${expectedTimestamp}`; | ||
| const base64Message = toBase64(utf8Message); | ||
|
|
||
| const result = parseRewardsMessage(base64Message); | ||
|
|
||
| expect(result.address).toBe(expectedAddress); | ||
| expect(result.timestamp).toBe(expectedTimestamp); | ||
| }); | ||
| }); | ||
|
|
||
| describe('invalid messages', () => { | ||
| it('rejects string that decodes but not rewards format', () => { | ||
| const base64Message = 'hello world'; | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must start with "rewards,"', | ||
| ); | ||
| }); | ||
|
|
||
| it('rejects empty string', () => { | ||
| const base64Message = ''; | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must start with "rewards,"', | ||
| ); | ||
| }); | ||
|
|
||
| it('rejects invalid base64 with special characters', () => { | ||
| const base64Message = '!!!@@@###'; | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Invalid base64 encoding', | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe('invalid message prefix', () => { | ||
| it.each([ | ||
| { | ||
| message: `reward,${validBitcoinMainnetAddress},${currentTimestamp}`, | ||
| description: "missing 's'", | ||
| }, | ||
| { | ||
| message: `Rewards,${validBitcoinMainnetAddress},${currentTimestamp}`, | ||
| description: 'wrong case (capitalized)', | ||
| }, | ||
| { | ||
| message: `bonus,${validBitcoinMainnetAddress},${currentTimestamp}`, | ||
| description: 'wrong prefix', | ||
| }, | ||
| { | ||
| message: `${validBitcoinMainnetAddress},${currentTimestamp}`, | ||
| description: 'no prefix', | ||
| }, | ||
| { | ||
| message: `REWARDS,${validBitcoinMainnetAddress},${currentTimestamp}`, | ||
| description: 'all caps', | ||
| }, | ||
| ])( | ||
| 'rejects message that does not start with "rewards,": $description', | ||
| ({ message: utf8Message }) => { | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must start with "rewards,"', | ||
| ); | ||
| }, | ||
| ); | ||
| }); | ||
|
|
||
| describe('invalid message structure', () => { | ||
| it('rejects message with only prefix', () => { | ||
| const utf8Message = 'rewards,'; | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must have exactly 3 parts', | ||
| ); | ||
| }); | ||
|
|
||
| it('rejects message missing timestamp', () => { | ||
| const utf8Message = `rewards,${validBitcoinMainnetAddress}`; | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must have exactly 3 parts', | ||
| ); | ||
| }); | ||
|
|
||
| it('rejects message with too many parts', () => { | ||
| const utf8Message = `rewards,${validBitcoinMainnetAddress},${currentTimestamp},extra`; | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must have exactly 3 parts', | ||
| ); | ||
| }); | ||
|
|
||
| it('rejects message with empty parts', () => { | ||
| const utf8Message = 'rewards,,,'; | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow(/timestamp/iu); | ||
| }); | ||
|
|
||
| it('rejects message with only prefix without comma', () => { | ||
| const utf8Message = 'rewards'; | ||
| const base64Message = toBase64(utf8Message); | ||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| 'Message must start with "rewards,"', | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe('invalid timestamps', () => { | ||
| it.each([ | ||
| { | ||
| timestamp: 'invalid', | ||
| description: 'non-numeric', | ||
| }, | ||
| { | ||
| timestamp: '', | ||
| description: 'empty timestamp', | ||
| }, | ||
| { | ||
| timestamp: '-1', | ||
| description: 'negative timestamp', | ||
| }, | ||
| { | ||
| timestamp: '0', | ||
| description: 'zero timestamp', | ||
| }, | ||
| { | ||
| timestamp: '123.456', | ||
| description: 'decimal timestamp', | ||
| }, | ||
| { | ||
| timestamp: '1.0', | ||
| description: 'decimal with .0', | ||
| }, | ||
| { | ||
| timestamp: 'abc123', | ||
| description: 'alphanumeric', | ||
| }, | ||
| ])( | ||
| 'rejects message with invalid timestamp: $description', | ||
| ({ timestamp }) => { | ||
| const utf8Message = `rewards,${validBitcoinMainnetAddress},${timestamp}`; | ||
| const base64Message = toBase64(utf8Message); | ||
|
|
||
| expect(() => parseRewardsMessage(base64Message)).toThrow( | ||
| /timestamp/iu, | ||
| ); | ||
| }, | ||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
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.