Skip to content

Commit

Permalink
feat(sdk): create builder instance from account key
Browse files Browse the repository at this point in the history
ex: `buildFromVaultTransaction` takes a key for a `VaultTransaction` account, deserializes it, and creates a builder instance from it
  • Loading branch information
joeymeere committed Oct 19, 2024
1 parent 50ca3b7 commit 0bee728
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 43 deletions.
6 changes: 5 additions & 1 deletion sdk/multisig/src/actions/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ type BaseMethodKeys =
| "sendAndConfirm"
| "customSend";

type BaseGetKeys = "getInstructions";
type BaseSendKeys = "send" | "sendAndConfirm" | "customSend";

// TODO: Split between sync and async getters.
type TransactionGetKeys =
| "getIndex"
| "getInstructions"
| "getTransactionKey"
| "getProposalKey"
| "getTransactionAccount"
Expand All @@ -31,7 +32,9 @@ type TransactionActionKeys =
| "withRejection"
| "withExecute";

// TODO: Split between sync and async getters.
type BatchGetKeys =
| "getInstructions"
| "getBatchKey"
| "getBatchTransactionKey"
| "getAllBatchTransactionKeys"
Expand Down Expand Up @@ -61,6 +64,7 @@ type MethodProgression = {
| BaseSendKeys
| TransactionGetKeys;
withExecute: BaseSendKeys | TransactionGetKeys;
reclaimRent: BaseSendKeys | TransactionGetKeys;
// Synchronous Getters
getInstructions: BaseMethodKeys | BaseSendKeys;
getIndex:
Expand Down
7 changes: 7 additions & 0 deletions sdk/multisig/src/actions/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export abstract class BaseBuilder<
protected buildPromise: Promise<void>;
protected args: Omit<U, keyof BaseBuilderArgs>;
private built: boolean = false;
// Use this as an indicator to clear all instructions?
private sent: boolean = false;

constructor(args: U) {
this.connection = args.connection;
Expand Down Expand Up @@ -145,6 +147,8 @@ export abstract class BaseBuilder<
tx,
settings?.options
);
this.sent = true;

return signature;
}

Expand Down Expand Up @@ -202,6 +206,7 @@ export abstract class BaseBuilder<
sent = true;
}
}
this.sent = true;

return signature;
}
Expand Down Expand Up @@ -243,6 +248,8 @@ export abstract class BaseBuilder<
});

const signature = await callback(message);
this.sent = true;

return signature;
}

Expand Down
70 changes: 45 additions & 25 deletions sdk/multisig/src/actions/createConfigTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
createRejectionCore,
} from "./common";
import { Methods } from "./actionTypes";
import { Member } from "../types";

interface CreateConfigTransactionActionArgs {
/** The connection to an SVM network cluster */
Expand Down Expand Up @@ -119,6 +120,21 @@ class ConfigTransactionBuilder extends BaseTransactionBuilder<
this.index = result.index;
}

/**
* Fetches deserialized account data for the corresponding `ConfigTransaction` account after it is built and sent.
*
* @returns `ConfigTransaction`
*/
async getTransactionAccount(key: PublicKey) {
this.ensureBuilt();
const txAccount = await ConfigTransaction.fromAccountAddress(
this.connection,
key
);

return txAccount;
}

