Developing a Full-Stack Project on Stacks with Clarity Smart Contracts and Stacks.js Part II: Backend
Tags: clarity, stacks, web3, blockchain
Published: 2022-10-06 16:22:16


## Backend

Our contract will simply post "gm" from a user to the chain for a small fee. We will do this by mapping the string "gm" to the users unique STX address. The functionality will be basic but enough to demonstrate concepts, testing, and frontend interaction.

In `gm.clar` goes the code: 

```
;; gm
;; smart contract that posts a GM to the chain for 1 STX

;; constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant PRICE u1000000)
(define-constant ERR_STX_TRANSFER (err u100))

;; data maps and vars
(define-data-var total-gms uint u0)
(define-map UserPost principal (string-ascii 2))

;; public functions
(define-read-only (get-total-gms)
  (var-get total-gms)
)

(define-read-only (get-gm (user principal))
  (map-get? UserPost user)
)

(define-public (say-gm)
  (begin
    (unwrap! (stx-transfer? PRICE tx-sender CONTRACT_OWNER) ERR_STX_TRANSFER)
    (map-set UserPost tx-sender "gm")
    (var-set total-gms (+ (var-get total-gms) u1))
    (ok "Success")
  )
)
```

That's it! The entire contract and all the functionality needed is in the above code.

We have data space at the top of the file and all of the functions listed after.

### Data:
- `define-constant`: We have 3 constants defined. A constant is simply an immutable piece of data, meaning it cannot be changed once defined. In our case, we are using constants to define the contract deployer (I will go into this concept more in a bit), the price of writing the message denoted in microstacks (1,000,000 microstacks is equal to 1 STX), and an error response.  
- `define-data-var`: A variable is a piece of data that can be changed by means of future calls. They are, however, only modifiable by the contract in which they are defined. We are defining a variable to track the total number of writes.
- `define-map`: A map is a data structure that allows you to map keys to values. We will be mapping a principal (Stacks wallet address) to the "gm" string. 

### Functions:

We have 3 functions, 2 read-only and 1 public.

A `read-only` function can only perform read operations. It cannot write or make changes to the chain. As you can see, our `read-only` functions are simply grabbing the value of our `variable` and `map`.

Now, we'll take a line-by-line look at the `say-gm` public function.

```
(define-public (say-gm)
  (begin
    (unwrap! (stx-transfer? PRICE tx-sender CONTRACT_OWNER) ERR_STX_TRANSFER)
    (map-set UserPost tx-sender "gm")
    (var-set total-gms (+ (var-get total-gms) u1))
    (ok "Success")
  )
)
```

A Clarity custom function takes the following form:

```
(define-public function-signature function-body)
```

The function definition can be any of the three Clarity function types: `public`, `private`, or `read-only`

The function signature is made up of the function name and any input parameters taken by the function.

The function body contains the function logic, is limited to one expression, and in the case of a `public-function` **must** return a response type of `ok` or `err`.

Our function signature is:

```
(define-public (say-gm))
```

Our function body is:

```
   (begin
    (unwrap! (stx-transfer? PRICE tx-sender CONTRACT_OWNER) ERR_STX_TRANSFER)
    (map-set UserPost tx-sender "gm")
    (var-set total-gms (+ (var-get total-gms) u1))
    (ok "Success")
  )
```

What exactly is happening here?

- `begin` is one of the Clarity [built-in functions](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-functions). Recall that I mentioned the function body is limited to one expression, we use `begin` to evaluate multiple expressions in a single function.

Next we have:
```
(unwrap! (stx-transfer? PRICE tx-sender CONTRACT_OWNER) ERR_STX_TRANSFER)
```

There are a couple of things happening in this line so let's break it into two:

`(stx-transfer? PRICE tx-sender CONTRACT_OWNER)` is using another built-in function `stx-transfer?` which increases the STX balance of the recipient by debiting the sender.

- `PRICE` is a constant that was defined at the top the file.
- `tx-sender` is a Clarity keyword representing the address that called the function.
- `CONTRACT_OWNER` is also a constant that was defined at the top of the file.

... But wait a minute!

```
(define-constant CONTRACT_OWNER tx-sender)
```

`CONTRACT_OWNER` **is** `tx-sender`.

