From 385486c0d1ae2dab0353c3aad1ac5e17f4b6ee6c Mon Sep 17 00:00:00 2001 From: trikunai Date: Tue, 7 Apr 2026 15:27:30 +0100 Subject: [PATCH] feat: sui staking-api docs --- docs/staking-api/sui/_category_.json | 8 + docs/staking-api/sui/index.md | 23 +++ .../sui/native-staking/_category_.json | 8 + .../sui/native-staking/endpoints.md | 194 ++++++++++++++++++ docs/staking-api/sui/native-staking/index.md | 55 +++++ docs/staking-api/sui/signing.md | 152 ++++++++++++++ 6 files changed, 440 insertions(+) create mode 100644 docs/staking-api/sui/_category_.json create mode 100644 docs/staking-api/sui/index.md create mode 100644 docs/staking-api/sui/native-staking/_category_.json create mode 100644 docs/staking-api/sui/native-staking/endpoints.md create mode 100644 docs/staking-api/sui/native-staking/index.md create mode 100644 docs/staking-api/sui/signing.md diff --git a/docs/staking-api/sui/_category_.json b/docs/staking-api/sui/_category_.json new file mode 100644 index 0000000..b384b6c --- /dev/null +++ b/docs/staking-api/sui/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Sui", + "position": 8, + "link": { + "type": "doc", + "id": "staking-api/sui/index" + } +} diff --git a/docs/staking-api/sui/index.md b/docs/staking-api/sui/index.md new file mode 100644 index 0000000..2a8c1fc --- /dev/null +++ b/docs/staking-api/sui/index.md @@ -0,0 +1,23 @@ +--- +title: Sui +--- + +# Sui + +This section covers Sui staking integrations using the Stakely Staking API. It includes Sui-specific notes, transaction signing examples, and native staking flows supported by Stakely. + +## Useful links + +- Stakely, Sui (SUI) staking overview: https://stakely.io/staking/sui-staking +- Official documentation: https://docs.sui.io + +## What you will find in this section + +- **Signing transactions**: how to sign Sui transactions returned by the crafting endpoints, with examples for common signing setups. +- **Native staking**: end-to-end flows and endpoints for staking, unstaking, and querying staked balances on Sui. + +## Notes + +- Sui integrations require specifying the correct `X-NETWORK` for the target network. The API provides a dedicated endpoint to list all available Sui networks and their corresponding `X-NETWORK` values. +- All staking operations are executed on-chain. Stakely prepares the transaction payloads, while signing is performed by your application using your preferred signing method. +- Sui staking responses may include values in both `SUI` and `MIST` units (`1 SUI = 1,000,000,000 MIST`). diff --git a/docs/staking-api/sui/native-staking/_category_.json b/docs/staking-api/sui/native-staking/_category_.json new file mode 100644 index 0000000..ea26700 --- /dev/null +++ b/docs/staking-api/sui/native-staking/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Native Staking", + "position": 3, + "link": { + "type": "doc", + "id": "staking-api/sui/native-staking/index" + } +} diff --git a/docs/staking-api/sui/native-staking/endpoints.md b/docs/staking-api/sui/native-staking/endpoints.md new file mode 100644 index 0000000..8131b1a --- /dev/null +++ b/docs/staking-api/sui/native-staking/endpoints.md @@ -0,0 +1,194 @@ +--- +sidebar_position: 3 +--- +# Endpoints + +import StakingOpenApiLink from '@site/src/components/StakingOpenApiLink'; + +## API Usage +Once you already have access to the Staking API with a validated user and existing apikeys you can start using this service. + +### API Reference docs + +:::tip + +You can check the **Staking API Reference** here: +- Rendered doc page. +- +::: + +## Authentication + +In order to use staking api related endpoints you need to include your **API KEY**. + +:::tip + +**Heads up!** +To obtain a valid API key required for authentication, please refer to the [Authentication > Auth](/staking-api/authentication#auth) section of the documentation. + +::: + +**Header:** + +| Name | Description | Example value | Required | +|-------------|-------------|---------------|----------| +| `X-API-KEY` | Your api key value | `` | ✅ | +| `X-NETWORK` | Blockchain network identifier | `mainnet` | ⚪ | + +### X-NETWORK header + +Use the `X-NETWORK` header to select the Sui network. + +::::info +If you omit the header the API falls back to the default Sui network configured for your account. +:::: + +____ + +### List available networks + +Use this helper endpoint to obtain the list of Sui staking networks currently enabled for your API key. + +- Endpoint: /api/v1/sui/native/networks +- Method: `GET` + +Response payload fields: + +| Field | Description | +|-------|-------------| +| `name` | Internal blockchain entry name | +| `type` | Blockchain type string (always `SUI`) | +| `chain_id` | Chain identifier | +| `is_default` | `true` when this network is used as fallback | + +____ + +### Stake action + +Craft an unsigned stake transaction: + +- Endpoint: /api/v1/sui/native/action/stake +- Method: `POST` + +#### Description + +This endpoint crafts an unsigned stake transaction ready to be signed. + +#### Request body parameters + +SuiStakeActionDto + +- `wallet_address` (required): Wallet address of the user that will perform the staking action. +- `validator_address` (optional): Validator address to stake to. If omitted, defaults to configured validator. +- `amount` (required): Amount of SUI to stake (minimum 1 SUI, up to 9 decimals). + +#### Returned + +SuiStakeActionResponseDto + +- `unsigned_tx_b64`: Unsigned transaction bytes encoded as base64. + +____ + +### Unstake action + +Craft an unsigned unstake transaction: + +- Endpoint: /api/v1/sui/native/action/unstake +- Method: `POST` + +#### Description + +This endpoint crafts an unsigned unstake transaction ready to be signed. + +#### Request body parameters + +SuiUnstakeActionDto + +- `wallet_address` (required): Wallet address of the user that will perform the unstaking action. +- `staked_sui_object_id` (required): Object ID of the `StakedSui` object to withdraw. + +#### Returned + +SuiStakeActionResponseDto + +- `unsigned_tx_b64`: Unsigned transaction bytes encoded as base64. + +____ + +### Prepare action + +Gathers signatures and unsigned transaction: + +- Endpoint: /api/v1/sui/native/action/prepare +- Method: `POST` + +#### Description + +Prepare a signed transaction by combining the unsigned transaction bytes and signatures into a single payload. + +#### Request body parameters + +SuiPrepareActionDto + +- `unsigned_tx_b64`: Unsigned transaction bytes in base64. +- `signatures`: Array of base64-encoded signatures. + +#### Returned + +SuiPrepareActionResponseDto + +____ + +### Broadcast action + +Broadcast a signed transaction: + +- Endpoint: /api/v1/sui/native/action/broadcast +- Method: `POST` + +#### Description + +Broadcast a signed Sui transaction. + +#### Request body parameters + +SuiBroadcastActionDto + +- `unsigned_tx_b64` (required): Unsigned transaction bytes encoded as base64. +- `signatures` (required): Array of base64-encoded signatures for the transaction. + +#### Returned + +SuiBroadcastActionResponseDto + +- `tx_digest`: Transaction digest of the broadcasted transaction. + +____ + +### Stake balance + +Get staked balance for the given address: + +- Endpoint: /api/v1/sui/native/stake-balance/{address} +- Method: `GET` + +#### Description + +Get staked balance information for the given Sui wallet address. + +#### Request parameters + +Path params: + +- `address`: Sui wallet address. + +#### Returned + +SuiStakedBalanceResponseDto + +- `wallet_address`: Wallet address that was queried. +- `total_mist`: Total staked balance in MIST. +- `total_sui`: Total staked balance in SUI. +- `staked_objects`: Array of `StakedSuiObjectDto`. +- `fungible_objects`: Array of `FungibleStakedSuiDto`. diff --git a/docs/staking-api/sui/native-staking/index.md b/docs/staking-api/sui/native-staking/index.md new file mode 100644 index 0000000..544e88c --- /dev/null +++ b/docs/staking-api/sui/native-staking/index.md @@ -0,0 +1,55 @@ +--- +sidebar_position: 1 +--- +# Staking Flow + +Before interacting with the API methods, it is useful to understand how native staking works on Sui. + +Sui staking follows an object-based model. A wallet stakes SUI into a validator staking pool and receives stake-related on-chain objects. Unstaking is performed using the specific `StakedSui` object identifier. + +All staking operations are executed on-chain. The Stakely Staking API crafts the required transactions, while signing and broadcasting are handled by your application. + +### Stake + +Staking delegates a specified amount of SUI to a validator. + +1. **Initiate Stake**: Use the stake action to create a transaction that stakes a chosen amount to a validator. +2. **Transaction Confirmation**: Once the transaction is confirmed on-chain, your stake position is created. +3. **Stake Objects**: The wallet can then query staked positions and balances. + +### Unstake + +Unstaking starts the process of withdrawing stake from an existing `StakedSui` object. + +1. **Select Staked Object**: Identify the `staked_sui_object_id` to unstake. +2. **Initiate Unstake**: Use the unstake action to craft an unstake transaction. +3. **Transaction Confirmation**: Once confirmed on-chain, the unstake operation is applied. + +___ + +## Staking API Diagram + +```mermaid +sequenceDiagram +autonumber +actor User +participant StakingAPI +User->>+StakingAPI: Stake action +Note left of User: Offline: Stake X SUI +Note right of StakingAPI: Craft unsigned transaction +StakingAPI-->>-User: Returns unsigned_tx_b64 +activate User +Note left of User: Sign unsigned transaction with\nprivate key or custodian +deactivate User + +User->>+StakingAPI: Prepare +Note right of StakingAPI: Combine unsigned tx + signature(s) +StakingAPI-->>-User: Returns signed payload +activate User +Note left of User: Optionally broadcast via your own RPC\nor Stakely broadcast endpoint +deactivate User + +User->>+StakingAPI: Broadcast +Note right of StakingAPI: Broadcast to Sui network +StakingAPI-->>-User: Returns tx digest/status +``` diff --git a/docs/staking-api/sui/signing.md b/docs/staking-api/sui/signing.md new file mode 100644 index 0000000..931fc46 --- /dev/null +++ b/docs/staking-api/sui/signing.md @@ -0,0 +1,152 @@ +--- +sidebar_position: 2 +title: Signing +--- +# How to sign transactions + +After calling a crafting endpoint, the API returns an unsigned Sui transaction payload. This payload must be signed before it can be sent to the network. + +Signing is fully handled by your application. The Stakely Staking API never has access to private keys. + +The signing concepts and examples described in this section apply to all Sui staking flows supported by the API. + +## Signing with a private key + +### Signing with Mysten Sui SDK + +This approach uses the official Mysten Sui SDK to sign transactions locally with an Ed25519 private key. + +The crafted transaction returned by the API can be decoded and signed with `Ed25519Keypair`. The resulting base64 signature can then be sent to the `prepare` endpoint. + +Complete with your own private key at `.env`: + +``` +SUI_PRIVATE_KEY= +``` + +```javascript +const { fromBase64 } = require('@mysten/sui/utils'); +const { Ed25519Keypair } = require('@mysten/sui/keypairs/ed25519'); + +const signWithPrivateKey = async (unsigned_tx_b64, privateKeyHex) => { + if (!privateKeyHex) { + throw new Error('SUI_PRIVATE_KEY is required for non-Fireblocks signing'); + } + + const txBytes = fromBase64(unsigned_tx_b64); + const cleanHex = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex; + const secretKeyBytes = Uint8Array.from(Buffer.from(cleanHex, 'hex')); + + const keypair = Ed25519Keypair.fromSecretKey(secretKeyBytes); + const { signature } = await keypair.signTransaction(txBytes); + + return { signature }; +}; + +// Get unsigned_tx_b64 from stake/unstake action response +// const { signature } = await signWithPrivateKey(unsigned_tx_b64, process.env.SUI_PRIVATE_KEY); +// Then call action/prepare with { unsigned_tx_b64, signatures: [signature] } +``` + +Steps: + +1. Get `unsigned_tx_b64` from a stake or unstake action response. +2. Pass it to the signer code with your `SUI_PRIVATE_KEY`. +3. Call `action/prepare` with the signature. + +# Signing using external custodians or MPC wallets + +Transactions can also be signed using external custodians or MPC-based wallets. In this setup, private keys are never exposed to the application and signing is delegated to the external signing service. + +### Fireblocks + +Fireblocks can be used to sign Sui transactions generated by the Stakely Staking API. + +Complete with your Fireblocks credentials at `.env`: + +``` +FIREBLOCKS_API_SECRET= +FIREBLOCKS_API_KEY= +FIREBLOCKS_BASE_URL= +FIREBLOCKS_VAULT_ID= +SUI_FIREBLOCKS_ASSET_ID= +``` + +```javascript +const { fromBase64, toBase64 } = require('@mysten/sui/utils'); +const { FireblocksSDK, TransactionOperation, PeerType, TransactionStatus } = require('fireblocks-sdk'); + +const fireblocks = new FireblocksSDK( + process.env.FIREBLOCKS_API_SECRET, + process.env.FIREBLOCKS_API_KEY, + process.env.FIREBLOCKS_BASE_URL, +); + +const waitForTxCompletion = async (fbTx) => { + let tx = fbTx; + while (tx.status !== TransactionStatus.COMPLETED) { + if ( + tx.status === TransactionStatus.BLOCKED || + tx.status === TransactionStatus.FAILED || + tx.status === TransactionStatus.CANCELLED + ) { + throw new Error(`Fireblocks signer: transaction ${tx.status}`); + } + tx = await fireblocks.getTransactionById(fbTx.id); + } + return fireblocks.getTransactionById(fbTx.id); +}; + +const signWithFb = async (txBytesB64, assetId, vaultAccountId = '1') => { + if (!fireblocks) { + throw new Error('Fireblocks SDK is not configured'); + } + + const txBytes = fromBase64(txBytesB64); + const contentHex = Buffer.from(txBytes).toString('hex'); + + const tx = { + assetId, + operation: TransactionOperation.RAW, + source: { + type: PeerType.VAULT_ACCOUNT, + id: vaultAccountId, + }, + note: 'Sign Sui transaction from stakely staking api', + extraParameters: { + rawMessageData: { + messages: [ + { + content: contentHex, + }, + ], + }, + }, + }; + + const fbTx = await fireblocks.createTransaction(tx); + const result = await waitForTxCompletion(fbTx); + + const signedMessage = result.signedMessages && result.signedMessages[0]; + if (!signedMessage || !signedMessage.signature || !signedMessage.signature.fullSig) { + throw new Error('Fireblocks did not return a usable signature for Sui'); + } + + // Fireblocks returns raw Ed25519 signature and public key data. + // Sui expects base64(flag || sig || pubkey). For now, assume fullSig is already + // the concatenation of signature || pubkey as hex and wrap with the Ed25519 flag (0x00). + const fullSigHex = signedMessage.signature.fullSig; + const fullSigBytes = Buffer.from(fullSigHex, 'hex'); + + const suiSigWithFlag = Buffer.concat([Buffer.from([0]), fullSigBytes]); + const suiSignatureB64 = toBase64(suiSigWithFlag); + + return suiSignatureB64; +}; +``` + +Steps: + +1. Get `unsigned_tx_b64` from a stake or unstake action response. +2. Call `signWithFb(unsigned_tx_b64, SUI_FIREBLOCKS_ASSET_ID, FIREBLOCKS_VAULT_ID)` to retrieve the signature. +3. Call `action/prepare` with the returned signature string.