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

feat: cross chain swaps - tx submit #27262

Merged
merged 93 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
504563b
feat: skeleton for submitting a bridge tx
infiniteflower Sep 17, 2024
028931a
chore: add comments
infiniteflower Sep 18, 2024
21f56e0
feat: send user back to home page after bridge started
infiniteflower Sep 19, 2024
b1af3f6
chore: pass in history obj to tx action
infiniteflower Sep 19, 2024
ebf3e50
chore: remove events, will handle in future pr
infiniteflower Sep 19, 2024
21d38c3
chore: add bridge to excluded types
infiniteflower Sep 19, 2024
af3176f
chore: move dummy quotes to own file, add temp gas work
infiniteflower Sep 20, 2024
d3c3c2f
chore: use temp strings for bridge tx types
infiniteflower Sep 20, 2024
f9b5cd3
chore: add comments
infiniteflower Sep 20, 2024
b972055
chore: add temp transaction list title for bridge txs
infiniteflower Sep 20, 2024
f789158
fix: wrong type
infiniteflower Sep 20, 2024
7970704
feat: add source and dest tokens on tx submit
infiniteflower Sep 20, 2024
9c78efd
feat: upsert dest chain if user does not have it already,
infiniteflower Sep 24, 2024
ee05c9a
feat: get tx submission working, needed to set gas field
infiniteflower Sep 24, 2024
56af33f
chore: add utils to get Ethereum USDT allowance reset tx
infiniteflower Sep 26, 2024
371c57f
chore: check if bridge is enabled before submitting tx
infiniteflower Sep 26, 2024
f1d9b04
chore: handle Eth USDT edge case with resetApproval
infiniteflower Sep 26, 2024
f53513f
fix: not properly detecting eth usdt
infiniteflower Sep 26, 2024
f977dd9
chore: pass in provider into bridgecontroller so we can look up allow…
infiniteflower Sep 27, 2024
2d8b380
chore: check for current allowance when handling Eth USDT reset appro…
infiniteflower Sep 27, 2024
266e955
fix: allowance not being a big number on the ui side
infiniteflower Sep 27, 2024
6ff712a
fix: don't require approval for usdt reset
infiniteflower Sep 27, 2024
0a3c17e
fix: not using hex for gas field
infiniteflower Sep 27, 2024
d11bc0f
chore: block tx submission if not bridge chain
infiniteflower Sep 27, 2024
754433b
chore: load swaps feature flags when entering loading bridge page
infiniteflower Oct 1, 2024
b46295c
fix: not adding tokens for dest chain eth mainnet
infiniteflower Oct 1, 2024
bdb932d
chore: fix adding dest token and accidentally adding wrong source token
infiniteflower Oct 1, 2024
cbf9ac2
chore: route user to activity tab on home page
infiniteflower Oct 2, 2024
36ecda4
chore: fix adding dest network + token infinite loop, get STX status …
infiniteflower Oct 2, 2024
0810e70
feat: add approval and bridge gas multipliers to BridgeController state
infiniteflower Oct 2, 2024
8ca87e1
chore: calc and use maxGasLimit with gasMultipliers
infiniteflower Oct 2, 2024
3a55b16
chore: fix types
infiniteflower Oct 2, 2024
b8c5be3
chore: key gasMultipliers by hex chainId, add tests, fix response vs …
infiniteflower Oct 2, 2024
27eaea0
fix: wrong types
infiniteflower Oct 2, 2024
b784528
chore: add tests for bridge selectors
infiniteflower Oct 2, 2024
3ba6592
chore: add skeleton for bridge action tests
infiniteflower Oct 2, 2024
c02a939
chore: new dummy quotes
infiniteflower Oct 3, 2024
bb85901
chore: update quotes
infiniteflower Oct 3, 2024
4d5b7b2
fix: maxGasLimit calc was missing an arg
infiniteflower Oct 3, 2024
8bf6ec2
chore: update quotes
infiniteflower Oct 3, 2024
f7e80e8
chore: refactor
infiniteflower Oct 3, 2024
d379c23
chore: remove unneeded comments
infiniteflower Oct 4, 2024
941cae5
chore: get first test working for submitBridgeTransaction
infiniteflower Oct 4, 2024
02535d0
chore: rename files
infiniteflower Oct 7, 2024
e9fc104
Revert "chore: rename files"
infiniteflower Oct 7, 2024
70d38e8
chore: refactor dummy constants to be more readable
infiniteflower Oct 7, 2024
88efc7e
chore: fix tests
infiniteflower Oct 7, 2024
0246c7e
chore: set a default gas multiplier of 1
infiniteflower Oct 7, 2024
9fb466d
chore: add more tests
infiniteflower Oct 7, 2024
f40252f
fix: not properly adding dest network and token
infiniteflower Oct 8, 2024
fb50e0b
chore: add test
infiniteflower Oct 8, 2024
737bce1
fix: gas estimates not hex wei
infiniteflower Oct 8, 2024
0427fc4
chore: add test
infiniteflower Oct 8, 2024
e4d01aa
chore: add more dummy quotes
infiniteflower Oct 9, 2024
4e3a6f2
chore: remove logs
infiniteflower Oct 9, 2024
ecc439e
chore: remove logs
infiniteflower Oct 9, 2024
e8834bd
chore: remove unneeded comments and code
infiniteflower Oct 9, 2024
6a306ae
fix: ts errors with redux state and bridge state
infiniteflower Oct 9, 2024
05c85af
chore: add tests for bridge controller
infiniteflower Oct 9, 2024
b2f9b1c
chore: add comment to clarify swaps feature flags usage
infiniteflower Oct 11, 2024
ff8ae92
chore: use zeroAddress() to be consistent with the rest of bridge
infiniteflower Oct 15, 2024
f1547be
chore: remove duplicate enum FeeType
infiniteflower Oct 15, 2024
db216e0
chore: use getSelectedAccount for getBridgeERC20Allowance rather than…
infiniteflower Oct 15, 2024
c9f7cbb
chore: use getSelectedNetworkClient for provider rather than passing …
infiniteflower Oct 15, 2024
5a01335
chore: use abiERC20 from library instead
infiniteflower Oct 15, 2024
c25307a
chore: don't need to check bridge enabled as we check already when ro…
infiniteflower Oct 15, 2024
10342b3
chore: refactor tx submission to be more generic
infiniteflower Oct 15, 2024
b0837b2
chore: wire up quote selector to bridge tx submission action
infiniteflower Oct 15, 2024
286ab3d
chore: remove gas multipliers as bridge api's gasLimit already has mu…
infiniteflower Oct 16, 2024
f770282
chore: remove approval and bridge gas multipliers since bridge API ga…
infiniteflower Oct 17, 2024
c49e380
chore: move dummy quote to test folder
infiniteflower Oct 17, 2024
2704cb0
chore: improve typing of meta field
infiniteflower Oct 17, 2024
a82f0ae
chore: use sentAmount for swapTokenValue
infiniteflower Oct 17, 2024
cff3c20
chore: remove unneeded thrown errors
infiniteflower Oct 17, 2024
02fca86
fix: rebase issues
infiniteflower Nov 4, 2024
ac8eb2f
fix: remove fields that no longer exist from test
infiniteflower Nov 7, 2024
91ff645
fix: broken tests
infiniteflower Nov 7, 2024
5661f58
fix: broken tests
infiniteflower Nov 7, 2024
868d920
fix: broken tests
infiniteflower Nov 7, 2024
c41e5ad
fix: lint issues
infiniteflower Nov 7, 2024
1e4af1b
fix: lint errors
infiniteflower Nov 8, 2024
3cd241c
chore: use TransactionType for bridge and bridgeApproval
infiniteflower Nov 14, 2024
09a9e2a
chore: force tests to rerun on ci
infiniteflower Nov 14, 2024
2b3d2c3
Revert "chore: force tests to rerun on ci"
infiniteflower Nov 14, 2024
6e1e281
fix: use TransactionController estimateGasFee for better estimations
infiniteflower Nov 15, 2024
49a05c0
chore: refactor bridge tx submission into multiple hooks
infiniteflower Nov 15, 2024
1efaef7
chore: remove usecallback for now
infiniteflower Nov 19, 2024
92790c9
chore: make more readable
infiniteflower Nov 19, 2024
258b0f3
chore: refactor to be more readable
infiniteflower Nov 20, 2024
94fbf88
chore: migrate tests to hook
infiniteflower Nov 20, 2024
b353c1a
chore: remove old tests for action and consume submitBridgeTx hook in ui
infiniteflower Nov 20, 2024
93db725
fix: lint errors
infiniteflower Nov 20, 2024
496fa98
fix: lint errors
infiniteflower Nov 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 47 additions & 2 deletions app/scripts/controllers/bridge/bridge-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,27 @@ const messengerMock = {
publish: jest.fn(),
} as unknown as jest.Mocked<BridgeControllerMessenger>;

