Skip to content

Commit

Permalink
feat: get bridge tx status fetching working
Browse files Browse the repository at this point in the history
  • Loading branch information
infiniteflower committed Oct 17, 2024
1 parent 4dfe889 commit 366505d
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 66 deletions.
32 changes: 20 additions & 12 deletions app/scripts/controllers/bridge-status/bridge-status-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,25 @@ export default class BridgeStatusController extends BaseController<
};

getBridgeTxStatus = async (statusRequest: StatusRequest) => {
const { bridgeStatusState } = this.state;

const bridgeStatus = await fetchBridgeTxStatus(statusRequest);
this.update((_state) => {
_state.bridgeStatusState = {
...bridgeStatusState,
txStatuses: {
...bridgeStatusState.txStatuses,
[statusRequest.srcTxHash]: bridgeStatus,
},
};
});
// Need to subscribe since if we try to fetch status too fast, API will fail with 500 error
// So fetch on tx confirmed
this.messagingSystem.subscribe(
'TransactionController:transactionConfirmed',
async (txMeta) => {
if (txMeta.hash === statusRequest.srcTxHash) {
const { bridgeStatusState } = this.state;
const bridgeTxStatus = await fetchBridgeTxStatus(statusRequest);
this.update((_state) => {
_state.bridgeStatusState = {
...bridgeStatusState,
txStatuses: {
...bridgeStatusState.txStatuses,
[statusRequest.srcTxHash]: bridgeTxStatus,
},
};
});
}
},
);
};
}
71 changes: 28 additions & 43 deletions app/scripts/controllers/bridge-status/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@ import {
ControllerStateChangeEvent,
RestrictedControllerMessenger,
} from '@metamask/base-controller';
import { Hex } from '@metamask/utils';
import { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';
import { ChainId, Quote } from '../../../../ui/pages/bridge/types';
import { BRIDGE_STATUS_CONTROLLER_NAME } from './constants';
import BridgeStatusController from './bridge-status-controller';
import { Hex } from '@metamask/utils';

type HexChainId = Hex;
type DecChainId = number;

export interface StatusRequest {
bridgeId: string; //lifi, socket, squid
srcTxHash: string; //lifi, socket, squid
bridge: string; //lifi, socket, squid
srcChainId: HexChainId; //lifi, socket, squid
destChainId: HexChainId; //lifi, socket, squid
quote?: Quote; //squid
refuel?: boolean; //lifi
}
export type StatusRequest = {
bridgeId: string; // lifi, socket, squid
srcTxHash: string; // lifi, socket, squid
bridge: string; // lifi, socket, squid
srcChainId: ChainId; // lifi, socket, squid
destChainId: ChainId; // lifi, socket, squid
quote?: Quote; // squid
refuel?: boolean; // lifi
};

export type Asset = {
chainId: DecChainId;
chainId: ChainId;
address: string;
symbol: string;
name: string;
Expand All @@ -29,7 +28,7 @@ export type Asset = {
};

export type ChainStatus = {
chainId: DecChainId;
chainId: ChainId;
txHash: string;
amount?: string;
token?: Asset;
Expand All @@ -42,7 +41,7 @@ export enum StatusTypes {
COMPLETE = 'COMPLETE',
}

export interface RefuelStatusResponse extends StatusResponse {}
export type RefuelStatusResponse = {} & StatusResponse;

export enum BridgeId {
HOP = 'hop',
Expand Down Expand Up @@ -72,50 +71,34 @@ export enum FeeType {
REFUEL = 'refuel',
}

export interface FeeData {
export type FeeData = {
amount: string;
asset: Asset;
}
};

export interface Protocol {
export type Protocol = {
displayName?: string;
icon?: string;
name?: string; // for legacy quotes
}
};

export enum ActionTypes {
BRIDGE = 'bridge',
SWAP = 'swap',
REFUEL = 'refuel',
}

export interface Step {
export type Step = {
action: ActionTypes;
srcChainId: HexChainId;
destChainId?: HexChainId;
srcChainId: ChainId;
destChainId?: ChainId;
srcAsset: Asset;
destAsset: Asset;
srcAmount: string;
destAmount: string;
protocol: Protocol;
}
export interface RefuelData extends Step {}

export interface Quote {
requestId: string;
srcChainId: HexChainId;
srcAsset: Asset;
srcTokenAmount: string;
destChainId: HexChainId;
destAsset: Asset;
destTokenAmount: string;
feeData: Record<FeeType.METABRIDGE, FeeData> &
Partial<Record<FeeType, FeeData>>;
bridgeId: string;
bridges: string[];
steps: Step[];
refuel?: RefuelData;
}
};
export type RefuelData = {} & Step;

export type BridgeHistoryItem = {
quote: Quote;
Expand Down Expand Up @@ -162,13 +145,15 @@ type BridgeStatusControllerEvents = ControllerStateChangeEvent<
BridgeStatusControllerState
>;

type AllowedEvents = TransactionControllerTransactionConfirmedEvent;

/**
* The messenger for the BridgeStatusController.
*/
export type BridgeStatusControllerMessenger = RestrictedControllerMessenger<
typeof BRIDGE_STATUS_CONTROLLER_NAME,
BridgeStatusControllerActions,
BridgeStatusControllerEvents,
BridgeStatusControllerEvents | AllowedEvents,
never,
never
AllowedEvents['type']
>;
20 changes: 17 additions & 3 deletions app/scripts/controllers/bridge-status/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,22 @@ const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID };
const CACHE_REFRESH_TEN_MINUTES = 10 * MINUTE;

export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => {
console.log('fetchBridgeTxStatus', { statusRequest });

// Assemble params
const { quote, ...statusRequestNoQuote } = statusRequest;
const statusRequestNoQuoteFormatted = Object.fromEntries(
Object.entries(statusRequestNoQuote).map(([key, value]) => [
key,
value.toString(),
]),
);
const params = new URLSearchParams(statusRequestNoQuoteFormatted);

// Fetch
const url = `${BRIDGE_API_BASE_URL}/getTxStatus`;
const baseUrl = `${BRIDGE_API_BASE_URL}/getTxStatus`;
const url = `${baseUrl}?${params.toString()}`;

const rawTxStatus = await fetchWithCache({
url,
fetchOptions: { method: 'GET', headers: CLIENT_ID_HEADER },
Expand All @@ -25,7 +39,7 @@ export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => {
const isValid = validateResponse<StatusResponse, unknown>(
validators,
rawTxStatus,
url,
baseUrl,
);
if (!isValid) {
throw new Error('Invalid response from bridge');
Expand All @@ -34,6 +48,6 @@ export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => {
// Convert to Extension format

// Return
console.log('fetchBridgeTxStatus', { statusRequest, rawTxStatus });
console.log('fetchBridgeTxStatus', { rawTxStatus });
return rawTxStatus;
};
10 changes: 9 additions & 1 deletion app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ import { PatchStore } from './lib/PatchStore';
import { sanitizeUIState } from './lib/state-utils';
import BridgeStatusController from './controllers/bridge-status/bridge-status-controller';
import { BRIDGE_STATUS_CONTROLLER_NAME } from './controllers/bridge-status/constants';
import { BridgeStatusAction } from './controllers/bridge-status/types';

export const METAMASK_CONTROLLER_EVENTS = {
// Fired after state changes that impact the extension badge (unapproved msg count)
Expand Down Expand Up @@ -2121,7 +2122,7 @@ export default class MetamaskController extends EventEmitter {
this.controllerMessenger.getRestricted({
name: BRIDGE_STATUS_CONTROLLER_NAME,
allowedActions: [],
allowedEvents: [],
allowedEvents: ['TransactionController:transactionConfirmed'],
});
this.bridgeStatusController = new BridgeStatusController({
provider: this.provider,
Expand Down Expand Up @@ -3963,6 +3964,13 @@ export default class MetamaskController extends EventEmitter {
`${BRIDGE_CONTROLLER_NAME}:${BridgeUserAction.SWITCH_TO_AND_FROM_INPUTS}`,
),

// Bridge Status
[BridgeStatusAction.GET_BRIDGE_TX_STATUS]:
this.controllerMessenger.call.bind(
this.controllerMessenger,
`${BRIDGE_STATUS_CONTROLLER_NAME}:${BridgeStatusAction.GET_BRIDGE_TX_STATUS}`,
),

// Smart Transactions
fetchSmartTransactionFees: smartTransactionsController.getFees.bind(
smartTransactionsController,
Expand Down
4 changes: 3 additions & 1 deletion ui/ducks/bridge-status/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export const getBridgeTxStatus = (statusRequest: StatusRequest) => {
return async (dispatch: MetaMaskReduxDispatch) => {
return dispatch(
callBridgeStatusControllerMethod<
Parameters<BridgeStatusController['getBridgeTxStatus']>
Parameters<
BridgeStatusController[BridgeStatusAction.GET_BRIDGE_TX_STATUS]
>
>(BridgeStatusAction.GET_BRIDGE_TX_STATUS, [statusRequest]),
);
};
Expand Down
28 changes: 22 additions & 6 deletions ui/ducks/bridge/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { FEATURED_RPCS } from '../../../shared/constants/network';
import { getEthUsdtResetData, isEthUsdt } from '../../pages/bridge/bridge.util';
import { ETH_USDT_ADDRESS } from '../../../shared/constants/bridge';
import BridgeController from '../../../app/scripts/controllers/bridge/bridge-controller';
import { getBridgeTxStatus } from '../bridge-status/actions';
import { bridgeSlice } from './bridge';
import { BridgeAppState } from './selectors';

Expand Down Expand Up @@ -371,7 +372,7 @@ export const submitBridgeTransaction = (
},
});

return txMeta.id;
return txMeta;
};

const handleBridgeTx = async ({
Expand Down Expand Up @@ -410,7 +411,7 @@ export const submitBridgeTransaction = (
},
});

return txMeta.id;
return txMeta;
};

const addSourceToken = () => {
Expand Down Expand Up @@ -484,21 +485,36 @@ export const submitBridgeTransaction = (
const { maxFeePerGas, maxPriorityFeePerGas } = calcFeePerGas();

// Execute transaction(s)
let approvalTxId: string | undefined;
let approvalTxMeta: TransactionMeta | undefined;
if (quoteResponse?.approval) {
approvalTxId = await handleApprovalTx({
approvalTxMeta = await handleApprovalTx({
approval: quoteResponse.approval,
maxFeePerGas,
maxPriorityFeePerGas,
});
}

await handleBridgeTx({
approvalTxId,
const bridgeTxMeta = await handleBridgeTx({
approvalTxId: approvalTxMeta?.id,
maxFeePerGas,
maxPriorityFeePerGas,
});

// Get bridge tx status
if (bridgeTxMeta.hash) {
dispatch(
getBridgeTxStatus({
bridgeId: quoteResponse.quote.bridgeId,
srcTxHash: bridgeTxMeta.hash,
bridge: quoteResponse.quote.bridges[0],
srcChainId: quoteResponse.quote.srcChainId,
destChainId: quoteResponse.quote.destChainId,
quote: quoteResponse.quote,
refuel: Boolean(quoteResponse.quote.refuel),
}),
);
}

// Add tokens if not the native gas token
if (quoteResponse.quote.srcAsset.address !== zeroAddress()) {
addSourceToken();
Expand Down

0 comments on commit 366505d

Please sign in to comment.