Repository: across-protocol/contracts
Pull request #926: feat(SpokePool): Remove enabledDepositRoutes check for unsafeDeposit
Author: login: nicholaspai
Labels: name: needs to be audited
Created: 2025-03-17T15:34:07Z | Merged: 2025-03-28T17:54:46Z

Allows Across to support inputTokens without enabling deposit routes for them, by forcing filler to take repayment of the token on the origin chain.


Diff:
diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol
index e0d544621..c338788fb 100644
--- a/contracts/SpokePool.sol
+++ b/contracts/SpokePool.sol
@@ -75,7 +75,7 @@ abstract contract SpokePool is
     RootBundle[] public rootBundles;
 
     // Origin token to destination token routings can be turned on or off, which can enable or disable deposits.
-    mapping(address => mapping(uint256 => bool)) public enabledDepositRoutes;
+    mapping(address => mapping(uint256 => bool)) public DEPRECATED_enabledDepositRoutes;
 
     // Each relay is associated with the hash of parameters that uniquely identify the original deposit and a relay
     // attempt for that deposit. The relay itself is just represented as the amount filled so far. The total amount to
@@ -315,21 +315,6 @@ abstract contract SpokePool is
         _setWithdrawalRecipient(newWithdrawalRecipient);
     }
 
-    /**
-     * @notice Enable/Disable an origin token => destination chain ID route for deposits. Callable by admin only.
-     * @param originToken Token that depositor can deposit to this contract.
-     * @param destinationChainId Chain ID for where depositor wants to receive funds.
-     * @param enabled True to enable deposits, False otherwise.
-     */
-    function setEnableRoute(
-        address originToken,
-        uint256 destinationChainId,
-        bool enabled
-    ) public override onlyAdmin nonReentrant {
-        enabledDepositRoutes[originToken][destinationChainId] = enabled;
-        emit EnabledDepositRoute(originToken, destinationChainId, enabled);
-    }
-
     /**
      * @notice This method stores a new root bundle in this contract that can be executed to refund relayers, fulfill
      * slow relays, and send funds back to the HubPool on L1. This method can only be called by the admin and is
@@ -627,10 +612,6 @@ abstract contract SpokePool is
      * corresponding fill might collide with an existing relay hash on the destination chain SpokePool,
      * which would make this deposit unfillable. In this case, the depositor would subsequently receive a refund
      * of `inputAmount` of `inputToken` on the origin chain after the fill deadline.
-     * @dev On the destination chain, the hash of the deposit data will be used to uniquely identify this deposit, so
-     * modifying any params in it will result in a different hash and a different deposit. The hash will comprise
-     * all parameters to this function along with this chain's chainId(). Relayers are only refunded for filling
-     * deposits with deposit hashes that map exactly to the one emitted by this contract.
      * @param depositNonce The nonce that uniquely identifies this deposit. This function will combine this parameter
      * with the msg.sender address to create a unique uint256 depositNonce and ensure that the msg.sender cannot
      * use this function to front-run another depositor's unsafe deposit. This function guarantees that the resultant
@@ -1311,10 +1292,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
-
         // Require that quoteTimestamp has a maximum age so that depositors pay an LP fee based on recent HubPool usage.
         // It is assumed that cross-chain timestamps are normally loosely in-sync, but clock drift can occur. If the
         // SpokePool time stalls or lags significantly, it is still possible to make deposits by setting quoteTimestamp
@@ -1400,9 +1377,6 @@ abstract contract SpokePool is
         uint32 quoteTimestamp,
         bytes memory message
     ) internal {
-        // Check that deposit route is enabled.
-        if (!enabledDepositRoutes[originToken][destinationChainId]) revert DisabledRoute();
-
         // We limit the relay fees to prevent the user spending all their funds on fees.
         if (SignedMath.abs(relayerFeePct) >= 0.5e18) revert InvalidRelayerFeePct();
         if (amount > MAX_TRANSFER_SIZE) revert MaxTransferSizeExceeded();
diff --git a/contracts/chain-adapters/Solana_Adapter.sol b/contracts/chain-adapters/Solana_Adapter.sol
index 429500e83..f85cd1e46 100644
--- a/contracts/chain-adapters/Solana_Adapter.sol
+++ b/contracts/chain-adapters/Solana_Adapter.sol
@@ -41,9 +41,6 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
     // Solana spoke pool address, mapped to its EVM address representation.
     address public immutable SOLANA_SPOKE_POOL_ADDRESS;
 
-    // USDC mint address on Solana, decoded from Base58 to bytes32.
-    bytes32 public immutable SOLANA_USDC_BYTES32;
-
     // USDC mint address on Solana, mapped to its EVM address representation.
     address public immutable SOLANA_USDC_ADDRESS;
 
@@ -56,8 +53,6 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
 
     // Custom errors for relayMessage validation.
     error InvalidRelayMessageTarget(address target);
-    error InvalidOriginToken(address originToken);
-    error InvalidDestinationChainId(uint256 destinationChainId);
 
     // Custom errors for relayTokens validation.
     error InvalidL1Token(address l1Token);
@@ -95,7 +90,6 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
         SOLANA_SPOKE_POOL_BYTES32 = solanaSpokePool;
         SOLANA_SPOKE_POOL_ADDRESS = solanaSpokePool.toAddressUnchecked();
 
-        SOLANA_USDC_BYTES32 = solanaUsdc;
         SOLANA_USDC_ADDRESS = solanaUsdc.toAddressUnchecked();
 
         SOLANA_SPOKE_POOL_USDC_VAULT = solanaSpokePoolUsdcVault;
@@ -111,17 +105,7 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
         if (target != SOLANA_SPOKE_POOL_ADDRESS) {
             revert InvalidRelayMessageTarget(target);
         }
-
-        bytes4 selector = bytes4(message[:4]);
-        if (selector == SpokePoolInterface.setEnableRoute.selector) {
-            cctpMessageTransmitter.sendMessage(
-                CircleDomainIds.Solana,
-                SOLANA_SPOKE_POOL_BYTES32,
-                _translateSetEnableRoute(message)
-            );
-        } else {
-            cctpMessageTransmitter.sendMessage(CircleDomainIds.Solana, SOLANA_SPOKE_POOL_BYTES32, message);
-        }
+        cctpMessageTransmitter.sendMessage(CircleDomainIds.Solana, SOLANA_SPOKE_POOL_BYTES32, message);
 
         // TODO: consider if we need also to emit the translated message.
         emit MessageRelayed(target, message);
@@ -159,32 +143,4 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
         // TODO: consider if we need also to emit the translated addresses.
         emit TokensRelayed(l1Token, l2Token, amount, to);
     }
-
-    /**
-     * @notice Translates a message to enable/disable a route on Solana spoke pool.
-     * @param message Message to translate, expecting setEnableRoute(address,uint256,bool).
-     * @return Translated message, using setEnableRoute(bytes32,uint64,bool).
-     */
-    function _translateSetEnableRoute(bytes calldata message) internal view returns (bytes memory) {
-        (address originToken, uint256 destinationChainId, bool enable) = abi.decode(
-            message[4:],
-            (address, uint256, bool)
-        );
-
-        if (originToken != SOLANA_USDC_ADDRESS) {
-            revert InvalidOriginToken(originToken);
-        }
-
-        if (destinationChainId > type(uint64).max) {
-            revert InvalidDestinationChainId(destinationChainId);
-        }
-
-        return
-            abi.encodeWithSignature(
-                "setEnableRoute(bytes32,uint64,bool)",
-                SOLANA_USDC_BYTES32,
-                uint64(destinationChainId),
-                enable
-            );
-    }
 }
