# About Firm

Firm is building the backbone of internet-native companies.

<figure><img src="/files/13pkGgMzkSzgCzjK75fM" alt=""><figcaption></figcaption></figure>

Firm builds software and legal infrastructure so that companies can be **primarily digital.** Primarily digital means that the company’s ‘canonical representation’ is a series of smart contracts, even if a legal structure representing the company exists to mirror the digital structure.&#x20;

We believe corporations to be a fantastic organization form which is behind some of humanity's most incredible feats but at the same time in need of an upgrade.

## Vision

The founders, operators, and investors of a company are abstracted away from most legal and bureaucratic work. The vision is that people interact with the digital product and perform actions on-chain while legal compliance is **automated in the background.**

By relying on smart contracts, a lot of operations and transactions in companies are performed autonomously which brings incredible **efficiency** (eg. fundraising from thousands of small investors is as costly as fundraising from two) while at the same time providing **transparency** (can see the real state of the company on-chain) and **security** (everyone’s ‘power’ is explicit and can be limited).


# Summary

<figure><img src="/files/ZKbpKlK930kRnsoo75ux" alt=""><figcaption></figcaption></figure>

Firm protocol is our interpretation of what the software core of internet-native companies should be. The protocol is non-custodial and allows founders to create and run a company whose basic rules and rights are controlled and enforced with code.

