From d4d62b0d11d370d169d76ebe74067e0158b52d64 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 14 Apr 2026 08:47:46 +0000
Subject: [PATCH 1/5] fix(scripts): harden operator key management security
- Remove private key logging from create-operator-keystore and
get-operator-key; address is sufficient for confirmation
- Remove staking provider and operator private keys from generated
.env files; keys written to disk are a git-leak risk
- Print staking provider key once to terminal with a prominent
"copy now" warning instead of persisting it to .env
- Require non-empty password in create-operator-keystore and
setup-new-staking-provider; empty-password keystores are
trivially decryptable
- Require explicit keystore path in get-operator-key; remove the
hardcoded developer-machine UUID default that caused ENOENT for
all other users
- Fix --list path in get-operator-key: ../../operator-1-keystore
resolved above repo root; corrected to ../operator-1-keystore
- Convert sync fs calls to fs.promises and add try/catch inside
main() across all three scripts
---
scripts/create-operator-keystore.js | 38 ++++++-----
scripts/get-operator-key.js | 56 +++++++++++------
scripts/setup-new-staking-provider.js | 90 +++++++++++++++------------
3 files changed, 109 insertions(+), 75 deletions(-)
diff --git a/scripts/create-operator-keystore.js b/scripts/create-operator-keystore.js
index 09250cdb..00d80c13 100644
--- a/scripts/create-operator-keystore.js
+++ b/scripts/create-operator-keystore.js
@@ -3,27 +3,31 @@ const fs = require("fs");
const path = require("path");
const { ethers } = require("ethers");
-const password = process.argv[2] || "";
+const password = process.argv[2];
+if (!password) {
+ console.error("Usage: node create-operator-keystore.js ");
+ console.error("A non-empty password is required to encrypt the keystore.");
+ process.exit(1);
+}
async function main() {
- const wallet = ethers.Wallet.createRandom();
- const encrypted = await wallet.encrypt(password);
+ try {
+ const wallet = ethers.Wallet.createRandom();
+ const encrypted = await wallet.encrypt(password);
- const dir = path.join(__dirname, "../operator-1-keystore");
- const filename = wallet.address.toLowerCase().slice(2, 10) + "-new-operator";
- const filepath = path.join(dir, filename);
+ const dir = path.join(__dirname, "../operator-1-keystore");
+ const filename = wallet.address.toLowerCase().slice(2, 10) + "-new-operator";
+ const filepath = path.join(dir, filename);
- if (!fs.existsSync(dir)) {
- fs.mkdirSync(dir, { recursive: true });
- }
- fs.writeFileSync(filepath, encrypted);
+ await fs.promises.mkdir(dir, { recursive: true });
+ await fs.promises.writeFile(filepath, encrypted);
- console.log("Address:", wallet.address);
- console.log("Private key:", wallet.privateKey);
- console.log("Keystore saved to:", filepath);
+ console.log("Address:", wallet.address);
+ console.log("Keystore saved to:", filepath);
+ } catch (e) {
+ console.error(e);
+ process.exit(1);
+ }
}
-main().catch((e) => {
- console.error(e);
- process.exit(1);
-});
+main();
diff --git a/scripts/get-operator-key.js b/scripts/get-operator-key.js
index 5c2837a6..b29e065c 100644
--- a/scripts/get-operator-key.js
+++ b/scripts/get-operator-key.js
@@ -3,29 +3,47 @@ const fs = require("fs");
const path = require("path");
const { ethers } = require("ethers");
-const keystorePath = process.argv[2] || path.join(__dirname, "../operator-1-keystore/c88df450-36e6-473e-908a-3349242f463e");
+if (!process.argv[2]) {
+ console.error("Usage: node get-operator-key.js [password]");
+ console.error(" node get-operator-key.js --list");
+ process.exit(1);
+}
+
+const keystorePath = process.argv[2];
const password = process.argv[3] || "";
-// If --list, show addresses for all keystores in operator-1-keystore
-if (process.argv[2] === "--list") {
- const dir = path.join(__dirname, "../../operator-1-keystore");
- const files = fs.readdirSync(dir).filter((f) => !f.startsWith(".") && f.length > 10);
- for (const file of files) {
- try {
- let content = fs.readFileSync(path.join(dir, file), "utf8");
- const json = content.split("\n")[0]; // some files have extra content
- if (json.startsWith("{")) {
- const wallet = ethers.Wallet.fromEncryptedJsonSync(json, "");
- console.log(file, "->", wallet.address);
+async function main() {
+ try {
+ if (keystorePath === "--list") {
+ const dir = path.join(__dirname, "../operator-1-keystore");
+ const files = (await fs.promises.readdir(dir)).filter(
+ (f) => !f.startsWith(".") && f.length > 10
+ );
+ for (const file of files) {
+ try {
+ const content = await fs.promises.readFile(
+ path.join(dir, file),
+ "utf8"
+ );
+ const json = content.split("\n")[0]; // some files have extra content
+ if (json.startsWith("{")) {
+ const wallet = await ethers.Wallet.fromEncryptedJson(json, "");
+ console.log(file, "->", wallet.address);
+ }
+ } catch (e) {
+ console.log(file, "-> error:", e.message);
+ }
}
- } catch (e) {
- console.log(file, "-> error:", e.message);
+ return;
}
+
+ const json = await fs.promises.readFile(keystorePath, "utf8");
+ const wallet = await ethers.Wallet.fromEncryptedJson(json, password);
+ console.log("Address:", wallet.address);
+ } catch (e) {
+ console.error(e);
+ process.exit(1);
}
- process.exit(0);
}
-const json = fs.readFileSync(keystorePath, "utf8");
-const wallet = ethers.Wallet.fromEncryptedJsonSync(json, password);
-console.log("Address:", wallet.address);
-console.log("Private key:", wallet.privateKey);
+main();
diff --git a/scripts/setup-new-staking-provider.js b/scripts/setup-new-staking-provider.js
index 9ea10bbe..35def3e9 100644
--- a/scripts/setup-new-staking-provider.js
+++ b/scripts/setup-new-staking-provider.js
@@ -3,14 +3,14 @@
* Creates a new staking provider wallet and operator keystore for Threshold/Keep operator setup.
*
* Usage:
- * node scripts/setup-new-staking-provider.js [operator-keystore-password] [operator-index]
+ * node scripts/setup-new-staking-provider.js [operator-index]
*
* If operator-index is a number (e.g. 1..8), writes .env.operator-{index} and a uniquely named keystore
* instead of .env.new-operator (for multi-operator setups).
*
* Output:
- * - Staking provider: address + private key (use for stake, authorize, registerOperator)
- * - Operator: address + keystore file (use for keep-client and joinSortitionPool)
+ * - Staking provider: address only (private key is NOT written to disk -- store it yourself)
+ * - Operator: address + encrypted keystore file (use for keep-client and joinSortitionPool)
*
* Prerequisites:
* - 80,000 T tokens (40k for RandomBeacon + 40k for WalletRegistry)
@@ -20,7 +20,15 @@ const fs = require("fs");
const path = require("path");
const { ethers } = require("ethers");
-const operatorPassword = process.argv[2] || "";
+const operatorPassword = process.argv[2];
+if (!operatorPassword) {
+ console.error(
+ "Usage: node setup-new-staking-provider.js [operator-index]"
+ );
+ console.error("A non-empty password is required to encrypt the operator keystore.");
+ process.exit(1);
+}
+
const indexArg = process.argv[3];
const opIndex =
indexArg !== undefined && /^[1-9][0-9]*$/.test(String(indexArg))
@@ -28,54 +36,58 @@ const opIndex =
: null;
async function main() {
- const stakingProvider = ethers.Wallet.createRandom();
- const operator = ethers.Wallet.createRandom();
- const operatorEncrypted = await operator.encrypt(operatorPassword);
+ try {
+ const stakingProvider = ethers.Wallet.createRandom();
+ const operator = ethers.Wallet.createRandom();
+ const operatorEncrypted = await operator.encrypt(operatorPassword);
- const keystoreDir = path.join(__dirname, "../operator-1-keystore");
- const operatorFilename = opIndex
- ? `op${opIndex}-${operator.address.toLowerCase().slice(2, 10)}`
- : operator.address.toLowerCase().slice(2, 10) + "-new-operator";
- const operatorFilepath = path.join(keystoreDir, operatorFilename);
+ const keystoreDir = path.join(__dirname, "../operator-1-keystore");
+ const operatorFilename = opIndex
+ ? `op${opIndex}-${operator.address.toLowerCase().slice(2, 10)}`
+ : operator.address.toLowerCase().slice(2, 10) + "-new-operator";
+ const operatorFilepath = path.join(keystoreDir, operatorFilename);
- if (!fs.existsSync(keystoreDir)) {
- fs.mkdirSync(keystoreDir, { recursive: true });
- }
- fs.writeFileSync(operatorFilepath, operatorEncrypted);
+ await fs.promises.mkdir(keystoreDir, { recursive: true });
+ await fs.promises.writeFile(operatorFilepath, operatorEncrypted);
- const envPath = path.join(
- __dirname,
- opIndex ? `../.env.operator-${opIndex}` : "../.env.new-operator"
- );
- const envContent = `# New staking provider + operator (generated by setup-new-staking-provider.js)
+ const envPath = path.join(
+ __dirname,
+ opIndex ? `../.env.operator-${opIndex}` : "../.env.new-operator"
+ );
+ const envContent = `# New staking provider + operator (generated by setup-new-staking-provider.js)
# Operator index: ${opIndex || "default (.env.new-operator)"}
-# Add these to your shell or .env. DO NOT commit private keys.
+# Source this file before running fund-new-operator.sh / run-new-operator-setup.sh.
+# NEVER commit this file -- add .env.new-operator and .env.operator-* to .gitignore.
# Staking provider (owns stake, authorizes, registers operator)
+# WARNING: private key is NOT stored here. Save it to a hardware wallet or password manager.
NEW_STAKING_PROVIDER_ADDRESS=${stakingProvider.address}
-NEW_STAKING_PROVIDER_KEY=${stakingProvider.privateKey}
# Operator (runs keep-client, calls joinSortitionPool)
NEW_OPERATOR_ADDRESS=${operator.address}
-NEW_OPERATOR_KEY=${operator.privateKey}
OPERATOR_KEYSTORE_PATH=${path.resolve(operatorFilepath)}
`;
- fs.writeFileSync(envPath, envContent);
+ await fs.promises.writeFile(envPath, envContent);
- console.log("=== New Staking Provider + Operator ===\n");
- console.log("Staking provider address:", stakingProvider.address);
- console.log("Staking provider key: ", stakingProvider.privateKey);
- console.log("\nOperator address: ", operator.address);
- console.log("Operator key: ", operator.privateKey);
- console.log("Operator keystore: ", operatorFilepath);
- console.log("\nEnv file written to: ", path.resolve(envPath));
- console.log("\nNext steps:");
- console.log(" 1. bash scripts/fund-new-operator.sh # Transfer T + get Sepolia ETH");
- console.log(" 2. bash scripts/run-new-operator-setup.sh");
+ console.log("=== New Staking Provider + Operator ===\n");
+ console.log("Staking provider address:", stakingProvider.address);
+ console.log("Staking provider key: ", stakingProvider.privateKey);
+ console.log(
+ " ^^^ COPY AND STORE THIS KEY NOW -- it is shown once and NOT saved to any file ^^^"
+ );
+ console.log("\nOperator address: ", operator.address);
+ console.log("Operator keystore: ", operatorFilepath);
+ console.log("\nEnv file written to: ", path.resolve(envPath));
+ console.log("\nNext steps:");
+ console.log(
+ " 1. bash scripts/fund-new-operator.sh # Transfer T + get Sepolia ETH"
+ );
+ console.log(" 2. bash scripts/run-new-operator-setup.sh");
+ } catch (e) {
+ console.error(e);
+ process.exit(1);
+ }
}
-main().catch((e) => {
- console.error(e);
- process.exit(1);
-});
+main();
From 42ca14f8e7a9f28b0b11939931e3b5e0719d8dd8 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 14 Apr 2026 08:50:53 +0000
Subject: [PATCH 2/5] fix(scripts): pass signing keys via ETH_PRIVATE_KEY env
var, not CLI args
Passing --private-key as a CLI argument exposes the key in ps aux
output and persists in shell history. Using ETH_PRIVATE_KEY as an
inline env assignment (ETH_PRIVATE_KEY="$key" cast send ...) keeps
the key out of the argv list.
Introduce _sp_cast_send_ok / _op_cast_send_ok wrappers in
run-new-operator-setup.sh that inject ETH_PRIVATE_KEY for the
respective signer, and replace all --private-key flag usages.
Update fund-new-operator.sh likewise for the deployer key.
Update run-new-operator-setup.sh usage comment to reflect that
NEW_STAKING_PROVIDER_KEY and NEW_OPERATOR_KEY are no longer
written to .env files and must be exported by the operator.
---
scripts/fund-new-operator.sh | 5 ++--
scripts/run-new-operator-setup.sh | 49 +++++++++++++++++++------------
2 files changed, 33 insertions(+), 21 deletions(-)
diff --git a/scripts/fund-new-operator.sh b/scripts/fund-new-operator.sh
index 07f5ec03..3470cecd 100755
--- a/scripts/fund-new-operator.sh
+++ b/scripts/fund-new-operator.sh
@@ -41,8 +41,9 @@ fi
: "${CONTRACT_OWNER_ACCOUNT_PRIVATE_KEY:?Set CONTRACT_OWNER_ACCOUNT_PRIVATE_KEY (deployer with T balance)}"
echo "=== Transfer 80,000 T to staking provider ==="
-cast send $T_TOKEN "transfer(address,uint256)" $NEW_STAKING_PROVIDER_ADDRESS $AMOUNT_80K \
- --rpc-url $CHAIN_API_URL --private-key $CONTRACT_OWNER_ACCOUNT_PRIVATE_KEY
+ETH_PRIVATE_KEY="$CONTRACT_OWNER_ACCOUNT_PRIVATE_KEY" \
+ cast send $T_TOKEN "transfer(address,uint256)" $NEW_STAKING_PROVIDER_ADDRESS $AMOUNT_80K \
+ --rpc-url $CHAIN_API_URL
echo ""
echo "=== Sepolia ETH ==="
diff --git a/scripts/run-new-operator-setup.sh b/scripts/run-new-operator-setup.sh
index fece929c..4526ac53 100644
--- a/scripts/run-new-operator-setup.sh
+++ b/scripts/run-new-operator-setup.sh
@@ -16,7 +16,9 @@
#
# Usage:
# source .env
-# source .env.new-operator # or export NEW_* vars manually
+# source .env.new-operator # provides addresses + OPERATOR_KEYSTORE_PATH
+# export NEW_STAKING_PROVIDER_KEY=0x... # key shown once by setup-new-staking-provider.js
+# export NEW_OPERATOR_KEY=0x... # or use OPERATOR_KEYSTORE_PATH + password instead
# bash scripts/run-new-operator-setup.sh
#
set -e
@@ -40,15 +42,24 @@ if [ -f .env.new-operator ]; then source .env.new-operator; fi
: "${CHAIN_API_URL:?Set CHAIN_API_URL}"
: "${NEW_STAKING_PROVIDER_ADDRESS:?Run setup-new-staking-provider.js first}"
-: "${NEW_STAKING_PROVIDER_KEY:?Run setup-new-staking-provider.js first}"
+: "${NEW_STAKING_PROVIDER_KEY:?Export NEW_STAKING_PROVIDER_KEY (shown once by setup-new-staking-provider.js)}"
: "${NEW_OPERATOR_ADDRESS:?Run setup-new-staking-provider.js first}"
-: "${NEW_OPERATOR_KEY:?Run setup-new-staking-provider.js first}"
+: "${NEW_OPERATOR_KEY:?Export NEW_OPERATOR_KEY (shown once by setup-new-staking-provider.js)}"
SP="$NEW_STAKING_PROVIDER_ADDRESS"
SP_KEY="$NEW_STAKING_PROVIDER_KEY"
OP="$NEW_OPERATOR_ADDRESS"
OP_KEY="$NEW_OPERATOR_KEY"
+# Use ETH_PRIVATE_KEY env var so keys are not passed as CLI arguments
+# (--private-key exposes the key in ps aux and shell history)
+_sp_cast_send_ok() {
+ ETH_PRIVATE_KEY="$SP_KEY" cast_send_ok "$@"
+}
+_op_cast_send_ok() {
+ ETH_PRIVATE_KEY="$OP_KEY" cast_send_ok "$@"
+}
+
cast_send_ok() {
local out tx st
out=$(cast send "$@" 2>&1) || {
@@ -71,40 +82,40 @@ cast_send_ok() {
}
echo "=== Step 1: Approve TokenStaking to spend T ==="
-cast_send_ok $T_TOKEN "approve(address,uint256)" $TOKEN_STAKING $AMOUNT_80K \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+_sp_cast_send_ok $T_TOKEN "approve(address,uint256)" $TOKEN_STAKING $AMOUNT_80K \
+ --rpc-url $CHAIN_API_URL
echo "=== Step 2: Stake 80,000 T (stakingProvider = beneficiary = authorizer) ==="
-cast_send_ok $TOKEN_STAKING "stake(address,address,address,uint96)" \
+_sp_cast_send_ok $TOKEN_STAKING "stake(address,address,address,uint96)" \
$SP $SP $SP $AMOUNT_80K \
--gas-limit "$OPERATOR_STAKE_GAS_LIMIT" \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+ --rpc-url $CHAIN_API_URL
echo "=== Step 3: Authorize for RandomBeacon (40,000 T) ==="
-cast_send_ok $TOKEN_STAKING "increaseAuthorization(address,address,uint96)" \
+_sp_cast_send_ok $TOKEN_STAKING "increaseAuthorization(address,address,uint96)" \
$SP $RANDOM_BEACON $AMOUNT_40K \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+ --rpc-url $CHAIN_API_URL
echo "=== Step 4: Authorize for WalletRegistry (40,000 T) ==="
-cast_send_ok $TOKEN_STAKING "increaseAuthorization(address,address,uint96)" \
+_sp_cast_send_ok $TOKEN_STAKING "increaseAuthorization(address,address,uint96)" \
$SP $WALLET_REGISTRY $AMOUNT_40K \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+ --rpc-url $CHAIN_API_URL
echo "=== Step 5: Register operator in RandomBeacon (staking provider signs) ==="
-cast_send_ok $RANDOM_BEACON "registerOperator(address)" $OP \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+_sp_cast_send_ok $RANDOM_BEACON "registerOperator(address)" $OP \
+ --rpc-url $CHAIN_API_URL
echo "=== Step 6: Register operator in WalletRegistry (staking provider signs) ==="
-cast_send_ok $WALLET_REGISTRY "registerOperator(address)" $OP \
- --rpc-url $CHAIN_API_URL --private-key $SP_KEY
+_sp_cast_send_ok $WALLET_REGISTRY "registerOperator(address)" $OP \
+ --rpc-url $CHAIN_API_URL
echo "=== Step 7: Operator joins BeaconSortitionPool ==="
-cast_send_ok $RANDOM_BEACON "joinSortitionPool()" \
- --rpc-url $CHAIN_API_URL --private-key $OP_KEY
+_op_cast_send_ok $RANDOM_BEACON "joinSortitionPool()" \
+ --rpc-url $CHAIN_API_URL
echo "=== Step 8: Operator joins EcdsaSortitionPool ==="
-cast_send_ok $WALLET_REGISTRY "joinSortitionPool()" \
- --rpc-url $CHAIN_API_URL --private-key $OP_KEY
+_op_cast_send_ok $WALLET_REGISTRY "joinSortitionPool()" \
+ --rpc-url $CHAIN_API_URL
echo ""
echo "=== Done. Operator $OP is registered and in both sortition pools. ==="
From 2347611752c9eeabcf3b44cc654087cf73b57393 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 14 Apr 2026 08:56:46 +0000
Subject: [PATCH 3/5] fix(deploy): remove hardcoded proxy kind and deduplicate
fs import
- Drop kind: "transparent" from upgradeProxy options; let the OZ
plugin infer the proxy type from the deployed proxy admin slot.
Hardcoding the kind risks a mismatch if the original deploy
defaulted differently. Add a comment with the cast storage
command to verify proxy type on-chain.
- Replace two inline const fs = require("fs") declarations with a
single top-level import * as fs from "fs" to match TypeScript
conventions and avoid the duplicate binding.
---
deploy/54_upgrade_token_staking_extended.ts | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/deploy/54_upgrade_token_staking_extended.ts b/deploy/54_upgrade_token_staking_extended.ts
index a15e2386..fdae21b5 100644
--- a/deploy/54_upgrade_token_staking_extended.ts
+++ b/deploy/54_upgrade_token_staking_extended.ts
@@ -1,5 +1,6 @@
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { DeployFunction } from "hardhat-deploy/types"
+import * as fs from "fs"
import { ethers, upgrades } from "hardhat"
@@ -29,7 +30,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
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`
if (!fs.existsSync(deploymentPath)) {
log("TokenStaking not deployed, skipping upgrade")
@@ -47,12 +47,16 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
"ExtendedTokenStaking"
)
+ // 07_deploy_token_staking uses deployProxy without specifying kind;
+ // the OZ plugin defaults to transparent for contracts that lack upgradeTo().
+ // Verify on-chain with:
+ // cast storage 0xb53127684a568b3173ae13b9f8a6016e243e63b4 --rpc-url $RPC
+ // Non-zero = transparent proxy (ProxyAdmin slot); zero = UUPS.
const upgraded = await upgrades.upgradeProxy(
proxyAddress,
ExtendedTokenStaking,
{
constructorArgs: [T.address],
- kind: "transparent",
}
)
await upgraded.deployed()
@@ -66,7 +70,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
address: upgraded.address,
abi: JSON.parse(jsonAbi as string),
}
- const fs = require("fs")
const deploymentsDir = `deployments/${hre.network.name}`
fs.writeFileSync(
`${deploymentsDir}/TokenStaking.json`,
From 300b1adfcde5f4957dd88454323fbb977aadba9c Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 14 Apr 2026 09:02:41 +0000
Subject: [PATCH 4/5] fix(deployments): restore deleted mainnet
TokenStaking.json
The file was removed in the parent branch commit without explanation.
Downstream consumers relying on deployments/mainnet/TokenStaking.json
break silently without it. Restored from the last known-good version
(commit ab29e02).
---
deployments/mainnet/TokenStaking.json | 1353 +++++++++++++++++++++++++
1 file changed, 1353 insertions(+)
create mode 100644 deployments/mainnet/TokenStaking.json
diff --git a/deployments/mainnet/TokenStaking.json b/deployments/mainnet/TokenStaking.json
new file mode 100644
index 00000000..79c9dd16
--- /dev/null
+++ b/deployments/mainnet/TokenStaking.json
@@ -0,0 +1,1353 @@
+{
+ "address": "0x01B67b1194C75264d06F808A921228a95C765dd7",
+ "abi": [
+ {
+ "type": "constructor",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "_token"
+ },
+ {
+ "type": "address",
+ "name": "_nucypherVendingMachine"
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "ApplicationStatusChanged",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "uint8",
+ "name": "newStatus",
+ "indexed": true
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AuthorizationCeilingSet",
+ "inputs": [
+ {
+ "type": "uint256",
+ "name": "ceiling",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AuthorizationDecreaseApproved",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "fromAmount",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "toAmount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AuthorizationDecreaseRequested",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "fromAmount",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "toAmount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AuthorizationIncreased",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "fromAmount",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "toAmount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AuthorizationInvoluntaryDecreased",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "fromAmount",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "toAmount",
+ "indexed": false
+ },
+ {
+ "type": "bool",
+ "name": "successfulCall",
+ "indexed": true
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "AutoIncreaseToggled",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "bool",
+ "name": "autoIncrease",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "DelegateChanged",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "delegator",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "fromDelegate",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "toDelegate",
+ "indexed": true
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "DelegateVotesChanged",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "delegate",
+ "indexed": true
+ },
+ {
+ "type": "uint256",
+ "name": "previousBalance",
+ "indexed": false
+ },
+ {
+ "type": "uint256",
+ "name": "newBalance",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "GovernanceTransferred",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "oldGovernance",
+ "indexed": false
+ },
+ {
+ "type": "address",
+ "name": "newGovernance",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "MinimumStakeAmountSet",
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "NotificationRewardPushed",
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "reward",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "NotificationRewardSet",
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "reward",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "NotificationRewardWithdrawn",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "recipient",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "NotifierRewarded",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "notifier",
+ "indexed": true
+ },
+ {
+ "type": "uint256",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "PanicButtonSet",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "panicButton",
+ "indexed": true
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "SlashingProcessed",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "caller",
+ "indexed": true
+ },
+ {
+ "type": "uint256",
+ "name": "count",
+ "indexed": false
+ },
+ {
+ "type": "uint256",
+ "name": "tAmount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "Staked",
+ "inputs": [
+ {
+ "type": "uint8",
+ "name": "stakeType",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "owner",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "address",
+ "name": "beneficiary",
+ "indexed": false
+ },
+ {
+ "type": "address",
+ "name": "authorizer",
+ "indexed": false
+ },
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "TokensSeized",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ },
+ {
+ "type": "bool",
+ "name": "discrepancy",
+ "indexed": true
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "ToppedUp",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "anonymous": false,
+ "name": "Unstaked",
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider",
+ "indexed": true
+ },
+ {
+ "type": "uint96",
+ "name": "amount",
+ "indexed": false
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "applicationInfo",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint8",
+ "name": "status"
+ },
+ {
+ "type": "address",
+ "name": "panicButton"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "applications",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "address"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "approveApplication",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "approveAuthorizationDecrease",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "authorizationCeiling",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "authorizedStake",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "checkpoints",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "account"
+ },
+ {
+ "type": "uint32",
+ "name": "pos"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "tuple",
+ "name": "checkpoint",
+ "components": [
+ {
+ "type": "uint32",
+ "name": "fromBlock"
+ },
+ {
+ "type": "uint96",
+ "name": "votes"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "delegateVoting",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "delegatee"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "delegates",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "account"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "address"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "disableApplication",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "forceDecreaseAuthorization",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "forceUnstakeLegacy",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "forceUnstakeLegacy",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address[]",
+ "name": "_stakingProviders"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "getApplicationsLength",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getAutoIncreaseFlag",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "bool"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getAvailableToAuthorize",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96",
+ "name": "availableTValue"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getMinStaked",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "uint8",
+ "name": "stakeTypes"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getPastTotalSupply",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint256",
+ "name": "blockNumber"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getPastVotes",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "account"
+ },
+ {
+ "type": "uint256",
+ "name": "blockNumber"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getSlashingQueueLength",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getStartStakingTimestamp",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "getVotes",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "account"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "governance",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "address"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "increaseAuthorization",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "application"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "initialize",
+ "constant": false,
+ "payable": false,
+ "inputs": [],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "minTStakeAmount",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint96"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "notificationReward",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "notifiersTreasury",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "numCheckpoints",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "account"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint32"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "pauseApplication",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "processSlashing",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint256",
+ "name": "count"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "pushNotificationReward",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "reward"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "requestAuthorizationDecrease",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "application"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "requestAuthorizationDecrease",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "rolesOf",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "address",
+ "name": "owner"
+ },
+ {
+ "type": "address",
+ "name": "beneficiary"
+ },
+ {
+ "type": "address",
+ "name": "authorizer"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "seize",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "amount"
+ },
+ {
+ "type": "uint256",
+ "name": "rewardMultiplier"
+ },
+ {
+ "type": "address",
+ "name": "notifier"
+ },
+ {
+ "type": "address[]",
+ "name": "_stakingProviders"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "setAuthorizationCeiling",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint256",
+ "name": "ceiling"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "setMinimumStakeAmount",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "setNotificationReward",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "reward"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "setPanicButton",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "application"
+ },
+ {
+ "type": "address",
+ "name": "panicButton"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "slash",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint96",
+ "name": "amount"
+ },
+ {
+ "type": "address[]",
+ "name": "_stakingProviders"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "slashingQueue",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "slashingQueueIndex",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [],
+ "outputs": [
+ {
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "stake",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "address",
+ "name": "beneficiary"
+ },
+ {
+ "type": "address",
+ "name": "authorizer"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "stakedNu",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint256",
+ "name": "nuAmount"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "stakes",
+ "constant": true,
+ "stateMutability": "view",
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "uint96",
+ "name": "tStake"
+ },
+ {
+ "type": "uint96",
+ "name": "keepInTStake"
+ },
+ {
+ "type": "uint96",
+ "name": "nuInTStake"
+ }
+ ]
+ },
+ {
+ "type": "function",
+ "name": "toggleAutoAuthorizationIncrease",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "topUp",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "transferGovernance",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "newGuvnor"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "unstakeAll",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "unstakeKeep",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "unstakeNu",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "unstakeT",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "stakingProvider"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ },
+ {
+ "type": "function",
+ "name": "withdrawNotificationReward",
+ "constant": false,
+ "payable": false,
+ "inputs": [
+ {
+ "type": "address",
+ "name": "recipient"
+ },
+ {
+ "type": "uint96",
+ "name": "amount"
+ }
+ ],
+ "outputs": []
+ }
+ ]
+}
\ No newline at end of file
From ea8ae812c880334aee5a7d959ae58454397d2853 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 14 Apr 2026 09:02:56 +0000
Subject: [PATCH 5/5] fix(TokenStaking): add zero-address guard to
increaseAuthorization
approveApplication already checks application != address(0) but
increaseAuthorization did not. The APPROVED status check provides
a functional backstop, but adding the explicit guard makes the
invariant consistent across both entry points.
---
contracts/staking/TokenStaking.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/staking/TokenStaking.sol b/contracts/staking/TokenStaking.sol
index 93464b28..d544affc 100644
--- a/contracts/staking/TokenStaking.sol
+++ b/contracts/staking/TokenStaking.sol
@@ -300,6 +300,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
address application,
uint96 amount
) external virtual override onlyAuthorizerOf(stakingProvider) {
+ require(application != address(0), "Parameters must be specified");
require(amount > 0, "Parameters must be specified");
ApplicationInfo storage applicationStruct = applicationInfo[
application