/DARC

Decentralized Autonomous Regulated Company (DARC), a company virtual machine that runs on any EVM-compatible blockchain, with on-chain law system, multi-level tokens and dividends mechanism.

Primary LanguageTypeScriptOtherNOASSERTION

Decentralized Autonomous Regulated Company (DARC)

Welcome to the official repository for the Decentralized Autonomous Regulated Company (DARC) project. DARC is a project that aims to create a decentralized autonomous company that is regulated by a plugin system based on commercial laws. The project is currently in the early stages of development and is not yet ready for production use.

English | 简体中文 | 日本語

Join our community

Telegram: https://t.me/projectdarc

What is DARC?

Decentralized Autonomous Regulated Company (DARC) is a company virtual machine that can be compiled and deployed to EVM-compatible blockchains with following features:

  • Multi-level tokens, each level token can be used as common stock, preferred stock, convertible bonds, board of directors, product tokens, non-fungible tokens (NFT), with different prices, voting power and dividend power, which are defined by the company's plugin(law) system.
  • Program composed of a series of DARC instructions that include managing tokens, dividends, voting, legislation, purchasing, withdrawing cash, and other company operations.
  • Dividend Mechanism for distributing dividends to token holders according to certain rules.
  • Plugin-as-a-Law.The plugin system serves as the by-law or commercial contract that supervises all operations. All company operations need to be approved by the plugin system or corresponding voting process.

By-Law Script

By-law script is a JavaScript-like programming language that is used to define the company's commercial rules and operations on DARC. For example:

mint_tokens(   // mint token operation
    [addr1, addr2, addr3],   // token address
    [0, 0, 0],   // token class 
    [500, 300, 200]  // number of tokens
);

pay_cash(100000000, 0, 1); // pay 0.1 ETH as purchase

transfer_tokens(   // transfer token operation
    [addr1, addr2, addr3],   // token address
    [0, 0, 0],   // token class 
    [100, 100, 200]  // number of tokens
);

add_withdraw_cash(10000000);  // add 0.01 ETH to withdraw balance

withdraw_cash_to(  // withdraw cash from my account to other address
    [addr4, addr5],       // withdraw cash to addr4, addr5
    [10000000, 10000000]  // withdraw amount 0.01 ETH, 0.01 ETH
);

Above By-law Script will be transpiled via code generator and sent to corresponding DARC VM contract. The DARC will execute the program if the plugin system approves. To add plugin and voting rules to the DARC, we can simple compose the plugin conditions and voting rules, then send them via operation add_voting_rule(), add_and_enable_plugins() or add_plugins(), and they will be deployed and effective immediately if the current plugin system approves the operation.

Here is a quick example, assume we need to limit the transfer of tokens by major shareholders (>25%) by asking the board of directors for an all-hand vote (assuming 5 tokens in total), and it requires 100% approval (5 out of 5) in 1 hour. We can add a new plugin and corresponding voting rule to the DARC VM contract:

add_voting_rule(  // add a voting rule (as index 5)
    [
        {
            voting_class: [1], // voting token class: 1, level-1 token ownners (board of directors) are required to vote
            approve_percentage: 99,  // 99% voting power is required to approve
            voting_duration: 3600,  // voting duration: 1 hour (3600 seconds)
            execute_duration: 3600,  // pending duration for execution: 1 hour (3600 seconds)
            is_absolute_majority: true,  // absolute majority is required, not relative majority
        }
    ]
)

add_and_enable_plugins(   // add and enable plugins (as index 7)
    [
        {
            condition:  // define the condition:
                (operation == "transfer_tokens")   // if operation is transfer_tokens
                & (operator_total_voting_power_percentage > 25),  // and addr1's voting power > 25%
            return_type: voting_needed,  // return type: requires a vote
            return_level: 100,  // priority: 100
            votingRuleIndex: 5 // voting rule index 5 (ask board of directors to vote and must 100% approve)
            note: "100% Approval is needed by board members to transfer tokens by major shareholders (>25%)"
            is_before_operation: false,  // check the plugin after the operation is executed in sandbox
        }
    ]
)

