a16z "Can't be Evil" licenses - a practical guide
Tags: blockchain, opensource, crypto, nft
Published: 2022-10-21 18:19:22

_This guide provides detailed documentation on how to install and use a16z “Can’t be Evil” licenses on Casper._

**Introduction**
There are many uncertainties regarding copyright and ownership in the NFT space. Users purchase tokenized avatars and artworks, whilst unaware of the legal situation. Resarachers at the venture capital firm “a16z” have made an effort to solve these issues with their “Can’t be evil” NFT licenses. According to a16z, NFTs need to be licensed, as owning the token Id on the blockchain may not be enough to legally reproduce or display an artwork.

The a16z “Can’t be evil” NFT licenses include [sets of rules](https://www.plagiarismtoday.com/wp-content/uploads/2022/09/CBE_LicenseChart_FA-1536x864-1-700x394.webp) regarding ownership and copyright. Rights and permissions granted through the license only apply to the owner of the NFT. Licenses make it easier to clearly communicate which rights are or are not granted to the customer.

[Read more](https://a16zcrypto.com/introducing-nft-licenses/) on a16z “Can’t be Evil” licenses.

**Smart Contracts**
a16z has already published their licenses on Arweave. Additionally, a16z released this Solidity smart contract:

```
// SPDX-License-Identifier: MIT
// a16z Contracts v0.0.1 (CantBeEvil.sol)
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "./ICantBeEvil.sol";

enum LicenseVersion {
    CBE_CC0,
    CBE_ECR,
    CBE_NECR,
    CBE_NECR_HS,
    CBE_PR,
    CBE_PR_HS
}

contract CantBeEvil is ERC165, ICantBeEvil {
    using Strings for uint;
    string internal constant _BASE_LICENSE_URI = "ar://_D9kN1WrNWbCq55BSAGRbTB4bS3v8QAPTYmBThSbX3A/";
    LicenseVersion public licenseVersion; // return string
    constructor(LicenseVersion _licenseVersion) {
        licenseVersion = _licenseVersion;
    }

    function getLicenseURI() public view returns (string memory) {
        return string.concat(_BASE_LICENSE_URI, uint(licenseVersion).toString());
    }

    function getLicenseName() public view returns (string memory) {
        return _getLicenseVersionKeyByValue(licenseVersion);
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
        return
            interfaceId == type(ICantBeEvil).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    function _getLicenseVersionKeyByValue(LicenseVersion _licenseVersion) internal pure returns (string memory) {
        require(uint8(_licenseVersion) <= 6);
        if (LicenseVersion.CBE_CC0 == _licenseVersion) return "CBE_CC0";
        if (LicenseVersion.CBE_ECR == _licenseVersion) return "CBE_ECR";
        if (LicenseVersion.CBE_NECR == _licenseVersion) return "CBE_NECR";
        if (LicenseVersion.CBE_NECR_HS == _licenseVersion) return "CBE_NECR_HS";
        if (LicenseVersion.CBE_PR == _licenseVersion) return "CBE_PR";
        else return "CBE_PR_HS";
    }
}
```
In order to support “Can’t be Evil” licenses in Casper smart contracts, the above contract has been translated to Rust:
```
#![no_std]
#![no_main]

#[cfg(not(target_arch = "wasm32"))]
compile_error!("target arch should be wasm32: compile with '--target wasm32-unknown-unknown'");
extern crate alloc;

use alloc::{
    string::{String, ToString},
    vec,
};

use casper_types::{
    contracts::NamedKeys, runtime_args, ApiError, CLType, CLValue, ContractHash, EntryPoint,
    EntryPointAccess, EntryPointType, EntryPoints, Parameter, RuntimeArgs, URef,
};

use casper_contract::{
    contract_api::{
        runtime,
        storage::{self},
    },
    unwrap_or_revert::UnwrapOrRevert,
};

#[repr(u16)]
enum A16ZError {
    InvalidLicenseVersion = 0,
}
impl From<A16ZError> for ApiError {
    fn from(e: A16ZError) -> Self {
        ApiError::User(e as u16)
    }
}

const _BASE_LICENSE_URI: &str = "ar://_D9kN1WrNWbCq55BSAGRbTB4bS3v8QAPTYmBThSbX3A/";
const CONTRACT_PACKAGE_HASH: &str = "a16z-contract-hash";
const CONTRACT_HASH_KEY_NAME: &str = "contract_hash";

#[no_mangle]
pub extern "C" fn getLicenseURI() {
    let licenseVersion: u64 = runtime::get_named_arg("_licenseVersion");
    match licenseVersion {
        0 => licenseVersion,
        1 => licenseVersion,
        2 => licenseVersion,
        3 => licenseVersion,
        4 => licenseVersion,
        5 => licenseVersion,
        _ => runtime::revert(A16ZError::InvalidLicenseVersion),
    };

    let licenseURI: String = String::from(_BASE_LICENSE_URI) + &licenseVersion.to_string();
    let licenseUriCLValue: CLValue = CLValue::from_t(licenseURI).unwrap_or_revert();
    runtime::ret(licenseUriCLValue);
}

#[no_mangle]
pub extern "C" fn getLicenseName() {
    let licenseVersion: u64 = runtime::get_named_arg("_licenseVersion");
    match licenseVersion {
        0 => runtime::ret(CLValue::from_t(String::from("CBE_CC0")).unwrap_or_revert()),
        1 => runtime::ret(CLValue::from_t(String::from("CBE_ECR")).unwrap_or_revert()),
        2 => runtime::ret(CLValue::from_t(String::from("CBE_NECR")).unwrap_or_revert()),
        3 => runtime::ret(CLValue::from_t(String::from("CBE_NECR_HS")).unwrap_or_revert()),
        4 => runtime::ret(CLValue::from_t(String::from("CBE_PR")).unwrap_or_revert()),
        5 => runtime::ret(CLValue::from_t(String::from("CBE_PR_HS")).unwrap_or_revert()),
        _ => runtime::revert(A16ZError::InvalidLicenseVersion),
    };
}

#[no_mangle]
pub extern "C" fn call() {
    let entry_points = {
        let mut entry_points = EntryPoints::new();
        let getLicenseURI = EntryPoint::new(
            "getLicenseURI",
            vec![Parameter::new("_licenseVersion", CLType::U64)],
            CLType::String,
            EntryPointAccess::Public,
            EntryPointType::Contract,
        );
        let getLicenseName = EntryPoint::new(
            "getLicenseName",
            vec![Parameter::new("_licenseVersion", CLType::U64)],
            CLType::String,
            EntryPointAccess::Public,
            EntryPointType::Contract,
        );
        entry_points.add_entry_point(getLicenseURI);
        entry_points.add_entry_point(getLicenseName);
        entry_points
    };
    let named_keys = {
        let mut named_keys = NamedKeys::new();
        named_keys
    };
    storage::new_contract(
        entry_points,
        Some(named_keys),
        Some(String::from(CONTRACT_PACKAGE_HASH)),
        Some(String::from(CONTRACT_HASH_KEY_NAME)),
    );
}
```
**a16z license URIs ( for NFT-developers )**
Platforms like Opensea comply with the [eip-721 metadata standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). Learn more [here](https://docs.opensea.io/docs/metadata-standards) .

Where the “Can’t be Evil” license URI is stored depends on which metadata schema or standard makes the most sense for a project to follow. Most NFT platforms on Casper will likely comply with the [CEP-78 metadata standard](https://github.com/casper-ecosystem/cep-78-enhanced-nft/blob/dev/README.md). As a metadata URI is a link to a server that returns JSON data, it makes the most sense to include the license in the data returned by the backend.

Important: The NFT metadata URI is not the same as the License URI. The License URI is an arweave link to a .doc file, whilst the NFT URI fetches JSON, such as the name, description and image URL from a backend.

As a16z licenses are quite novel, there is not yet a standardized schema to follow. Depending on the legal situation, it might be appropriate to include a license URI or URL in an NFT’s “description”. Casper supports custom metadata schemata, therefore the license URI could be included in the NFT metadata on mint. However, when using custom metadata, other platforms may not be able to integrate the NFTs without manually adding support for a collection / metadata schema.

Read more about metadata on Casper [here](https://github.com/casper-ecosystem/cep-78-enhanced-nft/blob/dev/README.md).
**Casper a16z Installation guide**
Follow the steps below to compile, and deploy the a16z “Can’t be Evil” contract on casper.

- Clone the git repository
```
chef@jonas:~/Desktop$ git clone https://github.com/jonas089/a16z-casper
chef@jonas:~/Desktop$ cd a16z-casper/contract
```
- Compile the a16z contract

```
chef@jonas:~/Desktop/a16z-casper/contract$ cargo build --release --target wasm32-unknown-unknown
```
=> compiles contract.wasm to ./target/wasm32-unknown-unknown/release


![project structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/86gvw7wq3rqn2yqb0djz.png)

- Deploy the a16z contract ( to Casper Testnet )
```
chef@jonas:~/Desktop/a16z-casper/contract/target/wasm32-unknown-unknown/release$ casper-client put-deploy --node-address NODE_ADDRESS --secret-key PRIVATE_KEY_PATH --payment-amount GAS --chain-name casper-test --session-path contract.wasm
```
Example:
![explorer snapshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cob8zwuvigiq8iua57vd.png)
Verify the deploy was successful using the [explorer](https://testnet.cspr.live/):
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0r69igb2ht2thg79c7wh.png)

- Query the contract hash of the installed contract

```
chef@jonas:~/Desktop$ casper-client get-state-root-hash -n NODE_ADDRESS
```
=> outputs a state root hash.

```
chef@jonas:~/Desktop$ casper-client query-global-state -n NODE_ADDRESS -s STATE_ROOT_HASH --key YOUR_ACCOUNT_HASH
```
=> outputs account information, including the contract hash.

**Call the a16z Contract to get a License URI**
Query the URI for a “Can’t be Evil” license by calling the “getLicenseURI” endpoint, passing the a16z contract hash and the license version as runtime arguments:

```
...
#[no_mangle]
pub extern "C" fn simulateThirdParty(){
    let _licenseVersion:u64 = 0;
    let contract_hash:ContractHash = runtime::get_named_arg("CONTRACT_HASH");
    let licenseURI:String = runtime::call_contract::<String>(
        contract_hash,
        "getLicenseURI",
        runtime_args!{
            "_licenseVersion" => _licenseVersion
        }
    );
    // do something with licenseURI
}
...
```

_runtime::call_contract_ takes in the contract hashto call the “getLicenseURI” entry point of the a16z contract.
The “getLicenseURI”entry point will return a URI for the “CBE_CC0” license ( licenseVersion 0 ).

_list of all a16z license versions_
```
0 => CBE_CC0
1 => CBE_ECR
2 => CBE_NECR
3 => CBE_NECR_HS
4 => CBE_PR
5 => CBE_PR_HS
```
**Summary**
Thanks to a16z, licensing NFTs is simple and straightforward. Both a Solidity and Rust smart contract is available today and users can start using “Can’t be Evil” licenses on various blockchains now. Licenses are relevant, as the current legal situation regarding NFT copyrights is unclear and users should have an interest in knowing which data they actually own and to what extent they can use it.