@shinami/clients
TypeScript icon, indicating that this package has built-in type declarations

0.7.3 • Public • Published

Shinami Clients SDK

npm version

TypeScript clients for Shinami services for the Sui blockchain.

Install

$ npm install @shinami/clients

Usage

Node service

To create a Sui RPC client:

import { createSuiClient } from "@shinami/clients";

// Obtain NODE_ACCESS_KEY from your Shinami web portal.
const sui = createSuiClient(NODE_ACCESS_KEY);

The returned sui object is a SuiClient configured to use Shinami's node service. It supports both HTTP JSON RPC requests as well as WebSocket subscriptions.

Note that NODE_ACCESS_KEY determines which Sui network later operations are targeting.

Gas station

Note that gas station should be integrated from your service backend. This is so you don't leak your GAS_ACCESS_KEY to your end users, and to allow you to control whose and what transactions to sponsor.

To use gas station with a local signer:

import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
import { fromB64 } from "@mysten/sui.js/utils";
import {
  GasStationClient,
  buildGaslessTransactionBytes,
  createSuiClient,
} from "@shinami/clients";

// Obtain NODE_ACCESS_KEY and GAS_ACCESS_KEY from your Shinami web portal.
// They MUST be associated with the same network.
const sui = createSuiClient(NODE_ACCESS_KEY);
const gas = new GasStationClient(GAS_ACCESS_KEY);

// You'll want to persist the key pair instead of always creating new ones.
const keypair = new Ed25519Keypair();

const gaslessTx = await buildGaslessTransactionBytes({
  sui,
  build: async (txb) => {
    // Program your TransactionBlock.
    // DO NOT set sender or gas data.
    txb.moveCall({
      target: `${EXAMPLE_PACKAGE_ID}::math::add`,
      arguments: [txb.pure(1), txb.pure(2)],
    });
  },
});

// Request gas sponsorship.
const { txBytes, signature: gasSignature } = await gas.sponsorTransactionBlock(
  gaslessTx,
  keypair.toSuiAddress(),
  5_000_000
);

// Sign the sponsored tx.
const { signature } = await keypair.signTransactionBlock(fromB64(txBytes));

// Execute the sponsored & signed tx.
const txResp = await sui.executeTransactionBlock({
  transactionBlock: txBytes,
  signature: [signature, gasSignature],
});

Invisible wallet

To use the invisible wallet as a signer for a regular (non-sponsored) transaction block:

import { TransactionBlock } from "@mysten/sui.js/transactions";
import {
  KeyClient,
  ShinamiWalletSigner,
  WalletClient,
  createSuiClient,
} from "@shinami/clients";

// Obtain NODE_ACCESS_KEY and WALLET_ACCESS_KEY from your Shinami web portal.
const sui = createSuiClient(NODE_ACCESS_KEY);
const key = new KeyClient(WALLET_ACCESS_KEY);
const wal = new WalletClient(WALLET_ACCESS_KEY);

// WALLET_SECRET MUST be used consistently with this wallet id.
// You are responsible for safe-keeping the (walletId, secret) pair.
// Shinami cannot recover it for you.
const signer = new ShinamiWalletSigner("my_wallet_id", wal, WALLET_SECRET, key);

// Program your TransactionBlock.
const txb = new TransactionBlock();
txb.moveCall({
  target: `${EXAMPLE_PACKAGE_ID}::math::add`,
  arguments: [txb.pure(1), txb.pure(2)],
});
txb.setSender(await signer.getAddress(true /* autoCreate */));
txb.setGasBudget(5_000_000);
// Your invisible wallet MUST have sufficient gas for this to succeed.
const txBytes = await txb.build({ client: sui });

// Sign tx with invisible wallet.
const { signature } = await signer.signTransactionBlock(txBytes);

// Execute the signed tx.
const txResp = await sui.executeTransactionBlock({
  transactionBlock: txBytes,
  signature,
});

To use the invisible wallet to execute a gasless transaction block, which seamlessly integrates with Shinami node service and gas station:

import {
  KeyClient,
  ShinamiWalletSigner,
  WalletClient,
  buildGaslessTransactionBytes,
  createSuiClient,
} from "@shinami/clients";

// Obtain SUPER_ACCESS_KEY from your Shinami web portal.
// It MUST be authorized for all of these services:
// - Node service
// - Gas station
// - Wallet service
const sui = createSuiClient(SUPER_ACCESS_KEY);
const key = new KeyClient(SUPER_ACCESS_KEY);
const wal = new WalletClient(SUPER_ACCESS_KEY);