After above By-Law Script is executed, the DARC VM contract will add a new plugin and voting rule, and the plugin will be effective immediately (if there exists any voting procedure related to add_voting_rule() and add_and_enable_plugins(), the plugin will be effective after the voting process is approved). If the operator (addr1) tries to transfer tokens to addr2, the plugin will check the condition and return voting_needed to the DARC VM contract, and the DARC VM contract will ask the board of directors (level-1 token owners) to vote. If the board of directors approves the operation, the operation will be executed in the sandbox, otherwise the operation will be rejected. For example, if there are 3 voting rules are triggerd, the voting operation will be:

vote([true, true, true])

If the voting process is approved by the existing voting rules and plugins, the new program will be approved to execute in the next execution pending duration (1 hour in this example), and the program owner or any other members can execute the program in the next 1 hour, or the program will be ignored and removed from the pending list.

"Plugin-as-a-Law"

The law of DARC is defined in below pseudo-code:

if (plugin_condition == true) {
    plugin_decision = allows / denies / requires a vote
}

Each plugin contains a condition expression tree and a corresponding decision (return type). When the condition tree is evaluated to true while the program is submitted before running, the plugin will make a decision by allows, denies or requires a vote. For example:

Example 1: Anti-Dilutive shares

Anti-Dilutive shares is a basic mechanism to prevent the company (including DAO and other on-chain "tokenomics") from issuing too many shares and dilute the ownership of the existing shareholders. In DARC, the company and early-stage investors can define a law of "anti-dilutive shares", and the law can be abolished by certain process.

Law 1 (Anti-Dilutive Shares): Shareholder X should always holds 10% of the total stock.

Design of Plugin: If operation is minting new level-0 tokens, plugin should check the state of token ownerships, X should always keep a minimum total voting power of 10%, as well as dividend power of 10% after executing the operation

In By-law script, we can define the plugin with following conditions:

// define X's address
const x_addr = "0x1234567890123456789012345678901234567890";

// define the plugin
const anti_delutive = {

    // define the trigger condition
    condition:
        ((operation == "mint_tokens")             // if operator is minting new tokens
            | (operation == "pay_to_mint_tokens"))   // or operator is paying to mint new tokens
        &                                          // and        
        ((total_voting_power_percentage(x) < 10)    // X's total voting power < 10%
            | (total_dividend_power_percentage(x) < 10)),   // or X's total dividend power < 10%

    // define the decision: reject the operation
    return_type: NO,

    // define the priority: 100
    return_level: 100,

    // check the plugin after the operation is executed in sandbox
    is_before_operation: false,
}

Since it checks the state of token ownerships, the plugin should be executed after the operation is executed inside the DARC's sandbox. If the plugin's condition is evaluated to true, the plugin will deny the operation after executing in the sandbox, and the operation will be rejected to be executed in the real environment. Otherwise, "minting new tokens" will be allowed to execute.

When this plugin is added to the DARC, the operator (the author of current program) must mint extra tokens to address x_addr to satisfy the Law 1 above, otherwise it will be rejected. For example, the DARC has only one level of tokens (level 0, voting power = 1, dividend power = 1), the stock ownerships are:

ShareHolders Number of tokens Percentage
CEO 400 40%
CTO 300 30%
CFO 200 20%
VC X 100 10%
Total 1000 100%

If the operator want to mint 200 tokens and issue them to VC Y, the operator must mint 20 tokens to address x_addr to satisfy the Law 1 above, otherwise the operation will be rejected. Here is a sample investment program by VC Y:

pay_cash(1000000000000)  // pay 1000 ETH to the DARC
mint_tokens(20, 0, x_addr)  // mint 20 level-0 tokens to address x_addr
mint_tokens(180, 0, y_addr)  // mint 180 level-0 tokens to address y_addr
add_and_enable_plugin([new_law_1, new_law_2, new_law_3])  // investment laws by VC Y

After the operation, the stock ownerships are:

ShareHolders Number of tokens Percentage
CEO 400 33.33%
CTO 300 25%
CFO 200 16.67%
VC X 120 10%
VC Y 180 15%
Total 1200 100%

