@0xsoulful/sdk
v0.1.8
Published
Typed client SDK for Soul contracts, generated from Foundry artifacts using viem + wagmi.
Readme
@0xsoulful/sdk
Typed client SDK for Soul contracts, generated from Foundry artifacts using viem + wagmi.
Install
npm i @0xsoulful/sdk viemQuickstart (env-aware SDK)
import { createWalletClient, createPublicClient, http } from "viem";
import { createSdk } from "@0xsoulful/sdk";
const RPC_URL = process.env.RPC_URL!;
const publicClient = createPublicClient({ transport: http(RPC_URL) });
const walletClient = createWalletClient({ transport: http(RPC_URL), account: /* your account */ });
// SOUL_ENV selects addresses from packages/sdk/src/addresses.ts for chain
// export SOUL_ENV=dev | main (defaults to main)
const sdk = createSdk({ client: publicClient });
// Use OfferManager module
const offer = sdk.offer();
await offer.depositLyx(walletClient, 1n * 10n ** 18n);SoulToken usage
High-level helpers for the SOUL host and its auction/burn extensions.
import { createWalletClient, createPublicClient, http } from "viem";
import { createSdk } from "@0xsoulful/sdk";
const client = createPublicClient({ transport: http(process.env.RPC_URL!) });
const wallet = createWalletClient({ transport: http(process.env.RPC_URL!), account: /* your account */ });
const sdk = createSdk({ client });
const soul = sdk.soul();
// Read current auction type ("native" | "stable" | "none")
const kind = await soul.getAuctionType();
if (kind === "native") {
// Quote current price in wei and extension address
const { priceWei, extension, round } = await soul.quotePurchaseWithLyx();
// Purchase with native LYX at the current price (optional slippage guard)
await soul.purchaseWithLyx({ walletClient: wallet, maxPriceWei: priceWei });
}
// Request burn of N SOUL via burn extension wired on host
await soul.requestBurn({ walletClient: wallet, amount: 1n });
// Native auction helpers
const { priceWei } = await soul.quotePurchaseWithLyx();
// Watch for USD crossing and trigger callback slightly before
const unwatchUsd = await soul.watchUsdCrossing({
targetUsd1e18: 30n * 10n ** 18n,
preflightSeconds: 5,
onTrigger: async ({ currentUsd, currentWei }) => {
// e.g., place buy or alert
},
});
// Stop watching
unwatchUsd();Notes:
- Address selection is environment-aware. Set
SOUL_ENV=devto use dev deployments on chain 42. - You can override addresses or merge extension ABIs when creating the SDK if needed.
Development
- Build contracts:
npm run contracts:build - Generate SDK:
npm run sdk:prepare - Only codegen (viem):
npm run sdk:codegen
Versioning & Releases
This repo uses Changesets. To release:
- Create a changeset:
npx changeset - Commit and merge to main.
- Run
npm run releaseto version and publish@0xsoulful/sdk.
Address management
Addresses are auto-extracted from Foundry broadcast/**/run-latest.json into src/addresses.ts.
Environments on the same chain (main vs dev)
- The SDK supports two environments on the same chain id (e.g., LUKSO mainnet
42):mainanddev. - Environment is inferred from the Foundry script folder name:
- If the broadcast path contains a script directory with
Devin its name (case-insensitive), those deployments go underdev. - Otherwise, deployments go under
main.
- If the broadcast path contains a script directory with
You can select the environment in-app by setting process.env.SOUL_ENV = "dev" | "main" (or globalThis.SOUL_ENV in the browser), or by passing the env parameter to helpers like getAddress.
The generated shape is:
export const deployments: {
[chainId: number]: {
main: Record<string, string>;
dev: Record<string, string>;
};
}For legacy flat outputs, the SDK will mirror the single map to both main and dev for backward compatibility.
Offer SDK (OfferManager + Extensions)
High-level API for creating and executing SOUL auction offers through the LSP17-extendable OfferManager host. Works for both user-side (signing + deposits) and keeper-side (execution).
Quickstart
import { createPublicClient, createWalletClient, http } from "viem";
import { createSdk } from "@0xsoulful/sdk";
const publicClient = createPublicClient({ transport: http(process.env.RPC_URL!) });
const walletClient = createWalletClient({ transport: http(process.env.RPC_URL!), account: /* your account */ });
const sdk = createSdk({ client: publicClient });
// 1) User deposits LYX
await sdk.offer().depositLyx(walletClient, 5n * 10n ** 18n);
// 2) User signs a Limit offer
const signedLimit = await sdk.offer().signLimitOffer(walletClient, {
maker: /* user */, recipient: /* 0x0 => defaults to maker */, maxPriceWei: 3_400_000_000_000_000_000n,
validAfter: 0n, validBefore: 0n, salt: BigInt(Date.now())
});
// 3) Keeper executes when eligible
await sdk.offer().executeLimit(walletClient, signedLimit);API
- Balances
depositLyx(wallet, amountWei)withdrawLyx(wallet, amountWei)getBalance(user)
- Signing
signLimitOffer(wallet, offer)→{ offer, signature }signDcaOffer(wallet, offer)→{ offer, signature }
- Execution (keeper or user)
executeLimit(walletOrKeeper, signedOffer)executeDca(walletOrKeeper, signedOffer)
- Introspection
getExtensionForSelector(selector)
Offchain storage
Store the object returned by signLimitOffer / signDcaOffer along with metadata:
{
"kind": "limit",
"offer": { /* typed offer */ },
"signature": "0x...",
"maker": "0x...",
"salt": "...",
"createdAt": 1710000000000
}The keeper fetches these, pre-filters by validity window, user balance, price cap (and DCA cadence via progress), then calls executeLimit / executeDca. See documents/technical/Keeper-Execution-Guide.md.
