> 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/units/custom-units.md).

# Custom units

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

Declare your own units in **Personalised units (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 units](/baas-console/contracts/units/automatic-units.md) you want to keep.

## The template

A template has three groups, all of them required: the units you define, then the functions and events where they apply.

```json
{
  "definitions": {
    "token": {
      "type": "dynamic",
      "target": { "type": "self" },
      "getter": "decimals()"
    }
  },
  "functions": [
    {
      "signature": "transfer(address recipient, uint256 amount)",
      "inputs": [{ "path": "amount", "unit": "token" }]
    }
  ],
  "events": []
}
```

It reads as a sentence: `token` takes its decimals from `decimals()` on the contract itself, and the amount of `transfer` is measured in `token`.

A unit name 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.

Signatures carry the parameter names of your ABI, as the `transfer` entry above does.

## Definitions

Each entry names a unit and says where its decimals come from.

`static` carries them in the template, for a contract that exposes none:

```json
"credit": { "type": "static", "decimals": 6 }
```

`dynamic` reads them from a getter, either on the deployment itself or on another contract:

```json
"shares": {
  "type": "dynamic",
  "target": { "type": "self" },
  "getter": "decimals()"
},
"asset": {
  "type": "dynamic",
  "target": { "type": "external", "getter": "asset()" },
  "getter": "decimals()"
}
```

An external target names a getter of your own contract that takes no argument and returns one address. BaaS calls it, then reads the decimals on the address it gives back.

Every getter you declare on your own contract has to be read-only, `view` or `pure`. A decimals getter, wherever it runs, returns a single unsigned integer.

## Bindings

A binding says that one value is measured in one unit. Where you declare it depends on where that value sits: `inputs` for a function's arguments, `outputs` for what it returns, and `values` for an event's parameters. A function can declare `inputs`, `outputs`, or both.

All the bindings of one function, or of one event, go in a single entry, so each signature appears once. Never write an empty list. Leave out an unused `inputs` or `outputs`, and leave out an event entry that binds nothing.

Here, an `events` entry measures the amount a `Transfer` carries in `token`:

```json
{
  "signature": "Transfer(address from, address to, uint256 value)",
  "values": [{ "path": "value", "unit": "token" }]
}
```

{% hint style="info" %}
**One unit per value.** Two bindings cannot claim the same value, and nothing is settled by order of declaration.
{% endhint %}

## Paths

A path names a value inside the function or event:

* a parameter name: `amount`
* a position, when the ABI leaves it unnamed, as function outputs often are: `0`
* a tuple member, after a dot: `order.amount`
* every entry of an array, with `.*`: `amounts.*`

It has to land on a whole number, since that is what decimals scale. And in an event, an indexed parameter of a dynamic type only carries its hash, so no path can use it.

## Parameterized units

A getter that takes an argument, such as a token id, gives each one its own decimals. Reference the unit by name, then map each of the getter's parameters to where its value comes from:

```json
{
  "definitions": {
    "multiToken": {
      "type": "dynamic",
      "target": { "type": "self" },
      "getter": "decimals(uint256 id)"
    }
  },
  "functions": [
    {
      "signature": "transfer(address to, uint256 tokenId, uint256 amount)",
      "inputs": [
        {
          "path": "amount",
          "unit": { "name": "multiToken", "arguments": { "id": "tokenId" } }
        }
      ]
    }
  ],
  "events": []
}
```

Every getter parameter needs a source, and a source is itself a path: `tokenId` here. It must sit in the function's own inputs, or in the event's parameters, and carry exactly the type its parameter expects. An output never provides one.

A source never carries a unit of its own. It either names a single value or unfolds exactly like the bound path, so `amounts.*` pairs with `ids.*`, one id per amount.

The two forms are not interchangeable: a unit whose getter takes no argument is referenced by name alone, and a parameterized one always with its arguments.

## Limits

| What                                          | Limit                       |
| --------------------------------------------- | --------------------------- |
| Definitions per ABI version                   | 32                          |
| Functions per ABI version                     | 128                         |
| Events per ABI version                        | 128                         |
| Bindings per `inputs`, `outputs`, or `values` | 32                          |
| Getter arguments                              | 8                           |
| Unit name                                     | 64 characters               |
| Decimals                                      | 0 to 80                     |
| Path                                          | 16 segments, 256 characters |

## Next

* [Automatic units](/baas-console/contracts/units/automatic-units.md): the set your template replaces.
* [Custom mappings](/baas-console/contracts/mappings/custom-mappings.md): the same kind of template, for stored values.
* [Units](/baas-console/contracts/units.md): where units appear, and when decimals are read.