diff --git a/contracts/interfaces/SpokePoolInterface.sol b/contracts/interfaces/SpokePoolInterface.sol
index 9eec066c1..d8ce51cc0 100644
--- a/contracts/interfaces/SpokePoolInterface.sol
+++ b/contracts/interfaces/SpokePoolInterface.sol
@@ -38,12 +38,6 @@ interface SpokePoolInterface {
 
     function setWithdrawalRecipient(address newWithdrawalRecipient) external;
 
-    function setEnableRoute(
-        address originToken,
-        uint256 destinationChainId,
-        bool enable
-    ) external;
-
     function pauseDeposits(bool pause) external;
 
     function pauseFills(bool pause) external;
diff --git a/storage-layouts/AlephZero_SpokePool.json b/storage-layouts/AlephZero_SpokePool.json
index 382132dd3..d22430ed9 100644
--- a/storage-layouts/AlephZero_SpokePool.json
+++ b/storage-layouts/AlephZero_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/AlephZero_SpokePool.sol:AlephZero_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Arbitrum_SpokePool.json b/storage-layouts/Arbitrum_SpokePool.json
index 14bd92871..a22965fda 100644
--- a/storage-layouts/Arbitrum_SpokePool.json
+++ b/storage-layouts/Arbitrum_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Arbitrum_SpokePool.sol:Arbitrum_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Base_SpokePool.json b/storage-layouts/Base_SpokePool.json
index 80934a3f8..9c6877c37 100644
--- a/storage-layouts/Base_SpokePool.json
+++ b/storage-layouts/Base_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Base_SpokePool.sol:Base_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Blast_SpokePool.json b/storage-layouts/Blast_SpokePool.json
index 2be5208f9..c0b95f53c 100644
--- a/storage-layouts/Blast_SpokePool.json
+++ b/storage-layouts/Blast_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Blast_SpokePool.sol:Blast_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Ethereum_SpokePool.json b/storage-layouts/Ethereum_SpokePool.json
index 416c2be2c..c6515c53e 100644
--- a/storage-layouts/Ethereum_SpokePool.json
+++ b/storage-layouts/Ethereum_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Ethereum_SpokePool.sol:Ethereum_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Linea_SpokePool.json b/storage-layouts/Linea_SpokePool.json
index 9dae90739..5deba0c0b 100644
--- a/storage-layouts/Linea_SpokePool.json
+++ b/storage-layouts/Linea_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Linea_SpokePool.sol:Linea_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Mode_SpokePool.json b/storage-layouts/Mode_SpokePool.json
index b0daad98f..2f1362150 100644
--- a/storage-layouts/Mode_SpokePool.json
+++ b/storage-layouts/Mode_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Mode_SpokePool.sol:Mode_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Optimism_SpokePool.json b/storage-layouts/Optimism_SpokePool.json
index ded4492f7..b0f7937a4 100644
--- a/storage-layouts/Optimism_SpokePool.json
+++ b/storage-layouts/Optimism_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Optimism_SpokePool.sol:Optimism_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/PolygonZkEVM_SpokePool.json b/storage-layouts/PolygonZkEVM_SpokePool.json
index 7e1fc8127..6996d2728 100644
--- a/storage-layouts/PolygonZkEVM_SpokePool.json
+++ b/storage-layouts/PolygonZkEVM_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/PolygonZkEVM_SpokePool.sol:PolygonZkEVM_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Polygon_SpokePool.json b/storage-layouts/Polygon_SpokePool.json
index 924750f6e..c7524b29a 100644
--- a/storage-layouts/Polygon_SpokePool.json
+++ b/storage-layouts/Polygon_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Polygon_SpokePool.sol:Polygon_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Redstone_SpokePool.json b/storage-layouts/Redstone_SpokePool.json
index 92b0654e8..630b8904f 100644
--- a/storage-layouts/Redstone_SpokePool.json
+++ b/storage-layouts/Redstone_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Redstone_SpokePool.sol:Redstone_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Scroll_SpokePool.json b/storage-layouts/Scroll_SpokePool.json
index 81782717b..1a6bd7119 100644
--- a/storage-layouts/Scroll_SpokePool.json
+++ b/storage-layouts/Scroll_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Scroll_SpokePool.sol:Scroll_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/WorldChain_SpokePool.json b/storage-layouts/WorldChain_SpokePool.json
index e46bb47d2..d15295df6 100644
--- a/storage-layouts/WorldChain_SpokePool.json
+++ b/storage-layouts/WorldChain_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/WorldChain_SpokePool.sol:WorldChain_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/ZkSync_SpokePool.json b/storage-layouts/ZkSync_SpokePool.json
index d76f701b5..aeeaa7ac8 100644
--- a/storage-layouts/ZkSync_SpokePool.json
+++ b/storage-layouts/ZkSync_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/ZkSync_SpokePool.sol:ZkSync_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/storage-layouts/Zora_SpokePool.json b/storage-layouts/Zora_SpokePool.json
index ca513b7cc..79c7343be 100644
--- a/storage-layouts/Zora_SpokePool.json
+++ b/storage-layouts/Zora_SpokePool.json
@@ -110,7 +110,7 @@
     },
     {
       "contract": "contracts/Zora_SpokePool.sol:Zora_SpokePool",
-      "label": "enabledDepositRoutes",
+      "label": "DEPRECATED_enabledDepositRoutes",
       "offset": 0,
       "slot": "2157"
     },
