NAV Navbar
javascript

Brreg Cap Table (BrregCapTable) Developer Documentation

Introduction

Welcome to the "Brønnøysund Business Cap Table" (beta) developer documentation, BrregCapTable in short.

There are some 321 000 limited liability companies in Norway - less than 1000 have their shareholding listed in a securities register. While every company is obliged to keep an updated cap table, the large majority of companies report shareholding only for tax purposes once a year.

A lot of people believe in a local business or a start-up, but few have the energy and insights to actually make an investment. What energy might be released if more people were able to take part in financing ideas they believe in. By providing an infrastructure, we hope to establish a platform that make ownership transparent and lower the risks of engaging with small and mediums sized enterprises. Doing so is part of a broad effort to make capital more efficient, for example by making shares liquid, allowing their use as collateral and link loans, mortgages and payments direct to shares and share transactions. In the process, it is quite possible to imagine a greatly simplified public reporting where regulators may themselves access the required data - and a greatly simplified corporate governance involving share loyalty programs, the payment of dividends and voting. In short, the platform may serve to open and democratizing the unlisted markets for new groups of investors.

Norwegian limited liability companies must have a share capital of at least NOK 30,000 assigned to one or more shares, each of identical nominal value. The total share capital, the number of shares and their nominal value are stated in the company's articles of association.

All limited liability companies shall have a register of shareholders unless the company's shares are registered in a securities register. The cap table shall at all times contain an overview of the company’s shareholders, and it will normally determine who may exercise shareholder rights including the right to vote at the general meeting and the right to receive dividends. The cap table is in principle public - insight may not be denied.

What is a cap table?

Upon listing a shareholder in the shareholders' register the company shall notify the shareholder thereof. The shareholders’ register shall be updated in accordance with established rules in the Companies Act (§ 4-7) to reflect changes of ownership or pledging of shares. Shares can be sold or given away without payment unless otherwise stated by law, articles of association or agreement between the shareholders. In the case of a sale, the price is normally calculated based on market value. Transfers of ownership below market value are referred to as “gift sale” and may trigger a tax liability. When shareholders sell or otherwise realize shares, gains will be taxable and losses deductible. The Companies Act contains rules on the consent of acquiring shares, and rules on pre-emption rights for the other shareholders. An agreement to transfer shares shall be reported to the board of the limited company by the person acquiring the shares. The agreement should as a minimum list the parties, the number of shares to be transferred, and at what price. Transfers in BrregCapTable will be reported automatically. The new shareholder must be notified by the company that she has been included in the shareholders' register and what is listed.

Shareholder agreements

Shareholder agreements are normally entered into between shareholders of a company and regulate the relationship between them. Shareholders are not obliged to enter into such an agreement, but it may be useful. A shareholder agreement obliges only the shareholders who have signed the agreement. There are no requirements as to how the agreement should look or what it should contain. Examples of conditions that can be regulated in a shareholder agreement are rules relating to ownership, demands for dividends, non-compete clauses, impartiality and work/employment requirements.

Conflict between shareholders

It is not uncommon for conflicts between shareholders to arise regarding the organization and running of a company. Since the causes of conflict may be difficult to spot in advance, it may make sense to

In cases where the shareholders no longer manage to cooperate with each other, the district court may, in response to a lawsuit from a shareholder or from the company, decide on the redemption or release of a shareholder's shares. Such lawsuits must be regarded as last resort, and there must be weighty reasons.

Integrate your application

A project that is looking to integrate with BrregCapTable has 5 different avenues to interact with the protocol implementation. The integration types are as follows:

The SDK makes it easy to make read and makes changes to company cap tables. A smart contract integration allows you to extend the register itself. For new smart contract developers we recommend this Learning Solidity tutorial series by Karl Floersch. The common methods existing applications use to integrate with our protocol implementation are through our SDK. Explore the SDK guides to learn more!

Demo data

We have extracted data from our registries for about 130 companies respectively. It is stored in two excel files.

Download here

Main Concepts

Common Terms

