# Smart Contract

## Check it on Mainnet

<https://explorer.stacks.co/txid/SP1SCEXE6PMGPAC6B4N5P2MDKX8V4GF9QDE1FNNGJ.trustless-rewards?chain=mainnet>

## Explanations

### Traits used

These traits enforce those standard are implemented in this smart contract

{% code overflow="wrap" lineNumbers="true" %}

```lisp
(use-trait nft-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
(use-trait ft-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)
```

{% endcode %}

### Constants

Used for signaling different errors , an ok messages and the default price used if one can't be found for the lobby

{% code overflow="wrap" lineNumbers="true" %}

```lisp
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-NOT-ACTIVE (err u403))
(define-constant ERR-ALREADY-JOINED (err u405))
(define-constant ERR-JOIN-FAILED (err u500))
(define-constant OK-SUCCESS u200)
(define-constant DEFAULT-PRICE u100)
```

{% endcode %}

### Variable Data Stored - Maps and Vars

Store the information for each lobby.

Store the highscore of each user to that lobby.

Keep the count of created lobbies, also used as id for the next lobby.

Keep the admin that makes the smart contract calls to submit highscores, pay users, end lobbies etc.  &#x20;

<pre class="language-lisp" data-line-numbers><code class="lang-lisp"><strong>(define-map lobbies {id: uint} {owner: principal, description: (string-ascii 99), balance: uint, price: uint, factor: uint, commission: uint, mapy: (string-ascii 30), length: (string-ascii 10), traffic: (string-ascii 10), curves: (string-ascii 10), hours: uint, active: bool})
</strong>(define-map scoreboard {lobby-id: uint, address: principal} {score: uint, rank: uint, sum-rank-factor: uint, rank-factor: uint, rewards: uint, rac: uint, nft: (string-ascii 99)})
(define-data-var lobby-count uint u0)
(define-data-var contract-owner principal tx-sender)
</code></pre>

### General Functions

Functions that anyone can call:

Create a lobby.

Join a lobby.

Get a lobby info - read only. Can get the information without paying STX directly calling the endpoint, as it does not write information to the blockchain.

Get the high score of a user address for a specified lobby-id.&#x20;

{% code lineNumbers="true" %}

```lisp
(define-public (create-lobby 
  (description (string-ascii 99)) (price uint) (factor uint) (commission uint) 
  (mapy (string-ascii 30)) (length (string-ascii 10)) (traffic (string-ascii 10)) (curves (string-ascii 10)) (hours uint)
  )
  (let ((lobby-id (increment-lobby-count)))
  (map-set lobbies {id: lobby-id} { owner: tx-sender, description: description, balance: u0, price: price, factor: factor, commission: commission, 
        mapy: mapy, length: length, traffic: traffic, curves: curves, hours: hours, active: true
  })
  (try! (join lobby-id))
  (ok lobby-id)))

(define-public (join (id uint))
  (let ((entry-price (default-to DEFAULT-PRICE (get price (map-get? lobbies {id: id}))))
    (joined (map-insert scoreboard {lobby-id: id, address: tx-sender} {score: u0, rank: u0, sum-rank-factor: u0, rank-factor: u0, rewards: u0, rac: u0, nft: ""})))
    (unwrap-panic (map-get? lobbies {id: id}))
    (asserts! (default-to false (get active (map-get? lobbies {id: id}))) ERR-NOT-ACTIVE)
    (asserts! joined ERR-ALREADY-JOINED)
    (add-balance id tx-sender entry-price)
    (print {action: "join", lobby-id: id, address: tx-sender })
    (ok OK-SUCCESS)))

(define-read-only (get-lobby (id uint))
    (ok (unwrap-panic (map-get? lobbies {id: id}))))

(define-read-only (get-score (lobby-id uint) (address principal))
    (ok (unwrap-panic (map-get? scoreboard {lobby-id: lobby-id, address: address}))))
```

{% endcode %}

### Admin Functions

Functions are public but only the admin can pass the asserts to make the calls. This is done for security reasons. Without it, anyone would be able to abuse the information. It also facilitates transparency on-chain by letting everybody see what the admin called.&#x20;

#### Simple functions &#x20;

Disable lobby - ends a specific lobby. After ending it high scores cannot be uploaded to it and payments cannot be done to the users from it.

Set owner - change the admin of the smart contract. The old address will not be able to call the admin functions after the call is confirmed, while the new address will be able afterwards.

{% code overflow="wrap" lineNumbers="true" %}

```lisp
(define-public (disable-lobby (id uint))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (match
      (map-get? lobbies {id: id})
      lobby
      (map-set lobbies {id: id} (merge lobby {active: false}))
      false)
    (ok true)))
    
(define-public (set-owner (new-owner principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (var-set contract-owner new-owner)
    (ok true)))
```

{% endcode %}

#### Complex functions

Publish - posts the high scores to the blockchain. It gets from the admin a list of maximum 50 high scores for one or more active lobbies and sets them in the scoreboard map.  &#x20;

{% code lineNumbers="true" %}