Also another plugin should be added to the DARC to define the legislation of the "Abolish Law 1":

Law 1.1(Law 1 Appendix): Both Law 1 and Law 1 Appendix (current Law) can be abolished if and only if the operator is X

Design of Plugin: If operation is "disable_plugins", and the plugin that to be disabled is with id == 1 or id == 2, and the operator is not X, then the plugin should reject the operation (assume the anti dilutive law index is 1, and the appendix law index is 2, both are before-operation plugins)

const law_1_appendix = {

    // define the trigger condition
    condition:
        (operation == "disable_plugins")
        & ((disable_after_op_plugin_id == 1) | (disable_after_op_plugin_id == 2))
        & (operator != x_addr),

    // define the decision
    return_type: no,

    // define the priority
    return_level: 100,

    // reject the operation before sandbox
    is_before_operation: true,
}

Example 2: Bet-on Agreement/Valuation-Adjustment Mechanism(VAM) Agreement

Law2: If total revenue < 1000 ETH by 2035/01/01, shareholder X can take over 75% of total voting power and 90% of dividend power.

Design of Plugin: After executing in sandbox, check the following conditions:

  • timestamp >= 2035/01/01

  • revenue since 2000/01/01 < 1000 ETH

  • operation is "mint_tokens"

  • total voting power of x <= 75%

  • the dividend power of x <= 90%

then the plugin should approve the operation

In By-law script, we can define the above plugin as following:

const bet_on_2 = {

    // define the trigger condition
    condition:
        (timestamp >= toTimestamp('2035/01/01')) &
        (revenue_since(946706400) < 1000000000000) & // 1000000000000 Gwei = 1000 ETH
        (operation == "mint_tokens") &
        (total_voting_power_percentage(x) < 75) &
        (total_dividend_power_percentage(x) < 90),

    // define the decision
    return_type: yes,

    // define the priority
    return_level: 100,

    // approve the operation after executing in sandbox
    is_before_operation: false,
}

Example 3: Employee Payroll

Law 3: The payroll for employees with role level X should be 10 ETH per month.

Design of Plugin: If operation is "add withdrawable cash", the amount is less than or equals to 10 ETH, and the last operation was at least 30 days, then this operation should be approved and skip sandbox check

In By-law script, we can define the plugin with following conditions (for example, level X = 2 can withdraw 10 ETH per 30 days):

const payroll_law_level_2 = {
    condition:
        (operation == "add_withdrawable_cash") &   // operation is "add withdrawable cash"
        (member_role_level == 2) &   // the operator address is in role level 2

        // add cash by < every 30 days = 2592000 seconds
        (operator_last_operation_window("add_withdrawable_cash") >= 2592000) &
        // each time add < 10000000000 Gwei = 10 ETH to the account
        (add_withdrawable_cash_amount <= 10000000000),

    // approve the operation and skip sandbox check
    return_type: yes_and_skip_sandbox,
    return_level: 1
    is_before_operation: true,
}

With the plugin above, the operator can add withdrawable cash to the employee's account with amount less than or equals to 10 ETH, and the last operation was at least 30 days. The plugin will approve the operation and skip the sandbox check. When the employee address is disable, removed from role level X, or other plugins with higher priority deny the operation, these operations will be rejected.

Example 4: Voting and legislation

For daily operations, the board of directors can be defined as a group of addresses, and the voting mechanism can be used to make decisions. For example, let's design the voting mechanism for the following scenario:

  1. Any address X with more than 10% total voting power can be added to the board by minting 1 token (level 2, board voting token), if and only if the behavior is approved by 2/3 of all the board members (voting rule 1).
const add_board_member = {
    condition:
        (operation == "mint_tokens") &   // operation is "mint_tokens"
        (mint_tokens_level == 2) &  // the token level is 2
        (mint_tokens_amount == 1) &  // the amount is 1
        (operator_total_voting_power_percentage >= 10),   // the operator address holds at least 10% of the total voting power
    return_type: voting_needed,
    voting_rule: 1,  // Under the voting rule 1, the operation will be approved if and only if 2/3 of all the board members approve the operation
    return_level: 100,
    is_before_operation: false, // make the decision after executing in sandbox
}
  1. Any operator with more than 7% of all voting power can submit enable_plugins() , and it needs to be approved by 100% of all the board members. Each operator can try to activate plugin per 10 days.
