Skip to content

Commit

Permalink
feat: support safe account cancel tx (#1762)
Browse files Browse the repository at this point in the history
* wip: safe cancel

* feat: support safe cancel

* chore: fix some bugs

* feat: add staleTime

* feat: change preExec to parseTx

* chore: remove useless code

* fix: gnosis safe send page cache
  • Loading branch information
cs1707 authored Sep 22, 2023
1 parent c8d541a commit 293647e
Show file tree
Hide file tree
Showing 16 changed files with 1,054 additions and 173 deletions.
38 changes: 33 additions & 5 deletions _raw/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,24 @@
"trustValue": "Trust value",
"importedDelegatedAddress": "Imported delegated address",
"noDelegatedAddress": "No imported delegated address",
"coboSafeNotPermission": "This delegate address does not have permission to initiate this transaction"
"coboSafeNotPermission": "This delegate address does not have permission to initiate this transaction",
"SafeNonceSelector": {
"explain": {
"contractCall": "Contract Call",
"send": "Send Token",
"unknown": "Unknown Transaction"
},
"optionGroup": {
"recommendTitle": "Recommended nonce",
"replaceTitle": "Replace the transaction in Queue "
},
"option": {
"new": "New Transaction"
},
"error": {
"pendingList": "Fail to load pending transactions, <1/><2>Retry</2>"
}
}
},
"signFooterBar": {
"requestFrom": "Request from",
Expand Down Expand Up @@ -1392,9 +1409,19 @@
"approvalExplain": "Approve {{count}} {{token}} for {{protocol}}",
"unlimited": "unlimited",
"action": {
"send": "Send"
},
"viewBtn": "View"
"send": "Send",
"cancel": "Cancel Pending Transaction"
},
"viewBtn": "View",
"replaceBtn": "Replace",
"ReplacePopup": {
"options": {
"send": "Send Token",
"reject": "Reject Transaction"
},
"title": "Select how to replace this transaction",
"desc": " A signed transaction cannot be removed but it can be replaced with a new transaction with the same nonce."
}
},
"importSuccess": {
"title": "Imported Successfully",
Expand Down Expand Up @@ -1610,7 +1637,8 @@
"Loading": "Loading",
"nonce": "nonce",
"Balance": "Balance",
"Done": "Done"
"Done": "Done",
"Nonce": "Nonce"
},
"background": {
"error": {
Expand Down
25 changes: 22 additions & 3 deletions _raw/locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"Loading": "加载中",
"Balance": "余额",
"Done": "完成",
"nonce": "nonce"
"nonce": "nonce",
"Nonce": "Nonce"
},
"page": {
"transactions": {
Expand Down Expand Up @@ -1215,7 +1216,16 @@
"wrapToken": "Wrap 代币",
"contractPopularity": "No.{{0}} on {{1}}",
"myMarkWithContract": "My mark on {{chainName}} contract",
"coboSafeNotPermission": "This delegate address does not have permission to initiate this transaction"
"coboSafeNotPermission": "This delegate address does not have permission to initiate this transaction",
"SafeNonceSelector": {
"optionGroup": {
"recommendTitle": "推荐的Nonce",
"replaceTitle": "替换Queue中的交易"
},
"error": {
"pendingList": "Pending列表获取失败, <1/><2>重试</2>"
}
}
},
"signTypedData": {
"buyNFT": {
Expand Down Expand Up @@ -1417,7 +1427,16 @@
"action": {
"send": "发送"
},
"viewBtn": "查看"
"viewBtn": "查看",
"replaceBtn": "替换",
"ReplacePopup": {
"title": "请选择替换交易的方式",
"desc": "已经签名的交易无法被清除,但是可以通过发起一笔相同 Nonce的交易来替换。",
"options": {
"send": "发送代币",
"reject": "拒绝交易"
}
}
},
"importSuccess": {
"title": "成功导入",
Expand Down
12 changes: 12 additions & 0 deletions src/background/controller/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,18 @@ export class WalletController extends BaseController {
};
};

getGnosisPendingTxs = async (address: string, networkId: string) => {
if (!networkId) {
return [];
}
const safe = await createSafeService({
networkId: networkId,
address,
});
const { results } = await safe.getPendingTransactions();
return results;
};

getGnosisOwners = async (
account: Account,
safeAddress: string,
Expand Down
11 changes: 11 additions & 0 deletions src/ui/assets/safe-nonce-select/checked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/ui/assets/safe-nonce-select/down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/ui/assets/safe-nonce-select/find.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/ui/assets/safe-nonce-select/unchecked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions src/ui/views/Approval/components/Actions/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1389,3 +1389,30 @@ export const getActionTypeText = (data: ParsedActionData) => {
}
return t('page.signTx.unknownAction');
};

export const getActionTypeTextByType = (type: string) => {
const t = i18n.t;

const dict = {
swap_token: t('page.signTx.swap.title'),
cross_token: t('page.signTx.crossChain.title'),
cross_swap_token: t('page.signTx.swapAndCross.title'),
send_token: t('page.signTx.send.title'),
approve_token: t('page.signTx.tokenApprove.title'),
revoke_token: t('page.signTx.revokeTokenApprove.title'),
permit2_revoke_token: t('page.signTx.revokePermit2.title'),
wrap_token: t('page.signTx.wrapToken'),
unwrap_token: t('page.signTx.unwrap'),
send_nft: t('page.signTx.sendNFT.title'),
approve_nft: t('page.signTx.nftApprove.title'),
revoke_nft: t('page.signTx.revokeNFTApprove.title'),
approve_collection: t('page.signTx.nftCollectionApprove.title'),
revoke_collection: t('page.signTx.revokeNFTCollectionApprove.title'),
deploy_contract: t('page.signTx.deployContract.title'),
cancel_tx: t('page.signTx.cancelTx.title'),
push_multisig: t('page.signTx.submitMultisig.title'),
contract_call: t('page.signTx.contractCall.title'),
};

return dict[type] || t('page.signTx.unknownAction');
};
1 change: 1 addition & 0 deletions src/ui/views/Approval/components/FooterBar/FooterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const Shadow = styled.div`
rgba(175, 175, 175, 0.168147) 41.66%,
rgba(130, 130, 130, 0.35) 83.44%
);
z-index: 10;
`;

const ChainLogo = styled.img`
Expand Down
99 changes: 59 additions & 40 deletions src/ui/views/Approval/components/SignTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { TokenDetailPopup } from '@/ui/views/Dashboard/components/TokenDetailPop
import { useSignPermissionCheck } from '../hooks/useSignPermissionCheck';
import { useTestnetCheck } from '../hooks/useTestnetCheck';
import { CoboDelegatedDrawer } from './TxComponents/CoboDelegatedDrawer';
import { SafeNonceSelector } from './TxComponents/SafeNonceSelector';

interface BasicCoboArgusInfo {
address: string;
Expand Down Expand Up @@ -821,6 +822,7 @@ const SignTx = ({ params, origin }: SignTxProps) => {
isSend,
isSwap,
isViewGnosisSafe,
safeTxGas,
} = normalizeTxParams(params.data[0]);

let updateNonce = true;
Expand Down Expand Up @@ -1124,6 +1126,7 @@ const SignTx = ({ params, origin }: SignTxProps) => {
to: tx.to,
data: tx.data,
value: tx.value,
safeTxGas: safeTxGas,
};
params.nonce = realNonce;
await wallet.buildGnosisTransaction(
Expand All @@ -1139,6 +1142,7 @@ const SignTx = ({ params, origin }: SignTxProps) => {
data: [account.address, JSON.stringify(typedData)],
session: params.session,
isGnosis: true,
isSend,
account: account,
method: 'ethSignTypedDataV4',
uiRequestComponent: 'SignTypedData',
Expand Down Expand Up @@ -1838,45 +1842,58 @@ const SignTx = ({ params, origin }: SignTxProps) => {
engineResults={engineResults}
/>
)}
<GasSelector
disabled={isGnosisAccount || isCoboArugsAccount}
isReady={isReady}
gasLimit={gasLimit}
noUpdate={isCancel || isSpeedUp}
gasList={gasList}
selectedGas={selectedGas}
version={txDetail.pre_exec_version}
gas={{
error: txDetail.gas.error,
success: txDetail.gas.success,
gasCostUsd: gasExplainResponse.gasCostUsd,
gasCostAmount: gasExplainResponse.gasCostAmount,
}}
gasCalcMethod={(price) => {
return explainGas({
gasUsed,
gasPrice: price,
chainId,
nativeTokenPrice: txDetail?.native_token.price || 0,
tx,
wallet,
gasLimit,
});
}}
recommendGasLimit={recommendGasLimit}
recommendNonce={recommendNonce}
chainId={chainId}
onChange={handleGasChange}
nonce={realNonce || tx.nonce}
disableNonce={isSpeedUp || isCancel}
is1559={support1559}
isHardware={isHardware}
manuallyChangeGasLimit={manuallyChangeGasLimit}
errors={checkErrors}
engineResults={engineResults}
nativeTokenBalance={nativeTokenBalance}
gasPriceMedian={gasPriceMedian}
/>
{isGnosisAccount ? (
<SafeNonceSelector
isReady={isReady}
chainId={chainId}
value={realNonce}
safeInfo={safeInfo}
onChange={(v) => {
setRealNonce(v);
setNonceChanged(true);
}}
/>
) : (
<GasSelector
disabled={isGnosisAccount || isCoboArugsAccount}
isReady={isReady}
gasLimit={gasLimit}
noUpdate={isCancel || isSpeedUp}
gasList={gasList}
selectedGas={selectedGas}
version={txDetail.pre_exec_version}
gas={{
error: txDetail.gas.error,
success: txDetail.gas.success,
gasCostUsd: gasExplainResponse.gasCostUsd,
gasCostAmount: gasExplainResponse.gasCostAmount,
}}
gasCalcMethod={(price) => {
return explainGas({
gasUsed,
gasPrice: price,
chainId,
nativeTokenPrice: txDetail?.native_token.price || 0,
tx,
wallet,
gasLimit,
});
}}
recommendGasLimit={recommendGasLimit}
recommendNonce={recommendNonce}
chainId={chainId}
onChange={handleGasChange}
nonce={realNonce || tx.nonce}
disableNonce={isSpeedUp || isCancel}
is1559={support1559}
isHardware={isHardware}
manuallyChangeGasLimit={manuallyChangeGasLimit}
errors={checkErrors}
engineResults={engineResults}
nativeTokenBalance={nativeTokenBalance}
gasPriceMedian={gasPriceMedian}
/>
)}
</>
)}
{isGnosisAccount && safeInfo && (
Expand Down Expand Up @@ -1952,7 +1969,9 @@ const SignTx = ({ params, origin }: SignTxProps) => {
(isLedger && !useLedgerLive && !hasConnectedLedgerHID) ||
!canProcess ||
!!checkErrors.find((item) => item.level === 'forbidden') ||
hasUnProcessSecurityResult
hasUnProcessSecurityResult ||
(isGnosisAccount &&
new BigNumber(realNonce || 0).isLessThan(safeInfo?.nonce || 0))
}
/>
</>
Expand Down
9 changes: 8 additions & 1 deletion src/ui/views/Approval/components/SignTypedData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface SignTypedDataProps {
name: string;
};
isGnosis?: boolean;
isSend?: boolean;
account?: Account;
}

Expand Down Expand Up @@ -145,7 +146,7 @@ const SignTypedData = ({ params }: { params: SignTypedDataProps }) => {
}
}, [engineResults, currentTx]);

const { data, session, method, isGnosis, account } = params;
const { data, session, method, isGnosis, isSend, account } = params;
let parsedMessage = '';
let _message = '';
try {
Expand Down Expand Up @@ -316,6 +317,9 @@ const SignTypedData = ({ params }: { params: SignTypedDataProps }) => {
version: 'V4',
}
);
if (isSend) {
wallet.clearPageStateCache();
}
resolveApproval({
uiRequestComponent: WaitingSignMessageComponent[params.account.type],
type: params.account.type,
Expand Down Expand Up @@ -345,6 +349,9 @@ const SignTypedData = ({ params }: { params: SignTypedDataProps }) => {
await wallet.gnosisAddSignature(params.account.address, result);
await wallet.postGnosisTransaction();
}
if (isSend) {
wallet.clearPageStateCache();
}
resolveApproval(result, false, true);
} catch (e) {
message.error(e.message);
Expand Down
Loading

0 comments on commit 293647e

Please sign in to comment.