A Companion Guide to “First impressions with ERC 725 and ERC 735 - identity and claims” (Part 3)
5. Detail Flow of the Demo: walking through main.js (cont.)
Step 5: Investor adds Fractal ID’s signed KYC claim to their identity contract
Now we have the signature of the claim from Fractal ID. Investor now adds the claim to Investor ClaimHolder contract using addClaim(). Here is the code in main.js.
The required arguments are passed to addClaim(). All the information is required according to the struct Claim defined in ERC-735.
Now we can inspect the struct Claim in Investor ClaimHolder contract. We use getClaimsIdByType() with type 7 (KYC), and getClaim() to get more detail about this claim.
If we match the struct Claim we will see
- the claim type (KYC, 7)
- scheme (ECDSA, 1)
- the issuer, which is the Fractal ID ClaimHolder Contract address
- signature generated in Step 4
- data upon which signature is created (which is a hex of the phrase added at Step 4)
- a URI
We also see the Claim ID, which is computed as the hash of issuer and claim type according to the ClaimHolder.sol (line 22).
bytes32 claimId = keccak256(_issuer, _claimType)
Step 6: Very Good deploys their token and crowdsale contracts
In real life the deployment of token and crowdsale contracts should be done independently but for sake of demo, the deployment happens in this step.
Note the fractalLpAccount in the main.js corresponds to the accounts[2], which is the Very Good Company.
When deployed, the crowdsale contract comes with these arguments,
- rate is 10 tokens per wei
- beneficiary is Very Good Company
- the token address is the Very Good Coin’s contract address deployed (note the Very Good Coin is deployed before the Crowdsale contract)
- the KYC claim provider that this Crowdsale contract accepts
The last item is an addition as it is a specific need for this crowdsale. If we see the inheritance of these contracts and compare the constructors among them, we can tell that trsutedClaimHolder in VeryGoodCrowdsale.sol is now keeping the Fractal ID ClaimHolder contract.
We can take a look on the trustedClaimHolder from the deployed Very Good Crowdsale contract,
The result is a 32-byte version of the Fractal ID ClaimHolder contract (see the table in the introduction part of this session).
Note: I use function selector to call this public variable. With this I do not need the ABI of the Crowdsale contract. The format is
> eth.call({to: <target contract address>, data: <function selector + arguments if needed>})
Step 7: Investor participates in Very Good’s ICO by making a transfer through their identity contract to Very Good’s crowdsale contract
After the Coin and Crowdsale contracts are deployed, Investor is issuing buyTokens() through the Investor ClaimHolder contract. Here is the code in main.js.
This buyTokens() is done in a proxy way,
- Investor Account (i.e. eth.accounts[3]) calls execute() in Investor ClaimHolder contract, passing the 1 ether and target Crowdsale contract address.
- Investor ClaimHolder Contract executes buyTokens() in Crowdsale contract.
Step 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 last step is to see how Very Good Crowdsale contract performs the checking. It is done by a function claimIsValid() in VeryGoodCrowdsale.sol.
The logic to check the validity of a claim
- Obtain the Investor ClaimHolder contract and the Claim Type (KYC, 7) required in this validity check.
- Construct back the Claim ID, which is from the Fractal ID ClaimHolder contract and the Claim Type. This should have the same result on the Claim ID when Investor is adding claim using addClaim() (see Step 5).
- Fetch the Claim from Investor ClaimHolder contract with the computed Claim ID, and collect all the required information from that Claim (see the Claim content in Step 5).
- Construct the data back for signature verification according to the specific algorithm.
- The way of validation is through recovery of the address based on the information available (stored in recovered).
- Perform the same hash on the recovered address to obtain the key, and check whether this key is inside Fractal ID ClaimHolder contract, and the type of the key is a Claim key (CLAIM, 3).
When it returns back a True, the requirement of _preValidatePurchase() is fulfilled in buyTokens(). What remains is the normal process of token handling, i.e. issue tokens to buyer.
We see it is the Investor ClaimHolder contract account (0xa86eb0…), not the Investor account (0x988b5e…), keeping the balance in the Very Good Coin Contract (0x8ac7230489e8000 = 10^19 tokens).
Finally we check the ether balance of both Crowdsale beneficiary account (accounts[2], ~101 ethers) and the Investor account (accounts[3], ~99 ethers). It is 1 ether being transferred from Investor to beneficiary account. Note some gas is paid from both accounts for transactions.
Closing
I wish this lengthy article can serve as a companion guide to Julio’s article and demo, showing more detail how ERC-725/735 is implemented and used in a crowdsale case.
Kudos again to Julio Santos of Fractal as his work really helps me understand more on ERC-725/735, and I also hope all reading this work can benefit as much as I did.