App-controlled wallets to make Web3 unseen

Overview

Use Cases

Shinami’s Invisible Wallets abstract away Web3 elements like seed phrases, third-party wallet connections, gas fees, and signing popups. They are backend wallets under the shared custody of your app and Shinami. Both parties must cooperate in order to obtain a valid signature.

We also offer user-controlled zkLogin wallet services.

Shinami Gas Station Integration

All methods below that write to the Sui blockchain have their gas fees sponsored by you via a Gas Station you create (see our product FAQ for how guidance on how to set up a fund and add free Testnet Sui to it). This is because Invisible Wallets are designed to easily onboard Web2-native users (who may not want to download a wallet app, manage a seed phrase, and complete KYC checks to buy SUI for gas).

Check out our TypeScript tutorial for more code samples and details on the end-to-end flow of creating and using Invisible Wallets.

Authentication and Error Handling

Authentication

See our Authentication and API Keys guide.

🚧

Security Warning: Do not call this API from your frontend.

We strongly recommend having your backend server integrate with Shinami's Invisible Wallet API to prevent exposing any access keys on the frontend. Exposed keys and wallet information could lead to malicious actors signing transactions on behalf of your users.

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 Invisible Wallet API.

WalletId and Secret Pairing

When you create an Invisible Wallet, you must create, store, link, and never change the following two values:

  • walletId: Your internal id for a wallet. When you provide us a walletId in a method call, it tells us which Invisible Wallet to use. It could be your internal userId value, or a new arbitrary and unique value you link to the userId.
  • secret: Your internal secret for a wallet. The sessionToken you generate with it is combined with Shinami data to obtain a signature from the associated wallet. Ideally it would be different for each wallet so that if one secret is compromised the rest are not.

When you create an Invisible Wallet, you forever link its walletId it to the secret you used:

So, if you try to use the walletId with a different secret, you'll get an error:

Methods

shinami_key_createSession

Description
For security purposes, you must generate a session token before you create a wallet, or sign or execute transactions. Session tokens are valid and can be reused for 10 minutes.

You may also use an instance of ShinamiWalletSigner to manage session token generation and refreshes for a given wallet. This is shown in the methods below that have a sessionToken parameter in an additional sample code tab.

Request Parameters

NameTypeDescription
secretStringUsed to encrypt and decrypt a wallet's private key. Therefore, it must always be used with the same walletId and cannot be changed in the future (see walletId and secret pairing)

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/key/v1 \
-X POST \
-H 'X-API-Key: {{walletAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc":"2.0", 
        "method":"shinami_key_createSession", 
        "params":[
            "{{secret}}"
        ], 
        "id":1
    }'
import { KeyClient } from "@shinami/clients/sui";

const key = new KeyClient({{walletAccessKey}});

await key.createSession({{secret}});

Example Response

{
     "jsonrpc":"2.0",
     "result":"eyJraWQiOiJrZXkyMDIzMDgxMSIsImVuYyI6IkEyNTZHQ00iLCJ0YWciOiI4SVpQWXlHeDlmOTd6U2NIdmN6N3lnIiwiYWxnIjoiQTI1NkdDTUtXIiwiaXYiOiJQWVJXZFJrbnNMMlNnVzhfIn0.ygDCI-NcvUcH7wYc0Bp0-59qeIfGOqLyXZGsLF4pW0M.aOAW0AwBvAWpaS-S.QmesdNIdNIYbT59RET-lNuzNMUvS-xb2exhXrAIlspnIkV3nuBx7PKC_GgJ7C1EqJx3tDtQaLLDGdrO8_s-75oK88ls5mzDRR-w2A0VdCcTH0_JwsQgijIbCKFWS0g.dULMzxZ4gGbm2unqOnzv8w",
     "id": 1
}
"eyJraWQiOiJrZXkyMDIzMDgxMSIsImVuYyI6IkEyNTZHQ00iLCJ0YWciOiI4SVpQWXlHeDlmOTd6U2NIdmN6N3lnIiwiYWxnIjoiQTI1NkdDTUtXIiwiaXYiOiJQWVJXZFJrbnNMMlNnVzhfIn0.ygDCI-NcvUcH7wYc0Bp0-59qeIfGOqLyXZGsLF4pW0M.aOAW0AwBvAWpaS-S.QmesdNIdNIYbT59RET-lNuzNMUvS-xb2exhXrAIlspnIkV3nuBx7PKC_GgJ7C1EqJx3tDtQaLLDGdrO8_s-75oK88ls5mzDRR-w2A0VdCcTH0_JwsQgijIbCKFWS0g.dULMzxZ4gGbm2unqOnzv8w"