jest.mock('@ethersproject/contracts', () => {
return {
Contract: jest.fn(() => ({
allowance: jest.fn(() => '100000000000000000000'),
})),
};
});

jest.mock('@ethersproject/providers', () => {
return {
Web3Provider: jest.fn(),
};
});

describe('BridgeController', function () {
let bridgeController: BridgeController;

beforeAll(function () {
bridgeController = new BridgeController({ messenger: messengerMock });
bridgeController = new BridgeController({
messenger: messengerMock,
});
});

beforeEach(() => {
Expand All @@ -43,6 +59,18 @@ describe('BridgeController', function () {
'extension-support': true,
'src-network-allowlist': [10, 534352],
'dest-network-allowlist': [137, 42161],
'approval-gas-multiplier': {
'137': 1.1,
'42161': 1.2,
'10': 1.3,
'534352': 1.4,
},
'bridge-gas-multiplier': {
'137': 2.1,
'42161': 2.2,
'10': 2.3,
'534352': 2.4,
},
});
nock(BRIDGE_API_BASE_URL)
.get('/getTokens?chainId=10')
Expand Down Expand Up @@ -507,7 +535,10 @@ describe('BridgeController', function () {
bridgeController,
'startPollingByNetworkClientId',
);
messengerMock.call.mockReturnValueOnce({ address: '0x123' } as never);
messengerMock.call.mockReturnValue({
address: '0x123',
provider: jest.fn(),
} as never);

bridgeController.updateBridgeQuoteRequestParams({
srcChainId: 1,
Expand Down Expand Up @@ -536,4 +567,18 @@ describe('BridgeController', function () {
}),
);
});

describe('getBridgeERC20Allowance', () => {
it('should return the atomic allowance of the ERC20 token contract', async () => {
messengerMock.call.mockReturnValue({
address: '0x123',
provider: jest.fn(),
} as never);
const allowance = await bridgeController.getBridgeERC20Allowance(
'0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
'0xa',
);
expect(allowance).toBe('100000000000000000000');
});
});
});
38 changes: 37 additions & 1 deletion app/scripts/controllers/bridge/bridge-controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { StateMetadata } from '@metamask/base-controller';
import { add0x, Hex } from '@metamask/utils';
import { StaticIntervalPollingController } from '@metamask/polling-controller';
import { NetworkClientId } from '@metamask/network-controller';
import { StateMetadata } from '@metamask/base-controller';
import { Contract } from '@ethersproject/contracts';
import { abiERC20 } from '@metamask/metamask-eth-abis';
import { Web3Provider } from '@ethersproject/providers';
import { BigNumber } from '@ethersproject/bignumber';
import {
fetchBridgeFeatureFlags,
fetchBridgeQuotes,
Expand All @@ -25,6 +29,7 @@ import {
DEFAULT_BRIDGE_CONTROLLER_STATE,
REFRESH_INTERVAL_MS,
RequestStatus,
METABRIDGE_CHAIN_TO_ADDRESS_MAP,
} from './constants';
import {
BridgeControllerState,
Expand Down Expand Up @@ -60,6 +65,8 @@ export default class BridgeController extends StaticIntervalPollingController<

this.setIntervalLength(REFRESH_INTERVAL_MS);

this.#abortController = new AbortController();
// Register action handlers
this.messagingSystem.registerActionHandler(
`${BRIDGE_CONTROLLER_NAME}:setBridgeFeatureFlags`,
this.setBridgeFeatureFlags.bind(this),
Expand All @@ -80,6 +87,10 @@ export default class BridgeController extends StaticIntervalPollingController<
`${BRIDGE_CONTROLLER_NAME}:resetState`,
this.resetState.bind(this),
);
this.messagingSystem.registerActionHandler(
`${BRIDGE_CONTROLLER_NAME}:getBridgeERC20Allowance`,
this.getBridgeERC20Allowance.bind(this),
);
}

_executePoll = async (
Expand Down Expand Up @@ -277,4 +288,29 @@ export default class BridgeController extends StaticIntervalPollingController<
chainId,
);
}

/**
*
* @param contractAddress - The address of the ERC20 token contract
* @param chainId - The hex chain ID of the bridge network
* @returns The atomic allowance of the ERC20 token contract
*/
getBridgeERC20Allowance = async (
contractAddress: string,
chainId: Hex,
): Promise<string> => {
const provider = this.#getSelectedNetworkClient()?.provider;
if (!provider) {
throw new Error('No provider found');
}

const web3Provider = new Web3Provider(provider);
const contract = new Contract(contractAddress, abiERC20, web3Provider);
const { address: walletAddress } = this.#getSelectedAccount();
const allowance = await contract.allowance(
walletAddress,
METABRIDGE_CHAIN_TO_ADDRESS_MAP[chainId],
);
return BigNumber.from(allowance).toString();
};
}
7 changes: 7 additions & 0 deletions app/scripts/controllers/bridge/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { zeroAddress } from 'ethereumjs-util';
import { Hex } from '@metamask/utils';
import { METABRIDGE_ETHEREUM_ADDRESS } from '../../../../shared/constants/bridge';
import { CHAIN_IDS } from '../../../../shared/constants/network';
import { BridgeControllerState, BridgeFeatureFlagsKey } from './types';

export const BRIDGE_CONTROLLER_NAME = 'BridgeController';
Expand Down Expand Up @@ -36,3 +39,7 @@ export const DEFAULT_BRIDGE_CONTROLLER_STATE: BridgeControllerState = {
quotesLoadingStatus: undefined,
quotesRefreshCount: 0,
};

export const METABRIDGE_CHAIN_TO_ADDRESS_MAP: Record<Hex, string> = {
[CHAIN_IDS.MAINNET]: METABRIDGE_ETHEREUM_ADDRESS,
};
2 changes: 2 additions & 0 deletions app/scripts/controllers/bridge/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export enum BridgeUserAction {
export enum BridgeBackgroundAction {
SET_FEATURE_FLAGS = 'setBridgeFeatureFlags',
RESET_STATE = 'resetState',
GET_BRIDGE_ERC20_ALLOWANCE = 'getBridgeERC20Allowance',
}

type BridgeControllerAction<FunctionName extends keyof BridgeController> = {
Expand All @@ -64,6 +65,7 @@ type BridgeControllerAction<FunctionName extends keyof BridgeController> = {
type BridgeControllerActions =
| BridgeControllerAction<BridgeBackgroundAction.SET_FEATURE_FLAGS>
| BridgeControllerAction<BridgeBackgroundAction.RESET_STATE>
| BridgeControllerAction<BridgeBackgroundAction.GET_BRIDGE_ERC20_ALLOWANCE>
| BridgeControllerAction<BridgeUserAction.SELECT_SRC_NETWORK>
| BridgeControllerAction<BridgeUserAction.SELECT_DEST_NETWORK>
| BridgeControllerAction<BridgeUserAction.UPDATE_QUOTE_PARAMS>;
Expand Down
5 changes: 5 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3971,6 +3971,11 @@ export default class MetamaskController extends EventEmitter {
this.controllerMessenger,
`${BRIDGE_CONTROLLER_NAME}:${BridgeBackgroundAction.RESET_STATE}`,
),
[BridgeBackgroundAction.GET_BRIDGE_ERC20_ALLOWANCE]:
this.controllerMessenger.call.bind(
this.controllerMessenger,
`${BRIDGE_CONTROLLER_NAME}:${BridgeBackgroundAction.GET_BRIDGE_ERC20_ALLOWANCE}`,
),
[BridgeUserAction.SELECT_SRC_NETWORK]: this.controllerMessenger.call.bind(
this.controllerMessenger,
`${BRIDGE_CONTROLLER_NAME}:${BridgeUserAction.SELECT_SRC_NETWORK}`,
Expand Down
4 changes: 4 additions & 0 deletions shared/constants/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ export const BRIDGE_API_BASE_URL = process.env.BRIDGE_USE_DEV_APIS
: BRIDGE_PROD_API_BASE_URL;

export const BRIDGE_CLIENT_ID = 'extension';

export const ETH_USDT_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7';
export const METABRIDGE_ETHEREUM_ADDRESS =
'0x0439e60F02a8900a951603950d8D4527f400C3f1';
1 change: 1 addition & 0 deletions shared/constants/metametrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,7 @@ export enum MetaMetricsNetworkEventSource {
Dapp = 'dapp',
DeprecatedNetworkModal = 'deprecated_network_modal',
NewAddNetworkFlow = 'new_add_network_flow',
Bridge = 'bridge',
}

export enum MetaMetricsSwapsEventSource {
Expand Down
2 changes: 2 additions & 0 deletions shared/constants/security-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export const SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES = [
TransactionType.swap,
TransactionType.swapApproval,
TransactionType.swapAndSend,
TransactionType.bridgeApproval,
TransactionType.bridge,
];

export const LOADING_SECURITY_ALERT_RESPONSE: SecurityAlertResponse = {
Expand Down
Loading