# The Crowdfunding Smart Contract (part 2)

## Configuring the contract

The previous chapter left us with a minimal contract as a starting point.

The first thing we need to do is to configure the desired target amount and the deadline. The deadline will be expressed as the block timestamp after which the contract can no longer be funded. We will be adding 2 more storage fields and arguments to the constructor.

<pre class="language-typescript"><code class="lang-typescript">constructor(
<strong>    target: BigUint,
</strong>    deadline: ManagedU64
) {
    super();

    this.target().set(target)
    this.deadline().set(deadline)
}

abstract target(): Mapping&#x3C;BigUint>

abstract deadline(): Mapping&#x3C;ManagedU64>

abstract deposit(donor: ManagedAddress): Mapping&#x3C;BigUint>
</code></pre>

The deadline being a block timestamp can be expressed as a regular 64-bits unsigned int. The target, however, being a sum of EGLD cannot. Note that 1 EGLD = 10^18 EGLD-wei (also known as atto-EGLD), the smallest unit of currency, and all payments are expressed in wei. So you can see that even for small payments the numbers get large. Luckily, the framework offers support for big numbers out of the box.

{% hint style="info" %}
Signed big numbers, a.k.a BigInt, are not currently supported by mx-sdk-as.
{% endhint %}

Also note that BigUint logic does not reside in the contract, but is built into the MultiversX VM API, to not bloat the contract code.

Let's test that initialization works.

```json
{
    "name": "crowdfunding deployment test",
    "steps": [
        {
            "step": "setState",
            "accounts": {
                "address:my_address": {
                    "nonce": "0",
                    "balance": "1,000,000"
                }
            },
            "newAddresses": [
                {
                    "creatorAddress": "address:my_address",
                    "creatorNonce": "0",
                    "newAddress": "sc:crowdfunding"
                }
            ]
        },
        {
            "step": "scDeploy",
            "txId": "deploy",
            "tx": {
                "from": "address:my_address",
                "contractCode": "file:../output/release.wasm",
                "arguments": [
                    "500,000,000,000",
                    "123,000"
                ],
                "gasLimit": "5,000,000",
                "gasPrice": "0"
            },
            "expect": {
                "out": [],
                "status": "0",
                "gas": "*",
                "refund": "*"
            }
        },
        {
            "step": "checkState",
            "accounts": {
                "address:my_address": {
                    "nonce": "1",
                    "balance": "1,000,000",
                    "storage": {}
                },
                "sc:crowdfunding": {
                    "nonce": "0",
                    "balance": "0",
                    "storage": {
                        "str:target": "500,000,000,000",
                        "str:deadline": "123,000"
                    },
                    "code": "file:../output/release.wasm"
                }
            }
        }
    ]
}
```

Note the added `"arguments"` field in `scDeploy` and the added fields in storage.

Run the following commands:

```bash
npm run build
npm run mandos
```

You should once again see this:

```
Scenario: crowdfunding-init.scen.json ...   ok
Done. Passed: 1. Failed: 0. Skipped: 0.
SUCCESS
```

## Funding the contract

It is not enough to receive the funds, the contract also needs to keep track of who donated how much.

```typescript
@contract
abstract class Crowdfunding extends ContractBase {

    ...

    fund(): void {
        const payment = this.callValue.egldValue
        const caller = this.blockchain.caller

        const oldDeposit = this.deposit(caller).get()
        const newDeposit = oldDeposit + payment

        this.deposit(caller).set(newDeposit)
    }
    
    abstract deposit(donor: ManagedAddress): Mapping<BigUint>

    ...

}
```

This storage mapper has an extra argument, for an address. This is how we define a map in the storage. The donor argument will become part of the storage key. Any number of such key arguments can be added, but in this case we only need one. The resulting storage key will be a concatenation of the specified base key `"deposit"` and the serialized argument.

{% hint style="warning" %}
AssemblyScript is statically typed, you **should** explicit the return type of every function, even if it is `void`.
{% endhint %}

{% hint style="warning" %}
Instead of Rust contracts, you don't have to mark a method as `endpoint` and/or `payable`. Every public method of the class contract will be reachable and will accept payments.

