Developer PortalPolymesh DocsSDK DocsRust DocsCommunity

CDD with the SDK

Provide customer due diligence with the Polymesh SDK


tip icon

You ought to look at the previous chapter on KYC with the SDK first.

Although, a CDD service provider is a privileged KYC service provider, adding a CDD claim is an entirely different proposition. A CDD service provider has access to the Polymesh Unique Identity System (PUIS) and is entrusted with publishing cryptographic commitments.

tip icon

See the introduction on identity for a refresher.

In particular:

  1. For brand new users of Polymesh, the CDD provider needs to:
    1. Record a new entry in the PUIS, and return to the user their unique off-chain identifier, or uID;
    2. Create a new account controlled by the primary key asked by the user, and return the Did to the user;
  2. For existing users that need a new CDD attestation, the CDD provider needs to ask or retrieve the user's uID in the PUIS;
  3. When the CDD service provider publishes a CDD attestation associated with an account, the attached CddId needs to be cryptographically calculated using the user's uID and the account's DID. This is needed so that an account can later on self-publish an investor uniqueness claim.
tip icon

See distribute with the SDK for a reminder of the process.

We assume the CDD service provider is named EzCdd and has a way to instantiate a Polymesh client.

const apiEzCdd: Polymesh = await Polymesh.connect({
    "nodeUrl": "wss://alcyone-rpc.polymesh.live", // For instance, or the proper network
    "accountMnemonic": "word51 word52 ..."
});

How to create a new account

This part only applies if the user, say Bob, is brand new to Polymesh, or if Bob wants to create another account for himself.

In that case, the CDD provider needs to create an account. This is a privileged operation, too, one that only a CDD service provider can accomplish. The CDD provider needs to ask Bob for the public key, or address, he wishes to associate with the account.

Information collection

Bob creates a private key and returns the computed address. For instance:

const bobAddress: string = "5ED27JHWh9dPVnhvRfazMRNqrfMgcAUdvgD7hs3969PBuxqe"; // For instance

The provider needs to confirm that this key is not yet assigned to any other account.

const bobKeyInfo: Account = apiEzCdd.getAccount({
    "address": bobAddress
});
const bob: Identity = await bobKeyInfo.getIdentity();
assert(bob === null);

Create the account

With the certainty that the key is not associated with any existing account, it is time to create a new account with this key:

const newAccountQueue: TransactionQueue<Identity> = await apiEzCdd.registerIdentity({
    "targetAccount": bobAddress
});
const bob: Identity = await newAccountQueue.run();

So does Bob have a new account on-chain? Not yet, sadly. Or rather, fortunately. You see, it is worth repeating that all actions of consequence need approval on both ends, and this is no different here. Bob needs to use his private key to approve having it associated with this new account.

So, either Bob goes to his dashboard, and looks for incoming authorisation requests, or Bob fires up his own apiBob and uses the SDK. We assume that EzCdd gave him their const ezCddDid: string.

const bobAccount: CurrentAccount = apiBob.getAccount();
const pendingAuthorizations: AuthorizationRequest[] = await bobAccount.authorizations.getReceived();
const ezCddAuthorization: AuthorizationRequest = pendingAuthorizations
    .find((pendingAuthorization: AuthorizationRequest) => {
        return pendingAuthorization.issuer.did === ezCddDid;
    });
const acceptQueue: TransactionQueue<void> = await ezCddAuthorization.accept();
await acceptQueue.run();

There is only a slight problem with the above. Bob has not yet received his CDD attestation, and therefore cannot publish a transaction to the Polymesh network. So we showed the above to stay in context, but really, Bob has to wait for the below CDD attestation to be published before he can approve, or reject, the account association.

Additionally, the address needs some POLYX to pay for transaction fees. Bob can get that by using the POLY to POLYX bridge, or he can have an agreement with EzCdd to send some to his address. Once again, only after the CDD attestation has been published.

The CDD attestation

Now that Bob has an account on Polymesh, the CDD provider needs to add a CDD attestation to the account. To calculate the cryptographic CddId to include in the attestation, we are going to inspire ourselves from this example script. Let's first clearly identify what is off-chain and what is on-chain:

  • Off-chain:
    • investor's uID, which is Bob's, unique identifier in the PUIS system, and
    • CDD service provider's private key, obviously.
  • On-chain:
    • bob.did: the account number the CDD provider created to identify (one of) Bob's account on-chain,
    • CddId: the identifier the CDD provider will publish after Bob has completed the CDD process, and
    • CDD service provider's address, or primary public key for the service provider's account.

The difficulty here is how to actually calculate the CddId.

Prepare your code

We need to do some extra work because, at its base, the confidential identity library is coded in Rust, like the rest of Polymesh's codebase, and compiled in WebAssembly (WASM), for use in NodeJs. So go ahead:

Calculate off-chain

Bob already has his uID, it was either retrieved from the PUIS by the CDD provider, or saved on file or in the Polymesh Wallet at the time of his first CDD. Either way, the CDD service provider obtained it:

const uIDv4: string = "21d18606-5971-4136-90c1-decdef95fc1e"; // For example
const uID: string = `0x${uIDv4.replace(/-/g, '')}`; // Results in 0x21d186065971413690c1decdef95fc1e

Because of this delegation to WebAssembly, we have to painstakingly format parameters in hexadecimal. This explains the convoluted lines above and below.

bob.did already comes formatted in hexadecimal.

With those in hand, let's calculate the CddId off-chain. Using the imports mentioned above:

const cddIdOutputString: string = confidential_identity.process_create_cdd_id(
    JSON.stringify({
        investor_did: Array.from(hexToU8a(bob.did)),
        investor_unique_id: Array.from(hexToU8a(uID))
    })
);
const cddIdOutput: JSON = JSON.parse(cddIdOutputString);
const cddId: string = `0x${createHexString(cddIdOutput.cdd_id)}`;

Not excessively difficult. We have all the arguments we need to go ahead and publish the result.

Publish the CDD attestation

And we are back to familiar territory.

const nextYear: Date = new Date();
nextYear.setFullYear(nextYear.getFullYear() + 1);
const cddClaimQueue: TransactionQueue<void> = await apiEzCdd.claims.addClaims({
    "claims": [
        {
            "claim": {
                "type": ClaimType.CustomerDueDiligence,
                "id": cddId,
            },
            "target": bob.did,
            "expiry": nextYear,
        },
    ]
});
await cddClaimQueue.run();

With this Bob is now CDD'd and can transact in materially-significant ways, including, as mentioned earlier by accepting the authorisation request.

Transact in ACME

If Bob wants to transact with ACME securities, have a look at his adding an investor uniqueness claim.

Conclusion

We have seen how a CDD service provider needs to:

  • access the PUIS and generate a uID,
  • create new accounts on Polymesh, and
  • publish carefully crafted CDD attestations.