```lisp
(define-public (publish-result-many (run-result (list 50 { lobby-id: uint, address: principal, score: uint, rank: uint, sum-rank-factor: uint, rank-factor: uint, rewards: uint, rac: uint, nft: (string-ascii 99)})))
  (fold check-err
    (map publish-result run-result)
    (ok true)))

(define-private (publish-result (run-result { lobby-id: uint, address: principal, score: uint, rank: uint, sum-rank-factor: uint, rank-factor: uint, rewards: uint, rac: uint, nft: (string-ascii 99)}))
  (publish-only (get lobby-id run-result) (get address run-result) (get score run-result) (get rank run-result) (get sum-rank-factor run-result) (get rank-factor run-result) (get rewards run-result) (get rac run-result) (get nft run-result)))

(define-private (publish-only (lobby-id uint) (address principal) (score uint) (rank uint) (sum-rank-factor uint) (rank-factor uint) (rewards uint) (rac uint) (nft (string-ascii 99)))
  (let
    ((publishOk (try! (publish lobby-id address score rank sum-rank-factor rank-factor rewards rac nft))))
    (ok publishOk)))

(define-private (publish (lobby-id uint) (address principal) (score uint) (rank uint) (sum-rank-factor uint) (rank-factor uint) (rewards uint) (rac uint) (nft (string-ascii 99)))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (unwrap-panic (map-get? scoreboard {lobby-id: lobby-id, address: address}))
    (asserts! (default-to false (get active (map-get? lobbies {id: lobby-id}))) ERR-NOT-ACTIVE)
    (map-set scoreboard {lobby-id: lobby-id, address: address} {score: score, rank: rank, sum-rank-factor: sum-rank-factor, rank-factor: rank-factor, rewards: rewards, rac: rac, nft: nft})
    (print {action: "publish", lobby-id: lobby-id, address: address, score: score, rank: rank, sum-rank-factor: sum-rank-factor, rank-factor: rank-factor, rewards: rewards, rac: rac, nft: nft})
    (ok true)))
```

{% endcode %}

Finish - when a lobby is done, finish it by submitting the last high scores. Calculate off-chain the rewards for each player who took part in that lobby and call it on-chain to distribute them directly from the pool.&#x20;

{% code lineNumbers="true" %}

```lisp
(define-public (finish-result-many  (run-result (list 50 { lobby-id: uint, address: principal, score: uint, rank: uint, sum-rank-factor: uint, rank-factor: uint, rewards: uint, rac: uint, nft: (string-ascii 99)})))
  (fold check-err
    (map finish-result run-result)
    (ok true)))
    
(define-private (finish-result (run-result { lobby-id: uint, address: principal, score: uint, rank: uint, sum-rank-factor: uint, rank-factor: uint, rewards: uint, rac: uint, nft: (string-ascii 99)}))
    (finish-only (get lobby-id run-result) (get address run-result) (get score run-result) (get rank run-result) (get sum-rank-factor run-result) (get rank-factor run-result) (get rewards run-result) (get rac run-result) (get nft run-result)))
    
(define-private (finish-only (lobby-id uint) (address principal) (score uint) (rank uint) (sum-rank-factor uint) (rank-factor uint) (rewards uint) (rac uint) (nft (string-ascii 99)))
  (let
  ((finishOk (try! (finish lobby-id address score rank sum-rank-factor rank-factor rewards rac nft))))
  (ok finishOk)))
  
;; distribute rewards for all runs in a lobby
(define-private (finish (lobby-id uint) (address principal) (score uint) (rank uint) (sum-rank-factor uint) (rank-factor uint) (rewards uint) (rac uint) (nft (string-ascii 99)))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (unwrap-panic (map-get? scoreboard {lobby-id: lobby-id, address: address}))
    (asserts! (default-to false (get active (map-get? lobbies {id: lobby-id}))) ERR-NOT-ACTIVE)
    (map-set scoreboard {lobby-id: lobby-id, address: address} {score: score, rank: rank, sum-rank-factor: sum-rank-factor, rank-factor: rank-factor, rewards: rewards, rac: rac, nft: nft})
    (try! (as-contract (stx-transfer? rac tx-sender address)))
    (print {action: "finish", lobby-id: lobby-id, address: address, score: score, rank: rank, sum-rank-factor: sum-rank-factor, rank-factor: rank-factor, rewards: rewards, rac: rac, nft: nft})
    (ok true)))
```

{% endcode %}

### Helper Functions

Functions are private and directly used when they are needed, in other functions.&#x20;

Increment lobby id.

Add balance - send x amount of STX from the tx-sender and add it to the lobby pool.

Check response is error.is

{% code lineNumbers="true" %}

```lisp
(define-private (increment-lobby-count)
  (begin
    (var-set lobby-count (+ (var-get lobby-count) u1))
    (var-get lobby-count)))

(define-private (add-balance (id uint) (participant principal) (amount uint))
  (begin
    (unwrap-panic (stx-transfer? amount participant (as-contract tx-sender)))
    (match
      (map-get? lobbies {id: id})
      lobby
      (map-set lobbies {id: id} (merge lobby {balance: (+ (default-to u0 (get balance (map-get? lobbies {id: id}))) amount)}))
      false)))

(define-private (check-err (result (response bool uint)) (prior (response bool uint)))
  (match prior ok-value result
    err-value (err err-value)))
```

{% endcode %}

### Safety Functions

Implement the standard trait for transferring assets: STX, FT and NFTs.

{% code lineNumbers="true" %}

```lisp
(define-public (transfer-stx (address principal) (amount uint))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (unwrap-panic (as-contract (stx-transfer? amount (as-contract tx-sender) address)))
    (ok true)))

(define-public (transfer-ft-token (address principal) (amount uint) (token <ft-trait>))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (try! (as-contract (contract-call? token transfer amount tx-sender address none)))
    (ok true)))

(define-public (transfer-nft-token (address principal) (id uint) (token <nft-trait>))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR-NOT-AUTHORIZED)
    (try! (as-contract (contract-call? token transfer id tx-sender address)))
    (ok true)))
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.degenlab.io/gamefistacks/trustless-rewards-m1/smart-contract.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
