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

chore: cross chain swap tx submit #27262

Draft
wants to merge 12 commits into
base: mb890-src-and-dest-asset-pickers-backup-0828
Choose a base branch
from
4 changes: 4 additions & 0 deletions shared/constants/security-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ export const SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES = [
TransactionType.swap,
TransactionType.swapApproval,
TransactionType.swapAndSend,
'bridgeApproval',
'bridge',
// TransactionType.bridgeApproval, // TODO
// TransactionType.bridge, // TODO
];

export const LOADING_SECURITY_ALERT_RESPONSE: SecurityAlertResponse = {
Expand Down
229 changes: 226 additions & 3 deletions ui/ducks/bridge/actions.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { Hex } from '@metamask/utils';
import { TransactionType } from '@metamask/transaction-controller';
import { useHistory } from 'react-router-dom';
import {
BridgeBackgroundAction,
BridgeUserAction,
} from '../../../app/scripts/controllers/bridge/types';
import { forceUpdateMetamaskState } from '../../store/actions';
import {
addToken,
addTransactionAndWaitForPublish,
forceUpdateMetamaskState,
} from '../../store/actions';
import { submitRequestToBackground } from '../../store/background-connection';
import { MetaMaskReduxDispatch } from '../../store/store';
import { bridgeSlice } from './bridge';
import { MetaMaskReduxDispatch, MetaMaskReduxState } from '../../store/store';
import { Numeric } from '../../../shared/modules/Numeric';
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
import {
checkNetworkAndAccountSupports1559,
getNetworkConfigurations,
getSelectedNetworkClientId,
} from '../../selectors';
import { getGasFeeEstimates } from '../metamask/metamask';
import {
addHexes,
decGWEIToHexWEI,
} from '../../../shared/modules/conversion.utils';
import {
getCustomMaxFeePerGas,
getCustomMaxPriorityFeePerGas,
} from '../swaps/swaps';
import bridge, { bridgeSlice } from './bridge';
import { DUMMY_QUOTES_APPROVAL } from './dummy-quotes';

const {
setToChainId: setToChainId_,
Expand Down Expand Up @@ -65,3 +88,203 @@ export const setToChain = (chainId: Hex) => {
);
};
};

