Skip to content

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

ParameterTypeDescription
paramsCancelLOCsAndWithdrawParamsThe 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

ParameterType
paramsBuildCancelLOCAuthorizationParams

Returns

Promise<CancelLOCAuthorizationDefinition>


buildCancelAuthorizationWorkflow()

buildCancelAuthorizationWorkflow(params): Promise<WalletOperationWorkflow>;

Builds a one-step workflow that signs a cancellation authorization.

Parameters

ParameterType
paramsBuildCancelLOCAuthorizationParams

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

ParameterTypeDescription
paramsCancelLOCsParamsThe 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

ParameterTypeDescription
paramsCancelLOCParamsThe 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

ParameterTypeDescription
paramsConvertLOCParamsThe 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

ParameterTypeDescription
paramsCreateDynamicLOCParamsThe 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

ParameterTypeDescription
paramsCreateStaticLOCParamsThe 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

ParameterTypeDescription
paramsExtendLOCParamsThe 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

ParameterTypeDescription
paramsModifyLOCCollateralParamsThe 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

ParameterType
paramsBuildRedeemLOCAuthorizationParams

Returns

Promise<RedeemLOCAuthorizationDefinition>


buildRedeemAuthorizationWorkflow()

buildRedeemAuthorizationWorkflow(params): Promise<WalletOperationWorkflow>;

Builds a one-step workflow that signs a redemption authorization.

Parameters

ParameterType
paramsBuildRedeemLOCAuthorizationParams

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

ParameterTypeDescription
paramsRedeemLOCsParamsThe 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

ParameterTypeDescription
paramsGetCollateralFactorParamsThe 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

ParameterTypeDescription
paramsGetCollateralTokenParamsThe 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

ParameterTypeDescription
paramsGetCollateralTokensParamsPaging and filter options for the collection.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetCollateralTokensForCreditedTokenParamsThe credited token to find eligible collateral for, plus paging options.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetCreditedTokenParamsThe 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

ParameterTypeDescription
paramsGetCreditedTokensParamsOptional paging and filters.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetLetterOfCreditParamsProfile-local LOC identity.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetLetterOfCreditHistoryParamsProfile-local LOC identity and transaction-page controls.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetLetterOfCreditsParamsCreator, beneficiary, creation-kind and pagination filters.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetOutstandingLetterOfCreditParamsProfile-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

ParameterTypeDescription
paramsGetOutstandingLetterOfCreditsParamsPaging and filter options for the collection.
optionsReadExecutionOptionsCancellation 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

ParameterTypeDescription
paramsGetRequiredCollateralForDynamicLOCParamsThe 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

ParameterTypeDescription
paramsGetRequiredCollateralForStaticLOCParamsThe 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

ParameterTypeDescription
paramsPartial<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

ParameterTypeDescription
paramsPartial<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

ParameterType
paramsPartial<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

ParameterTypeDescription
paramsPartial<CancelLOCsParams>The partial batch parameters currently available. A missing cancelCalls reports FieldRequired rather than being silently skipped.
signal?AbortSignalOptional 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

ParameterTypeDescription
paramsPartial<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?AbortSignalOptional 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

ParameterTypeDescription
paramsPartial<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

ParameterTypeDescription
paramsPartial<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

ParameterTypeDescription
paramsPartial<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

ParameterTypeDescription
paramsPartial<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

ParameterTypeDescription
paramsPartial<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

ParameterType
paramsPartial<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.