Response Fields

NameTypeDescription
resultStringsessionToken corresponding to the provided secret. Valid and can be reused for 10 minutes.

shinami_wal_createWallet

Description
Programmatically generates a unique wallet for a user that is Sui network agnostic (has the same address on Devnet, Testnet, and Mainnet).

🚧

Each walletId only works with the secret you create it with (via the sessionToken you pass to this method). Your application MUST remember the (walletId, secret) pair associated with each Invisible Wallet you create. If you forget or change either of these values, the wallet's private key will be lost and we cannot recover it for you.

Request Parameters

NameTypeDescription
walletIdStringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as username.
sessionTokenStringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_wal_createWallet",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}"
        ],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{walletAccessKey}});

await walletClient.createWallet(
    {{walletId}},
    {{sessionToken}}
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// Returns the Sui address of the invisible wallet, 
//  creating it if it hasn't been created yet.
const CREATE_WALLET_IF_NOT_FOUND = true;
await signer.getAddress(CREATE_WALLET_IF_NOT_FOUND);

Example Response

{
    "jsonrpc":"2.0",
    "result":"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f",
    "id":1
}
"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f"
"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f"

Response Data

TypeDescription
StringThe Sui address of the Invisible Wallet created for this walletId. Network-agnostic (the address will be the same on Devnet, Testnet, and Mainnet).

shinami_wal_getWallet

Description
Retrieve a user's wallet address based your unique walletId value for it.

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{ 
        "jsonrpc":"2.0", 
        "method":"shinami_wal_getWallet", 
        "params":[
            "{{walletId}}"
        ], 
        "id":1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{walletAccessKey}});

await walletClient.getWallet(
    {{walletId}}
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.getAddress();

Example Response

{
    "jsonrpc":"2.0",
    "result":"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f",
    "id":1
}
"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f"
"0xecaeb4a763dd49f2cd13aeaf2e7ab01f704bbc8c2bd9c2e991b726d0632c3b4f"

Response Fields

TypeDescription
StringThe Sui address of the Invisible Wallet created for this walletId. Network-agnostic (the address will be the same on Devnet, Testnet, and Mainnet).

shinami_wal_signTransactionBlock

Description
Signs a fully constructed transaction block so that it can be executed. This is a low level API - it requires integration with Gas Station API and Sui API for transaction sponsorship (if needed) and execution. This method gives you more control over how you submit transactions to Sui compared to shinami_wal_executeGaslessTransactionBlock, which sponsors, signs, and executes an Invisible Wallet transaction in one method call.

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet.
sessionTokenStringThe token generated by shinami_key_createSession with the same secret you used when creating this wallet.
txBytesStringBase64 encoded, BCS serialized TransactionData, which includes gas data. It lacks only the sender's signature (which this method generates) before it can be executed with sui_executeTransactionBlock

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_wal_signTransactionBlock",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{txBytes}}"
      	],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{walletAccessKey}});

await walletClient.signTransactionBlock(
    {{walletId}},
    {{sessionToken}},
    {{txBytes}}
)
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.signTransactionBlock(
    {{txBytes}}
);

Example Response

