Skip to content

Commit

Permalink
Merge pull request #513 from BitGo/WIN-3693
Browse files Browse the repository at this point in the history
feat(xrp): xrpl token support for non-bitgo recovery and build unsign…
  • Loading branch information
nvrakesh06 authored Nov 27, 2024
2 parents c177a9e + f063585 commit c76121e
Show file tree
Hide file tree
Showing 10 changed files with 950 additions and 398 deletions.
Binary file added .DS_Store
Binary file not shown.
5 changes: 4 additions & 1 deletion electron/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import { Polygon, Tpolygon, PolygonToken } from '@bitgo/sdk-coin-polygon';
import { Sol, Tsol, SolToken } from '@bitgo/sdk-coin-sol';
import { Trx, Ttrx, TrxToken } from '@bitgo/sdk-coin-trx';
import { Txlm, Xlm } from '@bitgo/sdk-coin-xlm';
import { Txrp, Xrp } from '@bitgo/sdk-coin-xrp';
import { Txrp, Xrp, XrpToken } from '@bitgo/sdk-coin-xrp';
import { Zec } from '@bitgo/sdk-coin-zec';
import { Zeta, Tzeta } from '@bitgo/sdk-coin-zeta';
import { Bsc, Tbsc } from '@bitgo/sdk-coin-bsc';
Expand Down Expand Up @@ -175,6 +175,9 @@ SolToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
TrxToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
});
XrpToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
sdk.register(name, coinConstructor);
});

