Skip to content

Commit 5b6a063

Browse files
authored
IDCOM-2089 Close Transaction Instruction (#213)
* feature: close_transaction implementation in program and client.
1 parent 349e707 commit 5b6a063

28 files changed

Lines changed: 607 additions & 170 deletions

File tree

packages/client/cli/src/service/cryptid/index.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
CryptidClient,
99
} from "@identity.com/cryptid";
1010
import { util } from "@identity.com/cryptid-core";
11-
import { ExecuteArrayResult } from "@identity.com/cryptid-core/dist/types/cryptid";
11+
import { TransactionArrayResult } from "@identity.com/cryptid-core/dist/types/cryptid";
1212

1313
const KEY_RESERVE_AIRDROP_LAMPORTS = 500_000;
1414

@@ -109,15 +109,13 @@ export const airdrop = async (
109109
const createCryptidTransaction = async (
110110
cryptid: CryptidClient,
111111
transaction: Transaction
112-
): Promise<ExecuteArrayResult> => {
112+
): Promise<TransactionArrayResult> => {
113113
if (cryptid.details.middlewares.length) {
114114
return cryptid.proposeAndExecute(transaction);
115115
} else {
116116
return {
117-
executeTransactions: await cryptid
118-
.directExecute(transaction)
119-
.then((tx) => [tx]),
120-
executeSigners: [],
117+
transactions: await cryptid.directExecute(transaction).then((tx) => [tx]),
118+
signers: [],
121119
};
122120
}
123121
};
@@ -126,13 +124,15 @@ export const signAndSendCryptidTransaction = async (
126124
cryptid: CryptidClient,
127125
transaction: Transaction
128126
): Promise<string[]> => {
129-
const { executeTransactions, executeSigners } =
130-
await createCryptidTransaction(cryptid, transaction);
131-
return executeTransactions.reduce(
127+
const { transactions, signers } = await createCryptidTransaction(
128+
cryptid,
129+
transaction
130+
);
131+
return transactions.reduce(
132132
(promise, tx) =>
133133
// send the tx strictly after the previous one, and add the signature to the array
134134
promise.then((sigs) =>
135-
cryptid.send(tx, executeSigners).then((sig) => [...sigs, sig])
135+
cryptid.send(tx, signers).then((sig) => [...sigs, sig])
136136
),
137137
Promise.resolve<string[]>([])
138138
);

packages/client/core/src/api/abstractCryptidClient.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { didService } from "../lib/did";
1717
import { CryptidService } from "../service/cryptid";
1818
import { CryptidAccountDetails } from "../lib/CryptidAccountDetails";
1919
import { TransactionAccount, TransactionState } from "../types";
20-
import { ProposalResult, ExecuteArrayResult } from "../types/cryptid";
20+
import { ProposalResult, TransactionArrayResult } from "../types/cryptid";
2121
import { translateError } from "@project-serum/anchor";
2222
import { CryptidTransaction } from "../lib/CryptidTransaction";
2323

@@ -75,7 +75,7 @@ export abstract class AbstractCryptidClient implements CryptidClient {
7575
async proposeAndExecute(
7676
transaction: Transaction,
7777
forceSingleTx = false
78-
): Promise<ExecuteArrayResult> {
78+
): Promise<TransactionArrayResult> {
7979
const service = await this.service();
8080

8181
// TODO this is likely temporary - we should not force the client to have to know whether
@@ -86,8 +86,8 @@ export abstract class AbstractCryptidClient implements CryptidClient {
8686
transaction
8787
);
8888
return {
89-
executeTransactions: [executeResult.executeTransaction],
90-
executeSigners: executeResult.executeSigners,
89+
transactions: [executeResult.transaction],
90+
signers: executeResult.signers,
9191
};
9292
}
9393

@@ -99,14 +99,11 @@ export abstract class AbstractCryptidClient implements CryptidClient {
9999
);
100100

101101
return {
102-
executeTransactions: [
102+
transactions: [
103103
proposalResult.proposeTransaction,
104-
executeResult.executeTransaction,
105-
],
106-
executeSigners: [
107-
...proposalResult.proposeSigners,
108-
...executeResult.executeSigners,
104+
executeResult.transaction,
109105
],
106+
signers: [...proposalResult.proposeSigners, ...executeResult.signers],
110107
};
111108
}
112109

@@ -138,7 +135,7 @@ export abstract class AbstractCryptidClient implements CryptidClient {
138135
async execute(
139136
transactionAccountAddress: PublicKey,
140137
cryptidTransactionRepresentation?: CryptidTransaction
141-
): Promise<ExecuteArrayResult> {
138+
): Promise<TransactionArrayResult> {
142139
return this.service()
143140
.then((service) =>
144141
service.execute(
@@ -148,13 +145,28 @@ export abstract class AbstractCryptidClient implements CryptidClient {
148145
)
149146
)
150147
.then((result) => ({
151-
executeTransactions: [result.executeTransaction],
152-
executeSigners: result.executeSigners,
148+
transactions: [result.transaction],
149+
signers: result.signers,
153150
}));
154151
}
155152

156-
// TODO reinstate
157-
// abstract cancelLarge(transactionAccount: PublicKey): Promise<string>;
153+
async close(
154+
transactionAccountAddress: PublicKey,
155+
cryptidTransactionRepresentation?: CryptidTransaction
156+
): Promise<TransactionArrayResult> {
157+
return this.service()
158+
.then((service) =>
159+
service.close(
160+
this.details,
161+
transactionAccountAddress,
162+
cryptidTransactionRepresentation
163+
)
164+
)
165+
.then((result) => ({
166+
transactions: [result.transaction],
167+
signers: result.signers,
168+
}));
169+
}
158170

159171
/**
160172
* Send a signed transaction, and optionally wait for it to be confirmed.

packages/client/core/src/api/cryptidClient.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Wallet } from "../types/crypto";
1111
import { ProposalResult, TransactionAccount, TransactionState } from "../types";
1212
import { CryptidAccountDetails } from "../lib/CryptidAccountDetails";
1313
import { Middleware } from "../lib/Middleware";
14-
import { ExecuteArrayResult } from "../types/cryptid";
14+
import { TransactionArrayResult } from "../types/cryptid";
1515
import { CryptidTransaction } from "../lib/CryptidTransaction";
1616

1717
export type PayerOption = "DID_PAYS" | "SIGNER_PAYS";
@@ -104,7 +104,7 @@ export interface CryptidClient {
104104
proposeAndExecute(
105105
transaction: Transaction,
106106
forceSingleTx?: boolean
107-
): Promise<ExecuteArrayResult>;
107+
): Promise<TransactionArrayResult>;
108108

109109
/**
110110
* Propose a transaction for execution by cryptid.
@@ -146,10 +146,19 @@ export interface CryptidClient {
146146
execute(
147147
transactionAccountAddress: PublicKey,
148148
cryptidTransactionRepresentation?: CryptidTransaction
149-
): Promise<ExecuteArrayResult>;
149+
): Promise<TransactionArrayResult>;
150150

151-
// TODO Reinstate
152-
// cancelLarge(transactionAccount: PublicKey): Promise<TransactionSignature>;
151+
/**
152+
* Execute a proposed transaction.
153+
*
154+
* The transaction must be in "Ready" state.
155+
* @param transactionAccountAddress
156+
* @param cryptidTransactionRepresentation
157+
*/
158+
close(
159+
transactionAccountAddress: PublicKey,
160+
cryptidTransactionRepresentation?: CryptidTransaction
161+
): Promise<TransactionArrayResult>;
153162

154163
/**
155164
* Retrieves the DID document for this Cryptid account

packages/client/core/src/lib/CryptidTransaction.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,33 @@ export class CryptidTransaction {
360360
.remainingAccounts(this.accountMetas);
361361
}
362362

363+
/**
364+
* Close an existing cryptidTransaction
365+
* @param program
366+
* @param transactionAccountAddress
367+
*/
368+
// TODO move transactionAccountAddress into constructor?
369+
// The anchor MethodsBuilder type is not exposed
370+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
371+
close(program: Program<Cryptid>, transactionAccountAddress: PublicKey) {
372+
return program.methods
373+
.closeTransaction(
374+
this.controllerChainReferences,
375+
this.cryptidAccount.bump,
376+
this.cryptidAccount.index,
377+
this.cryptidAccount.didAccountBump
378+
)
379+
.accounts({
380+
cryptidAccount: this.cryptidAccount.address,
381+
didProgram: DID_SOL_PROGRAM,
382+
did: this.cryptidAccount.didAccount,
383+
authority: this.authority,
384+
destination: this.authority,
385+
transactionAccount: transactionAccountAddress,
386+
})
387+
.remainingAccounts(this.accountMetasOnlyKeys);
388+
}
389+
363390
/**
364391
* Create and directly execute a cryptidTransaction
365392
* @param program

packages/client/core/src/service/cryptid.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { DID_SOL_PROGRAM } from "@identity.com/sol-did-client";
2626
import { MiddlewareRegistry } from "./middlewareRegistry";
2727
import {
2828
ControllerPubkeys,
29-
ExecuteResult,
29+
TransactionResult,
3030
ProposalResult,
3131
} from "../types/cryptid";
3232
import { MiddlewareResult } from "../types/middleware";
@@ -181,7 +181,7 @@ export class CryptidService {
181181
private async executeMiddlewareInstructions(
182182
account: CryptidAccountDetails,
183183
transactionAccount: PublicKey,
184-
stage: "Propose" | "Execute"
184+
stage: "Propose" | "Execute" | "Close"
185185
): Promise<MiddlewareResult> {
186186
const middlewareContexts =
187187
MiddlewareRegistry.get().getMiddlewareContexts(account);
@@ -198,8 +198,13 @@ export class CryptidService {
198198
cryptidAccountDetails: account,
199199
};
200200

201-
// Middlewares may have functions taht are executed on propose stage, on execute stage, or both
202-
const middlewareFn = stage === "Propose" ? "onPropose" : "onExecute";
201+
// Middlewares may have functions interface functions for propose, execute and close.
202+
const middlewareFn =
203+
stage === "Propose"
204+
? "onPropose"
205+
: stage === "Close"
206+
? "onClose"
207+
: "onExecute";
203208

204209
// For each middleware, execute their onPropose or onExecute function
205210
const middlewareExecutions = middlewareContexts.map(
@@ -346,7 +351,7 @@ export class CryptidService {
346351
public async proposeAndExecuteTransaction(
347352
account: CryptidAccountDetails,
348353
transaction: Transaction
349-
): Promise<ExecuteResult> {
354+
): Promise<TransactionResult> {
350355
const transactionAccountKeypair = Keypair.generate();
351356
const cryptidTransaction = CryptidTransaction.fromSolanaInstructions(
352357
account,
@@ -390,8 +395,8 @@ export class CryptidService {
390395
);
391396

392397
return {
393-
executeTransaction,
394-
executeSigners: [
398+
transaction: executeTransaction,
399+
signers: [
395400
transactionAccountKeypair,
396401
...middlewareProposeResults.signers,
397402
...middlewareExecuteResults.signers,
@@ -403,7 +408,7 @@ export class CryptidService {
403408
account: CryptidAccountDetails,
404409
transactionAccountAddress: PublicKey,
405410
cryptidTransaction?: CryptidTransaction
406-
): Promise<ExecuteResult> {
411+
): Promise<TransactionResult> {
407412
const middlewareResult = await this.executeMiddlewareInstructions(
408413
account,
409414
transactionAccountAddress,
@@ -421,8 +426,35 @@ export class CryptidService {
421426
.transaction();
422427

423428
return {
424-
executeTransaction,
425-
executeSigners: [...middlewareResult.signers],
429+
transaction: executeTransaction,
430+
signers: [...middlewareResult.signers],
431+
};
432+
}
433+
434+
public async close(
435+
account: CryptidAccountDetails,
436+
transactionAccountAddress: PublicKey,
437+
cryptidTransaction?: CryptidTransaction
438+
): Promise<TransactionResult> {
439+
const middlewareResult = await this.executeMiddlewareInstructions(
440+
account,
441+
transactionAccountAddress,
442+
"Close"
443+
);
444+
445+
const resolvedCryptidTransaction =
446+
cryptidTransaction ||
447+
(await this.getCryptidTransaction(account, transactionAccountAddress));
448+
449+
const closeTransaction = await resolvedCryptidTransaction
450+
.close(this.program, transactionAccountAddress)
451+
.preInstructions(middlewareResult.instructions)
452+
.signers(middlewareResult.signers)
453+
.transaction();
454+
455+
return {
456+
transaction: closeTransaction,
457+
signers: [...middlewareResult.signers],
426458
};
427459
}
428460

packages/client/core/src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export { ExtendedCluster } from "./types/solana";
55
export { Wallet } from "./types/crypto";
66
export {
77
ProposalResult,
8-
ExecuteResult,
9-
ExecuteArrayResult,
8+
TransactionResult,
9+
TransactionArrayResult,
1010
TransactionState,
1111
} from "./types/cryptid";
1212
export {

packages/client/core/src/types/cryptid.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ export type ProposalResult = {
2424
cryptidTransactionRepresentation: CryptidTransaction;
2525
};
2626

27-
export type ExecuteResult = {
28-
executeTransaction: Transaction;
29-
executeSigners: Signer[];
27+
export type TransactionResult = {
28+
transaction: Transaction;
29+
signers: Signer[];
3030
};
3131

32-
export type ExecuteArrayResult = {
33-
executeTransactions: Transaction[];
34-
executeSigners: Signer[];
32+
export type TransactionArrayResult = {
33+
transactions: Transaction[];
34+
signers: Signer[];
3535
};
3636

3737
export type ControllerAccountMetaInfo = [

packages/client/core/src/types/middleware.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ export interface MiddlewareClient<C extends GenericMiddlewareParams> {
3434
createMiddleware(params: C): Promise<Transaction>;
3535
onPropose(params: ExecuteMiddlewareParams): Promise<MiddlewareResult>;
3636
onExecute(params: ExecuteMiddlewareParams): Promise<MiddlewareResult>;
37+
onClose(params: ExecuteMiddlewareParams): Promise<MiddlewareResult>;
3738
}

0 commit comments

Comments
 (0)