npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@canopyhub/canopy-sdk

v1.1.0

Published

TypeScript SDK for Canopy Protocol

Readme

Canopy SDK

TypeScript SDK for Canopy Protocol on Movement and Aptos.

It includes:

  • Canopy vault reads and transaction builders
  • rewards staking / claim helpers
  • Meridian ALM vault support
  • deployment + ABI registries
  • contract lookup helpers
  • Movement helper-module-backed batch reads

Packages

The repo publishes four packages:

  • @canopyhub/canopy-sdk
  • @canopyhub/canopy-sdk-core
  • @canopyhub/canopy-sdk-deployments
  • @canopyhub/canopy-sdk-bindings

Most applications should install only the root SDK:

pnpm add @canopyhub/canopy-sdk

Quick Start

import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
import { createCanopySdk } from "@canopyhub/canopy-sdk";

const client = new Aptos(
  new AptosConfig({
    network: Network.MAINNET
  })
);

const sdk = createCanopySdk(client, {
  chain: "movement-mainnet",
  offchain: {
    sentioApiKey: process.env.SENTIO_API_KEY, // optional, enables dynamic rewards pool discovery
  },
});

CanopySdk only exposes protocol clients that exist on the selected chain:

  • sdk.canopy
  • sdk.rewards
  • sdk.alm.meridian

Chain Support

| Chain | Canopy | Rewards | Meridian ALM | | --- | --- | --- | --- | | movement-mainnet | yes | yes | yes | | movement-testnet | no | no | no | | aptos-testnet | yes | yes | no | | aptos-mainnet | no | no | yes |

What The SDK Exposes

Canopy vaults

const { vaults } = await sdk.canopy!.listVaults({ limit: 20, offset: 0 });

const vault = await sdk.canopy!.getVault(vaultAddress);

const position = await sdk.canopy!.getUserVaultPosition(userAddress, vaultAddress);

const depositPayload = await sdk.canopy!.buildDepositPayload({
  vaultAddress,
  amount: 1_000_000n,
  minSharesOut: 0n,
});

const withdrawPayload = await sdk.canopy!.buildWithdrawPayload({
  vaultAddress,
  shares: 1_000_000n,
  maxLossBps: 50n,
  minAmountOut: 0n,
});

Other Canopy methods:

  • unstakeAndWithdraw(...)
  • getStrategyDetails(...)
  • getVaultAllocation(...)

Canopy batch helpers

These are currently backed by the Movement helper module and are available on movement-mainnet.

const balances = await sdk.canopy!.getBatchFungibleAssetBalances(
  [metadataA, metadataB],
  userAddress
);

const shareBalances = await sdk.canopy!.getBatchVaultSharesBalances(
  [vaultA, vaultB],
  userAddress
);

const baseMetadata = await sdk.canopy!.getBatchVaultBaseMetadataAndBalances(
  [vaultA, vaultB],
  userAddress
);

const sharesMetadata = await sdk.canopy!.getBatchVaultSharesMetadataAndBalances(
  [vaultA, vaultB],
  userAddress
);

const fullMetadata = await sdk.canopy!.getBatchVaultAllMetadataAndBalances(
  [vaultA, vaultB],
  userAddress
);

Rewards

Transaction builders:

  • buildStakeCoinPayload(...)
  • buildStakeAndSubscribeCoinPayload(...)
  • buildStakeAssetPayload(...)
  • buildStakeAndSubscribeAssetPayload(...)
  • buildWithdrawCoinPayload(...)
  • buildWithdrawAssetPayload(...)
  • buildClaimRewardsPayload(...)
  • buildSubscribePayload(...)
  • buildUnsubscribePayload(...)
  • buildUnsubscribeAndWithdrawCoinPayload(...)
  • buildUnsubscribeAndWithdrawAssetPayload(...)
  • buildCreateStakingPoolPayload(...)
  • buildStakeTokenPayload(...)
  • buildStakeVaultSharesPayload(...)

Core rewards reads:

const earned = await sdk.rewards!.getEarned({
  userAddress,
  poolAddress,
  rewardTokenAddress,
});

const poolInfo = await sdk.rewards!.getPoolInfo(poolAddress);

const rewardData = await sdk.rewards!.getRewardData(poolAddress, rewardTokenAddress);

const stakingPosition = await sdk.rewards!.getUserStakingPosition({
  userAddress,
  stakingAsset,
});

rewardRate, rewardPerTokenStored, and rewardPerToken are returned as raw fixed-point values scaled by 1e12. Divide by 10^12 in application code when you want a human decimal representation.

Rewards helper-module reads

These helper-backed reads are currently available on movement-mainnet.

const snapshot = await sdk.rewards!.getRewardsSnapshot({
  offset: 0,
  limit: 20,
  userAddress,
});

const overview = await sdk.rewards!.getRegistryOverview({
  offset: 0,
  limit: 20,
  includePools: true,
});

const userOverview = await sdk.rewards!.getUserRewardsOverview({
  userAddress,
  offset: 0,
  limit: 20,
  includePools: true,
});

Additional helper reads:

  • getRegisteredPoolCount()
  • getPoolDetails(poolAddress)
  • getRewardTokenDetails(poolAddress)
  • getUserPoolPositions({ userAddress, offset, limit })
  • getUserPoolPositionsByToken({ userAddress, stakingAsset, offset, limit })
  • getUserPoolPositionsByTokens({ userAddress, stakingAssets, offset, limit })
  • isPoolRegistered(poolAddress)
  • getUnsubscribedPools(...)
  • getUserStakedBalance(...)
  • getUserSubscribedPools(...)
  • isUserSubscribed(...)

