From 6b5b2b26f2ed5193f47042420df711af9abee752 Mon Sep 17 00:00:00 2001 From: tkernell Date: Thu, 9 Apr 2026 10:01:30 -0500 Subject: [PATCH] add bridge update cutoff time (#1003) * add bridge update cutoff timeg * update mainnet tbridge addr (cherry picked from commit 39b614b8b515b8a7fe8f4e8e7e3ace2d3f8b425a) --- app/upgrades/v6.1.4/upgrade.go | 2 +- evm/contracts/token-bridge/TokenBridgeV2.sol | 3 ++ evm/test/TokenBridgeV2-FunctionTests.js | 31 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/upgrades/v6.1.4/upgrade.go b/app/upgrades/v6.1.4/upgrade.go index d00768a75..603d84dae 100644 --- a/app/upgrades/v6.1.4/upgrade.go +++ b/app/upgrades/v6.1.4/upgrade.go @@ -28,7 +28,7 @@ Upgrade to v6.1.4 includes: // TODO: change these to real addresses const ( MainnetChainID = "tellor1" - MainnetTokenBridgeV2 = "0xa5fE7C1dD35Ce246fdc7FA165A2782049202D3D2" + MainnetTokenBridgeV2 = "0x6ec401744008f4B018Ed9A36f76e6629799Ee50E" TestnetChainID = "layertest-4" TestnetTokenBridgeV2 = "0x55355157703A44f7516FBB831333317E98944e32" ) diff --git a/evm/contracts/token-bridge/TokenBridgeV2.sol b/evm/contracts/token-bridge/TokenBridgeV2.sol index 02cff5539..c22c6a166 100644 --- a/evm/contracts/token-bridge/TokenBridgeV2.sol +++ b/evm/contracts/token-bridge/TokenBridgeV2.sol @@ -26,6 +26,7 @@ contract TokenBridgeV2 is LayerTransition, RoleManager { uint256 public totalPauseTributeBalance; // total amount of tokens held as pause tribute uint256 public withdrawLimitUpdateTime; // last time the withdraw limit was updated uint256 public withdrawLimitRecord; // amount you can withdraw per limit period + uint256 public immutable DATA_BRIDGE_UPDATE_BUFFER; // buffer time at end of pause period when bridge can not longer be updated uint256 public constant DEPOSIT_LIMIT_DENOMINATOR = 100e18 / 20e18; // 100/depositLimitPercentage uint256 public constant MS_PER_SECOND = 1000; // factor to convert milliseconds to seconds uint256 public immutable PAUSE_PERIOD; // bridge pause period duration @@ -112,6 +113,7 @@ contract TokenBridgeV2 is LayerTransition, RoleManager { dataBridge = ITellorDataBridge(_dataBridge); deployer = msg.sender; PAUSE_PERIOD = _pausePeriod; + DATA_BRIDGE_UPDATE_BUFFER = _pausePeriod * 2 / 21; roles[keccak256("APPROVE_PAUSE")] = RoleInfo({ roleAddress: _subGuardian, @@ -251,6 +253,7 @@ contract TokenBridgeV2 is LayerTransition, RoleManager { function updateDataBridge(address _dataBridge) external { _roleRestricted(keccak256("UPDATE_DATA_BRIDGE")); require(bridgeState == BridgeState.PAUSED, "TokenBridgeV2: can only update data bridge when bridge is paused"); + require(block.timestamp < lastPauseTimestamp + PAUSE_PERIOD - DATA_BRIDGE_UPDATE_BUFFER, "TokenBridgeV2: can only update data bridge before the buffer time"); require(_dataBridge != address(0), "TokenBridgeV2: data bridge address cannot be the zero address"); dataBridge = ITellorDataBridge(_dataBridge); emit DataBridgeUpdated(_dataBridge); diff --git a/evm/test/TokenBridgeV2-FunctionTests.js b/evm/test/TokenBridgeV2-FunctionTests.js index 84a972bcb..2a6d1ac54 100644 --- a/evm/test/TokenBridgeV2-FunctionTests.js +++ b/evm/test/TokenBridgeV2-FunctionTests.js @@ -69,6 +69,11 @@ describe("TokenBridgeV2 - Function Tests", async function () { it("constructor", async function () { assert.equal(await tbridge.token(), await token.address) assert.equal(await tbridge.dataBridge(), await blobstream.address) + assert.equal( + (await tbridge.DATA_BRIDGE_UPDATE_BUFFER()).toString(), + String((PAUSE_PERIOD * 2) / 21), + "DATA_BRIDGE_UPDATE_BUFFER should be 2/21 of pause period" + ) const mainGuardianRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MAIN_GUARDIAN")) const approvePauseRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("APPROVE_PAUSE")) assert.equal((await tbridge.roles(mainGuardianRole)).roleAddress, await mainGuardian.address) @@ -508,6 +513,32 @@ describe("TokenBridgeV2 - Function Tests", async function () { assert.equal(await tbridge.dataBridge(), blobstream2.address) }) + it("updateDataBridge reverts after buffer window before pause period ends", async function () { + for (let i = 0; i < 10; i++) { + await token.faucet(accounts[1].address) + } + await token.connect(accounts[1]).approve(tbridge.address, h.toWei("10000")) + await tbridge.connect(accounts[1]).proposePauseBridge("layer") + await tbridge.connect(subGuardian).approvePause(0) + + const lastPause = await tbridge.lastPauseTimestamp() + const pausePeriod = await tbridge.PAUSE_PERIOD() + const buffer = await tbridge.DATA_BRIDGE_UPDATE_BUFFER() + const latest = ethers.BigNumber.from((await ethers.provider.getBlock("latest")).timestamp) + const deadline = lastPause.add(pausePeriod).sub(buffer) + const advance = deadline.sub(latest) + await h.advanceTime(advance.toNumber()) + + const blobstream3 = await ethers.deployContract( + "TellorDataBridge", [ + mainGuardian.address, + VALIDATOR_SET_DOMAIN_SEPARATOR_MAINNET + ] + ) + await blobstream3.init(1, 2, UNBONDING_PERIOD, ethers.utils.solidityKeccak256(["string"], ["buffer-test"])) + await h.expectThrow(tbridge.connect(mainGuardian).updateDataBridge(blobstream3.address)) + }) + it("role updates", async function () { const MAIN_GUARDIAN_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MAIN_GUARDIAN")) const APPROVE_PAUSE_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("APPROVE_PAUSE"))