Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 23 additions & 9 deletions contracts/HubPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,11 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable {
address indexed liquidityProvider
);
event WhitelistRoute(
uint256 originChainId,
uint256 destinationChainId,
address originToken,
address destinationToken
uint256 indexed originChainId,
uint256 indexed destinationChainId,
address indexed originToken,
address destinationToken,
bool enableRoute
);

event ProposeRootBundle(
Expand Down Expand Up @@ -330,22 +331,35 @@ contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable {
* @param originChainId Chain where deposit occurs.
* @param destinationChainId Chain where depositor wants to receive funds.
* @param originToken Deposited token.
* @param destinationToken Token that depositor wants to receive on destination chain.
* @param destinationToken Token that depositor wants to receive on destination chain. Unused if `enableRoute` is
* False.
* @param enableRoute Set to true to enable route on L2 and whitelist new destination token, or False to disable
* route on L2 and delete destination token mapping on this contract.
*/
function whitelistRoute(
uint256 originChainId,
uint256 destinationChainId,
address originToken,
address destinationToken
address destinationToken,
bool enableRoute
) public override onlyOwner nonReentrant {
whitelistedRoutes[_whitelistedRouteKey(originChainId, originToken, destinationChainId)] = destinationToken;
if (enableRoute)
whitelistedRoutes[_whitelistedRouteKey(originChainId, originToken, destinationChainId)] = destinationToken;
else delete whitelistedRoutes[_whitelistedRouteKey(originChainId, originToken, destinationChainId)];

// Whitelist the same route on the origin network.
_relaySpokePoolAdminFunction(
originChainId,
abi.encodeWithSignature("setEnableRoute(address,uint256,bool)", originToken, destinationChainId, true)
abi.encodeWithSignature(
"setEnableRoute(address,uint256,bool)",
originToken,
destinationChainId,
enableRoute
)
);
emit WhitelistRoute(originChainId, destinationChainId, originToken, destinationToken);

// @dev Client should ignore `destinationToken` value if `enableRoute == False`.
emit WhitelistRoute(originChainId, destinationChainId, originToken, destinationToken, enableRoute);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion contracts/HubPoolInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ interface HubPoolInterface {
uint256 originChainId,
uint256 destinationChainId,
address originToken,
address destinationToken
address destinationToken,
bool enableRoute
) external;

function enableL1TokenForLiquidityProvision(address l1Token) external;
Expand Down
32 changes: 30 additions & 2 deletions test/HubPool.Admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Contract, ethers, randomAddress, utf8ToHex } from "./utils";
import { originChainId, destinationChainId, bondAmount, zeroAddress, mockTreeRoot } from "./constants";
import { mockSlowRelayRoot, finalFeeUsdc, finalFee, totalBond } from "./constants";
import { hubPoolFixture } from "./fixtures/HubPool.Fixture";
import { ZERO_ADDRESS } from "@uma/common";

let hubPool: Contract,
weth: Contract,
Expand Down Expand Up @@ -43,11 +44,38 @@ describe("HubPool Admin functions", function () {
});
it("Can whitelist route for deposits and rebalances", async function () {
await hubPool.setCrossChainContracts(destinationChainId, mockAdapter.address, mockSpoke.address);
await expect(hubPool.whitelistRoute(originChainId, destinationChainId, weth.address, usdc.address))
await expect(hubPool.whitelistRoute(originChainId, destinationChainId, weth.address, usdc.address, true))
.to.emit(hubPool, "WhitelistRoute")
.withArgs(originChainId, destinationChainId, weth.address, usdc.address);
.withArgs(originChainId, destinationChainId, weth.address, usdc.address, true);

expect(await hubPool.whitelistedRoute(originChainId, weth.address, destinationChainId)).to.equal(usdc.address);

// Can disable a route.
await hubPool.whitelistRoute(originChainId, destinationChainId, weth.address, usdc.address, false);
expect(await hubPool.whitelistedRoute(originChainId, weth.address, destinationChainId)).to.equal(ZERO_ADDRESS);

// Check content of messages sent to mock spoke pool. The last call should have "disabled" a route, and the call
// right before should have enabled the route.

// 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, // Should be set to false to disable route on SpokePool
])
);
expect(relayMessageEvents[relayMessageEvents.length - 2].args?.message).to.equal(
mockSpoke.interface.encodeFunctionData("setEnableRoute", [
weth.address,
destinationChainId,
true, // Should be set to true because destination token wasn't 0x0
])
);
});

