Skip to content

Commit

Permalink
feat: adding new form for unsinged sweep for MPCv2 EVM coins.
Browse files Browse the repository at this point in the history
Ticket: WIN-4290
  • Loading branch information
parasgarg-bitgo committed Jan 16, 2025
1 parent 4ee3873 commit 75ecd2e
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 0 deletions.
80 changes: 80 additions & 0 deletions src/containers/BuildUnsignedSweepCoin/BuildUnsignedSweepCoin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { BitcoinForm } from './BitcoinForm';
import { BitcoinABCForm } from './BitcoinABCForm';
import { EthLikeTokenForm } from './EthLikeTokenForm';
import { EthLikeForm } from './EthLikeForm';
import { EthLikeFormTSS } from './EthLikeFormTSS';
import { EthereumWForm } from './EthereumWForm';
import { LitecoinForm } from './LitecoinForm';
import { PolygonForm } from './PolygonForm';
Expand Down Expand Up @@ -187,6 +188,85 @@ function Form() {
{ encoding: 'utf-8' }
);

navigate(
`/${bitGoEnvironment}/build-unsigned-sweep/${coin}/success`
);
} catch (err) {
if (err instanceof Error) {
setAlert(err.message);
} else {
console.error(err);
}
setSubmitting(false);
}
}}
/>
);
case 'coredao':
case 'tcoredao':
case 'oas':
case 'toas':
return (
<EthLikeFormTSS
key={coin}
coinName={coin}
onSubmit={async (values, { setSubmitting }) => {
setAlert(undefined);
setSubmitting(true);
try {
await window.commands.setBitGoEnvironment(
bitGoEnvironment,
coin,
values.apiKey
);
const chainData = await window.queries.getChain(coin);

const { maxFeePerGas, maxPriorityFeePerGas, ...rest } = values;
const recoverData = await window.commands.recover(coin, {
...rest,
userKey: values.commonKeyChain,
backupKey: values.commonKeyChain,
eip1559: getEip1559Params(
coin,
maxFeePerGas,
maxPriorityFeePerGas
),
replayProtectionOptions: {
chain: getEthLikeRecoveryChainId(coin, bitGoEnvironment),
hardfork: 'london',
},
bitgoKey: '',
ignoreAddressTypes: [],
});
assert(
isRecoveryTransaction(recoverData),
'Fully-signed recovery transaction not detected.'
);

const showSaveDialogData = await window.commands.showSaveDialog({
filters: [
{
name: 'Custom File Type',
extensions: ['json'],
},
],
defaultPath: `~/${chainData}-unsigned-sweep-${Date.now()}.json`,
});

if (!showSaveDialogData.filePath) {
throw new Error('No file path selected');
}

await window.commands.writeFile(
showSaveDialogData.filePath,
JSON.stringify(
recoverData,
null,
2
),
{ encoding: 'utf-8' }
);

navigate(
`/${bitGoEnvironment}/build-unsigned-sweep/${coin}/success`
);
Expand Down
153 changes: 153 additions & 0 deletions src/containers/BuildUnsignedSweepCoin/EthLikeFormTSS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { Form, FormikHelpers, FormikProvider, useFormik } from 'formik';
import { Link } from 'react-router-dom';
import * as Yup from 'yup';
import { Button, FormikTextfield } from '~/components';
import { allCoinMetas } from '~/helpers/config';

const validationSchema = Yup.object({
apiKey: Yup.string().required(),
commonKeyChain: Yup.string().required(),
derivationPath: Yup.string(),
derivationSeed: Yup.string(),
gasLimit: Yup.number()
.typeError('Gas limit must be a number')
.integer()
.positive('Gas limit must be a positive integer')
.required(),
maxFeePerGas: Yup.number().required(),
maxPriorityFeePerGas: Yup.number().required(),
recoveryDestination: Yup.string().required(),
walletContractAddress: Yup.string().required(),
isTss: Yup.boolean(),
}).required();

export type EthLikeFormTSSProps = {
coinName: string;
onSubmit: (
values: EthLikeFormTSSValues,
formikHelpers: FormikHelpers<EthLikeFormTSSValues>
) => void | Promise<void>;
};

type EthLikeFormTSSValues = Yup.Asserts<typeof validationSchema>;

export function EthLikeFormTSS({ onSubmit, coinName }: EthLikeFormTSSProps) {
const formik = useFormik<EthLikeFormTSSValues>({
onSubmit,
initialValues: {
apiKey: '',
commonKeyChain: '',
derivationPath: '',
derivationSeed: '',
gasLimit: allCoinMetas[coinName]?.defaultGasLimitNum ?? 500000,
maxFeePerGas: 20,
maxPriorityFeePerGas: 10,
recoveryDestination: '',
walletContractAddress: '',
isTss: true,
},
validationSchema,
});

return (
<FormikProvider value={formik}>
<Form>
<h4 className="tw-text-body tw-font-semibold tw-border-b-0.5 tw-border-solid tw-border-gray-700 tw-mb-4">
Self-managed cold wallet details
</h4>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Your common key chain, as found on your recovery KeyCard."
Label="Common Key Chain"
name="commonKeyChain"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Your key derivation path, as found on your KeyCard. Most wallets will not have this and you can leave it blank."
Label="Key Derivation Path (optional)"
name="derivationPath"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Your key derivation seed, as found on your KeyCard. Most wallets will not have this and you can leave it blank."
Label="Key Derivation Seed (optional)"
name="derivationSeed"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="This is the wallet's base address."
Label="Wallet Base Address"
name="walletContractAddress"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="The address your recovery transaction will send to."
Label="Destination Address"
name="recoveryDestination"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText={`Gas limit for the ETH transaction. The value should be between ${
allCoinMetas[coinName].minGasLimit ?? '30,000'
} and 20,000,000. The default is ${
allCoinMetas[coinName].defaultGasLimit ?? '500,000'
} unit of gas.`}
Label="Gas Limit"
name="gasLimit"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Max fee per gas for the ETH transaction. The default is 20 Gwei."
Label="Max Fee Per Gas (Gwei)"
name="maxFeePerGas"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText='"Tip" to the ETH miner. This is by default 10 Gwei.'
Label="Max Priority Fee Per Gas (Gwei)"
name="maxPriorityFeePerGas"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText={`An Api-Key Token from ${
allCoinMetas[coinName].ApiKeyProvider ?? 'etherscan.com'
} required for recoveries.`}
Label="API Key"
name="apiKey"
Width="fill"
/>
</div>
<div className="tw-flex tw-flex-col-reverse sm:tw-justify-between sm:tw-flex-row tw-gap-1 tw-mt-4">
<Button Tag={Link} to="/" Variant="secondary" Width="hug">
Cancel
</Button>
<Button
Variant="primary"
Width="hug"
type="submit"
Disabled={formik.isSubmitting}
disabled={formik.isSubmitting}
>
{formik.isSubmitting ? 'Recovering...' : 'Recover Funds'}
</Button>
</div>
</Form>
</FormikProvider>
);
}

0 comments on commit 75ecd2e

Please sign in to comment.