Skip to content

Commit f8d6131

Browse files
authored
Add ClientWithIdentity plugin interface (#1530)
This PR adds a new `ClientWithIdentity` interface to `@solana/plugin-interfaces` alongside the existing `ClientWithPayer`, exposing `client.identity` as a `TransactionSigner` representing the wallet that owns things in the application — such as the authority over accounts, tokens, or other on-chain assets owned by the current user. Whereas `ClientWithPayer` describes the signer responsible for paying transaction fees and storage costs, `ClientWithIdentity` describes the signer whose assets the app is acting upon. In many apps these refer to the same wallet, but they can differ — for instance, when a service funds transactions on behalf of a user.
1 parent 667a0f0 commit f8d6131

6 files changed

Lines changed: 78 additions & 1 deletion

File tree

.changeset/tidy-wasps-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@solana/plugin-interfaces': minor
3+
---
4+
5+
Add `ClientWithIdentity` interface for clients that provide a default identity signer. Whereas `ClientWithPayer` describes the signer responsible for paying transaction fees and storage costs, `ClientWithIdentity` describes the signer whose assets the application is acting upon — such as the authority over accounts, tokens, or other on-chain assets owned by the current user. In many apps the payer and identity refer to the same signer, but they can differ when a service pays fees on behalf of a user.

packages/plugin-interfaces/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ function memoPlugin() {
5252
}
5353
```
5454

55+
### `ClientWithIdentity`
56+
57+
Represents a client that provides a default identity signer — the wallet that owns things in the application, such as the authority over accounts, tokens, or other on-chain assets owned by the current user. Unlike `ClientWithPayer`, which describes the signer responsible for paying transaction fees and storage costs, the identity describes the signer whose assets the application is acting upon. In many apps, the payer and identity refer to the same signer, but they can differ — for example, when a service pays fees on behalf of a user.
58+
59+
```ts
60+
import { extendClient } from '@solana/plugin-core';
61+
import { ClientWithIdentity } from '@solana/plugin-interfaces';
62+
63+
function nftPlugin() {
64+
return <T extends ClientWithIdentity>(client: T) =>
65+
extendClient(client, {
66+
transferNft: (mint: Address, recipient: Address) => {
67+
// Use client.identity as the current owner of the NFT
68+
const owner = client.identity;
69+
// ...
70+
},
71+
});
72+
}
73+
```
74+
5575
### `ClientWithAirdrop`
5676

5777
Represents a client that can request SOL airdrops (typically on devnet/testnet). The airdrop succeeds when the promise resolves. Some implementations (e.g., LiteSVM) update balances directly without a transaction, so no signature is returned in those cases.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { TransactionSigner } from '@solana/signers';
2+
3+
import type { ClientWithIdentity } from '../identity';
4+
5+
// [DESCRIBE] ClientWithIdentity.
6+
{
7+
// It provides an identity property that is a TransactionSigner.
8+
{
9+
const client = null as unknown as ClientWithIdentity;
10+
client.identity satisfies TransactionSigner;
11+
}
12+
13+
// It can be combined with other interfaces via intersection.
14+
{
15+
type CustomClient = ClientWithIdentity & { customMethod(): void };
16+
const client = null as unknown as CustomClient;
17+
client.identity satisfies TransactionSigner;
18+
client.customMethod satisfies () => void;
19+
}
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { TransactionSigner } from '@solana/signers';
2+
3+
/**
4+
* Represents a client that provides a default identity signer.
5+
*
6+
* The identity is a {@link TransactionSigner} representing the wallet that owns
7+
* things in the application — for instance, the authority over accounts, tokens,
8+
* or other on-chain assets owned by the current user. Unlike {@link ClientWithPayer},
9+
* which describes the signer responsible for paying transaction fees and storage
10+
* costs, the identity describes the signer whose assets the application is acting
11+
* upon. In many apps, the payer and identity refer to the same signer, but they
12+
* can differ — for example, when a service pays fees on behalf of a user.
13+
*
14+
* @example
15+
* ```ts
16+
* function getOwnerAddress(client: ClientWithIdentity): Address {
17+
* return client.identity.address;
18+
* }
19+
* ```
20+
*
21+
* @see {@link ClientWithPayer}
22+
*/
23+
export type ClientWithIdentity = { identity: TransactionSigner };

packages/plugin-interfaces/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
export * from './airdrop';
99
export * from './instruction-plans';
1010
export * from './get-minimum-balance';
11+
export * from './identity';
1112
export * from './payer';
1213
export * from './rpc';

packages/plugin-interfaces/src/payer.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { TransactionSigner } from '@solana/signers';
55
*
66
* The payer is a {@link TransactionSigner} used to sign and pay for transactions.
77
* Clients implementing this interface can automatically fund transactions
8-
* without requiring callers to specify a fee payer explicitly.
8+
* without requiring callers to specify a fee payer explicitly. Unlike
9+
* {@link ClientWithIdentity}, which describes the signer whose assets the
10+
* application is acting upon, the payer describes the signer responsible for
11+
* paying transaction fees as well as storage costs — i.e. the minimum balance
12+
* required to keep newly created accounts alive based on their size.
13+
* In many apps the payer and identity refer to the same signer, but they can
14+
* differ — for example, when a service pays fees on behalf of a user.
915
*
1016
* @example
1117
* ```ts
@@ -14,5 +20,7 @@ import { TransactionSigner } from '@solana/signers';
1420
* // Use feePayer.address as the transaction fee payer
1521
* }
1622
* ```
23+
*
24+
* @see {@link ClientWithIdentity}
1725
*/
1826
export type ClientWithPayer = { payer: TransactionSigner };

0 commit comments

Comments
 (0)