This is a NEAR staking smart contract that allows users to stake NEP-141 tokens and earn rewards based on the Annual Percentage Yield (APY). Users can stake tokens multiple times, query their current stake and rewards, and unstake to retrieve their principal and accumulated rewards.
- Stake NEP-141 Tokens: Users can stake tokens by transferring them to the contract using the
ft_transfer_callmethod. - Multi-Stake Support: Users can stake multiple times, and their total stake will be accumulated.
- Real-Time Rewards Calculation: The contract dynamically calculates rewards based on the staking duration and APY.
- Unstake: Users can unstake their tokens at any time to retrieve their principal and accumulated rewards.
- Query Staking Information: Users can query their staking details, including the current principal and real-time rewards.
- AAR:
8.0%(represented as800with a precision factor of10000). - SECONDS_IN_A_YEAR:
31,536,000seconds (365 days).
pub fn new(owner_id: AccountId, token_contract: AccountId) -> SelfInitializes the contract with the following parameters:
owner_id: The account ID of the contract owner.token_contract: The NEP-141 token contract address to be used for staking.
fn ft_on_transfer(
&mut self,
sender_id: AccountId,
amount: U128,
_msg: String,
) -> PromiseOrValue<U128>Automatically called when a user stakes tokens using the ft_transfer_call method of the NEP-141 token contract. It updates the user's staking record, adding the new stake to the existing balance and recalculating rewards.
pub fn get_stake_info(&self, account_id: AccountId) -> Option<StakeInfo>Returns the staking details for the given account_id, including:
amount: The total principal staked by the user.accumulated_reward: The rewards earned so far, including real-time calculations.start_time: The timestamp when staking began.
pub fn unstake(&mut self)Allows users to retrieve their entire principal and accumulated rewards. After unstaking, the user's staking record is removed from the contract.
fn calculate_reward(&self, amount: u128, duration_in_seconds: u64) -> u128Calculates the rewards based on the staking amount, staking duration, APY, and the total seconds in a year using the following formula:
Reward = Principal * APY * Duration / (SECONDS_IN_A_YEAR * 10000)
-
Compile the contract:
cargo build --target wasm32-unknown-unknown --release
-
Deploy the contract to a NEAR account:
near deploy --accountId <contract_account_id> --wasmFile <path_to_wasm_file>
-
Initialize the contract:
near call <contract_account_id> new '{"owner_id": "<owner_account_id>", "token_contract": "<token_contract_id>"}' --accountId <owner_account_id>
Users can stake NEP-141 tokens by calling the ft_transfer_call method on the token contract.
Example command:
near call <token_contract_id> ft_transfer_call '{"receiver_id": "<contract_account_id>", "amount": "1000000000000000000000000", "msg": ""}' --accountId <user_account_id> --depositYocto 1Users can query their staking details, including real-time rewards, using the get_stake_info method.
Example command:
near view <contract_account_id> get_stake_info '{"account_id": "<user_account_id>"}'Users can unstake their tokens to retrieve their principal and accumulated rewards by calling the unstake method.
Example command:
near call <contract_account_id> unstake '{}' --accountId <user_account_id> --depositYocto 1After unstaking, the user's staking record is removed from the contract.
To test this contract, you can use NEAR SDK's simulation framework. The tests/staking_contract.rs file contains unit tests for the following functionalities:
- Contract initialization.
- Staking tokens using
ft_on_transfer. - Querying staking details with real-time rewards.
- Unstaking tokens and removing staking records.
Run the tests with the following command:
cargo test -- --nocaptureEnsures the contract correctly initializes with the provided owner and token contract.
Tests that staking tokens updates the user's staking record with the correct principal and rewards.
Verifies that multiple staking operations accumulate the user's principal and rewards correctly.
Checks that get_stake_info returns accurate real-time rewards.
Tests that unstaking retrieves the correct principal and rewards, and removes the user's staking record.
- Ensure the contract account has enough NEAR to cover storage costs.
- The contract uses integer calculations to avoid floating-point errors in reward calculations.
- Rewards are calculated dynamically based on real-time staking duration.
This contract is open-source and available under the MIT License.