/**
* Creates a transaction containing the ConfigTransaction creation instruction.
* @args feePayer - Optional signer to pay the transaction fee.
Expand Down Expand Up @@ -200,6 +216,10 @@ class ConfigTransactionBuilder extends BaseTransactionBuilder<

return this;
}

async reclaimRent() {
// TODO
}
}

export async function isConfigTransaction(
Expand All @@ -215,48 +235,48 @@ export async function isConfigTransaction(
}

export const ConfigActions = {
AddMember: (newMember: PublicKey) => [
AddMember: (newMember: Member) => [
{
__kind: "AddMember",
newMember,
},
],
RemoveMember: (oldMember: PublicKey) => [
{
RemoveMember: (oldMember: PublicKey) => {
return {
__kind: "RemoveMember",
oldMember,
},
],
ChangeThreshold: (newThreshold: number) => [
{
} as ConfigAction;
},
ChangeThreshold: (newThreshold: number) => {
return {
__kind: "ChangeThreshold",
newThreshold,
},
],
SetTimeLock: (newTimeLock: number) => [
{
} as ConfigAction;
},
SetTimeLock: (newTimeLock: number) => {
return {
__kind: "SetTimeLock",
newTimeLock,
},
],
AddSpendingLimit: (spendingLimit: SpendingLimit) => [
{
} as ConfigAction;
},
AddSpendingLimit: (spendingLimit: SpendingLimit) => {
return {
__kind: "AddSpendingLimit",
...spendingLimit,
},
],
RemoveSpendingLimit: (spendingLimit: PublicKey) => [
{
} as ConfigAction;
},
RemoveSpendingLimit: (spendingLimit: PublicKey) => {
return {
__kind: "RemoveSpendingLimit",
spendingLimit,
},
],
SetRentCollector: (rentCollector: PublicKey) => [
{
} as ConfigAction;
},
SetRentCollector: (rentCollector: PublicKey) => {
return {
__kind: "SetRentCollector",
newRentCollector: rentCollector,
},
],
} as ConfigAction;
},
} as const;

export interface SpendingLimit {
Expand Down
1 change: 0 additions & 1 deletion sdk/multisig/src/actions/createTransactionMultiStep.ts

This file was deleted.

129 changes: 120 additions & 9 deletions sdk/multisig/src/actions/createVaultTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ import {
TransactionInstruction,
TransactionMessage,
AddressLookupTableAccount,
Message,
} from "@solana/web3.js";
import { instructions, accounts } from "..";
import { PROGRAM_ID, VaultTransaction } from "../generated";
import {
PROGRAM_ID,
VaultTransaction,
multisigCompiledInstructionBeet,
vaultTransactionMessageBeet,
} from "../generated";
import {
createApprovalCore,
createRejectionCore,
createProposalCore,
BaseTransactionBuilder,
BuildResult,
ProposalResult,
} from "./common";
import { Methods } from "./actionTypes";

Expand All @@ -24,23 +32,23 @@ interface CreateVaultTransactionActionArgs {
creator: PublicKey;
/** Transaction message containing the instructions to execute */
message: TransactionMessage;
/** Index of the vault to target. Defaults to 0 */
/** (Optional) Index of the transaction to build. If omitted, this will be fetched from the multisig account. */
transactionIndex?: number;
/** (Optional) Index of the vault to target. Defaults to 0 */
vaultIndex?: number;
/** Specify a number of ephemeral signers to include.
/** (Optional) Specify a number of ephemeral signers to include.
* Useful if the underlying transaction requires more than one signer.
*/
ephemeralSigners?: number;
/** The public key of the fee payer, defaults to the creator */
/** (Optional) The public key of the fee payer, defaults to the creator */
rentPayer?: PublicKey;
/** Optional memo for indexing purposes */
/** (Optional) UTF-8 Memo for indexing purposes */
memo?: string;
/** Optional program ID (defaults to Solana mainnet-beta/devnet Program ID) */
/** (Optional) Squads Program ID (defaults to Solana mainnet-beta/devnet Program ID) */
programId?: PublicKey;
}

