Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e079214
init v2 tokenbridge
tkernell Jan 30, 2026
bde4548
new query type for bridge implementation
akremstudy Jan 31, 2026
d11d564
Fix tests
akremstudy Jan 31, 2026
6716fc0
create and test tokenbridgev2
tkernell Feb 2, 2026
99491d3
small improvements and cleanup
tkernell Feb 2, 2026
76c41d6
tokenbridgev2 deploy script
tkernell Feb 2, 2026
13fab66
add update guardian delay
tkernell Feb 2, 2026
7984ac6
comments
tkernell Feb 2, 2026
323d5cd
small improvements
tkernell Feb 2, 2026
9e86450
track extra withdraw amounts by id
tkernell Feb 4, 2026
372bc31
make bridge address based on chain id and add .env support
akremstudy Feb 5, 2026
855e6e9
added RoleManager
tkernell Feb 10, 2026
373824c
role fix
tkernell Feb 10, 2026
42946d5
update deploy scripts
tkernell Feb 23, 2026
c699841
update tests for tokenbridgeV2 roles
tkernell Mar 3, 2026
c5f8766
audit 1 changes
tkernell Mar 6, 2026
d02b962
audit 2 changes
tkernell Mar 6, 2026
bb6ff47
Merge branch 'bridge-query-type-update' of https://github.com/tellor-…
tkernell Mar 9, 2026
05fa23b
rpc
tkernell Mar 9, 2026
c649c75
sanity checks script updates
tkernell Mar 10, 2026
5939d95
merged
tkernell Mar 10, 2026
088fc71
block all v1 bridge reports
tkernell Mar 10, 2026
a436258
merge from main
tkernell Mar 10, 2026
db1bb14
upgrades
tkernell Mar 10, 2026
4963976
fix e2e upgrade test
tkernell Mar 10, 2026
48a63fc
add v2 contract addr to upgrade test env
danflo27 Mar 11, 2026
1271c4d
mintToOracle on every new withdraw and deposit period
tkernell Mar 17, 2026
e4d6218
pausePeriod in constructor
tkernell Mar 17, 2026
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
15 changes: 9 additions & 6 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"

"github.com/tellor-io/layer/app/upgrades"
v_6_1_3 "github.com/tellor-io/layer/app/upgrades/v6.1.3"
v_6_1_4 "github.com/tellor-io/layer/app/upgrades/v6.1.4"

upgradetypes "cosmossdk.io/x/upgrade/types"
)
Expand All @@ -13,22 +13,25 @@ var (
// `Upgrades` defines the upgrade handlers and store loaders for the application.
// New upgrades should be added to this slice after they are implemented.
Upgrades = []*upgrades.Upgrade{
&v_6_1_3.Upgrade,
&v_6_1_4.Upgrade,
}
Forks = []upgrades.Fork{}
)

// setupUpgradeHandlers registers the upgrade handlers to perform custom upgrade
// logic and state migrations for software upgrades.
func (app *App) setupUpgradeHandlers() {
if app.UpgradeKeeper.HasHandler(v_6_1_3.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_6_1_3.UpgradeName))
if app.UpgradeKeeper.HasHandler(v_6_1_4.UpgradeName) {
panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_6_1_4.UpgradeName))
}
app.UpgradeKeeper.SetUpgradeHandler(
v_6_1_3.UpgradeName,
v_6_1_3.CreateUpgradeHandler(
v_6_1_4.UpgradeName,
v_6_1_4.CreateUpgradeHandler(
app.ModuleManager(),
app.configurator,
app.OracleKeeper,
app.BridgeKeeper,
app.RegistryKeeper,
),
)
}
Expand Down
16 changes: 16 additions & 0 deletions app/upgrades/v6.1.4/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package v6_1_4

import (
"github.com/tellor-io/layer/app/upgrades"

store "cosmossdk.io/store/types"
)

const (
UpgradeName = "v6.1.4"
)

var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
StoreUpgrades: store.StoreUpgrades{},
}
107 changes: 107 additions & 0 deletions app/upgrades/v6.1.4/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package v6_1_4

