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

UX for revoking token spend approval #953

Merged
merged 13 commits into from
Jul 20, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ function nFormat (n, digits = 2) {
class TokenSpend extends React.Component {
constructor (...args) {
super(...args)
const { approval: { data } } = this.props

this.decimals = this.props.approval.data.decimals || 0
this.requestedAmount = '0x' + new BigNumber(this.props.approval.data.amount).integerValue().toString(16)
this.decimals = data.decimals || 0
this.requestedAmount = '0x' + new BigNumber(data.amount).integerValue().toString(16)
this.state = {
inPreview: false,
inEditApproval: false,
Expand Down Expand Up @@ -85,7 +86,7 @@ class TokenSpend extends React.Component {
}

render () {
const { req, approval } = this.props
const { req, revoke, approval, onApprove, onDecline } = this.props
const { data } = approval

const displayInt = new BigNumber(this.state.amount).shiftedBy(-this.decimals).integerValue()
Expand All @@ -95,7 +96,7 @@ class TokenSpend extends React.Component {
symbol: 'unlimited'
} : displayInt > 9e12 ? {
number: '',
symbol: this.props.approval.data.decimals ? '~unlimited' : 'unknown'
symbol: approval.data.decimals ? '~unlimited' : 'unknown'
} : nFormat(displayInt)

const symbol = data.symbol || '???'
Expand All @@ -113,23 +114,25 @@ class TokenSpend extends React.Component {
opacity: 0,
pointerEvents: 'none'
} : {}}
onClick={() => this.props.onDecline(req)}
onClick={() => onDecline(req)}
>
Reject
</div>
<div
className={this.state.inEditApproval ? 'approveTokenSpendEditButton approveTokenSpendDoneButton' : 'approveTokenSpendEditButton'}
role='button'
onClick={() => {
if (this.state.inEditApproval) {
this.doneEditing()
} else {
this.startEditing()
}
}}
>
{this.state.inEditApproval ? 'Done' : 'Edit' }
</div>
{!revoke &&
<div
className={this.state.inEditApproval ? 'approveTokenSpendEditButton approveTokenSpendDoneButton' : 'approveTokenSpendEditButton'}
role='button'
onClick={() => {
if (this.state.inEditApproval) {
this.doneEditing()
} else {
this.startEditing()
}
}}
>
{this.state.inEditApproval ? 'Done' : 'Edit' }
</div>
}
<div
className='approveTransactionWarningProceed'
role='button'
Expand All @@ -138,9 +141,9 @@ class TokenSpend extends React.Component {
pointerEvents: 'none'
} : {}}
onClick={() => {
this.props.onApprove(
this.props.req,
ApprovalType.TokenSpendApproval,
onApprove(
req,
revoke ? ApprovalType.TokenSpendRevocation : ApprovalType.TokenSpendApproval,
{ amount: this.state.amount }
)
}}
Expand All @@ -155,7 +158,7 @@ class TokenSpend extends React.Component {
<div className='approveTransactionWarningIcon approveTransactionWarningIconRight'>
{svg.alert(32)}
</div>
<div className='approveTransactionWarningTitle'>{'token approval'}</div>
<div className='approveTransactionWarningTitle'>{revoke ? 'revoke token approval' : 'token approval'}</div>
{this.state.inEditApproval ? (
<div className={'approveTokenSpend'}>
{this.state.exiting ? (
Expand Down Expand Up @@ -267,7 +270,7 @@ class TokenSpend extends React.Component {
</div>
) : null}
<div className='approveTokenSpendSub'>
{'wants approval to spend'}
{revoke ? 'wants to revoke approval to spend' : 'wants approval to spend'}
</div>
<div className='approveTokenSpendToken'>
<div className='approveTokenSpendTokenSymbol'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ApprovalType } from '../../../../../../../../resources/constants'
import { BasicApproval, TokenSpend } from './approvals'

const supportedApprovals = [
ApprovalType.GasLimitApproval, ApprovalType.OtherChainApproval, ApprovalType.TokenSpendApproval
ApprovalType.GasLimitApproval, ApprovalType.OtherChainApproval, ApprovalType.TokenSpendApproval, ApprovalType.TokenSpendRevocation
]

class TxApproval extends React.Component {
Expand Down Expand Up @@ -59,6 +59,18 @@ class TxApproval extends React.Component {
/>
)
}

if (approval.type === ApprovalType.TokenSpendRevocation) {
return (
<TokenSpend
req={req}
approval={approval}
onApprove={this.approve}
onDecline={this.decline}
revoke
/>
)
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion main/accounts/Account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getType as getSignerType, Type as SignerType } from '../../signers/Sign
import provider from '../../provider'
import { ApprovalType } from '../../../resources/constants'
import Erc20Contract from '../../contracts/erc20'
import { hexToNumber } from 'web3-utils'

const nebula = nebulaApi()

Expand Down Expand Up @@ -257,7 +258,7 @@ class FrameAccount {

this.addRequiredApproval(
req,
ApprovalType.TokenSpendApproval,
hexToNumber(amount) === 0 ? ApprovalType.TokenSpendRevocation : ApprovalType.TokenSpendApproval,
{
decimals,
name,
Expand Down
3 changes: 2 additions & 1 deletion resources/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export enum ApprovalType {
OtherChainApproval = 'approveOtherChain',
GasLimitApproval = 'approveGasLimit',
TokenSpendApproval = 'approveTokenSpend'
TokenSpendApproval = 'approveTokenSpend',
TokenSpendRevocation = 'revokeTokenSpend'
}

const ADDRESS_DISPLAY_CHARS = 8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`revoking an approval should render the expected HTML 1`] = `
<div>
<div
class="approveTransactionWarning"
>
<div
class="approveTransactionWarningOptions"
>
<div
class="approveTransactionWarningReject"
role="button"
>
Reject
</div>
<div
class="approveTransactionWarningProceed"
role="button"
>
Proceed
</div>
</div>
<div
class="approveTransactionWarningFill"
>
<div
class="approveTransactionWarningIcon approveTransactionWarningIconLeft"
>
<div
style="width: 32px; height: 32px;"
>
<svg
height="32px"
viewBox="0 0 24 24"
width="32px"
>
<path
d="M13 17.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-8.25a.75.75 0 00-1.5 0v4.5a.75.75 0 001.5 0v-4.5z"
fill="currentColor"
/>
<path
d="M9.836 3.244c.963-1.665 3.365-1.665 4.328 0l8.967 15.504c.963 1.667-.24 3.752-2.165 3.752H3.034c-1.926 0-3.128-2.085-2.165-3.752L9.836 3.244zm3.03.751a1 1 0 00-1.732 0L2.168 19.499A1 1 0 003.034 21h17.932a1 1 0 00.866-1.5L12.866 3.994z"
fill="currentColor"
/>
</svg>
</div>
</div>
<div
class="approveTransactionWarningIcon approveTransactionWarningIconRight"
>
<div
style="width: 32px; height: 32px;"
>
<svg
height="32px"
viewBox="0 0 24 24"
width="32px"
>
<path
d="M13 17.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-8.25a.75.75 0 00-1.5 0v4.5a.75.75 0 001.5 0v-4.5z"
fill="currentColor"
/>
<path
d="M9.836 3.244c.963-1.665 3.365-1.665 4.328 0l8.967 15.504c.963 1.667-.24 3.752-2.165 3.752H3.034c-1.926 0-3.128-2.085-2.165-3.752L9.836 3.244zm3.03.751a1 1 0 00-1.732 0L2.168 19.499A1 1 0 003.034 21h17.932a1 1 0 00.866-1.5L12.866 3.994z"
fill="currentColor"
/>
</svg>
</div>
</div>
<div
class="approveTransactionWarningTitle"
>
revoke token approval
</div>
<div
class="approveTokenSpend"
>
<div
class="approveTokenSpendDescription"
>
<div
class="approveTokenSpendSub"
>
wants to revoke approval to spend
</div>
<div
class="approveTokenSpendToken"
>
<div
class="approveTokenSpendTokenSymbol"
>
???
</div>
<div
class="approveTokenSpendTokenContract"
/>
</div>
<div
class="approveTokenSpendTokenName"
>
Unknown Token
</div>
</div>
</div>
</div>
</div>
</div>
`;

exports[`should render the expected HTML 1`] = `
<div>
<div
class="approveTransactionWarning"
>
<div
class="approveTransactionWarningOptions"
>
<div
class="approveTransactionWarningReject"
role="button"
>
Reject
</div>
<div
class="approveTokenSpendEditButton"
role="button"
>
Edit
</div>
<div
class="approveTransactionWarningProceed"
role="button"
>
Proceed
</div>
</div>
<div
class="approveTransactionWarningFill"
>
<div
class="approveTransactionWarningIcon approveTransactionWarningIconLeft"
>
<div
style="width: 32px; height: 32px;"
>
<svg
height="32px"
viewBox="0 0 24 24"
width="32px"
>
<path
d="M13 17.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-8.25a.75.75 0 00-1.5 0v4.5a.75.75 0 001.5 0v-4.5z"
fill="currentColor"
/>
<path
d="M9.836 3.244c.963-1.665 3.365-1.665 4.328 0l8.967 15.504c.963 1.667-.24 3.752-2.165 3.752H3.034c-1.926 0-3.128-2.085-2.165-3.752L9.836 3.244zm3.03.751a1 1 0 00-1.732 0L2.168 19.499A1 1 0 003.034 21h17.932a1 1 0 00.866-1.5L12.866 3.994z"
fill="currentColor"
/>
</svg>
</div>
</div>
<div
class="approveTransactionWarningIcon approveTransactionWarningIconRight"
>
<div
style="width: 32px; height: 32px;"
>
<svg
height="32px"
viewBox="0 0 24 24"
width="32px"
>
<path
d="M13 17.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-8.25a.75.75 0 00-1.5 0v4.5a.75.75 0 001.5 0v-4.5z"
fill="currentColor"
/>
<path
d="M9.836 3.244c.963-1.665 3.365-1.665 4.328 0l8.967 15.504c.963 1.667-.24 3.752-2.165 3.752H3.034c-1.926 0-3.128-2.085-2.165-3.752L9.836 3.244zm3.03.751a1 1 0 00-1.732 0L2.168 19.499A1 1 0 003.034 21h17.932a1 1 0 00.866-1.5L12.866 3.994z"
fill="currentColor"
/>
</svg>
</div>
</div>
<div
class="approveTransactionWarningTitle"
>
token approval
</div>
<div
class="approveTokenSpend"
>
<div
class="approveTokenSpendDescription"
>
<div
class="approveTokenSpendSub"
>
wants approval to spend
</div>
<div
class="approveTokenSpendToken"
>
<div
class="approveTokenSpendTokenSymbol"
>
???
</div>
<div
class="approveTokenSpendTokenContract"
/>
</div>
<div
class="approveTokenSpendTokenName"
>
Unknown Token
</div>
</div>
</div>
</div>
</div>
</div>
`;
Loading