feat: 账户设置后端 action、Safe 部署与设置引导弹窗复用

账户设置:
- 后端: execute-setup-step API(步骤1 Safe 一键部署/步骤2 启用交易/步骤3 代币授权)
- Safe 部署: Eip712Encoder SafeCreate/CreateProxy,RelayClientService.deploySafeViaBuilderRelayer
- BuilderRelayerApi: SignatureParams 增加 paymentToken/payment/paymentReceiver,TransactionRequest.nonce 可选
- 导入成功有未完成步骤时先弹设置引导再关导入弹窗;设置弹窗复用 AccountSetupStatusBlock(embedded)
- AccountSetupStatusBlock: 5s 轮询、embedded 模式、onAllCompleted、移除全部完成 Tag、授权信息始终展示且右对齐额度列
- 多语言: 步骤1/2/3 文案与 actionSuccess/actionFailed

CryptoTail:
- SpreadDirection/SpreadMode 枚举及 Converter,V38 迁移,策略列表与 api/types 调整

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WrBug
2026-02-18 03:01:35 +08:00
co-authored by Cursor
parent 4e1bb0bbcf
commit de89175c8e
28 changed files with 1619 additions and 188 deletions
+40 -20
View File
@@ -149,7 +149,8 @@ const CryptoTailStrategyList: React.FC = () => {
enabled: true,
amountMode: 'RATIO',
maxPrice: '1',
minSpreadMode: 'AUTO',
spreadMode: 'AUTO',
spreadDirection: 'MIN',
windowStartMinutes: 0,
windowStartSeconds: 0
})
@@ -170,8 +171,9 @@ const CryptoTailStrategyList: React.FC = () => {
maxPrice: record.maxPrice,
amountMode: record.amountMode,
amountValue: record.amountValue,
minSpreadMode: record.minSpreadMode ?? 'AUTO',
minSpreadValue: record.minSpreadValue ?? undefined,
spreadMode: record.spreadMode ?? 'AUTO',
spreadValue: record.spreadValue ?? undefined,
spreadDirection: record.spreadDirection ?? 'MIN',
enabled: record.enabled
})
setFormModalOpen(true)
@@ -204,8 +206,9 @@ const CryptoTailStrategyList: React.FC = () => {
maxPrice: v.maxPrice != null ? String(v.maxPrice) : undefined,
amountMode: v.amountMode as string,
amountValue: String(v.amountValue ?? 0),
minSpreadMode: (v.minSpreadMode as string) || 'AUTO',
minSpreadValue: v.minSpreadMode === 'FIXED' && v.minSpreadValue != null ? String(v.minSpreadValue) : (v.minSpreadMode === 'AUTO' && v.minSpreadValue != null ? String(v.minSpreadValue) : undefined),
spreadMode: (v.spreadMode as string) || 'AUTO',
spreadValue: v.spreadMode === 'FIXED' && v.spreadValue != null ? String(v.spreadValue) : (v.spreadMode === 'AUTO' && v.spreadValue != null ? String(v.spreadValue) : undefined),
spreadDirection: v.spreadDirection as string || 'MIN',
enabled: v.enabled !== false
}
if (editingId) {
@@ -218,8 +221,9 @@ const CryptoTailStrategyList: React.FC = () => {
maxPrice: payload.maxPrice,
amountMode: payload.amountMode,
amountValue: payload.amountValue,
minSpreadMode: payload.minSpreadMode,
minSpreadValue: payload.minSpreadValue,
spreadMode: payload.spreadMode,
spreadValue: payload.spreadValue,
spreadDirection: payload.spreadDirection,
enabled: payload.enabled
})
if (res.data.code === 0) {
@@ -232,7 +236,7 @@ const CryptoTailStrategyList: React.FC = () => {
} else {
const res = await apiService.cryptoTailStrategy.create({
...payload,
minSpreadValue: payload.minSpreadMode === 'FIXED' ? payload.minSpreadValue : undefined
spreadValue: payload.spreadMode === 'FIXED' ? payload.spreadValue : undefined
})
if (res.data.code === 0) {
message.success(t('common.success'))
@@ -740,7 +744,7 @@ const CryptoTailStrategyList: React.FC = () => {
destroyOnClose
>
<Alert type="warning" showIcon message={t('cryptoTailStrategy.form.walletTip')} style={{ marginBottom: 16 }} />
<Form form={form} layout="vertical" initialValues={{ amountMode: 'RATIO', maxPrice: '1', minSpreadMode: 'AUTO', enabled: true }}>
<Form form={form} layout="vertical" initialValues={{ amountMode: 'RATIO', maxPrice: '1', spreadMode: 'AUTO', spreadDirection: 'MIN', enabled: true }}>
<Form.Item name="accountId" label={t('cryptoTailStrategy.form.selectAccount')} rules={[{ required: true }]}>
<Select
placeholder={t('cryptoTailStrategy.form.selectAccount')}
@@ -834,37 +838,37 @@ const CryptoTailStrategyList: React.FC = () => {
}
</Form.Item>
<Form.Item
name="minSpreadMode"
name="spreadMode"
label={
<Space size={4}>
<span>{t('cryptoTailStrategy.form.minSpreadMode')}</span>
<Tooltip title={t('cryptoTailStrategy.form.minSpreadModeTip')}>
<span>{t('cryptoTailStrategy.form.spreadMode')}</span>
<Tooltip title={t('cryptoTailStrategy.form.spreadModeTip')}>
<InfoCircleOutlined style={{ color: '#999', cursor: 'help', fontSize: 14 }} />
</Tooltip>
</Space>
}
>
<Radio.Group>
<Radio value="AUTO">{t('cryptoTailStrategy.form.minSpreadModeAuto')}</Radio>
<Radio value="FIXED">{t('cryptoTailStrategy.form.minSpreadModeFixed')}</Radio>
<Radio value="NONE">{t('cryptoTailStrategy.form.minSpreadModeNone')}</Radio>
<Radio value="AUTO">{t('cryptoTailStrategy.form.spreadModeAuto')}</Radio>
<Radio value="FIXED">{t('cryptoTailStrategy.form.spreadModeFixed')}</Radio>
<Radio value="NONE">{t('cryptoTailStrategy.form.spreadModeNone')}</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prev, curr) => prev.minSpreadMode !== curr.minSpreadMode}
shouldUpdate={(prev, curr) => prev.spreadMode !== curr.spreadMode}
>
{({ getFieldValue }) =>
getFieldValue('minSpreadMode') === 'FIXED' ? (
getFieldValue('spreadMode') === 'FIXED' ? (
<Form.Item
name="minSpreadValue"
label={t('cryptoTailStrategy.form.minSpreadValue')}
name="spreadValue"
label={t('cryptoTailStrategy.form.spreadValue')}
rules={[{ required: true }]}
>
<InputNumber
min={0}
step={1}
placeholder={t('cryptoTailStrategy.form.minSpreadValuePlaceholder')}
placeholder={t('cryptoTailStrategy.form.spreadValuePlaceholder')}
style={{ width: '100%' }}
stringMode
/>
@@ -872,6 +876,22 @@ const CryptoTailStrategyList: React.FC = () => {
) : null
}
</Form.Item>
<Form.Item
name="spreadDirection"
label={
<Space size={4}>
<span>{t('cryptoTailStrategy.form.spreadDirection')}</span>
<Tooltip title={t('cryptoTailStrategy.form.spreadDirectionTip')}>
<InfoCircleOutlined style={{ color: '#999', cursor: 'help', fontSize: 14 }} />
</Tooltip>
</Space>
}
>
<Radio.Group>
<Radio value="MIN">{t('cryptoTailStrategy.form.spreadDirectionMin')}</Radio>
<Radio value="MAX">{t('cryptoTailStrategy.form.spreadDirectionMax')}</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="enabled" valuePropName="checked">
<Switch checkedChildren={t('common.enabled')} unCheckedChildren={t('common.disabled')} />
</Form.Item>