{
    "jsonrpc":"2.0",
    "result":{
        "signature":"AKzbe4FlhuT9saKFDUEdmCELBVa/NhsERc2XPahGC+8Ar6YMoK+DH+xs8xg/RSYF7HeZ4UmwnSPJFZpYjgWWZQB51Goyfzm4soRhJY9gDmt/wDZYCm81bkCP87eBm1T+Xw==",
        "txDigest":"BSFD6oDgftrtcVCZF8EAkUcmWWyd8ZRsMCGSh6EbtqCj"
    },
    "id":1
}
{
    signature: 'AKzbe4FlhuT9saKFDUEdmCELBVa/NhsERc2XPahGC+8Ar6YMoK+DH+xs8xg/RSYF7HeZ4UmwnSPJFZpYjgWWZQB51Goyfzm4soRhJY9gDmt/wDZYCm81bkCP87eBm1T+Xw==',
    txDigest: 'BSFD6oDgftrtcVCZF8EAkUcmWWyd8ZRsMCGSh6EbtqCj'
}
{
    signature: 'AKzbe4FlhuT9saKFDUEdmCELBVa/NhsERc2XPahGC+8Ar6YMoK+DH+xs8xg/RSYF7HeZ4UmwnSPJFZpYjgWWZQB51Goyfzm4soRhJY9gDmt/wDZYCm81bkCP87eBm1T+Xw==',
    txDigest: 'BSFD6oDgftrtcVCZF8EAkUcmWWyd8ZRsMCGSh6EbtqCj'
}

Response Fields

NameTypeDescription
signatureStringBase64 encoded transaction signature, signed by the wallet key. To be used alongside the txBytes sent to this method and the gas sponsor's signature (if applicable) in a call to sui_executeTransactionBlock
txDigestStringBase 58 encoded transaction digest.

shinami_wal_signPersonalMessage

Description
Signs a personal message using an Invisible Wallet. The signature can be verified with the PersonalMessage intent scope. The request template below titled End-to-end example with ShinamiWalletSigner - Shinami TS SDK shows an end-to-end example of signing and a message and verifying a signature.

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet.
sessionTokenStringThe token generated by shinami_key_createSession with the same secret you used when creating this wallet.
messageStringMessage bytes to be signed, as Base64 encoded string. See an example in the request template below titled End-to-end example with ShinamiWalletSigner - Shinami TS SDK
wrapBcsBooleanOptional. Set it to true when calling the API directly to match the verification behavior of the Sui TypeScript SDK. When using the Shinami TypeScript SDK it's set to true by default.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_wal_signPersonalMessage",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{message}}",
            {{wrapBCs}}
        ],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{walletAccessKey}});