This is a list of terms you might encounter when developing on BrregCapTable.

Cap table

Registry of cap tables

Entity registry

Chairman of the board

Share class

Block explorer

Blockchain

Smart Contracts

Wallet

Account

Address ("Public Key")

Public Key

Private Key

Mnemonic Phrase / Seed Phrase / Seed Words

Hardware Wallet:

Hexadecimal

Seed

Encryption

Encrypted vs Unencrypted Keys

All feedback, rewrites, clarification, typo-fixing, and requests for additions are more than welcome.

Thanks to MetaMask for this Glossary starting point.

Users

A user is a shareholder and/or CEO and/or chairman of the board. A user is either active or passive.

An active user is authenticated and can write changed directly into the shareholder register, like transferring shares or changing their address.

A passive user is a shareholder who is not registered on the platform. A passive user cannot make changes in the shareholder register, and any transfers have to be input by the chairman of the board, on behalf of the shareholder.

Networks and endpoints

Localhost

You can run the whole blockchain locally. This part is undocumented at this stage, but all of the source code is available on Gitlab.

Staging

The staging blockchain is named Toyen and is open to everyone. This is the recommended way to get started with integration with the platform.

Relevant details

Production

The production version of the platform is not live yet.

SDK

You can connect to the platform with the SDK, or you can extend the platform itself by developing and deploying smart contracts. This section outlines how to use the SDK.

Getting started

To install the SDK to your project, just use NPM in your project.

npm install @brreg/sdk

To develop on BrregCapTable, you start out by installing the SDK on your development machine.

You should also install MetaMask in Chrome as the middleware between your application and the blockchain. Download here.

Example frotend application

It can be hard getting started. We recommend you take a look at the source code of our example application to get a feel of the SDK. The application is written in Vue.js with VueX. A live version of the example application is available on Github.

Example backend application

If you are creating a backend service with NodeJs. Take a look at this demo application useing the SDK. brreg-typescript-starter.

In a backend applicatin you dont rely on the user providing a connection to Brreg network. To mock this we can do the following in Typescript. typescript import { ethers } from 'ethers' const provider = new ethers.providers.JsonRpcProvider('http://ethjygmk4-dns-reg1.northeurope.cloudapp.azure.com:8540') const ProviderWithWallet = ethers.Wallet.createRandom().connect(provider)

How do users connect to BrregCapTable blockchain network?

As BrregCapTable is a service facilitated by blockchain technology, end-users will interact with your service in ways that differs from a regular client-server approach.

Instead of credentials authenticating the user and giving the user the correct access, end users are expected to use a browser with blockchain wallet functionality. This functionality is available in

As a frontend developer, you must download this plugin and use it when developing frontend applications.

Web3 Browser Detection

The first thing your app will want to do is verify whether the user is using MetaMask or not, which is simple using a check like if (typeof window.ethereum !== 'undefined') { /* deal with it */ }.

Connecting to the BrregCapTable Network

In the top-right menu of MetaMask, you can select the network that you are currently connected to. Among several popular defaults, you'll find Custom RPC. Use it and set

Since your seed phrase is the power to control all your accounts, it is probably worth keeping at least one seed phrase for development, separate from any that you use for storing real value. One easy way to manage multiple seed phrases with MetaMask is with multiple browser profiles, each of which can have its own clean extension installations.

User State

There are a settings you should keep in mind when interacting with Metamask:

ethereum.networkVersion
ethereum.selectedAddress

Both of these are available synchronously. The networkVersion for BrregCapTable is 53387025.

Logging In

When you're ready to request the user log in, you can call this simple function.

ethereum.enable();

Since it returns a promise, if you're in an async function, you may log in like this.

const accounts = await ethereum.enable()
const account = accounts[0] // We currently only ever provide a single account,
                            // but the array gives us some room to grow.

This promise-returning function resolves with an array of hex-prefixed ethereum addresses, which can be used as general account references when sending transactions.

Over time, this method is intended to grow to include various additional parameters to help your site request all the setup it needs from the user during setup.