![](https://media.giphy.com/media/SVgKToBLI6S6DUye1Y/giphy.gif)

`tx-sender` returns the address of the transaction sender, but it's context changes based on where and how it is used.

In the case of `CONTRACT_OWNER`, `tx-sender` will take the context of the standard principal that deployed the contract a.k.a the contract deployer.

Whereas within the `say-gm` function, `tx-sender` has the context of the standard principal that is calling into the function.

You can also manually change the context by using the `as-contract` built-in function to set tx-sender to the contract principal.

I will demonstrate this when we do our manual testing to give you a visual. 

For now let's get back to the first expression.

The expression will return `(ok true)` if the transfer is successful, else it will return an `(err)` response. This is where `unwrap!` comes in. As the name suggests, it attempts to unwrap the result of the argument passed to it. Unwrapping is extracting the inner value and returning it. If the response is `(ok ...)` it will return the inner value otherwise it returns a throw value.

```
(unwrap! (stx-transfer? PRICE tx-sender CONTRACT_OWNER) ERR_STX_TRANSFER)
```

So there we are unwrapping the result of `stx-transfer?`

Now we have:
 
```
(map-set UserPost tx-sender "gm")
```

`map-set` sets the value of the input key to the input value. As part of this function call, we are setting the corresponding value of "gm" to the key of the standard principal calling the function.

Then:

```
(var-set total-gms (+ (var-get total-gms) u1))
```

`var-set` sets the value of the input variable to the expression passed as the second parameter. Here we are performing an incremental count to increase `total-gms` by one each time this function executes successfully.

Finally:

```
(ok "Success")
```

because we must return a response type at the end of a function and I just want a success message on completion.


## Manual Contract Calls

To make sure our contract is working we'll do a few things.

Run:
```
clarinet check
```

This checks your contract's syntax for errors. If all is well, you should return this message:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6c1idecqkyb9ecb4pt1y.png)

Now run:
```
clarinet console
```
You should see the following tables:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omda37yzon8yfeksjasu.png)

The first table contains the contract identifier (which is also the contract principal) and the available functions.

The second table contains 10 sets of dummy principals and balances. This data is pulled directly from the `Devnet.toml` file. If you open that file and compare the standard principals, you will notice they are the same.

From here we can make some contract calls to ensure the desired functionality of each function is there.

To make a contract call, we follow the format:

```
(contract-call? .contract-id function-name function-params)
```

Make the following call to our `say-gm` function:

```
(contract-call? .gm say-gm)
```

Did you get an `err u100`? We set our constant `ERR_STX_TRANSFER` to `err u100`. So why did we get this?

It is because we just attempted to transfer STX between the same addresses.

When we run `clarinet console`, `tx-sender` is automatically set to the contract deployer. You can verify this by running `tx-sender` from within the console. If you compare this to the data inside of `Devnet.toml` you'll see that the address is the same as the one listed under `[accounts.deployer]`. It is also the first address in the table that loads when opening console.

This comes back to the "context" I mentioned above. You can think of that first address as the deployer and all subsequent addresses as "users" that can call into your function.

We're going to want to change the `tx-sender` within console. You can do this by running 

```
::set_tx_sender address
```

You can copy/paste any address from the assets table or `Devnet.toml`.

Let's call `get-total-gms`:

```
(contract-call? .gm get-total-gms)
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ef8cehqndytbl5017knj.png)

Another error? `use of unresolved contract`.

This is happening because we changed our `tx-sender` we now have to explicitly state the contract-id as part of the contract call. You can grab this from the assets table. If you cleared the console and need to bring it up again run:

```
::get_contracts
```

Now try the following call:

```
(contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.gm get-total-gms)
```

This should return `u0`

```
(contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.gm get-gm tx-sender)
```

This should return `none`

Good this is expected because we have not yet called any write functions. 

Alright, let's call `say-gm`.
```
(contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.gm say-gm)
```
We should see `(ok "Success")`

Now if we make calls again to `get-total-gms` and `get-gm` we should get `u1` and `(some "gm")` respectively which means the functionality works!

You can also run:

```
::get_assets_maps
```

This is will bring up the assets table and you will be able to see the transfer of STX between addresses reflected in the balances.

Great! We've now manually tested our functions. Before moving on, we're going to do one more thing to demonstrate the context for `tx-sender`.

I mentioned the `as-contract` built-in function and how it changes the context of `tx-sender` from the standard principal to the contract principal.

Exit the console and replace your `CONTRACT_OWNER` definition with:

```
(define-constant CONTRACT_OWNER (as-contract tx-sender))
```

Now run `clarinet console` again and make the following contract-call:

```
(contract-call? .gm say-gm)
```

We didn't get an error this time and didn't have to change the `tx-sender`. Why?

Running

```
::get_assets_maps
```

will provide an answer.

Notice a **new** address has been added to the table. A contract principal. So even though we made the call from the deployer, we were able to transfer STX because `CONTRACT_OWNER` was initialized as the contract and so we aren't transferring STX between the same address, we are transferring from the deployer's standard principal to the contract principal which is capable of holding tokens as well.

This demonstrates what the `as-contract` function does.


## Unit Tests

Unit testing is a critical part of smart contract development. Due to the nature of smart contract and the potential use cases, as smart contract developers we want to ensure that we account for as many scenarios as possible. 

Tests are written in typescript and a template is auto generated for you.

Let's take a look at `gm_test.ts`

```
import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.31.0/index.ts';
import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts';

