Júlio Santos

@julio_santos

First impressions with ERC 725 and ERC 735 — identity and claims

Implementing a simple KYC claim verification for a crowdsale

At Fractal, we’re focused on improving the ICO experience, from both a UX and a regulatory compliance perspective. One of the crucial bits of a successful ICO is proper participation eligibility verification (KYC, AML, CFT, capital markets regulations, etc). As such, we’re closely watching the identity space, following projects such as Sovrin, Taqanu, uPort, and Civic, and experimenting with potential integrations.

Fabian Vogelsteller, the creator of the Mist Browser and web3.js, is working on a new identity standard for Ethereum, ERC 725. When used together with his other brainchild ERC 735, a standard for the management of identity claims, it is a very powerful tool for working with real identities on-chain.

Inspired by Nick Poulden, who took these standard drafts and created a great visual demonstration of how they can be combined, I decided to put together a small write-up that’s more focused on code.

Photo by CMDR Shane on Unsplash

An overview of the standards

One of the best things about ERC 725 and 735 is that they’re open, community-driven, and participatory standards. They’re also quite simple to understand, as they’re small, targeted standards which do one thing each but do it really well: manage identities and claims on those identities.

ERC 725: Identity

The following describes standard functions for a unique identity for humans, groups, objects and machines.
This identity can hold keys to sign actions (transactions, documents, logins, access, etc), and claims, which are attested from third parties (issuers) and self attested (#ERC735), as well as a proxy function to act directly on the blockchain.

ERC 725 is a standard for publishing and managing an identity via a smart contract on an EVM-based blockchain, such as Ethereum. This identity is represented by its chain address.

We can associate several keys with this identity. These keys can have different purposes: the standard currently proposes MANAGEMENT, ACTION, CLAIM and ENCRYPTION. Keys are keccak256s of public addresses¹, and as such map to externally owned accounts (wallets) or smart contracts.

Another important concept is that an ERC 725 contract can be used as a proxy, through its execute / approve methods. If a third party recognizes the legitimacy of a given identity contract, its owner can call execute on it to transact with said third party, as we’ll see below in our KYC example.

Multiple keys are particularly useful in the context of privileged access. For example, suppose this identity is not for an individual, but an organization. Members holding less privileged keys can submit transactions to execute, but only those with access to higher-privilege keys can approve the execution of those transactions.

We can go even further down this rabbit hole. Since keys can also map to smart contracts instead of wallets, further access requirements could potentially be implemented. For example, having an ACTION key which maps to an AccessControl contract allows us to build arbitrarily complex access control levels in this contract. This is important because it keeps the standard itself simple, while simultaneously supporting every imaginable use case.

Another example has to do with revoking claims. If we’re using our identity to make claims on another identity, it can be useful to have several CLAIM keys — deleting any one of them would cause the associated claims to lose their validity.

For a real-world example, suppose Mozilla has an Identity contract. Aside from whatever else Mozilla chooses to do with that identity, The Firefox software team could hold one of the keys belonging to that identity, with which they’d sign software checksums upon release.

For our KYC use case, described below, we proposed a change to the standard which would make execute a payable method, allowing us to forward Ether with the transaction to execute. An alternate approach would be to use two separate transactions: send Ether to the contract first, and then move it with execute.

ERC 735: Claim Holder

The following describes standard functions for adding, removing and holding of claims. These claims can attested from third parties (issuers) or self attested.
This standardised claim holder interface will allow Dapps and smart contracts to check the claims about a claim holder. Trust is here transfered to the issuers of claims.

ERC 735 deals with the management of claims made about an ERC 725 identity. It facilitates an emergence of a web of trust, by relying on the claims of trusted third parties about a given identity.

These claims can be of several topics, from biometric data, to email account ownership. This is particularly useful for our KYC use case, described below. As ICO issuers, we can work with a trusted KYC provider to determine whether or not an investor is eligible for purchasing tokens.

In order for someone to add a claim to their identity, they must first request it of a relevant trusted third party. This third party (the claim issuer) will sign a message containing three items: the identity’s address, the claim topic, and optionally some data to go along with it (for example, a hash of the KYC data). The identity owner would then store this claim in their identity contract (alternatively, the claim issuer can also add the claim themselves, which would have to be approved by the identity owner).

Note that claims can also be self-attested, which can be enough for a lot of other use cases (email and name come to mind, for simple applications with no strict KYC requirements, like a regular news website).

KYC claims for crowdsales

Entities

This demo considers the following entities.

  • Very Good, a construction company looking to upend the real estate development market.
  • Fractal ID, an identity provider who performs KYC/AML checks.
  • Investor, who needs to clear KYC to participate in a Very Good’s ICO.

Flow overview

After compiling the contracts, our demonstration takes the following steps.

  1. Fractal ID deploys its own identity contract.
  2. Fractal ID adds a CLAIM key to its identity contract.
  3. Investor deploys their identity contract.²
  4. After Investor successfully undergoes KYC, Fractal ID signs a KYC claim for Investor.
  5. Investor adds Fractal ID’s signed KYC claim to their identity contract.³
  6. Very Good deploys their token and crowdsale contracts.
  7. Investor participates in Very Good’s ICO by making a transfer through their identity contract to Very Good’s crowdsale contract.
  8. Very Good’s crowdsale contract confirms that the Investor’s identity contract contains a KYC claim by Fractal ID before accepting the investment.

The smart contracts

I took Origin’s implementation of the standards as a starting point for this work. I had to slightly change the way they worked: because of the payable issue described above, this implementation wasn’t prepared to relay value to the execution target. Here’s a simple inheritance diagram of the contracts, for clarity.

+---------------+     +---------------+
| | | |
| ERC 725 | | ERC 735 |
| | | |
+-------+-------+ +-------+-------+
^ ^
| |
+-------+-------+ |
| | |
| KeyHolder | |
| | |
+-------+-------+ |
^ |
| |
+-------+-------+ |
| | |
| ClaimHolder +-------------+
| |
+---------------+

The token and crowdsale contracts were lifted out of OpenZeppelin Solidity.

In order to deploy and interact with these contracts, I used web3.js to put together a simple JS script. (here’s the full repo if you’re curious)

Step 1/4 — Deploying the identity contracts

Both Fractal ID and Investor need to deploy a ClaimHolder contract, in order to hold all their keys and claims⁴. This contract implements both ERC 725 and ERC 735.

After deployment, Fractal ID will create a CLAIM purpose key. This is the key associated with the signature of KYC claims.

var fractalIdClaimsKey = web3.utils.keccak256(
fractalIdClaimsAccount
);
fractalIdClaimHolder.methods.addKey(
fractalIdClaimsKey,
KEY_PURPOSES.CLAIM,
KEY_TYPES.ECDSA,
).send({
from: fractalIdAccount,
gas: 4612388,
});

As described above, the key is the keccak256 hash of the address of its controlling account. Fractal ID calls the addKey method on the ClaimHolder contract (ERC 725), passing it this key, the key’s purpose, and its keyType.

Finally, Very Good needs to deploy their token and crowdsale contracts, and transfer VeryGoodCoin’s ownership to the VeryGoodCrowdsale contract, so it can mint tokens as needed to reward contributions.

veryGoodCoin.methods.transferOwnership(
veryGoodCrowdsale.options.address,
).send({
from: veryGoodAccount,
});

Step 2/4 — Signing a KYC claim

After Investor goes through Fractal ID’s online KYC process, Fractal ID signs a KYC claim.

var hexedData = web3.utils.asciiToHex("Investor is VBR V0 legit.");
var hashedDataToSign = web3.utils.soliditySha3(
investorClaimHolder.options.address,
CLAIM_TYPES.KYC,
hexedData,
);
var signature = await web3.eth.sign(
hashedDataToSign,
fractalClaimsAccount,
);

Step 3/4 — Adding the KYC claim to the Investor contract

Fractal ID then facilitates this signed claim to Investor, who adds it to their identity contract³.

var claimIssuer = fractalIdClaimHolder.options.address;
var addClaimABI = await investorClaimHolder.methods
.addClaim(
CLAIM_TYPES.KYC,
CLAIM_SCHEMES.ECDSA,
claimIssuer,
signature,
hexedData,
"https://www.trustfractal.com/business/",
).encodeABI();
investorClaimHolder.methods.execute(
investorClaimHolder.options.address,
0,
addClaimABI,
).send({
gas: 4612388,
from: investorAccount,
});

This is done through the addClaim method (ERC 735) on the ClaimHolder contract, which we feed a topic, a scheme, the issuer, the signature, the data, and a uri⁵.

The way execute works is as follows. We pass it a destination address, an Ether value, and a serialized method call. If we call it with a MANAGEMENT or ACTION key, the request gets approved and executes. If we call it with a key of any other purpose, a higher privilege key has to subsequently approve the execution.

Step 4/4 — Participating in the ICO

In order for Investor to participate in the ICO, we’ll also be making use of the execute method. The case here is much easier to understand: as Fractal ID’s identity claim is present on Investor’s identity contract, and Very Good’s crowdsale contract will be checking for a KYC claim on the message sender, this contract must necessarily be the caller.

var investABI = veryGoodCrowdsale.methods.buyTokens(
investorClaimHolder.options.address
).encodeABI();
var investmentAmount = web3.utils.toWei("1", "ether");
investorClaimHolder.methods.execute(
veryGoodCrowdsale.options.address,
investmentAmount,
investABI,
).send({
gas: 4612388,
from: investorAccount,
value: investmentAmount,
});

All checks should pass now, and Very Good’s crowdsale contract will mint new tokens and assign their ownership to Investor’s identity contract! 🎉

Thanks to Fabian Vogelsteller, Hugo Peixoto and João Gradim for reading drafts of this post.

¹ More precisely, keccak256 is only recommended by the standard for non-hex and keys longer than 32 bytes.

² Fractal ID could also do this on behalf of Investor.

³ There’s another flow suggested by ERC 725, which is for Fractal ID to call addClaim on the Investor contract. This claim could be accepted by the Investor’s identity contract immediately, or undergo an approval process first.

Technically, a KeyHolder contract for Fractal ID would suffice.

Note that we’re using encodeABI to serialize the addClaim method call, and piping it through execute. Investor could instead have called addClaim directly, using the account mapping to the appropriate privileges.

Planning an ICO? At Fractal, we offer a hassle-free, user-friendly launchpad solution combined with a comprehensive customer identification service (KYC/AML) to successfully deploy your token launch with ease.

More by Júlio Santos

Topics of interest

More Related Stories