This page covers everything from scaffolding the project to deploying the `InclusiveDeFi` contract on Rootstock Testnet. By the end of this page, you will have a live contract address on Chain ID 31 that the relay server can interact with.

:::note
`InclusiveDeFi` is just a name of the contract; developers can change it as needed.
:::

## Prerequisites

Before starting, ensure you have the following:

- Node.js v18+
- `npm` or `pnpm`
- A funded Rootstock Testnet wallet. Get tRBTC from the [Rootstock Testnet Faucet](https://faucet.rootstock.io).
- Basic familiarity with [Solidity](https://soliditylang.org/) and [Hardhat](https://hardhat.org/) is recommended.

## Scaffolding the Project

This project uses Hardhat 3 Beta with the `mocha` + `ethers` toolbox. The folder structure is automatically generated by Hardhat's initializer, so no manual setup is required.

Run the following in your terminal to scaffold a new Hardhat 3 project:

```bash
mkdir ussd-rsk && cd ussd-rsk
npx hardhat --init
```
When prompted, select the TypeScript + mocha + ethers template. Hardhat will generate the base project structure:

```plaintext
ussd-rsk/
├── contracts/           ← Place your Solidity contracts here
├── ignition/
│   └── modules/         ← Hardhat Ignition deployment modules
├── test/                ← Mocha test files
├── hardhat.config.ts    ← Network and compiler configuration
├── tsconfig.json
└── package.json
```

After scaffolding, install dependencies:

```bash
npm install
```

Then add the additional runtime dependencies needed for the relay server:

```bash
npm install express dotenv
```
```bash
npm install --save-dev @types/express tsx
```
```bash
npm install --save-dev @nomicfoundation/hardhat-toolbox-mocha-ethers
```

Your final `package.json` should look like this:

```json
{
  "name": "ussd-rsk",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start-bridge": "tsx index.ts"
  },
  "dependencies": {
    "dotenv": "^17.3.1",
    "express": "^5.2.1"
  },
  "devDependencies": {
    "@nomicfoundation/hardhat-toolbox-mocha-ethers": "^3.0.3",
    "@types/express": "^5.0.6",
    "@types/node": "^22.19.11",
    "chai": "^5.1.2",
    "ethers": "^6.16.0",
    "hardhat": "^3.1.11",
    "tsx": "^4.21.0",
    "typescript": "~5.8.0"
  }
}
```

## Environment Variables

Add a `.env` file to the project root with the following variables:

```dotenv
PRIVATE_KEY=your_relayer_wallet_private_key_here
RSK_TESTNET_RPC=https://rpc.rootstock.io/
```

:::warning
Never commit `.env` to version control. It contains the relayer private key.
:::

Add `.env` to your `.gitignore` immediately:

```gitignore
node_modules/
dist/
artifacts/
cache/
.env
```

## Hardhat Configuration

Update `hardhat.config.ts` to include Rootstock(RSK) Testnet as a named HTTP network:

```typescript



dotenv.config();

export default defineConfig({
  plugins: [hardhatToolboxMochaEthersPlugin],

  solidity: {
    profiles: {
      default: {
        version: "0.8.28",
      },
      production: {
        version: "0.8.28",
        settings: {
          optimizer: { enabled: true, runs: 200 },
        },
      },
    },
  },

  networks: {
    // Local simulated networks for testing
    hardhatMainnet: {
      type: "edr-simulated",
      chainType: "l1",
    },
    hardhatOp: {
      type: "edr-simulated",
      chainType: "op",
    },

    // RSK Testnet - Chain ID 31
    rskTestnet: {
      type: "http",
      chainType: "l1",
      url: "https://rpc.rootstock.io/",
      chainId: 31,
      accounts: [process.env.PRIVATE_KEY!],
    },
  },
});
```

:::note
Rootstock is fully EVM-compatible; therefore, set `chainType: "l1"`. The public node at `https://rpc.rootstock.io/` is rate-limited and intended for development only. Use a dedicated RPC endpoint for production environments.
:::

## The Smart Contract

Create `contracts/InclusiveDeFi.sol`, which serves as the on-chain core of the system. It maintains internal balance and loan state for all users who interact through the relay:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract InclusiveDeFi {
    mapping(address => uint256) public balances;
    mapping(address => uint256) public loans;

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event LoanIssued(address indexed user, uint256 amount);

    // P2P Transfer - moves balance between two internal accounts
    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        balances[to] += amount;
        emit Transfer(msg.sender, to, amount);
    }

    // Micro-Loan - issues 0.01 tRBTC, one active loan per address
    function applyForLoan() public {
        require(loans[msg.sender] == 0, "Existing loan active");
        uint256 loanAmount = 0.01 ether;
        loans[msg.sender] = loanAmount;
        balances[msg.sender] += loanAmount;
        emit LoanIssued(msg.sender, loanAmount);
    }

    // Read - returns internal balance for any address
    function getBalance(address user) public view returns (uint256) {
        return balances[user];
    }

    // Deposit tRBTC into the contract (used for seeding and testing)
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }
}
```

### Contract Design Notes

The contract uses internal accounting and does not implement ERC-20 or hold externally transferred tokens. All balances tracked in `getBalance()` are denominated in wei (tRBTC) and represent funds that have been deposited via `deposit()` or credited via `applyForLoan()`.

The `transfer()` function moves value between two entries in the `getBalance()` mapping. It does not send native tRBTC but updates internal records. This is intentional, as it allows the relayer to call `transfer()` on behalf of any user without requiring the user to hold tRBTC for gas.

:::note
`applyForLoan()` credits the loan amount to the caller's `getBalance()` entry without requiring collateral. This is suitable for a demo only a production system must add a repayment flow along with either collateral requirements or a credit-scoring oracle before enabling this feature.
:::

## Ignition Deployment Module

Create `ignition/modules/InclusiveDeFi.ts` as the Hardhat Ignition module that describes how the contract should be deployed:

```typescript

const InclusiveDeFiModule = buildModule("InclusiveDeFiModule", (m) => {
  const inclusiveDeFi = m.contract("InclusiveDeFi");
  return { inclusiveDeFi };
});

export default InclusiveDeFiModule;
```

## Deploying to Rootstock Testnet

Run the following command to deploy:

```bash
npx hardhat ignition deploy --network rskTestnet ignition/modules/InclusiveDeFi.ts
```

On success, Hardhat Ignition outputs the deployed contract address and writes deployment artifacts to `ignition/deployments/chain-31/`:

```plaintext
Deployed Addresses
InclusiveDeFiModule#InclusiveDeFi: 0xYourDeployedContractAddress
```

The `deployed_addresses.json` file under `ignition/deployments/chain-31/` will also record this address for future reference.

:::note
Copy this contract address. You will need to paste it into `CONTRACT_ADDRESS` in `index.ts` before starting the relay server.
:::

## Verifying the Deployment

To verify your deployment, search for your contract address on the [Rootstock Testnet Explorer](https://explorer.testnet.rootstock.io) to view the transaction, bytecode, and ABI. You can also inspect `ignition/deployments/chain-31/journal.jsonl`, a successful deployment entry ends with:

```json
{
  "futureId": "InclusiveDeFiModule#InclusiveDeFi",
  "result": {
    "address": "0xYourDeployedContractAddress",
    "type": "SUCCESS"
  },
  "type": "DEPLOYMENT_EXECUTION_STATE_COMPLETE"
}
```

## Next Steps
Once your contract is deployed to Rootstock Testnet, follow the [Relay Server & Gateway Integration](../relay-server) guide to build the USSD bridge for feature phone users.