// WALLET_SECRET MUST be used consistently with this wallet id.
// You are responsible for safe-keeping the (walletId, secret) pair.
// Shinami cannot recover it for you.
const signer = new ShinamiWalletSigner("my_wallet_id", wal, WALLET_SECRET, key);

// Safe to do if unsure about the wallet's existence.
await signer.tryCreate();

const gaslessTx = await buildGaslessTransactionBytes({
  sui,
  build: async (txb) => {
    // Program your TransactionBlock.
    // DO NOT set sender or gas data.
    txb.moveCall({
      target: `${EXAMPLE_PACKAGE_ID}::math::add`,
      arguments: [txb.pure(1), txb.pure(2)],
    });
  },
});

// Execute the gasless tx using your invisible wallet.
const txResp = await signer.executeGaslessTransactionBlock(
  gaslessTx,
  5_000_000
);

zkLogin wallet

You can use Shinami's zkLogin wallet services as the salt provider and zkProver in your zkLogin implementation.

import { ZkProverClient, ZkWalletClient } from "@shinami/clients";

// Obtain WALLET_ACCESS_KEY from your Shinami web portal.
const zkw = new ZkWalletClient(WALLET_ACCESS_KEY);
const zkp = new ZkProverClient(WALLET_ACCESS_KEY);

// 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 { salt, address } = await zkw.getOrCreateZkLoginWallet(jwt);

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

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

Beneficiary graph API

Apps using Shinami invisible wallets can participate in Bullshark Quests by allowing users to link their invisible wallets with self-custody wallets that own Bullshark NFTs, through the use of beneficiary graph.

import {
  EXAMPLE_BENEFICIARY_GRAPH_ID_TESTNET,
  KeyClient,
  ShinamiWalletSigner,
  WalletClient,
} from "@shinami/clients";

// Obtain SUPER_ACCESS_KEY from your Shinami web portal.
// It MUST be authorized for all of these services:
// - Node service
// - Gas station
// - Wallet service
const key = new KeyClient(SUPER_ACCESS_KEY);
const wal = new WalletClient(SUPER_ACCESS_KEY);

// WALLET_SECRET MUST be used consistently with this wallet id.
// You are responsible for safe-keeping the (walletId, secret) pair.
// Shinami cannot recover it for you.
const signer = new ShinamiWalletSigner("my_wallet_id", wal, WALLET_SECRET, key);

// Safe to do if unsure about the wallet's existence.
await signer.tryCreate();

// Use BULLSHARK_QUEST_BENEFICIARY_GRAPH_ID_MAINNET for Mainnet.
const graphId = EXAMPLE_BENEFICIARY_GRAPH_ID_TESTNET;

const txDigest = await signer.setBeneficiary(
  graphId,
  // Replace with user's actual wallet address that owns the Bullshark.
  "0x1234"
);

// This should return the address we just set.
const beneficiary = await signer.getBeneficiary(graphId);

Development

Build

$ npm run build

Lint

$ npm run lint

Unit test

$ npm run test

Integration test

The integration tests make use of the Move example package, which has been deployed to Sui Testnet. Obtain <your_super_access_key> from Shinami web portal. The key must be authorized for all of these services, targeting Sui Testnet:

  • Node service
  • Gas station - you must also have some available balance in your gas fund.
  • Wallet service
export NODE_ACCESS_KEY=<your_super_access_key>
export GAS_ACCESS_KEY=<your_super_access_key>
export WALLET_ACCESS_KEY=<your_super_access_key>

npm run integration

The integration tests by default talk to Shinami's production endpoints. You can also make it talk to alternative endpoints by modifying integration.env.ts.

Code coverage

Similar to integration test:

export NODE_ACCESS_KEY=<your_super_access_key>
export GAS_ACCESS_KEY=<your_super_access_key>
export WALLET_ACCESS_KEY=<your_super_access_key>

npm run coverage

The HTML coverage report is available at coverage/lcov-report/index.html.

Clean

$ npm run clean

Package Sidebar

Install

npm i @shinami/clients

Weekly Downloads

566

Version

0.7.3

License

Apache-2.0

Unpacked Size

130 kB

Total Files

36

Last publish

Collaborators

  • jasonxh