Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
61e6a88
Adds new functionality for application approval and authorization inc…
lionakhnazarov Apr 7, 2026
f7f93f8
Restrict application approval to governance only in TokenStaking
lionakhnazarov Apr 13, 2026
2117bae
Merge branch 'main' into feat/testnet4-deployment-support
lionakhnazarov Apr 13, 2026
e75cf76
Update GitHub Actions workflows to use Node.js v4 and enable Corepack…
lionakhnazarov Apr 13, 2026
8aede8d
Merge branch 'feat/testnet4-deployment-support' of github.com:thresho…
lionakhnazarov Apr 13, 2026
31c1b2e
Update Yarn configuration and package manager version
lionakhnazarov Apr 13, 2026
b5a532f
Update GitHub Actions workflows to reference local reusable Solidity …
lionakhnazarov Apr 13, 2026
d4d62b0
fix(scripts): harden operator key management security
piotr-roslaniec Apr 14, 2026
42ca14f
fix(scripts): pass signing keys via ETH_PRIVATE_KEY env var, not CLI …
piotr-roslaniec Apr 14, 2026
2347611
fix(deploy): remove hardcoded proxy kind and deduplicate fs import
piotr-roslaniec Apr 14, 2026
300b1ad
fix(deployments): restore deleted mainnet TokenStaking.json
piotr-roslaniec Apr 14, 2026
ea8ae81
fix(TokenStaking): add zero-address guard to increaseAuthorization
piotr-roslaniec Apr 14, 2026
1e32bf1
Merge pull request #177 from threshold-network/fix/testnet4-security-…
lionakhnazarov Apr 15, 2026
b524ca4
Merge branch 'main' into feat/testnet4-deployment-support
lrsaturnino Apr 17, 2026
0889e0a
Enhance deployment scripts and update .gitignore for operator setup
lionakhnazarov Apr 17, 2026
ead0615
Merge branch 'feat/testnet4-deployment-support' of github.com:thresho…
lionakhnazarov Apr 17, 2026
8d3c75c
Enhance operator setup scripts to handle private keys securely
lionakhnazarov Apr 23, 2026
71fd589
Improve private key handling in operator setup script
lionakhnazarov Apr 23, 2026
b993dde
Enhance operator setup script with T token minting and validation
lionakhnazarov Apr 23, 2026
ec06657
Enhance operator setup script with T token balance checks and minting…
lionakhnazarov Apr 24, 2026
0e9702d
Update deployment configurations and enhance operator setup script
lionakhnazarov Apr 24, 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
1,120 changes: 1,120 additions & 0 deletions TokenStaking.json

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions contracts/staking/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ interface IStaking {
//
//

/// @notice Allows the Governance to approve the particular application
/// before individual stake authorizers are able to authorize it.
function approveApplication(address application) external;

/// @notice Increases the authorization of the given staking provider for
/// the given application by the given amount. Can only be called by
/// the given staking provider's authorizer.
/// @dev Calls `authorizationIncreased` callback on the given application to
/// notify the application about authorization change.
/// See `IApplication`.
function increaseAuthorization(
address stakingProvider,
address application,
uint96 amount
) external;

/// @notice Requests decrease of the authorization for the given staking
/// provider on the given application by the provided amount.
/// It may not change the authorized amount immediatelly. When
Expand Down
78 changes: 78 additions & 0 deletions contracts/staking/TokenStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,84 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
//
//

/// @notice Allows the Governance to approve the particular application
/// before individual stake authorizers are able to authorize it.
function approveApplication(address application)
external
virtual
override
onlyGovernance
{
require(application != address(0), "Parameters must be specified");
ApplicationInfo storage info = applicationInfo[application];
require(
info.status == ApplicationStatus.NOT_APPROVED ||
info.status == ApplicationStatus.PAUSED,
"Can't approve application"
);

if (info.status == ApplicationStatus.NOT_APPROVED) {
applications.push(application);
}
info.status = ApplicationStatus.APPROVED;
emit ApplicationStatusChanged(application, ApplicationStatus.APPROVED);
}

/// @notice Increases the authorization of the given staking provider for
/// the given application by the given amount. Can only be called by
/// the given staking provider's authorizer.
/// @dev Calls `authorizationIncreased` callback on the given application to
/// notify the application about authorization change.
/// See `IApplication`.
function increaseAuthorization(
address stakingProvider,
address application,
uint96 amount
) external virtual override onlyAuthorizerOf(stakingProvider) {
require(amount > 0, "Parameters must be specified");
ApplicationInfo storage applicationStruct = applicationInfo[
application
];
require(
applicationStruct.status == ApplicationStatus.APPROVED,
"Application is not approved"
);

StakingProviderInfo storage stakingProviderStruct = stakingProviders[
stakingProvider
];
AppAuthorization storage authorization = stakingProviderStruct
.authorizations[application];
uint96 fromAmount = authorization.authorized;
if (fromAmount == 0) {
require(
authorizationCeiling == 0 ||
stakingProviderStruct.authorizedApplications.length <
authorizationCeiling,
"Too many applications"
);
stakingProviderStruct.authorizedApplications.push(application);
}

uint96 availableTValue = getAvailableToAuthorize(
stakingProvider,
application
);
require(availableTValue >= amount, "Not enough stake to authorize");
authorization.authorized += amount;
emit AuthorizationIncreased(
stakingProvider,
application,
fromAmount,
authorization.authorized
);
IApplication(application).authorizationIncreased(
stakingProvider,
fromAmount,
authorization.authorized
);
}

/// @notice Called by the application at its discretion to approve the
/// previously requested authorization decrease request. Can only be
/// called by the application that was previously requested to
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/TokenStakingTestSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ contract ExtendedTokenStaking is TokenStaking {
address stakingProvider,
address application,
uint96 amount
) external onlyAuthorizerOf(stakingProvider) {
) external override onlyAuthorizerOf(stakingProvider) {
require(amount > 0, "Parameters must be specified");
ApplicationInfo storage applicationStruct = applicationInfo[
application
Expand Down Expand Up @@ -261,7 +261,7 @@ contract ExtendedTokenStaking is TokenStaking {

/// @notice Allows the Governance to approve the particular application
/// before individual stake authorizers are able to authorize it.
function approveApplication(address application) external {
function approveApplication(address application) external override {
Comment thread
lionakhnazarov marked this conversation as resolved.
Outdated
require(application != address(0), "Parameters must be specified");
ApplicationInfo storage info = applicationInfo[application];
require(
Expand Down
2 changes: 1 addition & 1 deletion deploy/07_deploy_token_staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {

// TODO: Consider upgradable deployment also for sepolia.
let tokenStakingAddress
if (hre.network.name == "mainnet") {
if (hre.network.name == "mainnet" || hre.network.name == "sepolia") {
Comment thread
lionakhnazarov marked this conversation as resolved.
const TokenStaking = await ethers.getContractFactory("TokenStaking")

const tokenStaking = await upgrades.deployProxy(
Expand Down
82 changes: 82 additions & 0 deletions deploy/54_upgrade_token_staking_extended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { DeployFunction } from "hardhat-deploy/types"

import { ethers, upgrades } from "hardhat"

/**
* Upgrades TokenStaking proxy to ExtendedTokenStaking implementation.
* ExtendedTokenStaking adds the stake() function required for native T staking.
* The base TokenStaking does not have stake() - it only supports legacy KEEP/NU
* migrations. Run this on sepolia (or other testnets) where operators need to
* stake T directly.
*
* From repo root (syncs deployments, upgrades, copies ABI to tbtc-v2):
* bash scripts/upgrade-token-staking-sepolia.sh
Comment thread
lionakhnazarov marked this conversation as resolved.
Outdated
*/
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre
const { log } = deployments
const { deployer } = await getNamedAccounts()

if (hre.network.name !== "sepolia") {
log("Skipping TokenStaking upgrade (only for sepolia)")
return
}

let proxyAddress: string
const existing = await deployments.getOrNull("TokenStaking")
if (existing) {
proxyAddress = existing.address
} else {
// 07_deploy_token_staking saves to TokenStaking.json in deployments dir
const fs = require("fs")
const deploymentPath = `deployments/${hre.network.name}/TokenStaking.json`
Comment thread
lionakhnazarov marked this conversation as resolved.
if (!fs.existsSync(deploymentPath)) {
log("TokenStaking not deployed, skipping upgrade")
return
}
const deployment = JSON.parse(fs.readFileSync(deploymentPath, "utf8"))
proxyAddress = deployment.address
}

log(`Upgrading TokenStaking at ${proxyAddress} to ExtendedTokenStaking`)

const T = await deployments.get("T")

const ExtendedTokenStaking = await ethers.getContractFactory(
"ExtendedTokenStaking"
)

const upgraded = await upgrades.upgradeProxy(
proxyAddress,
ExtendedTokenStaking,
{
constructorArgs: [T.address],
kind: "transparent",
}
)
await upgraded.deployed()

log(`Upgraded TokenStaking to ExtendedTokenStaking at ${upgraded.address}`)

// Update deployment JSON with new ABI (includes stake)
const implementationInterface = upgraded.interface
const jsonAbi = implementationInterface.format(ethers.utils.FormatTypes.json)
const tokenStakingDeployment = {
address: upgraded.address,
abi: JSON.parse(jsonAbi as string),
}
const fs = require("fs")
const deploymentsDir = `deployments/${hre.network.name}`
fs.writeFileSync(
`${deploymentsDir}/TokenStaking.json`,
JSON.stringify(tokenStakingDeployment, null, 2),
"utf8"
)
log(`Updated ${deploymentsDir}/TokenStaking.json with ExtendedTokenStaking ABI`)
}

export default func

func.tags = ["TokenStakingUpgrade", "UpgradeTokenStaking"]
func.dependencies = ["T"]
Loading
Loading