Meridian ALM

Available on movement-mainnet and aptos-mainnet.

const vaultAddresses = await sdk.alm.meridian!.listVaults({ limit: 20, offset: 0 });

const count = await sdk.alm.meridian!.getVaultCount();

const summary = await sdk.alm.meridian!.getVaultSummary(vaultAddress);

const position = await sdk.alm.meridian!.getUserVaultPosition(vaultAddress, userAddress);

const preview = await sdk.alm.meridian!.previewWithdraw(vaultAddress, 1_000_000n);

const depositPayload = sdk.alm.meridian!.buildDepositPayload({
  vaultAddress,
  amount: 1_000_000n,
  minSharesOut: 0n,
});

Movement batch-view-backed Meridian reads:

  • getBatchVaultInfo(vaultAddresses)
  • getBatchUserVaultBalances(vaultAddresses, userAddress)
  • getBatchVaultPositions(vaultAddresses)

Transactions

All build*Payload methods return InputEntryFunctionData compatible with @aptos-labs/ts-sdk.

const payload = await sdk.canopy!.buildDepositPayload({
  vaultAddress,
  amount: 1_000_000n,
  minSharesOut: 0n,
});

await client.transaction.build.simple({
  sender: account.accountAddress,
  data: payload,
});

await sdk.simulateTransaction({
  sender: account.accountAddress,
  payload,
});

If you are using a wallet adapter, pass the same payload object into your wallet’s sign-and-submit flow. If simulation hits a known Move abort, the SDK throws a CanopyError with code: "MOVE_ABORT" and structured details.moveAbort metadata for UI handling.

Offchain Helpers

The SDK exposes one optional data client under sdk.data:

  • sdk.data.rewardsDiscovery

This is useful for rewards pool discovery. It is only constructed on chains with rewards support, or when you explicitly pass offchain.sentioEndpoint.

Rewards pool resolution for buildStakeVaultSharesPayload(...) uses:

  1. explicit poolAddresses
  2. Sentio lookup, if configured for the chain

You can inspect the active discovery source with:

const status = sdk.data.rewardsDiscovery?.getStatus();

Contract And ABI Lookup

import {
  getContract,
  requireContract,
  getCanopyStrategyContract,
  inferCanopyStrategyProtocol,
} from "@canopyhub/canopy-sdk";
import { getDeployment, getContractAddress } from "@canopyhub/canopy-sdk/deployments";
import { getAbi, requireAbi } from "@canopyhub/canopy-sdk/bindings";

const deployment = getDeployment("movement-mainnet");
const vaultAddress = getContractAddress("movement-mainnet", "canopy.vault");
const rewardsAbi = requireAbi("movement-mainnet", "rewards.module");
const meridianRegistry = requireContract("movement-mainnet", "meridian.registry");
const maybeCanopy = getContract("movement-testnet", "canopy.router");

const protocol = inferCanopyStrategyProtocol("movement-mainnet", strategyAddress);
const strategy = protocol
  ? getCanopyStrategyContract("movement-mainnet", protocol)
  : null;

Lookup semantics:

  • get* returns undefined or null when a supported chain lacks that deployment
  • require* throws for missing deployments or ABIs
  • unsupported chain names throw explicit errors

Subpath Imports

The root package also exports three subpaths:

import { normalizeMoveAddress } from "@canopyhub/canopy-sdk/core";
import { getDeployment } from "@canopyhub/canopy-sdk/deployments";
import { requireAbi } from "@canopyhub/canopy-sdk/bindings";

If you need the leaf packages directly:

import { normalizeMoveAddress } from "@canopyhub/canopy-sdk-core";
import { getDeployment } from "@canopyhub/canopy-sdk-deployments";
import { requireAbi } from "@canopyhub/canopy-sdk-bindings";

Repo Layout

canopy-sdk/
├── packages/
│   ├── core/
│   ├── deployments/
│   ├── bindings/
│   └── sdk/

Surf Follow-Ups

Possible next improvements on top of the current Surf integration:

  • Add typed simulate* SDK helpers for common Canopy, Rewards, and Meridian transaction flows.
  • Use Surf useABI(...).view / useABI(...).entry selectively for the simplest internal module calls where it reduces SDK plumbing.
  • Consider exposing typed resource readers for useful account resources if a real consumer needs them.
  • Keep generated ABI files as the source of truth, but consider using Surf fetchABI(...) in internal diagnostics or ABI drift tooling.
  • Continue reducing custom view plumbing only where Surf return typing stays readable and does not make the SDK API worse. ├── scripts/ ├── tests/ └── examples/

Package roles:

- `packages/core`
  shared Move/address/view/payload/error utilities
- `packages/deployments`
  chain registry, feature flags, contract addresses
- `packages/bindings`
  checked-in ABI registry by chain
- `packages/sdk`
  user-facing protocol clients

## Development

```bash
pnpm install
pnpm run hooks:install
pnpm run typecheck
pnpm test
pnpm run check:exports
pnpm run check:imports
pnpm run abi:check-local
pnpm build

pnpm run hooks:install configures the repo-local .githooks/pre-commit hook, which runs abi:check-local when staged changes touch deployment addresses, generated ABI files, chain bindings, or the ABI manifest.

For the example app:

cd examples/react
pnpm install
pnpm dev

License

MIT