const enable_plugin = {
    condition:
        (operation == "enable_plugins") &   // operation is "enable_plugins"
        (operator_total_voting_power_percentage >= 7) &   // the operator address holds at least 7% of the total voting power
        (operator_last_operation_window("enable_plugin") >= 864000),  // each operator can try to enable plugins once per 864000 seconds (10 days)

    return_type: voting_needed,
    voting_rule: 2,  // Under the voting rule 2, the operation will be approved if and only if 100% of all the board members approve the operation
    return_level: 100,
    is_before_operation: false, // make the decision after executing in sandbox
}
  1. To disable plugins 2,3 and 4, the operator needs to hold at least 20% of total voting power, and the operation needs to be approved by 70% of all common stock token(level-0) voters as relative majority(voting rule 2). For each member of DARC, this operation can be executed once per 15 days (1296000 seconds).
const disable_2_3_4 = {
    condition:
        (operation == "disable_plugins") &   // operation is "disable_plugins"
        (
            disable_after_op_plugin_id == 2
            | disable_after_op_plugin_id == 3
            | disable_after_op_plugin_id == 4
        ) &  // disable after operation plugins 2,3 and 4
        (operator_total_voting_power_percentage >= 20) &   // the operator address holds at least 20% of the total voting power
        (operator_last_operation_window("disable_plugins") >= 1296000),  // each operator can try to disable plugins once per 1296000 seconds (15 days)
    return_type: voting_needed,
    voting_rule: 3,  // Under the voting rule 3, the operation will be approved if and only if 70% of all the common stock holders approve the operation
    is_before_operation: false, // make the decision after sandbox check
}

Example 5: Multi-level Tokens: Product tokens and Non-fungible tokens

Here is an example of how to design a token with different levels of voting power and dividend power. The voting power and dividend power are used to calculate the voting power and dividend power of each token holder. Here is the table of the token levels:

Level Token Voting Power Dividend Power Total Supply
0 Level-0 Common Stock 1 1 100,000
1 Level-1 Stock 20 1 10,000
2 Board of Directors 1 0 5
3 Executives 1 0 5
4 Non-Voting Shares 0 1 200,000
5 Product Token A (0.01 ETH/token) 0 0
6 Product Token B (10 ETH/token) 0 0
7 Non-Fungible Token #1 0 0 1
8 Non-Fungible Token #2 0 0 1
9 Non-Fungible Token #3 0 0 1
10 Non-Fungible Token #4 0 0 1
11 Non-Fungible Token #5 0 0 1
... ... ... ... ...

To pay for service or prochase for products, customers can use pay_cash() to pay for the service directly, or use pay_to_mint_tokens() as a payment method and receive product tokens/NFTs.

Here is an example about how to define "Product Token A" and "NFT" price and total supply.

const product_token_A_price_law = {
    condition:
        (operation == "pay_to_mint_tokens") &   // operation is "pay_to_mint_tokens"
        (pay_to_mint_tokens_level == 5) &  // the token level is 5
        (pay_to_mint_price_per_token >= 10000000000000000),   // price per token >= 0.01 ETH = 10000000000000000 wei

    return_type: yes_and_skip_sandbox,  // approve the operation and skip sandbox check
    return_level: 1,
    is_before_operation: true, // approve the operation and skip sandbox check
}

const NFT_price_law = {
    condition:
        (operation == "pay_to_mint_tokens") &   // operation is "pay_to_mint_tokens"
        (pay_to_mint_tokens_level >= 7) &  // the token level is 7 or higher
        (pay_to_mint_token_amount == 1) &  // only allow to mint 1 token at a time
        (pay_to_mint_current_level_total_supply == 0) &  // current total supply is 0
        (pay_to_mint_price_per_token >= 10000000000000000000),   // price per token >= 10 ETH = 10000000000000000000 wei

    return_type: yes_and_skip_sandbox,  // approve the operation and skip sandbox check
    return_level: 1,
    is_before_operation: true, // approve the operation and skip sandbox check
}

