⚓Instruction Factory
The InstructionFactory
component of the SDK is outlined in the interface found here. This component allows the user to craft composable solana instructions that can be executed by wrapping them around a transaction.
Instantiating an InstructionFactory
InstructionFactory
Mainnet
import { clusterApiUrl, Connection } from "@solana/web3.js";
import { InstructionFactoryImpl } from "@seabed-labs/pre-authorized-debit";
// You can use any connection object you'd like, this is just an example
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const ixFactory = InstructionFactoryImpl.mainnet(connection);
Devnet
import { clusterApiUrl, Connection } from "@solana/web3.js";
import { InstructionFactoryImpl } from "@seabed-labs/pre-authorized-debit";
// You can use any connection object you'd like, this is just an example
const connection = new Connection(clusterApiUrl("devnet"));
const ixFactory = InstructionFactoryImpl.devnet(connection);
Custom
Point the instruction factory to a custom deployment on any cluster:
import { clusterApiUrl, Connection } from "@solana/web3.js";
import { InstructionFactoryImpl } from "@seabed-labs/pre-authorized-debit";
const connection = new Connection(/* your connection args */);
const CUSTOM_PAD_PROGRAM_ID = /* your custom program ID */;
const ixFactory = InstructionFactoryImpl.custom(
connection,
CUSTOM_PAD_PROGRAM_ID,
// optionlly, pass in a custom read client
// (if not provided, our read client will be pointed to the custom program)
);
Supported Methods
The instruction factory supports the following methods:
⚠️ NOTE
Not all these instructions map 1:1 to the instructions in the program. For example, pause and unpause are modeled as 2 methods here but utilize the same instruction with different parameters in the program.
Approve smart delegate is an instruction that is sent to the SPL token (or token 2022) program and not the pre-authorized-debit program.
These abstractions will hopefully make the SDK slightly easier to use.
All the methods listed above return the following generic type:
// Return Type
type InstructionWithMetadata<T> = {
instruction: TransactionInstruction; // solana type
expectedSigners: ExpectedSigner[]; // see below
meta: T; // any method-specific metadata such as pubkeys of new accounts, etc.
};
// Inner Types
type ExpectedSigner = {
publicKey: PublicKey; // this pubkey needs to sign for IX to be successful
reason: string; // human-readable reason for why this pubkey needs to sign
};
You can run the instruction returned by the methods by wrapping them around a transaction. For example, consider the buildInitSmartDelegateIx
method (same approach applies to all methods):
import {
Connection,
Message,
VersionedTransaction,
} from "@solana/web3.js";
import { InstructionFactoryImpl } from "@seabed-labs/pre-authorized-debit";
// You can use any connection object you'd like, this is just an example
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const ixFactory = InstructionFactoryImpl.mainnet(connection);
const txPayerKeypair = // load it however;
const accountPayerKeypair = // load it however (can be same as above also);
const ixWithMetadata = await ixFactory.buildInitSmartDelegateIx({
payer: accountPayerKeypair.publicKey,
});
const {
instruction: initSmartDelegateIx,
expectedSigners,
meta: { smartDelegate: smartDelegatePubkey },
} = ixWithMetadata;
const latestBlockhash = await connection.getLatestBlockhash();
const message = Message.compile({
payerKey: txPayerKeypair.publicKey,
instructions: [initSmartDelegateIx],
recentBlockhash: latestBlockhash.blockhash,
});
const tx = new VersionedTransaction(message);
// You can also alternatively use an anchor provider to sign & send the TX
tx.sign([txPayerKeypair, accountPayerKeypair]);
const signature = await connection.sendTransaction(tx);
Build InitSmartDelegate
instruction
InitSmartDelegate
instructionBuilds the init_smart_delegate
instruction. While this is available to SDK users, this instruction only needs to be run once and it will likely be run by us for any instances of the program we deploy.
This instruction will initialize the singleton smart_delegate
account.
const ixWithMetadata = await ixFactory.buildInitSmartDelegateIx({
payer: // the payer pubkey that'll pay for smart_delegate account creation,
});
const {
instruction: initSmartDelegateIx,
expectedSigners,
meta: { smartDelegate: smartDelegatePubkey },
} = ixWithMetadata;
Build InitOneTimePreAuthorization
instruction
InitOneTimePreAuthorization
instructionBuild the init_pre_authorization
instruction with one-time configuration.
This instruction will initialize a pre_authorization
account that represents a one-time pre-authorization.
const ixWithMetadata = await ixFactory.buildInitOneTimePreAuthorizationIx({
// pre-auth generic
payer: // the pubkey paying for pre_authorization account creation,
tokenAccount: // the token account pubkey,
debitAuthority: // the debit authority pubkey,
activation: // a Date instance representing the pre-auth's activation time
// one-time specific
amountAuthorized: // a bigint that represents the one-time pre-auth's amount
expiry: // an optional Date instance representing the pre-auth's expiry time
});
const {
instruction: initOneTimePreAuthIx,
expectedSigners,
meta: { preAuthorization: preAuthPubkey },
} = ixWithMetadata;
Build InitRecurringPreAuthorization
instruction
InitRecurringPreAuthorization
instructionBuild the init_pre_authorization
instruction with recurring configuration.
This instruction will initialize a pre_authorization
account that represents a recurring pre-authorization.
const ixWithMetadata = await ixFactory.buildInitRecurringPreAuthorizationIx({
// pre-auth generic
payer: // the pubkey paying for pre_authorization account creation,
tokenAccount: // the token account pubkey,
debitAuthority: // the debit authority pubkey,
activation: // a Date instance representing the pre-auth's activation time
// recurring specific
repeatFrequencySeconds: // the recurring frequency in seconds (type: bigint),
recurringAmountAuthorized: // amount authorized each cycle (type: bigint),
numCycles: // optional, total cycles this pre-auth will be active (type: bigint),
resetEveryCycle: // if false, the unused "recurringAmountAuthorized" will accrue across cycles (type: boolean)
});
const {
instruction: initRecurringPreAuthIx,
expectedSigners,
meta: { preAuthorization: preAuthPubkey },
} = ixWithMetadata;
Build PausePreAuthorization
instruction
PausePreAuthorization
instructionBuild the update_pause_pre_authorization
instruction with pause = true configuration.
This instruction will pause a pre_authorization
account.
const ix = await ixFactory.buildPausePreAuthorizationIx({
preAuthorization: // the pre-authorization account's pubkey
});
const {
instruction: pausePreAuthIx,
expectedSigners,
// no metadata for this
} = ix;
Build UnpausePreAuthorization
instruction
UnpausePreAuthorization
instructionBuild the update_pause_pre_authorization
instruction with pause = false configuration.
This instruction will un-pause a pre_authorization
account.
const ix = await ixFactory.buildUnpausePreAuthorizationIx({
preAuthorization: // the pre-authorization account's pubkey
});
const {
instruction: unpausePreAuthIx,
expectedSigners,
// no metadata for this
} = ix;
Build ClosePreAuthorizationAsOwner
instruction
ClosePreAuthorizationAsOwner
instructionBuild the close_pre_authorization
instruction with authority as pre-authorization's token account's owner.
This instruction will close a pre_authorization
account.
const ix = await ixFactory.buildClosePreAuthorizationAsOwnerIx({
preAuthorization: // the pre-authorization account's pubkey,
rentReceiver: // optional, the account pubkey that'll receive the lamports in the pre-auth account
});
const {
instruction: closePreAuthAsOwnerIx,
expectedSigners,
// no metadata for this
} = ix;
Build ClosePreAuthorizationAsDebitAuthority
instruction
ClosePreAuthorizationAsDebitAuthority
instructionBuild the close_pre_authorization
instruction with authority as pre-authorization's debit authority.
This instruction will close a pre_authorization
account.
const ix = await ixFactory.buildClosePreAuthorizationAsDebitAuthorityIx({
preAuthorization: // the pre-authorization account's pubkey,
});
const {
instruction: closePreAuthAsDebitAuthorityIx,
expectedSigners,
// no metadata for this
} = ix;
Build Debit
instruction
Debit
instructionBuild the debit
instruction.
This instruction will debit the token account via the pre-authorization if it is valid.
const ix = await ixFactory.buildDebitIx({
preAuthorization: // the pre-authorization account's pubkey,
amount: // bigint, amount to debit,
destinationTokenAccount: // destination token account pubkey to send "amount" to,
checkSmartDelegateEnabled: // optional boolean, checks delegate is smart delegate and fails otherwise
});
const {
instruction: debitIx,
expectedSigners,
// no metadata for this
} = ix;
Build ApproveSmartDelegate
instruction
ApproveSmartDelegate
instructionBuild the approve
instruction (on SPL Token Program or on SPL Token2022 Program) for a given token account to set it's delegate to the smart_delegate
account and delegated amount to u64::MAX
.
When a pre-authorization account is created, the delegate of the token account is already configured as the smart_delegate
and the delegated amount is set to u64::MAX
. This instruction can be used to "reset" this delegation so that the token account's associated pre-authorizations are still valid if it was unset by another Dapp. Alternatively, if the delegated amount isn't sufficient anymore, this same method can be used to reset it back to u64::MAX
.
⚠️ NOTE
This instruction does not call our pre-authorized-debit-v1 program, it calls the SPL Token/Token2022 program directly.
const ix = await ixFactory.buildApproveSmartDelegateIx({
tokenAccount: // token account pubkey,
});
const {
instruction: approveSmartDelegateIx,
expectedSigners,
// no metadata for this
} = ix;
Last updated