@alchemy/wallet-apis 5.0.0-beta.28 → 5.0.0-beta.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/actions/prepareCalls.d.ts +8 -3
- package/dist/esm/actions/prepareCalls.js +8 -0
- package/dist/esm/actions/prepareCalls.js.map +1 -1
- package/dist/esm/actions/requestAccount.d.ts +4 -3
- package/dist/esm/actions/requestAccount.js.map +1 -1
- package/dist/esm/actions/sendPreparedCalls.d.ts +3 -1
- package/dist/esm/actions/sendPreparedCalls.js.map +1 -1
- package/dist/esm/actions/signPreparedCalls.js +1 -1
- package/dist/esm/actions/signPreparedCalls.js.map +1 -1
- package/dist/esm/actions/signSignatureRequest.d.ts +4 -4
- package/dist/esm/actions/signSignatureRequest.js +1 -1
- package/dist/esm/actions/signSignatureRequest.js.map +1 -1
- package/dist/esm/actions/signTypedData.d.ts +14 -4
- package/dist/esm/actions/signTypedData.js.map +1 -1
- package/dist/esm/actions/solana/getCallsStatus.d.ts +24 -0
- package/dist/esm/actions/solana/getCallsStatus.js +44 -0
- package/dist/esm/actions/solana/getCallsStatus.js.map +1 -0
- package/dist/esm/actions/solana/prepareCalls.d.ts +46 -0
- package/dist/esm/actions/solana/prepareCalls.js +70 -0
- package/dist/esm/actions/solana/prepareCalls.js.map +1 -0
- package/dist/esm/actions/solana/sendCalls.d.ts +34 -0
- package/dist/esm/actions/solana/sendCalls.js +44 -0
- package/dist/esm/actions/solana/sendCalls.js.map +1 -0
- package/dist/esm/actions/solana/sendPreparedCalls.d.ts +24 -0
- package/dist/esm/actions/solana/sendPreparedCalls.js +32 -0
- package/dist/esm/actions/solana/sendPreparedCalls.js.map +1 -0
- package/dist/esm/actions/solana/signPreparedCalls.d.ts +23 -0
- package/dist/esm/actions/solana/signPreparedCalls.js +24 -0
- package/dist/esm/actions/solana/signPreparedCalls.js.map +1 -0
- package/dist/esm/actions/solana/signSignatureRequest.d.ts +10 -0
- package/dist/esm/actions/solana/signSignatureRequest.js +33 -0
- package/dist/esm/actions/solana/signSignatureRequest.js.map +1 -0
- package/dist/esm/actions/solana/waitForCallsStatus.d.ts +24 -0
- package/dist/esm/actions/solana/waitForCallsStatus.js +46 -0
- package/dist/esm/actions/solana/waitForCallsStatus.js.map +1 -0
- package/dist/esm/adapters/SolanaSignerError.d.ts +4 -0
- package/dist/esm/adapters/SolanaSignerError.js +13 -0
- package/dist/esm/adapters/SolanaSignerError.js.map +1 -0
- package/dist/esm/adapters/fromKeypair.d.ts +21 -0
- package/dist/esm/adapters/fromKeypair.js +46 -0
- package/dist/esm/adapters/fromKeypair.js.map +1 -0
- package/dist/esm/adapters/fromKitSigner.d.ts +21 -0
- package/dist/esm/adapters/fromKitSigner.js +61 -0
- package/dist/esm/adapters/fromKitSigner.js.map +1 -0
- package/dist/esm/adapters/fromWalletAdapter.d.ts +26 -0
- package/dist/esm/adapters/fromWalletAdapter.js +45 -0
- package/dist/esm/adapters/fromWalletAdapter.js.map +1 -0
- package/dist/esm/adapters/fromWalletStandard.d.ts +31 -0
- package/dist/esm/adapters/fromWalletStandard.js +53 -0
- package/dist/esm/adapters/fromWalletStandard.js.map +1 -0
- package/dist/esm/adapters/resolveSignerSlot.d.ts +10 -0
- package/dist/esm/adapters/resolveSignerSlot.js +39 -0
- package/dist/esm/adapters/resolveSignerSlot.js.map +1 -0
- package/dist/esm/client.d.ts +29 -14
- package/dist/esm/client.js +63 -19
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/decorators/smartWalletActions.js +2 -2
- package/dist/esm/decorators/smartWalletActions.js.map +1 -1
- package/dist/esm/decorators/solanaSmartWalletActions.d.ts +16 -0
- package/dist/esm/decorators/solanaSmartWalletActions.js +16 -0
- package/dist/esm/decorators/solanaSmartWalletActions.js.map +1 -0
- package/dist/esm/experimental/actions/requestQuoteV0.d.ts +34 -7
- package/dist/esm/experimental/actions/requestQuoteV0.js.map +1 -1
- package/dist/esm/exports/index.d.ts +3 -3
- package/dist/esm/exports/index.js.map +1 -1
- package/dist/esm/exports/solana.d.ts +24 -0
- package/dist/esm/exports/solana.js +15 -0
- package/dist/esm/exports/solana.js.map +1 -0
- package/dist/esm/types.d.ts +28 -4
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/assertions.d.ts +4 -2
- package/dist/esm/utils/assertions.js +6 -0
- package/dist/esm/utils/assertions.js.map +1 -1
- package/dist/esm/utils/capabilities.d.ts +22 -6
- package/dist/esm/utils/capabilities.js +19 -2
- package/dist/esm/utils/capabilities.js.map +1 -1
- package/dist/esm/utils/format.js +1 -1
- package/dist/esm/utils/format.js.map +1 -1
- package/dist/esm/utils/schema.d.ts +14 -14
- package/dist/esm/utils/schema.js +35 -39
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/types/actions/prepareCalls.d.ts +8 -3
- package/dist/types/actions/prepareCalls.d.ts.map +1 -1
- package/dist/types/actions/requestAccount.d.ts +4 -3
- package/dist/types/actions/requestAccount.d.ts.map +1 -1
- package/dist/types/actions/sendPreparedCalls.d.ts +3 -1
- package/dist/types/actions/sendPreparedCalls.d.ts.map +1 -1
- package/dist/types/actions/signSignatureRequest.d.ts +4 -4
- package/dist/types/actions/signSignatureRequest.d.ts.map +1 -1
- package/dist/types/actions/signTypedData.d.ts +14 -4
- package/dist/types/actions/signTypedData.d.ts.map +1 -1
- package/dist/types/actions/solana/getCallsStatus.d.ts +25 -0
- package/dist/types/actions/solana/getCallsStatus.d.ts.map +1 -0
- package/dist/types/actions/solana/prepareCalls.d.ts +47 -0
- package/dist/types/actions/solana/prepareCalls.d.ts.map +1 -0
- package/dist/types/actions/solana/sendCalls.d.ts +35 -0
- package/dist/types/actions/solana/sendCalls.d.ts.map +1 -0
- package/dist/types/actions/solana/sendPreparedCalls.d.ts +25 -0
- package/dist/types/actions/solana/sendPreparedCalls.d.ts.map +1 -0
- package/dist/types/actions/solana/signPreparedCalls.d.ts +24 -0
- package/dist/types/actions/solana/signPreparedCalls.d.ts.map +1 -0
- package/dist/types/actions/solana/signSignatureRequest.d.ts +11 -0
- package/dist/types/actions/solana/signSignatureRequest.d.ts.map +1 -0
- package/dist/types/actions/solana/waitForCallsStatus.d.ts +25 -0
- package/dist/types/actions/solana/waitForCallsStatus.d.ts.map +1 -0
- package/dist/types/adapters/SolanaSignerError.d.ts +5 -0
- package/dist/types/adapters/SolanaSignerError.d.ts.map +1 -0
- package/dist/types/adapters/fromKeypair.d.ts +22 -0
- package/dist/types/adapters/fromKeypair.d.ts.map +1 -0
- package/dist/types/adapters/fromKitSigner.d.ts +22 -0
- package/dist/types/adapters/fromKitSigner.d.ts.map +1 -0
- package/dist/types/adapters/fromWalletAdapter.d.ts +27 -0
- package/dist/types/adapters/fromWalletAdapter.d.ts.map +1 -0
- package/dist/types/adapters/fromWalletStandard.d.ts +32 -0
- package/dist/types/adapters/fromWalletStandard.d.ts.map +1 -0
- package/dist/types/adapters/resolveSignerSlot.d.ts +11 -0
- package/dist/types/adapters/resolveSignerSlot.d.ts.map +1 -0
- package/dist/types/client.d.ts +29 -14
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/decorators/solanaSmartWalletActions.d.ts +17 -0
- package/dist/types/decorators/solanaSmartWalletActions.d.ts.map +1 -0
- package/dist/types/experimental/actions/requestQuoteV0.d.ts +34 -7
- package/dist/types/experimental/actions/requestQuoteV0.d.ts.map +1 -1
- package/dist/types/exports/index.d.ts +3 -3
- package/dist/types/exports/index.d.ts.map +1 -1
- package/dist/types/exports/solana.d.ts +25 -0
- package/dist/types/exports/solana.d.ts.map +1 -0
- package/dist/types/types.d.ts +28 -4
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/assertions.d.ts +4 -2
- package/dist/types/utils/assertions.d.ts.map +1 -1
- package/dist/types/utils/capabilities.d.ts +22 -6
- package/dist/types/utils/capabilities.d.ts.map +1 -1
- package/dist/types/utils/schema.d.ts +14 -14
- package/dist/types/utils/schema.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +26 -9
- package/src/actions/prepareCalls.ts +21 -3
- package/src/actions/requestAccount.ts +7 -5
- package/src/actions/sendPreparedCalls.ts +4 -1
- package/src/actions/signPreparedCalls.ts +1 -1
- package/src/actions/signSignatureRequest.ts +8 -8
- package/src/actions/signTypedData.ts +15 -12
- package/src/actions/solana/getCallsStatus.ts +79 -0
- package/src/actions/solana/prepareCalls.ts +120 -0
- package/src/actions/solana/sendCalls.ts +66 -0
- package/src/actions/solana/sendPreparedCalls.ts +65 -0
- package/src/actions/solana/signPreparedCalls.ts +50 -0
- package/src/actions/solana/signSignatureRequest.ts +63 -0
- package/src/actions/solana/waitForCallsStatus.ts +84 -0
- package/src/adapters/SolanaSignerError.ts +5 -0
- package/src/adapters/fromKeypair.ts +58 -0
- package/src/adapters/fromKitSigner.ts +82 -0
- package/src/adapters/fromWalletAdapter.ts +58 -0
- package/src/adapters/fromWalletStandard.ts +100 -0
- package/src/adapters/resolveSignerSlot.ts +46 -0
- package/src/client.ts +131 -18
- package/src/decorators/smartWalletActions.ts +2 -2
- package/src/decorators/solanaSmartWalletActions.ts +62 -0
- package/src/experimental/actions/requestQuoteV0.ts +26 -11
- package/src/exports/index.ts +8 -4
- package/src/exports/solana.ts +36 -0
- package/src/types.ts +38 -7
- package/src/utils/assertions.ts +17 -2
- package/src/utils/capabilities.ts +40 -8
- package/src/utils/format.ts +1 -1
- package/src/utils/schema.ts +58 -69
- package/src/version.ts +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Prettify } from "viem";
|
|
2
|
+
import type {
|
|
3
|
+
DistributiveOmit,
|
|
4
|
+
InnerSolanaWalletApiClient,
|
|
5
|
+
} from "../../types.js";
|
|
6
|
+
import { isSolanaChain } from "../../utils/assertions.js";
|
|
7
|
+
import type { SolanaChainId } from "@alchemy/wallet-api-types";
|
|
8
|
+
import { SolanaPrepareCallsParams as SolanaPrepareCallsSchema } from "@alchemy/wallet-api-types";
|
|
9
|
+
import { wallet_prepareCalls as MethodSchema } from "@alchemy/wallet-api-types/rpc";
|
|
10
|
+
import type { z } from "zod";
|
|
11
|
+
import { BaseError } from "@alchemy/common";
|
|
12
|
+
import { LOGGER } from "../../logger.js";
|
|
13
|
+
import {
|
|
14
|
+
mergeSolanaClientCapabilities,
|
|
15
|
+
type WithCapabilities,
|
|
16
|
+
} from "../../utils/capabilities.js";
|
|
17
|
+
import {
|
|
18
|
+
methodSchema,
|
|
19
|
+
encode,
|
|
20
|
+
decode,
|
|
21
|
+
type MethodResponse,
|
|
22
|
+
} from "../../utils/schema.js";
|
|
23
|
+
|
|
24
|
+
const schema = methodSchema(MethodSchema);
|
|
25
|
+
type PrepareCallsResponse = MethodResponse<typeof MethodSchema>;
|
|
26
|
+
|
|
27
|
+
type BaseSolanaPrepareCallsParams = z.output<typeof SolanaPrepareCallsSchema>;
|
|
28
|
+
|
|
29
|
+
export type SolanaPrepareCallsParams = Prettify<
|
|
30
|
+
WithCapabilities<
|
|
31
|
+
DistributiveOmit<BaseSolanaPrepareCallsParams, "from" | "chainId"> & {
|
|
32
|
+
account?: string;
|
|
33
|
+
chainId?: SolanaChainId;
|
|
34
|
+
}
|
|
35
|
+
>
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
type SolanaPrepareCallsResponse = Extract<
|
|
39
|
+
PrepareCallsResponse,
|
|
40
|
+
{ type: "solana-transaction-v0" }
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
function isSolanaResponse(
|
|
44
|
+
response: PrepareCallsResponse,
|
|
45
|
+
): response is SolanaPrepareCallsResponse {
|
|
46
|
+
return response.type === "solana-transaction-v0";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type SolanaPrepareCallsResult = SolanaPrepareCallsResponse;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prepares Solana instructions for execution by building a versioned transaction.
|
|
53
|
+
* Returns the compiled transaction and a signature request that needs to be signed
|
|
54
|
+
* before submitting to sendPreparedCalls.
|
|
55
|
+
*
|
|
56
|
+
* @param {InnerSolanaWalletApiClient} client - The Solana wallet API client
|
|
57
|
+
* @param {SolanaPrepareCallsParams} params - Parameters for preparing Solana calls
|
|
58
|
+
* @param {Array<{programId: string, accounts?: Array, data: Hex}>} params.calls - Array of Solana instructions
|
|
59
|
+
* @param {string} [params.account] - The Solana address to execute from. Defaults to the signer's address.
|
|
60
|
+
* @param {SolanaChainId} [params.chainId] - The Solana chain ID. Defaults to the client's chain.
|
|
61
|
+
* @param {object} [params.capabilities] - Optional capabilities (e.g. paymaster sponsorship)
|
|
62
|
+
* @returns {Promise<SolanaPrepareCallsResult>} The prepared Solana transaction with signature request
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const result = await client.prepareCalls({
|
|
67
|
+
* calls: [{
|
|
68
|
+
* programId: "11111111111111111111111111111111",
|
|
69
|
+
* data: "0x...",
|
|
70
|
+
* }],
|
|
71
|
+
* capabilities: {
|
|
72
|
+
* paymaster: { policyId: "your-policy-id" }
|
|
73
|
+
* }
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export async function prepareCalls(
|
|
78
|
+
client: InnerSolanaWalletApiClient,
|
|
79
|
+
params: SolanaPrepareCallsParams,
|
|
80
|
+
): Promise<SolanaPrepareCallsResult> {
|
|
81
|
+
const { account, chainId, capabilities: caps, ...rest } = params;
|
|
82
|
+
const from = account ?? client.solanaAccount;
|
|
83
|
+
if (!isSolanaChain(client.chain)) {
|
|
84
|
+
throw new BaseError("Expected a Solana chain on the client");
|
|
85
|
+
}
|
|
86
|
+
const resolvedChainId = chainId ?? client.chain.solanaChainId;
|
|
87
|
+
|
|
88
|
+
const merged = mergeSolanaClientCapabilities(client, caps);
|
|
89
|
+
const capabilities = merged?.paymaster
|
|
90
|
+
? { paymasterService: merged.paymaster }
|
|
91
|
+
: undefined;
|
|
92
|
+
|
|
93
|
+
LOGGER.debug("solana:prepareCalls:start", {
|
|
94
|
+
callsCount: params.calls?.length,
|
|
95
|
+
hasCapabilities: !!caps,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const rpcParams = encode(SolanaPrepareCallsSchema, {
|
|
99
|
+
...rest,
|
|
100
|
+
chainId: resolvedChainId,
|
|
101
|
+
from,
|
|
102
|
+
capabilities,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const rpcResp = await client.request({
|
|
106
|
+
method: "wallet_prepareCalls",
|
|
107
|
+
params: [rpcParams],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
LOGGER.debug("solana:prepareCalls:done");
|
|
111
|
+
const decoded = decode(schema.response, rpcResp);
|
|
112
|
+
|
|
113
|
+
if (!isSolanaResponse(decoded)) {
|
|
114
|
+
throw new BaseError(
|
|
115
|
+
`Unexpected EVM response from Solana prepareCalls: ${decoded.type}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return decoded;
|
|
120
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Prettify } from "viem";
|
|
2
|
+
import type {
|
|
3
|
+
DistributiveOmit,
|
|
4
|
+
InnerSolanaWalletApiClient,
|
|
5
|
+
} from "../../types.js";
|
|
6
|
+
import type { SolanaChainId } from "@alchemy/wallet-api-types";
|
|
7
|
+
import { prepareCalls, type SolanaPrepareCallsParams } from "./prepareCalls.js";
|
|
8
|
+
import { signPreparedCalls } from "./signPreparedCalls.js";
|
|
9
|
+
import {
|
|
10
|
+
sendPreparedCalls,
|
|
11
|
+
type SolanaSendPreparedCallsResult,
|
|
12
|
+
} from "./sendPreparedCalls.js";
|
|
13
|
+
import { LOGGER } from "../../logger.js";
|
|
14
|
+
|
|
15
|
+
export type SolanaSendCallsParams = Prettify<
|
|
16
|
+
DistributiveOmit<SolanaPrepareCallsParams, "chainId"> & {
|
|
17
|
+
chainId?: SolanaChainId;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
export type SolanaSendCallsResult = Prettify<SolanaSendPreparedCallsResult>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Prepares, signs, and submits Solana instructions in a single call.
|
|
25
|
+
* Internally calls `prepareCalls`, `signPreparedCalls`, and `sendPreparedCalls`.
|
|
26
|
+
*
|
|
27
|
+
* @param {InnerSolanaWalletApiClient} client - The Solana wallet API client
|
|
28
|
+
* @param {SolanaSendCallsParams} params - Parameters for sending Solana calls
|
|
29
|
+
* @param {Array<{programId: string, accounts?: Array, data: Hex}>} params.calls - Array of Solana instructions
|
|
30
|
+
* @param {string} [params.account] - The Solana address to execute from. Defaults to the signer's address.
|
|
31
|
+
* @param {object} [params.capabilities] - Optional capabilities (e.g. paymaster sponsorship)
|
|
32
|
+
* @returns {Promise<SolanaSendCallsResult>} The result containing the call ID
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const result = await client.sendCalls({
|
|
37
|
+
* calls: [{
|
|
38
|
+
* programId: "11111111111111111111111111111111",
|
|
39
|
+
* data: "0x...",
|
|
40
|
+
* }],
|
|
41
|
+
* capabilities: {
|
|
42
|
+
* paymaster: { policyId: "your-policy-id" }
|
|
43
|
+
* }
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function sendCalls(
|
|
48
|
+
client: InnerSolanaWalletApiClient,
|
|
49
|
+
params: SolanaSendCallsParams,
|
|
50
|
+
): Promise<SolanaSendCallsResult> {
|
|
51
|
+
LOGGER.info("solana:sendCalls:start", {
|
|
52
|
+
calls: params.calls?.length,
|
|
53
|
+
hasCapabilities: !!params.capabilities,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const { chainId, ...rest } = params;
|
|
57
|
+
const prepared = await prepareCalls(client, {
|
|
58
|
+
...rest,
|
|
59
|
+
...(chainId != null ? { chainId } : {}),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const signed = await signPreparedCalls(client, prepared);
|
|
63
|
+
const res = await sendPreparedCalls(client, signed);
|
|
64
|
+
LOGGER.info("solana:sendCalls:done");
|
|
65
|
+
return res;
|
|
66
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Prettify } from "viem";
|
|
2
|
+
import type { InnerSolanaWalletApiClient } from "../../types.js";
|
|
3
|
+
import { PreparedCall_SolanaV0_Signed as PreparedCall_SolanaV0_SignedSchema } from "@alchemy/wallet-api-types";
|
|
4
|
+
import type { z } from "zod";
|
|
5
|
+
import { BaseError } from "@alchemy/common";
|
|
6
|
+
import { LOGGER } from "../../logger.js";
|
|
7
|
+
import { wallet_sendPreparedCalls as MethodSchema } from "@alchemy/wallet-api-types/rpc";
|
|
8
|
+
import {
|
|
9
|
+
methodSchema,
|
|
10
|
+
encode,
|
|
11
|
+
decode,
|
|
12
|
+
type MethodResponse,
|
|
13
|
+
} from "../../utils/schema.js";
|
|
14
|
+
|
|
15
|
+
const schema = methodSchema(MethodSchema);
|
|
16
|
+
type SendPreparedCallsResponse = MethodResponse<typeof MethodSchema>;
|
|
17
|
+
|
|
18
|
+
export type SolanaSendPreparedCallsParams = Prettify<
|
|
19
|
+
z.output<typeof PreparedCall_SolanaV0_SignedSchema>
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
type SolanaSendPreparedCallsResponse = Extract<
|
|
23
|
+
SendPreparedCallsResponse,
|
|
24
|
+
{ details: { type: "solana-transaction-v0" } }
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export type SolanaSendPreparedCallsResult = SolanaSendPreparedCallsResponse;
|
|
28
|
+
|
|
29
|
+
function isSolanaResponse(
|
|
30
|
+
response: SendPreparedCallsResponse,
|
|
31
|
+
): response is SolanaSendPreparedCallsResult {
|
|
32
|
+
return response.details.type === "solana-transaction-v0";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sends a signed Solana transaction.
|
|
37
|
+
* This method is used after signing the signature request returned from prepareCalls.
|
|
38
|
+
*
|
|
39
|
+
* @param {InnerSolanaWalletApiClient} client - The Solana wallet API client
|
|
40
|
+
* @param {SolanaSendPreparedCallsParams} params - The signed Solana transaction
|
|
41
|
+
* @returns {Promise<SolanaSendPreparedCallsResult>} The result containing the call ID
|
|
42
|
+
*/
|
|
43
|
+
export async function sendPreparedCalls(
|
|
44
|
+
client: InnerSolanaWalletApiClient,
|
|
45
|
+
params: SolanaSendPreparedCallsParams,
|
|
46
|
+
): Promise<SolanaSendPreparedCallsResult> {
|
|
47
|
+
LOGGER.debug("solana:sendPreparedCalls:start", { type: params.type });
|
|
48
|
+
|
|
49
|
+
const rpcParams = encode(PreparedCall_SolanaV0_SignedSchema, params);
|
|
50
|
+
const rpcResp = await client.request({
|
|
51
|
+
method: "wallet_sendPreparedCalls",
|
|
52
|
+
params: [rpcParams],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
LOGGER.debug("solana:sendPreparedCalls:done");
|
|
56
|
+
const decoded = decode(schema.response, rpcResp);
|
|
57
|
+
|
|
58
|
+
if (!isSolanaResponse(decoded)) {
|
|
59
|
+
throw new BaseError(
|
|
60
|
+
`Unexpected EVM response from Solana sendPreparedCalls: ${decoded.details.type}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return decoded;
|
|
65
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Prettify } from "viem";
|
|
2
|
+
import type { InnerSolanaWalletApiClient } from "../../types.js";
|
|
3
|
+
import {
|
|
4
|
+
signSolanaSignatureRequest,
|
|
5
|
+
type SolanaSignatureResult,
|
|
6
|
+
} from "./signSignatureRequest.js";
|
|
7
|
+
import type { SolanaPrepareCallsResult } from "./prepareCalls.js";
|
|
8
|
+
import { LOGGER } from "../../logger.js";
|
|
9
|
+
|
|
10
|
+
export type SolanaSignPreparedCallsParams = Prettify<SolanaPrepareCallsResult>;
|
|
11
|
+
|
|
12
|
+
export type SolanaSignPreparedCallsResult = Prettify<
|
|
13
|
+
Omit<
|
|
14
|
+
SolanaPrepareCallsResult,
|
|
15
|
+
"signatureRequest" | "feePayment" | "details"
|
|
16
|
+
> & {
|
|
17
|
+
signature: SolanaSignatureResult;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Signs a prepared Solana transaction using the client's Ed25519 signer.
|
|
23
|
+
*
|
|
24
|
+
* @param {InnerSolanaWalletApiClient} client - The Solana wallet client
|
|
25
|
+
* @param {SolanaSignPreparedCallsParams} params - The prepared Solana transaction with signature request
|
|
26
|
+
* @returns {Promise<SolanaSignPreparedCallsResult>} The signed Solana transaction
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const prepared = await client.prepareCalls({ ... });
|
|
31
|
+
* const signed = await client.signPreparedCalls(prepared);
|
|
32
|
+
* const result = await client.sendPreparedCalls(signed);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export async function signPreparedCalls(
|
|
36
|
+
client: InnerSolanaWalletApiClient,
|
|
37
|
+
params: SolanaSignPreparedCallsParams,
|
|
38
|
+
): Promise<SolanaSignPreparedCallsResult> {
|
|
39
|
+
LOGGER.debug("solana:signPreparedCalls:start");
|
|
40
|
+
|
|
41
|
+
const signature = await signSolanaSignatureRequest(
|
|
42
|
+
client.owner,
|
|
43
|
+
params.signatureRequest,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const { signatureRequest: _, feePayment: __, details: ___, ...rest } = params;
|
|
47
|
+
|
|
48
|
+
LOGGER.debug("solana:signPreparedCalls:done");
|
|
49
|
+
return { ...rest, signature };
|
|
50
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SolanaSigner } from "../../types.js";
|
|
2
|
+
import { LOGGER } from "../../logger.js";
|
|
3
|
+
import { hexToBytes } from "viem";
|
|
4
|
+
import { Base58 } from "ox";
|
|
5
|
+
import { findSignerSlot } from "../../adapters/resolveSignerSlot.js";
|
|
6
|
+
import { SolanaSignerError } from "../../adapters/SolanaSignerError.js";
|
|
7
|
+
|
|
8
|
+
export type SolanaSignatureRequestParams = {
|
|
9
|
+
type: "solana_signTransaction";
|
|
10
|
+
data: `0x${string}`;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SolanaSignatureResult = {
|
|
14
|
+
type: "ed25519";
|
|
15
|
+
data: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function signSolanaSignatureRequest(
|
|
19
|
+
signer: SolanaSigner,
|
|
20
|
+
signatureRequest: SolanaSignatureRequestParams,
|
|
21
|
+
): Promise<SolanaSignatureResult> {
|
|
22
|
+
const txBytes = hexToBytes(signatureRequest.data);
|
|
23
|
+
|
|
24
|
+
const slotIndex = await findSignerSlot(txBytes, signer.address);
|
|
25
|
+
if (slotIndex < 0) {
|
|
26
|
+
throw new SolanaSignerError(
|
|
27
|
+
`Signer ${signer.address} is not a required signer in this transaction`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
LOGGER.debug("signSolanaSignatureRequest:signing");
|
|
32
|
+
|
|
33
|
+
const { signedTransaction } = await signer.signTransaction({
|
|
34
|
+
transaction: txBytes,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (signedTransaction[0] !== txBytes[0]) {
|
|
38
|
+
throw new SolanaSignerError(
|
|
39
|
+
`Signer returned a transaction with a different signature count (expected ${txBytes[0]}, got ${signedTransaction[0]})`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const offset = 1 + slotIndex * 64;
|
|
44
|
+
const rawSignature = signedTransaction.slice(offset, offset + 64);
|
|
45
|
+
|
|
46
|
+
if (rawSignature.length !== 64) {
|
|
47
|
+
throw new SolanaSignerError(
|
|
48
|
+
`Signed transaction too short to contain signature at slot ${slotIndex}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (rawSignature.every((b) => b === 0)) {
|
|
53
|
+
throw new SolanaSignerError(
|
|
54
|
+
`Signer ${signer.address} did not produce a signature at slot ${slotIndex}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
LOGGER.debug("signSolanaSignatureRequest:ok");
|
|
59
|
+
return {
|
|
60
|
+
type: "ed25519",
|
|
61
|
+
data: Base58.fromBytes(rawSignature),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { InnerSolanaWalletApiClient } from "../../types.js";
|
|
2
|
+
import {
|
|
3
|
+
getCallsStatus,
|
|
4
|
+
type SolanaGetCallsStatusResult,
|
|
5
|
+
} from "./getCallsStatus.js";
|
|
6
|
+
import { BaseError } from "@alchemy/common";
|
|
7
|
+
import { LOGGER } from "../../logger.js";
|
|
8
|
+
|
|
9
|
+
export type SolanaWaitForCallsStatusParams = {
|
|
10
|
+
id: `0x${string}`;
|
|
11
|
+
pollingInterval?: number;
|
|
12
|
+
retryCount?: number;
|
|
13
|
+
retryDelay?: (opts: { count: number }) => number;
|
|
14
|
+
status?: (result: SolanaGetCallsStatusResult) => boolean;
|
|
15
|
+
throwOnFailure?: boolean;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type SolanaWaitForCallsStatusResult = SolanaGetCallsStatusResult;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Polls getCallsStatus until the call reaches a terminal state.
|
|
23
|
+
* Similar API to viem's waitForCallsStatus with retry, exponential backoff,
|
|
24
|
+
* configurable status predicate, throwOnFailure, and timeout.
|
|
25
|
+
*
|
|
26
|
+
* @param {InnerSolanaWalletApiClient} client - The Solana wallet API client
|
|
27
|
+
* @param {SolanaWaitForCallsStatusParams} params - The call ID and optional polling config
|
|
28
|
+
* @returns {Promise<SolanaWaitForCallsStatusResult>} The final status
|
|
29
|
+
*/
|
|
30
|
+
export async function waitForCallsStatus(
|
|
31
|
+
client: InnerSolanaWalletApiClient,
|
|
32
|
+
params: SolanaWaitForCallsStatusParams,
|
|
33
|
+
): Promise<SolanaWaitForCallsStatusResult> {
|
|
34
|
+
const {
|
|
35
|
+
id,
|
|
36
|
+
pollingInterval = client.pollingInterval,
|
|
37
|
+
retryCount = 4,
|
|
38
|
+
retryDelay = ({ count }: { count: number }) => ~~(1 << count) * 200,
|
|
39
|
+
status: isReady = ({ statusCode }) =>
|
|
40
|
+
statusCode === 200 || statusCode >= 300,
|
|
41
|
+
throwOnFailure = false,
|
|
42
|
+
timeout = 60_000,
|
|
43
|
+
} = params;
|
|
44
|
+
|
|
45
|
+
LOGGER.debug("solana:waitForCallsStatus:start", { id });
|
|
46
|
+
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
|
|
49
|
+
while (Date.now() - start < timeout) {
|
|
50
|
+
let result: SolanaGetCallsStatusResult | undefined;
|
|
51
|
+
|
|
52
|
+
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
53
|
+
try {
|
|
54
|
+
result = await getCallsStatus(client, { id });
|
|
55
|
+
break;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (attempt >= retryCount) throw error;
|
|
58
|
+
await new Promise((r) => setTimeout(r, retryDelay({ count: attempt })));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!result) throw new BaseError("Unexpected: no result after retries");
|
|
63
|
+
|
|
64
|
+
if (throwOnFailure && result.status === "failure") {
|
|
65
|
+
throw new BaseError(
|
|
66
|
+
`Solana call bundle failed with status code ${result.statusCode}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isReady(result)) {
|
|
71
|
+
LOGGER.debug("solana:waitForCallsStatus:done", {
|
|
72
|
+
status: result.status,
|
|
73
|
+
statusCode: result.statusCode,
|
|
74
|
+
});
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await new Promise((r) => setTimeout(r, pollingInterval));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new BaseError(
|
|
82
|
+
`Timed out while waiting for call bundle with id "${id}" to be confirmed.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { SolanaSigner } from "../types.js";
|
|
2
|
+
import { SolanaSignerError } from "./SolanaSignerError.js";
|
|
3
|
+
import { findSignerSlot } from "./resolveSignerSlot.js";
|
|
4
|
+
|
|
5
|
+
/** Raw Ed25519 keypair signer (e.g. `Keypair` from `@solana/web3.js` v1 or a bare Ed25519 key). */
|
|
6
|
+
export interface SolanaKeypairSigner {
|
|
7
|
+
address: string;
|
|
8
|
+
signMessage(message: Uint8Array): Promise<Uint8Array>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Adapts a raw Ed25519 keypair signer into a {@link SolanaSigner}.
|
|
13
|
+
*
|
|
14
|
+
* Use this for legacy `@solana/web3.js` v1 `Keypair` signers or any signer
|
|
15
|
+
* that exposes a `signMessage(bytes) => signature` interface. For
|
|
16
|
+
* `@solana/kit` signers, use {@link fromKitSigner}. For browser wallets
|
|
17
|
+
* (wallet adapter, Phantom, etc.), use {@link fromWalletAdapter}. For
|
|
18
|
+
* wallet-standard wallets, use {@link fromWalletStandard}.
|
|
19
|
+
*
|
|
20
|
+
* Requires `@solana/kit` or `@solana/web3.js` as a peer dependency.
|
|
21
|
+
*
|
|
22
|
+
* @param {SolanaKeypairSigner} signer - The raw Ed25519 keypair signer to adapt
|
|
23
|
+
* @returns {SolanaSigner} A SolanaSigner compatible with `createSmartWalletClient`
|
|
24
|
+
*/
|
|
25
|
+
export function fromKeypair(signer: SolanaKeypairSigner): SolanaSigner {
|
|
26
|
+
return {
|
|
27
|
+
address: signer.address,
|
|
28
|
+
async signTransaction({ transaction }) {
|
|
29
|
+
const numSigs = transaction[0];
|
|
30
|
+
const messageStart = 1 + numSigs * 64;
|
|
31
|
+
const messageBytes = transaction.slice(messageStart);
|
|
32
|
+
let sig: Uint8Array;
|
|
33
|
+
try {
|
|
34
|
+
sig = await signer.signMessage(messageBytes);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
throw new SolanaSignerError("Keypair signer failed to sign message", {
|
|
37
|
+
cause: e as Error,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (sig.length !== 64) {
|
|
41
|
+
throw new SolanaSignerError(
|
|
42
|
+
`Expected a 64-byte Ed25519 signature but received ${sig.length} bytes`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const slotIndex = await findSignerSlot(transaction, signer.address);
|
|
47
|
+
if (slotIndex < 0) {
|
|
48
|
+
throw new SolanaSignerError(
|
|
49
|
+
`Signer ${signer.address} is not a required signer in this transaction`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const signedTx = new Uint8Array(transaction);
|
|
54
|
+
signedTx.set(sig, 1 + slotIndex * 64);
|
|
55
|
+
return { signedTransaction: signedTx };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { SolanaSigner } from "../types.js";
|
|
2
|
+
import { SolanaSignerError } from "./SolanaSignerError.js";
|
|
3
|
+
import { findSignerSlot } from "./resolveSignerSlot.js";
|
|
4
|
+
|
|
5
|
+
/** Any {@link https://solanakit.com @solana/kit} signer that implements `signTransactions` (e.g. `TransactionPartialSigner`, `KeyPairSigner`). */
|
|
6
|
+
export interface SolanaTransactionPartialSigner {
|
|
7
|
+
address: string;
|
|
8
|
+
signTransactions(
|
|
9
|
+
transactions: readonly unknown[],
|
|
10
|
+
): Promise<readonly Record<string, Uint8Array>[]>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Adapts an {@link https://solanakit.com @solana/kit} signer into a {@link SolanaSigner}.
|
|
15
|
+
*
|
|
16
|
+
* Accepts any signer that implements `signTransactions`, including
|
|
17
|
+
* `KeyPairSigner`, `TransactionPartialSigner`, and `NoopSigner`. For raw
|
|
18
|
+
* Ed25519 keypairs, use {@link fromKeypair}. For browser wallets (wallet
|
|
19
|
+
* adapter, Phantom, etc.), use {@link fromWalletAdapter}. For
|
|
20
|
+
* wallet-standard wallets, use {@link fromWalletStandard}.
|
|
21
|
+
*
|
|
22
|
+
* Requires `@solana/kit` as a peer dependency.
|
|
23
|
+
*
|
|
24
|
+
* @param {SolanaTransactionPartialSigner} signer - The @solana/kit signer to adapt
|
|
25
|
+
* @returns {SolanaSigner} A SolanaSigner compatible with `createSmartWalletClient`
|
|
26
|
+
*/
|
|
27
|
+
export function fromKitSigner(
|
|
28
|
+
signer: SolanaTransactionPartialSigner,
|
|
29
|
+
): SolanaSigner {
|
|
30
|
+
return {
|
|
31
|
+
address: signer.address,
|
|
32
|
+
async signTransaction({ transaction }) {
|
|
33
|
+
let tx: unknown;
|
|
34
|
+
try {
|
|
35
|
+
const { getTransactionCodec } = await import("@solana/kit");
|
|
36
|
+
const codec = getTransactionCodec();
|
|
37
|
+
tx = codec.decode(transaction);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new SolanaSignerError("Failed to decode transaction", {
|
|
40
|
+
cause: e as Error,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let sigDict: Record<string, Uint8Array> | undefined;
|
|
45
|
+
try {
|
|
46
|
+
[sigDict] = await signer.signTransactions([tx]);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
throw new SolanaSignerError("Kit signer failed to sign transaction", {
|
|
49
|
+
cause: e as Error,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!sigDict) {
|
|
53
|
+
throw new SolanaSignerError(
|
|
54
|
+
"TransactionPartialSigner returned no signatures",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sig = sigDict[signer.address];
|
|
59
|
+
if (!sig) {
|
|
60
|
+
throw new SolanaSignerError(
|
|
61
|
+
`TransactionPartialSigner did not produce a signature for ${signer.address}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (sig.length !== 64) {
|
|
65
|
+
throw new SolanaSignerError(
|
|
66
|
+
`Expected a 64-byte Ed25519 signature but received ${sig.length} bytes`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const slotIndex = await findSignerSlot(transaction, signer.address);
|
|
71
|
+
if (slotIndex < 0) {
|
|
72
|
+
throw new SolanaSignerError(
|
|
73
|
+
`Signer ${signer.address} is not a required signer in this transaction`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const signedTx = new Uint8Array(transaction);
|
|
78
|
+
signedTx.set(sig, 1 + slotIndex * 64);
|
|
79
|
+
return { signedTransaction: signedTx };
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { SolanaSigner } from "../types.js";
|
|
2
|
+
import type { VersionedTransaction } from "@solana/web3.js";
|
|
3
|
+
import { SolanaSignerError } from "./SolanaSignerError.js";
|
|
4
|
+
|
|
5
|
+
/** Any signer with a `publicKey` and `signTransaction(VersionedTransaction)` method — matches `useWallet()` from `@solana/wallet-adapter-react` and injected wallet providers like `window.phantom.solana`. */
|
|
6
|
+
export interface WalletAdapterSigner {
|
|
7
|
+
publicKey: { toBase58(): string };
|
|
8
|
+
signTransaction: <T extends VersionedTransaction>(
|
|
9
|
+
transaction: T,
|
|
10
|
+
) => Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Adapts a wallet that signs `VersionedTransaction` objects into a {@link SolanaSigner}.
|
|
15
|
+
*
|
|
16
|
+
* Works with `useWallet()` from `@solana/wallet-adapter-react` and injected
|
|
17
|
+
* browser wallet providers (e.g. `window.phantom.solana`). Handles the
|
|
18
|
+
* `Uint8Array` ↔ `VersionedTransaction` conversion internally.
|
|
19
|
+
*
|
|
20
|
+
* For `@solana/kit` signers, use {@link fromKitSigner}. For raw Ed25519
|
|
21
|
+
* keypairs, use {@link fromKeypair}. For wallet-standard wallets, use
|
|
22
|
+
* {@link fromWalletStandard}.
|
|
23
|
+
*
|
|
24
|
+
* Requires `@solana/web3.js` as a peer dependency.
|
|
25
|
+
*
|
|
26
|
+
* @param {WalletAdapterSigner} signer - The wallet adapter signer
|
|
27
|
+
* @returns {SolanaSigner} A SolanaSigner compatible with `createSmartWalletClient`
|
|
28
|
+
*/
|
|
29
|
+
export function fromWalletAdapter(signer: WalletAdapterSigner): SolanaSigner {
|
|
30
|
+
return {
|
|
31
|
+
address: signer.publicKey.toBase58(),
|
|
32
|
+
async signTransaction({ transaction }) {
|
|
33
|
+
let tx: VersionedTransaction;
|
|
34
|
+
try {
|
|
35
|
+
const web3 = await import("@solana/web3.js");
|
|
36
|
+
tx = web3.VersionedTransaction.deserialize(transaction);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
throw new SolanaSignerError("Failed to deserialize transaction", {
|
|
39
|
+
cause: e as Error,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let signed: VersionedTransaction;
|
|
44
|
+
try {
|
|
45
|
+
signed = await signer.signTransaction(tx);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
throw new SolanaSignerError(
|
|
48
|
+
"Wallet adapter failed to sign transaction",
|
|
49
|
+
{
|
|
50
|
+
cause: e as Error,
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { signedTransaction: new Uint8Array(signed.serialize()) };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|