function handleSdkError(e: unknown): string {
if (typeof e === 'string' && e !== null) {
Expand Down
847 changes: 493 additions & 354 deletions package-lock.json

Large diffs are not rendered by default.

84 changes: 42 additions & 42 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,49 @@
}
},
"dependencies": {
"@bitgo/abstract-cosmos": "11.1.0",
"@bitgo/abstract-utxo": "9.1.0",
"@bitgo/sdk-api": "1.56.1",
"@bitgo/sdk-coin-ada": "4.3.2",
"@bitgo/sdk-coin-algo": "2.1.41",
"@bitgo/sdk-coin-arbeth": "21.0.35",
"@bitgo/sdk-coin-atom": "13.0.12",
"@bitgo/sdk-coin-avaxc": "5.1.4",
"@bitgo/sdk-coin-bch": "2.0.43",
"@bitgo/sdk-coin-bcha": "2.1.27",
"@bitgo/sdk-coin-bld": "3.0.12",
"@bitgo/sdk-coin-bsc": "22.1.27",
"@bitgo/sdk-coin-bsv": "2.0.43",
"@bitgo/sdk-coin-btc": "2.3.4",
"@bitgo/sdk-coin-btg": "2.0.43",
"@bitgo/sdk-coin-coreum": "21.0.12",
"@bitgo/sdk-coin-dash": "2.0.43",
"@bitgo/sdk-coin-doge": "2.0.43",
"@bitgo/sdk-coin-dot": "4.1.21",
"@bitgo/sdk-coin-eos": "2.1.36",
"@bitgo/sdk-coin-etc": "2.2.10",
"@bitgo/sdk-coin-eth": "24.2.32",
"@bitgo/sdk-coin-ethlike": "1.1.24",
"@bitgo/sdk-coin-ethw": "20.0.43",
"@bitgo/sdk-coin-hash": "3.0.12",
"@bitgo/sdk-coin-hbar": "2.0.43",
"@bitgo/sdk-coin-injective": "3.0.12",
"@bitgo/sdk-coin-ltc": "3.0.43",
"@bitgo/sdk-coin-near": "2.0.43",
"@bitgo/sdk-coin-opeth": "18.1.28",
"@bitgo/sdk-coin-osmo": "3.0.12",
"@bitgo/sdk-coin-polygon": "21.0.12",
"@bitgo/sdk-coin-sei": "3.0.12",
"@bitgo/sdk-coin-sol": "4.7.0",
"@bitgo/sdk-coin-sui": "5.8.2",
"@bitgo/sdk-coin-tia": "3.0.12",
"@bitgo/sdk-coin-trx": "2.0.43",
"@bitgo/sdk-coin-xlm": "3.2.17",
"@bitgo/sdk-coin-xrp": "3.3.0",
"@bitgo/sdk-coin-zec": "2.0.43",
"@bitgo/sdk-coin-zeta": "3.0.12",
"@bitgo/abstract-cosmos": "11.2.4",
"@bitgo/abstract-utxo": "9.2.1",
"@bitgo/sdk-api": "1.56.6",
"@bitgo/sdk-coin-ada": "4.3.7",
"@bitgo/sdk-coin-algo": "2.1.46",
"@bitgo/sdk-coin-arbeth": "21.0.40",
"@bitgo/sdk-coin-atom": "13.1.4",
"@bitgo/sdk-coin-avaxc": "5.1.9",
"@bitgo/sdk-coin-bch": "2.0.48",
"@bitgo/sdk-coin-bcha": "2.1.32",
"@bitgo/sdk-coin-bld": "3.0.17",
"@bitgo/sdk-coin-bsc": "22.2.1",
"@bitgo/sdk-coin-bsv": "2.0.48",
"@bitgo/sdk-coin-btc": "2.3.9",
"@bitgo/sdk-coin-btg": "2.0.48",
"@bitgo/sdk-coin-coreum": "21.0.17",
"@bitgo/sdk-coin-dash": "2.0.48",
"@bitgo/sdk-coin-doge": "2.0.48",
"@bitgo/sdk-coin-dot": "4.1.26",
"@bitgo/sdk-coin-eos": "2.1.41",
"@bitgo/sdk-coin-etc": "2.2.15",
"@bitgo/sdk-coin-eth": "24.2.37",
"@bitgo/sdk-coin-ethlike": "1.1.29",
"@bitgo/sdk-coin-ethw": "20.0.48",
"@bitgo/sdk-coin-hash": "3.0.17",
"@bitgo/sdk-coin-hbar": "2.0.48",
"@bitgo/sdk-coin-injective": "3.0.17",
"@bitgo/sdk-coin-ltc": "3.0.48",
"@bitgo/sdk-coin-near": "2.0.48",
"@bitgo/sdk-coin-opeth": "18.1.33",
"@bitgo/sdk-coin-osmo": "3.0.17",
"@bitgo/sdk-coin-polygon": "21.0.17",
"@bitgo/sdk-coin-sei": "3.0.17",
"@bitgo/sdk-coin-sol": "4.7.5",
"@bitgo/sdk-coin-sui": "5.8.7",
"@bitgo/sdk-coin-tia": "3.0.17",
"@bitgo/sdk-coin-trx": "2.0.48",
"@bitgo/sdk-coin-xlm": "3.2.22",
"@bitgo/sdk-coin-xrp": "3.6.1",
"@bitgo/sdk-coin-zec": "2.0.48",
"@bitgo/sdk-coin-zeta": "3.0.17",
"@bitgo/sdk-opensslbytes": "2.0.0",
"@bitgo/utxo-lib": "11.0.1",
"@bitgo/utxo-lib": "11.1.0",
"@ethereumjs/common": "2.6.5",
"@lottiefiles/react-lottie-player": "3.4.9",
"clsx": "1.2.1",
Expand Down
65 changes: 65 additions & 0 deletions src/containers/BuildUnsignedSweepCoin/BuildUnsignedSweepCoin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { BackToHomeHelperText } from '~/components/BackToHomeHelperText';
import { buildUnsignedSweepCoins, tokenParentCoins } from '~/helpers/config';
import { HederaForm } from './HederaForm';
import { AlgorandForm } from '~/containers/BuildUnsignedSweepCoin/AlgorandForm';
import { RippleTokenForm } from '~/containers/BuildUnsignedSweepCoin/RippleTokenForm';

function Form() {
const { env, coin } = useParams<'env' | 'coin'>();
Expand Down Expand Up @@ -467,6 +468,70 @@ 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 'txrpToken':
return (
<RippleTokenForm
key={coin}
onSubmit={async (values, { setSubmitting }) => {
setAlert(undefined);
setSubmitting(true);
try {
await window.commands.setBitGoEnvironment(bitGoEnvironment, coin);
const parentCoin = tokenParentCoins[coin];
const chainData = coin;
const recoverData = await window.commands.recover(parentCoin, {
...(await updateKeysFromIds(coin, values)),
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(
includePubsInUnsignedSweep
? {
...recoverData,
...(await includePubsFor(coin, values)),
}
: recoverData,
null,
2
),
{ encoding: 'utf-8' }
);

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

const validationSchema = Yup.object({
backupKey: Yup.string().required(),
backupKeyId: Yup.string(),
recoveryDestination: Yup.string().required(),
rootAddress: Yup.string().required(),
userKey: Yup.string().required(),
userKeyId: Yup.string(),
issuerAddress: Yup.string().required(),
currencyCode: Yup.string().required(),
}).required();

export type RippleTokenFormProps = {
onSubmit: (
values: RippleTokenFormValues,
formikHelpers: FormikHelpers<RippleTokenFormValues>
) => void | Promise<void>;
};

type RippleTokenFormValues = Yup.Asserts<typeof validationSchema>;

export function RippleTokenForm({ onSubmit }: RippleTokenFormProps) {
const formik = useFormik<RippleTokenFormValues>({
onSubmit,
initialValues: {
backupKey: '',
backupKeyId: '',
recoveryDestination: '',
rootAddress: '',
userKey: '',
userKeyId: '',
issuerAddress: '',
currencyCode: '',
},
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 user public key, as found on your recovery KeyCard."
Label="User Public Key"
name="userKey"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Your user Key ID, as found on your KeyCard. Most wallets will not have this and you can leave it blank."
Label="User Key ID (optional)"
name="userKeyId"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="The backup public key for the wallet, as found on your recovery KeyCard."
Label="Backup Public Key"
name="backupKey"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Your backup Key ID, as found on your KeyCard. Most wallets will not have this and you can leave it blank."
Label="Backup Key ID (optional)"
name="backupKeyId"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="The root address of the wallet."
Label="Root Address"
name="rootAddress"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="The issuer address of the token."
Label="Token Issuer Address"
name="issuerAddress"
Width="fill"
/>
</div>
<div className="tw-mb-4">
<FormikTextfield
HelperText="Token Currency Code"
Label="Token Currency Code"
name="currencyCode"
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-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>
);
}
56 changes: 56 additions & 0 deletions src/containers/NonBitGoRecoveryCoin/NonBitGoRecoveryCoin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TronTokenForm } from './TronTokenForm';
import { AvalancheCTokenForm } from './AvalancheCTokenForm';
import { HederaForm } from './HederaForm';
import { AlgorandForm } from '~/containers/NonBitGoRecoveryCoin/AlgorandForm';
import { RippleTokenForm } from '~/containers/NonBitGoRecoveryCoin/RippleTokenForm';

function Form() {
const { env, coin } = useParams<'env' | 'coin'>();
Expand Down Expand Up @@ -638,6 +639,61 @@ function Form() {
{ encoding: 'utf-8' }
);

navigate(
`/${bitGoEnvironment}/non-bitgo-recovery/${coin}/success`
);
} catch (err) {
if (err instanceof Error) {
setAlert(err.message);
} else {
console.error(err);
}
setSubmitting(false);
}
}}
/>
);
case 'txrpToken':
return (
<RippleTokenForm
key={coin}
onSubmit={async (values, { setSubmitting }) => {
setAlert(undefined);
setSubmitting(true);
try {
await window.commands.setBitGoEnvironment(bitGoEnvironment, coin);
const parentCoin = tokenParentCoins[coin];
const chainData = coin;
const recoverData = await window.commands.recover(parentCoin, {
...values,
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}-recovery-${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}/non-bitgo-recovery/${coin}/success`
);
Expand Down
Loading

0 comments on commit c76121e

Please sign in to comment.