wallet/unfreezebalancev2
Unstake (unfreeze) TRX to reclaim bandwidth or energy resources using TRON's Stake 2.0 mechanism.
Endpoint
POST /wallet/unfreezebalancev2
Parameters
Required Parameters
Parameter | Type | Description |
---|---|---|
owner_address | string | Account address initiating unstaking (base58) |
resource | string | Resource type: "BANDWIDTH" or "ENERGY" |
unfreeze_balance | number | Amount to unstake in SUN (1 TRX = 1,000,000 SUN) |
Optional Parameters
Parameter | Type | Description |
---|---|---|
receiver_address | string | Address that received the delegation (if unstaking delegated resources) |
permission_id | number | Permission ID for multi-signature (default: 0) |
visible | boolean | Return base58 addresses (default: false returns hex) |
Response
Returns unsigned transaction containing the unstaking operation. Transaction must be signed and broadcast.
Response Fields
txID
- Transaction hashraw_data
- Transaction raw datacontract
- Unstaking contract detailsexpiration
- Transaction expiration timestamptimestamp
- Transaction creation timestamp
visible
- Address format indicator
Important Notes
Unstaking Process
- 14-Day Waiting Period: After unstaking, TRX enters a 14-day withdrawal period
- No Resources During Wait: Resources are lost immediately, TRX remains locked
- Manual Withdrawal: After 14 days, must call
withdrawexpireunfreeze
to claim TRX - Partial Unstaking: Can unstake portions of staked amount
Resource Implications
- Bandwidth/Energy is removed immediately upon unstaking
- Cannot re-stake the same TRX during withdrawal period
- Consider keeping minimum stake for basic operations
Implementation Examples
- JavaScript
- Python
- cURL
class TronUnstakingManager {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = `https://api-tron-mainnet.n.dwellir.com/${apiKey}`;
}
// Create unstaking transaction
async createUnstakeTransaction(ownerAddress, resource, amountTRX) {
const response = await fetch(`${this.baseUrl}/wallet/unfreezebalancev2`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
owner_address: ownerAddress,
resource: resource,
unfreeze_balance: amountTRX * 1000000, // Convert TRX to SUN
visible: true
})
});
if (!response.ok) {
throw new Error(`Unstaking failed: ${response.statusText}`);
}
return response.json();
}
// Check pending withdrawals
async getPendingWithdrawals(address) {
const response = await fetch(`${this.baseUrl}/wallet/getaccount`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: address,
visible: true
})
});
const account = await response.json();
// Parse unfreezing information
const unfreezing = account.unfrozenV2 || [];
return unfreezing.map(item => ({
type: item.type,
amount: item.unfreeze_amount / 1000000, // Convert to TRX
expireTime: new Date(item.unfreeze_expire_time),
canWithdraw: Date.now() > item.unfreeze_expire_time
}));
}
// Calculate optimal unstaking amount
async calculateOptimalUnstake(address, targetResourceReduction) {
const response = await fetch(`${this.baseUrl}/wallet/getaccountresource`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: address,
visible: true
})
});
const resources = await response.json();
// Calculate based on current resource usage
const energyStaked = resources.EnergyLimit || 0;
const bandwidthStaked = resources.NetLimit || 0;
return {
energy: {
current: energyStaked,
toUnstake: Math.floor(energyStaked * targetResourceReduction),
remaining: energyStaked * (1 - targetResourceReduction)
},
bandwidth: {
current: bandwidthStaked,
toUnstake: Math.floor(bandwidthStaked * targetResourceReduction),
remaining: bandwidthStaked * (1 - targetResourceReduction)
}
};
}
// Withdraw expired unfrozen balance
async withdrawExpiredBalance(ownerAddress) {
const response = await fetch(`${this.baseUrl}/wallet/withdrawexpireunfreeze`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
owner_address: ownerAddress,
visible: true
})
});
if (!response.ok) {
throw new Error(`Withdrawal failed: ${response.statusText}`);
}
return response.json();
}
}
// Example usage
async function manageUnstaking() {
const manager = new TronUnstakingManager('YOUR_API_KEY');
try {
// Check pending withdrawals first
const pending = await manager.getPendingWithdrawals('TYourAddress...');
console.log('Pending withdrawals:', pending);
// Withdraw any expired funds
const withdrawable = pending.filter(p => p.canWithdraw);
if (withdrawable.length > 0) {
const withdrawal = await manager.withdrawExpiredBalance('TYourAddress...');
console.log('Withdrawal transaction:', withdrawal);
}
// Calculate optimal unstaking (reduce resources by 30%)
const optimal = await manager.calculateOptimalUnstake('TYourAddress...', 0.3);
console.log('Optimal unstaking amounts:', optimal);
// Create unstaking transaction for energy
const unstakeTx = await manager.createUnstakeTransaction(
'TYourAddress...',
'ENERGY',
optimal.energy.toUnstake / 28.5 // Convert energy to TRX (approximate ratio)
);
console.log('Unstaking transaction created:', unstakeTx.txID);
console.log('Sign and broadcast this transaction to complete unstaking');
} catch (error) {
console.error('Unstaking error:', error);
}
}
import requests
import json
from datetime import datetime, timedelta
from typing import Dict, List, Optional
class TronUnstakingManager:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = f"https://api-tron-mainnet.n.dwellir.com/{api_key}"
def create_unstake_transaction(
self,
owner_address: str,
resource: str,
amount_trx: float
) -> Dict:
"""Create an unstaking transaction"""
response = requests.post(
f"{self.base_url}/wallet/unfreezebalancev2",
json={
"owner_address": owner_address,
"resource": resource,
"unfreeze_balance": int(amount_trx * 1_000_000), # Convert to SUN
"visible": True
}
)
response.raise_for_status()
return response.json()
def get_pending_withdrawals(self, address: str) -> List[Dict]:
"""Check pending withdrawal amounts and times"""
response = requests.post(
f"{self.base_url}/wallet/getaccount",
json={
"address": address,
"visible": True
}
)
response.raise_for_status()
account = response.json()
unfreezing = account.get('unfrozenV2', [])
withdrawals = []
for item in unfreezing:
expire_time = datetime.fromtimestamp(item['unfreeze_expire_time'] / 1000)
withdrawals.append({
'type': item.get('type', 'UNKNOWN'),
'amount_trx': item['unfreeze_amount'] / 1_000_000,
'expire_time': expire_time,
'can_withdraw': datetime.now() > expire_time,
'days_remaining': max(0, (expire_time - datetime.now()).days)
})
return withdrawals
def calculate_resource_impact(
self,
address: str,
unstake_amount: float,
resource_type: str
) -> Dict:
"""Calculate the impact of unstaking on available resources"""
# Get current resources
response = requests.post(
f"{self.base_url}/wallet/getaccountresource",
json={
"address": address,
"visible": True
}
)
response.raise_for_status()
resources = response.json()
if resource_type == "ENERGY":
current = resources.get('EnergyLimit', 0)
ratio = 28.5 # Approximate TRX to energy ratio
reduction = unstake_amount * ratio
return {
'resource_type': 'ENERGY',
'current_amount': current,
'reduction': reduction,
'remaining': max(0, current - reduction),
'percentage_reduction': (reduction / current * 100) if current > 0 else 0
}
elif resource_type == "BANDWIDTH":
current = resources.get('NetLimit', 0)
ratio = 1 # 1 TRX = 1 bandwidth
reduction = unstake_amount * ratio
return {
'resource_type': 'BANDWIDTH',
'current_amount': current,
'reduction': reduction,
'remaining': max(0, current - reduction),
'percentage_reduction': (reduction / current * 100) if current > 0 else 0
}
def batch_unstake(
self,
owner_address: str,
unstake_plan: Dict[str, float]
) -> List[Dict]:
"""Create multiple unstaking transactions"""
transactions = []
for resource, amount in unstake_plan.items():
if amount > 0:
tx = self.create_unstake_transaction(
owner_address,
resource,
amount
)
transactions.append({
'resource': resource,
'amount': amount,
'txID': tx['txID'],
'transaction': tx
})
return transactions
def withdraw_expired_balance(self, owner_address: str) -> Optional[Dict]:
"""Withdraw expired unfrozen balance"""
# Check if there's anything to withdraw
withdrawals = self.get_pending_withdrawals(owner_address)
withdrawable = [w for w in withdrawals if w['can_withdraw']]
if not withdrawable:
return None
response = requests.post(
f"{self.base_url}/wallet/withdrawexpireunfreeze",
json={
"owner_address": owner_address,
"visible": True
}
)
response.raise_for_status()
result = response.json()
result['withdrawn_amount'] = sum(w['amount_trx'] for w in withdrawable)
return result
# Example usage with monitoring
def monitor_unstaking_lifecycle():
manager = TronUnstakingManager('YOUR_API_KEY')
address = 'TYourAddress...'
# 1. Check current unstaking status
print("=== Unstaking Status ===")
withdrawals = manager.get_pending_withdrawals(address)
for w in withdrawals:
status = "READY TO WITHDRAW" if w['can_withdraw'] else f"{w['days_remaining']} days remaining"
print(f"{w['type']}: {w['amount_trx']:.2f} TRX - {status}")
# 2. Withdraw any expired amounts
if any(w['can_withdraw'] for w in withdrawals):
print("\n=== Processing Withdrawals ===")
withdrawal_tx = manager.withdraw_expired_balance(address)
if withdrawal_tx:
print(f"Withdrawal TX: {withdrawal_tx['txID']}")
print(f"Amount: {withdrawal_tx['withdrawn_amount']:.2f} TRX")
# 3. Calculate impact before unstaking
print("\n=== Unstaking Impact Analysis ===")
energy_impact = manager.calculate_resource_impact(address, 1000, "ENERGY")
bandwidth_impact = manager.calculate_resource_impact(address, 500, "BANDWIDTH")
print(f"Energy Impact: {energy_impact['percentage_reduction']:.1f}% reduction")
print(f"Bandwidth Impact: {bandwidth_impact['percentage_reduction']:.1f}% reduction")
# 4. Create unstaking plan
unstake_plan = {
"ENERGY": 1000, # Unstake 1000 TRX from energy
"BANDWIDTH": 500 # Unstake 500 TRX from bandwidth
}
print("\n=== Creating Unstaking Transactions ===")
transactions = manager.batch_unstake(address, unstake_plan)
for tx in transactions:
print(f"{tx['resource']}: Unstaking {tx['amount']} TRX")
print(f"Transaction ID: {tx['txID']}")
print("Note: Transaction must be signed and broadcast")
if __name__ == "__main__":
monitor_unstaking_lifecycle()
# Basic unstaking request for energy
curl -X POST https://api-tron-mainnet.n.dwellir.com/YOUR_API_KEY/wallet/unfreezebalancev2 \
-H "Content-Type: application/json" \
-d '{
"owner_address": "TYourAddress...",
"resource": "ENERGY",
"unfreeze_balance": 1000000000,
"visible": true
}'
# Unstake bandwidth
curl -X POST https://api-tron-mainnet.n.dwellir.com/YOUR_API_KEY/wallet/unfreezebalancev2 \
-H "Content-Type: application/json" \
-d '{
"owner_address": "TYourAddress...",
"resource": "BANDWIDTH",
"unfreeze_balance": 500000000,
"visible": true
}'
# Unstake delegated resources
curl -X POST https://api-tron-mainnet.n.dwellir.com/YOUR_API_KEY/wallet/unfreezebalancev2 \
-H "Content-Type: application/json" \
-d '{
"owner_address": "TYourAddress...",
"resource": "ENERGY",
"unfreeze_balance": 2000000000,
"receiver_address": "TReceiverAddress...",
"visible": true
}'
# Check pending withdrawals (via getaccount)
curl -X POST https://api-tron-mainnet.n.dwellir.com/YOUR_API_KEY/wallet/getaccount \
-H "Content-Type: application/json" \
-d '{
"address": "TYourAddress...",
"visible": true
}'
# Withdraw expired unfrozen balance
curl -X POST https://api-tron-mainnet.n.dwellir.com/YOUR_API_KEY/wallet/withdrawexpireunfreeze \
-H "Content-Type: application/json" \
-d '{
"owner_address": "TYourAddress...",
"visible": true
}'
Example Response
{
"visible": true,
"txID": "8f3c4f9e2b8a7d6c5e4f3b2a1d9c8e7f6b5a4d3c2e1f9a8b7c6d5e4f3a2b1c9d",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"owner_address": "TYourAddress...",
"unfreeze_balance": 1000000000,
"resource": "ENERGY"
},
"type_url": "type.googleapis.com/protocol.UnfreezeBalanceV2Contract"
},
"type": "UnfreezeBalanceV2Contract"
}
],
"ref_block_bytes": "5a4b",
"ref_block_hash": "3c2d4f9e8b7a6d5c",
"expiration": 1702456849000,
"timestamp": 1702456789000
}
}
Unstaking Timeline
graph LR
A[Staked TRX] --> B[Initiate Unstaking]
B --> C[14-Day Waiting Period]
C --> D[Withdrawal Available]
D --> E[Execute Withdrawal]
E --> F[TRX in Wallet]
style A fill:#4CAF50
style C fill:#FF9800
style F fill:#2196F3
Best Practices
1. Plan Unstaking Carefully
- Resources are lost immediately
- Cannot re-stake during withdrawal period
- Keep minimum stake for basic operations
2. Monitor Withdrawal Periods
- Track 14-day waiting periods
- Set reminders for withdrawal dates
- Batch withdrawals for efficiency
3. Resource Management
// Keep minimum operational resources
const MIN_ENERGY_STAKE = 5000; // TRX
const MIN_BANDWIDTH_STAKE = 1000; // TRX
function calculateSafeUnstake(current, minimum) {
return Math.max(0, current - minimum);
}
4. Error Handling
- Check account has staked balance
- Verify resource type is correct
- Handle insufficient stake errors
Common Errors
Error | Description | Solution |
---|---|---|
No frozen balance | Account has no staked TRX | Check staking status first |
Invalid resource type | Resource must be BANDWIDTH or ENERGY | Use correct resource string |
Insufficient frozen balance | Trying to unstake more than available | Check current stake amount |
Invalid address | Malformed TRON address | Verify base58 format |
Use Cases
- Resource Optimization: Unstake excess resources not being used
- Liquidity Management: Free up TRX for trading or transfers
- Delegation Cleanup: Reclaim delegated resources from inactive accounts
- Portfolio Rebalancing: Adjust resource allocation between bandwidth/energy
- Emergency Liquidity: Access staked TRX when needed (with 14-day delay)
Related Methods
- wallet/freezebalancev2 - Stake TRX for resources
- wallet/getaccountresource - Check resource status
- wallet/delegateresource - Delegate resources to others