await walletClient.signPersonalMessage(
    {{walletId}},
    {{sessionToken}},
    {{message}},
    {{wrapBCS}} // optional, defaults to true if not provided
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";
import { verifyPersonalMessage } from '@mysten/sui.js/verify';

const walletClient = new WalletClient({{walletAccessKey}});
const key = new KeyClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// encode the as a base64 string
let message = "I have the private keys."; 
let messageAsBase64String = btoa(message);

// use Shinami Wallet Service to sign the message
let signature = await signer.signPersonalMessage(
  messageAsBase64String
);

// When we check the signature, we encode the message as a byte array
// and not a base64 string like when we signed it
let messageBytes = new TextEncoder().encode(message); 

// Failure throws a `Signature is not valid for the provided message` Error
let publicKey = await verifyPersonalMessage(messageBytes, signature);

// Get the wallet address we signed with so we can check against it
let walletAddress = await signer.getAddress();

console.log(walletAddress == publicKey.toSuiAddress());

Example Response

{
    "jsonrpc":"2.0",
    "result":"AFKIGo7e/eaqCbrrDKIVh4wjpHVudqP8Pbdzo+spztZGmUfiDPY9EPnTx7RnadSQHCSpxgP+QwaAvsJc4JMfswR51Goyfzm4soRhJY9gDmt/wDZYCm81bkCP87eBm1T+Xw==",
    "id":1
}
"AFKIGo7e/eaqCbrrDKIVh4wjpHVudqP8Pbdzo+spztZGmUfiDPY9EPnTx7RnadSQHCSpxgP+QwaAvsJc4JMfswR51Goyfzm4soRhJY9gDmt/wDZYCm81bkCP87eBm1T+Xw=="

true

Response Data

TypeDescription
StringBase64 encoded signature, produced by the private key of the Invisible Wallet associated with the provided walletId.

shinami_wal_executeGaslessTransactionBlock

Description
Sponsors, signs, and executes a gasless transaction from a wallet. This is a convenient end-to-end method for submitting sponsored transactions to the chain when you also use Shinami Gas Station. It sponsors the transaction using the Gas Station fund tied to the access key used to make the request. To see how to set up an Access Key with rights to all services, see our Authentication and API Keys guide. Note that this call produces a Node service sui_executeTransactionBlock request which counts against your daily request total (and so your billing).

You cannot use the gas object in a sponsored transaction for other purposes: For example, you cannot write const [coin] = txb.splitCoins(txb.gas,[txb.pure(100)]); because it's accessing txb.gas. If you try to sponsor a TransactionKind that uses the gas object you will get a JSON-RPC -32602 error back from the Gas Station sponsorship attempt.

Shinami sponsorship fees: We charge a small fee (in SUI) per sponsorship request to cover our costs. For details, visit the Billing tab in your Shinami dashboard.

📘

To call this method, you need an access key that is authorized for all of these Shinami products:

  • Wallet Services
  • Gas Station
  • Node Service

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet.
sessionTokenStringThe token generated by shinami_key_createSession with the same secret you used when creating this wallet.
txBytesStringBase64 encoded TransactionKind (as opposed to TransactionData) bytes. So, it does not include gas information.
gasBudgetString(Optional) The gas budget you wish to use for the transaction, in MIST. The transaction will fail if the gas cost exceeds this value.

- If provided, we use the value as the budget of the sponsorship.

- If omitted, we estimate the transaction cost for you. We then add a buffer (5% for non-shared objects, 25% for shared objects) and use that total value as the budget of the sponsorship.
optionsObject<TransactionBlockResponseOptions> - Optional. Sui options for specifying the transaction content to be returned
requestTypeString<ExecuteTransactionRequestType> - Optional. The execution request type (WaitForEffectsCert or WaitForLocalExecution)

Auto-budgeting notes

  • As a part of auto-budgeting, we put your transactionBytes through a sui_dryRunTransactionBlock request as a free service before we attempt to sponsor it. This call will generate error messages for certain invalid transactions, such as if the transactionBytes are transferring an object that's not owned by the sender address you provide. We'll return these errors back to you, which should be the same as if you had made a sui_dryRunTransactionBlock request yourself. We do not do this step if you manually budget, so any issues that would be caught by sui_dryRunTransactionBlock will instead produce an error when you try to execute the transaction.
  • In the time between sponsorship and execution, shared objects can change in a way that increases their transaction cost. Therefore, we encourage you to execute sponsored transactions quickly, if possible, to ensure that the sponsorship amount is sufficient. This is why we add a larger buffer on auto-budgeted sponsorships when a shared object is involved. While we believe this buffer will work in most cases, we encourage you to monitor the success rate of your auto-budgeted transactions to gauge whether your specific use-case requires manually setting an even larger gasBudget.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{allServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_wal_executeGaslessTransactionBlock",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{txBytes}}",
            "{{gasBudget}}",
            {
                "showInput": false,
                "showRawInput": false,
                "showEffects": false,
                "showEvents": false,
                "showObjectChanges": false,
                "showBalanceChanges": false
            },
            "{{requestType}}"
        ],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{allServicesAccessKey}});

