Add Sign-In with Ethereum (#14438)
Co-authored-by: Gregório Granado Magalhães <greg.magalhaes@gmail.com> Co-authored-by: George Marshall <georgewrmarshall@gmail.com> Co-authored-by: georgewrmarshall <george.marshall@consensys.net> Co-authored-by: Ariella Vu <20778143+digiwand@users.noreply.github.com> Co-authored-by: brad-decker <bhdecker84@gmail.com>feature/default_network_editable
parent
2e6aea412a
commit
5802805597
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,150 @@ |
||||
import { stripHexPrefix } from 'ethereumjs-util'; |
||||
import { ParsedMessage } from '@spruceid/siwe-parser'; |
||||
import log from 'loglevel'; |
||||
|
||||
const msgHexToText = (hex) => { |
||||
try { |
||||
const stripped = stripHexPrefix(hex); |
||||
const buff = Buffer.from(stripped, 'hex'); |
||||
return buff.length === 32 ? hex : buff.toString('utf8'); |
||||
} catch (e) { |
||||
log.error(e); |
||||
return hex; |
||||
} |
||||
}; |
||||
|
||||
/** |
||||
* A locally defined object used to provide data to identify a Sign-In With Ethereum (SIWE)(EIP-4361) message and provide the parsed message |
||||
* |
||||
* @typedef localSIWEObject |
||||
* @param {boolean} isSIWEMessage - Does the intercepted message conform to the SIWE specification? |
||||
* @param {ParsedMessage} parsedMessage - The data parsed out of the message |
||||
*/ |
||||
|
||||
/** |
||||
* This function intercepts a sign message, detects if it's a |
||||
* Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with |
||||
* relevant SIWE data. |
||||
* |
||||
* {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}
|
||||
* |
||||
* @param {object} msgParams - The params of the message to sign |
||||
* @returns {localSIWEObject} |
||||
*/ |
||||
export const detectSIWE = (msgParams) => { |
||||
try { |
||||
const { data } = msgParams; |
||||
const message = msgHexToText(data); |
||||
const parsedMessage = new ParsedMessage(message); |
||||
|
||||
return { |
||||
isSIWEMessage: true, |
||||
parsedMessage, |
||||
}; |
||||
} catch (error) { |
||||
// ignore error, it's not a valid SIWE message
|
||||
return { |
||||
isSIWEMessage: false, |
||||
parsedMessage: null, |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
/** |
||||
* Takes in a parsed Sign-In with Ethereum Message (EIP-4361) |
||||
* and generates an array of label-value pairs |
||||
* |
||||
* @param {object} parsedMessage - A parsed SIWE message with message contents |
||||
* @param {Function} t - i18n function |
||||
* @returns {Array} An array of label-value pairs with the type of the value as the label |
||||
*/ |
||||
export const formatMessageParams = (parsedMessage, t) => { |
||||
const output = []; |
||||
|
||||
const { |
||||
statement, |
||||
uri, |
||||
version, |
||||
chainId, |
||||
nonce, |
||||
issuedAt, |
||||
expirationTime, |
||||
notBefore, |
||||
requestId, |
||||
resources, |
||||
} = parsedMessage; |
||||
|
||||
if (statement) { |
||||
output.push({ |
||||
label: t('SIWELabelMessage'), |
||||
value: statement, |
||||
}); |
||||
} |
||||
|
||||
if (uri) { |
||||
output.push({ |
||||
label: t('SIWELabelURI'), |
||||
value: uri, |
||||
}); |
||||
} |
||||
|
||||
if (version) { |
||||
output.push({ |
||||
label: t('SIWELabelVersion'), |
||||
value: version, |
||||
}); |
||||
} |
||||
|
||||
if (chainId) { |
||||
output.push({ |
||||
label: t('SIWELabelChainID'), |
||||
value: chainId, |
||||
}); |
||||
} |
||||
|
||||
if (nonce) { |
||||
output.push({ |
||||
label: t('SIWELabelNonce'), |
||||
value: nonce, |
||||
}); |
||||
} |
||||
|
||||
if (issuedAt) { |
||||
output.push({ |
||||
label: t('SIWELabelIssuedAt'), |
||||
value: issuedAt, |
||||
}); |
||||
} |
||||
|
||||
if (expirationTime) { |
||||
output.push({ |
||||
label: t('SIWELabelExpirationTime'), |
||||
value: expirationTime, |
||||
}); |
||||
} |
||||
|
||||
if (notBefore) { |
||||
output.push({ |
||||
label: t('SIWELabelNotBefore'), |
||||
value: notBefore, |
||||
}); |
||||
} |
||||
|
||||
if (requestId) { |
||||
output.push({ |
||||
label: t('SIWELabelRequestID'), |
||||
value: requestId, |
||||
}); |
||||
} |
||||
|
||||
if (resources && resources.length > 0) { |
||||
output.push({ |
||||
label: t('SIWELabelResources', [resources.length]), |
||||
value: resources |
||||
.reduce((previous, resource) => `${previous}${resource}\n`, '') |
||||
.trim(), |
||||
}); |
||||
} |
||||
|
||||
return output; |
||||
}; |
@ -0,0 +1,15 @@ |
||||
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; |
||||
|
||||
import SignatureRequestSIWE from '.'; |
||||
|
||||
# Signature Request SIWE |
||||
|
||||
dApp requesting the user to Sign in with Ethereum. |
||||
|
||||
<Canvas> |
||||
<Story id="ui-components-app-signature-request-siwe-signature-request-siwe-stories-js--default-story" /> |
||||
</Canvas> |
||||
|
||||
## Component API |
||||
|
||||
<ArgsTable of={SignatureRequestSIWE} /> |
@ -0,0 +1 @@ |
||||
export { default } from './signature-request-siwe'; |
@ -0,0 +1,74 @@ |
||||
@import 'signature-request-siwe-header/index'; |
||||
@import 'signature-request-siwe-message/index'; |
||||
|
||||
.signature-request-siwe { |
||||
display: flex; |
||||
flex-direction: column; |
||||
height: 100%; |
||||
min-width: 0; |
||||
background-color: var(--color-background-default); |
||||
margin-right: auto; |
||||
margin-left: auto; |
||||
overflow-y: auto; |
||||
|
||||
@media screen and (min-width: $break-large) { |
||||
width: 408px; |
||||
max-height: 82vh; |
||||
min-height: 570px; |
||||
flex: 0 0 auto; |
||||
border-radius: 8px; |
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, 0.08); |
||||
} |
||||
|
||||
.signature-request-siwe__actionable-message { |
||||
margin: 4px 16px; |
||||
|
||||
svg { |
||||
width: 13px; |
||||
height: 13px; |
||||
left: 10px; |
||||
top: 20px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
.signature-request-siwe__page-container-footer.page-container__footer { |
||||
border-top: none; |
||||
} |
||||
|
||||
.signature-request-siwe__warning-popover { |
||||
.page-container__footer { |
||||
border-top: none; |
||||
padding: 0; |
||||
display: block; |
||||
} |
||||
|
||||
.popover-footer { |
||||
padding: inherit; |
||||
display: block; |
||||
} |
||||
|
||||
&__checkbox-wrapper { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: flex-start; |
||||
padding: 8px 16px 24px; |
||||
|
||||
&__label { |
||||
@include H7; |
||||
|
||||
color: var(--color-text-default); |
||||
margin-inline-start: 8px; |
||||
margin-top: 1px; |
||||
user-select: none; |
||||
-moz-user-select: none; |
||||
-webkit-user-select: none; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.check-box { |
||||
color: var(--color-error-default); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
export { default } from './signature-request-siwe-header'; |
@ -0,0 +1,26 @@ |
||||
.signature-request-siwe-header { |
||||
display: flex; |
||||
padding: 16px; |
||||
justify-content: center; |
||||
align-items: center; |
||||
flex-direction: column; |
||||
|
||||
&__tooltip__container { |
||||
display: flex !important; |
||||
} |
||||
|
||||
.account-list-item { |
||||
margin: 8px 0 0; |
||||
|
||||
&__top-row { |
||||
align-items: center; |
||||
margin: 0; |
||||
} |
||||
|
||||
&__account-name { |
||||
@include H5; |
||||
|
||||
font-weight: 500; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,69 @@ |
||||
import React, { useContext } from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import AccountListItem from '../../account-list-item'; |
||||
import { I18nContext } from '../../../../contexts/i18n'; |
||||
import Tooltip from '../../../ui/tooltip'; |
||||
import InfoIcon from '../../../ui/icon/info-icon.component'; |
||||
import { SEVERITIES } from '../../../../helpers/constants/design-system'; |
||||
|
||||
import PermissionsConnectHeader from '../../permissions-connect-header'; |
||||
|
||||
export default function SignatureRequestSIWEHeader({ |
||||
fromAccount, |
||||
domain, |
||||
isSIWEDomainValid, |
||||
subjectMetadata, |
||||
}) { |
||||
const t = useContext(I18nContext); |
||||
|
||||
return ( |
||||
<div className="signature-request-siwe-header"> |
||||
<PermissionsConnectHeader |
||||
iconUrl={subjectMetadata.iconUrl} |
||||
iconName={subjectMetadata.name} |
||||
headerTitle={t('SIWESiteRequestTitle')} |
||||
headerText={t('SIWESiteRequestSubtitle')} |
||||
siteOrigin={domain} |
||||
className={isSIWEDomainValid ? '' : 'bad-domain'} |
||||
rightIcon={ |
||||
!isSIWEDomainValid && ( |
||||
<Tooltip |
||||
position="bottom" |
||||
html={<p>{t('SIWEDomainWarningBody', [domain])}</p>} |
||||
wrapperClassName="signature-request-siwe-header__tooltip" |
||||
containerClassName="signature-request-siwe-header__tooltip__container" |
||||
> |
||||
<InfoIcon severity={SEVERITIES.DANGER} /> |
||||
</Tooltip> |
||||
) |
||||
} |
||||
/> |
||||
{fromAccount && ( |
||||
<AccountListItem |
||||
account={fromAccount} |
||||
className="signature-request-siwe-header__account-list-item" |
||||
/> |
||||
)} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
SignatureRequestSIWEHeader.propTypes = { |
||||
/** |
||||
* The account that is requesting permissions |
||||
*/ |
||||
fromAccount: PropTypes.object, |
||||
/** |
||||
* The domain that the request is for |
||||
*/ |
||||
domain: PropTypes.string, |
||||
/** |
||||
* Whether the domain is valid |
||||
*/ |
||||
isSIWEDomainValid: PropTypes.bool, |
||||
/** |
||||
* The metadata for the subject. This is used to display the icon and name |
||||
* and is selected from the domain in the SIWE request. |
||||
*/ |
||||
subjectMetadata: PropTypes.object, |
||||
}; |
@ -0,0 +1,36 @@ |
||||
import React from 'react'; |
||||
import testData from '../../../../../.storybook/test-data'; |
||||
import SignatureRequestSIWEHeader from '.'; |
||||
|
||||
const primaryIdentity = Object.values(testData.metamask.identities)[0]; |
||||
const subjectMetadata = { |
||||
iconUrl: '/images/logo/metamask-fox.svg', |
||||
name: 'MetaMask', |
||||
origin: 'http://localhost:8080', |
||||
}; |
||||
|
||||
export default { |
||||
title: 'Components/App/SignatureRequestSIWE/SignatureRequestSIWEHeader', |
||||
id: __filename, |
||||
argTypes: { |
||||
fromAccount: { |
||||
table: { |
||||
address: { control: 'text' }, |
||||
balance: { control: 'text' }, |
||||
name: { control: 'text' }, |
||||
}, |
||||
}, |
||||
domain: { control: 'text' }, |
||||
subjectMetadata: { control: 'object' }, |
||||
}, |
||||
}; |
||||
|
||||
export const DefaultStory = (args) => <SignatureRequestSIWEHeader {...args} />; |
||||
|
||||
DefaultStory.storyName = 'Default'; |
||||
|
||||
DefaultStory.args = { |
||||
fromAccount: primaryIdentity, |
||||
domain: window.location.host, |
||||
subjectMetadata, |
||||
}; |
@ -0,0 +1 @@ |
||||
export { default } from './signature-request-siwe-message'; |
@ -0,0 +1,13 @@ |
||||
.signature-request-siwe-message { |
||||
flex: 1 100%; |
||||
border-radius: 8px; |
||||
padding: 8px 16px; |
||||
margin: 16px; |
||||
border: 1px solid var(--color-border-muted); |
||||
|
||||
&__sub-text { |
||||
white-space: pre-line; |
||||
overflow: hidden; |
||||
word-wrap: break-word; |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
import React from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import Box from '../../../ui/box'; |
||||
import Typography from '../../../ui/typography'; |
||||
|
||||
import { |
||||
FLEX_DIRECTION, |
||||
TYPOGRAPHY, |
||||
} from '../../../../helpers/constants/design-system'; |
||||
|
||||
const SignatureRequestSIWEMessage = ({ data }) => { |
||||
return ( |
||||
<Box className="signature-request-siwe-message"> |
||||
<Box flexDirection={FLEX_DIRECTION.COLUMN}> |
||||
{data.map(({ label, value }, i) => ( |
||||
<Box key={i.toString()} marginTop={2} marginBottom={2}> |
||||
<Typography variant={TYPOGRAPHY.H4} marginTop={2} marginBottom={2}> |
||||
{label} |
||||
</Typography> |
||||
<Typography |
||||
className="signature-request-siwe-message__sub-text" |
||||
variant={TYPOGRAPHY.H6} |
||||
marginTop={2} |
||||
marginBottom={2} |
||||
> |
||||
{value} |
||||
</Typography> |
||||
</Box> |
||||
))} |
||||
</Box> |
||||
</Box> |
||||
); |
||||
}; |
||||
|
||||
SignatureRequestSIWEMessage.propTypes = { |
||||
/** |
||||
* The data array that contains objects of data about the message |
||||
*/ |
||||
data: PropTypes.arrayOf( |
||||
PropTypes.shape({ |
||||
/** |
||||
* The label or title of the value data |
||||
*/ |
||||
label: PropTypes.string, |
||||
/** |
||||
* The value of the data |
||||
*/ |
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), |
||||
}), |
||||
), |
||||
}; |
||||
|
||||
export default React.memo(SignatureRequestSIWEMessage); |
@ -0,0 +1,51 @@ |
||||
import React from 'react'; |
||||
import SignatureRequestMessage from '.'; |
||||
|
||||
export default { |
||||
title: 'Components/App/SignatureRequestSIWE/SignatureRequestMessage', |
||||
id: __filename, |
||||
argTypes: { |
||||
data: { |
||||
controls: 'object', |
||||
}, |
||||
}, |
||||
args: { |
||||
data: [ |
||||
{ label: 'Label:', value: 'value' }, |
||||
{ |
||||
label: 'Message:', |
||||
value: |
||||
'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos', |
||||
}, |
||||
{ |
||||
label: 'URI:', |
||||
value: 'http://localhost:8080', |
||||
}, |
||||
{ |
||||
label: 'Version:', |
||||
value: '1', |
||||
}, |
||||
{ |
||||
label: 'Chain ID:', |
||||
value: 1, |
||||
}, |
||||
{ |
||||
label: 'Nonce:', |
||||
value: 'STMt6KQMwwdOXE306', |
||||
}, |
||||
{ |
||||
label: 'Issued At:', |
||||
value: '2022-03-18T21:40:40.823Z', |
||||
}, |
||||
{ |
||||
label: 'Resources: 2', |
||||
value: |
||||
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu\nhttps://example.com/my-web2-claim.json', |
||||
}, |
||||
], |
||||
}, |
||||
}; |
||||
|
||||
export const DefaultStory = (args) => <SignatureRequestMessage {...args} />; |
||||
|
||||
DefaultStory.storyName = 'Default'; |
@ -0,0 +1,174 @@ |
||||
import React, { useCallback, useContext, useState } from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import { useSelector } from 'react-redux'; |
||||
import log from 'loglevel'; |
||||
import ActionableMessage from '../../ui/actionable-message'; |
||||
import Popover from '../../ui/popover'; |
||||
import Checkbox from '../../ui/check-box'; |
||||
import { I18nContext } from '../../../contexts/i18n'; |
||||
import { PageContainerFooter } from '../../ui/page-container'; |
||||
import { |
||||
accountsWithSendEtherInfoSelector, |
||||
getSubjectMetadata, |
||||
} from '../../../selectors'; |
||||
import { getAccountByAddress } from '../../../helpers/utils/util'; |
||||
import { formatMessageParams } from '../../../../shared/modules/siwe'; |
||||
import Header from './signature-request-siwe-header'; |
||||
import Message from './signature-request-siwe-message'; |
||||
|
||||
export default function SignatureRequestSIWE({ |
||||
txData, |
||||
cancelPersonalMessage, |
||||
signPersonalMessage, |
||||
}) { |
||||
const allAccounts = useSelector(accountsWithSendEtherInfoSelector); |
||||
const subjectMetadata = useSelector(getSubjectMetadata); |
||||
|
||||
const { |
||||
msgParams: { |
||||
from, |
||||
origin, |
||||
siwe: { parsedMessage }, |
||||
}, |
||||
} = txData; |
||||
|
||||
const fromAccount = getAccountByAddress(allAccounts, from); |
||||
const targetSubjectMetadata = subjectMetadata[origin]; |
||||
|
||||
const t = useContext(I18nContext); |
||||
|
||||
const isMatchingAddress = |
||||
from.toLowerCase() === parsedMessage.address.toLowerCase(); |
||||
|
||||
const checkSIWEDomain = () => { |
||||
let isSIWEDomainValid = false; |
||||
|
||||
if (origin) { |
||||
const { host } = new URL(origin); |
||||
isSIWEDomainValid = parsedMessage.domain === host; |
||||
} |
||||
return isSIWEDomainValid; |
||||
}; |
||||
|
||||
const isSIWEDomainValid = checkSIWEDomain(); |
||||
|
||||
const [isShowingDomainWarning, setIsShowingDomainWarning] = useState(false); |
||||
const [agreeToDomainWarning, setAgreeToDomainWarning] = useState(false); |
||||
|
||||
const onSign = useCallback( |
||||
async (event) => { |
||||
try { |
||||
await signPersonalMessage(event); |
||||
} catch (e) { |
||||
log.error(e); |
||||
} |
||||
}, |
||||
[signPersonalMessage], |
||||
); |
||||
|
||||
const onCancel = useCallback( |
||||
async (event) => { |
||||
try { |
||||
await cancelPersonalMessage(event); |
||||
} catch (e) { |
||||
log.error(e); |
||||
} |
||||
}, |
||||
[cancelPersonalMessage], |
||||
); |
||||
|
||||
return ( |
||||
<div className="signature-request-siwe"> |
||||
<Header |
||||
fromAccount={fromAccount} |
||||
domain={parsedMessage.domain} |
||||
isSIWEDomainValid={isSIWEDomainValid} |
||||
subjectMetadata={targetSubjectMetadata} |
||||
/> |
||||
<Message data={formatMessageParams(parsedMessage, t)} /> |
||||
{!isMatchingAddress && ( |
||||
<ActionableMessage |
||||
className="signature-request-siwe__actionable-message" |
||||
type="warning" |
||||
message={t('SIWEAddressInvalid', [ |
||||
parsedMessage.address, |
||||
fromAccount.address, |
||||
])} |
||||
iconFillColor="var(--color-warning-default)" |
||||
useIcon |
||||
withRightButton |
||||
/> |
||||
)} |
||||
{!isSIWEDomainValid && ( |
||||
<ActionableMessage |
||||
className="signature-request-siwe__actionable-message" |
||||
type="danger" |
||||
message={t('SIWEDomainInvalid', [parsedMessage.domain])} |
||||
iconFillColor="var(--color-error-default)" |
||||
useIcon |
||||
withRightButton |
||||
/> |
||||
)} |
||||
<PageContainerFooter |
||||
footerClassName="signature-request-siwe__page-container-footer" |
||||
onCancel={onCancel} |
||||
onSubmit={ |
||||
isSIWEDomainValid ? onSign : () => setIsShowingDomainWarning(true) |
||||
} |
||||
cancelText={t('cancel')} |
||||
submitText={t('signin')} |
||||
/> |
||||
{isShowingDomainWarning && ( |
||||
<Popover |
||||
onClose={() => setIsShowingDomainWarning(false)} |
||||
title={t('SIWEWarningTitle')} |
||||
subtitle={t('SIWEWarningSubtitle')} |
||||
className="signature-request-siwe__warning-popover" |
||||
footerClassName="signature-request-siwe__warning-popover__footer" |
||||
footer={ |
||||
<PageContainerFooter |
||||
footerClassName="signature-request-siwe__warning-popover__footer__warning-footer" |
||||
onCancel={() => setIsShowingDomainWarning(false)} |
||||
cancelText={t('cancel')} |
||||
cancelButtonType="default" |
||||
onSubmit={onSign} |
||||
submitText={t('confirm')} |
||||
submitButtonType="danger-primary" |
||||
disabled={!agreeToDomainWarning} |
||||
/> |
||||
} |
||||
> |
||||
<div className="signature-request-siwe__warning-popover__checkbox-wrapper"> |
||||
<Checkbox |
||||
id="signature-request-siwe_domain-checkbox" |
||||
checked={agreeToDomainWarning} |
||||
className="signature-request-siwe__warning-popover__checkbox-wrapper__checkbox" |
||||
onClick={() => setAgreeToDomainWarning((checked) => !checked)} |
||||
/> |
||||
<label |
||||
className="signature-request-siwe__warning-popover__checkbox-wrapper__label" |
||||
htmlFor="signature-request-siwe_domain-checkbox" |
||||
> |
||||
{t('SIWEDomainWarningBody', [parsedMessage.domain])} |
||||
</label> |
||||
</div> |
||||
</Popover> |
||||
)} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
SignatureRequestSIWE.propTypes = { |
||||
/** |
||||
* The display content of transaction data |
||||
*/ |
||||
txData: PropTypes.object.isRequired, |
||||
/** |
||||
* Handler for cancel button |
||||
*/ |
||||
cancelPersonalMessage: PropTypes.func.isRequired, |
||||
/** |
||||
* Handler for sign button |
||||
*/ |
||||
signPersonalMessage: PropTypes.func.isRequired, |
||||
}; |
@ -0,0 +1,165 @@ |
||||
import React from 'react'; |
||||
import testData from '../../../../.storybook/test-data'; |
||||
import README from './README.mdx'; |
||||
import SignatureRequestSIWE from './signature-request-siwe'; |
||||
|
||||
const { identities, selectedAddress } = testData.metamask; |
||||
const otherIdentity = Object.values(identities)[0]; |
||||
|
||||
export default { |
||||
title: 'Components/App/SignatureRequestSIWE', |
||||
id: __filename, |
||||
component: SignatureRequestSIWE, |
||||
parameters: { |
||||
docs: { |
||||
page: README, |
||||
}, |
||||
}, |
||||
argTypes: { |
||||
txData: { control: 'object' }, |
||||
cancelPersonalMessage: { action: 'Cancel' }, |
||||
signPersonalMessage: { action: 'Sign' }, |
||||
}, |
||||
}; |
||||
|
||||
const msgParams = { |
||||
from: selectedAddress, |
||||
data: '0x6c6f63616c686f73743a383038302077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078466232433135303034333433393034653566343038323537386334653865313131303563463765330a0a436c69636b20746f207369676e20696e20616e642061636365707420746865205465726d73206f6620536572766963653a2068747470733a2f2f636f6d6d756e6974792e6d6574616d61736b2e696f2f746f730a0a5552493a20687474703a2f2f6c6f63616c686f73743a383038300a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a2053544d74364b514d7777644f58453330360a4973737565642041743a20323032322d30332d31385432313a34303a34302e3832335a0a5265736f75726365733a0a2d20697066733a2f2f516d653773733341525667787636725871565069696b4d4a3875324e4c676d67737a673133705972444b456f69750a2d2068747470733a2f2f6578616d706c652e636f6d2f6d792d776562322d636c61696d2e6a736f6e', |
||||
origin: 'http://localhost:8080', |
||||
siwe: { |
||||
isSIWEMessage: true, |
||||
isSIWEDomainValid: true, |
||||
parsedMessage: { |
||||
domain: 'localhost:8080', |
||||
address: selectedAddress, |
||||
statement: |
||||
'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos', |
||||
uri: 'http://localhost:8080', |
||||
version: '1', |
||||
nonce: 'STMt6KQMwwdOXE306', |
||||
chainId: 1, |
||||
issuedAt: '2022-03-18T21:40:40.823Z', |
||||
resources: [ |
||||
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu', |
||||
'https://example.com/my-web2-claim.json', |
||||
], |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
const badDomainParams = { |
||||
from: selectedAddress, |
||||
data: '0x6c6f63616c686f73743a383038302077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078466232433135303034333433393034653566343038323537386334653865313131303563463765330a0a436c69636b20746f207369676e20696e20616e642061636365707420746865205465726d73206f6620536572766963653a2068747470733a2f2f636f6d6d756e6974792e6d6574616d61736b2e696f2f746f730a0a5552493a20687474703a2f2f6c6f63616c686f73743a383038300a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a2053544d74364b514d7777644f58453330360a4973737565642041743a20323032322d30332d31385432313a34303a34302e3832335a0a5265736f75726365733a0a2d20697066733a2f2f516d653773733341525667787636725871565069696b4d4a3875324e4c676d67737a673133705972444b456f69750a2d2068747470733a2f2f6578616d706c652e636f6d2f6d792d776562322d636c61696d2e6a736f6e', |
||||
origin: 'http://localhost:8080', |
||||
siwe: { |
||||
isSIWEMessage: true, |
||||
isSIWEDomainValid: false, |
||||
parsedMessage: { |
||||
domain: 'baddomain.com', |
||||
address: selectedAddress, |
||||
statement: |
||||
'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos', |
||||
uri: 'http://localhost:8080', |
||||
version: '1', |
||||
nonce: 'STMt6KQMwwdOXE306', |
||||
chainId: 1, |
||||
issuedAt: '2022-03-18T21:40:40.823Z', |
||||
resources: [ |
||||
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu', |
||||
'https://example.com/my-web2-claim.json', |
||||
], |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
const badAddressParams = { |
||||
from: otherIdentity.address, |
||||
data: '0x6c6f63616c686f73743a383038302077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078466232433135303034333433393034653566343038323537386334653865313131303563463765330a0a436c69636b20746f207369676e20696e20616e642061636365707420746865205465726d73206f6620536572766963653a2068747470733a2f2f636f6d6d756e6974792e6d6574616d61736b2e696f2f746f730a0a5552493a20687474703a2f2f6c6f63616c686f73743a383038300a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a2053544d74364b514d7777644f58453330360a4973737565642041743a20323032322d30332d31385432313a34303a34302e3832335a0a5265736f75726365733a0a2d20697066733a2f2f516d653773733341525667787636725871565069696b4d4a3875324e4c676d67737a673133705972444b456f69750a2d2068747470733a2f2f6578616d706c652e636f6d2f6d792d776562322d636c61696d2e6a736f6e', |
||||
origin: 'http://localhost:8080', |
||||
siwe: { |
||||
isSIWEMessage: true, |
||||
isSIWEDomainValid: true, |
||||
parsedMessage: { |
||||
domain: 'localhost:8080', |
||||
address: selectedAddress, |
||||
statement: |
||||
'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos', |
||||
uri: 'http://localhost:8080', |
||||
version: '1', |
||||
nonce: 'STMt6KQMwwdOXE306', |
||||
chainId: 1, |
||||
issuedAt: '2022-03-18T21:40:40.823Z', |
||||
resources: [ |
||||
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu', |
||||
'https://example.com/my-web2-claim.json', |
||||
], |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
const badDomainAndAddressParams = { |
||||
from: otherIdentity.address, |
||||
data: '0x6c6f63616c686f73743a383038302077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078466232433135303034333433393034653566343038323537386334653865313131303563463765330a0a436c69636b20746f207369676e20696e20616e642061636365707420746865205465726d73206f6620536572766963653a2068747470733a2f2f636f6d6d756e6974792e6d6574616d61736b2e696f2f746f730a0a5552493a20687474703a2f2f6c6f63616c686f73743a383038300a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a2053544d74364b514d7777644f58453330360a4973737565642041743a20323032322d30332d31385432313a34303a34302e3832335a0a5265736f75726365733a0a2d20697066733a2f2f516d653773733341525667787636725871565069696b4d4a3875324e4c676d67737a673133705972444b456f69750a2d2068747470733a2f2f6578616d706c652e636f6d2f6d792d776562322d636c61696d2e6a736f6e', |
||||
origin: 'http://localhost:8080', |
||||
siwe: { |
||||
isSIWEMessage: true, |
||||
isSIWEDomainValid: false, |
||||
parsedMessage: { |
||||
domain: 'baddomain.com', |
||||
address: selectedAddress, |
||||
statement: |
||||
'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos', |
||||
uri: 'http://localhost:8080', |
||||
version: '1', |
||||
nonce: 'STMt6KQMwwdOXE306', |
||||
chainId: 1, |
||||
issuedAt: '2022-03-18T21:40:40.823Z', |
||||
resources: [ |
||||
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu', |
||||
'https://example.com/my-web2-claim.json', |
||||
], |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
export const DefaultStory = (args) => { |
||||
return <SignatureRequestSIWE {...args} />; |
||||
}; |
||||
|
||||
DefaultStory.storyName = 'Default'; |
||||
|
||||
DefaultStory.args = { |
||||
txData: { |
||||
msgParams, |
||||
}, |
||||
}; |
||||
|
||||
export const BadDomainStory = (args) => { |
||||
return <SignatureRequestSIWE {...args} />; |
||||
}; |
||||
|
||||
BadDomainStory.args = { |
||||
txData: { |
||||
msgParams: badDomainParams, |
||||
}, |
||||
}; |
||||
|
||||
export const BadAddressStory = (args) => { |
||||
return <SignatureRequestSIWE {...args} />; |
||||
}; |
||||
|
||||
BadAddressStory.args = { |
||||
txData: { |
||||
msgParams: badAddressParams, |
||||
}, |
||||
}; |
||||
|
||||
export const BadDomainAndAddressStory = (args) => { |
||||
return <SignatureRequestSIWE {...args} />; |
||||
}; |
||||
|
||||
BadDomainAndAddressStory.args = { |
||||
txData: { |
||||
msgParams: badDomainAndAddressParams, |
||||
}, |
||||
}; |
Loading…
Reference in new issue