Clarinet.test({
    name: "Ensure that <...>",
    async fn(chain: Chain, accounts: Map<string, Account>) {
        let block = chain.mineBlock([
            /* 
             * Add transactions with: 
             * Tx.contractCall(...)
            */
        ]);
        assertEquals(block.receipts.length, 0);
        assertEquals(block.height, 2);

        block = chain.mineBlock([
            /* 
             * Add transactions with: 
             * Tx.contractCall(...)
            */
        ]);
        assertEquals(block.receipts.length, 0);
        assertEquals(block.height, 3);
    },
});
```

This is your boilerplate test code. I recommend browsing the Deno documentation provided at the top the file.

We are just going to write a single basic test together as part of this example.

```
Clarinet.test({
    name: "A user can say gm",
    async fn(chain: Chain, accounts: Map<string, Account>) {
        const deployer = accounts.get('deployer')!.address;
        const user = accounts.get('wallet_1')!.address

        let block = chain.mineBlock([
            Tx.contractCall(
                "gm",
                "say-gm",
                [],
                user
            )
        ]);
        assertEquals(block.receipts.length, 1);
        assertEquals(block.height, 2);

        const messageMapped = chain.callReadOnlyFn(
            "gm",
            "get-gm",
            [types.principal(user)],
            user
        )
        assertEquals(messageMapped.result, types.some(types.ascii("gm")));

        const totalCountIncreased = chain.callReadOnlyFn(
            "gm",
            "get-total-gms",
            [],
            user
        )
        assertEquals(totalCountIncreased.result, types.uint(1));
    }
});
```

Here we're adding a few things to the boilerplate code provided.

```
const deployer = accounts.get('deployer')!.address;
const user = accounts.get('wallet_1')!.address
```
This is grabbing the standard principals from `Devnet.toml` that correspond to the string we pass to `accounts.get()`

```
Tx.contractCall(
 "gm",
 "say-gm",
 [],
 user
)
```

`Tx.contractCall` is how we can simulate a contract call from our test. It takes 4 parameters:
1. The contract name
2. The function name
3. Any params accepted by the function as an array
4. The address calling the function

```
assertEquals(block.receipts.length, 1);
assertEquals(block.height, 2);
```

We need to also simulate a block being mined. Our test assumes a start at genesis block 1.

`block.receipts.length` accounts for the number of transactions in that block.

`block.height` accounts for the block height at mining. 

In this case we are calling one tx and mining one block. 

```
const messageMapped = chain.callReadOnlyFn(
 "gm",
 "get-gm",
 [types.principal(user)],
 user
)
assertEquals(messageMapped.result, types.some(types.ascii("gm")));
```

Here we are calling a `read-only` function with `chain.callReadOnlyFn()` which takes the same parameters as `Tx.contractCall()` and we are asserting that the result is an ascii string "gm". Why? If the `say-gm` call is successful we can assume that the result of this `get-gm` will be that string mapped to the user. 

```
const totalCountIncreased = chain.callReadOnlyFn(
  "gm",
  "get-total-gms",
  ],
  user
)
assertEquals(totalCountIncreased.result, types.uint(1));
```

Finally, we make a call to `get-total-gms` and assert that the total has been incremented to 1. 

Now in the terminal you can run 

```
clarinet test
```

You should see a successful pass:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jvxefy2vc21rpsd4m67.png)

Clarinet offers an extensive [testing suite](https://github.com/hirosystems/clarinet#execute-a-test-suite) that includes `lcov` code coverage reports.

Curious about TDD for contracts? Check out this [blog](https://dev.to/stacks/test-driven-stacks-development-with-clarinet-2e4i).

## Spinning up DevNet

Our backend is complete! Let's get DevNet running and hook it up to our web wallet so that it's ready to go after we build the frontend. 

Make sure Docker is running.

In your terminal run:

```
clarinet integrate
```

With this command, Clarinet fetches the appropriate Docker images for the Bitcoin node, Stacks node, Stacks API node, Hyperchain node, and the Bitcoin and Stacks Explorers.

Boot up can take several minutes the first time you launch.

When complete, you should see something like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5qpo11db35fkh7n1f0g.png)

Great! You've got DevNet running. You can read more about the interface in the [docs](https://docs.hiro.so/smart-contracts/devnet#devnet-interface).

There is some configuration left to do before you can interact with your frontend app.

You need to import information from your Hiro [web wallet](https://wallet.hiro.so/) into your `Devnet.toml` file.

**Note:** `Devnet.toml` is not listed in `.gitignore` meaning the information you add to the configuration may be  visible, so you want to either add the file to `.gitignore`, or create a separate wallet for testing if you plan to push your code to GitHub.

From your browser open up your web wallet and change your network to Devnet. This setting can be found by clicking the three dots on the top right corner and selecting Change Network.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubmrtuy1b8k19nfzvuj5.png)
 
Devnet will only be available for selection while you have your local DevNet running.

Once you change networks, open up the menu again and click View Secret Key. You need to copy this and paste it in `Devnet.toml` under `[accounts.wallet_1]` where it says `mnemonic=`

You will be replacing the generated keys with the ones copied from your web wallet.

That's it! Configuration is complete and you're ready to start building the frontend app.