tip: 467
title: Stake 2.0 - A new TRON staking model
author: lxcmyf@gmail.com
discussions-to: https://github.com/tronprotocol/tips/issues/467
status: Final
type: Standards Track
category: Core
created: 2022-09-28
This TIP aims to establish a more flexible stake model, called Stake 2.0, Stake 2.0 can improve the utilization of network resources, and at the same time enhance the stability of the TRON stake system.
At present, by staking TRX on the TRON network, you can obtain voting rights called TRON Power, as well as bandwidth or energy as resources. The users need to choose whether to obtain whether energy or bandwidth during the stake operation. The obtained resources can also be delegated to other addresses only if they are specified when staking. Once the staking is completed, it is not allowed to unstake within 3 days. Then, the users can perform the unstake operation and immediately obtain the staked TRX.
The delegating of resources under the current stake mechanism is hardly flexible, resulting in low resource utilization and complicated user operations. The stake and delegating operation are bound together at present. If you want to alter your resource delegating object, you need to perform unstaking first, then make another staking and designate a new delegating object. Additionally, the unstake operation must wait for 3 days after staking, which means that the resources obtained cannot be re-delegated to others within 3 days. If the user has already voted before unstaking, the unstake operation will also result in the automatic cancellation of the vote. Hence, all these rules greatly reduce the efficiency of resource allocation and incur extremely complicated user operations to stake and delegate at will.
Since the TVM does not support stake, vote, or delegate commands, the developers are not able to implement related operations in a smart contract, which limits the applications involved staking and delegating, like tokenization and decentralized resource exchange. In addition, the principal will arrive immediately after the stake is released, when the TRX market fluctuates violently, a large number of staking or unstaking operations will be triggered, which will also have a certain impact on the overall stake model.
Therefore, we need a new stake mechanism to solve these problems, separate low-frequency staking operations and high-frequency resource delegating operations, support resource re-delegating without unstaking, and improve resource utilization. Add stake, delegate, vote, and related commands should be added in the TVM, meanwhile, the new mechanism should implement the delayed arrival of unstaking TRX, improve the stability of the stake model under extreme market conditions, and help network participants to form more stable expectations for the total circulation of the entire network.
Based on the description above, a newly designed staking model is shown as below, seven new APIs should be added to the java-tron client:
/wallet/freezebalancev2
/wallet/unfreezebalancev2
/wallet/delegateresource
/wallet/undelegateresource
/wallet/withdrawexpireunfreeze
/wallet/getavailableunfreezecount
/wallet/getcanwithdrawunfreezeamount
/wallet/getcandelegatedmaxsize
/wallet/getdelegatedresourcev2
/wallet/getdelegatedresourceaccountindexv2
and six new opcodes should be added to TVM:
FREEZEBALANCEV2
, UNFREEZEBALANCEV2
, DELEGATERESOURCE
, UNDELEGATERESOURCE
, CANCELALLUNFREEZEV2
, WITHDRAWEXPIREUNFREEZE
The mechanism of Stake 2.0 is shown as follows:
- User calls
/wallet/freezebalancev2
to stake TRX and obtain resources, specifing the type of resource. The corresponding resource goes to the owner address that has staked TRX. - After staking and obtaining resources, the owner is able to call
/wallet/delegateresource
to delegate resources to multiple recipients respectively. The delegating time locker is an optional, if selected, the delegated resources cannot be undelegated within 3 days, if not selected, there will not be lock-up period. When the owner repeat the delegating operation with time lock of same amount to the same receiver address, the lock-up period will be reset to 3 days. - The owner call
/wallet/undelegateresource
to cancel the resource delegating for a recipient, supporting partial cancellation by amount. - Call
/wallet/unfreezebalancev2
to unstake TRX, partial unstaking is supported, but only for the available part, TRX related to the delegated resources are considered locked and not available, therefore, cannot be unstaked. - After the owner performs the unstaking operation, he needs to wait
N
days, then call/wallet/withdrawexpireunfreeze
to get back TRX,N
is a TRON network parameter.
If the recipient has consumed a part of the resource, after undelegating, this part of resources will still be not available status in the owner account, and needs some time to recover. The owner can only delegate the available resources to others.
Example:
- Assuming that A has 1000 units of energy, delegate all of them to B. After B used 400 units, A called
/wallet/undelegateresource
to undelegate all the energy left. Now, A has a balance of 600 units of energy in the account, 400 units are in recovering.
TRX partial unstaking is supported, but a maximum of 32 partial unstaking operations is allowed simultaneously, in other words, there should be no more than 32 delay arrivals of the unstaking assets in progress at the same time.
Example,
- Assuming N is 3, and the user staked 50 TRX. On day 1, the user unstakes 1 TRX, he needs to wait for 3 days, that is, on day 4, the user will be able to get back 1 TRX. Then on day 2, the user unstakes another 1 TRX, he would be able to get it back on day 5. Each unstaking operation needs to wait for 3 days before redeeming. There could be at most 32 unstaking operations in processing at the same time in the new model.
Unstaking will first reclaim the idle TRON Power, then the TRON Power that has been used. For the reclaiming of TRON Power that has been used, if vote for multiple SRs, it will revoke votes in proportion.
Example:
-
Suppose that a user stakes 2000 TRX and gets 2000 TRON Power, 1,000 TRON Power is idle and 1,000 TRON Power votes for 5 SRs. Each SR gets 200 votes. Now the user unstake 1500 TRX, which means 1500 TRON Power needs to be reclaimed, in this case, the 1000 idle TRON Power will be reclaimed first, then revoke 100 votes from each SR.
-
Revoked votes from SR(A) = (Total revoked votes from all SRs/ Total previous votes to all SRs) * Total previous votes to SR(A)
/wallet/freezebalancev2
Description: stake TRX to obtain TRON Power (voting rights) and bandwidth or energy.
Params:
owner_address
- Address of transaction initiator, data type is stringfrozen_balance
- Amount of TRX to be staked, unit is sun, data type is uint256resource
- Resource type, "BANDWIDTH" or "ENERGY", data type is stringvisible
- Whether the address is in Base58 format, data type is bool
Returns - unsigned transaction, data type is json string
Example:
curl -X POST http://127.0.0.1:8092/wallet/freezebalancev2 -d \
'{"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"frozen_balance":1000000,
"resource":"BANDWIDTH",
"visible":true
}'
/wallet/unfreezebalancev2
Description: Unstake TRX to release bandwidth and energy and at the same time TRON Power will be reduced and all corresponding votes will be canceled.
Params:
owner_address
- Address of transaction initiator, data type is stringunfreeze_balance
- Amount of TRX to be unstaked, unit is sun, data type is uint256resource
- Resource type, "BANDWIDTH" or "ENERGY", data type is stringvisible
- Whether the address is in base58 format, data type is bool
Returns - unsigned transaction, data type is json string
Example:
curl -X POST http://127.0.0.1:8092/wallet/unfreezebalancev2 -d \
'{"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"unfreeze_balance":1000000,
"resource":"BANDWIDTH",
"visible":true
}'
/wallet/delegateresource
Description: delegate resource
Params:
owner_address
- Address of transaction initiator, data type is stringreceiver_address
- Receiver address of resource to be delegated tobalance
- Amount of TRX staked for resource to be delegated, unit is sun, data type is unit256resource
- Resource type, "BANDWIDTH" or "ENERGY", data type is stringlock
- Whether it is locked, if it is set totrue
, the delegated resources cannot be undelegated within 3 days.When the lock time is not over, if the owner delegates the same resources using the lock to the same address, the lock time will be reset to 3 daysvisible
- Whether the address is in base58 format, data type is bool
Returns - unsigned transaction, data type is json string
Example:
curl -X POST http://127.0.0.1:8092/wallet/delegateresource -d \
'{"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"receiver_address":"TQ4gjjpAjLNnE67UFbmK5wVt5fzLfyEVs3",
"balance":1000000,
"resource":"BANDWIDTH",
"lock": true,
"visible":true
}'
/wallet/undelegateresource
Description: undelegate API
Params:
owner_address
- Address of transaction initiator, data type is stringreceiver_address
- Receiver address of resource to be delegated tobalance
- Amount of TRX staked for resource to be undelegated, unit is sun, data type is unit256resource
- Resource type, "BANDWIDTH" or "ENERGY", data type is stringvisible
- Whether the address is in base58 format, data type is bool
Returns - unsigned transaction, data type is json string
Example:
curl -X POST http://127.0.0.1:8092/wallet/undelegateresource -d \
'{"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"receiver_address":"TQ4gjjpAjLNnE67UFbmK5wVt5fzLfyEVs3",
"balance":1000000,
"resource":"BANDWIDTH",
"visible":true
}'
/wallet/withdrawexpireunfreeze
Description: withdraw unfrozen balance API
Params:
owner_address
- Address of transaction initiator, data type is stringvisible
- Whether the address is in base58 format, data type is bool
Returns - unsigned transaction, data type is json string
Example:
curl -X POST http://127.0.0.1:8092/wallet/withdrawexpireunfreeze -d \
'{
"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"visible":true
}'
/wallet/getavailableunfreezecount
Description: remaining times of available unstaking API
Params:
owner_address
- account address, data type is string.visible
- whether the address is in base58 format, data type is bool
Returns - Remaining times of available unstaking, data type is number.
Example:
curl -X POST http://127.0.0.1:8092/wallet/getavailableunfreezecount -d \
'{
"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"visible":true
}'
/wallet/getcanwithdrawunfreezeamount
Description: query the withdrawable balance at the specified timestamp
Params:
owner_address
- account address, data type is string.timestamp
- query cutoff timestamp, in millisecond.visible
- Whether the address is in Base58 format, data type is bool
Returns - withdrawable balance,unit is sun.
Example:
curl -X POST http://127.0.0.1:8092/wallet/getcanwithdrawunfreezeamount -d \
'{
"owner_address":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"timestamp": 1667977444000
"visible":true
}'
/wallet/getcandelegatedmaxsize Description: query the amount of delegatable resources of the specified resource Type for target address, unit is sun.
Params:
owner_address
- account address, data type is string.type
- resource type,data type is number, 0 is bandwidth, 1 is energyvisible
- whether the address is in base58 format, data type is bool
Returns - the amount of delegatable resource, unit is sun.
Note: The interface indicates the return of the maximum amount of delegated resources for normal transactions, but there are some special transactions in actual situations, such as multi-signature, memo and other transactions that will deduct a part of the resource usage. At this time, the maximum value of specific resources that can be used may be smaller than the recommended value returned through this interface.
Example:
curl -X POST http://127.0.0.1:8092/wallet/getcandelegatedmaxsize -d \
'{
"owner_address": "TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"type": 1,
"visible": true
}'
/wallet/getdelegatedresourcev2
Description: query the amount of resources detail delegated by fromAddress
to toAddress
Params:
fromAddress
- resource from address, data type is string.toAddress
- resource to address, data type is string.visible
- whether the address is in base58 format, data type is bool
Returns - Resource delegation information
Example:
curl -X POST http://127.0.0.1:8092/wallet/getdelegatedresourcev2 -d \
'{
"fromAddress": "TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"toAddress": "TQ4gjjpAjLNnE67UFbmK5wVt5fzLfyEVs3",
"visible": true
}'
/wallet/getdelegatedresourceaccountindexv2
Description: query the resource delegation index by an account
Params:
value
- account ddress, data type is string.visible
- whether the address is in base58 format, data type is bool
Returns - Resource delegation index
Example:
curl -X POST http://127.0.0.1:8092/wallet/getdelegatedresourceaccountindexv2 -d \
'{
"value":"TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh",
"visible": true
}'
The FREEZEBALANCEV2
takes 2 operands pop up from stack:
amount
: amount to freeze in SUN.resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
If operation succeed, push 1 to stack, otherwise push 0 to stack.
The UNFREEZEBALANCEV2
takes 2 operands pop up from stack.
amount
: amount to unfreeze in SUN.resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
If operation succeed, push 1 to stack, otherwise push 0 to stack.
The CANCELALLUNFREEZEV2
takes no operand pop up from stack.
If operation succeed, push 1 to stack, otherwise push 0 to stack.
The WITHDRAWEXPIREUNFREEZE
takes no operand pop up from stack.
If operation succeed, push the actual withdrawal amount to stack, otherwise push 0 to stack.
The DELEGATERESOURCE
takes 3 operands pop up from stack:
amount
: resource amount to delegate in SUN.resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.receiver
: account address to receive the resource.
If operation succeed, push 1 to stack, otherwise push 0 to stack.
The UNDELEGATERESOURCE
takes 3 operands pop up from stack:
amount
: resource amount to undelegate in SUN.resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.receiver
: account address to return the resource.
If operation succeed, push 1 to stack, otherwise push 0 to stack.
The GetChainParameter
takes 1 parameters:
index
: index of the parameter to be queried.
Query the specific chain parameters, and push the query result to stack. The solidity syntax is shown below.
chain.totalNetLimit
chain.totalNetWeight
chain.totalEnergyCurrentLimit
chain.totalEnergyWeight
chain.unfreezeDelayDays
The AvailableUnfreezeV2Size
takes 1 parameters:
target
: target account address.
Query the size of the available unfreeze queue for target
address, and push the query result to stack.
The UnfreezableBalanceV2
takes 2 parameters:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the unfreezable balance of a specified resourceType
for target
address, and push the query result to stack.
The ExpireUnfreezeBalanceV2
takes 2 parameters:
target
: target account address.
timestamp
: query cutoff timestamp.
Query the withdrawal balance at the specified timestamp
for target
address, and push the query result to stack.
The DelegatableResource
takes 2 parameters:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the amount of delegatable resources(unit: SUN) of the specified resourceType
for target
address, and push the query result to stack.
The ResourceV2
takes 3 parameters:
target
: target account address.
from
: from account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the amount of resources(unit: SUN) of a specific resourceType
delegated by from
address to target
address
The CheckUnDelegateResource
takes 3 parameters:
target
: target account address.
amount
: resource amount to undelegate in SUN.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Check whether the contract can recycle the specified amount
of resources of a specific resourceType
that have been delegated to target
address, and push the amount of clean resource(unit: SUN), the amount of dirty resource(unit: SUN) and the restore time to stack.
The ResourceUsage
takes 2 parametes:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the usage of a specific resourceType
of resources for target
address, and push the amount of usage(unit: SUN) and the restore time to stack.
The TotalResource
takes 2 parameters:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the total amount of available resources(unit: SUN) of a specific resourceType
for target
address, and push the query reslut to stack.
The TotalDelegatedResource
takes 2 parameters:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the amount of delegated resources of a specific resourceType
for target
address, and push the query reslut to stack.
The TotalAcquiredResource
takes 2 parameters:
target
: target account address.
resourceType
: 0 as bandwidth, 1 as energy, 2 as tron power.
Query the amount of acquired resources(unit: SUN) of a specific resourceType
for tareget
address, and push the query reslut to stack.
Here is an example showing how to call the above TVM instructions in solidity:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract FreezeV2Example {
event BalanceFreezedV2(uint, uint);
event BalanceUnfreezedV2(uint, uint);
event AllUnFreezeV2Canceled();
event ExpireUnfreezeWithdrew(uint);
event ResourceDelegated(uint, uint, address);
event ResourceUnDelegated(uint, uint, address);
constructor() payable {}
fallback() payable external {}
receive() payable external {}
function freezeBalanceV2(uint amount, uint resourceType) external {
freezebalancev2(amount, resourceType);
emit BalanceFreezedV2(amount, resourceType);
}
function unfreezeBalanceV2(uint amount, uint resourceType) external {
unfreezebalancev2(amount, resourceType);
emit BalanceUnfreezedV2(amount, resourceType);
}
function cancelAllUnfreezeV2() external {
cancelallunfreezev2();
emit AllUnFreezeV2Canceled();
}
function withdrawExpireUnfreeze() external returns(uint amount) {
amount = withdrawexpireunfreeze();
emit ExpireUnfreezeWithdrew(amount);
}
function delegateResource(uint amount, uint resourceType, address payable receiver) external {
receiver.delegateResource(amount, resourceType);
emit ResourceDelegated(amount, resourceType, receiver);
}
function unDelegateResource(uint amount, uint resourceType, address payable receiver) external {
receiver.unDelegateResource(amount, resourceType);
emit ResourceUnDelegated(amount, resourceType, receiver);
}
function getChainParameters() view public returns(uint, uint, uint, uint, uint) {
return (chain.totalNetLimit, chain.totalNetWeight,
chain.totalEnergyCurrentLimit, chain.totalEnergyWeight,
chain.unfreezeDelayDays);
}
function getAvailableUnfreezeV2Size(address target) view public returns(uint) {
return target.availableUnfreezeV2Size();
}
function getUnfreezableBalanceV2(address target, uint resourceType) view public returns(uint) {
return target.unfreezableBalanceV2(resourceType);
}
function getExpireUnfreezeBalanceV2(address target, uint timestamp) view public returns(uint) {
return target.expireUnfreezeBalanceV2(timestamp);
}
function getDelegatableResource(address target, uint resourceType) view public returns(uint, uint) {
return (target.delegatableResource(resourceType), block.number);
}
function getResourceV2(address target, address from, uint resourceType) view public returns(uint) {
return target.resourceV2(from, resourceType);
}
function checkUnDelegateResource(address target, uint amount, uint resourceType) view public returns(uint, uint, uint, uint) {
(uint clean, uint dirty, uint restoreTime) = target.checkUnDelegateResource(amount, resourceType);
return (clean, dirty, restoreTime, block.number);
}
function getResourceUsage(address target, uint resourceType) view public returns(uint, uint, uint) {
(uint dirty, uint restoreTime) = target.resourceUsage(resourceType);
return (dirty, restoreTime, block.number);
}
function getTotalResource(address target, uint resourceType) view public returns(uint) {
return target.totalResource(resourceType);
}
function getTotalDelegatedResource(address from, uint resourceType) view public returns(uint) {
return from.totalDelegatedResource(resourceType);
}
function getTotalAcquiredResource(address target, uint resourceType) view public returns(uint) {
return target.totalAcquiredResource(resourceType);
}
function killme(address payable target) external {
selfdestruct(target);
}
}
After the proposal of the new mechanism is opened, it will not affect the previously frozen TRX. For those TRX frozen through the old mechanism, it can be redeemed using the old unstake interface. If needed, the only way to stake in the future is through the new mechanism.