export const signBridgeTransaction = (
history: ReturnType<typeof useHistory>,
) => {
return async (
dispatch: MetaMaskReduxDispatch,
getState: () => MetaMaskReduxState,
) => {
const state = getState();

// TODO Check feature flags to see if enabled
// if (!isLive) {
// history.push(BRIDGE_MAINTENANCE_ROUTE);
// return;
// }

const quoteMeta = DUMMY_QUOTES_APPROVAL[0]; // TODO: actually use live quotes
// const quoteMeta = DUMMY_QUOTES_NO_APPROVAL[0]; // TODO: actually use live quotes

// Track event TODO

let maxFeePerGas;
let maxPriorityFeePerGas;
let baseAndPriorityFeePerGas;
let decEstimatedBaseFee;

const networkAndAccountSupports1559 =
checkNetworkAndAccountSupports1559(state);
const customMaxFeePerGas = getCustomMaxFeePerGas(state);
const customMaxPriorityFeePerGas = getCustomMaxPriorityFeePerGas(state);

if (networkAndAccountSupports1559) {
const {
high: { suggestedMaxFeePerGas, suggestedMaxPriorityFeePerGas },
estimatedBaseFee = '0',
} = getGasFeeEstimates(state);
decEstimatedBaseFee = decGWEIToHexWEI(estimatedBaseFee);
maxFeePerGas =
customMaxFeePerGas || decGWEIToHexWEI(suggestedMaxFeePerGas);
maxPriorityFeePerGas =
customMaxPriorityFeePerGas ||
decGWEIToHexWEI(suggestedMaxPriorityFeePerGas);
baseAndPriorityFeePerGas = addHexes(
decEstimatedBaseFee,
maxPriorityFeePerGas,
);
}

const handleApprovalTx = async () => {
console.log('Bridge', 'handleApprovalTx');

const hexChainId = new Numeric(
quoteMeta.approval.chainId,
10,
).toPrefixedHexString() as `0x${string}`;
if (!hexChainId) {
throw new Error('Invalid chain ID');
}

const txMeta = await addTransactionAndWaitForPublish(
{
...quoteMeta.approval,
chainId: hexChainId,
gasLimit: quoteMeta.approval.gasLimit.toString(),
// maxFeePerGas,
// maxPriorityFeePerGas,
},
{
requireApproval: false,
// @ts-expect-error Need TransactionController v37+, TODO add this type
type: 'bridgeApproval', // TransactionType.bridgeApproval,
swaps: {
hasApproveTx: true,
meta: {
type: 'bridgeApproval', // TransactionType.bridgeApproval, // TODO
sourceTokenSymbol: quoteMeta.quote.srcAsset.symbol,
},
},
},
);

console.log('Bridge', { approvalTxId: txMeta.id });
return txMeta.id;
};

const handleBridgeTx = async (approvalTxId: string | undefined) => {
console.log('Bridge', 'handleBridgeTx');
const hexChainId = new Numeric(
quoteMeta.trade.chainId,
10,
).toPrefixedHexString() as `0x${string}`;
if (!hexChainId) {
throw new Error('Invalid chain ID');
}

const txMeta = await addTransactionAndWaitForPublish(
{
...quoteMeta.trade,
chainId: hexChainId,
gasLimit: quoteMeta.trade.gasLimit.toString(),
// maxFeePerGas,
// maxPriorityFeePerGas,
},
{
requireApproval: false,
// @ts-expect-error Need TransactionController v37+, TODO add this type
type: 'bridge', // TransactionType.bridge,
bridge: {
hasApproveTx: Boolean(quoteMeta?.approval),
meta: {
// estimatedBaseFee: decEstimatedBaseFee,
// swapMetaData,
type: 'bridge', // TransactionType.bridge, // TODO add this type
sourceTokenSymbol: quoteMeta.quote.srcAsset.symbol,
destinationTokenSymbol: quoteMeta.quote.destAsset.symbol,
destinationTokenDecimals: quoteMeta.quote.destAsset.decimals,
destinationTokenAddress: quoteMeta.quote.destAsset.address,
approvalTxId,
// this is the decimal (non atomic) amount (not USD value) of source token to swap
swapTokenValue: new Numeric(
quoteMeta.quote.srcTokenAmount,
10,
).shiftedBy(quoteMeta.quote.srcAsset.decimals),
},
},
},
);

console.log('Bridge', { bridgeTxId: txMeta.id });
return txMeta.id;
};

const addSourceToken = () => {
const sourceNetworkClientId = getSelectedNetworkClientId(state);
const {
address,
decimals,
symbol,
icon: image,
} = quoteMeta.quote.srcAsset;
dispatch(
addToken({
address,
decimals,
symbol,
image,
networkClientId: sourceNetworkClientId,
}),
);
};

const addDestToken = () => {
const networkConfigurations = getNetworkConfigurations(state);
const networkConfigurationsArr = Object.values<{
chainId: Hex;
id: string;
}>(networkConfigurations);
const hexDestChainId = new Numeric(
quoteMeta.quote.destChainId,
10,
).toPrefixedHexString() as `0x${string}`;
const destNetworkConfig = networkConfigurationsArr.find(
(networkConfig) => networkConfig.chainId === hexDestChainId,
);
if (destNetworkConfig) {
const {
address,
decimals,
symbol,
icon: image,
} = quoteMeta.quote.destAsset;
dispatch(
addToken({
address,
decimals,
symbol,
image,
networkClientId: destNetworkConfig.id,
}),
);
} else {
// TODO import the dest chain, then add the dest token
}
};

// Execute transaction(s)
let approvalTxId: string | undefined;
if (quoteMeta?.approval) {
approvalTxId = await handleApprovalTx();
}
await handleBridgeTx(approvalTxId);

// Add tokens
addSourceToken();
addDestToken();

// Return user to home screen
history.push(DEFAULT_ROUTE);
};
};
Loading
Loading