Firm protocol is open source and licensed under GPL-3.0. You can check the repo here: [firm-org/firm-protocol](https://github.com/firm-org/firm-protocol)

Companies running on Firm have stronger guarantees and tighter controls than fiat companies. A fiat company (a company with a legal core) has to rely on the threat of post-hoc legal repercussions for misconduct and breaches of trust. Internet-native companies can be very explicit about how power is delegated all the way from shareholders and companies are inherently transparent due to the fact it runs on a public blockchain.

At the same time, as Firm companies can also opt into having an off-protocol legal entity (bringing the same level of protections of a fiat company), and the nature of how companies are typically formed, we find ourselves in an environment which isn’t as adversarial as a DAO (e.g. a DAO that controls a large DeFi protocol needs to be designed in a highly adversarial way).

It is with these principles that Firm protocol has been designed in a way to always respect an explicit delegation of power from shareholders but enabling a sane operating model which companies need to be effective.

Firm protocol v1 is a system of smart contracts which revolves around two main feature groups:

* **Captable and basic corporate governance:** the captable of the company is fully on-chain and shares are represented with fungible tokens for different classes of stock. Shareholders have the right to elect the board of directors on-chain (resulting in the addition/removal from the main company multisig).
* **Hustle-free use of funds with on-chain guarantees:** in the same way that shareholders delegate their authority into the board, the board is able to delegate partial spending ability to the people running the daily operations of the company. This authority delegation pattern is recursively applied so that bucketed spending authority can go down to the lowest level of the company (e.g. a junior engineer can have a budget for out of pocket technical expenses, with an implicit authority delegation all the way from shareholders)


# Architecture

<figure><img src="/files/xVIbOhaj7n3tF2DjOPyP" alt=""><figcaption></figcaption></figure>

In terms of architecture, a [Safe](https://safe.global) is always at the core and acts as both the corporate crypto asset account for the company and the authority to trigger most corporate actions. Safe signers are always the legal board members of the company and therefore signatures to approve Safe actions can be assumed to be legal signed board consents.

Building on Safe has several important advantages:

* **From a security perspective:** All funds and most valuable access control rights stay in a highly battle tested contract which current holds tens of billions of dollars. Firm is used by plugging modules from the protocol into the Safe, which allows to perform actions based on arbitrary logic instead of multisig transactions.
* **From a user experience and anti lock-in perspective**: Users are always able to just use their Safe directly via all of the available interfaces and it’s compatible with other finance products built on top of Safe. Also, if a company wants to stop using Firm, it’s as easy as removing a couple of modules from their Safe and they will be left with a perfectly working vanilla Safe.

Firm protocol is built with a series of modules that can be added to any Safe. They have been built with these principles in mind:

* **Simple primitives, plug in customizability and granularity:** the core of Firm protocol is as simple as it could be, but not less. We have built some reasonable defaults that we think are what most companies will need to operate, but because companies might grow different in their structuring, most critical parts can be customized with optional code (e.g. controlling who can transfer shares).
* **Module independence:** not all modules have to be used, there’s the possibility to just pick some of them. The only shared dependency between all modules is the Safe itself. Even the roles system is opt-in and (although very recommended) it is not enforced that it must be the same between modules.
* **Upgradeability:** companies are rich in state by definition, so we have opted for allowing upgrades controlled by the Safe using proxies. Companies can optionally freeze all future upgrades and stay with their present code, effectively locking themselves.

<figure><img src="/files/HS5Q68bY3LdodII37wRl" alt=""><figcaption></figcaption></figure>


# Main components


# Budget

<figure><img src="/files/cmjazRHQw8g7nM1NJgBm" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/budget/Budget.sol>
{% endhint %}

## Overview

Budget is installed to a Safe as a module and allows the execution of payments bypassing multisig transactions. Authorized entities can directly perform payments using the Safe’s funds if they are within the spending allowance of a specific budget.

Budget uses natural dates (e.g. an allowance can restart the first day of the month) instead of timestamps (e.g. an allowance restarts every 30 \* 24 \* 3600 seconds) for UX purposes as most business dates revolve around schelling points that are unequal amounts of seconds apart (months, quarters, years).

Budget allows spenders of an allowance (which can only be granted by the Safe itself, [more on this below](#creating-allowances)) to create an arbitrary number of sub-allowances from which another set of spenders can spend (and recursively create other sub-allowances as well).

It can be used with a role-based permissioning system (external to this module, see Roles) which allows to authorize groups within the company to spend from a particular allowance (e.g. executives role or operations team role). This enables an easier onboarding/offboarding process (i.e. granting someone one role immediately allows them to spend from all the allowances that the role is the authorized spender).

Budget needs to be set as a module in a Safe in order to function since otherwise it won’t be able to execute transactions through it.

## Lifecycle

### Creating allowances

The allowance is the main data structure/primitive that Budget is built around. They are created with this function:

```solidity
function createAllowance(
        uint256 parentAllowanceId,
        address spender,
        address token,
        uint256 amount,
        EncodedTimeShift recurrency,
        string memory name
) public returns (uint256 allowanceId);
```

Allowances can have any number of sub-allowances, effectively forming a tree of allowances. Top-level allowances are allowances that aren’t a sub-allowance of any other allowance. Only the Safe itself can create top-level allowances; as they aren’t bound by the controls of a parent allowance, they could potentially spend all the Safe’s assets if the allowance parameters allowed so.

The parameters that an allowance has are:

* **Parent allowance ID**: which allowance this allowance belongs to. This will be zero in case of a top-level allowance. It can’t be modified after creation.
* **Spender address**: address of the account or flag for the role that is authorized to trigger payments from the allowance.
* **Token**: address of the token contract or flag for the native asset that this allowance will spend. It can’t be modified after creation and can only be decided for top-level allowances as all sub-allowances below it must use the same token.
* **Amount**: amount of token in its smallest unit (e.g. wei amount for ETH) that can be spent from this allowance per period.
* **Recurrency**: cadence with which the spent amount in the allowance is reset to zero. It can’t be modified after creation.
* **Name**: human readable name for the allowance being created.

#### Recurrency

The recurrency of an allowance is defined with a data structure we call `TimeShift` . A `TimeShift` is comprised of the following two fields:

* **Time unit**: specifies when the amount spent in the allowance will be reset to zero, allowing to spend the full amount of the allowance again. Options:
  * **Daily**: resets every day at midnight UTC.
  * **Weekly**: resets every Monday at midnight UTC (Monday is considered the first day of the week, and can be modified with an offset).
  * **Monthly**: resets the first day of every month at midnight UTC.
  * **Quarterly**: resets the first day of every quarter (Jan 1st, Apr 1st, Jul 1st, Oct 1st) at midnight UTC.
  * **Semiyearly**: resets on Jan 1st and Jul 1st at midnight UTC.
  * **Yearly**: resets on Jan 1st at midnight UTC.
  * Non-time units:
    * **Non-recurrent**: never resets, uses offset value as the date until which the allowance will become inactive. Useful to create time-bounced one-time allowances.
    * **Inherit**: flags that the allowance has the same recurrency as its parent (see more on [Parameter inheritance](#parameter-inheritance))
* **Offset**: delta in seconds to UTC to offset time calculations. This is useful for both handling timezones (e.g. daily allowance which resets at midnight in UTC+2 would have an offset of `+2 * 60 * 60`) and exact moments in time (e.g. an allowance that resets the last day of the month would have an offset of `-24 * 60 * 60`)

The recurrency argument of `Budget.createAllowance(...)` takes a `EncodedTimeShift` value which is a custom type over a `bytes6` value. The first byte is the encoded time unit and the next 5 are a `int40` for the offset. You can see more about the encoding/decoding [here](https://github.com/firm-org/firm-protocol/blob/master/src/budget/TimeShiftLib.sol).

#### Sub-allowances

In order to create a sub-allowance, an account must be allowed to make payments from that particular allowance (it’s address is the spender or has the concrete role). Sub-allowances are created to bucket spending within a larger allowance and grant spending permissions to another set of accounts. The creator of a sub-allowance has full freedom to set any parameters for it (except for the token address which must be the same as its parent’s), but it will always be bound by the limits of its parent.

When spending from a sub-allowance, the amount of the payment is credited not only its own authorized amount, but from the authorized amount of its parent and all ancestors in the chain until getting to the top-level allowance.

Some peculiarities of sub-allowances:

* Their recurrency can be different from the parent’s in both directions. It is possible to have a monthly sub-allowance under both a weekly or yearly parent allowance.
* The amount of a sub-allowance can be greater than the parent, but since spending controls are applied recursively, a sub-allowance will never be able to spend more than any of its ancestors allow
* Since allowances can be paused or disabled temporarily, pausing an allowance will effectively disable spending from its descendants. All ancestor allowances in the chain to its top-level allowances must be enabled for a sub-allowance to be able to spend.

#### Parameter inheritance

Keeping the allowance primitive simple came with a few short-comings (e.g. a single spender, not modifiable recurrency). These are tolerable because most goals can be achieved with sub-allowances (e.g. adding a spender can be done creating a sub-allowance for the same amount).

Because of how Budget tracks spending at the level of each individual allowance, the use of a deep tree of sub-allowances could become expensive gas wise. This is why we allow for inheriting certain parameters from a parent allowance, which stops the sub-allowance from tracking them itself and just relying on them being checked up the chain (multiple levels of recurrency are allowed).

* **Inheriting recurrency:** a sub-allowance can inherit the recurrency of its parent, reseting its spent amount whenever its parent amount is reset. This can be used to create buckets of spending which are tracked using the same time unit (e.g. a yearly general budget which has sub-budgets for different departments).
* **Inheriting amount:** a sub-allowance which inherits the recurrency of its parent can also be set to inherit its amount. This effectively means that the sub-allowance doesn’t keep track of how much it has been spent through it and just uses its parent spent amount. This is useful to authorize additional accounts to spend from a particular allowance while still keeping control over it.

### Executing payments

The authorized spender for an allowance can use `Budget:executePayment(uint256 allowanceId, address to, uint256 amount, string description)` or `Budget:executeMultiPayment(uint256 allowanceId, address[] tos, uint256[] amounts, string description)` to trigger a payment from the Safe to the specified address of an amount if and only if it is within the spending limit.

When performing a payment, the contract will check whether the allowance (and all its ancestors) need to have their spent amount reset according to their recurrency and will calculate when it will reset next.

### Debiting payments

It is possible to use `Budget:debitAllowance(uint256 allowanceId, uint256 amount, bytes description)` to return a certain amount of tokens (e.g. some funds are returned from a payment) and remove that amount from the spent amount for the period.

Any account can debit a payment to a particular allowance if those tokens can be successfully deposited from the account triggering the debit to the Safe.

Debiting payments only has an effect towards the current period and the spent amount for the allowance can never go negative (meaning at no time it is possible to spend from an allowance more than its amount)

### Modifying parameters

Any spender of the parent allowance (or the Safe itself in the case of top-level allowances) is considered an admin to all its sub-allowances and as such, can modify certain parameters:

* **Amount** (`Budget:setAllowanceAmount(uint256 allowanceId, uint256 amount)`): changes the amount of token that can be spent per period. It applies immediately to the current period.
* **State** (`Budget:setAllowanceState(uint256 allowanceId, bool isEnabled)`)**:** enable or disable the allowance (allowances are enabled by default on creation)
* **Spender address** (`Budget.setAllowanceSpender(uint256 allowanceId, address spender)`)**:** changes who can spend from the allowance.
* **Name** (`Budget:setAllowanceName(uint256 allowanceId, string name)`)**:** changes the name of the allowance.

## Extensibility: Budget modules

Building on top of the allowance primitive, Budget can be extended with other smart contract modules to do further programmatic spending. Budget modules are simply smart contracts that get set as the spender of an allowance.

There’s a base [BudgetModule](https://github.com/firm-org/firm-protocol/blob/master/src/budget/modules/BudgetModule.sol) contract that Budget modules can derive from which provides some common utilities for building modules.

Even though there’s nothing forcing this, the way we envision Budget modules built is so that there’s a single instance per Firm organization that can support using the module from different allowances (with access control deferred to the admins of the particular allowance). This allows minimizing the amount of contracts to be deployed while at the same time keeping agency over upgrades to the organization itself.

### LlamaPayStreams module

{% hint style="success" %}
Source cod&#x65;**:** <https://github.com/firm-org/firm-protocol/blob/master/src/budget/modules/streams/LlamaPayStreams.sol>
{% endhint %}

LlamaPayStreams module is a module we have built which allows to manage and fund [LlamaPay](https://llamapay.io) v1 from a Budget allowance.

LlamaPayStreams module allows the admins of an allowance to configure a set of LlamaPay streams from the allowance and set up streamed payments right from Budget. LlamaPayStreams module automatically deposits/withdraws from LlamaPay to fund all the active streams (based on a configurable prepay buffer time)

Due to how LlamaPay v1 works, intermediate [forwarder](https://github.com/firm-org/firm-protocol/blob/master/src/budget/modules/streams/ForwarderLib.sol) contracts are used to manage deposits in LlamaPay separately for each allowance. These are the contracts that appear as payers on LlamaPay.

## Security considerations

### Unbounded allowance recursion

As evident from the above, an infinite chain of sub-allowances can be created. When spending from an allowance at depth `N` , there’s an unbounded recursive function (`_checkAndUpdateAllowanceChain`) which checks and modifies storage for all levels in the allowance chain until it reaches its top-level allowance.

For a Solidity program, this is certainly a fat red flag as things like this can lead to gas griefing attacks or locking the contract making the gas cost to operate something go over the limit. However, there are a few reasons why due to the constraints of this system, this is a non-issue:

* **Anti-griefing**: even though any spender of an allowance can create sub-allowances with custom parameters (and therefore being able to create an ‘infinite’ chain below any allowance in which they are a spender), this doesn’t impact the gas required to interact with its sibling or ancestor sub-allowances.
* **Contract locking due to forcing gas limit:** similarly to the one above, even though it is possible to end up creating a sub-allowance at such depth that it is impossible to execute or debit payments to it, the issue will be localized to those specific allowances the bad actor is creating.
* **Infinite loops:** as the parent of a sub-allowance has to exist when it is created and it cannot be changed, it is impossible to form a loop in which a sub-allowance can have itself in its ancestry.

### Multi-payment via a delegatecall to self from the Safe

The way multi-payments are executed is by triggering a module transaction in the Safe that will make it `delegatecall` back into the implementation contract at `Budget:__safeContext_performMultiTransfer`. When this delegatecall is received, since we are running in the Safe’s context, we can perform the ERC20 transfers directly. This results in considerable gas savings when performing a multi-payment compared to doing multiple individual safe module transactions.

`Budget:__safeContext_performMultiTransfer` is an external function since it needs to be accessible via a `delegatecall`. Using the `onlyForeignContext` modifier we make sure that the call isn’t being executed in the context of a Budget proxy contract nor on its implementation base contract. Since the EIP-1967 upgradeability slot has a value on both instances for Firm contract, but the Safe doesn’t store anything there, we can safely assume that if the value at that slot is zero, we are not running in our context and it might be a Safe.

This is the most gas-efficient way we could think of doing this, however it has two low probability/impact issues:

* Another `delagatecall` transaction in the Safe could write to that slot and we would no longer identify the Safe as a foreign context making multi-payment break for that Budget instance. Since only Safe signers can trigger such a transaction, it would be a self-grief and they could just remove the Budget module from the Safe if they wished for the module to stop working.
* Future Safe versions might start using that slot preventing this feature from working on Safes with that newer version. It would require an upgrade on our end for Budget to be fully compatible with it.


# Roles

<figure><img src="/files/I5PU9Xn0HPsG2PDGnO08" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/roles/Roles.sol>
{% endhint %}

## Overview

Roles is a module which enables optional role-based access control in other core Firm modules. Roles handles the creation and management of roles as well as granting and revoking these roles to different accounts interacting with the organization.

Except for some special roles (more on this under [Special roles](#special-roles)), Roles doesn’t care about the semantic meaning of roles nor it handles what permissions or rights are derived from having a certain role.

Roles can be thought of as the directory for who has what role and handling the logic for performing administrative functions over roles. Other components of the system can have certain permissions in them assigned to a particular role, and will use Roles to check whether a particular user has a role or not when performing access control for an action.

As such, the Roles module is not a Safe module and cannot perform any actions in the Safe directly. It is instead a module that other Firm modules will use as a centralized directory for keeping track and managing user roles.

## Motivations and limitations

Roles, which was heavily inspired by [Solmate’s](https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol) and [OpenZeppelin’s](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol) implementations, has been built to optimize for setups in which many users might have many roles which might be changing.

As an initial implementation constraint and optimization, Roles supports a total of 256 roles (253 user-defined roles, as there are 3 special roles out of the box). It is optimized for bulk edits of a particular user’s roles with minimal gas impact (constant storage gas costs).

## Key concepts

### Role admins

All roles (expect for [special roles](#special-roles) ‘Root’ and ‘Safe owner’) have a series of other roles that can act as admins to the role. If a user holds a role A which is a role admin of another role B, they will be able to grant or revoke that role B. Also, even if they don’t explicitly hold the role, a user that has a role which is an admin to another role, Roles will consider the user to have the role.

Example setup:

```
- There are two user-defined roles (1 and 2):
    - Role 1 admins: Just root role (assigned to Safe initially)
    - Role 2 admins: Root role and Role 1
- We have two external users (A and B):
    - User A: explicitly granted Role 1
    - User B: explicitly granted Role 2
    - Safe: has Root role
- Checking externally for who has what role:
		- User A (Holds Role 1 which is an admin to Role 2): has Role 1 and 2
    - User B: has Role 2
    - Safe (Holds Root role which is an admin to roles 1 and 2): has Role 1 and 2
- For assigning roles to a new user C
	  - Role 1 (Only Root role is admin): Safe can grant/revoke
		- Role 2 (Root role and Role 1 are admins): Safe and user A can grant/revoke
```

### Special roles

There are a few special roles in the system that have some special properties:

#### Root role (Role ID = 0)

Assigned to just the Safe initially, holding the Root role will make the user appear as having all roles and being able to admin all roles even if they haven’t been explicitly granted to them.

Some notes that only apply to the Root role as special security measures:

* The Root role has the special peculiarity that only other accounts holding the Root role can change the admins of the root role.
* Another role could be set as admin of the Root role which would be very dangerous.
* The Root role is the only one that can be set without any admin roles, effectively freezing who has the Root role forever, since not even Root role holders could override.

#### Role manager role (Role ID = 1)

Aside from the Root role exceptions specified above, all other roles’ admins can only be changed by accounts with the Role manager role. This role is also the one required to create a new role.

#### Safe owner role (Role ID = 255)

The Safe owner role is a dynamic role. It cannot be granted or revoked, but instead, an account will appear as having or not having this role depending on whether the account is an owner of the organization’s Safe.

It is possible that more dynamic roles could be introduced, potentially allowing user-defined dynamic roles that can perform an arbitrary call to determine role membership.

## Lifecycle

### Creating roles

An account with the Role manager role can create a new role using `Roles:createRole(bytes32 roleAdmins, string name)`. Roles are assigned incremental IDs up until ID 254 which is the last possible user-defined role.

* **Role admins**: is a bitmap of which roles are admins to the new role (if the value of bit N of the bitmap is a `1`, role N is considered an admin). Holders of those roles will be able to grant and revoke the role. Some considerations:
  * All new roles must have at least one role which admins it.
  * A role can be one of its own role admins or sole admin (accounts with the role will be able to grant or revoke it)
  * Upon role creation, it isn’t checked whether any of the admin roles exist. Therefore it is possible that a role which doesn’t exist yet (and will be created later on) will be the admin for the role. It is not recommended to do so as it could be confusing.

### Granting and revoking roles

Accounts that hold a role which admins a particular role can grant it or revoke it using `Roles:setRole(address *user*, uint8 *roleId*, bool *isGrant*)`.

It is also possible to grant and revoke multiple roles for a particular user in just one call by using `Roles:setRoles(address *user*, uint8[] memory *grantingRoles*, uint8[] memory *revokingRoles*)`. In order for the call to be successful, the actor must have an admin role for all the roles being granted or revoked.

### Modifying role parameters

{% hint style="info" %}
**Root role exception:** modifying parameters for the Root role requires holding a role that admins the Root role
{% endhint %}

Accounts with the Role manager role can perform the following parameter changes in a role:

* `Roles:setRoleAdmins(uint8 *roleId*, bytes32 *roleAdmins)*`: changes which roles are admins to the role
* `Roles:setRoleName(uint8 *roleId*, string memory *name*)`: changes the name of the role (has no on-chain side-effects)

## Access control with RolesAuth

As explained above, Roles only takes care of keeping track of who has what role, but it has no notion of what any particular role does (with the exception of performing admin actions on Roles itself).

External contracts in the system can use the [RolesAuth base contract](https://github.com/firm-org/firm-protocol/blob/master/src/common/RolesAuth.sol) to use roles instead of individual accounts for permission to perform specific actions.

When a contract uses RolesAuth, if an address with a [specific flag format is used](https://github.com/firm-org/firm-protocol/blob/master/src/common/RolesAuth.sol#L8), checking whether the sender is authorized to perform an action will result in checking whether the sender has a role.

The format of this role flag is `0x00...[byte with roleId][01]` (e.g. `0x0000000000000000000000000000000000000301` is a role flag for roleId = 3)

### Role checking considerations

`Roles:hasRole(address user, uint8 roleId)` will return true if at least one of the following is true:

* User explicitly was granted that role.
* User has been granted a role which is an admin to the role (this is only checked with one level of depth).
* User has the root role.
* The role being checked is the Safe owner role (`roleId=255`) and the user is an owner of the Safe.


# Captable

<figure><img src="/files/KBKWZ7oQrHKAu0WuUlZb" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/captable/Captable.sol>
{% endhint %}

## Overview

Captable manages ownership and voting rights in a company. It supports different classes of shares which are different tokens which may have different rights and voting weights. Each share class is represented with a separate ERC20 token. Shares aren’t fungible across classes, although they may be convertible from one class into another.

Given that the goal of Captable is to represent shares of stock of legal companies in a broad array of jurisdictions, it has been built to allow a very high degree of configuration, restrictions and forced actions.

## Key concepts

### Share classes

Captable allows the management of a large number of different classes of stock or different tokens. There’s currently a hard coded limit of 128 classes. Share classes can be completely standalone or may have another class that those shares of this class can convert to.

A separate ERC20 token is created for every class, we call these tokens `EquityToken`. These tokens are controlled by Captable so all issuance and conversions can be managed from a central place. If transfer restrictions allow, these tokens are compatible with different DeFi apps like DEXes or lending markets.

For each class, there are three important figures that Captable keeps track of:

* **Authorized shares:** the total number of shares of this class that can be issued.
* **Convertible shares:** the total number of shares of other classes that can be converted into this class of shares and therefore issued. If no shares have been converted yet, this figure will be the sum of the authorized shares of all classes that convert into a class. If no classes convert into a class, it will be zero.
* **Issued shares:** amount of shares of the class that have been issued and are outstanding. It’s equal to the total supply of the token associated with this class.

One can think of the authorized amount of a class that converts into another one as reserved shares. This gives the certainty to holders of a class that they will always be able to perform the conversion at any time. To allow this, Captable never allows issuing shares if that could result in the inability to convert shares.

### Transfer restrictions

Shareholders can perform transfers of shares by using the standard ERC20 transfer functions in the Equity token contract associated with a particular class. However, before a transfer occurs, the token checks with Captable is checked for whether the transfer is allowed.

Transfers can be restricted in different ways in order for the company to stay compliant. The two types of restrictions are bouncers and controllers. These restrictions only apply for shareholder initiated transfers. If both a bouncer and a controller are checked for a certain transfer, both must allow the transfer for it to go through.

<figure><img src="/files/Pn620gCctLWDlIyZoLHh" alt=""><figcaption></figcaption></figure>

#### Bouncers

A Bouncer is an optional transfer restriction which applies to all token transfers within a certain class. There are a few embedded/default basic bouncers which have been built into the protocol, but any arbitrary logic can be used to determine whether a transfer can go through or not.

The embedded bouncer options are:

* **Allow all**: all transfers are allowed.
* **Deny all**: all transfers are denied and shares of this class are non-transferable.
* **Allow transfer to class holder:** only transfers to other accounts that already own some shares of the class are allowed.
* **Allow transfer to all holders**: only transfers to other accounts that own any shares of any class are allowed.

Any smart contract which complies to the `IBouncer` interface can be used as a bouncer to check for transfer validity. For example, a company may want to curate a custom list of accounts allowed to receive shares depending on whether they have gone through a KYC process.

When choosing an embedded bouncer as the bouncer for a class, an address with a [specific flag format is used](https://github.com/firm-org/firm-protocol/blob/master/src/captable/BouncerChecker.sol#L8). The format of this bouncer flag is: `0x00...[embedded bouncer id][02]` e.g. `0x0000000000000000000000000000000000000102` is embedded bouncer 1 (‘Allow all’)

It is important to note that bouncers aren’t checked when share conversions occur. For example, a company may have a class of shares with a higher voting weight per share which is non-transferable (uses the ‘Deny all’ bouncer), but allows the shareholder to convert those into regular shares if they want to transfer them.

#### Controllers

As opposed to bouncers that apply to all holders within a class, controllers are per-account restrictions. An account which holds shares of a certain class can be attached a controller which will be able to block its transfers based on some arbitrary logic.

Controllers (or account controllers) are used to control a certain account’s transfer based on some logic which is individualized. A controller is also able to forfeit shares from an account based on its logic.

An example controller that we have built is a [vesting controller](https://github.com/firm-org/firm-protocol/blob/master/src/captable/controllers/VestingController.sol). It allows to issue shares to an account but ensure that the account can never transfer shares that they still have not vested. It also allows some authorized account to cancel their vesting and forfeit the shares which didn’t vest.

Differently than with bouncers, controller checks do apply to share conversions. If a controller would have blocked a transfer of a certain amount, it is likely that it would also be blocking a conversion in the same amount (e.g. our own vesting controller works this way).

## Lifecycle

### Creating share classes

New share classes can only be created by the Safe of the organization. They are created with the following function:

```solidity
function createClass(
        string calldata className,
        string calldata ticker,
        uint256 authorized,
        uint32 convertsToClassId,
        uint64 votingWeight,
        IBouncer bouncer
 ) external returns (uint256 classId, EquityToken token)
```

* **Class name/ticker:** metadata for identifying the class. They can’t be modified after creation.
* **Authorized:** number of initially authorized shares for the class. If shares of the class can convert into another class, this class must have a sufficient unissued amount of shares authorized to accommodate for the potential conversion of all authorized shares of this class. This amount can be modified at a later point by the Safe except if the share class has been frozen.
* **Converts to class ID:** ID of the class into which shares of this class can convert. It cannot be modified after creation.
* **Voting weight:** multiplier for token balances when getting voting power. Setting a voting weight of zero makes the share class non-voting.
* **Bouncer:** address of the bouncer contract for the class. It cannot be unspecified, instead one of the embedded bouncer types can be used (see above). The bouncer can be modified at a later point by the Safe except if the share class has been frozen.

When created, the Safe is set as the initial manager for the class. Managers can issue shares and set account controllers in the class.

### Issuing shares

A manager for the class can issue any account any amount of shares so long as the full amount of issued shares is within the authorized amount for the class. Issuing shares always goes through and is not subject to any bouncer or controller restrictions.

The recipient of the issued shares will be minted that amount of tokens in the EquityToken associated with the class.

Direct issuance is done via `Captable:issue(address *account*, uint256 *classId*, uint256 *amount*)`

#### Controlled shares

At the time of issuing shares, managers can set a controller in the receiving account using `Captable:issueAndSetController(address account, uint256 classId, uint256 amount, IAccountController controller, bytes calldata controllerParams) external`.

By doing this, the controller specified will be notified that a new account is under its control and it will now be able to block transfers of that account for this share class. Controllers are very critical and must be set with caution.

An important consideration is that only one controller can be set at the same time for a share class/account pair and the controller controls the entire account. This has two notable side effects:

* If this function is called again setting a different controller, the previously set controller will stop being the controller. This could also be done by a different manager, which would effectively override whatever the first manager did.
* Even if this function is called when issuing one share, the controller will have full control over previously owned shares.

### Class managers

As mentioned in some sections above, there’s a level of privileged permissions on a per share class basis that can be done by class managers. Only the Safe can add or remove managers using `Captable:setManager(uint256 classId, address manager, bool isManager)`

Even though there are critical parameter changes for a class that are solely reserved for the Safe, class managers have a lot of power and it should be granted with extreme caution. It is highly unrecommended (and it should be a red flag) that an externally-owned account is set as a class manager, and in most cases, only other smart contracts should be managers. Managers should be considered a local ‘owner’ for the class and, as such, the same level of scrutiny must be had.

The actions that class managers can perform are:

* **Issue shares:** a class manager can issue any amount of shares (never surpassing the authorized amount) to any account
* **Setting account controller:** a manager in a class can set an account controller for any account. An account controller can block all transfers for that account or forfeit shares.
* **Forcing transfers**: managers can directly force a transfer from any account to any other account. Forced transfers bypass account controller checks.

Note that while the Safe is the sole manager of all share classes by default and has the unique ability to set accounts as managers, if the Safe were to remove itself as a manager for a class, it will no longer be able to perform the actions explained above. After the Safe freezes the class, this will be locked forever and the current set of managers (including the ability to have none or just an automated one) will be locked.

Managers were built as a way to extend the capabilities of Captable and allow further programmability over the captable of the company. Unless for this purpose, companies should probably never add managers to their classes and just have the Safe as the default manager for issuance.

### Transferring shares

#### By shareholder

Shareholders can perform transfers by interacting directly with the Equity token of the specific class they want to transfer. All regular ERC-20 transfer methods plus [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) permits are available to perform transfers.

As explained in the ‘Transfer restrictions’ section above, when performing share transfers, the Equity token will check with Captable whether the transfer is allowed based on its bouncer and the sender’s account controller.

#### Forced transfers

Both the class manager and the controller (on an account basis) can forcibly transfer any account’s shares to any other account, bypassing the bouncer check.

### Converting shares

If a share class can be converted into another class, a holder can decide to perform a conversion of a certain amount of shares at any time (unless a controller for the origin conversion class blocks it) using `Captable:convert(uint256 fromClassId, uint256 amount)`

Conversions cannot be forced by the Safe, account controller or any class managers. A way to implicitly force conversions is to change the bouncer of the class to the ‘Deny all’ bouncer which would force all holders to convert should they want to move their shares.

### Modifying class parameters

The Safe of an organization can perform the following actions to change parameters:

* `Captable:setAuthorized(uint256 classId, uint256 newAuthorized)`: changes the authorized amount of shares in a class. It must be at least the amount of shares currently issued plus the sum of all authorized shares of classes that convert into it.
* `Captable:setBouncer(uint256 classId, IBouncer bouncer)`: sets a new bouncer to control transfer restrictions in the class.
* `Captable:setManager(uint256 classId, address manager, bool isManager)`: changes the manager status for an account.

#### Freezing

The Safe can decide to freeze a class to disable any future changes to any of its parameters. Freezing a class is a non-reversible action. Once a class is frozen the current set of parameters will be kept forever.

Only Safe can use `Captable:freeze(uint256 classId)` to freeze the parameters of a class.

## Checking voting power

Both Captable and EquityToken have been built to be conformant with OpenZeppelin [ERC20Votes](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Votes.sol) (in the case of Captable, just to a [subset of it](https://github.com/firm-org/firm-protocol/blob/master/src/captable/utils/ICaptableVotes.sol)) so they can be used out of the box with [Governor](https://docs.openzeppelin.com/contracts/4.x/api/governance) (OpenZeppelin’s governance contract). ERC20Votes also supports single voting delegation.

Performing checks of voting power against Captable directly will result in checking for what the absolute voting power of a certain account is across all classes of shares, taking into account the different voting weights.

It is also possible to check directly with any EquityToken contracts to get voting power within a class alone. This allows the possibility of having both global voting for some matters while also being able to conduct certain votes in which only shares of a certain class can be voted.

{% hint style="warning" %}
Due to how ERC20Votes works, in order for an account’s votes to be counted and be able to participate in votes, the account must first delegate their votes to someone even if its to itself.
{% endhint %}

## Extensibility

### Custom bouncers

Apart from the embedded bouncers described above, it is possible to set a smart contract as the bouncer with custom logic.

Bouncers must conform to the [`IBouncer`](https://github.com/firm-org/firm-protocol/blob/master/src/captable/bouncers/IBouncer.sol) interface:

```solidity
interface IBouncer {
    function isTransferAllowed(address from, address to, uint256 classId, uint256 amount)
        external
        view
        returns (bool);
}
```

When set, Captable will perform a `staticall` to the bouncer before any transfer initiated by a shareholder. Bouncers can perform any arbitrary checks to determine whether to allow a transfer but must not try to modify storage. In order for the transfer to go through, the bouncer must not revert and return true to `Bouncer.isTransferAllowed`.

It should be possible to reuse a Bouncer across many organizations for checks that can apply across companies. For example, an identity provider may provide a generic bouncer that returns true only if the recipient address has undergone a KYC process with them.

It is possible to obtain which specific company is performing the check with `Captable(msg.sender)` within the bouncer’s context.

### Custom controllers

Any smart contract that conforms to the `IAccountController` interface (also contains the `IBouncer` function to check for transferability) can be set as a controller:

```solidity
abstract contract IAccountController is IBouncer {
    function addAccount(address owner, uint256 classId, uint256 amount, bytes calldata extraParams) external virtual;
}
```

An optional implementation is provided with `AccountController` which is meant to be used as the contract to derive from when building an account controller.

**Vesting controller**

We have implemented a Vesting account controller both as an example of how to build controllers.

### Smart contract managers

As explained through the document, managers must be set with extreme caution due to their critical powers within a share class.

They were designed to allow extensibility by creating smart contracts which can interact with Captable directly.

The entrypoints that a manager has are:

* `Captable:issue` and `Captable:issueAndSetController`: for issuance
* `Captable:setController`: to set account controllers
* `Captable:managerForcedTransfer`: to perform arbitrary transfers

For example, a fundraising smart contract could be written which when set as the manager would be able to mint shares in exchange for an investment in the company. Another example could be automatic stock options execution, which would issue shares to the purchaser when exercising the option.


# Voting

<figure><img src="/files/yqgEI4lMmoRKffBC04Vm" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/voting/Voting.sol>
{% endhint %}

## Overview

Voting is a Safe module which is a thin wrapper over OpenZeppelin’s [Governor](https://docs.openzeppelin.com/contracts/4.x/api/governance). When installed in a Safe, it will be able to execute transactions through the Safe when approved by a token vote.

For Firm it’s been built to work together with Captable and allow shareholders to control some important aspects of the main Safe such as changing its owners (as a way of electing members of the board of directors.)

## Technical implementation

We haven’t implemented any custom logic for Voting which is not in the base OpenZeppelin contracts. The exact configuration chosen for Firm’s Voting in terms of OZ Governor components can be seen in [`OZGovernor.sol`](https://github.com/firm-org/firm-protocol/blob/master/src/voting/OZGovernor.sol).

In summary, the chosen components were:

* **Governor Votes** ([link to OZ docs](https://docs.openzeppelin.com/contracts/4.x/api/governance#GovernorVotes)): as the voting power sourcing component. It checks for voting power and total number of votes on a ERC20Votes token. We had to slightly modify it for Firm with `GovernorCaptableVotes` in order to make it comply with our reduced `ICaptableVotes` interface and make storage compatible with upgradeability.
* **Governor Counting Simple** ([link to OZ docs](https://docs.openzeppelin.com/contracts/4.x/api/governance#GovernorCountingSimple)): as the counting mechanism. It allows for ballots with three options: For, Against and Abstain. It determines that a proposal has been successful if the number of For votes is higher than Against votes and it has passed its quorum threshold if the sum of all For and Abstain votes is greater than the quorum requirement.
* **Governor Votes Quorum Fraction** ([link to OZ docs](https://docs.openzeppelin.com/contracts/4.x/api/governance#GovernorVotesQuorumFraction)): as the quorum calculator component. It defines quorum as a fraction of all the votes that could potentially be casted. We had to slightly modify it to make it compatible with `GovernorCaptableVotes`. We use 10,000 as the base for the quorum numerator (e.g. 2,000 = 20% quorum requirement).
* **Governor Settings** ([link to OZ docs](https://docs.openzeppelin.com/contracts/4.x/api/governance#GovernorSettings)): to allow Voting parameters to be upgradeable using proposals. Note: even though the Safe is the executor for Voting proposals, making settings updates always requires a proposal passing (i.e. the Safe can’t just trigger a change)

The main notable difference from the standard OZ components is that when executing the actions associated to a proposal which has passed, rather than performing the calls directly from the Governor (or another executor like a Timelock), these calls are executed directly from the Safe’s context (using the same technique as we do for Budget’s multipayment execution).

## Voting settings

The following settings are passed to Voting on its initialization:

* **Quorum numerator:** fraction of the total voting power that must cast a For or Abstain vote for the vote to meet quorum.
* **Voting delay:** Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes.
* **Voting period:** Delay (in number of blocks) since voting in the proposal starts until voting ends.
* **Proposal threshold:** The number of votes required in order for a voter to submit a proposal.

Note that all time periods are expressed in numbers of blocks. This allows precise snapshotting of token balances. On Ethereum mainnet, after the POS transition, the amount of time between blocks is constant and predictable at 12 seconds. This will vary depending on the block proposal and consensus mechanism of network used.

As explained in the section above, all these settings can only be changed by a vote. Trying to perform a change through the Safe directly without a successful vote will fail.

## Lifecycle

### Creating a proposal

In order to create a proposal, the following function (from `Governor`) is used:

```solidity
function propose(
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    string memory description
) public virtual override returns (uint256) {
```

It requires the proposer to have enough voting power to meet the proposal threshold.

### Casting votes

Votes can be cast by calling `Voting:castVote(uint256 proposalId, uint8 support`) or `Voting:castVoteWithReason(uint256 proposalId, uint8 support, string reason)`.

The support parameter defines the vote being cast:

* Against: `0`
* For: `1`
* Abstain: `2`

### Proposal execution

After a proposal’s voting period is over, the proposal can be executed if it was successful. A proposal is considered successful if its quorum requirement was met (only For and Abstain votes are counted for this) and there were more For than Against votes.

The proposal can then be executed using the following function in which the original proposal parameters must be passed:

```solidity
function execute(
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    bytes32 descriptionHash
)
```

When a proposal is executed, given that Voting is a module in the Safe, it will execute the proposal actions in the context of the Safe (via a module `delegatecall`)

### Changing parameters

As explained above, only a Voting proposal can change the four Voting parameters.

* **Changing quorum numerator:** voting proposal must call `Voting:updateQuorumNumerator(uint256 newQuorumNumerator)`. The new numerator applies for all new proposals and also proposals which are currently pending (in the middle of their voting delay.)
* **Changing voting delay:** voting proposal must call `Voting:setVotingDelay(uint256 *newVotingDelay)`. The new voting delay only affects new proposals. If there are any pending proposals, those will still have the previous voting delay.
* **Changing voting period:** voting proposal must call `Voting:setVotingPeriod(uint256 newVotingPeriod)`. The new voting period only affects new proposals. If there are any pending or active proposals, those will still have the previous voting period.
* **Changing proposal threshold:** voting proposal must call `Voting:setProposalThreshold(uint256 newProposalThreshold)`. The new threshold applies to new proposals only. If a pending or active proposal was created by an account that no longer meets the threshold after the change, the proposal won’t suffer any changes.


# Semaphore

<figure><img src="/files/OAMU9qK5hIGxXMd1VBvR" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/beautify-readme/src/semaphore/Semaphore.sol>
{% endhint %}

## Overview

Semaphore is an opt-in component of Firm protocol which can be used to limit which actions certain modules can perform within an instantiation of the system.

It was originally designed to balance power between what owners of the Safe can do via multisig transactions (board actions) and which actions only Voting can perform.

It is called Semaphore because its intended way of being used is by balancing permissions of specific action pairs, blocking access to one while allowing access to the other.

It is attached to a Safe by setting it as its guard. This will cause the Safe to check with Semaphore before performing any multisig transaction executed by its owners. Firm module transactions (i.e. those coming from Budget or Voting) bypass this check and must explicitly perform their own Semaphore checks (Voting does if it has a Semaphore attached).

## Lifecycle

### Setting semaphore state

Only the Safe can set the global semaphore state for callers using `Semaphore:setSemaphoreState(address caller, DefaultMode defaultMode, bool allowDelegateCalls, bool allowValueCalls)`

Each caller (i.e. Safe or module performing calls) has a global configuration or semaphore state. It’s comprised of three variables:

* **Default mode:** dictates which is the default authorization state for the caller for all calls which do not have an explicit exception.
* **Allow delegatecall:** some callers, like Safe, may have the ability to perform delegatecalls (which by their nature makes it really hard to know exactly what their sideeffects will be unless the target is known or trusted). This boolean setting acts as a blanket switch for delegatecalls (exceptions do not apply here.)
* **Allow value calls:** some callers may perform calls whose value is greater than zero which will cause the Safe to transfer some amount of the native asset (i.e. ETH on Ethereum). This boolean setting acts as a blanket switch for delegatecalls (exceptions do not apply here.)

### Setting exceptions

Only the Safe can add exceptions to the default mode of operation using `Semaphore:addExceptions(ExceptionInput[] exceptions)`

Exception types:

* **Signature exceptions:** applies exception to all calls with that selector for all targets
* **Target exceptions:** applies exception to all calls to a specific target
* **Signature and target exceptions:** applies exceptions only to the specific signature target pair.

### Checking access control

Semaphore exposes two functions to check whether it allows a certain caller to perform a call to a target.

`Semaphore:canPerform(address *caller*, address *target*, uint256 *value*, bytes calldata *data*, bool *isDelegateCall*)` is used to perform a single check and `Semaphore:canPerformMany(address *caller*, address[] calldata *targets*, uint256[] calldata *values*, bytes[] calldata *calldatas*, bool *isDelegateCall*)` is the optimized version to check several calls for a single caller.

The base contract [SemaphoreAuth](https://github.com/firm-org/firm-protocol/blob/master/src/bases/SemaphoreAuth.sol) is provided for modules that want to perform checks against Semaphore before performing a call.


# Supporting Components


# Firm Modules

<figure><img src="/files/7rb5erGEIUJla6ewFIBM" alt=""><figcaption></figcaption></figure>

## Firm Base

`FirmBase` is the base contract that Firm modules use which includes all dependencies and handles initialization in a standard way.

All modules must call `__init_firmBase` as part of their initialization flow. This method can only be called once and will revert if attempted again (it is used as the implicit guard against re-initialization).

Initialization will set both the Safe and an initial trusted forwarder for meta-txs for the module. It is imperative that these get set to correct and trusted values, given the overarching powers that these two have.

## Safe Module

`SafeModule` is an optional base contract which only modules which are intended to be a Safe module (have the ability to send transactions through the Safe) should use.

`SafeModule` has several internal functions that will allow the module to send transactions through the Safe bypassing Safe owner confirmations.

There’s a special `_moduleExecDelegateCallToSelf` method, which will cause the Safe to call back into the code of the module in order to act from the Safe context. This is useful for performing several actions at the same time as it results in considerable gas savings. However it must be noted how critical this is since it will cause the Safe to execute some arbitrary code, it must be used with extreme caution. It is also recommended to always decorate the callback function where the Safe will enter with the `onlyForeignContext` modifier.

## Safe Aware

`SafeAware` is the base-most contract. It is responsible for storing at fetching a reference to the address of the Safe.

Other base contracts derive from it so they can perform access control depending on whether the caller is the Safe. The `onlySafe` modifier can also be used by modules to protect functions which are only intended to be called by the Safe.

## EIP-1967 upgradeable proxies

Firm uses the [EIP-1967 standard](https://eips.ethereum.org/EIPS/eip-1967) for upgradeability. In our proxies, the implementation address is always stored in the slot defined by EIP-1967.

The implementation contracts themselves are responsible for making changes to that value which will result in an upgrade for the proxy. Since the proxy itself has no logic to handle upgrades, if a proxy is upgraded to a certain implementation address which has no upgrade logic, it will cease being upgradeable.

## ERC-2771 context

Firm modules support the [ERC-2771 standard](https://eips.ethereum.org/EIPS/eip-2771) to allow for meta-transactions through a compliant relayer.

All modules in initialization should allow for setting an initial trusted forwarder or relay. Once set, the module will accept meta-transactions relayed through that relayer.

The Safe can edit this at any time by adding or removing extra relayers. This is an extremely critical operation, as a rogue relayer will allow impersonating almost all accounts for access control. One exception is the `onlySafe` check, which is always performed on `msg.sender` and doesn’t support meta-transactions.


# Firm Factory

<figure><img src="/files/7G2oXmRVuUv7BF6ndyoG" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/factory/FirmFactory.sol>
{% endhint %}

## Overview

Firm Factory manages the creation and setup of new Firm organizations. The has been built to allow a one-transaction setup after which the organization can be fully functional in the desired initial state.

For v1, Firm Factory allows creating a new organization with one instance of all the modules that Firm protocol v1 offers.

Since at the core of every Firm there’s a Safe which represents the board of directors of the company, either Firm is installed into a pre-existing Safe (via a Safe multisig transaction) or Firm Factory creates one for the user by using Safe’s own factory.

## Entrypoints

### Create Firm

The `createFirm()` entry point allows users to create a new organization from scratch with a brand new Safe and one instance of all the modules that Firm protocol v1 offers. This entry point takes in two parameters, a `SafeConfig` and a `FirmConfig`:

* The `SafeConfig` is used to configure the Safe which acts as the board of directors of the organization. It contains details such as the owners of the Safe and the number of confirmations required for Safe transactions.
* The `FirmConfig` is used to configure the Firm-specific modules. It contains details such as the Budget, Roles, Captable and Voting configuration of the organization (Budget and Roles are compulsory, but Captable and Voting are optional based on the `withCaptableAndVoting` boolean).
  * The `budgetConfig` parameter contains an array of `AllowanceCreationInput` objects which specify the initial allowances to be created for the organization.
  * The `rolesConfig` parameter contains an array of `RoleCreationInput` objects which specify the roles to be created along with their associated grantees.
  * The `captableConfig` parameter contains the name of the captable and an array of `ClassCreationInput` and `ShareIssuanceInput` objects which specify the details of the Captable classes and share issuances.
  * Finally, the `votingConfig` parameter contains details about the voting settings for the organization.

### Setup Firm

The `setupFirm()` entry point allows existing Safes to install the modules associated with Firm protocol v1. This entry point takes in a `FirmConfig` as its configuration parameter, and is used to configure the Firm-specific modules.

In order to use it, existing Safe’s must perform a `delegatecall` to `setupFirm()`. It is the same function which gets internally used in a new Safe when `createFirm` is used

## Appendix: modules factory

`UpgradeableModuleProxyFactory` is used for keeping a versioning registry of different modules, deploying them, and initializing proxies.

The `register()` function allows the owner of the system to register an implementation of a module with a specific version number. This function can only be called by the owner of the contract, and it ensures that an implementation of the same version is not registered twice.

The `deployUpgradeableModule()` function allows users to deploy a proxy for a given module. This function has two overloads; one which takes in a `moduleId` and version number and the other which takes in the address of the implementation contract directly. When specifying the version number, there’s a special flag `type(uint256).max` which can be passed to request the latest available version the module.


# Firm Relayer

<figure><img src="/files/scVAn1UbHGWy9z4ZN7x1" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Source code: <https://github.com/firm-org/firm-protocol/blob/master/src/metatx/FirmRelayer.sol>
{% endhint %}

## Overview

Firm Relayer is a [ERC-2771](https://eips.ethereum.org/EIPS/eip-2771) meta-transactions relayer. Contracts that trust the relayer will accept calls as if they came from other accounts. The relayer authenticates requests to relay a batch of calls that are executed atomically by checking an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signature by the originating account.

Given its critical nature when it is trusted by other contracts, Firm Relayer is not upgradeable. Contracts, in Firm’s case, modules, are able to add or remove relayers at any moment.

On Firm protocol, via Firm Factory, all modules which are initialized start trusting an instance of Firm Relayer.

{% hint style="info" %}
**Note**: Firm Relayer will be discontinued as account abstraction EIPs (namely [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337)) are adopted. It was built to allow gasless/sponsored transactions and secure batched actions which are better fit at the wallet level.
{% endhint %}

{% hint style="info" %}
**Note**: Firm Relayer might be discontinued as account abstraction EIPs (namely [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337)) are adopted. It was built to allow gasless/sponsored transactions and secure batched actions which are better fit at the wallet level.
{% endhint %}

## Assertions

Even though the relay logic is pretty standard and indeed was heavily inspired in OpenZeppelin’s implementation of it, Firm Relayer introduces the concept of assertions which allow checking the return value of calls.

This allows safely chaining/batching actions which need to ensure that a given call returned a value which has an impact in the calldata of subsequent calls. As all calls in a batch are precomputed, assertions allow injecting a sanity check to stop execution should a transaction been mined before the batch was executed.

An example in Firm’s context: A relay request contains a batch of calls to Roles. The first call creates a new role (`Roles:createRole(bytes32 roleAdmins, string memory name)(uint8 roleId)`) and then there are several other calls to assign the role to some accounts (`Roles:setRole(address user, uint8 roleId, bool isGrant)`).

Since `roleId`s are assigned incrementally by creation order, whoever creates the relay request needs to calculate which will be the id of the next role to be created (by simulating the transaction or checking the number of existing roles) so it can be passed as a parameter to the `setRole` actions. However if another role is created between the moment in which the relay payload is created and signed and when it’s executed, the role that the batch creates will get a different roleId, and the batch will assign a different role to those accounts.

An assertion in this context would allow to ensure that the `roleId` returned from the `createRole` call is the one expected (which is passed as input to the `setRole` calls) or revert.

## Entrypoints

### Relay

```solidity
function relay(RelayRequest calldata request, bytes calldata signature) external payable
```

Relay is the entrypoint for performing a metatransaction in which the sender of the transaction is decoupled from the actor who is executing the call.

Prior to executing the batch of actions in the relay request, Firm Relayer verifies the following:

* **Relay nonce:** in order to prevent replays (actor signs once but the payload is executed several times), Firm Relayer keeps an account nonce counter. Each request needs to specify the current account nonce which starts at zero. If the nonce is incorrect, the relay will revert.
* **Signature**: an EIP-712 signature of the `RelayRequest` struct. It must be exactly 65 bytes with the following format `[r (32 bytes)][s (32 bytes)][v (1 byte)]`. `request.from` must be the account that gets recovered from the signature.

If the verification succeeds, FirmRelayer will execute all the calls in the request one by one, setting the correct ERC-2771 context for the call (`sender = request.from`).

If any call reverts, the relay action will revert, effectively reverting all previous calls. If a call is successful and it has an assertion associated with it, it will read the return data of the call and check that the return value is the one expected by the assertion. If this check fails, the entire execution will be reverted as well.

### Self-relay

```solidity
function selfRelay(Call[] calldata calls, Assertion[] calldata assertions) external payable
```

There’s an additional entrypoint which allows using Firm Relayer for batching only. Using it won’t perform the `relay` checks, but the ERC-2771 sender gets set the the `msg.sender` of the call to Firm Relayer.

It was built to allow using the assertions feature for batched actions even if metatransactions aren’t used.


# Deployments

<figure><img src="/files/tXTzNNdpiTaZDTHh1wxF" alt=""><figcaption></figcaption></figure>

Firm protocol v1 is currently live on:

* Ethereum mainnet
* Polygon mainnet
* Ethereum Goerli

| **Network**      | **FirmFactory**                                                                                                                | **UpgradeableModuleProxyFactory**                                                                                              |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Ethereum mainnet | [`0xFbeC16EECD4558297FB3deA9934A162Ef76b14bd`](https://etherscan.io/address/0xFbeC16EECD4558297FB3deA9934A162Ef76b14bd)        | [`0x8EBa12de23C24D27469a748904CA6ba16aff6803`](https://etherscan.io/address/0x8EBa12de23C24D27469a748904CA6ba16aff6803)        |
| Polygon mainnet  | [`0xaC722c66312fC581Bc57Ee4141871Fe0bf22fA08`](https://polygonscan.com/address/0xaC722c66312fC581Bc57Ee4141871Fe0bf22fA08)     | [`0x6232B169db8e0f5A1FD1eEC3B802aa3FFC430bB4`](https://polygonscan.com/address/0x6232B169db8e0f5A1FD1eEC3B802aa3FFC430bB4)     |
| Ethereum Goerli  | [`0x1Ce5621D386B2801f5600F1dBe29522805b8AC11`](https://goerli.etherscan.io/address/0x1Ce5621D386B2801f5600F1dBe29522805b8AC11) | [`0x159200D27301776D5412D52Ed2605b7c5371AcFe`](https://goerli.etherscan.io/address/0x159200D27301776D5412D52Ed2605b7c5371AcFe) |

**Historic deployments**: <https://github.com/firm-org/firm-protocol/blob/master/deployments/factory.json>


# Security

<figure><img src="/files/j6nA64a736Vl4NIsppRa" alt=""><figcaption></figcaption></figure>

Previous to releasing v1, we conducted an initial audit with [Coinspect](https://www.coinspect.com/).

However, the protocol should still be treated with caution given it still hasn't been live for a long period of time.

## Audits

<table><thead><tr><th>Audit</th><th>Security firm</th><th data-type="files"></th><th>Date</th></tr></thead><tbody><tr><td>Firm protocol v1</td><td><a href="https://www.coinspect.com/">Coinspect</a></td><td><a href="/files/jjOc6ckSHjEkUXqSpTgS">/files/jjOc6ckSHjEkUXqSpTgS</a></td><td>February 2023</td></tr></tbody></table>

## Responsible disclosure

We currently do not have a bug bounty program for Firm protocol. However, since v1 is currently deployed in live networks, user funds could be at risk should a critical vulnerability exist.

If you believe you have found a vulnerability in Firm protocol, please responsibly disclose it by emailing [**security@firm.org**](mailto:security@firm.org)**.**