diff --git a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol
index 897fb116d..f47ed02b0 100644
--- a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol
+++ b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol
@@ -48,8 +48,6 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test {
         );
         spokePool = MockSpokePool(payable(proxy));
 
-        spokePool.setEnableRoute(address(mockWETH), destinationChainId, true);
-
         vm.stopPrank();
 
         deal(depositor, depositAmount * 2);
diff --git a/test/evm/foundry/local/SpokePoolVerifier.t.sol b/test/evm/foundry/local/SpokePoolVerifier.t.sol
index 403984eee..a95b1a293 100644
--- a/test/evm/foundry/local/SpokePoolVerifier.t.sol
+++ b/test/evm/foundry/local/SpokePoolVerifier.t.sol
@@ -62,8 +62,6 @@ contract SpokePoolVerifierTest is Test {
             new ERC1967Proxy(address(implementation), abi.encodeCall(Ethereum_SpokePool.initialize, (0, owner)))
         );
         ethereumSpokePool = Ethereum_SpokePool(payable(proxy));
-        ethereumSpokePool.setEnableRoute(address(mockWETH), destinationChainId, true);
-        ethereumSpokePool.setEnableRoute(address(mockERC20), destinationChainId, true);
         spokePoolVerifier = new SpokePoolVerifier();
         vm.stopPrank();
 
