Off-chain SDK for Smart Handles, offering configuration datatypes with corresponding transaction builders for all possible endpoints.
All endpoints provide 2 functions for 2 targets: single and batch. The script CBOR single targets require are fully applied spending scripts, while batch variants expect the CBOR to be that of a fully applied staking script.
The staking script's CBOR is internally applied to the included generic batch spending script in order to yield the batch spending script of the instance.
Given a LucidEvolution
object and target script, the two offered functions
query UTxOs sitting at their corresponding spending addresses.
Functions for submitting simple and/or advanced requests to the instance:
RouteRequest
is a sum type with 2 constructors, one for the simple requests,
and one for the advanced ones:
export type TSRequiredMint = {
policyId: PolicyId;
tokenName: string;
};
export type SimpleRouteRequest = {
valueToLock: Assets;
};
export type AdvancedRouteRequest = SimpleRouteRequest & {
owner?: Address;
routerFee: bigint;
reclaimRouterFee: bigint;
routeRequiredMint: TSRequiredMint | null;
reclaimRequiredMint: TSRequiredMint | null;
extraInfoDataBuilder: () => DatumJson;
};
export type RouteRequest =
| { kind: "simple"; data: SimpleRouteRequest }
| { kind: "advanced"; data: AdvancedRouteRequest };
Single and batch config datatypes are the following:
export type SingleRequestConfig = {
scriptCBOR: CBORHex;
routeRequest: RouteRequest;
additionalRequiredLovelaces: bigint;
};
export type BatchRequestConfig = {
stakingScriptCBOR: CBORHex;
routeRequests: RouteRequest[];
additionalRequiredLovelaces: bigint;
};
additionalRequiredLovelaces
allow you to provide more safety by preventing
request submissions that can lead to the funds getting permanently locked.
Reclaiming a simple request only requires the signature of its owner.
Advanced reclaim is very similar to an advanced route, and therefore is expanded upon in the next section.
Before going over the configs themselves, let's look at some other common interfaces:
Both of these endpoints need to provide a function that, given input assets and datum, returns the datum that should be attached to the produced UTxO at route address:
export type SimpleOutputDatumMaker = (
inputAssets: Assets,
inputDatum: SimpleDatumFields
) => Promise<Result<OutputDatum>>;
export type AdvancedOutputDatumMaker = (
inputAssets: Assets,
inputDatum: AdvancedDatumFields
) => Promise<Result<OutputDatum>>;
As a route transaction can be consuming a simple datum, route config may be provided with both of these datum makers.
Note the output type, Promise<Result<OutputDatum>>
, allows your function to
be both asynchronous, and fail-able with an error message.
Advanced requests can require mints/burns of a single asset for both route and reclaim. The datatype to model this is:
export type RequiredMintConfig = {
mintQuantityFinder: (
inputAssets: Assets,
inputDatum: AdvancedDatumFields
) => Promise<Result<bigint>>;
mintRedeemer: string;
mintScript: Script;
};
Since the quantity of mint/burn can depend on the spent UTxO, a function similar to the output datum finder has to be provided. The other two should be fairly clear.
The underlying logic of an instance may have some extra requirements that smart handles wrapper does not provide. This function offers instance's off-chain to perform additional actions on a partially built transaction before passing it to be signed (e.g. including witness of an additional staking script):
export type AdditionalAction =
(tx: TxBuilder, utxo: UTxO) => Promise<Result<TxBuilder>>;
export type CommonSingle = {
scriptCBOR: CBORHex;
requestOutRef: OutRef;
};
export type CommonBatch = {
stakingScriptCBOR: CBORHex;
requestOutRefs: OutRef[];
};
With these datatypes out of the way, we can now look at the advanced reclaim and route configs:
export type AdvancedReclaimConfig = {
outputDatumMaker: AdvancedOutputDatumMaker;
requiredMintConfig?: RequiredMintConfig;
additionalAction: AdditionalAction;
};
export type SingleReclaimConfig = CommonSingle & {
advancedReclaimConfig?: AdvancedReclaimConfig;
};
export type BatchReclaimConfig = CommonBatch & {
advancedReclaimConfig?: AdvancedReclaimConfig;
};
First we have route configs for simple and advanced requests:
export type SimpleRouteConfig = {
additionalAction: AdditionalAction;
outputDatumMaker: SimpleOutputDatumMaker;
};
export type AdvancedRouteConfig = {
outputDatumMaker: AdvancedOutputDatumMaker;
requiredMintConfig?: RequiredMintConfig;
additionalAction: AdditionalAction;
};
And use them to define single and batch route configs:
export type SingleRouteConfig = CommonSingle & {
routeAddress: Address;
simpleRouteConfig?: SimpleRouteConfig;
advancedRouteConfig?: AdvancedRouteConfig;
};
export type BatchRouteConfig = CommonBatch & {
routeAddress: Address;
simpleRouteConfig?: SimpleRouteConfig;
advancedRouteConfig?: AdvancedRouteConfig;
};
The example
folder contains a project that not only uses this package, but
also smart-handles-agent
to implement a full off-chain solution for
its on-chain counterpart.
The bulk of the implementation resides at minswap-v1.ts
.
Additionally, there are also two preprod scenarios implemented
at ./example/src/scenarios/
which should give you a more practical usage of
this SDK.
Its sample transactions on preprod are linked in the
example's README.md
.