it("Can change the bond token and amount", async function () {
Expand Down
8 changes: 4 additions & 4 deletions test/chain-adapters/Arbitrum_Adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ describe("Arbitrum Chain Adapter", function () {

await hubPool.setCrossChainContracts(arbitrumChainId, arbitrumAdapter.address, mockSpoke.address);

await hubPool.whitelistRoute(arbitrumChainId, l1ChainId, l2Weth, weth.address);
await hubPool.whitelistRoute(arbitrumChainId, l1ChainId, l2Weth, weth.address, true);

await hubPool.whitelistRoute(arbitrumChainId, l1ChainId, l2Dai, dai.address);
await hubPool.whitelistRoute(arbitrumChainId, l1ChainId, l2Dai, dai.address, true);

await hubPool.setCrossChainContracts(l1ChainId, mockAdapter.address, mockSpoke.address);

await hubPool.whitelistRoute(l1ChainId, arbitrumChainId, dai.address, l2Dai);
await hubPool.whitelistRoute(l1ChainId, arbitrumChainId, weth.address, l2Weth);
await hubPool.whitelistRoute(l1ChainId, arbitrumChainId, dai.address, l2Dai, true);
await hubPool.whitelistRoute(l1ChainId, arbitrumChainId, weth.address, l2Weth, true);
});

it("relayMessage calls spoke pool functions", async function () {
Expand Down
4 changes: 2 additions & 2 deletions test/chain-adapters/Ethereum_Adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ describe("Ethereum Chain Adapter", function () {

await hubPool.setCrossChainContracts(l1ChainId, ethAdapter.address, mockSpoke.address);

await hubPool.whitelistRoute(l1ChainId, l1ChainId, weth.address, weth.address);
await hubPool.whitelistRoute(l1ChainId, l1ChainId, weth.address, weth.address, true);

await hubPool.whitelistRoute(l1ChainId, l1ChainId, dai.address, dai.address);
await hubPool.whitelistRoute(l1ChainId, l1ChainId, dai.address, dai.address, true);
});

it("relayMessage calls spoke pool functions", async function () {
Expand Down
8 changes: 4 additions & 4 deletions test/chain-adapters/Optimism_Adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ describe("Optimism Chain Adapter", function () {
).deploy(weth.address, l1CrossDomainMessenger.address, l1StandardBridge.address);

await hubPool.setCrossChainContracts(optimismChainId, optimismAdapter.address, mockSpoke.address);
await hubPool.whitelistRoute(optimismChainId, l1ChainId, l2Weth, weth.address);
await hubPool.whitelistRoute(optimismChainId, l1ChainId, l2Dai, dai.address);
await hubPool.whitelistRoute(optimismChainId, l1ChainId, l2Weth, weth.address, true);
await hubPool.whitelistRoute(optimismChainId, l1ChainId, l2Dai, dai.address, true);

await hubPool.setCrossChainContracts(l1ChainId, mockAdapter.address, mockSpoke.address);
await hubPool.whitelistRoute(l1ChainId, optimismChainId, weth.address, l2Weth);
await hubPool.whitelistRoute(l1ChainId, optimismChainId, dai.address, l2Dai);
await hubPool.whitelistRoute(l1ChainId, optimismChainId, weth.address, l2Weth, true);
await hubPool.whitelistRoute(l1ChainId, optimismChainId, dai.address, l2Dai, true);
});

it("relayMessage calls spoke pool functions", async function () {
Expand Down
8 changes: 4 additions & 4 deletions test/chain-adapters/Polygon_Adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ describe("Polygon Chain Adapter", function () {
).deploy(rootChainManager.address, fxStateSender.address, weth.address);

await hubPool.setCrossChainContracts(polygonChainId, polygonAdapter.address, mockSpoke.address);
await hubPool.whitelistRoute(polygonChainId, l1ChainId, l2Weth, weth.address);
await hubPool.whitelistRoute(polygonChainId, l1ChainId, l2Dai, dai.address);
await hubPool.whitelistRoute(polygonChainId, l1ChainId, l2Weth, weth.address, true);
await hubPool.whitelistRoute(polygonChainId, l1ChainId, l2Dai, dai.address, true);

await hubPool.setCrossChainContracts(l1ChainId, mockAdapter.address, mockSpoke.address);
await hubPool.whitelistRoute(l1ChainId, polygonChainId, weth.address, l2Weth);
await hubPool.whitelistRoute(l1ChainId, polygonChainId, dai.address, l2Dai);
await hubPool.whitelistRoute(l1ChainId, polygonChainId, weth.address, l2Weth, true);
await hubPool.whitelistRoute(l1ChainId, polygonChainId, dai.address, l2Dai, true);
});

it("relayMessage calls spoke pool functions", async function () {
Expand Down
12 changes: 6 additions & 6 deletions test/fixtures/HubPool.Fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ export const hubPoolFixture = hre.deployments.createFixture(async ({ ethers }) =
// Deploy mock l2 tokens for each token created before and whitelist the routes.
const mockTokens = { l2Weth: randomAddress(), l2Dai: randomAddress(), l2Usdc: randomAddress() };

await hubPool.whitelistRoute(originChainId, repaymentChainId, weth.address, mockTokens.l2Weth);
await hubPool.whitelistRoute(originChainId, repaymentChainId, dai.address, mockTokens.l2Dai);
await hubPool.whitelistRoute(originChainId, repaymentChainId, usdc.address, mockTokens.l2Usdc);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, weth.address, mockTokens.l2Weth);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, dai.address, mockTokens.l2Dai);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, usdc.address, mockTokens.l2Usdc);
await hubPool.whitelistRoute(originChainId, repaymentChainId, weth.address, mockTokens.l2Weth, true);
await hubPool.whitelistRoute(originChainId, repaymentChainId, dai.address, mockTokens.l2Dai, true);
await hubPool.whitelistRoute(originChainId, repaymentChainId, usdc.address, mockTokens.l2Usdc, true);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, weth.address, mockTokens.l2Weth, true);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, dai.address, mockTokens.l2Dai, true);
await hubPool.whitelistRoute(mainnetChainId, repaymentChainId, usdc.address, mockTokens.l2Usdc, true);

return { ...tokens, ...mockTokens, hubPool, mockAdapter, mockSpoke, crossChainAdmin, ...parentFixture };
});
Expand Down