docs: Add security token locking docs for TS SDK and TS Compat SDK v1.2.0#123
docs: Add security token locking docs for TS SDK and TS Compat SDK v1.2.0#123
Conversation
….2.0 TS SDK (api-reference.mdx): - Add Security Token Locking section with all 6 methods: approveSecurityToken, escrowSecurityTokens, getLockedBalance, initiateSecurityTokensWithdrawal, cancelSecurityTokensWithdrawal, withdrawSecurityTokens TS SDK (examples.mdx): - Add locking + app registration example TS SDK (configuration.mdx): - Document lockingContractAddress in blockchain config TS Compat SDK (overview.mdx): - Add Security Token Locking method cheat sheet - Add locking usage example section - Add acknowledge, checkTokenAllowance, getBlockchains, getActionAllowances, getEscrowChannel to queries table - Add App Registry table (getApps, registerApp) - Add rebalanceAppSessions, waitForClose - Add OngoingStateTransitionError to error table - Fix getUserFacingMessage(err) -> getUserFacingMessage(typed) Made-with: Cursor
📝 WalkthroughWalkthroughThis PR expands TypeScript SDK documentation across four files, introducing security token locking methods, additional query and registry operations, lifecycle management, and a new typed error. Notably, the security token locking example appears duplicated in the examples file. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/build/sdk/typescript-compat/overview.mdx (1)
216-223:⚠️ Potential issue | 🟠 MajorImport
NitroliteClientin this error-handling snippet.Line 222 calls
NitroliteClient.classifyError(err), but the import only brings ingetUserFacingMessageandAllowanceError. As written, the example does not compile when copied.🩹 Proposed fix
-import { getUserFacingMessage, AllowanceError } from '@yellow-org/sdk-compat'; +import { + NitroliteClient, + getUserFacingMessage, + AllowanceError, +} from '@yellow-org/sdk-compat';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/build/sdk/typescript-compat/overview.mdx` around lines 216 - 223, The snippet calls NitroliteClient.classifyError(err) but NitroliteClient is not imported; update the import statement to include NitroliteClient alongside getUserFacingMessage and AllowanceError so the example compiles. Locate the import line that currently reads import { getUserFacingMessage, AllowanceError } from '@yellow-org/sdk-compat'; and add NitroliteClient to that named import list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/build/sdk/typescript/api-reference.mdx`:
- Around line 151-153: Update the "Security Token Locking" intro to include a
short "Requires:" note: state that these on-chain NonSlashableAppRegistry
methods (used for staking security tokens and gated allowances) are available
only when the SDK is configured with lockingContractAddress and must be invoked
via withBlockchainRPC(); mention both the config key lockingContractAddress and
the withBlockchainRPC() helper so readers don't assume the methods work on any
chain.
In `@docs/build/sdk/typescript/configuration.mdx`:
- Around line 60-70: The docs currently conflate the return shapes of
getConfig() and getBlockchains(); update the text and examples to disambiguate
by showing two short examples: one that calls getConfig() and accesses the array
via config.blockchains (e.g., const config = await client.getConfig(); for
(const chain of config.blockchains) { ... }) and a second that calls
getBlockchains() which returns the array directly (e.g., const blockchains =
await client.getBlockchains(); for (const chain of blockchains) { ... }), and/or
add a single clarifying sentence explicitly stating that getBlockchains()
returns the blockchain array while getConfig() returns an envelope object with a
blockchains property.
In `@docs/build/sdk/typescript/examples.mdx`:
- Around line 358-391: The example function lockAndRegisterApp currently calls
client.close() only on the success path; wrap the main flow in a try/finally so
the WebSocket client is always closed: declare the client variable outside the
try, assign it via Client.create(...) inside the try, perform
approveSecurityToken, escrowSecurityTokens, getLockedBalance, and registerApp
inside the try, and call await client.close() in the finally (checking the
client is non-null) to guarantee cleanup even on errors.
---
Outside diff comments:
In `@docs/build/sdk/typescript-compat/overview.mdx`:
- Around line 216-223: The snippet calls NitroliteClient.classifyError(err) but
NitroliteClient is not imported; update the import statement to include
NitroliteClient alongside getUserFacingMessage and AllowanceError so the example
compiles. Locate the import line that currently reads import {
getUserFacingMessage, AllowanceError } from '@yellow-org/sdk-compat'; and add
NitroliteClient to that named import list.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3a61505a-8911-4e84-92f2-51e8e35a6504
📒 Files selected for processing (4)
docs/build/sdk/typescript-compat/overview.mdxdocs/build/sdk/typescript/api-reference.mdxdocs/build/sdk/typescript/configuration.mdxdocs/build/sdk/typescript/examples.mdx
| ## Security Token Locking | ||
|
|
||
| Methods for interacting with the on-chain Locking (NonSlashableAppRegistry) contract. Used for staking security tokens to register apps and receive gated action allowances. |
There was a problem hiding this comment.
Add the locking prerequisites to the section intro.
This reads as if the six methods are available on any chain. The new configuration docs say locking is only supported when lockingContractAddress is present, and these are still on-chain calls that need withBlockchainRPC(). A short Requires: note here would prevent copy-paste failures.
✏️ Proposed doc tweak
## Security Token Locking
Methods for interacting with the on-chain Locking (NonSlashableAppRegistry) contract. Used for staking security tokens to register apps and receive gated action allowances.
+
+**Requires:** `withBlockchainRPC()` for the target chain, and a blockchain whose config includes `lockingContractAddress`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## Security Token Locking | |
| Methods for interacting with the on-chain Locking (NonSlashableAppRegistry) contract. Used for staking security tokens to register apps and receive gated action allowances. | |
| ## Security Token Locking | |
| Methods for interacting with the on-chain Locking (NonSlashableAppRegistry) contract. Used for staking security tokens to register apps and receive gated action allowances. | |
| **Requires:** `withBlockchainRPC()` for the target chain, and a blockchain whose config includes `lockingContractAddress`. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/build/sdk/typescript/api-reference.mdx` around lines 151 - 153, Update
the "Security Token Locking" intro to include a short "Requires:" note: state
that these on-chain NonSlashableAppRegistry methods (used for staking security
tokens and gated allowances) are available only when the SDK is configured with
lockingContractAddress and must be invoked via withBlockchainRPC(); mention both
the config key lockingContractAddress and the withBlockchainRPC() helper so
readers don't assume the methods work on any chain.
| The node's `getConfig()` and `getBlockchains()` methods return blockchain details including contract addresses: | ||
|
|
||
| ```typescript | ||
| const config = await client.getConfig(); | ||
| for (const chain of config.blockchains) { | ||
| console.log(`${chain.name} (${chain.id})`); | ||
| console.log(` ChannelHub: ${chain.channelHubAddress}`); | ||
| if (chain.lockingContractAddress) { | ||
| console.log(` Locking: ${chain.lockingContractAddress}`); | ||
| } | ||
| } |
There was a problem hiding this comment.
Disambiguate the getConfig() and getBlockchains() return shapes.
Line 60 groups both methods together, but Lines 63-70 only show the getConfig() envelope (config.blockchains). Elsewhere the docs show getBlockchains() returning the array directly, so this wording makes it easy to copy the wrong access pattern. Either split the examples or state explicitly that getBlockchains() already returns the array.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/build/sdk/typescript/configuration.mdx` around lines 60 - 70, The docs
currently conflate the return shapes of getConfig() and getBlockchains(); update
the text and examples to disambiguate by showing two short examples: one that
calls getConfig() and accesses the array via config.blockchains (e.g., const
config = await client.getConfig(); for (const chain of config.blockchains) { ...
}) and a second that calls getBlockchains() which returns the array directly
(e.g., const blockchains = await client.getBlockchains(); for (const chain of
blockchains) { ... }), and/or add a single clarifying sentence explicitly
stating that getBlockchains() returns the blockchain array while getConfig()
returns an envelope object with a blockchains property.
| async function lockAndRegisterApp() { | ||
| const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); | ||
| const client = await Client.create( | ||
| process.env.WS_URL!, | ||
| stateSigner, | ||
| txSigner, | ||
| withBlockchainRPC(11155111n, process.env.SEPOLIA_RPC!), | ||
| ); | ||
|
|
||
| const chainId = 11155111n; | ||
| const lockAmount = new Decimal(100); | ||
|
|
||
| // Approve the locking contract to spend tokens | ||
| await client.approveSecurityToken(chainId, lockAmount); | ||
|
|
||
| // Lock tokens for yourself | ||
| const userAddress = client.getUserAddress(); | ||
| await client.escrowSecurityTokens(userAddress, chainId, lockAmount); | ||
|
|
||
| // Check locked balance | ||
| const balance = await client.getLockedBalance(chainId, userAddress); | ||
| console.log('Locked balance:', balance.toString()); | ||
|
|
||
| // Register an app (requires locked tokens) | ||
| await client.registerApp('my-app', '{"name": "My App", "description": "Demo"}', false); | ||
| console.log('App registered'); | ||
|
|
||
| // Later: initiate unlock | ||
| // await client.initiateSecurityTokensWithdrawal(chainId); | ||
|
|
||
| // After unlock period: withdraw | ||
| // await client.withdrawSecurityTokens(chainId, userAddress); | ||
|
|
||
| await client.close(); |
There was a problem hiding this comment.
Close the client in a finally block.
Unlike the other examples in this file, this snippet only calls close() on the success path. Any failure during approval, escrow, balance lookup, or app registration leaves the WebSocket open in the copied example.
♻️ Proposed fix
async function lockAndRegisterApp() {
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
const client = await Client.create(
process.env.WS_URL!,
stateSigner,
txSigner,
withBlockchainRPC(11155111n, process.env.SEPOLIA_RPC!),
);
const chainId = 11155111n;
const lockAmount = new Decimal(100);
- // Approve the locking contract to spend tokens
- await client.approveSecurityToken(chainId, lockAmount);
-
- // Lock tokens for yourself
- const userAddress = client.getUserAddress();
- await client.escrowSecurityTokens(userAddress, chainId, lockAmount);
-
- // Check locked balance
- const balance = await client.getLockedBalance(chainId, userAddress);
- console.log('Locked balance:', balance.toString());
-
- // Register an app (requires locked tokens)
- await client.registerApp('my-app', '{"name": "My App", "description": "Demo"}', false);
- console.log('App registered');
-
- // Later: initiate unlock
- // await client.initiateSecurityTokensWithdrawal(chainId);
-
- // After unlock period: withdraw
- // await client.withdrawSecurityTokens(chainId, userAddress);
-
- await client.close();
+ try {
+ // Approve the locking contract to spend tokens
+ await client.approveSecurityToken(chainId, lockAmount);
+
+ // Lock tokens for yourself
+ const userAddress = client.getUserAddress();
+ await client.escrowSecurityTokens(userAddress, chainId, lockAmount);
+
+ // Check locked balance
+ const balance = await client.getLockedBalance(chainId, userAddress);
+ console.log('Locked balance:', balance.toString());
+
+ // Register an app (requires locked tokens)
+ await client.registerApp('my-app', '{"name": "My App", "description": "Demo"}', false);
+ console.log('App registered');
+
+ // Later: initiate unlock
+ // await client.initiateSecurityTokensWithdrawal(chainId);
+
+ // After unlock period: withdraw
+ // await client.withdrawSecurityTokens(chainId, userAddress);
+ } finally {
+ await client.close();
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function lockAndRegisterApp() { | |
| const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); | |
| const client = await Client.create( | |
| process.env.WS_URL!, | |
| stateSigner, | |
| txSigner, | |
| withBlockchainRPC(11155111n, process.env.SEPOLIA_RPC!), | |
| ); | |
| const chainId = 11155111n; | |
| const lockAmount = new Decimal(100); | |
| // Approve the locking contract to spend tokens | |
| await client.approveSecurityToken(chainId, lockAmount); | |
| // Lock tokens for yourself | |
| const userAddress = client.getUserAddress(); | |
| await client.escrowSecurityTokens(userAddress, chainId, lockAmount); | |
| // Check locked balance | |
| const balance = await client.getLockedBalance(chainId, userAddress); | |
| console.log('Locked balance:', balance.toString()); | |
| // Register an app (requires locked tokens) | |
| await client.registerApp('my-app', '{"name": "My App", "description": "Demo"}', false); | |
| console.log('App registered'); | |
| // Later: initiate unlock | |
| // await client.initiateSecurityTokensWithdrawal(chainId); | |
| // After unlock period: withdraw | |
| // await client.withdrawSecurityTokens(chainId, userAddress); | |
| await client.close(); | |
| async function lockAndRegisterApp() { | |
| const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!); | |
| const client = await Client.create( | |
| process.env.WS_URL!, | |
| stateSigner, | |
| txSigner, | |
| withBlockchainRPC(11155111n, process.env.SEPOLIA_RPC!), | |
| ); | |
| const chainId = 11155111n; | |
| const lockAmount = new Decimal(100); | |
| try { | |
| // Approve the locking contract to spend tokens | |
| await client.approveSecurityToken(chainId, lockAmount); | |
| // Lock tokens for yourself | |
| const userAddress = client.getUserAddress(); | |
| await client.escrowSecurityTokens(userAddress, chainId, lockAmount); | |
| // Check locked balance | |
| const balance = await client.getLockedBalance(chainId, userAddress); | |
| console.log('Locked balance:', balance.toString()); | |
| // Register an app (requires locked tokens) | |
| await client.registerApp('my-app', '{"name": "My App", "description": "Demo"}', false); | |
| console.log('App registered'); | |
| // Later: initiate unlock | |
| // await client.initiateSecurityTokensWithdrawal(chainId); | |
| // After unlock period: withdraw | |
| // await client.withdrawSecurityTokens(chainId, userAddress); | |
| } finally { | |
| await client.close(); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/build/sdk/typescript/examples.mdx` around lines 358 - 391, The example
function lockAndRegisterApp currently calls client.close() only on the success
path; wrap the main flow in a try/finally so the WebSocket client is always
closed: declare the client variable outside the try, assign it via
Client.create(...) inside the try, perform approveSecurityToken,
escrowSecurityTokens, getLockedBalance, and registerApp inside the try, and call
await client.close() in the finally (checking the client is non-null) to
guarantee cleanup even on errors.
Summary
Adds documentation for the v1.2.0 security token locking features across both the TypeScript SDK and TypeScript Compat SDK docs.
TypeScript SDK Docs
approveSecurityToken,escrowSecurityTokens,getLockedBalance,initiateSecurityTokensWithdrawal,cancelSecurityTokensWithdrawal,withdrawSecurityTokenslockingContractAddressin theBlockchaintypeTypeScript Compat SDK Docs
getApps,registerApp)getBlockchains,getActionAllowances,getEscrowChannel,checkTokenAllowance)acknowledge)rebalanceAppSessions)waitForClose)OngoingStateTransitionErrorin error tablegetUserFacingMessage(err)→getUserFacingMessage(typed)in error exampleTest Plan
npm run buildSummary by CodeRabbit