I’d like to re-visit this and thank the earlier responses. I wasn’t thinking of utility beyond a standard for setting a token type as staked. But I can see now that utility is what the original was missing.
I’d like to propose a design pattern that enables configurable access to staked tokens. The core concept is simple: a contract holds staked tokens, and NFT ownership represents the right to designate who can access or utilize those tokens under configurable conditions.
// Simplified example of the core pattern
contract StakedTokenAccessNFT is ERC721 {
address public owner;
// NFT ID -> accessor address -> permission
mapping(uint256 => mapping(address => bool)) public tokenAccessors;
// NFT ID -> max number of accessors
mapping(uint256 => uint256) public accessorLimits;
// NFT ID -> current number of accessors
mapping(uint256 => uint256) public currentAccessorCounts;
// Staking related state variables
mapping(uint256 => uint256) public stakedAmounts; // NFT ID -> amount of tokens staked
IERC20 public stakingToken;
constructor(string memory name, string memory symbol, address _stakingToken) ERC721(name, symbol) {
owner = msg.sender;
stakingToken = IERC20(_stakingToken);
}
// Mint NFT and stake tokens in one transaction
function mintWithStake(address to, uint256 tokenId, uint256 stakeAmount, uint256 _accessorLimit) external {
require(stakingToken.transferFrom(msg.sender, address(this), stakeAmount), "Stake transfer failed");
_mint(to, tokenId);
stakedAmounts[tokenId] = stakeAmount;
accessorLimits[tokenId] = _accessorLimit;
}
// NFT owner can manage accessors
function addAccessor(uint256 tokenId, address accessor) external {
require(ownerOf(tokenId) == msg.sender, "Not the NFT owner");
require(currentAccessorCounts[tokenId] < accessorLimits[tokenId], "Accessor limit reached");
require(!tokenAccessors[tokenId][accessor], "Already an accessor");
tokenAccessors[tokenId][accessor] = true;
currentAccessorCounts[tokenId]++;
}
function removeAccessor(uint256 tokenId, address accessor) external {
require(ownerOf(tokenId) == msg.sender, "Not the NFT owner");
require(tokenAccessors[tokenId][accessor], "Not an accessor");
tokenAccessors[tokenId][accessor] = false;
currentAccessorCounts[tokenId]--;
}
// Accessor can use the staked tokens as defined by implementation
function useStakedTokens(uint256 tokenId, uint256 amount, address recipient, bytes calldata data) external {
require(tokenAccessors[tokenId][msg.sender], "Not an authorized accessor");
require(amount <= stakedAmounts[tokenId], "Insufficient staked amount");
// Implementation would depend on specific use case
// Examples:
// - Transfer a portion of staked tokens to recipient
// - Use tokens as collateral for a loan
// - Execute a custom action defined by the data parameter
// This is where application-specific logic would be implemented
_executeTokenUtilization(tokenId, amount, recipient, data);
}
// Private function that defines how tokens can actually be used
function _executeTokenUtilization(uint256 tokenId, uint256 amount, address recipient, bytes calldata data) private {
// Application-specific implementation
}
}
The specification combines:
- standard interface for identification of staking
- utility benefits
I am not aware of this, if it exists.
It’s foreseeable to have uses, an example use case:
A child in Nethers is raised by his mother in impoverished conditions. The child performs well at school and gets a reward. Company X pools staked assets and deposits rewards accessible to the child.
I can think of many more and want to hear your thoughts on this. I welcome your feedback and hope this is interesting to you.