oleganza
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Tokenized Stakers Pool ## Problem Pooled validation aims at allowing large number of participants with smaller amount of coins to participate in TON validation. This requires solving three technical problems: 1. How to make contract operating at O(1) cost, avoid inconvenient hard limits and prevent any possibility for DoS attacks? 2. How to efficiently and correctly store information about stakes that arrive at different times and with different amounts, given that the incoming profit varies? 3. How to perform withdrawals given that almost all the time coins are locked into validation cycle and are not available for immediate withdrawal? The design below aims at solving these three problems. ## Key ideas 1. Contract implements a pool of *stakers* and works with a single target for validation. A pool of validators could be implemented as a separate contract used as a target instead of the elector contract. 2. Contract does not store variable-length data, **all operations are O(1)**. 3. Each user pays their own blockchain fees. 4. The information about stakes is encoded into the **price** and **amount** of shares issued by the pool. At the end of the cycle the pool knows the total amount of outstanding shares and an up-to-date amount of coins available (deposits+profits). As profits accumulate, the price of a share grows. 5. The information about withdrawals is encoded in **withdrawal receipts** implemented as NFTs. When the user returns their share-tokens to the pool, the pool issues a **receipt-contract** that indicates the time (timestamp or validation cycle number) when the exact amount of coins will become available for collection. When the receipt is transferred to the pool, it destroys it and returns back the requested amount of coins. ## Specifics * We should study all quirks in Nominator contract: https://github.com/ton-blockchain/nominator-pool/tree/main/func * The spec below does not implement voting yet. We may start with a simpler scheme where the operator of the pool votes, then figure out how to tokenize voting by pool members. * Sometimes a validation cycle may be skipped: https://github.com/ton-blockchain/nominator-pool/blob/main/func/pool.fc#L575 * (Something else? Add here.) ## Questions? * Not clear what's the API of the Elector? Do we request the stake back manually or toncoins return automatically? * How to implement voting? * Do we use election cycle count or timestamp? * Do we use proper NFTs or OnceTokens for withdrawal receipts and destroy them when redeeming? ## Specification ### Elector proxy Elector contract requires access from the same chain (-1, aka masterchain). Since token creation/destruction is relatively expensive, we move the actual logic to the workchain. The workchain contract sends messages to the proxy as if it was an elector. And vice versa. ``` contract Proxy { // hard-coded address of the elector contract and the stakers pool let elector: Address; let owner: Address; fn recv_internal(msg: InternalMessage) { if msg.address == self.elector { send(InboundCoins) self.owner.send_raw_message(msg); } else if msg.address == self.owner { send(InboundCoins) self.elector.send_raw_message(msg); } } bounced handle() { if msg.address == self.elector { send self.owner.unexpected_bounce(msg); } else if msg.address == self.owner { // TODO: ignore? } } } ``` ### Stakers Pool This implements the core logic: getting money in and out of Elector (via the Elector Proxy, see above) and issuing and redeeming **Stake Tokens** and **Withdrawal Receipts**. Stake Tokens are simple jetton contracts with default behavior. Offchain infra may read the current price from the pool and display it in the wallets. Withdrawal Receipts are NFT-like tokens ("OnceToken") that leave <24h and encode the time and amount of Toncoins to be withdrawn. When the receipt is redeemed, it is destroyed; its identity is verified on the recepient's side. ``` contract StakersPool { // this address may be changed // to a ValidatorsPool that manages allocation // of stake among multiple validators. let ElectorAddress = 0xe1ec705...; // address of the proxy let StakeTokenID = 0x1234567...; let RentReserve = 1 TON; let MinShare = 1 TON; // also: min withdrawal amount var coins_validating: uint64; // == C var shares_outstanding: uint64; // == S var coins_to_withdraw: uint64; var validation_cycle: uint64; // When we receive the stake from the elector, // we update our current count of coins to take into account profits, // and increment cycle count to permit withdrawals fn receive_stake_from_elector(value: Coins) { self.coins_validating = value - self.coins_to_withdraw; self.validation_cycle += 1; } // Sends all coins to the elector // except for those requested for withdrawal. fn send_stake_to_elector() { // TODO: do we update coins_validating here? reserve(self.withdrawals_requested + RentReserve); send(AllCoins) ElectorAddress.deposit(); } // Adds incoming coins for validation, // returns the number of stakes fn deposit() { // TODO: allocate necessary fees let c = self.coins_validating; let s = self.shares_outstanding; let new_shares = deposit*s/c; // use MULDIVMODR self.working_coins += msg.value; self.shares_outstanding += new_shares; send StakeJetton(amount: new_shares, owner: msg.sender); } // Withdrawal is requested when shares are returned. // Contract increased amount of coins to send out // and gives back a receipt. fn request_withdrawal(shares: uint64, user: Address) { // TODO: allocate necessary fees let c = self.coins_validating; let s = self.shares_outstanding; let coins = shares*c/s; // use MULDIVMODR self.coins_validating -= coins; self.shares_outstanding -= shares; self.coins_to_withdraw += coins; // Issue an NFT to the user. // It can be exchanged for coins at the next validation cycle. send WithdrawalReceipt.init( amount: coins, owner: users, cycle: self.validation_cycle + 1, ); } // User has sent a withdrawal receipt. // If it is sent untimely, it is returned back. // If it is timely, the NFT is destroyed and // user received their coins. fn withdrawal_receipt( amount: Coins owner: Address, cycle: uint64, msg.sender: WithdrawalReceipt ) { // check that it's a correctly issued receipt let trusted_receipt = WithdrawalReceipt.init(amount, owner, cycle); verify(msg.sender == trusted_receipt.address()); // Verify timing: the withdrawal can only work // when the target cycle has arrived and funds are available. if (cycle > self.validation_cycle) { // wrong timing - return the token back to the sender // todo: subtract the gas costs from their amount send msg.sender.transfer(owner: owner); } else { // funds should be ready - send them out self.coins_to_withdraw -= amount; send msg.sender.burn(excesses_recipient: self.address()); send owner.transfer(msg.amount: amount); } } } ``` ### Stake Token A default "jetton wallet" implementation with a master address set to the StakersPool. ``` contract StakeToken: FungibleToken { let master: StakersPool; } ``` ### Withdrawal Receipt NFT-like token that can be redeemed on maturity date by its owner. We don't really need to authenticate redemption so that any 3rd party infra could trigger it to collect funds on behalf of the user. We also do not need to implement NFT `transfer` method since this token is short-lived and we could simplify the logic by outright destroying the token when it's redeemed. If there's not enough funds on the other end we could simply re-create the token and return it back to the user (subtracting the fees from their withdrawal request). ``` // Invariants: // - must be created by StakersPool // - message should be coming from a verified address contract WithdrawalReceipt { init(amount: Coins, owner: Address, cycle: uint64, msg.sender: Address) { } } ``` # Spec ## Elector 1. Отправка стейка. Возможна ошибка сразу, возможна при получении. - Нужно хендлить баунсы. - Нужно хендлить асинхронные сообщения. 2. Получение стейка. Нужно послать запрос на получение стейка, и он вернется в следующем сообщении. ## Nominator Pool ### Users actions 1. Deposit (deposit coins, get the tokens) 2. Request withdrawal (return tokens, get the receipt) 3. Finalize withdrawal (burn receipt, receive coins) ### Withdraw Receipt actions 1. Withdraw to the address. ### Validator actions 1. New stake. Validator should send message with opcode NEW_STAKE and body that will be sent to the proxy as message body. Amount of coins that should be send to the proxy is equal to WORKING_COINS. This is validator responsibility to check if the message body is valid, Pool should just re-send body to the Proxy. ### Unknown domain actions 1. Recover stake. Anyone or Validator can send message to recover stake. In this case Pool should send message to the elector using following format: ``` recover_stake#47657424 query_id:uint64 = Msg; ``` Unresolved questions: - Magic in https://github.com/ton-blockchain/nominator-pool/blob/main/func/pool.fc#L566-L582. - Who can call this action - anyone or only the validator? ### Proxy actions It is expected that messages sent to these actions will not be bounced. 1. Return Stake action. This action should be called if the Elector return stake sent by the Pool. This message should be sent as-is from the Elector through Proxy to the Pool. Unresolved question: what should we do with this message? Format of the message: ``` return_stake#ee6f454c query_id:uint32 reason:Reason = Msg; reason0#00000000 = Reason; // no elections active, or source is not in masterchain, or elections already finished reason1#00000001 = Reason; // incorrect signature reason2#00000006 = Reason; // factor must be >= 1. = 65536/65536 reason3#00000002 = Reason; // stake smaller than 1/4096 of the total accumulated stakes, return reason4#00000003 = Reason; // stake for some other elections, return reason5#00000004 = Reason; // can make stakes for a public key from one address only reason6#00000005 = Reason; // stake too small, return it ``` 2. Confirmation. This action should be called if the Elector returns confirmation message. This message should be sent as-is from the Elector through Proxy to the Pool. Unresolved question: what should we do with this message? Format of the message: ``` confirmation#f374484c query_id:uint32 comment:uint32 = Msg; ``` 3. Bounced stake. This action should be called if the Elector bounce message with the "Send Stake" message sent by the Proxy. This should not be happened by default, but this is possible situation so we should handle it. Unresolved question: what should we do with this message? TODO: format. 4. Recover Stake Error. This action should be called if the Elector returns error message on the "Recover Stake" action. Format of the message: ``` recover_stake_err#fffffffe query_id:uint32 body:0x47657424 = Msg; ``` 5. Recover Stake Ok. This action should be called if the Elector returns ok message on the "Recover Stake" action. Format of the message: ``` recover_stake_ok#f96f7324 query_id:uint32 = Msg; ``` ## Withdraw Receipt 1. Withdraw. Owner of the receipt can withdraw amount of coins that described in the ## Jetton ## Operator(Validator) ## Anyone ## Elector Proxy Proxy should readressed default Elector messages to the Pool, and the Pool messages to the Elector. If message that was sent to the Elector was bounced, Proxy should send special "Bounced stake" message to the Pool.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully