# @wireio/stake — Outpost & LIQ Staking SDK

TypeScript / JavaScript SDK for the **Outpost** and **Liquidity Staking (LIQ)** protocol on the **Wire Network**.
It gives dApps a single façade (`Staker`) over Solana and EVM implementations, while still exposing
chain‑specific power features (Outpost, pretokens, SquadsX, validator tools, etc.).

## What this SDK does
- One `Staker` that selects the right chain client by `chainId`.
- Deposit / withdraw native → LIQ tokens (SOL→liqSOL, ETH→liqETH).
- Stake / unstake LIQ into yield pools (Solana Outpost; Ethereum staking manager).
- Pre-launch WIRE pretokens purchase helpers.
- Rich `getPortfolio` view (native, liq, staked, $WIRE, yield, extras).
- Gas / fee helpers (`getDepositFee`, `getDepositBuffer`, `getSystemAPY`).
- Solana extras: balance correction + registration, Outpost state, multisig (SquadsX) flows.
- Ethereum extras: withdrawal receipts, validator deposit/lock, tranche snapshots.

## SquadsX (Solana multisig) at a glance
- Pass `extras.squadsX` when constructing a Solana `StakerConfig`:
  ```ts
  const cfg: StakerConfig = {
    provider: solAdapter,
    network: solNetwork,
    pubKey: new WirePubKey(KeyType.ED, solAdapter.publicKey!.toBytes()),
    extras: { squadsX: { multisigPDA: '<base58>', vaultIndex: 0 } },
  };
  ```
- When present, every write call (`deposit`, `withdraw`, `stake`, `unstake`, `buy`) is wrapped as a Squads vault transaction via `sendSquadsIxs`, producing a proposal that you approve/execute in Squads.  
- The Hub staking service persists the PDAs in `localStorage` (`hub_squadsx_multisigpda`, `hub_squadsx_vaultindex`) and reinitializes the `Staker` with Squads enabled; copy that pattern for multisig-first apps.

## Mental model
- **Facade first:** Use `Staker` everywhere; it routes to the right chain client and keeps the API identical.
- **Base units only:** All amounts are lamports/wei (no floats internally).
- **Read-only vs write:** If you omit `provider`/`pubKey`, clients spin up in **read-only** mode—perfect for tranche snapshots, APY, and portfolio lookups against public RPCs. Add a signer (`provider` + `pubKey`) to unlock writes (deposit/withdraw/stake/unstake/buy/register). The Hub service uses read-only configs to prefill dashboards before wallets connect, then re-initializes with signer configs once a wallet is present.
- **State views:** `getPortfolio()` is the fastest way to know “native, liq, staked, $WIRE, yield” plus handy PDAs/addresses.
- **Snapshots:** `getTrancheSnapshot(chainId)` powers price ladders, liquidity/utilization charts, and native USD hints.

## Supported networks (today)
- **Solana**: `SolChainID.Mainnet`, `SolChainID.Devnet`.
- **EVM**: `EvmChainID.Ethereum`, `EvmChainID.Hoodi` (testnet).

The unified interface is chain-id driven: `new Staker(configs, selectedChainId)` builds per-chain clients behind the scenes, and `staker.setChain(chainId)` swaps the active client. Adding future chains (e.g., Sui) only requires implementing `IStakingClient` and wiring its `chainId` into the router—no API changes for consuming apps.

## Installation
```bash
npm install @wireio/stake
```
Peer requirement: `@wireio/core >=0.1.0 <0.4.0`.

## Quick start: pick a chain, then call through `client`

```ts
import { Staker, StakerConfig, SolChainID, EvmChainID } from '@wireio/stake';
import { PublicKey as WirePubKey, KeyType } from '@wireio/core';
import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
import { ethers } from 'ethers';

// ---- Solana example (wallet adapter) ----
const solAdapter: BaseSignerWalletAdapter = /* connected wallet */;
const solCfg: StakerConfig = {
  provider: solAdapter,
  network: {
    chainId: SolChainID.Devnet,
    name: 'Wire Solana Devnet',
    rpcUrls: ['https://...', 'wss://...'],
    nativeCurrency: { symbol: 'SOL', decimals: 9 },
  },
  pubKey: new WirePubKey(KeyType.ED, solAdapter.publicKey!.toBytes()),
  extras: { squadsX: { multisigPDA: '<base58>', vaultIndex: 0 } }, // optional
};

// ---- Ethereum example (browser provider) ----
const ethProvider = new ethers.providers.Web3Provider(window.ethereum);
const ethCfg: StakerConfig = {
  provider: ethProvider,
  network: {
    chainId: EvmChainID.Ethereum,
    name: 'Ethereum',
    rpcUrls: ['https://...'],
    nativeCurrency: { symbol: 'ETH', decimals: 18 },
  },
};

const staker = new Staker([solCfg, ethCfg], SolChainID.Devnet); // default active chain
const client = staker.client; // typed staking client for the selected chain
```

### Common calls
Amounts are always **base units** (lamports / wei).
```ts
await client?.deposit(1_000_000_000n);   // stake 1 SOL -> liqSOL (or 1e9 wei -> liqETH)
await client?.withdraw(500_000_000n);    // liq -> native withdraw request
await client?.stake(200_000_000n);       // stake liq into yield pool (Sol Outpost / ETH manager)
await client?.unstake(100_000_000n);     // reverse stake (where supported)
await client?.buy(50_000_000n);          // purchase WIRE pretokens with liq
const portfolio = await client?.getPortfolio(); // unified balance snapshot
```

### Solana flow (what happens)
- `deposit(lamports)`: builds ix via `DepositClient`, optionally wraps in SquadsX vault tx, sends and confirms.
- `withdraw(lamports)`: burns liqSOL and mints a withdrawal NFT receipt (payout later via operators).
- `stake/unstake`: interacts with Outpost (`synd`/`desynd`) to move liqSOL into/out of yield pool.
- `buy(lamports)`: purchase $WIRE pretokens using liqSOL (Token-2022).
- Multisig: if `extras.squadsX` is set, write calls become Squads proposals (`sendSquadsIxs`).
- Portfolio extras include key PDAs (vault, reserve pool, user ATA) plus Outpost index/shares math.

### Ethereum flow (what happens)
- `deposit(amountWei)`: via `ConvertClient.performDeposit`; returns tx hash.
- `withdraw(amountWei)`: burns liqETH and enqueues withdrawal; receipts are NFTs.
- `getPendingWithdraws()` / `claimWithdraw(tokenId)`: manage withdrawal queue.
- `stake(amountWei)`: stakes liqETH; `unstakePrelaunch(tokenId, recipient)` handles prelaunch path.
- `buy(amountWei)`: pretokens via `PretokenClient.purchasePretokensWithLiqETH`.
- `validatorDeposit()`: validator bond/lock helper.
- Portfolio includes native/liq/staked/$WIRE and yield (index/shares) when contracts expose them.

## Solana specifics
- **Clients composed under the hood**: `DepositClient`, `DistributionClient`, `OutpostClient`,
  `TokenClient`, `ValidatorLeaderboardClient`, `SolanaProgramService`.
- **SquadsX multisig**: provide `extras.squadsX` to have deposits / stakes wrapped as vault
  transactions (`sendSquadsIxs`).
- **Outpost / Yield**: `getPortfolio()` returns `yield` (index-scale math) and `staked` amounts; `wire`
  reports pretokens (1e8 scale).
- **Helpers**: `getDepositBuffer`, `getDepositFee`, `getSystemAPY`, tranche snapshots via
  `getTrancheSnapshot(chainId)`.

## Ethereum specifics
- Uses `ethers` provider/signer; falls back to JSON RPC for read-only.
- Modules used: `ConvertClient` (deposit/withdraw), `StakeClient`, `PretokenClient`, `OPPClient`,
  `ReceiptClient` (withdrawal NFTs), `ValidatorClient`.
- Additional endpoints:
  - `getPendingWithdraws()` → pending withdrawal receipts.
  - `claimWithdraw(tokenId)` → claim queued withdrawal by NFT id.
  - `unstakePrelaunch(tokenId, recipient)` → prelaunch unstake path.
  - `validatorDeposit()` → validator bond/lock flow.
  - `getTrancheSnapshot(chainId)` → pretoken ladder view with price/supply growth.
  - `getOPPMessages(address?)` and `getEthStats()` for operator/pay-rate data.

## Portfolio shape (all chains)
```ts
type Portfolio = {
  native: { amount: bigint; symbol: string; decimals: number };
  liq:    { amount: bigint; symbol: string; decimals: number; ata?: PublicKey };
  staked: { amount: bigint; symbol: string; decimals: number };
  wire:   { amount: bigint; symbol: string; decimals: number };
  yield?: { currentIndex: bigint; indexScale: bigint; totalShares: bigint;
            userShares: bigint; estimatedClaim?: bigint; estimatedYield?: bigint };
  extras?: Record<string, any>;
  chainID: number;
};
```

## Error handling
- All write methods throw on failure; wrap with `try/catch`.
- Solana transaction failures include logs; Ethereum methods surface tx hashes for inspection.
- Passing `provider` is required for writes; read-only is supported for many getters.

## Using from a service (pattern)
Your `staking.service.ts` can simply forward to the selected `client`. The Wire Hub webapp
(`wire-hub-webapp/src/app/_services/staking.service.ts`) does this and adds read-only Outpost
clients, tranche/APY polling, SquadsX, and retryable portfolio refresh. Typical shape:
```ts
export class StakingService {
  constructor(private staker: Staker) {}
  deposit(amount: bigint) { return this.staker.client?.deposit(amount); }
  withdraw(amount: bigint) { return this.staker.client?.withdraw(amount); }
  stake(amount: bigint) { return this.staker.client?.stake(amount); }
  unstake(amount: bigint) { return this.staker.client?.unstake(amount); }
  buy(amount: bigint) { return this.staker.client?.buy(amount); }
  portfolio() { return this.staker.client?.getPortfolio(); }
  stats(chainId: number) { return this.staker.client?.getTrancheSnapshot(chainId); }
}
```
Switch chains at runtime with `staker.setChain(chainId)`.

Hub-specific extras you can reuse:
- Read-only bootstrap: configs with only `network` to fetch tranche snapshots/APY before a wallet connects.
- APY helpers: `getSystemAPY()` per chain; normalize number/string/bigint shapes.
- Deposit buffer: `getDepositBuffer({ balanceOverrideLamports })` to compute max-spendable.
- SquadsX multisig: store `multisigPDA` + `vaultIndex` (localStorage keys `hub_squadsx_multisigpda`, `hub_squadsx_vaultindex`) and pass via `extras.squadsX` so write calls become Squads proposals.
- ETH prelaunch/receipts: `getPendingWithdraws`, `claimWithdraw(tokenId)`, `unstakePrelaunch(tokenId, recipient)`, `fetchPrelaunchReceipts`.
- Tranche ladders: cache `getTrancheSnapshot(chainId)` per chain for dashboards (liquidity/utilization/arb spread).

## Portfolio fields (returned by `getPortfolio`)
- `native`: wallet balance in base units; symbol/decimals from network.
- `liq`: liquid staking token balance (Token-2022 ATA on Sol; ERC-20 on EVM).
- `staked`: liq staked into Outpost (Sol) or staking manager (ETH).
- `wire`: pretoken balance (1e8 scale).
- `yield`: `currentIndex`, `indexScale`, `totalShares`, `userShares`, `estimatedClaim`, `estimatedYield`.
- `extras`: PDAs, price/index hints useful for UIs and debugging.
- `chainID`: the chain the snapshot came from.

## Tranche snapshot fields (via `getTrancheSnapshot`)
- `currentTranche`, `currentPriceUsd` (1e8), `currentTrancheSupply`, `initialTrancheSupply`.
- `ladder`: nearby rows `{ id, capacity, sold, remaining, priceUsd }` (1e8 scale).
- `currentIndex`, `totalShares`, `totalPretokensSold`, `supplyGrowthBps`, `priceGrowthCents`.
- `nativePriceUsd` if available (SDK backfills from market data when the chain doesn’t provide it).

## Building, testing, docs
```bash
npm ci            # install
npm run prepare   # build to lib/ via rollup
npm test          # full test suite (make targets exist for sol/eth split)
npm run docs      # generate typedoc to docs/ (gh-pages ready)
```

## Project structure (src/)
- `staker.ts` – chain router / façade.
- `types.ts` – shared interfaces (IStakingClient, Portfolio, tranche snapshots).
- `networks/solana/*` – clients, IDL wiring, constants, utils.
- `networks/ethereum/*` – contract service, clients, tranche math.
- `assets/` – IDLs, ABIs, artifacts.

## Versioning & license
- License: **FSL-1.1-Apache-2.0** (see `LICENSE.md`).

## Links
- API reference: <https://Wire-Network.github.io/sdk-stake/>
- Source: <https://github.com/Wire-Network/sdk-stake>
