@steerprotocol/collateral-rail-sdk
v0.1.0
Published
TypeScript SDK for Collateral Rail hook integrations (browser and Node.js)
Readme
Collateral Rail SDK
TypeScript SDK for interacting with Collateral Rail protocol contracts from Node.js and browser environments.
The package is built with viem and wagmi and supports:
- Protocol action read/write helpers (
CollateralManager,CollateralSettlementController,RegisteredIssuerBrokerAdapter,RiskConfig,HybridCollateralSettlementHook,ChainlinkOracleAdapter,SampleStablecoin) - Swap call builders with hook-aware
hookDataand optional signed-quote flow - Settlement and risk event decoding
- Error decoding for protocol-specific reverts
- Market launch planning helpers
- Signed quote helper surfaces (
signSignedSwapQuote,buildSignedSwapQuotePayload,splitSignature) - Registered issuer hook-data and broker-payload builders for the USDM1 AtomicBroker rail
Signed Quote Quick Rules
- Signed quote mode is optional; no signed quote uses canonical Model 1 behavior.
- If a signed quote is supplied and validation fails, settlement reverts (no same-transaction fallback).
expiryis a Unix timestamp in seconds.maxAbsAmountis compared againstabs(amountSpecified)for the swap.
Client Model
Primary SDK API is viem-first and is stable across environments:
PublicClientfor reads (Node.js, scripts, backend services, server-side rendering)WalletClientfor writes (Node.js + browser signing)
@wagmi/cli generated actions are included as an optional typed layer for teams that already use wagmi.
Why this design
- Avoids a breaking refactor in environments that don’t use wagmi
- Keeps transport and signer concerns explicit
- Preserves compatibility for backend tooling, tests, and multi-runtime usage
Client Compatibility
Recommended pattern (stable)
import {
createProtocolPublicClient,
createProtocolWalletClient,
previewCollateralManagerSwap,
} from "@steerprotocol/collateral-rail-sdk";
import { optimismSepolia } from "viem/chains";
const publicClient = createProtocolPublicClient({
chain: optimismSepolia,
rpcUrl: "https://sepolia.optimism.io",
});
const walletClient = createProtocolWalletClient({
chain: optimismSepolia,
rpcUrl: "https://sepolia.optimism.io",
});
await previewCollateralManagerSwap(
publicClient,
collateralManager,
sender,
poolKey,
swapParams,
);Optional wagmi-typed action path
For teams already on wagmi, generated typed contract actions are available as:
import { generated } from "@steerprotocol/collateral-rail-sdk";
// generated.readCollateralManagerMaxMintable(config, { address, args: [account] })
// generated.writeCollateralManagerSetOracleAdapter(config, { address, args: [adapter] })This is additive and does not replace the viem-first helpers.
Installation
npm install @steerprotocol/collateral-rail-sdkBrowser (wagmi) Example
import {
clientsFromWagmiConfig,
buildExactInputSignedQuoteCallDescriptor,
buildSwapIntent,
signSignedSwapQuote,
type SignedSwapQuoteBase,
} from "@steerprotocol/collateral-rail-sdk";
import { sepolia } from "viem/chains";
const clients = clientsFromWagmiConfig({ config: wagmiConfig });
const params = {
amountSpecified: -10_000000n,
sqrtPriceLimitX96: 0n,
amountOutMinimum: 0n,
zeroForOne: true,
};
const intent = buildSwapIntent(params, undefined);
console.log(intent.direction); // "mint"
const swapInput = {
executorAddress: "0x...", // SignedCollateralSwapRouter address
poolKey: {
currency0: "0x111...",
currency1: "0x222...",
fee: 3000,
tickSpacing: 60,
hooks: "0x...",
},
amountIn: 1n,
amountOutMin: 1n,
sqrtPriceLimitX96: 0n, // executor applies the direction-based unbounded native v4 default
zeroForOne: true,
hookData: "0x1234", // signed quote bytes or explicit controller hook data
receiver: "0xRecipient",
deadline: BigInt(Math.floor(Date.now() / 1000) + 180),
};
const call = buildExactInputSignedQuoteCallDescriptor(swapInput);
// pass `call` to your write path.
// Signed-quote mode
const quote: SignedSwapQuoteBase = {
poolId: "0x2222222222222222222222222222222222222222222222222222222222222222",
trader: "0x3333333333333333333333333333333333333333",
maxAbsAmount: 1_000000n,
quotedPriceE18: 1_000_000000000000000000n,
// unix timestamp in seconds
expiry: 1_700_000_000n,
};
const signedSwapQuote = await signSignedSwapQuote(
clients.walletClient,
"0xCollateralManager",
11155111,
quote,
"0x3333333333333333333333333333333333333333",
);
const signedCall = buildExactInputSignedQuoteCallDescriptor({
...swapInput,
signedSwapQuote,
});
// pass `signedCall` when a signed quote is required.Registered issuer AtomicBroker hook data
For the registered-issuer path, build compact hook data from the signed quote plus the AtomicBroker permit payload. The SDK does not fetch or bless API responses; callers should pass the settlement-ready subset returned by their issuer service.
Registered-issuer broker payloads are exact issuer-leg intents. The signed swap quote can bound the overall swap with maxAbsAmount, but the broker payload must authorize the exact issuer leg that should be settled through the issuer. If the pool, broker, or manager risk state makes that exact issuer leg unavailable at execution time, the hook can route zero through the issuer and leave execution to the pool path; it will not resize a 10-unit broker permit into a 4-unit issuer fill.
import {
REGISTERED_ISSUER_ACTION,
encodeRegisteredIssuerBrokerPayload,
encodeRegisteredIssuerHookData,
} from "@steerprotocol/collateral-rail-sdk";
const brokerPayload = encodeRegisteredIssuerBrokerPayload({
issuerId,
action: REGISTERED_ISSUER_ACTION.Mint,
collateralPrice,
usdmPrice,
depositPermit,
});
const hookData = encodeRegisteredIssuerHookData({
issuerId,
quote: signedSwapQuote,
brokerPayload,
});
const call = buildExactInputSignedQuoteCallDescriptor({
...swapInput,
hookData,
});Use getRegisteredIssuerConfig, setRegisteredIssuerConfig, and getRegisteredIssuerBrokerCapacity for controller/adapter reads and admin setup.
Liquidation helpers
Liquidation account discovery is intentionally caller-owned. Bots and sidecars should choose their own candidate source, block window, indexing strategy, and retry policy. Once a candidate account is known, the SDK provides the controller reads and hook-data construction needed to build an exact liquidation swap.
import {
buildExactInputLiquidationCallDescriptor,
buildLiquidationHookDataFromPreview,
getMaxLiquidatable,
previewLiquidation,
simulateExactInputLiquidationSwap,
} from "@steerprotocol/collateral-rail-sdk";
const maxRepay = await getMaxLiquidatable(
publicClient,
liquidationController,
collateralManager,
account,
);
if (maxRepay === 0n) {
return;
}
const preview = await previewLiquidation(
publicClient,
liquidationController,
collateralManager,
account,
maxRepay,
);
const deadline = BigInt(Math.floor(Date.now() / 1000) + 180);
const hookData = buildLiquidationHookDataFromPreview({
manager: collateralManager,
account,
repayAmount: maxRepay,
preview,
deadline,
});
const route = {
executorAddress: "0xSignedCollateralSwapRouter",
amountIn: maxRepay,
amountOutMin: preview.collateralOut,
zeroForOne: true,
receiver: liquidator,
deadline,
poolKey,
hookData,
};
await simulateExactInputLiquidationSwap(publicClient, route);
const call = buildExactInputLiquidationCallDescriptor(route);The encoded hook data binds the manager, account, repay amount, expected collateral out, minimum collateral out, and deadline. If the account state changes between preview and execution, the controller/hook path is designed to fail closed rather than silently resize the liquidation leg.
Node.js Example
import {
createProtocolPublicClient,
createProtocolWalletClient,
getCollateralManagerMaxMintable,
previewCollateralManagerSwap,
} from "@steerprotocol/collateral-rail-sdk";
import { optimismSepolia } from "viem/chains";
const rpc = "https://sepolia.optimism.io";
const publicClient = createProtocolPublicClient({
chain: optimismSepolia,
rpcUrl: rpc,
});
const walletClient = createProtocolWalletClient({
chain: optimismSepolia,
rpcUrl: rpc,
});
const maxMintable = await getCollateralManagerMaxMintable(
publicClient,
"0xCollateralManager",
"0xUser"
);
const preview = await previewCollateralManagerSwap(
publicClient,
"0xCollateralManager",
"0xUser",
{
currency0: "0x111...",
currency1: "0x222...",
fee: 3000,
tickSpacing: 60,
hooks: "0x...",
},
{
amountSpecified: -1_000000n,
sqrtPriceLimitX96: 0n,
amountOutMinimum: 0n,
zeroForOne: true,
}
);Scripts
npm run build– buildsdist/ESM and CJS outputsnpm run generate– regenerates typed action constants from local ABIs via@wagmi/clinpm test– runs unit testsnpm run typecheck– runs TypeScript checking
Typed action generation
To refresh the generated action layer:
npm run generateThe generated output is exposed as:
import { generated } from "@steerprotocol/collateral-rail-sdk";Notes
- The SDK intentionally keeps transports injectable so the same action helpers work in both backend and frontend runtimes.
setMinterand other owner-only actions require your wallet to be authorized by the destination contract.settleCollateralManagerSwapis for hook-authorized callback contexts (tests/simulations/operator tooling), not standard app swap execution.
Notes on migration timeline
- Existing viem helpers remain the stable path and default for now.
- Generated wagmi actions are available for typed access where useful.
- A full migration to a wagmi-centric runtime would be an explicit separate release.
For production signed quote swaps, prefer the SignedCollateralSwapRouter builders. They expose sqrtPriceLimitX96 directly; 0n leaves the executor on the direction-based unbounded default, while amount min/max fields remain the final economic slippage checks.