Accessing the user's wallet

To access the user's wallet, use this code:

let ethereum
if (typeof window.ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) {
    ethereum = window.ethereum;
}

The first line of code includes the SDK. This needs to be installed before you can use it. See Getting started.

In a regular client-server architecture, the user's browser works as a thin client, where the business logic is running on the server. In an application utilizing blockchain, the business logic is run in the user's browser and she connects directly to the backend/blockchain. For this to work, you need to use her access keys for every write operation.

MetaMask injects a global API into websites visited by its users at window.ethereum (Also available at window.web3.currentProvider for legacy reasons). This API allows websites to request user login, load data from blockchains the user has a connection to, and suggest the user sign messages and transactions. You can use this API to detect a user of a web3 browser.

If you want to deep dive into the Metmask API and their best practices, check the Metamask documentation.

How to use the SDK

In BrregCapTable, there are mainly four classes to work with: * Company API - All functionality connected to a spesific company. * Company Factory API - Functionality to create a new company. * Entity Registry API - Functionality to store or get information about a person or a company. * Registry ofc Cap Tables API - Functionality working on all companies in this network.

Init

In each class there is an init function. This function is the bridge between the users Ethereum network connection (through Metamask) and the smart contracts the frontend know how to work with.

So before useing a spesific API class, the developer should init the api class passing in the users Ethereum network connection (which you get from the Browser thorugh Metamask). ```javascript let ethereum if (typeof window.ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { ethereum = window.ethereum; }

const RegistryOfCapTables = require("@brreg/sdk").RegistryOfCapTables const RegistryOfCapTablesContract = await RegistryOfCapTables.init(ethereum); // RegistryOfCapTablesContract is now an instance of this smart contract. and we can run its interfaces. By example await RegistryOfCapTablesContract.list() to list all the companies saved within this smart contract. ```

Registry Of Cap Tables

While most functions are specific to a given company, the functions in this chapter are platform-wide (they relate to all of BrregCapTable).

init(externalSignerProvider: Signer | any, proxyAddress?: string)

The Class always has to be initialized before using

const RegistryOfCapTables = require("@brreg/sdk").RegistryOfCapTables
const RegistryOfCapTablesContract = await RegistryOfCapTables.init(ethereum);
Parameter Type Description
externalSignerProvider Ethereum provider Should be set to the user's wallet i.e. ethereum from the section Accessing the user's wallet from above.
proxyAddress String (Ethereum address) If you want to point the SDK at another blockchain than the Staging server

list()

Parameter Type Description
RETURN Promise< CapTableInfo[] >
const list = await RegistryOfCapTablesContract.list();
console.log(list);

Example of return data:

return [ 
    { name: 'Empty inc.',
    totalSupply: 0,
    denomination: 0,
    denominationPerShare: 0,
    director:
     { uuid: '195199646563',
       name: 'Ethel Rice',
       country: 'Norway',
       city: 'Oslo',
       postalcode: '3014',
       streetAddress: '843 Fewus Center',
       type: 'person',
       address: '0xe88C9fE335185b62a530d2B67A2438e55E5bf39A' },
    address: '0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E',
    isController: false },
]

List all companies on the platform

BrregCapTable deals only with companies that have been onboarded. Use this code to list these.

Company Factory

Used to onboard new company.

init(externalSignerProvider: Signer | any, proxyAddress?: string) : Promise

The Class always has to be initialized before using

const CompanyFactoryClass = require("@brreg/sdk").StockFactory;
const companyFactory = await CompanyFactoryClass.init(ethereum);
Parameter Type Description
externalSignerProvider Provider Should be set to the user's wallet i.e. ethereum from the section Accessing the user's wallet from above.
proxyAddress String If you want to point the SDK at another blockchain than the Staging server

createNew(name: string, uuid: string, options: { partitions: string[], symbol: string }) : Promise

const Company = await companyFactory.createNew("Blockchangers AS", "915772137");

Note that you need to wait for the transaction to go through before continuing. This can take up to 5 seconds.

