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
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 exampleconstconnection=newConnection(clusterApiUrl("mainnet-beta"));constixFactory=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 exampleconstconnection=newConnection(clusterApiUrl("devnet"));constixFactory=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";constconnection=newConnection(/* your connection args */);constCUSTOM_PAD_PROGRAM_ID=/* your custom program ID */;constixFactory=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:
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 TypetypeInstructionWithMetadata<T> = { instruction:TransactionInstruction; // solana type expectedSigners:ExpectedSigner[]; // see below meta:T; // any method-specific metadata such as pubkeys of new accounts, etc.};// Inner TypestypeExpectedSigner= { 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 exampleconstconnection=newConnection(clusterApiUrl("mainnet-beta"));constixFactory=InstructionFactoryImpl.mainnet(connection);consttxPayerKeypair=// load it however;constaccountPayerKeypair=// load it however (can be same as above also);constixWithMetadata=awaitixFactory.buildInitSmartDelegateIx({ payer:accountPayerKeypair.publicKey,});const { instruction: initSmartDelegateIx,expectedSigners, meta: { smartDelegate: smartDelegatePubkey },} = ixWithMetadata;constlatestBlockhash=awaitconnection.getLatestBlockhash();constmessage=Message.compile({ payerKey:txPayerKeypair.publicKey, instructions: [initSmartDelegateIx], recentBlockhash:latestBlockhash.blockhash,});consttx=newVersionedTransaction(message);// You can also alternatively use an anchor provider to sign & send the TXtx.sign([txPayerKeypair, accountPayerKeypair]);constsignature=awaitconnection.sendTransaction(tx);
Build InitSmartDelegate instruction
Builds 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 a pre_authorization account that represents a one-time pre-authorization.
constixWithMetadata=awaitixFactory.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;
This instruction will initialize a pre_authorization account that represents a recurring pre-authorization.
constixWithMetadata=awaitixFactory.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;
constix=awaitixFactory.buildPausePreAuthorizationIx({ preAuthorization:// the pre-authorization account's pubkey});const { instruction: pausePreAuthIx,expectedSigners,// no metadata for this} = ix;
constix=awaitixFactory.buildUnpausePreAuthorizationIx({ preAuthorization:// the pre-authorization account's pubkey});const { instruction: unpausePreAuthIx,expectedSigners,// no metadata for this} = ix;
constix=awaitixFactory.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;
constix=awaitixFactory.buildClosePreAuthorizationAsDebitAuthorityIx({ preAuthorization:// the pre-authorization account's pubkey,});const { instruction: closePreAuthAsDebitAuthorityIx,expectedSigners,// no metadata for this} = ix;
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.