API ReferenceLOC lifecycleInterface
LOCModule
LOC (letter of credit) operations: queries, workflow builders, and pre-flight validation.
Reached as sdk.loc on a constructed AnvilSDK instance, which creates its
own modules — consumers never call new LOCModule(...). The class is exported type-only so its
methods appear in the API reference and so integrators can name the type without reaching into
package internals.
Methods
buildCancelAndWithdrawWorkflow()
buildCancelAndWithdrawWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that cancels one or more LOCs and withdraws the collateral they release, in a single sequence.
Because Multicall is the immediate caller, every unexpired LOC must be supplied through
cancelCalls with the beneficiary’s valid authorization, even when the connected transaction signer
is that beneficiary. Use locIDs only as shorthand for already-expired LOCs. The withdrawal leg is
subject to the vault’s withdrawal fee
(VaultModule.getWithdrawalFeeBasisPoints).
Parameters
| Parameter | Type | Description |
|---|---|---|
params | CancelLOCsAndWithdrawParams | The LOCs to cancel and what to withdraw afterwards. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildCancelAuthorization()
buildCancelAuthorization(params): Promise<CancelLOCAuthorizationDefinition>;Builds the exact EIP-712 cancellation definition without signing it.
Parameters
| Parameter | Type |
|---|---|
params | BuildCancelLOCAuthorizationParams |
Returns
Promise<CancelLOCAuthorizationDefinition>
buildCancelAuthorizationWorkflow()
buildCancelAuthorizationWorkflow(params): Promise<WalletOperationWorkflow>;Builds a one-step workflow that signs a cancellation authorization.
Parameters
| Parameter | Type |
|---|---|
params | BuildCancelLOCAuthorizationParams |
Returns
Promise<WalletOperationWorkflow>
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildCancelManyWorkflow()
buildCancelManyWorkflow(params): Promise<WalletOperationWorkflow>;Builds a workflow that cancels one or more LOCs atomically through Multicall.
Because Multicall is the immediate caller, every unexpired LOC must carry the beneficiary’s valid authorization, even when the connected transaction signer is that beneficiary. Authorizations consume sequential nonces per beneficiary in batch order; expired LOCs need no authorization and consume no nonce. See CancelLOCsParams for a complete signing example.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | CancelLOCsParams | The ordered LOC cancellation calls to submit atomically. |
Returns
Promise<WalletOperationWorkflow>
The signer-pinned workflow to present and execute. It reruns fresh validation before every execution attempt.
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree while building. A signer change after
the workflow is built is returned by execute() in the cancellation operation’s status rather than
thrown by this builder.
buildCancelWorkflow()
buildCancelWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that cancels a LOC, releasing its reserved collateral back to the creator’s vault balance.
Before expiration the beneficiary, or a caller holding the beneficiary’s valid authorization, may cancel since cancelling gives up the beneficiary’s claim. Once the LOC has expired, cancellation is permissionless — any account may do it, and the collateral still returns to the creator.
To cancel and pull the freed collateral out of the vault in one pass, use LOCModule.buildCancelAndWithdrawWorkflow.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | CancelLOCParams | The LOC to cancel. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildConvertWorkflow()
buildConvertWorkflow(params): Promise<WalletOperationWorkflow>;Builds the permissionless workflow that converts one unexpired, unhealthy dynamic LOC into its credited token. The creator has no privileged path: a healthy LOC is rejected for every caller.
Omit liquidator (or pass the zero address) to supply approved credited tokens yourself and receive
collateral plus the incentive. Name an ILiquidator contract to forward collateral and require the
credited amount back from it. Core selects no Anvil-operated liquidator.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | ConvertLOCParams | The LOC, settlement mode, liquidator calldata, and price mode. |
Returns
Promise<WalletOperationWorkflow>
The inspectable one-step conversion workflow.
Example
const params: ConvertLOCParams = { id: 42n, // Any ILiquidator is valid; the SDK does not choose one for you. liquidator, liquidatorParams, // Omitted: fetch a fresh Pyth update when the transaction is built.};
const issues = await sdk.loc.validateConvert(params);if (issues.length > 0) throw new Error(JSON.stringify(issues));
// Healthy LOCs still revert for every caller. Conversion is// permissionless// only after the current pair's liquidation threshold is reached.const workflow = await sdk.loc.buildConvertWorkflow(params);await workflow.execute();Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildCreateDynamicWorkflow()
buildCreateDynamicWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that creates a dynamic LOC — one collateralized in a different token from the one it credits, so its collateral is priced against the credited token and can be liquidated if it falls too far.
Creation is priced, and the price update is yours to supply: pass priceUpdate from
PricingModule.getOraclePriceUpdate, whose fee the builder
attaches as the transaction’s value. Fetch it immediately before building — it goes stale within
PricingModule.getMaxPriceUpdateSecondsAgo seconds,
and the contract reverts once it has.
Building does not validate. Call LOCModule.validateCreateDynamic for early
form feedback; execute() always repeats full validation against fresh state before its first
prompt.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | CreateDynamicLOCParams | The beneficiary, credited amount, collateral token and amount, and expiration. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Example
// WETH is any registered collateral token address, e.g. one listed// by `sdk.loc.getCollateralTokens()`.const USDC = testnetProfile.contracts.USDC.address;
// A fresh Pyth price so creation does not depend on the on-chain price// being recent; its fee is attached to the transaction automatically.const priceUpdate = await sdk.pricing.getOraclePriceUpdate({ inputToken: WETH, outputToken: USDC,});
const params: CreateDynamicLOCParams = { beneficiary: '0x1111111111111111111111111111111111111111', // 1,000 USDC face value backed by 0.5 WETH of collateral. creditedTokenAmount: { tokenAddress: USDC, amount: 1_000_000_000n }, collateralTokenAmount: { tokenAddress: WETH, amount: 5n * 10n ** 17n }, expirationTimestampSeconds: dateToEthereumTimestamp( await sdk.loc.getMaxDate() ), priceUpdate,};
const issues = await sdk.loc.validateCreateDynamic(params);if (issues.length > 0) throw new Error(JSON.stringify(issues));
const workflow = await sdk.loc.buildCreateDynamicWorkflow(params);await workflow.execute();Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildCreateStaticWorkflow()
buildCreateStaticWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that creates a static LOC — one collateralized in the same token it is credited in, so it carries no price exposure and cannot be liquidated.
The workflow is assembled from current chain state: deposit, ERC-20 approve, and vault-allowance
steps appear only when the account is actually short. Read workflow.operations to show the user
every signature they are about to give before calling workflow.execute().
Building does not validate. Call LOCModule.validateCreateStatic for early
form feedback; execute() always repeats full validation against fresh state before its first
prompt.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | CreateStaticLOCParams | The beneficiary, credited amount, and expiration. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Example
const params: CreateStaticLOCParams = { beneficiary: '0x1111111111111111111111111111111111111111', // 1,000 USDC (6 decimals): face value and, for a static LOC, // collateral. tokenAmount: { tokenAddress: testnetProfile.contracts.USDC.address, amount: 1_000_000_000n, }, expirationTimestampSeconds: dateToEthereumTimestamp( await sdk.loc.getMaxDate() ),};
// Validate before touching the wallet; an empty array means valid// params.const issues = await sdk.loc.validateCreateStatic(params);if (issues.length > 0) throw new Error(JSON.stringify(issues));
// Deposit, approve, and allowance steps appear only if the vault is// short.const workflow = await sdk.loc.buildCreateStaticWorkflow(params);await workflow.execute(operations => { console.log(operations.map(operation => operation.status));});Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildExtendWorkflow()
buildExtendWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that moves a LOC’s expiration later.
Only the creator may extend, the new expiration must be later than the current one, and the LOC must not have expired already — an expired LOC cannot be revived, only cancelled. The new remaining duration must also fit within LOCModule.getMaxDuration.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | ExtendLOCParams | The LOC id and its new expiration. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildModifyCollateralWorkflow()
buildModifyCollateralWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that adds collateral to, or removes collateral from, an existing dynamic LOC.
This is the lever for managing liquidation risk on a live LOC: adding collateral moves it away from its liquidation threshold, removing moves it closer. Removing is the price-dependent direction — the projected position still has to back the credited amount — so a removal can be rejected on a price move that an addition would not be.
A LOC whose collateral and credited tokens are the same cannot be modified at all.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | ModifyLOCCollateralParams | The LOC and the collateral change to apply. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildRedeemAuthorization()
buildRedeemAuthorization(params): Promise<RedeemLOCAuthorizationDefinition>;Builds the exact EIP-712 redemption definition without signing it.
Parameters
| Parameter | Type |
|---|---|
params | BuildRedeemLOCAuthorizationParams |
Returns
Promise<RedeemLOCAuthorizationDefinition>
buildRedeemAuthorizationWorkflow()
buildRedeemAuthorizationWorkflow(params): Promise<WalletOperationWorkflow>;Builds a one-step workflow that signs a redemption authorization.
Parameters
| Parameter | Type |
|---|---|
params | BuildRedeemLOCAuthorizationParams |
Returns
Promise<WalletOperationWorkflow>
Example
const loc = await sdk.loc.getOutstandingLetterOfCredit({ id: 42n });if (!loc) throw new Error('LOC 42 is no longer outstanding');
// Beneficiary's wallet: authorize a third party to redeem half to// itself.const destinationAddress = '0x2222222222222222222222222222222222222222';const authWorkflow = await sdk.loc.buildRedeemAuthorizationWorkflow({ id: loc.reference.id, redeemAmount: loc.remainingCredited.amount / 2n, locCreditedAmount: loc.remainingCredited.amount, destinationAddress,});console.log(authWorkflow.operations[0]?.authorization?.typedData);const authorizationExecution = await authWorkflow.execute();if (authorizationExecution.status !== 'executed') { throw new Error( `authorization preflight ended as ${authorizationExecution.status}` );}const [authorization] = authorizationExecution.operations;if ( authorization?.status !== WalletOperationStatus.Signed || !authorization.signature) { throw new Error('beneficiary declined to sign');}
// Third party's wallet: the same id, amount, and destination the// beneficiary signed, plus the signature.const workflow = await sdk.loc.buildRedeemWorkflow({ redemptions: [ { id: loc.reference.id, amount: loc.remainingCredited.amount / 2n, destinationAddress, beneficiaryAuthorization: authorization.signature, }, ],});await workflow.execute();Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
buildRedeemWorkflow()
buildRedeemWorkflow(params): Promise<WalletOperationWorkflow>;Builds the workflow that redeems one or more LOCs to each resolved payout destination and releases any collateral left over to the creator. A beneficiary authorization may direct its payout to another account.
Redemption may be partial: pass less than the remaining credited amount and the LOC stays open for
the balance. Redeeming the full remaining amount removes the LOC from contract storage, after which
LOCModule.getOutstandingLetterOfCredit returns undefined for it.
On a dynamic LOC the payout asset depends on liquidator, and the SDK picks none by default because
the two modes pay out differently. With a liquidator, the collateral is converted and the redemption
pays in the credited token. Without one, the redeemer takes the collateral plus the liquidator
incentive and must itself hold and approve the credited amount the contract pulls back. Decide which
mode you want before calling.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | RedeemLOCsParams | The redemptions to perform, each an id and an amount. |
Returns
Promise<WalletOperationWorkflow>
The workflow to present and execute.
Example
// The connected wallet is the LOC's beneficiary.const loc = await sdk.loc.getOutstandingLetterOfCredit({ id: 42n });if (!loc) throw new Error('LOC 42 is no longer outstanding');
// One entry, full remaining face value, paid to the signer.const params: RedeemLOCsParams = { redemptions: [ { id: loc.reference.id, amount: loc.remainingCredited.amount }, ],};
const issues = await sdk.loc.validateRedeem(params);if (issues.length > 0) throw new Error(JSON.stringify(issues));
const workflow = await sdk.loc.buildRedeemWorkflow(params);await workflow.execute();Throws
WorkflowSignerChangedError If the SDK’s configured
accountAddress and the connected wallet’s account disagree, or if the signer observed while
executing differs from the one observed while building. Rebuild the workflow for the currently
connected signer; re-executing the same workflow succeeds only if the original signer reconnects.
getCollateralFactor()
getCollateralFactor(params): Promise<CollateralFactor | undefined>;Returns the CollateralFactor the LetterOfCredit contract holds for a collateral/credited token pair: the creation, liquidation, and incentive basis points that govern a dynamic LOC on that pair.
Returns undefined for an unconfigured pair: the SDK normalizes the contract getter’s zero-filled
tuple to an absent result. A configured pair can still have
creationCollateralFactorBasisPoints === 0n, which disables dynamic LOC creation. In either case,
LOCModule.getRequiredCollateralForDynamic returns undefined.
A ContractFunctionExecutionError from the read also returns undefined. Other errors cause this
call to reject.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCollateralFactorParams | The collateral and credited token pair to look up. |
Returns
Promise<CollateralFactor | undefined>
The collateral factor, or undefined if the pair is unconfigured or the read raises a
ContractFunctionExecutionError.
getCollateralToken()
getCollateralToken(params): Promise<CollateralToken | undefined>;Returns the CollateralToken at an address, or undefined if the vault does
not know it as collateral.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCollateralTokenParams | The collateral token address to look up. |
Returns
Promise<CollateralToken | undefined>
The collateral token, or undefined if there is none.
getCollateralTokens()
getCollateralTokens(params?, options?): Promise<CollectionPage<CollateralToken>>;Returns a page of every ERC-20 the CollateralVault knows as collateral, each with its enabled flag
and indexed name, symbol, and decimals.
Served from the subgraph — so a subgraph URL is required. Each call performs a fresh read; integrations that need caching should use the SDK’s shared query definition or their own query layer.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCollateralTokensParams | Paging and filter options for the collection. |
options | ReadExecutionOptions | Cancellation controls for this page read. |
Returns
Promise<CollectionPage<CollateralToken>>
The matching page of collateral tokens.
Example
// Every token the CollateralVault currently accepts, across all pages.const tokens: CollateralToken[] = [];let cursor: string | undefined;do { const page = await sdk.loc.getCollateralTokens({ enabled: true, cursor, }); tokens.push(...page.items); cursor = page.nextCursor ?? undefined;} while (cursor);Throws
ConfigError If neither subgraphUrl nor the configured profile
supplies a subgraph endpoint. Thrown before any read.
getCollateralTokensForCreditedToken()
getCollateralTokensForCreditedToken(params, options?): Promise<CollectionPage<CollateralToken>>;Returns the collateral tokens that may actually back a LOC credited in the given token — the list to populate a collateral picker with.
A token qualifies when it is not disabled and either is the credited token itself (the static case, where a token collateralizes itself), or has an enabled asset pair and a collateral factor with a non-zero creation requirement against the credited token (the dynamic case).
This is narrower than LOCModule.getCollateralTokens, which lists everything the vault accepts without regard to what it can back. Requires a subgraph URL.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCollateralTokensForCreditedTokenParams | The credited token to find eligible collateral for, plus paging options. |
options | ReadExecutionOptions | Cancellation controls for this page read. |
Returns
Promise<CollectionPage<CollateralToken>>
The matching page of eligible collateral tokens.
getCreateTagByteLimit()
getCreateTagByteLimit(): number;Maximum reference-data bytes accepted by the LetterOfCredit deployment this SDK instance is bound to: 32 for V2 and 512 for V3.
Use this value for form counters and client-side validation so users do not compose a tag that the selected deployment ABI cannot encode.
Returns
number
The deployment’s create-LOC tag capacity in bytes.
getCreditedToken()
getCreditedToken(params): Promise<CreditedToken | undefined>;Returns the CreditedToken configuration the LetterOfCredit contract holds for an address — the per-token minimum, per-LOC maximum, and global cap that bound what may be credited.
The contract getter is a mapping read: an ERC-20 that is simply not configured as credited comes
back with zero limits rather than as undefined. undefined means the address did not answer as an
ERC-20 at all, or the read failed.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCreditedTokenParams | The credited token address to look up. |
Returns
Promise<CreditedToken | undefined>
The credited token, with zero limits when the address is not configured, or undefined if it is not
a readable ERC-20.
getCreditedTokens()
getCreditedTokens(params?, options?): Promise<CollectionPage<CreditedToken>>;Returns a page of the credited tokens configured for dynamic LOCs, each with its per-LOC bounds and global cap.
Served from the subgraph, so a subgraph URL is required. enabled and tokenAddressAllowlist
narrow the page; the allowlist comparison is case-insensitive.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetCreditedTokensParams | Optional paging and filters. |
options | ReadExecutionOptions | Cancellation controls for this page read. |
Returns
Promise<CollectionPage<CreditedToken>>
The page of credited tokens.
getLetterOfCredit()
getLetterOfCredit(params, options?): Promise<LetterOfCredit | undefined>;Reads the canonical indexed LOC, including terminal origin and lifecycle facts.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetLetterOfCreditParams | Profile-local LOC identity. |
options | ReadExecutionOptions | Cancellation controls for the complete read. |
Returns
Promise<LetterOfCredit | undefined>
The indexed entity or undefined from a compatible index with no such LOC.
Throws
SubgraphCompatibilityError If the index cannot supply complete canonical facts.
Example
const loc = await sdk.loc.getLetterOfCredit({ id: 42n });if (!loc) return;const now = BigInt(Math.floor(Date.now() / 1000));const status = deriveLetterOfCreditStatus(loc, now);console.log( loc.origin.credited, loc.lifecycle.resolution, status.displayStatus);console.log(loc.indexedAt.deployment, loc.indexedAt.blockHash);// No caller selected: every direct capability is false.const capabilities = deriveLetterOfCreditDirectCapabilities( loc, status, null, null);console.log(capabilities);getLetterOfCreditHistory()
getLetterOfCreditHistory(params, options?): Promise<LetterOfCreditHistoryPage>;Reads complete per-LOC transaction entries with ordered operation and event evidence.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetLetterOfCreditHistoryParams | Profile-local LOC identity and transaction-page controls. |
options | ReadExecutionOptions | Cancellation controls shared by all nested evidence reads. |
Returns
Promise<LetterOfCreditHistoryPage>
Complete history entries under history model version 1.
Throws
SubgraphCompatibilityError If history evidence is unsupported or incomplete.
Example
const id = 42n;let page = await sdk.loc.getLetterOfCreditHistory({ id, limit: 25 });const entries = [...page.items];while (page.nextCursor !== null) { page = await sdk.loc.getLetterOfCreditHistory({ id, limit: 25, cursor: page.nextCursor, }); entries.push(...page.items);}for (const entry of entries) { console.log(entry.transaction.hash, entry.before, entry.after); for (const change of entry.changes) { console.log(change.type, change); for (const event of change.events) { console.log(event.sourceEvent, event.transactionLogIndex); } }}getLetterOfCredits()
getLetterOfCredits(params?, options?): Promise<LetterOfCreditPage>;Lists canonical indexed LOCs, preserving one index boundary across cursor pages.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetLetterOfCreditsParams | Creator, beneficiary, creation-kind and pagination filters. |
options | ReadExecutionOptions | Cancellation controls for the complete page. |
Returns
Promise<LetterOfCreditPage>
A complete page with index provenance and an exact continuation cursor.
Throws
SubgraphCompatibilityError If any row or the index boundary is incompatible.
Example
const creator = await sdk.getSignerAddress();const filters = { creators: [creator], creationKind: 'dynamic' as const, limit: 50,};let page = await sdk.loc.getLetterOfCredits(filters);const locs = [...page.items];while (page.nextCursor !== null) { page = await sdk.loc.getLetterOfCredits({ ...filters, cursor: page.nextCursor, }); locs.push(...page.items);}// Includes resolved LOCs. Every continuation preserves the first index// boundary.console.log(locs.map(loc => [loc.reference, loc.lifecycle.resolution]));getMaxDate()
getMaxDate(): Promise<Date>;Returns the latest expiration a LOC created right now may carry: the current time plus the deployment’s maximum duration, rounded down to the hour.
Like LOCModule.getMinDate it is conservative relative to the contract and it moves with the clock.
Returns
Promise<Date>
The latest expiration date the SDK treats as valid.
getMaxDuration()
getMaxDuration(): Promise<number>;Returns the deployment’s maximum LOC duration, in seconds.
This bounds the window from now to expiration, not the LOC’s total lifetime: extending repeatedly can keep a LOC alive far longer, so long as its remaining duration never exceeds this value at any moment.
Returns
Promise<number>
The maximum duration in seconds.
getMinDate()
getMinDate(): Promise<Date>;Returns the earliest expiration a LOC created right now may carry.
The SDK pads the current time by two hours so a LOC is not already invalid by the time the user signs. The result can therefore sit further out than the contract’s own minimum, and it moves with the clock — read it when you build the form, not once at startup.
Returns
Promise<Date>
The earliest expiration date the SDK treats as valid.
getOutstandingLetterOfCredit()
getOutstandingLetterOfCredit(params): Promise<OutstandingLetterOfCredit | undefined>;Reads current contract-resident state, including expired unresolved LOCs. No subgraph, token metadata, or current risk configuration is required.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetOutstandingLetterOfCreditParams | Profile-local LOC identity. |
Returns
Promise<OutstandingLetterOfCredit | undefined>
Outstanding state, or undefined for an unknown or resolved LOC.
Throws
InvalidArgumentError If the id is outside uint96.
Throws
ConfigError If the connected chain differs from the configured profile.
Example
const loc = await sdk.loc.getOutstandingLetterOfCredit({ id: 42n });if (loc === undefined) { // Fully redeemed or canceled LOCs are purged from contract state. console.log('LOC 42 is no longer outstanding'); return;}const expires = ethereumTimestampToDate(loc.expirationTimestamp);const { amount, tokenAddress } = loc.remainingCredited;console.log( `${amount} ${tokenAddress} owed to ` + `${loc.beneficiary}, expires ${expires.toISOString()}`);getOutstandingLetterOfCredits()
getOutstandingLetterOfCredits(params?, options?): Promise<CollectionPage<OutstandingLetterOfCredit>>;Returns a page of contract-resident OutstandingLetterOfCredits — those neither fully redeemed nor canceled. Expired unresolved LOCs are included; anyone may cancel them.
The subgraph supplies the page, then each LOC is enriched from chain state, so the SDK must have
been configured with a subgraph URL. An entry the subgraph still lists but the chain has purged is
dropped, which means a page can hold fewer than limit results even while nextCursor is non-null
— page until nextCursor is null rather than until a short page appears.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetOutstandingLetterOfCreditsParams | Paging and filter options for the collection. |
options | ReadExecutionOptions | Cancellation controls for this page read. |
Returns
Promise<CollectionPage<OutstandingLetterOfCredit>>
The matching page of LOCs.
Example
// Every outstanding dynamic LOC created by the connected account,// including expired LOCs.const creator = await sdk.getSignerAddress();const filter = { creators: [creator], creationKind: 'dynamic' as const };// Keep execution controls outside the filters. A route or component can// call controller.abort(reason) to stop the in-flight page and any retry// backoff immediately; the SDK also bounds each page with its own// deadline.const controller = new AbortController();let page = await sdk.loc.getOutstandingLetterOfCredits( { ...filter, limit: 50 }, { signal: controller.signal });const locs = [...page.items];while (page.nextCursor !== null) { page = await sdk.loc.getOutstandingLetterOfCredits( { ...filter, limit: 50, cursor: page.nextCursor, }, { signal: controller.signal } ); locs.push(...page.items);}// Pages can be short: LOCs the subgraph still lists but the chain has// purged are dropped.console.log(`${locs.length} outstanding dynamic LOCs`);getRequiredCollateralForDynamic()
getRequiredCollateralForDynamic(params): Promise<bigint | undefined>;Returns a contract-verified collateral estimate for a dynamic LOC, given the collateral/credited token pair and the requested credited amount.
For a positive credited amount, computeRequiredCollateral finds the smallest accepted amount at or above its initial floor estimate. The result can exceed the absolute minimum accepted by the contract.
Returns undefined when no collateral amount can satisfy the pair’s creation collateral factor:
either the pair is unconfigured or disabled (a zero creation factor), or the oracle reports a zero
price. Treat undefined as “this pair cannot back this LOC right now”, not as zero.
For an enabled pair, a price that cannot be fetched causes this call to reject. An unconfigured pair
returns undefined even if its concurrent price read fails; a failed factor read takes precedence
over a price error.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetRequiredCollateralForDynamicLOCParams | The token pair and credited amount to collateralize. |
Returns
Promise<bigint | undefined>
The required collateral, or undefined if there is no valid amount for these parameters.
getRequiredCollateralForStatic()
getRequiredCollateralForStatic(params): Promise<bigint>;Returns the minimum collateral a static LOC needs for the requested credited amount.
A static LOC is collateralized in the credited token itself, so this is close to the face value — but not equal to it: the vault’s withdrawal fee dominates the difference.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | GetRequiredCollateralForStaticLOCParams | The credited token and amount to collateralize. |
Returns
Promise<bigint>
The required collateral, in the token’s smallest units.
validateCancel()
validateCancel(params, signal?): Promise<ValidationIssue[]>;Validates cancellation parameters without touching the wallet: the LOC has outstanding contract state and cancellation is permitted — which means the signer is the beneficiary, or the LOC has expired, or a valid beneficiary authorization was supplied.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<CancelLOCParams> | The partial cancellation parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateCancelAndWithdraw()
validateCancelAndWithdraw(params, signal?): Promise<ValidationIssue[]>;Validates combined cancel-and-withdraw parameters without touching the wallet.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<CancelLOCsAndWithdrawParams> | The partial parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateCancelAuthorization()
validateCancelAuthorization(params, signal?): Promise<ValidationIssue[]>;Validates a cancellation authorization before resolving its nonce.
Parameters
| Parameter | Type |
|---|---|
params | Partial<BuildCancelLOCAuthorizationParams> |
signal? | AbortSignal |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateCancelMany()
validateCancelMany(params, signal?): Promise<ValidationIssue[]>;Validates an atomic batch of LOC cancellations without touching the wallet.
cancelCalls is required: an absent batch reports FieldRequired for it, and an explicitly empty
batch is invalid for a different reason (a separate issue on the same field). Every supplied LOC
must be outstanding and unique. Because Multicall is the caller, every unexpired LOC also needs the
beneficiary’s valid authorization with the expected per-beneficiary nonce. Expired LOCs need no
authorization and consume no nonce.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<CancelLOCsParams> | The partial batch parameters currently available. A missing cancelCalls reports FieldRequired rather than being silently skipped. |
signal? | AbortSignal | Optional cancellation signal for on-chain validation reads. |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateConvert()
validateConvert(params, signal?): Promise<ValidationIssue[]>;Validates a V3 LOC conversion without resolving caller identity. Lifecycle and input-shape failures
are reported immediately. Health is preflighted only when oraclePriceUpdate is null, because an
automatic or supplied update can change the price the contract will use.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<ConvertLOCParams> | The currently available conversion fields. A missing id reports FieldRequired rather than being silently skipped; a missing non-required dependency of another rule still defers that rule. |
signal? | AbortSignal | Optional cancellation signal for on-chain validation reads. |
Returns
Promise<ValidationIssue[]>
Field-scoped validation issues for the supplied values; an empty array means a complete request passed preflight, while the contract remains authoritative for a supplied or automatically fetched price. Never partial — an infrastructure failure rejects instead of resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateCreateDynamic()
validateCreateDynamic(params, signal?): Promise<ValidationIssue[]>;Validates dynamic-LOC creation parameters without touching the wallet: beneficiary format and allowlist, expiration format and bounds, collateral token and amount against the vault balance, credited token validity, tag length, and the freshness of a price update you supply.
Vault allowance is deliberately not checked here. The workflow builder resolves a short allowance on its own, so failing validation on it would block a submission that would have succeeded.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<CreateDynamicLOCParams> | The partial creation parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateCreateStatic()
validateCreateStatic(params, signal?): Promise<ValidationIssue[]>;Validates static-LOC creation parameters without touching the wallet.
Takes a Partial, so it is safe to call on a half-filled form: a missing field that is only a
foreign dependency of a rule defers that rule silently, but a missing field a rule itself requires
reports FieldRequired for it as a domain issue, not a throw. An infrastructure failure still
rejects — see runValidation.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<CreateStaticLOCParams> | The partial creation parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Examples
import { createPublicClient, http } from 'viem';import { sepolia } from 'viem/chains';
import { AnvilSDK, type CreateStaticLOCParams, testnetProfile } from '@anvil/sdk/core';
const sdk = new AnvilSDK({ publicClient: createPublicClient({ chain: sepolia, transport: http('https://your-rpc-url'), }), profile: testnetProfile, // In an app this is the connected user (wagmi's `useAccount().address`). accountAddress: '0x50f91632bd0fb4d4521718e73db3c59ecee69351',});
const params: CreateStaticLOCParams = { beneficiary: '0x3c5bea6f8edba748330ad0c0e19bea6731ac57dd', tokenAmount: { tokenAddress: testnetProfile.contracts.USDC.address, amount: 25_000_000n, // 25 USDC at 6 decimals }, expirationTimestampSeconds: BigInt(Math.floor(Date.now() / 1000) + 86400 * 30),};
// An empty array means the params are valid.const issues = await sdk.loc.validateCreateStatic(params);console.log(issues);import { ContactSupportError } from '@anvil/sdk/core';import { ProviderConnectionError } from '@anvil/sdk/core';
import type { ValidationIssue } from '@anvil/sdk/core';
type ValidationOutcome = | { readonly kind: 'issues'; readonly issues: ValidationIssue[] } | { readonly kind: 'retry' } | { readonly kind: 'unavailable'; readonly cause: unknown };
async function validateOrClassifyFailure(): Promise<ValidationOutcome> { try { const issues = await sdk.loc.validateCreateStatic(params); return { kind: 'issues', issues }; } catch (error) { if (error instanceof ProviderConnectionError) { // Recognizable RPC/subgraph failure -- safe to retry. return { kind: 'retry' }; } if (error instanceof ContactSupportError) { // Unclassifiable failure -- not retryable; surface it as-is. return { kind: 'unavailable', cause: error }; } // A typed SDK error a rule already threw, or caller cancellation -- // propagate unchanged. throw error; }}
console.log(await validateOrClassifyFailure());Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateExtend()
validateExtend(params, signal?): Promise<ValidationIssue[]>;Validates extension parameters without touching the wallet. Creator authorization uses the configured accountAddress or wallet account.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<ExtendLOCParams> | The partial extension parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
WalletNotConnectedError if neither acting-account source is configured.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateModifyCollateral()
validateModifyCollateral(params, signal?): Promise<ValidationIssue[]>;Validates a collateral change on an existing dynamic LOC without touching the wallet: the change is non-zero, the LOC exists and has not expired, and its collateral and credited tokens actually differ. Creator authorization requires the configured accountAddress or wallet account. With no oracle update, projected backing is checked against the current chain price. Automatic or supplied updates defer that price-dependent check to the contract because the new price can change the outcome; an empty issue list in those modes does not guarantee sufficient backing at execution.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<ModifyLOCCollateralParams> | The partial modification parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
WalletNotConnectedError if neither acting-account source is configured.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateRedeem()
validateRedeem(params, signal?): Promise<ValidationIssue[]>;Validates redemption parameters without touching the wallet: each amount is positive and within the LOC’s remaining credited value, the LOC exists and has not expired, and the redemption is authorized.
Authorization is a choice, not a single rule: for one redemption with no beneficiaryAuthorization
the signer must be the beneficiary; supply an authorization, or redeem several LOCs at once, and a
beneficiary signature is verified instead.
Parameters
| Parameter | Type | Description |
|---|---|---|
params | Partial<RedeemLOCsParams> | The partial redemption parameters to check. |
signal? | AbortSignal | - |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Example
// The connected wallet is the LOC's beneficiary.const loc = await sdk.loc.getOutstandingLetterOfCredit({ id: 42n });if (!loc) throw new Error('LOC 42 is no longer outstanding');
// One entry, full remaining face value, paid to the signer.const params: RedeemLOCsParams = { redemptions: [ { id: loc.reference.id, amount: loc.remainingCredited.amount }, ],};
const issues = await sdk.loc.validateRedeem(params);if (issues.length > 0) throw new Error(JSON.stringify(issues));
const workflow = await sdk.loc.buildRedeemWorkflow(params);await workflow.execute();Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.
validateRedeemAuthorization()
validateRedeemAuthorization(params, signal?): Promise<ValidationIssue[]>;Validates a redemption authorization before resolving its nonce.
Parameters
| Parameter | Type |
|---|---|
params | Partial<BuildRedeemLOCAuthorizationParams> |
signal? | AbortSignal |
Returns
Promise<ValidationIssue[]>
The issues found: empty means checked and valid — a rule whose own required field is absent reports
FieldRequired rather than being silently skipped, so empty never means “nothing could run.”
Non-empty means domain-invalid. Never partial — an infrastructure failure rejects instead of
resolving.
Throws
The same failure modes as runValidation: an existing typed SDK error propagates unchanged, a recognizable provider failure becomes ProviderConnectionError, an unclassifiable failure becomes ContactSupportError, and caller abort rejects with the signal’s exact reason.