If you don't want to make a method reachable, set is visibility to `private`.
{% endhint %}

To test the function, we'll add a new test file, in the same `mandos` folder. Let's call it `crowdfunding-fund.scen.json` .

To avoid duplicating the deployment code, we import it from `crowdfunding-init.scen.json` .

```json
{
  "name": "crowdfunding funding",
  "steps": [
    {
      "step": "externalSteps",
      "path": "crowdfunding-init.scen.json"
    },
    {
      "step": "setState",
      "accounts": {
        "address:donor1": {
          "nonce": "0",
          "balance": "400,000,000,000"
        }
      }
    },
    {
      "step": "scCall",
      "txId": "fund-1",
      "tx": {
        "from": "address:donor1",
        "to": "sc:crowdfunding",
        "egldValue": "250,000,000,000",
        "function": "fund",
        "arguments": [],
        "gasLimit": "100,000,000",
        "gasPrice": "0"
      },
      "expect": {
        "out": [],
        "status": "",
        "gas": "*",
        "refund": "*"
      }
    },
    {
      "step": "checkState",
      "accounts": {
        "address:my_address": {
          "nonce": "1",
          "balance": "1,000,000",
          "storage": {}
        },
        "address:donor1": {
          "nonce": "1",
          "balance": "150,000,000,000",
          "storage": {}
        },
        "sc:crowdfunding": {
          "nonce": "0",
          "balance": "250,000,000,000",
          "storage": {
            "str:target": "500,000,000,000",
            "str:deadline": "123,000",
            "str:deposit|address:donor1": "250,000,000,000"
          },
          "code": "file:../output/release.wasm"
        }
      }
    }
  ]
}
```

Explanation:

1. `"externalSteps"`allows us to import steps from another json file. This is very handy, because we can write test scenarios that branch out from each other without having to duplicate code. Here we will be reusing the deployment steps in all tests. These imported steps get executed again each time they are imported.
2. We need a donor, so we add another account using a new `"setState"` step.
3. The actual simulated transaction. Note that we use `"scCall"` instead of `"scDeploy"`. There is a `"to"` field, and no `"contractCode"`. The rest functions the same. The `"egldValue"` field indicates the amount paid to the function.
4. When checking the state, we have a new user, we see that the donor's balance is decreased by the amount paid, and the contract balance increased by the same amount.
5. There is another entry in the contract storage. The pipe symbol`|`in the key means concatenation. The addresses are serialized as itself, and we can represent it in the same readable format.

Test it by running the commands again:

```bash
npm run build
npm run mandos
```

You should then see that both tests pass:

```
Scenario: crowdfunding-fund.scen.json ...   ok
Scenario: crowdfunding-init.scen.json ...   ok
Done. Passed: 2. Failed: 0. Skipped: 0.
SUCCESS
```

## Validation

It doesn't make sense to fund after the deadline has passed, so fund transactions after a certain block timestamp must be rejected. The idiomatic way to do this is:

```typescript
fund(): void {
    const payment = this.callValue.egldValue

    const currentTime = this.blockchain.currentBlockTimestamp
    this.require(
        currentTime < this.deadline().get(),
        "cannot fund after deadline"
    )

    const caller = this.blockchain.caller

    const oldDeposit = this.deposit(caller).get()
    const newDeposit = oldDeposit + payment

    this.deposit(caller).set(newDeposit)
}
```

{% hint style="success" %}
`this.require(expression, errorMessage)` is the same as `if (!expression) { throw new Error(errorMessage) }`
{% endhint %}

{% hint style="warning" %}
`this.require` may be replaced by a global function `require` in the future.
{% endhint %}

We'll create another test file to verify that the validation works: `test-fund-too-late.scen.json` .