await walletClient.executeGaslessTransactionBlock(
    {{walletId}},
    {{sessionToken}},
    {{txBytes}},
    {{gasBudget}},  // if `undefined` we'll auto-budget for you
    {
      	showInput: false,
        showRawInput: false,
        showEffects: false,
        showEvents: false,
        showObjectChanges: false,
        showBalanceChanges: false
    },
    {{requestType}} // must set to `None` or `WaitForLocalExecution` if showEffects, showObjectChanges, or showBalanceChanges are set to true
);

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

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.executeGaslessTransactionBlock(
    {{txBytes}},
    {{gasBudget}}, // if `undefined` we'll auto-budget for you
    {
        showInput: false,
        showRawInput: false,
        showEffects: false,
        showEvents: false,
        showObjectChanges: false,
        showBalanceChanges: false
    },
    {{requestType}} // must set to `None` or `WaitForLocalExecution` if showEffects, showObjectChanges, or showBalanceChanges are set to true
);

Example Response

{
    "jsonrpc": "2.0",
    "result": {
        "digest": "Em4C8d6rRSUQ72kUWd627UfXTqDAVWQjJq9tmUFfnrmm",
        "confirmedLocalExecution": true
    },
    "id": 1
}
{
    digest: 'B6j8ePkw84R1rpUqxmjaZ3dTuu6GhPdY9MqoSso6kAn7',
    confirmedLocalExecution: true
}
{
    digest: 'B6j8ePkw84R1rpUqxmjaZ3dTuu6GhPdY9MqoSso6kAn7',
    confirmedLocalExecution: true
}

Response Fields

TypeDescription
SuiTransactionBlockResponse<SuiTransactionBlockResponse> containing information about the executed transaction.

shinami_walx_setBeneficiary

📘

Beneficiary Graph API

This API interacts with the Account Graph Move package.

Description
Designates a beneficiary account for this wallet in the specified beneficiary graph instance. Calling this method multiple times will override the previous designations.

Apps participating in Bullshark Quests can use this method to link up Shinami Invisible Wallets with their users' self-custody wallets. This way, the user's self-custody wallet (the beneficiary, containing a Bullshark NFT) will get credit for actions done by the associated Invisible Wallet. This method accepts any value that is a possible Sui Address someone could own. It does not guarantee that anyone actually owns a keypair for it and can sign transactions from it (because it does not require the beneficiary address owner to sign a transaction).

This method will use your Shinami Gas Station fund to pay for the transaction. It sponsors the transaction using the Gas Station fund tied to the access key used to make the request. To see how to set up an Access Key with rights to all services, see our Authentication and API Keys guide. Note that this call produces a Node service sui_executeTransactionBlock request which counts against your daily request total (and so your billing).

📘

To call this method, you need an access key that is authorized for all of these Shinami products:

  • Wallet Services (for signing the transaction)
  • Gas Station (for sponsoring the transaction)
  • Node Service (for executing the transaction)

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated invisible wallet whose beneficiary you're setting.
sessionTokenStringThe token generated by shinami_key_createSession with the same secret you used when creating this wallet.
beneficiaryGraphIdString<ObjectID> - Id of the beneficiary graph instance.
beneficiaryAddressString<SuiAddress> - Address of the beneficiary account.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{allServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_walx_setBeneficiary",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{beneficiaryGraphId}}",
            "{{beneficiaryAddress}}"
        ],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{allServicesAccessKey}});

await walletClient.setBeneficiary(
    {{walletId}},
    {{sessionToken}},
    {{beneficiaryGraphId}},
    {{beneficiaryAddress}}
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.setBeneficiary(
    {{beneficiaryGraphId}},
    {{beneficiaryAddress}}
);

Example Response

{
    "jsonrpc":"2.0",
    "result":"43i3D8vhQaJBhDGd1sdQY346w4HjTb79EPv2faNmoSGA",
    "id":1
}
"5J671ff8U9CHAABek3PvYDKpYqFcZAVzHJPjp8Nyj3Mp"
"5J671ff8U9CHAABek3PvYDKpYqFcZAVzHJPjp8Nyj3Mp"

Response Data

TypeDescription
String<TransactionDigest> - Transaction digest for this operation.

shinami_walx_unsetBeneficiary

📘

Beneficiary Graph API

This API interacts with the Account Graph Move package.

Description
Clears any beneficiary designation for this wallet in the specified beneficiary graph instance.

This method will use your Shinami Gas Station fund to pay for the transaction. It sponsors the transaction using the Gas Station fund tied to the access key used to make the request. To see how to set up an Access Key with rights to all services, see our Authentication and API Keys guide. Note that this call produces a Node service sui_executeTransactionBlock request which counts against your daily request total (and so your billing).

📘

To call this method, you need an access key that is authorized for all of these Shinami products:

  • Wallet Services
  • Gas Station
  • Node Service

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet whose beneficiary you're unsetting.
sessionTokenStringThe token generated by shinami_key_createSession with the same secret you used when creating this wallet.
beneficiaryGraphIdString<ObjectID> - Id of the beneficiary graph instance.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {{allServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_walx_unsetBeneficiary",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{beneficiaryGraphId}}"
        ],
        "id": 1
    }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{allServicesAccessKey}});