This actions will queue the company for verification.

Parameter Type Description
name String Business name
uuid String Organization number, note that it's a string
options Object Object with the following possible options
partitions String[] List of share classes as strings. Example ['a-share', 'b-share']
symbol String Shorthand for the company, 4 characters length.
RETURN Promise Company API

Entity Registry

init(externalSignerProvider: Signer | any, proxyAddress?: string)

const EntityRegistryClass = require("@brreg/sdk").EntityRegistry
const entityRegistry = await EntityRegistryClass.init(ethereum);
Parameter Type Description
externalSignerProvider Provider Should be set to the user's wallet i.e. ethereum from the section Accessing the user's wallet from above.
proxyAddress String (Ethereum address) If you want to point the SDK at another blockchain than the Stagning server
RETURN Promise If you want to point the SDK at another blockchain than the Stagning server

generateAddress() : Promise

const arbitraryAddress = await entityRegistry.generateAddress();

Many companies will have stockholders who are not on the BrregCapTable platform. Hence the shares can not be sent to the user. Technically these shares still need to be generated on the blockchain, and put on seperate users. This method generates an arbitrary address for you to put shares on, for users not on the platform. Generate one address for every user who does not have a prior address, i.e. those not registered as users on BrregCapTable.

Parameter Type Description
RETURN String (Ethereum address) A random ethereum address

allTransactionsAllCapTables(uuid: string) : Promise

let captable = await entityRegistry.allTransactionsAllCapTables("24078612345");
console.log(captable);

Example of return data:

[ { capTable:
     { name: 'Empty inc.',
       totalSupply: 0,
       denomination: 0,
       denominationPerShare: 0,
       director: [Object],
       address: '0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E',
       isController: false },
    transactions: [] },
]

Returns all transactions done for the given person. This function can be used for most views, analytics and statistics that you want to present the user with. Get all the data with this function, then filter and customize the returned data, before presenting it to the user.

Parameter Type Description
uuid String The uuid of which you want to get all transactions

interface CapTableTransactions

interface CapTableTransactions {
  capTable: CapTableInfo
  transactions: Transaction[]
}

getEntityByUuid(uuid: string) : Promise

let entityData = await entityRegistry.getEntityByUuid("915772137");
console.log(entityData);

Get data about the given entity.

Parameter Type Description
uuid String The personal identification number of a person, or a organization number of a company.

type EntityData

{
  address: string
  uuid: string
  type: string
  name: string
  country: string
  city: string
  postalcode: string
  streetAddress: string
}

getEntityByAddress(address: string)

let entityData = await entityRegistry.getEntityByAddress("0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");
console.log(entityData);

Gets the given entity. Use an ethereum address as lookup parameter.

addEntity(data: EntityData) : Promise

let companyFactory = await StockFactory.init(ethereum);
let Company = await companyFactory.createNew("Blockchangers AS", "915772137")
let companyAddress = await Company.getAddress()

const entityRegistry = await EntityRegistry.init(ethereum)
let tx = await entityRegistry.addEntity({
    address: companyAddress,
    uuid: "915772137",
    type: 'person' , // or 'organization
    name: "Blockchangers AS" , 
    country: "Norway",
    city: "Oslo",
    postalcode: "0179",
    streetAddress: "Møllergata 6"
});
await tx.wait();

Adds an entity to the entityRegistry.

Note that you need to wait for the transaction to go through before continuing. This can take up to 5 seconds

Parameter Type Description
EntityData object The EntityData object is described with the following parameters
address string The blockchain address for the given company or person
uuid string String. The organizational number of a company or the identification number of a person
type string String. 'person' or 'organization.
name string String. Name of the entity.
country string String. Country of the entity.
city string String. City of the entity.
postalcode string String. Postal code of the entity.
streetAddress string String. Street Address of the entity.

updateEntity(data: EntityData) : Promise

Updates an entity in the entityRegistry.

