Skip to content

Commit 0e50e56

Browse files
committed
Add relay quotes unit tests
1 parent a40bd4c commit 0e50e56

File tree

3 files changed

+235
-1
lines changed

3 files changed

+235
-1
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { TransactionMeta } from '@metamask/transaction-controller';
2+
3+
import { getRelayQuotes } from './relay-quotes';
4+
import { submitRelayQuotes } from './relay-submit';
5+
import { RelayStrategy } from './RelayStrategy';
6+
import type { RelayQuote } from './types';
7+
import type {
8+
TransactionPayControllerMessenger,
9+
TransactionPayPublishHookMessenger,
10+
} from '../..';
11+
import type { TransactionPayQuote } from '../../types';
12+
13+
jest.mock('./relay-quotes');
14+
jest.mock('./relay-submit');
15+
16+
const QUOTE_MOCK = {
17+
estimatedDuration: 5,
18+
} as TransactionPayQuote<RelayQuote>;
19+
20+
describe('RelayStrategy', () => {
21+
const getBridgeQuotesMock = jest.mocked(getRelayQuotes);
22+
const submitRelayQuotesMock = jest.mocked(submitRelayQuotes);
23+
24+
beforeEach(() => {
25+
jest.resetAllMocks();
26+
getBridgeQuotesMock.mockResolvedValue([QUOTE_MOCK]);
27+
});
28+
29+
describe('getQuotes', () => {
30+
it('returns result from util', async () => {
31+
const result = new RelayStrategy().getQuotes({
32+
messenger: {} as TransactionPayControllerMessenger,
33+
requests: [],
34+
});
35+
36+
expect(await result).toStrictEqual([QUOTE_MOCK]);
37+
});
38+
});
39+
40+
describe('execute', () => {
41+
it('calls util', async () => {
42+
await new RelayStrategy().execute({
43+
isSmartTransaction: () => false,
44+
quotes: [QUOTE_MOCK],
45+
messenger: {} as TransactionPayPublishHookMessenger,
46+
transaction: { txParams: { from: '0x1' } } as TransactionMeta,
47+
});
48+
49+
expect(submitRelayQuotesMock).toHaveBeenCalledTimes(1);
50+
expect(
51+
submitRelayQuotesMock.mock.calls[0][0].transaction.txParams.from,
52+
).toBe('0x1');
53+
});
54+
});
55+
});
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { successfulFetch } from '@metamask/controller-utils';
2+
3+
import {
4+
ARBITRUM_USDC_ADDRESS,
5+
CHAIN_ID_ARBITRUM,
6+
RELAY_URL_QUOTE,
7+
} from './constants';
8+
import { getRelayQuotes } from './relay-quotes';
9+
import type { RelayQuote } from './types';
10+
import type { QuoteRequest } from '../../types';
11+
12+
jest.mock('@metamask/controller-utils', () => ({
13+
...jest.requireActual('@metamask/controller-utils'),
14+
successfulFetch: jest.fn(),
15+
}));
16+
17+
const QUOTE_REQUEST_MOCK: QuoteRequest = {
18+
from: '0x123',
19+
sourceBalanceRaw: '10000000000000000000',
20+
sourceChainId: '0x1',
21+
sourceTokenAddress: '0xabc',
22+
sourceTokenAmount: '1000000000000000000',
23+
targetAmountMinimum: '123',
24+
targetChainId: '0x2',
25+
targetTokenAddress: '0xdef',
26+
};
27+
28+
const QUOTE_MOCK: RelayQuote = {
29+
details: {
30+
currencyIn: {
31+
amountUsd: '2.34',
32+
},
33+
currencyOut: {
34+
amountUsd: '1.23',
35+
},
36+
timeEstimate: 300,
37+
},
38+
steps: [
39+
{
40+
items: [
41+
{
42+
check: {
43+
endpoint: '/test',
44+
method: 'GET',
45+
},
46+
data: {
47+
chainId: 1,
48+
data: '0x123',
49+
from: '0x1',
50+
gas: '21000',
51+
maxFeePerGas: '1000000000',
52+
maxPriorityFeePerGas: '2000000000',
53+
to: '0x2',
54+
value: '300000',
55+
},
56+
status: 'complete',
57+
},
58+
],
59+
kind: 'transaction',
60+
},
61+
],
62+
};
63+
64+
describe('Relay Quotes Utils', () => {
65+
const successfulFetchMock = jest.mocked(successfulFetch);
66+
67+
beforeEach(() => {
68+
jest.resetAllMocks();
69+
});
70+
71+
describe('getRelayQuotes', () => {
72+
it('returns quotes from Relay', async () => {
73+
successfulFetchMock.mockResolvedValue({
74+
json: async () => QUOTE_MOCK,
75+
} as never);
76+
77+
const result = await getRelayQuotes({
78+
messenger: {} as never,
79+
requests: [QUOTE_REQUEST_MOCK],
80+
});
81+
82+
expect(result).toStrictEqual([
83+
expect.objectContaining({
84+
original: QUOTE_MOCK,
85+
}),
86+
]);
87+
});
88+
89+
it('sends request to Relay', async () => {
90+
successfulFetchMock.mockResolvedValue({
91+
json: async () => QUOTE_MOCK,
92+
} as never);
93+
94+
await getRelayQuotes({
95+
messenger: {} as never,
96+
requests: [QUOTE_REQUEST_MOCK],
97+
});
98+
99+
expect(successfulFetchMock).toHaveBeenCalledWith(
100+
RELAY_URL_QUOTE,
101+
expect.objectContaining({
102+
body: JSON.stringify({
103+
amount: QUOTE_REQUEST_MOCK.targetAmountMinimum,
104+
destinationChainId: 2,
105+
destinationCurrency: QUOTE_REQUEST_MOCK.targetTokenAddress,
106+
originChainId: 1,
107+
originCurrency: QUOTE_REQUEST_MOCK.sourceTokenAddress,
108+
recipient: QUOTE_REQUEST_MOCK.from,
109+
tradeType: 'EXPECTED_OUTPUT',
110+
user: QUOTE_REQUEST_MOCK.from,
111+
}),
112+
}),
113+
);
114+
});
115+
116+
it('normalizes quotes', async () => {
117+
successfulFetchMock.mockResolvedValue({
118+
json: async () => QUOTE_MOCK,
119+
} as never);
120+
121+
const result = await getRelayQuotes({
122+
messenger: {} as never,
123+
requests: [QUOTE_REQUEST_MOCK],
124+
});
125+
126+
expect(result[0]).toStrictEqual(
127+
expect.objectContaining({
128+
estimatedDuration: 300,
129+
fees: expect.objectContaining({
130+
provider: {
131+
usd: '1.11',
132+
fiat: '1.11',
133+
},
134+
}),
135+
}),
136+
);
137+
});
138+
139+
it('throws if fetching quote fails', async () => {
140+
successfulFetchMock.mockRejectedValue(new Error('Fetch error'));
141+
142+
await expect(
143+
getRelayQuotes({
144+
messenger: {} as never,
145+
requests: [QUOTE_REQUEST_MOCK],
146+
}),
147+
).rejects.toThrow('Fetch error');
148+
});
149+
150+
it('updates request if Arbitrum deposit to Hyperliquid', async () => {
151+
const arbitrumToHyperliquidRequest: QuoteRequest = {
152+
...QUOTE_REQUEST_MOCK,
153+
targetChainId: CHAIN_ID_ARBITRUM,
154+
targetTokenAddress: ARBITRUM_USDC_ADDRESS,
155+
};
156+
157+
successfulFetchMock.mockResolvedValue({
158+
json: async () => QUOTE_MOCK,
159+
} as never);
160+
161+
await getRelayQuotes({
162+
messenger: {} as never,
163+
requests: [arbitrumToHyperliquidRequest],
164+
});
165+
166+
const body = JSON.parse(
167+
successfulFetchMock.mock.calls[0][1]?.body as string,
168+
);
169+
170+
expect(body).toStrictEqual(
171+
expect.objectContaining({
172+
amount: '12300',
173+
destinationChainId: 1337,
174+
destinationCurrency: '0x00000000000000000000000000000000',
175+
}),
176+
);
177+
});
178+
});
179+
});

packages/transaction-pay-controller/src/strategy/relay/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export type RelayQuote = {
3535
}[];
3636
kind: 'transaction';
3737
}[];
38-
skipTransaction: boolean;
38+
skipTransaction?: boolean;
3939
};
4040

4141
export type RelayStatus = {

0 commit comments

Comments
 (0)