npm package: @silencelaboratories/walletprovider-sdk
Description: Frontend SDK for Wallet Providers
Author: Silence Laboratories
Latest version: 4.1.1

# walletprovider-sdk

The client library for Silent Network Wallet Provider Service.

## Table of contents


- [Installing](#installing)
- [Quick start](#quick-start)
- [Documentation](#documentation)
- [SDK Usage](#sdk-usage)
  - [Authentication](#authentication)
  - [Network Without Authentication](#network-without-authentication)
    - [Keygen](#keygen)
    - [Signgen](#signgen)
  - [Network with Authentication](#network-with-authentication)
    - [Keygen](#keygen-1)
      - [Authenticate with EOA wallet](#authenticate-with-eoa-wallet)
      - [Authenticate with Passkey](#authenticate-with-passkey)
    - [Signing](#signing)
    - [Key refresh](#key-refresh)
- [Development](#development)
  - [Build the library](#build-the-library)
  - [End to end tests](#end-to-end-tests)
  - [Generate the documentation](#generate-the-documentation)
  - [Lint the code](#lint-the-code)

# Installing

```sh
npm i @silencelaboratories/walletprovider-sdk
```

# Quick start
We provide simple demo-page what showcases the features of this SDK.
Check the [demo](https://github.com/silence-laboratories/walletprovider-sdk/tree/main/demo/) for a quick start guide.

# Documentation

For description of classes, interfaces, types, please refer to [documentation](https://github.com/silence-laboratories/walletprovider-sdk/tree/main/docs/index.html).

# SDK Usage
## Authentication
Requests to the network can be authenticated by the User, using Externally Owned Account (EOA), by Passkeys, some of the requests, by session keys (also known as ephemeral keys).

- [Wallet-based](./docs/classes/EOAAuth.html) authenticate users using their digital wallet.
- [Passkey](./docs/classes/PasskeyAuth.html) has 2 steps: **register** and **login**. The user registers a passkey with the network, then authenticates request in with the Passkey.
- [Ephemeral keys](./docs/classes/EphAuth.html) Used to issue requests to the network and not prompt the user for the acceptance, contains expiry, and can be (self-)revoked.

The authentication can be required, or not, depending on the version of Silent Network backend.

In *no-authentication* case It's backend responsibility to authenticate the User, and then forward the request to the Silent Network nodes.

For more information, please read [Types of architectures](https://docs.silent.sg/silent-network/architecture) in our high-level documentation.

> This SDK supports both versions of Silent Network, *no-authentication*, and where User authentication is required.

## Network Without Authentication
The request goes to the backend, without any sort of User authentication.

### Initialize the Client object
Create the [NoAuthWalletProviderServiceClient](./docs/classes/NoAuthWalletProviderServiceClient.html), using [ClientConfig](./docs/types/ClientConfig.html). The `wpClient` will connect to the Wallet Provider Backend Service (WPBE).

```typescript
const config = {
  walletProviderUrl: "https://silent-network-no-auth.sandbox.silencelaboratories.com/wpbe1",
  apiVersion: 'v2',
} as ClientConfig;

const wpClient = new NoAuthWalletProviderServiceClient(clientConfig);
const sdk = new NetworkSigner(wpClient);
```

Following snippet will create client to `wpbe1` on our `silent-network-no-auth` sandbox, and will use `v2` endpoints.

Then pass the `wpClient` to the [NetworkSigner](./docs/classes/NetworkSigner.html). And use `NetworkSigner` for interacting with the WPBE.

### Keygen
During keygen provide number of parties `n`, the threshold `t` and [types of the keys](./docs/types/MPCSignAlgorithm.html) you want to generate.

[Example usage](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/e2e-tests/client/src/actions/noAuth/keygen.ts#L75)

```typescript
 const response = await sdk.generateKey(
    config.threshold,
    config.parties,
    keySignAlgs,
  );
```

### Signgen
The message to be signed can be of [different types](./docs/types/SignRequestType.html).
The example usage of different types is [shown here](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/e2e-tests/client/src/test/signature_verification.ts#L48).

[Example usage](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/e2e-tests/client/src/actions/noAuth/signgen.ts#L58-L74)

```typescript
  let sample_message = ...;

  const response = await sdk.signMessage(
    config.threshold,
    config.keyId,
    config.signAlg,
    sample_message,
  );
```

## Network with Authentication
### Initialize the Client object
Create the [WalletProviderServiceClient](./docs/classes/WalletProviderServiceClient.html), using [ClientConfig](./docs/types/ClientConfig.html). The `wpClient` will connect to the Wallet Provider Backend Service (WPBE).

```typescript
  const config = {
    walletProviderUrl: "https://silent-network-auth.sandbox.silencelaboratories.com/wpbe1",
    apiVersion: 'v1',
  } as ClientConfig;

 const wpClient = new WalletProviderServiceClient(clientConfig);
```
Following snippet will create client to our `wpbe1` on our `silent-network-auth` sandbox, and will use `v1` endpoints.


### Keygen
#### Authenticate with EOA wallet
The full working example is in the [demo](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/demo/src/routes/%2Bpage.svelte#L295-L316).

Apart from `wpClient` that connects to the Backend part of the SDK, you are going to need **authenticator module**.

We provide EOA authentication via [EOAAuth](./docs/classes/EOAAuth.html) module. Let's create the `NetworkSigner` with associated `EOAAuth` object.

```ts
  // Authenticate using EOA
  const eoaAuth = new EOAAuth(accountsFromBrowserWallet[0], new BrowserWallet());

  // Create a new signer instance
  const sdk = new NetworkSigner(wpClient, eoaAuth);

```

Now you can generate a key, using the [generateKey](./docs/classes/NetworkSigner.html#generatekey) method. The method accepts optional Policy. No Policy means _allow all operations_.

```ts
const POLICY_ERC20_TRANSFER = new Policy({
  version: '1.0',
  description: 'Simple ERC20 transfer policy',
  rules: [
    new Rule({
      description: 'ERC20 transferFrom: sender/recipient match and value < 10000',
      chain_type: ChainType.Ethereum,
      conditions: [
        {
          logic: Logic.And,
          abi: {
            name: 'transfer',
            type: 'function',
            inputs: [
              {
                name: 'to',
                type: 'address',
              },
              {
                name: 'amount',
                type: 'uint256',
              },
            ],
            outputs: [
              {
                name: '',
                type: 'bool',
              },
            ],
          },
          group: [
            {
              transaction_type: TransactionType.Erc20,
              transaction_attr: TransactionAttribute.Receiver,
              operator: Operator.Eq,
              value: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
            },
            {
              transaction_type: TransactionType.Erc20,
              transaction_attr: "to",
              operator: Operator.Eq,
              value: '0x1758f42af7026fbbb559dc60ece0de3ef81f665e',
            },
            {
              transaction_type: TransactionType.Erc20,
              transaction_attr: "amount",
              operator: Operator.Lt,
              value: 10000,
            },
          ],
        },
      ],
    }),
  ],
});

let signAlgs = ['secp256k1', 'ed25519'];

// Generate new eph key, will be later used in sign requests
const selectedEphSignAlg = 'secp256k1'; // Signing algorithm of Ephemeral Key
const sk = generateEphPrivateKey(selectedEphSignAlg);
const ephPK = getEphPublicKey(sk, selectedEphSignAlg);
const ephId = uuidv4();

const ephClaim = new EphKeyClaim(ephId, ephPK, selectedEphSignAlg, expireAt(60 * 60));

// Generate keys for secp256k1, ed25519, and include Ephemeral Key, Policy in the request
let resp: KeygenResponse[] = await sdk.generateKey(+threshold, +partiesNumber, signAlgs, ephClaim, POLICY_ERC20_TRANSFER);
```

Calling this method will cause to the Digital Wallet window to pop up, requesting the User to sign the request.

The returned response [KeygenResponse](./docs/interfaces/KeygenResponse.html) is a list of DKG results, each contains `keyId`, `publicKey` and `signAlg`. The `publicKey` is the public part of the key generated by Silent Network. The `signAlg` is the sign algorithm of the MPC key. Use the `keyId` in subsequent calls to sign.

The ephemeral `sk` key can be later used in subsequent signgen requests for authenticating.

#### Authenticate with Passkey
First, we need to register user passkey to the network.

[Example usage](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/demo/src/routes/+page.svelte#L513)

```ts
const rpConfig: RelyingPartyConfig = {
  rpId: clusterConfig.rpConfig.rpId,
  rpName: clusterConfig.rpConfig.rpName,
};
userId = newUser();
const passkeyUser = {
  id: userId,
  displayName: 'Alice',
  name: 'alice@gmail.com ' + userId, // For development purposes
};

const passkeyAuth = new PasskeyRegister(rpConfig, passkeyUser);
// Create a new signer instance
const sdk = new NetworkSigner(wpClient, passkeyAuth);

// Generate a new key
let resp: RegisterPasskeyResponse = await sdk.registerPasskey();
```

We provide Passkey login authentication via [PasskeyAuth](./docs/classes/PasskeyAuth.html) module. Let's create the `NetworkSigner` with associated `PasskeyAuth` object.

[Example usage](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/demo/src/routes/+page.svelte#L441)
```ts
const credentialId = getPasskeyCredentialId();
if (!credentialId) {
  throw new Error('Must register passkey first');
}
const passkeyAuth = new PasskeyAuth(
  rpConfig,
  credentialId,
);

// Create a new signer instance
const sdk = new NetworkSigner(wpClient, passkeyAuth);

// Generate a new key
const sk = generateEphPrivateKey(selectedEphSignAlg);
const ephPK = getEphPublicKey(sk, selectedEphSignAlg);
const ephId = uuidv4();

const ephClaim = new EphKeyClaim(ephId, ephPK, selectedEphSignAlg, expireAt(60 * 60));
let resp: KeygenResponse[] = await sdk.generateKey(+threshold, +partiesNumber, signAlgs, ephClaim);
```

Now you can generate a key like in the EOA example by calling the [generateKey](./docs/classes/NetworkSigner.html#generatekey) method.

Calling this method will prompt the device to request [Passkey User Verification](https://web.dev/articles/webauthn-user-verification). Once user verification is done, the [KeygenResponse](./docs/interfaces/KeygenResponse.html) is returned.

The `sk` key can be later used in subsequent signgen requests.

### Signing
The full signing example is [here](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/demo/src/routes/+page.svelte#L374).

The workflow is similar to the keygen process. The core objects to use are the [NetworkSigner](./docs/classes/NetworkSigner.html), [WalletProviderServiceClient](./docs/classes/WalletProviderServiceClient.html), and the [ephemeral authenticator module](./docs/classes/EphAuth.html).
```ts
const authModule = new EphAuth(selectedEphId, ephSK, selectedEphSignAlg);
// Create a new signer instance
const sdk = new NetworkSigner(wpClient, authModule);
```

Use the [SignRequestBuilder](./docs/classes/SignRequestBuilder.html) builder to generate the sign message payload. Then call the [signMessage](./docs/classes/NetworkSigner.html#signmessage) method to run the signing process.

```ts
const data = encodeFunctionData({
      abi: ERC_20_ABI,
      functionName: 'transfer',
      args: ['0x1758f42af7026fbbb559dc60ece0de3ef81f665e', 1000n],
});

const eip1559Tx: UnsignedTransactionRequest = {
      from: ethAddress,
      to: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238', // USDC Contract
      value: '0x0',
      data,
      chainId: 80002,
      nonce: 0,
      gas: '0x18473',
      maxFeePerGas: '0x18473',
      maxPriorityFeePerGas: '0x1000',
};
const signMessage = new SignRequestBuilder()
    .setRequest(
      uuidv4(),
      JSON.stringify(eip1559Tx),
      'EIP1559',
    )
    .setRequest(
      uuidv4(),
      '4549502d313931206d657373616765',
      'rawBytes',
    )
    .build();

let resp = await sdk.signMessage(threshold, selectedKeyId, 'secp256k1', signMessage);
```

The [SignResponse](./docs/interfaces/SignResponse.html) contains the signature `sign`, the recovery ID `recid` and the transaction ID `transactionId`.

### Key refresh

The full key refresh example is [here](https://github.com/silence-laboratories/walletprovider-sdk/blob/564cca4bb59658a6e477a59e8ea554a67c26b161/demo/src/routes/+page.svelte#L351).

The workflow is similar to the keygen process.

```ts
const algSign = 'secp256k1'; // Signing algorithms of Ephemeral Key

// Create EOA authenticator
const eoaAuth = new EOAAuth(
  accountsFromBrowserWallet[0],
  new BrowserWallet(),
);

// Create a new signer instance
const sdk = new NetworkSigner(wpClient, eoaAuth);
```

Now you can refresh the key (before doing this, make sure you've already generated the key), using the [refreshKey](./docs/classes/NetworkSigner.html#refreshkey) method.

```ts
// Refresh the key
let resp: KeyRefreshResponse = await sdk.refreshKey(+threshold, selectedKeyId, mpcKeySignAlg);
```
The returned response [KeyRefreshResponse](./docs/types/KeyRefreshResponse.html) contains `keyId`, `publicKey` and `signAlg` of the refreshed MPC key.


# Development

## Build the library

```bash
npm i
npm run build
```

The output will be in the `dist` folder.

## End to end tests
Please refer to [README.md](./e2e-tests/README.md) for instructions how to execute them.

## Generate the documentation

```bash
npm run docs
```

## Lint the code

```bash
./local_ci.sh
```