```json
{
  "name": "trying to fund one block too late",
  "steps": [
    {
      "step": "externalSteps",
      "path": "crowdfunding-fund.scen.json"
    },
    {
      "step": "setState",
      "currentBlockInfo": {
        "blockTimestamp": "123,001"
      }
    },
    {
      "step": "scCall",
      "txId": "fund-too-late",
      "tx": {
        "from": "address:donor1",
        "to": "sc:crowdfunding",
        "egldValue": "10,000,000,000",
        "function": "fund",
        "arguments": [],
        "gasLimit": "100,000,000",
        "gasPrice": "0"
      },
      "expect": {
        "out": [],
        "status": "4",
        "message": "str:cannot fund after deadline",
        "gas": "*",
        "refund": "*"
      }
    }
  ]
}
```

We branch this time from `crowdfunding-fund.scen.json`, where we already had a donor. Now the same donor wants to donate, again, but in the meantime the current block timestamp has become 123,001, one block later than the deadline. The transaction fails with status 4 (user error - all errors from within the contract will return this status). The testing environment allows us to also check that the correct message was returned.

By building and testing the contract again, you should see that all three tests pass:

```
Scenario: crowdfunding-fund-too-late.scen.json ...   ok
Scenario: crowdfunding-fund.scen.json ...   ok
Scenario: crowdfunding-init.scen.json ...   ok
Done. Passed: 3. Failed: 0. Skipped: 0.
SUCCESS
```

## Querying the contract status

The contract status can be known by anyone by looking into the storage and on the blockchain, but it is really inconvenient right now. Let's create an endpoint that gives this status directly. The status will be one of: `FundingPeriod`, `Successful` or `Failed`. We could use a number to represent it in code, but the nice way to do it is with an enum. We will take this opportunity to show how to create a serializable type that can be taken as argument, returned as result or saved in storage.

This is the enum:

```typescript
@enumtype
enum Status {
    FundingPeriod,
    Successful,
    Failed
}
```

Make sure to add it outside the contract class.

The `@enumtype` annotation tells the framework to automatically implement features like storage encoding.

{% hint style="warning" %}
At compile time enums the framework transforms `enums` into `classes`. This leads to some issues with `switch` statements, make sure to use `if/else if/else` instead
{% endhint %}

We can now use the type Status just like we use the other types, so we can write the following method in the contract class:

<pre class="language-typescript"><code class="lang-typescript">getCurrentFunds(): BigUint {
<strong>    return this.blockchain
</strong>        .getSCBalance(
            TokenIdentifier.egld(),
            ManagedU64.fromValue(0)
        )
}

status(): Status {
    if (this.blockchain.currentBlockTimestamp &#x3C;= this.deadline().get()) {
        return Status.FundingPeriod
    } else if (this.getCurrentFunds() >= this.target().get()) {
        return Status.Successful
    } else {
        return Status.Failed
    }
}
</code></pre>

{% hint style="warning" %}
Remember, these two methods are publics, so reachable by anyone on-chain.
{% endhint %}

To test this method, we append one more step to the last test we worked on, `test-fund-too-late.scen.json` :

```json
{
    "name": "trying to fund one block too late",
    "steps": [
        {
            "step": "externalSteps",
            "path": "crowdfunding-fund.scen.json"
        },
        {
            "step": "setState",
            "currentBlockInfo": {
                "blockTimestamp": "123,001"
            }
        },
        {
            "step": "scCall",
            "txId": "fund-too-late",
            "tx": {
                "from": "address:donor1",
                "to": "sc:crowdfunding",
                "egldValue": "10,000,000,000",
                "function": "fund",
                "arguments": [],
                "gasLimit": "100,000,000",
                "gasPrice": "0"
            },
            "expect": {
                "out": [],
                "status": "4",
                "message": "str:cannot fund after deadline",
                "gas": "*",
                "refund": "*"
            }
        },
        {
            "step": "scQuery",
            "txId": "check-status",
            "tx": {
                "to": "sc:crowdfunding",
                "function": "status",
                "arguments": []
            },
            "expect": {
                "out": [
                    "2"
                ],
                "status": "0"
            }
        }
    ]
}
```

Since the function we're trying to call is a view function, we use the `scQuery` step instead of the `scCall` step. The difference is that for `scQuery`, there is no `caller`, no payment, and gas price/gas limit. On the real blockchain, a smart contract query does not create a transaction on the blockchain, so no account is needed. `scQuery` simulates this exact behaviour.