diff --git a/test/evm/hardhat/HubPool.Admin.ts b/test/evm/hardhat/HubPool.Admin.ts
index d9ee9312d..452ff3bbf 100644
--- a/test/evm/hardhat/HubPool.Admin.ts
+++ b/test/evm/hardhat/HubPool.Admin.ts
@@ -70,11 +70,7 @@ describe("HubPool Admin functions", function () {
     ).to.be.reverted;
   });
   it("Only owner can relay spoke pool admin message", async function () {
-    const functionData = mockSpoke.interface.encodeFunctionData("setEnableRoute", [
-      weth.address,
-      destinationChainId,
-      false,
-    ]);
+    const functionData = mockSpoke.interface.encodeFunctionData("pauseDeposits", [true]);
     await expect(hubPool.connect(other).relaySpokePoolAdminFunction(destinationChainId, functionData)).to.be.reverted;
 
     // Cannot relay admin function if spoke pool is set to zero address or adapter is set to non contract..
@@ -91,44 +87,6 @@ describe("HubPool Admin functions", function () {
       .to.emit(hubPool, "SpokePoolAdminFunctionTriggered")
       .withArgs(destinationChainId, functionData);
   });
-  it("Only owner can whitelist route for deposits and rebalances", async function () {
-    await hubPool.setCrossChainContracts(destinationChainId, mockAdapter.address, mockSpoke.address);
-    await expect(hubPool.connect(other).setPoolRebalanceRoute(destinationChainId, weth.address, usdc.address)).to.be
-      .reverted;
-    await expect(hubPool.setPoolRebalanceRoute(destinationChainId, weth.address, usdc.address))
-      .to.emit(hubPool, "SetPoolRebalanceRoute")
-      .withArgs(destinationChainId, weth.address, usdc.address);
-
-    // Relay deposit route to spoke pool. Check content of messages sent to mock spoke pool.
-    await expect(hubPool.connect(other).setDepositRoute(originChainId, destinationChainId, weth.address, true)).to.be
-      .reverted;
-    await expect(hubPool.setDepositRoute(originChainId, destinationChainId, weth.address, true))
-      .to.emit(hubPool, "SetEnableDepositRoute")
-      .withArgs(originChainId, destinationChainId, weth.address, true);
-
-    // Disable deposit route on SpokePool right after:
-    await hubPool.setDepositRoute(originChainId, destinationChainId, weth.address, false);
-
-    // Since the mock adapter is delegatecalled, when querying, its address should be the hubPool address.
-    const mockAdapterAtHubPool = mockAdapter.attach(hubPool.address);
-    const relayMessageEvents = await mockAdapterAtHubPool.queryFilter(
-      mockAdapterAtHubPool.filters.RelayMessageCalled()
-    );
-    expect(relayMessageEvents[relayMessageEvents.length - 1].args?.message).to.equal(
-      mockSpoke.interface.encodeFunctionData("setEnableRoute", [
-        weth.address,
-        destinationChainId,
-        false, // Latest call disabled the route
-      ])
-    );
-    expect(relayMessageEvents[relayMessageEvents.length - 2].args?.message).to.equal(
-      mockSpoke.interface.encodeFunctionData("setEnableRoute", [
-        weth.address,
-        destinationChainId,
-        true, // Second to last call enabled the route
-      ])
-    );
-  });
 
   it("Can change the bond token and amount", async function () {
     expect(await hubPool.callStatic.bondToken()).to.equal(weth.address); // Default set in the fixture.
diff --git a/test/evm/hardhat/SpokePool.Admin.ts b/test/evm/hardhat/SpokePool.Admin.ts
index 1818e10bd..5562daece 100644
--- a/test/evm/hardhat/SpokePool.Admin.ts
+++ b/test/evm/hardhat/SpokePool.Admin.ts
@@ -19,12 +19,6 @@ describe("SpokePool Admin Functions", async function () {
     );
     expect(await spokePool.numberOfDeposits()).to.equal(1);
   });
-  it("Enable token path", async function () {
-    await expect(spokePool.connect(owner).setEnableRoute(erc20.address, destinationChainId, true))
-      .to.emit(spokePool, "EnabledDepositRoute")
-      .withArgs(erc20.address, destinationChainId, true);
-    expect(await spokePool.enabledDepositRoutes(erc20.address, destinationChainId)).to.equal(true);
-  });
 
   it("Pause deposits", async function () {
     expect(await spokePool.pausedDeposits()).to.equal(false);
diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts
index d8956cad1..96967b31b 100644
--- a/test/evm/hardhat/SpokePool.Deposit.ts
+++ b/test/evm/hardhat/SpokePool.Deposit.ts
@@ -13,7 +13,6 @@ import {
 } from "../../../utils/utils";
 import {
   spokePoolFixture,
-  enableRoutes,
   getDepositParams,
   V3RelayData,
   getUpdatedV3DepositSignature,
@@ -51,9 +50,6 @@ describe("SpokePool Depositor Logic", async function () {
     await erc20.connect(depositor).approve(spokePool.address, amountToDeposit.mul(10));
     await weth.connect(depositor).approve(spokePool.address, amountToDeposit.mul(10));
 
-    // Whitelist origin token => destination chain ID routes:
-    await enableRoutes(spokePool, [{ originToken: erc20.address }, { originToken: weth.address }]);
-
     quoteTimestamp = (await spokePool.getCurrentTime()).toNumber();
   });
 
@@ -226,65 +222,6 @@ describe("SpokePool Depositor Logic", async function () {
     ).to.emit(spokePool, "FundsDeposited");
   });
 
-  it("Deposit route is disabled", async function () {
-    const revertReason = "DisabledRoute";
-
-    // Verify that routes are disabled by default.
-    await expect(
-      spokePool.connect(depositor).depositDeprecated_5947912356(
-        ...getDepositParams({
-          originToken: unwhitelistedErc20.address,
-          amount,
-          destinationChainId,
-          relayerFeePct,
-          quoteTimestamp,
-        })
-      )
-    ).to.be.revertedWith(revertReason);
-
-    // Verify that the route is enabled.
-    await expect(
-      spokePool.connect(depositor).depositDeprecated_5947912356(
-        ...getDepositParams({
-          originToken: erc20.address,
-          amount,
-          destinationChainId,
-          relayerFeePct,
-          quoteTimestamp,
-        })
-      )
-    ).to.emit(spokePool, "FundsDeposited");
-
-    // Disable the route.
-    await spokePool.connect(depositor).setEnableRoute(erc20.address, destinationChainId, false);
-    await expect(
-      spokePool.connect(depositor).depositDeprecated_5947912356(
-        ...getDepositParams({
-          originToken: erc20.address,
-          amount,
-          destinationChainId,
-          relayerFeePct,
-          quoteTimestamp,
-        })
-      )
-    ).to.be.revertedWith(revertReason);
-
-    // Re-enable the route and verify that it works again.
-    await spokePool.connect(depositor).setEnableRoute(erc20.address, destinationChainId, true);
-    await erc20.connect(depositor).approve(spokePool.address, amountToDeposit);
-    await expect(
-      spokePool.connect(depositor).depositDeprecated_5947912356(
-        ...getDepositParams({
-          originToken: erc20.address,
-          amount,
-          destinationChainId,
-          relayerFeePct,
-          quoteTimestamp,
-        })
-      )
-    ).to.emit(spokePool, "FundsDeposited");
-  });
-
   it("Relayer fee is invalid", async function () {
     const revertReason = "InvalidRelayerFee";
 
@@ -456,15 +393,6 @@ describe("SpokePool Depositor Logic", async function () {
         .connect(depositor)
         .depositV3(...getDepositArgsFromRelayData(relayData, destinationChainId, quoteTimestamp, true));
     });
-    it("route disabled", async function () {
-      // Verify that routes are disabled by default for a new route
-      const _depositArgs = getDepositArgsFromRelayData(relayData, 999);
-      await expect(spokePool.connect(depositor).deposit(..._depositArgs)).to.be.revertedWith("DisabledRoute");
-
-      // Enable the route:
-      await spokePool.connect(depositor).setEnableRoute(erc20.address, 999, true);
-      await expect(spokePool.connect(depositor).deposit(..._depositArgs)).to.not.be.reverted;
-    });
     it("invalid quoteTimestamp", async function () {
       const quoteTimeBuffer = await spokePool.depositQuoteTimeBuffer();
       const currentTime = await spokePool.getCurrentTime();
diff --git a/test/evm/hardhat/chain-adapters/Solana_Adapter.ts b/test/evm/hardhat/chain-adapters/Solana_Adapter.ts
index 86dcb576e..699a14d2c 100644
--- a/test/evm/hardhat/chain-adapters/Solana_Adapter.ts
+++ b/test/evm/hardhat/chain-adapters/Solana_Adapter.ts
@@ -117,24 +117,4 @@ describe("Solana Chain Adapter", function () {
       usdc.address
     );
   });
-
-  it("Correctly translates setEnableRoute calls to the spoke pool", async function () {
-    // Enable deposits for USDC on Solana.
-    const destinationChainId = 1;
-    const depositsEnabled = true;
-    await hubPool.setDepositRoute(solanaChainId, destinationChainId, solanaUsdcAddress, depositsEnabled);
-
-    // Solana spoke pool expects to receive full bytes32 token address and uint64 for chainId.
-    const solanaInterface = new ethers.utils.Interface(["function setEnableRoute(bytes32, uint64, bool)"]);
-    const solanaMessage = solanaInterface.encodeFunctionData("setEnableRoute", [
-      solanaUsdcBytes32,
-      destinationChainId,
-      depositsEnabled,
-    ]);
-    expect(cctpMessageTransmitter.sendMessage).to.have.been.calledWith(
-      solanaDomainId,
-      solanaSpokePoolBytes32,
-      solanaMessage
-    );
-  });
 });
diff --git a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts
index 723e513e9..e30fbf584 100644
--- a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts
+++ b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts
@@ -77,12 +77,6 @@ describe("Arbitrum Spoke Pool", function () {
     expect(await arbitrumSpokePool.l2GatewayRouter()).to.equal(rando.address);
   });
 
-  it("Only cross domain owner can enable a route", async function () {
-    await expect(arbitrumSpokePool.setEnableRoute(l2Dai, 1, true)).to.be.reverted;
-    await arbitrumSpokePool.connect(crossDomainAlias).setEnableRoute(l2Dai, 1, true);
-    expect(await arbitrumSpokePool.enabledDepositRoutes(l2Dai, 1)).to.equal(true);
-  });
-
   it("Only cross domain owner can whitelist a token pair", async function () {
     await expect(arbitrumSpokePool.whitelistToken(l2Dai, dai.address)).to.be.reverted;
     await arbitrumSpokePool.connect(crossDomainAlias).whitelistToken(l2Dai, dai.address);
diff --git a/test/evm/hardhat/chain-specific-spokepools/Ethereum_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Ethereum_SpokePool.ts
index a7b2d0823..215eb7308 100644
--- a/test/evm/hardhat/chain-specific-spokepools/Ethereum_SpokePool.ts
+++ b/test/evm/hardhat/chain-specific-spokepools/Ethereum_SpokePool.ts
@@ -52,12 +52,6 @@ describe("Ethereum Spoke Pool", function () {
     expect(await spokePool.crossDomainAdmin()).to.equal(rando.address);
   });
 
-  it("Only owner can enable a route", async function () {
-    await expect(spokePool.connect(rando).setEnableRoute(dai.address, 1, true)).to.be.reverted;
-    await spokePool.connect(owner).setEnableRoute(dai.address, 1, true);
-    expect(await spokePool.enabledDepositRoutes(dai.address, 1)).to.equal(true);
-  });
-
   it("Only owner can set the hub pool address", async function () {
     await expect(spokePool.connect(rando).setWithdrawalRecipient(rando.address)).to.be.reverted;
     await spokePool.connect(owner).setWithdrawalRecipient(rando.address);
diff --git a/test/evm/hardhat/chain-specific-spokepools/Optimism_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Optimism_SpokePool.ts
index f629007fb..304d54bdd 100644
--- a/test/evm/hardhat/chain-specific-spokepools/Optimism_SpokePool.ts
+++ b/test/evm/hardhat/chain-specific-spokepools/Optimism_SpokePool.ts
@@ -90,13 +90,6 @@ describe("Optimism Spoke Pool", function () {
     expect(await optimismSpokePool.tokenBridges(l2Dai)).to.equal(rando.address);
   });
 
-  it("Only cross domain owner can enable a route", async function () {
-    await expect(optimismSpokePool.setEnableRoute(l2Dai, 1, true)).to.be.reverted;
-    crossDomainMessenger.xDomainMessageSender.returns(owner.address);
-    await optimismSpokePool.connect(crossDomainMessenger.wallet).setEnableRoute(l2Dai, 1, true);
-    expect(await optimismSpokePool.enabledDepositRoutes(l2Dai, 1)).to.equal(true);
-  });
-
   it("Only cross domain owner can set the cross domain admin", async function () {
     await expect(optimismSpokePool.setCrossDomainAdmin(rando.address)).to.be.reverted;
     crossDomainMessenger.xDomainMessageSender.returns(owner.address);
diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts
index 0628a7f21..c0f8a6474 100644
--- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts
+++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts
@@ -142,24 +142,6 @@ describe("Polygon Spoke Pool", function () {
     expect(await polygonSpokePool.withdrawalRecipient()).to.equal(rando.address);
   });
 
-  it("Only correct caller can enable a route", async function () {
-    // Cannot call directly
-    await expect(polygonSpokePool.setEnableRoute(l2Dai, 1, true)).to.be.reverted;
-
-    const setEnableRouteData = polygonSpokePool.interface.encodeFunctionData("setEnableRoute", [l2Dai, 1, true]);
-
-    // Wrong rootMessageSender address.
-    await expect(polygonSpokePool.connect(fxChild).processMessageFromRoot(0, rando.address, setEnableRouteData)).to.be
-      .reverted;
-
-    // Wrong calling address.
-    await expect(polygonSpokePool.connect(rando).processMessageFromRoot(0, owner.address, setEnableRouteData)).to.be
-      .reverted;
-
-    await polygonSpokePool.connect(fxChild).processMessageFromRoot(0, owner.address, setEnableRouteData);
-    expect(await polygonSpokePool.enabledDepositRoutes(l2Dai, 1)).to.equal(true);
-  });
-
   it("Only correct caller can initialize a relayer refund", async function () {
     // Cannot call directly
     await expect(polygonSpokePool.relayRootBundle(mockTreeRoot, mockTreeRoot)).to.be.reverted;


Reviews:
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T15:42:45Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T15:44:09Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T16:11:18Z
user: login: bmzig
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T16:41:50Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-19T15:03:40Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T14:01:51Z
user: login: mrice32
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T03:08:28Z
user: login: mrice32
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T03:09:05Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T19:29:02Z
user: login: mrice32
body: Looks great! @Reinis-FRP can you take a pass?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T17:27:24Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T17:28:03Z
user: login: mrice32
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T17:29:13Z
user: login: nicholaspai
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T17:30:47Z

Review comments:
diff_hunk: @@ -557,6 +562,9 @@ abstract contract SpokePool is
             exclusivityParameter: exclusivityParameter,
             message: message
         });
+        // Check that deposit route is enabled for the input token. There are no checks required for the output token
+        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
+        if (!enabledDepositRoutes[inputToken.toAddress()][destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: nicholaspai
body: Open question for reviewers: should we unilaterally remove this check for ALL deposits or just unsafeDeposit? The thinking behind just doing unsafeDeposit is so we can slowly roll out this feature as needed.
created_at: 2025-03-17T15:42:45Z
author_association: MEMBER
diff_hunk: @@ -1330,10 +1341,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: nicholaspai
body: Second open question: Should we remove this check in the Solana SpokePool as well? I assume no, its not needed for now.
created_at: 2025-03-17T15:44:09Z
author_association: MEMBER
diff_hunk: @@ -1330,10 +1343,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: nicholaspai
body: third open question: removing this check now allows depositor to set any value for `destinationChainId` they want to. This opens up griefing vectors against offchain components that we need to make sure relayers and dataworkers protect against
created_at: 2025-03-17T16:11:18Z
author_association: MEMBER
diff_hunk: @@ -557,6 +562,9 @@ abstract contract SpokePool is
             exclusivityParameter: exclusivityParameter,
             message: message
         });
+        // Check that deposit route is enabled for the input token. There are no checks required for the output token
+        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
+        if (!enabledDepositRoutes[inputToken.toAddress()][destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: bmzig
body: I'm of the opinion that if we are getting this audited, then we might as well make it across all deposits. I think that if integrators do want to use this feature, then it is a little annoying to have to make an unsafe deposit, which forces more responsibility on them to make "correctly structured" deposits (i.e. no duplicates). This fixes the griefing vector, and I'd think that if we modify the dataworker to force origin repayment on these token types when a pool rebalance root is not set, then it seems like it would be OK to just fully support anyways. 
created_at: 2025-03-17T16:41:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -557,6 +562,9 @@ abstract contract SpokePool is
             exclusivityParameter: exclusivityParameter,
             message: message
         });
+        // Check that deposit route is enabled for the input token. There are no checks required for the output token
+        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
+        if (!enabledDepositRoutes[inputToken.toAddress()][destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: nicholaspai
body: https://github.com/across-protocol/contracts/pull/926/commits/d94d98554241e460fafec4865027efd69536adcf
created_at: 2025-03-19T15:03:40Z
author_association: MEMBER
diff_hunk: @@ -113,7 +113,7 @@ contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
         }
 
         bytes4 selector = bytes4(message[:4]);
-        if (selector == SpokePoolInterface.setEnableRoute.selector) {
path: contracts/chain-adapters/Solana_Adapter.sol
user: login: nicholaspai
body: Note to self: can delete this section if we choose to remove deposit routes in Solana spoke 
created_at: 2025-03-26T14:01:49Z
author_association: MEMBER
diff_hunk: @@ -1330,10 +1341,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: mrice32
body: Yes, but we'll do it in a separate PR.
created_at: 2025-03-27T03:08:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1330,10 +1343,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: mrice32
body: What griefing vectors? Just that people can send completely invalid deposits?
created_at: 2025-03-27T03:09:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1330,10 +1343,6 @@ abstract contract SpokePool is
         // Verify depositor is a valid EVM address.
         params.depositor.checkAddress();
 
-        // Check that deposit route is enabled for the input token. There are no checks required for the output token
-        // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
-        if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute();
path: contracts/SpokePool.sol
user: login: nicholaspai
body: yeah, basically you can set the destination chain ID so high that validators can't handle it. Shouldn't be an issue if they use BN.js
created_at: 2025-03-27T19:29:02Z
author_association: MEMBER
diff_hunk: @@ -1519,7 +1498,7 @@ abstract contract SpokePool is
         uint256 returnValue;
         bytes memory data = abi.encodeCall(IERC20Upgradeable.transfer, (to, amount));
         assembly {
-            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
+            success := call(MAX_ERC20_TRANSFER_GAS_COST, token, 0, add(data, 0x20), mload(data), 0, 0x20)
path: contracts/SpokePool.sol
user: login: mrice32
body: Since each leaf is a separate token, shouldn't we just assume that the executor will protect themselves against this and not execute leaves where the token is expensive to transfer? You could even imagine the executor having a token whitelist where it refuses to pay out tokens that are not on that list.
created_at: 2025-03-28T17:25:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1519,7 +1498,7 @@ abstract contract SpokePool is
         uint256 returnValue;
         bytes memory data = abi.encodeCall(IERC20Upgradeable.transfer, (to, amount));
         assembly {
-            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
+            success := call(MAX_ERC20_TRANSFER_GAS_COST, token, 0, add(data, 0x20), mload(data), 0, 0x20)
path: contracts/SpokePool.sol
user: login: nicholaspai
body: thats a good point too, and reduces contract complexity +1
created_at: 2025-03-28T17:28:03Z
author_association: MEMBER
diff_hunk: @@ -1519,7 +1498,7 @@ abstract contract SpokePool is
         uint256 returnValue;
         bytes memory data = abi.encodeCall(IERC20Upgradeable.transfer, (to, amount));
         assembly {
-            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
+            success := call(MAX_ERC20_TRANSFER_GAS_COST, token, 0, add(data, 0x20), mload(data), 0, 0x20)
path: contracts/SpokePool.sol
user: login: nicholaspai
body: https://github.com/across-protocol/contracts/pull/926/commits/263dcd915b9a6d2e9aba7b88f6691d7be020e1d6
created_at: 2025-03-28T17:30:47Z
author_association: MEMBER

PR comments:
user: login: socket-security[bot]
created_at: 2025-03-25T18:21:46Z
body: **No dependency changes detected.** Learn more about [Socket for GitHub ↗︎](https://socket.dev?utm_medium=gh)

👍 No dependency changes detected in pull request
author_association: NONE