# IDENTITY SDK

> This package facilitates the interaction with Identities stored in the BlockChain.

## Specifications

### Features

- Deploy identities
- List and Manage keys on an Identity
- List and manage Claims on an Identity.
- Issue claims for an Identity.
- Fetch claim data using their URI.

---

## Usage

### Installation

**Install with `npm install @onchain-id/identity-sdk`**

Then require with:

```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');
```


### Tutorials

- [Tutorial Homepage](./docs/tutorials/README.md)
  - [Implement a Claim Issuer service](./docs/tutorials/implement%20a%20claim%20issuer.md)
  - [Restrict access to compliant Identities](./docs/tutorials/restrict%20to%20compliant%20identities.md)
  - [Connect with Identity](./docs/tutorials/connect%20with%20identity.md)

### BlockChain Provider

To interact with the BlockChain, you will need to instantiate a Provider.

The SDK is using [Ethers](https://github.com/ethers-io/ethers.js) to connect with Ethereum network.
Thus, any provider supported by Ethers can be used with the SDK.
This means any standard web3 provider should by supported.

Connect to a default provider:

```javascript
// You can use any standard network name
//  - "homestead"
//  - "rinkeby"
//  - "ropsten"
//  - "kovan"
const ethers = require('ethers');

const provider = ethers.getDefaultProvider('ropsten');

const identity = await Identity.at('0x...', { provider });
```

Connect to JSON RPC:

```javascript
// When using the JSON-RPC API, the network will be automatically detected
// Default: http://localhost:8545
let httpProvider = new ethers.providers.JsonRpcProvider();
```

Connect to any Web3 Provider:

```javascript
// When using a Web3 provider, the network will be automatically detected

// e.g. HTTP provider
let currentProvider = new web3.providers.HttpProvider('http://localhost:8545');

let web3Provider = new ethers.providers.Web3Provider(currentProvider);
```

Connect to metamask:

```javascript
// The network will be automatically detected; if the network is
// changed in MetaMask, it causes a page refresh.

let provider = new ethers.providers.Web3Provider(web3.currentProvider);
```

To use a signer instead of a provider (required to sign transactions):

```javascript
const signer = new ethers.Wallet('PRIVATE_KEY', provider); // Or other signers from EthersJS.

const identity = await Identity.at('0x...', { signer });
```

> Please refer to the [Ethers Providers Documentation](https://docs.ethers.io/ethers.js/html/api-providers.html) for more information.

#### Overriding provider or signer for operation

Every methods that connects to the Blockchain (write or read) accept last `options` arguments to specify the
signer or provider to use for the operation, and for write operation, to override transaction parameters:

```javascript
const ethers = require('ethers');
const identity = new IdentitySDK.Identity('0xadD92F8Ef0729E969c5a98Ea5740c9b644B362e3', provider);

await identity.addKey('0x..', 1, 3, { signer: someSigner, overrides: { gasPrice: ethers.parseUnits('2.0', 'gwei') } });
```  

### Configuration

#### Providers
By default, unsecured providers are not allowed. The SDK will refuse to fetch data on these endpoints.
A claim that has an uri which is not an HTTPS endpoint won't be retrieved.

Allow unsecured endpoints with:
```
const IdentitySDK = require('@onchain-id/identity-sdk');
IdentitySDK.Config.config({ allowUnsecuredProviders: true });
``` 

### SignerModule

Many interaction with identities, and especially claims, require to sign a challenge message.
Functions requiring these signatures expect a SignerModule as argument.

A SignerModule must expose a .getPublicKey() and a .signMessage(message: string) functions.

This is, for instance, a valid SignerModule:

```javascript
const jsrasign = require('jsrasign');

const signer = new SignerModule({
    getPublicKey: async () => ({
        key: "-----BEGIN CERTIFICATE----- my_super_public_key -----END CERTIFICATE-----",
        type: "X.509",
        signingMethod: "SHA-256",
    }),
    signMessage: async (message) => {
        const signer = new jsrsasign.Signature({ alg: 'SHA256withRSA' });
        signer.init("-----BEGIN CERTIFICATE----- my_super_PRIVATE_no_really_super_secret_PRIVATE_key -----END CERTIFICATE-----");
        signer.updateString(message);
        return signer.sign();
    },
});
```

As a convenient method, a SignerModule can be created from an ethers Wallet:

```javascript
const wallet = new IdentitySDK.Providers.Wallet('PRIVATE_KEY', provider);
const signer = new IdentitySDK.SignerModule(wallet);
```

### Examples

Find examples in the [Example folder](./examples).

#### Load a contract

```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');

const provider = new ethers.providers.JsonRpcProvider();

(async () => {
  const identity = new IdentitySDK.Identity(); // Create the Identity Object

  console.log(identity.instantiateAtAddress('0xadD92F8Ef0729E969c5a98Ea5740c9b644B362e3', provider)); // Get the instance of the Identity

  console.log(await identity.instance.getClaimIdsByTopic(1)); // Call directly a function from the Contract.
})();
```

#### Get claims of an Identity

```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');

const provider = new ethers.providers.JsonRpcProvider();

(async () => {
  const identity = new IdentitySDK.Identity('0xadD92F8Ef0729E969c5a98Ea5740c9b644B362e3', provider);

  const claims = await identity.getClaimsByTopic(1);

  console.log(claims);
})();
```

#### Get keys of an Identity

```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');

const provider = new ethers.providers.JsonRpcProvider();

(async () => {
  const identity = new IdentitySDK.Identity('0xadD92F8Ef0729E969c5a98Ea5740c9b644B362e3', provider);

  const keys = await identity.getKeysByPurpose(IdentitySDK.utils.enums.KeyPurpose.CLAIM);
  
  console.log(keys);
  
  console.log(await identity.getKeyPurposes(keys[0].key));
})();
```

#### Deploy an identity

```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');

const provider = new ethers.providers.JsonRpcProvider();

const CLAIM_ISSUER_PRIVATE_KEY = 'issuer_private_key';
const claimIssuerWallet = new IdentitySDK.Providers.Wallet(CLAIM_ISSUER_PRIVATE_KEY, provider);

const DEPLOY_PRIVATE_KEY = 'deploy_private_key';
const deployWallet = new IdentitySDK.Providers.Wallet(DEPLOY_PRIVATE_KEY, provider);

(async () => {
  // Deploy a new Identity
    const identity = await IdentitySDK.Identity.deployNew(
      {
        managementKey: deployWallet.address,
        implementationAuthority: IdentitySDK.constants.implementationAuthorities.kovan,
      },
      { signer: deployWallet }
    );
    await identity.deployed();

  await identity.addKey(IdentitySDK.utils.crypto.keccak256(claimIssuerWallet.address), IdentitySDK.utils.enums.KeyPurpose.CLAIM, IdentitySDK.utils.enums.KeyType.ECDSA);
  
  identity.useProvider(claimIssuerWallet);

  await identity.addClaim(IdentitySDK.utils.enums.ClaimTopic.KYC, IdentitySDK.utils.enums.ClaimScheme.SOME, claimIssuerWallet.address, "a signature", "what a lot of data", "http://localhost:8080/claims/666");
})();
```

#### Deploy an implementation

```javascript
const ethers = require('ethers');
const OnchainID = require('@onchain-id/solidity');

(async () => {
  const provider = ethers.getDefaultProvider('kovan');
  const signer = new ethers.Wallet('<private key>', provider);

  const implementation = await new ethers.ContractFactory(
    OnchainID.contracts.Identity.abi,
    OnchainID.contracts.Identity.bytecode,
    signer
  ).deploy(
    signer.address,
    true,
  );

  await implementation.deployed();

  console.log(implementation.address);
})();
```

#### Deploy an implementation authority

```javascript
const ethers = require('ethers');
const OnchainID = require('@onchain-id/solidity');

(async () => {
  const provider = ethers.getDefaultProvider('kovan');
  const signer = new ethers.Wallet('<private key>', provider);

  const implementationAddress = '<implementation address>';

  const implementationAuthority = await new ethers.ContractFactory(
    OnchainID.contracts.ImplementationAuthority.abi,
    OnchainID.contracts.ImplementationAuthority.bytecode,
    signer
  ).deploy(implementationAddress);

  await implementationAuthority.deployed();

  console.log(implementationAuthority.address);
})();
```

---

#### Get details of a claim
```javascript
const { IdentitySDK } = require('@onchain-id/identity-sdk');

const provider = new ethers.providers.JsonRpcProvider();

(async () => {
    IdentitySDK.config({ allowUnsecuredProviders: true });

    const identity = new IdentitySDK.Identity('0xadD92F8Ef0729E969c5a98Ea5740c9b644B362e3', provider);

    const claims = await identity.getClaimsByTopic(IdentitySDK.utils.enums.ClaimTopic.KYC);

    const claim = new IdentitySDK.Claim(claims[0]);

    await claim.populate();

    console.log(claim);
    
    /*
    Claim {
      data: '0x65773261724950755a302f626e5a744e327961676676376139462f6a3672744a4e3761666a52414c6871493d',
      id: '0x3c6532cc1f4d1a44de8f58d4bde617bef8e744168bf92d783a0e1b66e7c6a44a',
      issuer: '0x8c78fF753c63ea0e8CA1FcA9997A132bC3e6a8F1',
      scheme: 1,
      topic: 1,
      uri: 'http://localhost:8080/claims/b701e350-2a08-11e9-ac7e-517ddf10b60e',
      issuanceDate: 2019-02-06T12:14:12.996Z,
      emissionDate: 2019-02-06T12:15:02.039Z,
      status: 'PENDING',
      publicData: { result: 'clear' } }
     */
})();
```

## Development

- Install dependencies: `yarn`.
- Lint code with `yarn lint`.
- Run unit tests: `yarn test:unit`. You can run unit tests each time you modify a file with `yarn test:unit:watch`.
- Build project with `yarn build`. This will build package into the `dist/` folder from the TypeScript sources.
- Run end to end tests against a builded package: `yarn test:e2e`.
- You can generate type documentation with `yarn build:docs`. This will build the TypeDoc website into `docs/type_doc`.