interface CreateVaultTransactionResult {
/** `vaultTransactionCreate` instruction */
instructions: TransactionInstruction[];
interface CreateVaultTransactionResult extends BuildResult {
/** Transaction index of the resulting VaultTransaction */
index: number;
}
Expand All @@ -67,6 +75,19 @@ interface ExecuteVaultTransactionResult {
lookupTableAccounts: AddressLookupTableAccount[];
}

interface ReclaimRentActionArgs {
/** The connection to an SVM network cluster */
connection: Connection;
/** The public key of the multisig config account */
multisig: PublicKey;
/** Transaction index of the VaultTransaction to execute */
index: number;
/** Optional memo for indexing purposes */
memo?: string;
/** Optional program ID (defaults to Solana mainnet-beta/devnet Program ID) */
programId?: PublicKey;
}

/**
* Builds an instruction to create a new VaultTransaction and returns the instruction with the corresponding transaction index.
* Can optionally chain additional methods for transactions, and sending.
Expand Down Expand Up @@ -146,6 +167,21 @@ class VaultTransactionBuilder extends BaseTransactionBuilder<
this.index = result.index;
}

/**
* Fetches deserialized account data for the corresponding `VaultTransaction` account after it is built and sent.
*
* @returns `VaultTransaction`
*/
async getTransactionAccount(key: PublicKey) {
this.ensureBuilt();
const txAccount = await VaultTransaction.fromAccountAddress(
this.connection,
key
);

return txAccount;
}

/**
* Creates a transaction containing the VaultTransaction creation instruction.
* @args feePayer - Optional signer to pay the transaction fee.
Expand Down Expand Up @@ -231,6 +267,56 @@ class VaultTransactionBuilder extends BaseTransactionBuilder<

return this;
}

async reclaimRent(): Promise<
Pick<VaultTransactionBuilder, Methods<"reclaimRent">>
> {
const { instruction } = await reclaimRentCore({
connection: this.connection,
multisig: this.args.multisig,
index: this.index,
programId: this.args.programId,
});

this.instructions.push(instruction);
return this;
}
}

/**
* Creates a transaction builder instance from an existing `VaultTransaction` account key.
* @args `{ connection: Connection, transaction: PublicKey, programId?: PublicKey }`
* @returns `VaultTransactionBuilder`
*/
export async function buildFromVaultTransaction({
connection,
transaction,
programId,
}: {
connection: Connection;
transaction: PublicKey;
programId?: PublicKey;
}) {
const txAccount = await VaultTransaction.fromAccountAddress(
connection,
transaction
);

const compiledMessage = Message.from(
vaultTransactionMessageBeet.serialize(txAccount.message)[0]
);

const message = TransactionMessage.decompile(compiledMessage);

const builder = createVaultTransaction({
connection,
multisig: txAccount.multisig,
creator: txAccount.creator,
message: message,
programId: programId,
});

return builder;
}

export async function isVaultTransaction(
Expand Down Expand Up @@ -300,6 +386,31 @@ async function executeVaultTransactionCore(
};
}

async function reclaimRentCore(
args: ReclaimRentActionArgs
): Promise<ProposalResult> {
const { connection, multisig, index, programId = PROGRAM_ID } = args;
const multisigInfo = await accounts.Multisig.fromAccountAddress(
connection,
multisig
);

if (!multisigInfo.rentCollector) {
throw new Error("No rent collector found in Multisig config.");
}

const ix = instructions.vaultTransactionAccountsClose({
multisigPda: multisig,
rentCollector: multisigInfo.rentCollector,
transactionIndex: BigInt(index),
programId: programId,
});

return {
instruction: ix,
};
}

/*
async function Example() {
const connection = new Connection("https://api.mainnet-beta.solana.com");
Expand Down
11 changes: 9 additions & 2 deletions sdk/multisig/src/actions/members.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PublicKey } from "@solana/web3.js";
import { Permissions } from "../types";
import { Member, Permissions } from "../types";

export enum SquadPermissions {
Proposer = 1,
Expand All @@ -11,6 +11,13 @@ export enum SquadPermissions {
All = 7,
}

export function createMember(member: { key: PublicKey; permissions: number }) {
return {
key: member.key,
permissions: Permissions.fromMask(member.permissions),
} as Member;
}

export function createMembers(
members: { key: PublicKey; permissions: number }[]
) {
Expand All @@ -19,5 +26,5 @@ export function createMembers(
key: member.key,
permissions: Permissions.fromMask(member.permissions),
};
});
}) as Member[];
}
Loading

0 comments on commit 0bee728

Please sign in to comment.