This smart contract is designed for a token-weighted voting system on the NEAR blockchain. It allows users to create voting proposals, vote on them using specific tokens, and view the results. The contract is built with NEAR's Rust SDK.
VotingContract
Struct: Main struct containing all proposals and votes.Proposal
Struct: Represents a voting proposal, including its description, voting period, and voting options.FtOnTransferMessage
Struct: Used for passing voting data when tokens are transferred.- Functions: Key functions for creating proposals, voting, tallying votes, and viewing results.
-
Create Proposal
- Function:
create_proposal
- Description: Allows a user to create a new voting proposal.
- Parameters:
description
: Description of the proposal.start_time
: Start time of the voting period (in nanoseconds).end_time
: End time of the voting period (in nanoseconds).options
: List of voting options.token_contract_id
: NEP-141 Token contract used for staking.
- Returns:
proposal_id
of the newly created proposal.
- Function:
-
Vote
- Function:
vote
- Description: Allows a user to vote on a proposal by staking tokens.
- Parameters:
proposal_id
: ID of the proposal to vote on.option_name
: Name of the chosen option.amount
: Amount of tokens to stake.token_contract_id
: NEP-141 Token contract ID from which tokens are staked.
- Note: This function triggers a cross-contract call to the specified token contract for the transfer of tokens.
- Function:
-
Tally Votes
- Function:
tally_votes
- Description: Tallies the votes for a proposal after the voting period has ended.
- Parameter:
proposal_id
: ID of the proposal to tally votes for.
- Note: This function updates the winning option based on the current votes.
- Function:
-
Get Options and Stakes
- Function:
get_options_and_stakes
- Description: Retrieves the current vote count and stakes for each option in a proposal.
- Parameter:
proposal_id
: ID of the proposal to view.
- Returns: A tuple containing the current winning option and a
HashMap
with each option's name and total staked tokens.
- Function:
The ft_on_transfer
function is a critical part of the NEAR smart contract that interacts with fungible tokens (FTs) adhering to the NEP-141 standard, similar to ERC-20 tokens in Ethereum. This function is automatically called by a token contract when tokens are transferred to the smart contract using the ft_transfer_call
method.
- Handling Incoming Token Transfers: It is responsible for handling tokens transferred to the contract and performing actions based on the received tokens and accompanying message.
- Cross-Contract Communication: It facilitates cross-contract communication, allowing the voting contract to react to token transfers from various NEP-141 compliant token contracts.
pub fn ft_on_transfer(&mut self, sender_id: AccountId, amount: U128, msg: String) -> PromiseOrValue<u128>
sender_id
: The account ID of the user who sent the tokens.amount
: The amount of tokens transferred, wrapped inU128
for JSON compatibility.msg
: A string containing additional information or instructions. In the context of the voting contract, this message includes details likeproposal_id
and the chosenoption_name
.
- Returns
PromiseOrValue<u128>
, which typically indicates the amount of tokens that were not used or need to be refunded. In most cases, this will be0
if all tokens are utilized.
- When a user wants to vote, they initiate a token transfer to the voting contract with a message specifying their voting choice.
- The token contract then calls
ft_on_transfer
on the voting contract as part of theft_transfer_call
process. - The voting contract processes the vote based on the message and updates the voting records accordingly.
The internal_vote
function is an integral part of the voting process in the smart contract. It is designed to handle the logic of recording a vote once a token transfer has been initiated and received by the contract.
- Vote Recording: Processes and records a user's vote in a proposal.
- Token-Weighted Voting: Accommodates token-weighted voting by associating the number of staked tokens with the chosen voting option.
fn internal_vote(&mut self, proposal_id: u64, option_name: String, voter: AccountId, staked_amount: u128)
proposal_id
: The ID of the proposal being voted on.option_name
: The name of the option the user is voting for.voter
: The account ID of the user who is voting.staked_amount
: The amount of tokens that the user has staked for this vote.
- Retrieve Proposal:
- Fetches the proposal by
proposal_id
from theproposals
map. If the proposal is not found, the function will panic.
- Fetches the proposal by
- Update Vote Count:
- Retrieves the current vote count for the selected
option_name
in the proposal. - Updates the vote count by adding the
staked_amount
.
- Retrieves the current vote count for the selected
- Record Voter's Stake:
- Records the
staked_amount
against the voter's account ID and theproposal_id
in thevotes
map. This is crucial for keeping track of how much each voter has staked on each option.
- Records the
- Update the Proposal:
- Inserts the updated proposal back into the
proposals
map.
- Inserts the updated proposal back into the
-
Creating a Proposal:
- User calls
create_proposal
, specifying the description, start and end times, voting options, and the token contract for staking. - The function returns the
proposal_id
of the newly created proposal.
- User calls
-
Voting on a Proposal:
- User sends tokens to their NEP-141 token contract, calling its
ft_transfer_call
method. - The
msg
parameter offt_transfer_call
includes theproposal_id
and chosen option. - The token contract calls
ft_on_transfer
on the voting contract, which processes the vote.
- User sends tokens to their NEP-141 token contract, calling its
-
Tallying Votes:
- Once the voting period ends,
tally_votes
can be called with theproposal_id
to determine the winning option.
- Once the voting period ends,
-
Viewing Options and Stakes:
get_options_and_stakes
returns a detailed view of all options and the total tokens staked for each, along with the winning option.
- Time Period Feature: Will allow setting more flexible voting periods.
- Unstaking Feature: Will enable users to unstake their tokens after voting.
near call [VOTING_CONTRACT] create_proposal '{"description": "New Proposal", "start_time": [START_TIME], "end_time": [END_TIME], "options": ["Yes", "No"], "token_contract_id": "[TOKEN_CONTRACT]"}' --accountId [YOUR_ACCOUNT]
- Replace placeholders with actual values.
[START_TIME]
and[END_TIME]
should be in nanoseconds.
-
User sends tokens to the token contract:
near call [TOKEN_CONTRACT] ft_transfer_call '{"receiver_id": "[VOTING_CONTRACT]", "amount": "10", "msg": "{\"proposal_id\":0, \"option_name\":\"Yes\"}"}' --accountId [USER_ACCOUNT] --depositYocto 1 --gas 300000000000000
- The
msg
includes theproposal_id
and the chosen option.
- The
-
This triggers the
ft_on_transfer
method in the voting contract, recording the vote.
near call [VOTING_CONTRACT] tally_votes '{"proposal_id": 0}' --accountId [YOUR_ACCOUNT]
- This finalizes the winning option after the voting period.
near view [VOTING_CONTRACT] get_options_and_stakes '{"proposal_id": 0}'
- Returns details of all options and the total stakes for each.