Example 6: Lock dividend yield rate for 5 years

The dividend mechanism is designed to distribute dividends to token holders under certain rules:

  1. For each X purchase transactions, take Y‱ of the total income as the dividendable cash
  2. The offer_dividend() operation can be called, which will distribute the dividendable cash to token holders' dividend withdraw balance
  3. The amount of dividends per token holder (X) is calculated by the following formula: dividend_X = dividendable_cash * dividend_power(X) / total_dividend_power
  4. After the offer_dividend() operation is called, the dividendable cash and dividendable transaction counter will be set to 0, and the dividend withdraw balance of each token holder will be increased by dividend_X

To make sure the dividend yield rate is stable, we can add a plugin to the DARC to lock the dividend yield rate for 5 years by limiting the set_parameters() function.

Law 6: The dividend yield rate should be locked > 500‱ (5%) before 2030-01-01.

const dividend_yield_rate_law = {
    condition:
        (operation == "set_parameters") &  // operation is "set_parameters"
        (set_parameters_key == "dividendPermyriadPerTransaction") &  // the key is "dividend_yield_rate"
        (set_parameters_value < 500) &  // the value is < 500‱ (5%)
        (timestamp < 1893477600),  // the timestamp < unix timestamp  2030-01-01 00:00:00 (UTC) 

    return_type: no,  // reject the operation
    return_level: 1,
    is_before_operation: true, // reject the operation and skip sandbox check
}

Example 7: Investment program package

Here is an unofficial example program of Simple agreement for future equity (SAFE), a common investment contract by a VC firm:

  1. The VC firm will pay 1000 ETH (1000000000000 Gwei) cash to the DARC as investment
  2. The VC firm will be granted 100,000,000 level-0 tokens (common stock) and 1 level-2 token (board of members)
  3. The VC firm will be granted the right to disable plugins 5, 6, 7
  4. The VC firm will be granted the right to enable plugins 8, 9, 10, 11
  5. The VC firm will be granted the right to change its role to level-5 (majority shareholder level)
  6. It's recommended to sign and scan a PDF document to record the agreement, upload the PDF document to IPFS, and add the IPFS hash QmcpxHN3a5HYnPurtuDs3jDfDSg1LPDe2KVBUG4RifcSbC to the DARC permanent storage array. This can help the emergency agent to verify and fix DARC technical issues if needed.
const vc_addr = "0x1234567890123456789012345678901234567890";  // define my address

pay_cash(1000000000000, 0, 1);  // pay 1000 ETH = 1000000000000 Gwei cash

mint_token([vc_addr], [100000000], [0]);  // mint 100,000,000 level-0 tokens (common stock) to VC firm

mint_token([vc_addr], [1], [2]);  // mint a single 2-level token (board of members) to VC firm

disable_plugins([5, 6, 7], [false, false, false]) // disable previes after-operation plugins 5, 6, 7

enable_plugins([8, 9, 10, 11], [false, false, false, false]) // enable new added plugins 8, 9, 10, which were added before this program

change_member_role(vc_addr, 5);  // change the role of VC firm to level-5 (majority shareholder level)

/** Finally, sign and scan a SAFE document,
 * upload and pin on IPFS, and add the IPFS hash value to the DARC
 * just in case if DARC needs emergency agent to take over the DARC
 */
add_storage(['QmcpxHN3a5HYnPurtuDs3jDfDSg1LPDe2KVBUG4RifcSbC']);

Building the source

Since Hardhat and OpenZeppelin are used, the project can be built using the following commands:

  1. Install dependencies

    We recommend that you use pnpm instead of npm, but npm can also work.

    pnpm is a newer package manager that has some advantages over npm. It is faster, more efficient, and disk-space friendly.

    cd darc-protocal
    npm install
  2. Compile the contracts

    npx hardhat compile
  3. Run the Darc test network

    npm run node
  4. Test contracts

    npx hardhat test
    REPORT_GAS=true npm run test
  5. Deploy contracts

    npm run deploy