import (
"context"
"fmt"
"os"

"github.com/ethereum/go-ethereum/common"
bridgekeeper "github.com/tellor-io/layer/x/bridge/keeper"
bridgetypes "github.com/tellor-io/layer/x/bridge/types"
oraclekeeper "github.com/tellor-io/layer/x/oracle/keeper"
registrykeeper "github.com/tellor-io/layer/x/registry/keeper"
registrytypes "github.com/tellor-io/layer/x/registry/types"

"cosmossdk.io/math"
upgradetypes "cosmossdk.io/x/upgrade/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

/*
Upgrade to v6.1.4 includes:
- Deprecation of TRBBridge query type in favor of TRBBridgeV2
- Updates to oracle keeper to handle TRBBridgeV2 query type and prevent usage of TRBBridge
*/
// TODO: change these to real addresses
const (
MainnetChainID = "tellor1"
MainnetTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
TestnetChainID = "layertest-4"
TestnetTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
)

func CreateUpgradeHandler(
mm *module.Manager,
configurator module.Configurator,
oracleKeeper oraclekeeper.Keeper,
bridgeKeeper bridgekeeper.Keeper,
registryKeeper registrykeeper.Keeper,
) upgradetypes.UpgradeHandler {
trbbridgeV2 := registrytypes.DataSpec{
DocumentHash: "",
ResponseValueType: "address, string, uint256, uint256",
AbiComponents: []*registrytypes.ABIComponent{
{
Name: "toLayer",
FieldType: "bool",
NestedComponent: []*registrytypes.ABIComponent{},
},
{
Name: "depositId",
FieldType: "uint256",
NestedComponent: []*registrytypes.ABIComponent{},
},
},
AggregationMethod: "weighted-mode",
Registrar: "genesis",
ReportBlockWindow: 2000,
QueryType: "trbbridgev2",
}

return func(ctx context.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
err := registryKeeper.SetDataSpec(sdkCtx, "trbbridgev2", trbbridgeV2)
if err != nil {
return nil, err
}

tokenBridgeV2Address, err := resolveTokenBridgeV2Address(sdkCtx.ChainID())
if err != nil {
return nil, err
}

withdrawalId := uint64(1_000_000_000_000)
recipient := common.HexToAddress(tokenBridgeV2Address).Bytes()
sender := authtypes.NewModuleAddress(bridgetypes.ModuleName)
amount := sdk.NewCoin("loya", math.NewInt(2_800_000_000_000)) // 2.8 million TRB - 1 TRB = 1_000_000 loya

aggregate, queryData, err := bridgeKeeper.CreateWithdrawalAggregate(ctx, "TRBBridge", amount, sender, recipient, withdrawalId)
if err != nil {
return nil, err
}
err = oracleKeeper.SetAggregate(sdkCtx, aggregate, queryData, "TRBBridge-withdraw")
if err != nil {
return nil, err
}
sdkCtx.Logger().Info(fmt.Sprintf("Running %s Upgrade...", UpgradeName))
return mm.RunMigrations(ctx, configurator, vm)
}
}

func resolveTokenBridgeV2Address(chainID string) (string, error) {
switch chainID {
case MainnetChainID:
return MainnetTokenBridgeV2, nil
case TestnetChainID:
return TestnetTokenBridgeV2, nil
default:
addr := os.Getenv("TOKEN_BRIDGE_V2_ADDRESS")
if addr == "" {
return "", fmt.Errorf("TOKEN_BRIDGE_V2_ADDRESS env var not set for chain-id %s", chainID)
}
return addr, nil
}
}
4 changes: 3 additions & 1 deletion e2e/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import (
const (
haltHeightDelta = 12 // will propose upgrade this many blocks in the future
blocksAfterUpgrade = 12
testTokenBridgeV2 = "0x0000000000000000000000000000000000000000"
)

func TestLayerUpgrade(t *testing.T) {
ChainUpgradeTest(t, "layer", "layer", "local", "v6.1.3")
ChainUpgradeTest(t, "layer", "layer", "local", "v6.1.4")
}

func ChainUpgradeTest(t *testing.T, chainName, upgradeContainerRepo, upgradeVersion, upgradeName string) {
Expand Down Expand Up @@ -78,6 +79,7 @@ func ChainUpgradeTest(t *testing.T, chainName, upgradeContainerRepo, upgradeVers
EncodingConfig: e2e.LayerEncoding(),
ModifyGenesis: cosmos.ModifyGenesis(modifyGenesis),
AdditionalStartArgs: []string{"--key-name", "validator"},
Env: []string{"TOKEN_BRIDGE_V2_ADDRESS=" + testTokenBridgeV2},
},
},
})
Expand Down
28 changes: 28 additions & 0 deletions evm/contracts/testing/TestTokenBridgeV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { TokenBridgeV2 } from "../token-bridge/TokenBridgeV2.sol";