Parameter Type Description
EntityData object The EntityData object is described with the following parameters
address string The blockchain address for the given company or person
uuid string String. The organizational number of a company or the identification number of a person
type string String. 'person' or 'organization.
name string String. Name of the entity.
country string String. Country of the entity.
city string String. City of the entity.
postalcode string String. Postal code of the entity.
streetAddress string String. Street Address of the entity.
const entityRegistry = await EntityRegistry.init(ethereum)
let tx = await entityRegistry.updateEntity({
    address: companyAddress,
    uuid: "915772137",
    type: 'person' , // or 'organization
    name: "Blockchangers AS" , 
    country: "Norway",
    city: "Oslo",
    postalcode: "0179",
    streetAddress: "Møllergata 6"
});
await tx.wait();

Company API

init(externalSignerProvider: Signer | any, address: string, proxyAddress?: string)

The Class always has to be initialized before using

const CompanyClass = require("@brreg/sdk").Stock;
const Company = await CompanyClass.init(ethereum, "0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");
Parameter Type Description
externalSignerProvider Ethereum provider Should be set to the user's wallet i.e. ethereum from the section Accessing the user's wallet from above.
address String The ethereum address of the company. In the future, this will be able to accept UUID.
proxyAddress (optional) string If you want to point the SDK at another blockchain than the Stagning server
RETURN Promise Company API

In next version of Company API, this will be able to accept UUID instead of the Etehreum address to initiate the company class.

address()

Returns a new Company Class for this address.

balanceOf(address: string) : Promise

const CompanyClass = require("@brreg/sdk").Stock;
const Company = await CompanyClass.init(ethereum, "0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");

let usersAmountOfShares = Company.balanceOf("0xab5801a7d398351b8be11c439e05c5b3259aec9b");
console.log(usersAmountOfShares);

Returns the number of shares for the given ethereum address for any partition, be it a person, company or smart contract.

balanceOfByPartition(partition: string, address: string) : Promise

Returns the number of shares of the given share class for the given ethereum address.

denominationPerShare() : Promise

Returns the denomination of a share i.e., "Pålydende" in Norwegian.

director() : Promise

Returns the board director of the company.

getDefaultPartitions(asBytes = false) : Promise

Returns an array of default partitions. By default, they will be returned as Strings.

totalSupply() : Promise

const CompanyClass = require("@brreg/sdk").Stock;
const Company = await CompanyClass.init(ethereum, "0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");

let totalAmountOfSharesInTheCompany = await Company.totalSupply();
console.log(totalAmountOfSharesInTheCompany);

Returns the total number of shares.

getTotalSupplyByPartition(partition: string) : Promise

Returns the number of shares in the given share class.

info() : Promise

Returns a collection of information about the company. name, totalSupply, denomination, denominationPerShare, director, address, isController

isController(address: string) : Promise

Returns true if the given address is a controller of the company. An address can be a person or a smart contract.

A controller is an address that can make certain changes to the company — by example transferring board director role. Controllers can in a transparent and with documentation requirement do certain transactions like transferring stocks. This could, by example, be used in the case where a person has died, and the shares need to be transferred to the heir.

setDenomination(newDenomination: number) :Promise

Updates the denomination. This supplied number should not be the denomination per share, but the total denomination: denomination * number of shares e.g. NOK 2 * 30000 shares = denomination 60000.

shareholders() : Promise

Returns all shareholders.

transactions(filter?: { address?: string }) : Promise

Returns all transactions

name() : Promise

Returns the name of the company.

partitionsOf(address: string, asBytes = false) : Promise

Returns all partitions of shares an address is in ownership of.

transferDirector(address: string) : Promise

Change the board director of the company.

uuid() : Promise

Returns the organizational number of the company.

transfer(toUuid: string, numberOfSharesToTransfer: number, options?: { partition?: string, data?: string }) : Promise

const CompanyClass = require("@brreg/sdk").Stock;
const company = await CompanyClass.init(ethereum, "0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");
company.transfer("24078612345", 100);
await tx.wait();

