> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shinami.com/llms.txt
> Use this file to discover all available pages before exploring further.

# zkLogin wallet API

> User-controlled wallets to make Web3 unseen

## Overview

zkLogin is a Sui primitive that allows users to transact on Sui using the OAuth login flows they're familiar with, eliminating the friction of handling cryptographic keys or remembering mnemonics. zkLogin can be thought of as a two-factor authentication scheme, since sending a transaction requires both a credential from a recent OAuth login and a salt managed by someone other than the OAuth provider (in this case, Shinami). To learn more, see [the Sui Foundation's zkLogin doc](https://docs.sui.io/concepts/cryptography/zklogin).

You'll find API endpoints and key usage notes below. If you ever need help you can [reach out to us](/help-center/overview#contacting-support).

### Use Cases

Core use cases include app-managed NFTs or closed-loop tokens. For a breakdown of the wallets we offer and wallet use-cases, see our [high-level guide](/product-overviews/sui/wallets#wallet-services-overview).

### Authentication, Rate Limits, and Error Handling

**Authentication**

You authenticate via an access key passed in a header ('X-Api-Key: ACCESS\_KEY') or in the request url, e.g. `https://api.us1.shinami.com/sui/zkprover/v1/ACCESS_KEY`. We recommend using a request header and not putting access keys in your request URLs for reduced visibility (in logs, etc). These steps are done automatically by our [TypeScript SDK](https://www.npmjs.com/package/@shinami/clients).

For more information, including how to set up an access key with Wallet Services rights, see our [Authentication and API Keys](/developer-guides/core-integration-topics/authentication-and-api-keys) guide.

<Warning>
  **Call this API from your backend**

  Shinami Wallet Services do not support CORS requests, so if you call these APIs from your frontend you'll get a CORS error. This is for security reasons: exposed wallet information could lead to malicious actors signing transactions on behalf of your users.
</Warning>

**Error Handling**

See our Error Reference for guidance on the errors you may receive from our services, including a section on errors specific to the [zkLogin wallet API](/developer-guides/core-integration-topics/error-reference#sui-zklogin-wallet-api).

**Rate Limits**

When you surpass the QPS limit for a key, we return a JSON-RPC error code `-32010`. We recommend implementing retries with a backoff to handle any rate limits errors that arise. You can also [adjust the rate limits of your keys](/developer-guides/core-integration-topics/authentication-and-api-keys#update-a-key%E2%80%99s-qps%2Fcups-values) to better balance your QPS allotment across your keys.

We also have a limit of two zk proofs per address per minute. When you hit this limit, we return a [JSON-RPC code `-32012`](/developer-guides/core-integration-topics/error-reference#sui-zklogin-wallet-api).

### zkLogin Addresses

A zkLogin wallet address is derived from `iss, aud, sub, salt` in the OpenID connect response, where:

* `iss`: The OpenID provider.
* `aud` : The unique identifier assigned to your application by the OpenID provider.
* `sub`: The OpenID provider's locally unique and never reassigned identifier for the user.
* `salt`: A consistent, ideally unique, value used to unlink the OpenID identifier with the on-chain address.

As a result, a user will have a new zkLogin wallet address if any of the above values change. For example, if one of your users logs in via Google and then Twitch - or with one Google email and then another - and you use both credentials with zkLogin, each credential will be linked to a different address. To learn more, see [the Sui Foundation's zkLogin doc.](https://docs.sui.io/concepts/cryptography/zklogin#will-my-zklogin-address-ever-change)

## Tutorial + SDKs

The endpoints below show sample requests using our [TypeScript SDK](https://www.npmjs.com/package/@shinami/clients) (in addition to cURL).

We also have a [Next.js zkLogin SDK](https://www.npmjs.com/package/@shinami/nextjs-zklogin), which aims to provide full-stack support for building user authentication and Sui transaction execution into your Next.js application, using zkLogin primitives. We built a [Next.js starter template](https://github.com/shinamicorp/shinami-typescript-sdk/tree/aaeed1e9774dcf027e082bb699165710e9e6a4cb/examples/nextjs-zklogin) to quickly get you off the ground using this SDK. Even if you are starting with an existing Next.js application, it's recommended to check out that example as a reference end-to-end implementation. Our [Next.js tutorial](/developer-guides/sui/tutorials/zklogin-nextjs-app-template) has guidance on running our zkLogin app starter template.

## zkLogin wallet service methods

### shinami\_zkw\_getOrCreateZkLoginWallet

Retrieves a zkLogin wallet along with its associated salt, creating a new wallet if necessary. This method provides the option to create and use multiple zkLogin wallets per (user, OAuth provider) pair by using the optional `subWallet` parameter. Note that your users will already have a different zkLogin wallet for each OAuth provider they use, whose `jwt` you pass to this method (see [zkLogin Addresses](/api-docs/sui/wallet-services/zklogin-wallet-api#zklogin-addresses) for more details).

On the free tier you have a limit of wallet creations per month as shown on the "Sui Wallet Services" tab of the [billing page](https://app.shinami.com/billing/) in your dashboard (where you can also see how to upgrade if needed). If you hit this limit, you will get a [JSON-RPC code `-32012`](/developer-guides/core-integration-topics/error-reference#sui-zklogin-wallet-api) and should not retry. All other wallet operations will still work for the month, like signing with wallets you've already created.

**Request Parameters**

| Name         | Type      | Description                                                                                                                                                                                                                                                                                   |
| :----------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| jwt          | `String`  | A valid JWT signed by one of the supported OpenID providers. The JWT nonce must be prepared according to the [zkLogin requirements](https://docs.sui.io/guides/developer/cryptography/zklogin-integration#get-jwt) .                                                                          |
| keyClaimName | `String`  | Optional. The claim name in the JWT that identifies a particular user. Defaults to "sub". Currently, Mysten's zkLogin implementation only supports the "sub" field of the OpenID spec. This parameter allows for forward compatibility as the implementation expands to support other values. |
| subWallet    | `Integer` | Optional. The sub-wallet id, which enables the same OpenID user to have more than one wallet address tied to your app. Defaults to `0`                                                                                                                                                        |

**Example Request Template**

The TypeScript example uses the [Shinami Clients SDK](https://www.npmjs.com/package/@shinami/clients), which you can install with:

<CodeGroup>
  ```bash Shell theme={null}
  npm install @shinami/clients
  ```
</CodeGroup>

Replace all instances of `{{name}}` with the actual value for that name.

<CodeGroup>
  ```bash cURL expandable theme={null}
   curl https://api.us1.shinami.com/sui/zkwallet/v1 \
   -X POST \
   -H 'X-API-Key: {{walletAccessKey}}' \
   -H 'Content-Type: application/json' \
   -d '{
           "jsonrpc":"2.0",
           "method":"shinami_zkw_getOrCreateZkLoginWallet",
           "params":[
               "{{signedJWTToken}}"
           ],
           "id":1
        }'
  ```

  ```bash Shinami TypeScript SDK theme={null}
  import { ZkWalletClient } from "@shinami/clients/sui";

  // Obtain wallet access key from your Shinami web portal.
  const zkWalletClient = new ZkWalletClient({{walletAccessKey}});

  // Prepare a nonce according to the zkLogin requirements.
  // Obtain a valid jwt with that nonce from a supported OpenID provider.

  // Get zkLogin wallet salt.
  const walletInfo = await zkWalletClient.getOrCreateZkLoginWallet({{jwt}});
  ```
</CodeGroup>

**Example Response**

<CodeGroup>
  ```bash cURL expandable theme={null}
  {
      "jsonrpc": "2.0",
      "result": {
          "userId": {
              "iss": "https://id.twitch.tv/oauth2",
              "aud": "2bc0920c90ufn0adomxlca3pldbdpo",
              "keyClaimName": "sub",
              "keyClaimValue": "984333370"
          },
          "subWallet": 0,
          "salt": "kL+fRWN8888cKl5D72qzCQ==",
          "address": "0x6a7b07da9d2c210bbbe044dbe29f0c5ae20444584a4d8ee9a804839f8256ea3c"
      },
      "id": 1
  }
  ```

  ```bash Shinami TypeScript SDK theme={null}
  {
      userId: {
          iss: 'https://id.twitch.tv/oauth2',
          aud: '2bc0920c90ufn0adomxlca3pldbdpo',
          keyClaimName: 'sub',
          keyClaimValue: '984333370'
      },
      subWallet: 0,
      salt: 192403790493605555550623021243959522057n,
      address: '0x6a7b07da9d2c210bbbe044dbe29f0c5ae20444584a4d8ee9a804839f8256ea3c'
  }
  ```
</CodeGroup>

**Response Data**

| Name      | Type                                        | Description                                           |
| :-------- | :------------------------------------------ | :---------------------------------------------------- |
| userId    | `Object`                                    | The OAuth user information used to create the wallet. |
| subWallet | `Integer`                                   | The subWallet number.                                 |
| salt      | cURL: `Base64 encoded String` SDK: `BigInt` | The salt associated with this zkLogin wallet.         |
| address   | `Hex encoded String`                        | The Sui zkLogin address of this wallet.               |

## zkProver service methods

### shinami\_zkp\_createZkLoginProof

Generate a zkLogin proof prior to signing and executing a transaction. The example below uses the [Shinami Clients SDK](https://www.npmjs.com/package/@shinami/clients).

**Notes:**

* Devnet has a different zkey than Testnet and Mainnet. Shinami's zk prover currently only works with Testnet and Mainnet.
* We have a limit of two zk proofs per address per minute. When you hit this limit, we return a [JSON-RPC code `-32012`](/developer-guides/core-integration-topics/error-reference#sui-zklogin-wallet-api).

**Request Parameters**

| Name                       | Type                                                                  | Description                                                                                                                                                                                                                                                                                                                                                                                |
| :------------------------- | :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| jwt                        | `string`                                                              | A valid JWT signed by one of the supported OpenID providers. The JWT nonce must be prepared according to the [zkLogin requirements](https://docs.sui.io/guides/developer/cryptography/zklogin-integration#get-jwt).                                                                                                                                                                        |
| maxEpoch                   | cURL: `string`SDK: `number`                                           | The max epoch used to prepare the JWT nonce. This governs the expiration of the zkLogin proof. Once it expires, you will have to ask the user to sign in again. One epoch is roughly one day. See the expiration of recent epochs in a Sui explorer like [Suivision](https://suivision.xyz/) or [Suiscan](https://suiscan.xyz/) (note that Mainnet and Testnet are not on the same epoch). |
| extendedEphemeralPublicKey | cURL: `BigInt representation or Base64 encoded`SDK: `PublicKey`       | The extended ephemeral public key used to prepare the JWT nonce ([see example of how to generate it here](https://docs.sui.io/guides/developer/cryptography/zklogin-integration#get-the-zero-knowledge-proof)). The corresponding private key must be used to sign any transactions to be executed with this proof.                                                                        |
| jwtRandomness              | cURL: `string: BigInt representation or Base64 encoded` SDK: `bigint` | The random bytes used to prepare the JWT nonce ([see example here](https://docs.sui.io/guides/developer/cryptography/zklogin-integration#get-jwt)).                                                                                                                                                                                                                                        |
| salt                       | cURL: `string: BigInt representation or Base64 encoded` SDK: `bigint` | The zkLogin wallet salt. Together with the provided JWT, this determines the zkLogin wallet's on-chain address. This method works with salts managed by Shinami, buy you, by another third party, or by the user. Note, though, that [if the salt changes, the user's address changes](/api-docs/sui/wallet-services/zklogin-wallet-api#zklogin-addresses).                                |
| keyClaimName               | `string`                                                              | Optional. The claim name in the JWT that identifies a particular user. Defaults to "sub". Currently, Mysten's zkLogin implementation only supports the `sub` field of the OpenID spec. This parameter allows for forward compatibility as the implementation expands to support other values.                                                                                              |

**Example Request Template**

The TypeScript example uses the [Shinami Clients SDK](https://www.npmjs.com/package/@shinami/clients), which you can install with:

<CodeGroup>
  ```bash Shell theme={null}
  npm install @shinami/clients
  ```
</CodeGroup>

Replace all instances of `{{name}}` with the actual value for that name.

<CodeGroup>
  ```bash cURL expandable theme={null}
  curl https://api.us1.shinami.com/sui/zkprover/v1 \
  -X POST \
  -H 'X-API-Key: {{walletAccessKey}}' \
  -H 'Content-Type: application/json' \
  -d '{
          "jsonrpc":"2.0",
          "method":"shinami_zkp_createZkLoginProof",
          "params":[
              "{{jwt}}",
              "{{maxEpoch}}",
              "{{extendedEphemeralPublicKey}}",
              "{{jwtRandomness}}",
              "{{salt}}"
          ],
          "id":1
      }'
  ```

  ```bash Shinami TypeScript SDK expandable theme={null}
  import { ZkProverClient } from "@shinami/clients/sui";

  // Obtain wallet access key from your Shinami web portal.
  const zkProverClient = new ZkProverClient({{walletAccessKey}});

  // Create a zkProof.
  const zkProof = await zkProverClient.createZkLoginProof(
      {{jwt}},
      {{maxEpoch}},
      {{ephemeralPublicKey}},
      {{jwtRandomness}},
      {{salt}}
  );

  // Now you can sign transaction blocks with ephemeralPrivateKey, and assemble the zkLogin signature
  // using zkProof.
  ```

  ```bash cURL partial examples expandable theme={null}

  curl https://api.us1.shinami.com/sui/zkprover/v1 \
  -X POST \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \

  // extendedEphemeralPublicKey, jwtRandomness, and salt as base64 string
  -d '{
          "jsonrpc":"2.0",
          "method":"shinami_zkp_createZkLoginProof",
          "params":[
              "{jwt}",
              "{{maxEpoch}}",
              "AF1VXNxX7pN1rJW7K+P9Ml/f0n2gk1e/MOKcIt1rws08",
              "T6LZabVx1kjRq/kUiRdgdg==",
              "Jpu+BrIH4yijTBhkgJg1xw=="
          ],
          "id":1
      }'

  // extendedEphemeralPublicKey, jwtRandomness, and salt as BigInt string
  -d '{
          "jsonrpc":"2.0",
          "method":"shinami_zkp_createZkLoginProof",
          "params":[
              "{jwt}",
              "{{maxEpoch}}",
              "107198930224357545647865893180858238822499898890599700364072551532840382986657",
              "105854573416086459770441403231754477686",
              "51319324041191237311498376502115317191"
          ],
          "id":1
      }'
  ```
</CodeGroup>

**Example Response**

<CodeGroup>
  ```bash cURL expandable theme={null}
  {
      "jsonrpc": "2.0",
      "result": {
          "zkProof": {
              "proofPoints": {
                  "a": ["15523188928150068463877187176143113666714843703470604151076285692983325640642", "5937622317780822197753258881898720685630444182643789312895382585679701582730", "1"],
                  "b": [
                      ["16826302723856343423452057269835204629749355045593813315170205467027472681113", "13797818130832460450334887874511731864855590610477696974834238052347712422314"],
                      ["13373917712679082794928414217966585760669198351828608413595476883128268622714", "5700527829855762859349841977943997112007134227264278415875624634861579977183"],
                      ["1", "0"]
                  ],
                  "c": ["7520248514700601656024563691610669758825183429353083260336955084416144661718", "20552359067831153630006195193902483617859079319246848227965146473634030062290", "1"]
              },
              "issBase64Details": {
                  "value": "wiaXNzIjoiaHR0cHM6Ly9pZC50d2l0Y2gudHYvb2F1dGgyIiw",
                  "indexMod4": 2
              },
              "headerBase64": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ"
          }
      },
      "id": 1
  }
  ```

  ```bash Shinami TypeScript SDK expandable theme={null}
  {
      proofPoints: {
          a: [
              '10210145730043037811940974600447596138332245137712632111543948579663620583434',
              '7161160787659733995589070724990899004457677439433421417304252171408748983773',
              '1'
          ],
          b: [ [Array], [Array], [Array] ],
          c: [
              '14336916288409630215040261300089886420211729238372809076105326107110995622366',
              '16791539167604801843541798961147781044566621780138018818605704529730677460898',
              '1'
          ]
      },
      issBase64Details: {
          value: 'wiaXNzIjoiaHR0cHM6Ly9pZC50d2l0Y2gudHYvb2F1dGgyIiw',
          indexMod4: 2
      },
      headerBase64: 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ'
  }
  ```
</CodeGroup>

**Response Data**

| Type          | Description                          |
| :------------ | :----------------------------------- |
| `JSON object` | The zkLogin proof for the given JWT. |

## Appendix

### Common issues

#### JSON RPC -32002 on transaction execution (Invalid user signature)

**Example error**

```bash theme={null}
JsonRpcError: Invalid user signature: Required Signature from 0x6e20e4e7e3822ccd19614dd8db4fafdab3e0a888d54d272ef678a4c24937c5b4 is absent ["0x1c1a56df0bd80abc6dd097b98cd4ff341c3ef2bdb0f2ea7d0ebd87eff3268b6c", "0x3452ddf0448cc52e62d263c1c49553cb744dbb76bca56e9f12c98b2ffb9b82a5"]
    at SuiHTTPTransport.request (...node_modules/@mysten/sui/src/client/http-transport.ts:123:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at SuiClient.executeTransactionBlock (...src/server/main.ts:135:30) {
  code: -32002,
  type: 'TransactionExecutionClientError'
}
```

**Possible cause: passing incorrect or missing values when generating address seed and zk signature**

Ensure that you're providing all the correct values when generating your emphemeral Keypair signature, address seed, and zkLogin signature (all the steps [in this section](https://docs.sui.io/guides/developer/cryptography/zklogin-integration#assemble-the-zklogin-signature-and-submit-the-transaction)).

The above error happened for me because I wasn't properly storing the `sub` value, and I was retrieving it as `undefined` from session storage when generating my address seed. Once I printed out my key variable values (sub, aud, salt, maxEpoch, ephemeral Keypair public key or address) at the time of fetching the JWT and compared them to the what I fetched from session storage right before generating my address seed and zkLogin signature, I was able to catch and fix the issue. Then my transaction when through without an error.

#### Other issues

See the [zkLogin section of our Error Guide](/developer-guides/core-integration-topics/error-reference#sui-zklogin-wallet-api) for examples.