/// @title TokenBridgeV2
/// @dev allows us to test internal limit refreshes externally
contract TestTokenBridgeV2 is TokenBridgeV2 {
constructor(
address _token,
address _blobstream,
address _tellorFlex,
address _mainGuardian,
address _subGuardian,
uint256 _defaultRoleUpdateDelay,
uint256 _pausePeriod
) TokenBridgeV2(_token, _blobstream, _tellorFlex, _mainGuardian, _subGuardian, _defaultRoleUpdateDelay, _pausePeriod) {}

/// @notice refreshes the deposit limit every 12 hours so no one can spam layer with new tokens
function refreshDepositLimit() external returns (uint256) {
return _refreshDepositLimit();
}

/// @notice refreshes the withdraw limit every 12 hours so no one can spam layer with new tokens
function refreshWithdrawLimit() external returns (uint256) {
return _refreshWithdrawLimit();
}
}
83 changes: 83 additions & 0 deletions evm/contracts/token-bridge/RoleManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/// @title RoleManager
/// @notice Role manager for the TokenBridgeV2
contract RoleManager {
/*Storage*/
mapping(bytes32 => RoleInfo) public roles;
mapping(bytes32 => RoleUpdate) public roleUpdateProposals;

struct RoleInfo {
address roleAddress;
uint256 roleUpdateDelay;
}

struct RoleUpdate {
address newAddress;
uint256 newUpdateDelay;
uint256 proposalTime;
}

/*Events*/
event RoleUpdateProposed(bytes32 _role, address _newAddress, uint256 _newUpdateDelay);
event RoleUpdateAccepted(bytes32 _role, address _newAddress);
event RoleUpdateRejected(bytes32 _role);

/*Functions*/
/// @notice constructor
/// @param _guardian the address of the guardian
/// @param _guardianUpdateDelay the delay before the guardian update can be accepted
constructor(address _guardian, uint256 _guardianUpdateDelay) {
roles[keccak256("MAIN_GUARDIAN")] = RoleInfo({
roleAddress: _guardian,
roleUpdateDelay: _guardianUpdateDelay
});
}

/// @notice proposes a role update
/// @param _role the role to update
/// @param _newAddress the new address for the role
/// @param _newUpdateDelay the delay before the role update can be accepted
function proposeRoleUpdate(bytes32 _role, address _newAddress, uint256 _newUpdateDelay) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdate = roleUpdateProposals[_role];
require(_roleUpdate.newAddress == address(0), "RoleManager: Role update already proposed");
require(_newAddress != address(0), "RoleManager: New address cannot be the zero address");
_roleUpdate.newAddress = _newAddress;
_roleUpdate.newUpdateDelay = _newUpdateDelay;
_roleUpdate.proposalTime = block.timestamp;
emit RoleUpdateProposed(_role, _newAddress, _newUpdateDelay);
}

/// @notice accepts a role update
/// @param _role the role to update
function acceptRoleUpdate(bytes32 _role) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdateProposal = roleUpdateProposals[_role];
require(_roleUpdateProposal.newAddress != address(0), "RoleManager: Role update not proposed");
RoleInfo storage _roleInfo = roles[_role];
require(block.timestamp - _roleUpdateProposal.proposalTime > _roleInfo.roleUpdateDelay, "RoleManager: Role update delay not passed");
_roleInfo.roleAddress = _roleUpdateProposal.newAddress;
_roleInfo.roleUpdateDelay = _roleUpdateProposal.newUpdateDelay;
emit RoleUpdateAccepted(_role, _roleUpdateProposal.newAddress);
delete roleUpdateProposals[_role];
}

/// @notice rejects a role update
/// @param _role the role to update
function rejectRoleUpdate(bytes32 _role) external {
_roleRestricted(keccak256("MAIN_GUARDIAN"));
RoleUpdate storage _roleUpdateProposal = roleUpdateProposals[_role];
require(_roleUpdateProposal.newAddress != address(0), "RoleManager: Role update not proposed");
delete roleUpdateProposals[_role];
emit RoleUpdateRejected(_role);
}

/* Internal Functions */
/// @notice restricts the function to the role
/// @param _role the role to restrict the function to
function _roleRestricted(bytes32 _role) internal view {
require(msg.sender == roles[_role].roleAddress, "RoleManager: Only role can call this function");
}
}
Loading
Loading