await walletClient.unsetBeneficiary(
    {{walletId}},
    {{sessionToken}},
    {{beneficiaryGraphId}}
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.unsetBeneficiary(
    {{beneficiaryGraphId}}
);

Example Response

{
    "jsonrpc":"2.0",
    "result":"7Ha6scuitsHKETDvShoBa1axv3qdGaspfxFjYdBMDDAQ",
    "id":1
}
"5fsyXsmutHWsSnVVqrsNNLnaWRk84pYiwHHhHMZiX4Ea"
"5fsyXsmutHWsSnVVqrsNNLnaWRk84pYiwHHhHMZiX4Ea"

Response Data

TypeDescription
String<TransactionDigest> - Transaction digest for this operation

shinami_walx_getBeneficiary

📘

Beneficiary Graph API

This API interacts with the Account Graph Move package.

Description
Gets the beneficiary designation for this wallet in the specified beneficiary graph instance.

This is a convenience method on top of suix_getDynamicFieldObject.

📘

To call this method, you need an access key that is authorized for all of these Shinami products:

  • Wallet Services
  • Node Service

Request Parameters

NameTypeDescription
walletIdStringYour unique, internal id for the associated Invisible Wallet whose beneficiary you're asking for.
beneficiaryGraphIdString<ObjectID> - Id of the beneficiary graph instance

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

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

curl https://api.shinami.com/wallet/v1 \
-X POST \
-H 'X-API-Key: {walletAndNodeServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "shinami_walx_getBeneficiary",
        "params": [
            "{{walletId}}",
            "{{beneficiaryGraphId}}"
        ],
        "id": 1
     }'
import { WalletClient } from "@shinami/clients/sui";

const walletClient = new WalletClient({{walletAndNodeServicesAccessKey}});

await walletClient.getBeneficiary(
    {{walletId}},
    {{beneficiaryGraphId}}
);
import { 
    WalletClient, 
    KeyClient, 
    ShinamiWalletSigner
} from "@shinami/clients/sui";

const keyClient = new KeyClient({{walletAccessKey}});
const walletClient = new WalletClient({{walletAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.getBeneficiary(
    {{beneficiaryGraphId}}
);

Example Response

// The wallet has a beneficiary in the provided beneficiary graph
{
    "jsonrpc":"2.0",
    "result":"0xa39edfb89e6a21e89570711845c6c8f412d86bb208e571faf6ea1fc6470172d7",
    "id":1
}

// The wallet does not have a beneficiary in the provided beneficiary graph
{
    "jsonrpc":"2.0",
    "result":null,
    "id":1
}
// The wallet has a beneficiary in the provided beneficiary graph
"0xa39edfb89e6a21e89570711845c6c8f412d86bb208e571faf6ea1fc6470172d7"

// The wallet does not have a beneficiary in the provided beneficiary graph
null
// The wallet has a beneficiary in the provided beneficiary graph
"0xa39edfb89e6a21e89570711845c6c8f412d86bb208e571faf6ea1fc6470172d7"

// The wallet does not have a beneficiary in the provided beneficiary graph
null

Response Data

TypeMove TypeDescription
String || nulladdressThe network-agnostic Sui address of the Invisible Wallet created for this walletId. Null if no beneficiary is designated.