> For the complete documentation index, see [llms.txt](https://docs.baas.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.baas.sh/baas-console/contracts/mappings/custom-mappings.md).

# Custom mappings

Declare the mappings of an ABI version yourself, as a JSON template that replaces the automatic set.

Declare your own mappings in **Personalised mappings (JSON)**, under **Expert settings** when you [register a contract](/baas-console/contracts/register-a-contract.md#expert-settings). What you write there becomes the whole set for that ABI version, so carry over the [automatic mappings](/baas-console/contracts/mappings/automatic-mappings.md) you want to keep.

In this guide, a carbon-credit contract tracks a balance per user and per project, from an event that names both.

## The template

The template is a JSON object, one entry per mapping, named by its data key:

```json
{
  "co2Balance": {
    "getter": {
      "signature": "balanceOf(uint256 projectId, address account)",
      "arguments": { "projectId": "key", "account": "wallet" }
    },
    "events": [
      {
        "signature": "CO2Transferred(address sender, address recipient, uint256 projectId, uint256 amount)",
        "selectors": {
          "wallet": { "out": ["sender"], "in": ["recipient"] },
          "key": "projectId"
        },
        "values": ["projectId", "amount"]
      }
    ]
  }
}
```

It reads as a sentence: when `CO2Transferred` fires, call `balanceOf` for the sender and the recipient, on the project the event names, and store each result under `co2Balance`.

A data key is an identifier: it starts with a letter or an underscore, then continues with letters, digits, or underscores. A few reserved names, such as `constructor` and `prototype`, are refused.

## The getter

`signature` names the function to call, with the parameter names of your ABI, as in `balanceOf(uint256 projectId, address account)`. The function has to be read-only, `view` or `pure`, and return at least one value. If it returns several values, they are stored together as a list.

`arguments` gives each parameter its role:

* `wallet` receives the address to read for, so the parameter has to be an `address`.
* `key` receives the free dimension, such as a project or a token id, so the parameter has to be a number, an address, or fixed-size bytes.

Bind every parameter exactly once, and use each role at most once. Omit `arguments` entirely for a getter that takes no parameter. The roles you bind decide [the shape](/baas-console/contracts/mappings.md#the-four-shapes) of the value. Our example binds both, so `co2Balance` is kept per wallet and key.

## The events

Each entry names an event and says what to take from it. `selectors.wallet` lists the [paths](#paths) of the addresses to read, grouped by role:

| Role      | The address it names                                                                                |
| --------- | --------------------------------------------------------------------------------------------------- |
| `out`     | The side a resource leaves                                                                          |
| `in`      | The side it reaches                                                                                 |
| `related` | An address the event concerns without taking a side, such as the delegate in `DelegateVotesChanged` |

* `selectors.key` gives the path of the free dimension, whose type has to match the getter parameter it feeds.
* `values` picks what to show from the event in the [transaction feed](/baas-console/contracts/explorer.md#transactions). It changes nothing about the value stored.

Give the event exactly the roles its getter expects: with a getter that takes no parameter, declare no selectors at all.

{% hint style="info" %}
**Directions describe the wallet's role.** They label the event on a user's page, and every declared address is read the same way. When two mappings watch one event, a path keeps the same direction in both.
{% endhint %}

An empty `events` list declares a value that BaaS reads once, when you register the deployment, and never refreshes. Use it for a constant, such as a token's name.

With no event to supply a wallet or a key, the getter has to take no parameter, and its read must succeed when you register the deployment.

## Paths

A path points at a piece of an event's data:

* A parameter name: `recipient`.
* A tuple member, with a dot: `order.owner`.
* Every entry of an array, with `.*`: `recipients.*`.

Each `.*` unfolds an array, and a path can hold one at most. A path cannot serve as both a wallet and the key.

Within one event, the wallets and the key cannot both unfold. Pair unfolded wallets with a scalar key, or scalar wallets with an unfolded key. The ERC-1155 `balances` mapping takes the second shape on `TransferBatch`: its wallets stay scalar while the array of token ids unfolds into one key per entry.

Two things cannot be mapped: an anonymous event, and an indexed parameter of a dynamic type, which carries only its hash.

## Limits

| What                       | Limit                       |
| -------------------------- | --------------------------- |
| Mappings per ABI version   | 64                          |
| Events per mapping         | 32                          |
| Value paths per event      | 32                          |
| Wallet paths per direction | 16                          |
| Data key                   | 100 characters              |
| Path                       | 16 segments, 256 characters |

## Next

* [Automatic mappings](/baas-console/contracts/mappings/automatic-mappings.md): the set your template replaces.
* [Custom units](/baas-console/contracts/units/custom-units.md): the same kind of template, for decimals.
* [Mappings](/baas-console/contracts/mappings.md): how the values you declare here are kept current.