interface TransactionsRespons { 
  nonce: 2,
  gasPrice: BigNumber { _hex: '0x00' },
  gasLimit: BigNumber { _hex: '0x02faf080' },
  to: '0x12004352Ba03376bf6B2dd8B8Fa1767B9a5679Df',
  value: BigNumber { _hex: '0x00' },
  data:
   '0x67c8491941000000000000000000000000000000000000000000000000000000000000000000000000000000000000002419d23dcbd5eb48c7e6f6f1b9e40c4a57aea71f00000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000',
  chainId: 53387025,
  v: 106774085, // This is a signature value, dont mind it now.
  r: 
   '0x7fb685f478ea5b5764df12956bf26a644c670238a206363ad435b1fb898fd66d',  // This is a signature value, dont mind it now.
  s:
   '0x4f17dfb0d01c72bed0abcd719f82701c3aa7562da7a0e3cb7db5c81dffc51e81',  // This is a signature value, dont mind it now.
  from: '0xCa982614b8cc6983A3B5ACe725F1964C9Eec6f2e',
  hash:
   '0x3ac3293e6e16afa43ec9dad604c6d26a75ae642d34a52ce1f559fdb8e00f6720',
  wait: [Function] 
  }

Note that you need to wait for the transaction to go through before continuing. This can take up to 5 seconds

Parameter Type Description
toUuid string The personal identification number of a person, or an organization number of a company.
numberOfSharesToTransfer number Amount
options (optional) object A object detailed with the following parameters.
data string This can be an arbitrary blob of data that Companies can utilize in different ways.
partition string The class of the shares to issue.

Function for moving shares from one person or company, another. Note that you as a developer do not input who the shares come from. When the user initiates the transfer from your service, the transaction is signed and sent from them. Hence the shares are sent from her.

operatorTransfer(fromUuid: string, toUuid: string, numberOfSharesToTransfer: number, options?: { partition?: string, data?: string, operatorData?: string }) :: Promise

If a controller wants to force a transaction the controller may use this function to make the transactions. The controller can attach data about the forced transaction in operatorData.

Note that you need to wait for the transaction to go through before continuing. This can take up to 5 seconds

Parameter Type Description
toUuid string The personal identification number of a person, or an organization number of a company.
numberOfSharesToTransfer number Amount
options (optional) object A object detailed with the following parameters.
data string This can be an arbitrary blob of data that companies can utilize in different ways.
partition string The class of the shares to issue.
operatorData string This can be an arbitrary blob of data that companies can utilize in different ways.

issue(toUuid: string, numberOfSharesToTransfer: number, options?: { data?: string, partition?: string }) : Promise

const CompanyClass = require("@brreg/sdk").Stock;
const Company = await CompanyClass.init(ethereum, "0x2358cEE56BEf4Ac13d65e9F96677f6E40b0abC3E");

let tx = await Company.issue("24078612345", 1000);
await tx.wait();

Issue new shares to the given entity.

Parameter Type Description
toUuid string The personal identification number of a person, or an organization number of a company.
numberOfSharesToTransfer number Amount
options (optional) object A object detailed with the following parameters.
data string This can be an arbitrary blob of data that companies can utilize in different ways.
partition string The class of the shares to issue.

redeem(numberOfSharesToRedeem: number, options?: { partition?: string, data?: string })

Remove shares from the sending address.

Parameter Type Description
numberOfSharesToRedeem number AMount of shares to remove.
options (optional) object A object detailed with the following parameters.
data string This can be an arbitrary blob of data that companies can utilize in different ways.
partition string The class of the shares to redeem.

operatorRedeem(fromUuid: string, numberOfSharesToRedeem: number, options?: { partition?: string, data?: string, operatorData?: string }) : Promise

Remove shares from the sending address.

Parameter Type Description
fromUuid string From which UUID shall the operator remove shares.
numberOfSharesToRedeem number Amount of shares to remove.
options (optional) object A object detailed with the following parameters.
data string This can be an arbitrary blob of data that companies can utilize in different ways.
partition string The class of the shares to redeem.
operatorData string This can be an arbitrary blob of data that companies can in different ways.