# Contract Development

{% hint style="info" %}
Before getting started, you can apply for TestNet **ONG** tokens that will be used for invoking any contracts you deploy over at the [**faucet here**](https://developer.ont.io/).
{% endhint %}

Now we will demonstrate the full process of contract development, deployment and testing using Hardhat.

## Set up Environment

* Install [nodejs](https://nodejs.org/en/)
* Install [Hardhat](https://hardhat.org/getting-started/)

## Contract Design

### **Contract Logic**

The contract we use as an example here is for sending red packets, which is used when users send crypto assets as gifts. The core functions are:

* Send red packets
* Receive red packets

Before sending red packets, the user need to determine the amount of tokens to be sent and the number of red packets. For instance, 100 tokens will be sent in 10 red packets (to 10 different wallets). For ease of understanding, each red packet contains the same amount, i.e., each contains 10 tokens.

Consequently, we define the data structure:

```javascript
EIP20Interface public token; // support token address
uint public nextPacketId; // the next redpacket ID

// packetId -> Packet, store all the redpacket
mapping(uint => Packet) public packets;

//packetId -> address -> bool,  store receive redpacket record
mapping(uint => mapping(address => bool)) public receiveRecords;

struct Packet {
    uint[] assetAmounts;// Number of tokens per copy
    uint receivedIndex; // Number of red packets received
}
```

### **Define Contract Events**

When executing the contract, we can trace the process by adding events.

Here we design two events:

1. When the user send a red packet, the contract generates an ID for the red packets, which will be sent through this event notification:

```javascript
event SendRedPacket(uint packetId, uint amount); 
```

2\. When a user receives a red packet, this event notification is sent to record the ID and token amount of the received red packet:

```javascript
event ReceiveRedPacket(uint packetId, uint amount);
```

### **Define Functions**

**`sendRedPacket`**

Sends red packets. Any system is able to call the function and send certain amount of tokens to the contract address. Other addresses can receive red packets from this contract address.

{% hint style="info" %}
**Note:** Before invoking this function, the contract has to be authorized to transfer tokens from users' addresses. To do so, call the `approve` method of the token first.
{% endhint %}

```javascript
function sendRedPacket(uint amount, uint packetNum) public payable returns (uint) {
    require(amount >= packetNum, "amount >= packetNum");
    require(packetNum > 0 && packetNum < 100, "packetNum>0 && packetNum < 100");
    uint before = token.universalBalanceOf(address(this));
    token.universalTransferFrom(address(msg.sender), address(this), amount);
    uint afterValue = token.universalBalanceOf(address(this));
    uint delta = afterValue - before;
    uint id = nextPacketId;
    uint[] memory assetAmounts = new uint[](packetNum);
    for (uint i = 0; i < packetNum; i++) {
        assetAmounts[i] = delta / packetNum;
    }
    packets[id] = Packet({assetAmounts : assetAmounts, receivedIndex : 0});
    nextPacketId = id + 1;
    emit SendRedPacket(id, amount);
    return id;
}
```

**`receivePacket`**

Receives red packets. Any address can call this function by red packet ID to receive a red packet, meaning that you need to specify which one to receive.

```javascript
function receivePacket(uint packetId) public payable returns (bool) {
    require(packetId < nextPacketId, "not the redpacket");
    Packet memory p = packets[packetId];
    if (p.assetAmounts.length < 1) {
        return false;
    }
    require(p.receivedIndex < p.assetAmounts.length - 1, "It's over");
    require(receiveRecords[packetId][address(msg.sender)] == false, "has received");
    p.receivedIndex = p.receivedIndex + 1;
    bool res = token.universalTransfer(msg.sender, p.assetAmounts[p.receivedIndex]);
    require(res, "token transfer failed");
    packets[packetId] = p;
    receiveRecords[packetId][address(msg.sender)] == true;
    emit ReceiveRedPacket(packetId, p.assetAmounts[p.receivedIndex]);
    return true;
}
```

View the full code [here](https://github.com/ontio/ontology/blob/master/docs/specifications/evm_refernce/contract-demo/hardhatdemo/contracts/Redpacket.sol).

## Compile and Test Contract using Hardhat

### **Create a Hardhat Project**

```bash
mkdir hardhatdemo
cd hardhatdemo
npm init
npm install --save-dev hardhat
npx hardhat
```

### **Configure hardhat.config**

Include TestNet node information:

```javascript
module.exports = {
    defaultNetwork: "ontology_testnet",
    networks: {
        hardhat: {},
        ontology_testnet: {
            url: "http://polaris2.ont.io:20339",
            chainId: 5851,
            gasPrice:2500000000000,
            gas:2000000,
            timeout:10000000,
            accounts: ["your private key1","your private key2"]
        }
    },
    solidity: {
        version: "0.8.0",
        settings: {
            optimizer: {
                enabled: true,
                runs: 200
            }
        }
    },
};
```

`accounts` field takes the array of selected private key. There should be enough ONG balance in the corresponding address to pay for transactions. You can apply for TestNet ONG [here](https://developer.ont.io/).

### **File Preparation**

Add the contract file in the `contracts` folder. To support ERC-20 token transfer, we also need `EIP20Interface.sol`, `UniversalERC20.sol`, and `TokenDemo.sol` which you can download from [here](https://github.com/ontio/ontology/blob/master/docs/specifications/evm_refernce/contract-demo/hardhatdemo/contracts).

### **Include Code in the test Folder**

```javascript
describe("RedPacket", function () {
    let tokenDemo, redPacket, owner, acct1, assetAmount, packetAmount;
    beforeEach(async function () {
        const TokenDemo = await ethers.getContractFactory("TokenDemo");
        tokenDemo = await TokenDemo.deploy(10000000, "L Token", 18, "LT");
        await tokenDemo.deployed();
        const RedPacket = await ethers.getContractFactory("RedPacket");
        redPacket = await RedPacket.deploy(tokenDemo.address);
        await redPacket.deployed();
        [owner, acct1] = await ethers.getSigners();
        assetAmount = 1000;
        packetAmount = 10;
    });
    it("token", async function () {
        expect(await redPacket.token()).to.equal(tokenDemo.address);
    });
    it("sendRedPacket", async function () {
        const approveTx = await tokenDemo.approve(redPacket.address, assetAmount);
        await approveTx.wait();

        const sendRedPacketTx = await redPacket.sendRedPacket(assetAmount, packetAmount);
        await sendRedPacketTx.wait();
        let balance = await tokenDemo.balanceOf(redPacket.address);
        expect(balance.toString()).to.equal(assetAmount.toString());

        res = await redPacket.nextPacketId();
        expect(res.toString()).to.equal("1");

        await redPacket.connect(acct1).receivePacket(0);
        balance = await tokenDemo.balanceOf(acct1.address);
        expect(balance.toString()).to.equal((assetAmount / packetAmount).toString());
    });
});
```

### **Compile Contract**

Run this command in the root directory to compile the contract.

```bash
$ npx hardhat compile
Compiling 5 files with 0.8.0
Compilation finished successfully
```

Then the following folders are generated.

```bash
.
├── artifacts
├── cache
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test
```

### **Test Contract**

```bash
npx hardhat test
```

You will get the following result:

```bash
sss@sss hardhatdemo % npx hardhat test
  RedPacket
    ✓ token
    ✓ sendRedPacket (16159ms)


  2 passing (41s)
```

{% hint style="info" %}
You can refer to the **Ethereum Web3 API** by following the link below.
{% endhint %}

{% content-ref url="/pages/-MiKeP3mdf5O969ZxyCs" %}
[Web3 API](/developer-tools/api/eth-web3-api.md)
{% endcontent-ref %}


---

# 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.ont.io/guides-and-tutorials/development-guides/smart-contract-dev/evm-contract/contract-development.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.