Note the call to "status" at the end and the result `"out": [ "2" ]` , which is the encoding for `Status::Failure`. Enums are encoded as an index of their values. In this example, `Status::FundingPeriod` is `"0"` (or `""`), `Status::Successful` is `"1"` and, as you've already seen, `Status::Failure` is `"2"`.

Contract functions can return in principle any number of results, that is why `"out"` is a list.

## Claim functionality

Finally, let's add the `claim` method. The `status` method we just implemented helps us keep the code tidy:

```typescript
claim(): void {
    const status = this.status()

    if (status === Status.FundingPeriod) {
        throw new Error("cannot claim before deadline")
    } else if (status === Status.Successful) {
        const caller = this.blockchain.caller
        this.require(
            caller == this.blockchain.owner,
            "only owner can claim successful funding"
        )
        const scBalance = this.getCurrentFunds()
        this.send
            .directEgld(
                caller,
                scBalance
            )
    } else if (status === Status.Failed) {
        const caller = this.blockchain.caller
        const deposit = this.deposit(caller).get()

        if (deposit > BigUint.fromU64(0)) {
            this.deposit(caller).clear()
            this.send
                .directEgld(
                    caller,
                    deposit
                )
        }
    }
}
```

The only new function here is `this.send().directEgld()`, which simply forwards EGLD from the contract to the given address.

## The final contract code

If you followed all the steps presented until now, you should have ended up with a contract that looks something like:

```typescript
//@ts-nocheck

import {
    ContractBase,
    Mapping,
    BigUint,
    ManagedU64,
    ManagedAddress,
    TokenIdentifier
} from "@gfusee/mx-sdk-as";

@enumtype
export enum Status {
    FundingPeriod,
    Successful,
    Failed
}

@contract
abstract class Crowdfunding extends ContractBase {

    constructor(
        target: BigUint,
        deadline: ManagedU64
    ) {
        super();

        this.target().set(target)
        this.deadline().set(deadline)
    }

    fund(): void {
        const payment = this.callValue.egldValue

        const currentTime = this.blockchain.currentBlockTimestamp
        this.require(
            currentTime < this.deadline().get(),
            "cannot fund after deadline"
        )

        const caller = this.blockchain.caller

        const oldDeposit = this.deposit(caller).get()
        const newDeposit = oldDeposit + payment

        this.deposit(caller).set(newDeposit)
    }

    getCurrentFunds(): BigUint {
        return this.blockchain
            .getSCBalance(
                TokenIdentifier.egld(),
                ManagedU64.fromValue(0)
            )
    }

    status(): Status {
        if (this.blockchain.currentBlockTimestamp <= this.deadline().get()) {
            return Status.FundingPeriod
        } else if (this.getCurrentFunds() >= this.target().get()) {
            return Status.Successful
        } else {
            return Status.Failed
        }
    }

    claim(): void {
        const status = this.status()

        if (status === Status.FundingPeriod) {
            throw new Error("cannot claim before deadline")
        } else if (status === Status.Successful) {
            const caller = this.blockchain.caller
            this.require(
                caller == this.blockchain.owner,
                "only owner can claim successful funding"
            )
            const scBalance = this.getCurrentFunds()
            this.send
                .directEgld(
                    caller,
                    scBalance
                )
        } else if (status === Status.Failed) {
            const caller = this.blockchain.caller
            const deposit = this.deposit(caller).get()

            if (deposit > BigUint.fromU64(0)) {
                this.deposit(caller).clear()
                this.send
                    .directEgld(
                        caller,
                        deposit
                    )
            }
        }
    }

    abstract target(): Mapping<BigUint>

    abstract deadline(): Mapping<ManagedU64>

    abstract deposit(donor: ManagedAddress): Mapping<BigUint>

}
```

As an exercise, try to add some more tests, especially ones involving the claim function.

## Next steps

This concludes the first AssemblyScript MultiversX tutorial.

If you have any issue you can open one in the GitHub repo : <https://github.com/gfusee/mx-sdk-as>


---

# 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://fusee.gitbook.io/mx-sdk-as/the-crowdfunding-smart-contract/the-crowdfunding-smart-contract-part-2.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.
