@faremeter/payment-solana 0.20.0 → 0.21.0
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/src/charge/client.d.ts +10 -6
- package/dist/src/charge/client.d.ts.map +1 -1
- package/dist/src/charge/client.js +117 -102
- package/dist/src/charge/common.d.ts +3 -3
- package/dist/src/charge/server.d.ts +18 -7
- package/dist/src/charge/server.d.ts.map +1 -1
- package/dist/src/charge/server.js +22 -24
- package/dist/src/compat.d.ts +38 -0
- package/dist/src/compat.d.ts.map +1 -0
- package/dist/src/compat.js +86 -0
- package/dist/src/compat.test.d.ts +3 -0
- package/dist/src/compat.test.d.ts.map +1 -0
- package/dist/src/compat.test.js +70 -0
- package/dist/src/exact/client.d.ts +18 -15
- package/dist/src/exact/client.d.ts.map +1 -1
- package/dist/src/exact/client.js +124 -96
- package/dist/src/exact/common.d.ts +1 -1
- package/dist/src/exact/facilitator.d.ts +19 -12
- package/dist/src/exact/facilitator.d.ts.map +1 -1
- package/dist/src/exact/facilitator.js +19 -18
- package/dist/src/exact/memo.d.ts +0 -2
- package/dist/src/exact/memo.d.ts.map +1 -1
- package/dist/src/exact/memo.js +0 -9
- package/dist/src/exact/verify.d.ts +5 -1
- package/dist/src/exact/verify.d.ts.map +1 -1
- package/dist/src/exact/verify.js +8 -2
- package/dist/src/exact/verify.test.js +80 -3
- package/dist/src/flex/client/handler.d.ts +31 -0
- package/dist/src/flex/client/handler.d.ts.map +1 -0
- package/dist/src/flex/client/handler.js +104 -0
- package/dist/src/flex/client/index.d.ts +3 -0
- package/dist/src/flex/client/index.d.ts.map +1 -0
- package/dist/src/flex/client/index.js +1 -0
- package/dist/src/flex/common.d.ts +15 -0
- package/dist/src/flex/common.d.ts.map +1 -0
- package/dist/src/flex/common.js +7 -0
- package/dist/src/flex/facilitator/handler.d.ts +48 -0
- package/dist/src/flex/facilitator/handler.d.ts.map +1 -0
- package/dist/src/flex/facilitator/handler.js +705 -0
- package/dist/src/flex/facilitator/index.d.ts +5 -0
- package/dist/src/flex/facilitator/index.d.ts.map +1 -0
- package/dist/src/flex/facilitator/index.js +2 -0
- package/dist/src/flex/hono/index.d.ts +3 -0
- package/dist/src/flex/hono/index.d.ts.map +1 -0
- package/dist/src/flex/hono/index.js +1 -0
- package/dist/src/flex/hono/upto-handler.d.ts +20 -0
- package/dist/src/flex/hono/upto-handler.d.ts.map +1 -0
- package/dist/src/flex/hono/upto-handler.js +72 -0
- package/dist/src/flex/hono/upto-handler.test.d.ts +3 -0
- package/dist/src/flex/hono/upto-handler.test.d.ts.map +1 -0
- package/dist/src/flex/hono/upto-handler.test.js +381 -0
- package/dist/src/flex/index.d.ts +4 -0
- package/dist/src/flex/index.d.ts.map +1 -0
- package/dist/src/flex/index.js +3 -0
- package/dist/src/flex/logger.d.ts +2 -0
- package/dist/src/flex/logger.d.ts.map +1 -0
- package/dist/src/flex/logger.js +2 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -7
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type { MPPPaymentHandler } from "@faremeter/types/mpp";
|
|
2
|
-
import {
|
|
3
|
-
import type
|
|
2
|
+
import { type Address, type Rpc, type SolanaRpcApi } from "@solana/kit";
|
|
3
|
+
import { type Wallet } from "../exact/client.js";
|
|
4
4
|
export type CreateMPPSolanaChargeClientArgs = {
|
|
5
5
|
wallet: Wallet;
|
|
6
|
-
mint:
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
mint: Address | {
|
|
7
|
+
toBase58(): string;
|
|
8
|
+
};
|
|
9
|
+
rpc?: Rpc<SolanaRpcApi> | string;
|
|
10
|
+
tokenProgramId?: Address | {
|
|
11
|
+
toBase58(): string;
|
|
12
|
+
};
|
|
9
13
|
broadcast?: boolean;
|
|
10
14
|
};
|
|
11
15
|
export declare function createMPPSolanaChargeClient(args: CreateMPPSolanaChargeClientArgs): MPPPaymentHandler;
|
|
12
16
|
export type CreateMPPSolanaNativeChargeClientArgs = {
|
|
13
17
|
wallet: Wallet;
|
|
14
|
-
|
|
18
|
+
rpc?: Rpc<SolanaRpcApi> | string;
|
|
15
19
|
broadcast?: boolean;
|
|
16
20
|
};
|
|
17
21
|
export declare function createMPPSolanaNativeChargeClient(args: CreateMPPSolanaNativeChargeClientArgs): MPPPaymentHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/charge/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/charge/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAIL,KAAK,OAAO,EAGZ,KAAK,GAAG,EAER,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,KAAK,MAAM,EAEZ,MAAM,iBAAiB,CAAC;AAmFzB,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IACvC,GAAG,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,OAAO,GAAG;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,+BAA+B,GACpC,iBAAiB,CAwHnB;AAED,MAAM,MAAM,qCAAqC,GAAG;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,qCAAqC,GAC1C,iBAAiB,CAoFnB"}
|
|
@@ -1,32 +1,79 @@
|
|
|
1
1
|
import { decodeBase64URL } from "@faremeter/types/mpp";
|
|
2
2
|
import { isValidationError } from "@faremeter/types";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { fetchMint, findAssociatedTokenPda, getTransferCheckedInstruction, TOKEN_PROGRAM_ADDRESS, } from "@solana-program/token";
|
|
4
|
+
import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, } from "@solana-program/compute-budget";
|
|
5
|
+
import { getTransferSolInstruction } from "@solana-program/system";
|
|
6
|
+
import { address, createNoopSigner, getBase64EncodedWireTransaction, } from "@solana/kit";
|
|
7
|
+
import { buildAndSignClientTransaction, } from "../exact/client.js";
|
|
5
8
|
import { mppChargeRequest } from "./common.js";
|
|
6
|
-
|
|
9
|
+
import { toAddress, toRpc } from "../compat.js";
|
|
10
|
+
async function broadcastAndConfirm(tx, wallet, rpc, challenge, md, lifetimeConstraint) {
|
|
7
11
|
if (md?.feePayer) {
|
|
8
12
|
throw new Error("push mode is not allowed with fee sponsorship");
|
|
9
13
|
}
|
|
10
|
-
const { lastValidBlockHeight } = await connection.getLatestBlockhash({
|
|
11
|
-
commitment: "confirmed",
|
|
12
|
-
});
|
|
13
14
|
let signature;
|
|
14
15
|
if (wallet.sendTransaction) {
|
|
15
16
|
signature = await wallet.sendTransaction(tx);
|
|
16
17
|
}
|
|
17
18
|
else {
|
|
18
|
-
|
|
19
|
+
const wire = getBase64EncodedWireTransaction(tx);
|
|
20
|
+
signature = await rpc.sendTransaction(wire, { encoding: "base64" }).send();
|
|
21
|
+
}
|
|
22
|
+
// Poll until the signature is confirmed or the blockhash expires.
|
|
23
|
+
const maxPolls = 60;
|
|
24
|
+
let confirmed = false;
|
|
25
|
+
for (let i = 0; i < maxPolls; i++) {
|
|
26
|
+
const status = await rpc
|
|
27
|
+
.getSignatureStatuses([signature])
|
|
28
|
+
.send();
|
|
29
|
+
if (status.value[0]?.err) {
|
|
30
|
+
throw new Error(`transaction failed: ${JSON.stringify(status.value[0].err)}`);
|
|
31
|
+
}
|
|
32
|
+
if (status.value[0]?.confirmationStatus === "confirmed" ||
|
|
33
|
+
status.value[0]?.confirmationStatus === "finalized") {
|
|
34
|
+
confirmed = true;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const currentHeight = await rpc.getBlockHeight().send();
|
|
38
|
+
if (lifetimeConstraint.lastValidBlockHeight > 0n &&
|
|
39
|
+
currentHeight > lifetimeConstraint.lastValidBlockHeight) {
|
|
40
|
+
throw new Error("blockhash expired before confirmation");
|
|
41
|
+
}
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
43
|
+
}
|
|
44
|
+
if (!confirmed) {
|
|
45
|
+
throw new Error("transaction confirmation timed out");
|
|
19
46
|
}
|
|
20
|
-
await connection.confirmTransaction({ signature, blockhash: recentBlockhash, lastValidBlockHeight }, "confirmed");
|
|
21
47
|
return {
|
|
22
48
|
challenge,
|
|
23
49
|
payload: { type: "signature", signature },
|
|
24
50
|
};
|
|
25
51
|
}
|
|
52
|
+
async function fetchLifetimeConstraint(rpc, supplied) {
|
|
53
|
+
if (supplied) {
|
|
54
|
+
return {
|
|
55
|
+
blockhash: supplied,
|
|
56
|
+
lastValidBlockHeight: 0n,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!rpc) {
|
|
60
|
+
throw new Error("no blockhash available");
|
|
61
|
+
}
|
|
62
|
+
const { value } = await rpc.getLatestBlockhash().send();
|
|
63
|
+
return {
|
|
64
|
+
blockhash: value.blockhash,
|
|
65
|
+
lastValidBlockHeight: value.lastValidBlockHeight,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
26
68
|
export function createMPPSolanaChargeClient(args) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
69
|
+
const mint = toAddress(args.mint);
|
|
70
|
+
const defaultTokenProgram = args.tokenProgramId
|
|
71
|
+
? toAddress(args.tokenProgramId)
|
|
72
|
+
: undefined;
|
|
73
|
+
const rpc = args.rpc ? toRpc(args.rpc) : undefined;
|
|
74
|
+
const { wallet, broadcast = false } = args;
|
|
75
|
+
if (broadcast && !rpc) {
|
|
76
|
+
throw new Error("rpc is required when broadcast is true");
|
|
30
77
|
}
|
|
31
78
|
return async (challenge) => {
|
|
32
79
|
if (challenge.method !== "solana")
|
|
@@ -50,69 +97,60 @@ export function createMPPSolanaChargeClient(args) {
|
|
|
50
97
|
exec: async () => {
|
|
51
98
|
const md = request.methodDetails;
|
|
52
99
|
const amount = BigInt(request.amount);
|
|
53
|
-
const recipientKey =
|
|
54
|
-
const feePayerKey = md?.feePayer === true
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
else if (connection) {
|
|
60
|
-
recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
throw new Error("no blockhash available");
|
|
64
|
-
}
|
|
100
|
+
const recipientKey = address(request.recipient);
|
|
101
|
+
const feePayerKey = md?.feePayer === true && md.feePayerKey
|
|
102
|
+
? address(md.feePayerKey)
|
|
103
|
+
: undefined;
|
|
104
|
+
const lifetimeConstraint = await fetchLifetimeConstraint(rpc, md?.recentBlockhash);
|
|
65
105
|
let decimals;
|
|
66
106
|
if (md?.decimals !== undefined) {
|
|
67
107
|
decimals = md.decimals;
|
|
68
108
|
}
|
|
69
|
-
else if (
|
|
70
|
-
const mintInfo = await
|
|
71
|
-
decimals = mintInfo.decimals;
|
|
109
|
+
else if (rpc) {
|
|
110
|
+
const mintInfo = await fetchMint(rpc, mint);
|
|
111
|
+
decimals = mintInfo.data.decimals;
|
|
72
112
|
}
|
|
73
113
|
else {
|
|
74
114
|
throw new Error("no decimals available");
|
|
75
115
|
}
|
|
76
116
|
const tokenProgramId = md?.tokenProgram
|
|
77
|
-
?
|
|
78
|
-
: (
|
|
79
|
-
const sourceAccount =
|
|
80
|
-
|
|
117
|
+
? address(md.tokenProgram)
|
|
118
|
+
: (defaultTokenProgram ?? TOKEN_PROGRAM_ADDRESS);
|
|
119
|
+
const [sourceAccount] = await findAssociatedTokenPda({
|
|
120
|
+
mint,
|
|
121
|
+
owner: wallet.publicKey,
|
|
122
|
+
tokenProgram: tokenProgramId,
|
|
123
|
+
});
|
|
124
|
+
const [receiverAccount] = await findAssociatedTokenPda({
|
|
125
|
+
mint,
|
|
126
|
+
owner: recipientKey,
|
|
127
|
+
tokenProgram: tokenProgramId,
|
|
128
|
+
});
|
|
129
|
+
const walletSigner = createNoopSigner(wallet.publicKey);
|
|
81
130
|
const instructions = [
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
131
|
+
getSetComputeUnitLimitInstruction({ units: 200_000 }),
|
|
132
|
+
getSetComputeUnitPriceInstruction({ microLamports: 1n }),
|
|
133
|
+
getTransferCheckedInstruction({
|
|
134
|
+
source: sourceAccount,
|
|
135
|
+
mint,
|
|
136
|
+
destination: receiverAccount,
|
|
137
|
+
authority: walletSigner,
|
|
138
|
+
amount,
|
|
139
|
+
decimals,
|
|
140
|
+
}, { programAddress: tokenProgramId }),
|
|
85
141
|
];
|
|
86
|
-
const payerKey = feePayerKey
|
|
87
|
-
|
|
88
|
-
: wallet.publicKey;
|
|
89
|
-
let tx;
|
|
90
|
-
if (wallet.buildTransaction) {
|
|
91
|
-
tx = await wallet.buildTransaction(instructions, recentBlockhash);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
const message = new TransactionMessage({
|
|
95
|
-
instructions,
|
|
96
|
-
payerKey,
|
|
97
|
-
recentBlockhash,
|
|
98
|
-
}).compileToV0Message();
|
|
99
|
-
tx = new VersionedTransaction(message);
|
|
100
|
-
}
|
|
101
|
-
if (wallet.partiallySignTransaction) {
|
|
102
|
-
tx = await wallet.partiallySignTransaction(tx);
|
|
103
|
-
}
|
|
142
|
+
const payerKey = feePayerKey ?? wallet.publicKey;
|
|
143
|
+
const tx = await buildAndSignClientTransaction(wallet, instructions, payerKey, lifetimeConstraint);
|
|
104
144
|
if (broadcast) {
|
|
105
|
-
if (!
|
|
106
|
-
throw new Error("
|
|
107
|
-
return broadcastAndConfirm(tx, wallet,
|
|
145
|
+
if (!rpc)
|
|
146
|
+
throw new Error("rpc is required");
|
|
147
|
+
return broadcastAndConfirm(tx, wallet, rpc, challenge, md, lifetimeConstraint);
|
|
108
148
|
}
|
|
109
|
-
const wireBytes = tx.serialize();
|
|
110
|
-
const base64Transaction = btoa(String.fromCharCode.apply(null, [...wireBytes]));
|
|
111
149
|
return {
|
|
112
150
|
challenge,
|
|
113
151
|
payload: {
|
|
114
152
|
type: "transaction",
|
|
115
|
-
transaction:
|
|
153
|
+
transaction: getBase64EncodedWireTransaction(tx),
|
|
116
154
|
},
|
|
117
155
|
};
|
|
118
156
|
},
|
|
@@ -120,9 +158,10 @@ export function createMPPSolanaChargeClient(args) {
|
|
|
120
158
|
};
|
|
121
159
|
}
|
|
122
160
|
export function createMPPSolanaNativeChargeClient(args) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
161
|
+
const rpc = args.rpc ? toRpc(args.rpc) : undefined;
|
|
162
|
+
const { wallet, broadcast = false } = args;
|
|
163
|
+
if (broadcast && !rpc) {
|
|
164
|
+
throw new Error("rpc is required when broadcast is true");
|
|
126
165
|
}
|
|
127
166
|
return async (challenge) => {
|
|
128
167
|
if (challenge.method !== "solana")
|
|
@@ -146,57 +185,33 @@ export function createMPPSolanaNativeChargeClient(args) {
|
|
|
146
185
|
exec: async () => {
|
|
147
186
|
const md = request.methodDetails;
|
|
148
187
|
const amount = BigInt(request.amount);
|
|
149
|
-
const recipientKey =
|
|
150
|
-
const feePayerKey = md?.feePayer === true
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
else if (connection) {
|
|
156
|
-
recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
throw new Error("no blockhash available");
|
|
160
|
-
}
|
|
188
|
+
const recipientKey = address(request.recipient);
|
|
189
|
+
const feePayerKey = md?.feePayer === true && md.feePayerKey
|
|
190
|
+
? address(md.feePayerKey)
|
|
191
|
+
: undefined;
|
|
192
|
+
const lifetimeConstraint = await fetchLifetimeConstraint(rpc, md?.recentBlockhash);
|
|
193
|
+
const walletSigner = createNoopSigner(wallet.publicKey);
|
|
161
194
|
const instructions = [
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
getSetComputeUnitLimitInstruction({ units: 200_000 }),
|
|
196
|
+
getSetComputeUnitPriceInstruction({ microLamports: 1n }),
|
|
197
|
+
getTransferSolInstruction({
|
|
198
|
+
source: walletSigner,
|
|
199
|
+
destination: recipientKey,
|
|
200
|
+
amount,
|
|
168
201
|
}),
|
|
169
202
|
];
|
|
170
|
-
const payerKey = feePayerKey
|
|
171
|
-
|
|
172
|
-
: wallet.publicKey;
|
|
173
|
-
let tx;
|
|
174
|
-
if (wallet.buildTransaction) {
|
|
175
|
-
tx = await wallet.buildTransaction(instructions, recentBlockhash);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
const message = new TransactionMessage({
|
|
179
|
-
instructions,
|
|
180
|
-
payerKey,
|
|
181
|
-
recentBlockhash,
|
|
182
|
-
}).compileToV0Message();
|
|
183
|
-
tx = new VersionedTransaction(message);
|
|
184
|
-
}
|
|
185
|
-
if (wallet.partiallySignTransaction) {
|
|
186
|
-
tx = await wallet.partiallySignTransaction(tx);
|
|
187
|
-
}
|
|
203
|
+
const payerKey = feePayerKey ?? wallet.publicKey;
|
|
204
|
+
const tx = await buildAndSignClientTransaction(wallet, instructions, payerKey, lifetimeConstraint);
|
|
188
205
|
if (broadcast) {
|
|
189
|
-
if (!
|
|
190
|
-
throw new Error("
|
|
191
|
-
return broadcastAndConfirm(tx, wallet,
|
|
206
|
+
if (!rpc)
|
|
207
|
+
throw new Error("rpc is required");
|
|
208
|
+
return broadcastAndConfirm(tx, wallet, rpc, challenge, md, lifetimeConstraint);
|
|
192
209
|
}
|
|
193
|
-
const wireBytes = tx.serialize();
|
|
194
|
-
const base64Transaction = btoa(String.fromCharCode.apply(null, [...wireBytes]));
|
|
195
210
|
return {
|
|
196
211
|
challenge,
|
|
197
212
|
payload: {
|
|
198
213
|
type: "transaction",
|
|
199
|
-
transaction:
|
|
214
|
+
transaction: getBase64EncodedWireTransaction(tx),
|
|
200
215
|
},
|
|
201
216
|
};
|
|
202
217
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const solanaChargeMethodDetails: import("arktype/internal/
|
|
1
|
+
export declare const solanaChargeMethodDetails: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
2
2
|
network?: string;
|
|
3
3
|
decimals?: number;
|
|
4
4
|
tokenProgram?: string;
|
|
@@ -12,7 +12,7 @@ export declare const solanaChargeMethodDetails: import("arktype/internal/methods
|
|
|
12
12
|
}[];
|
|
13
13
|
}, {}>;
|
|
14
14
|
export type solanaChargeMethodDetails = typeof solanaChargeMethodDetails.infer;
|
|
15
|
-
export declare const mppChargeRequest: import("arktype/internal/
|
|
15
|
+
export declare const mppChargeRequest: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
16
16
|
amount: string;
|
|
17
17
|
currency: string;
|
|
18
18
|
recipient: string;
|
|
@@ -33,7 +33,7 @@ export declare const mppChargeRequest: import("arktype/internal/methods/object.t
|
|
|
33
33
|
};
|
|
34
34
|
}, {}>;
|
|
35
35
|
export type mppChargeRequest = typeof mppChargeRequest.infer;
|
|
36
|
-
export declare const chargeCredentialPayload: import("arktype/internal/
|
|
36
|
+
export declare const chargeCredentialPayload: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
37
37
|
type: "transaction";
|
|
38
38
|
transaction: string;
|
|
39
39
|
} | {
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { MPPMethodHandler } from "@faremeter/types/mpp";
|
|
2
2
|
import { type SolanaCAIP2Network } from "@faremeter/info/solana";
|
|
3
|
-
import { type Rpc, type SolanaRpcApi } from "@solana/kit";
|
|
4
|
-
import { Keypair, PublicKey } from "@solana/web3.js";
|
|
3
|
+
import { type Address, type KeyPairSigner, type Rpc, type SolanaRpcApi } from "@solana/kit";
|
|
5
4
|
import type { ReplayStore } from "./replay.js";
|
|
6
5
|
export type CreateMPPSolanaChargeHandlerArgs = {
|
|
7
6
|
network: string | SolanaCAIP2Network;
|
|
8
|
-
rpc: Rpc<SolanaRpcApi
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
rpc: Rpc<SolanaRpcApi> | string;
|
|
8
|
+
feePayerSigner?: KeyPairSigner | {
|
|
9
|
+
secretKey: Uint8Array;
|
|
10
|
+
publicKey: {
|
|
11
|
+
toBase58(): string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
mint: Address | {
|
|
15
|
+
toBase58(): string;
|
|
16
|
+
};
|
|
11
17
|
replayStore: ReplayStore;
|
|
12
18
|
realm: string;
|
|
13
19
|
secretKey: Uint8Array;
|
|
@@ -18,8 +24,13 @@ export type CreateMPPSolanaChargeHandlerArgs = {
|
|
|
18
24
|
export declare function createMPPSolanaChargeHandler(args: CreateMPPSolanaChargeHandlerArgs): Promise<MPPMethodHandler>;
|
|
19
25
|
export type CreateMPPSolanaNativeChargeHandlerArgs = {
|
|
20
26
|
network: string | SolanaCAIP2Network;
|
|
21
|
-
rpc: Rpc<SolanaRpcApi
|
|
22
|
-
|
|
27
|
+
rpc: Rpc<SolanaRpcApi> | string;
|
|
28
|
+
feePayerSigner?: KeyPairSigner | {
|
|
29
|
+
secretKey: Uint8Array;
|
|
30
|
+
publicKey: {
|
|
31
|
+
toBase58(): string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
23
34
|
replayStore: ReplayStore;
|
|
24
35
|
realm: string;
|
|
25
36
|
secretKey: Uint8Array;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/charge/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/charge/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,GAAG,EAER,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAWrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAkD5C,MAAM,MAAM,gCAAgC,GAAG;IAC7C,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,EACX,aAAa,GACb;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACjE,IAAI,EAAE,OAAO,GAAG;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAsGF,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,gCAAgC,GACrC,OAAO,CAAC,gBAAgB,CAAC,CA+M3B;AAID,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,EACX,aAAa,GACb;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACjE,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,sCAAsC,GAC3C,OAAO,CAAC,gBAAgB,CAAC,CAwM3B"}
|
|
@@ -2,12 +2,13 @@ import { encodeBase64URL, canonicalizeSortedJSON, decodeBase64URL, } from "@fare
|
|
|
2
2
|
import { isValidationError } from "@faremeter/types";
|
|
3
3
|
import { lookupX402Network, caip2ToCluster, } from "@faremeter/info/solana";
|
|
4
4
|
import { fetchMint } from "@solana-program/token";
|
|
5
|
-
import {
|
|
5
|
+
import { decompileTransactionMessage, getBase64Encoder, getCompiledTransactionMessageDecoder, } from "@solana/kit";
|
|
6
6
|
import { getBase64EncodedWireTransaction, getTransactionDecoder, partiallySignTransaction, } from "@solana/transactions";
|
|
7
|
-
|
|
7
|
+
const LAMPORTS_PER_SOL = 1_000_000_000;
|
|
8
8
|
import { mppChargeRequest, chargeCredentialPayload } from "./common.js";
|
|
9
9
|
import { verifyChargeTransaction, verifyNativeChargeTransaction, } from "./verify.js";
|
|
10
10
|
import { logger } from "./logger.js";
|
|
11
|
+
import { toAddress, toKeyPairSigner, toRpc } from "../compat.js";
|
|
11
12
|
async function generateChallengeID(secret, params) {
|
|
12
13
|
const slots = [
|
|
13
14
|
params.realm,
|
|
@@ -106,26 +107,25 @@ const decodeWireTransaction = (base64Transaction) => {
|
|
|
106
107
|
};
|
|
107
108
|
};
|
|
108
109
|
export async function createMPPSolanaChargeHandler(args) {
|
|
109
|
-
const { network,
|
|
110
|
+
const { network, replayStore, realm, secretKey, maxRetries = 30, retryDelayMs = 1000, maxPriorityFee = 100_000, } = args;
|
|
111
|
+
const rpc = toRpc(args.rpc);
|
|
112
|
+
const mint = toAddress(args.mint);
|
|
113
|
+
const feePayerSigner = args.feePayerSigner
|
|
114
|
+
? await toKeyPairSigner(args.feePayerSigner)
|
|
115
|
+
: undefined;
|
|
110
116
|
const solanaNetwork = lookupX402Network(network);
|
|
111
|
-
const mintAddress = mint
|
|
112
|
-
const
|
|
113
|
-
const feePayerAddress =
|
|
114
|
-
const mintInfo = await fetchMint(rpc,
|
|
117
|
+
const mintAddress = mint;
|
|
118
|
+
const hasFeePayer = feePayerSigner !== undefined;
|
|
119
|
+
const feePayerAddress = feePayerSigner?.address;
|
|
120
|
+
const mintInfo = await fetchMint(rpc, mint);
|
|
115
121
|
const tokenProgram = mintInfo.programAddress;
|
|
116
|
-
const feePayerSigner = hasFeePayerKeypair
|
|
117
|
-
? await (async () => {
|
|
118
|
-
const { createKeyPairSignerFromBytes } = await import("@solana/kit");
|
|
119
|
-
return createKeyPairSignerFromBytes(feePayerKeypair.secretKey);
|
|
120
|
-
})()
|
|
121
|
-
: null;
|
|
122
122
|
const getChallenge = async (intent, pricing, _resourceURL, opts) => {
|
|
123
123
|
const methodDetails = {
|
|
124
124
|
network: caip2ToCluster(solanaNetwork.caip2) ?? solanaNetwork.caip2,
|
|
125
125
|
decimals: mintInfo.data.decimals,
|
|
126
126
|
tokenProgram: tokenProgram,
|
|
127
127
|
};
|
|
128
|
-
if (
|
|
128
|
+
if (hasFeePayer && feePayerAddress) {
|
|
129
129
|
const latestBlockhash = await rpc.getLatestBlockhash().send();
|
|
130
130
|
methodDetails.feePayer = true;
|
|
131
131
|
methodDetails.feePayerKey = feePayerAddress;
|
|
@@ -253,22 +253,20 @@ export async function createMPPSolanaChargeHandler(args) {
|
|
|
253
253
|
}
|
|
254
254
|
const SOL_DECIMALS = Math.log10(LAMPORTS_PER_SOL);
|
|
255
255
|
export async function createMPPSolanaNativeChargeHandler(args) {
|
|
256
|
-
const { network,
|
|
256
|
+
const { network, replayStore, realm, secretKey, maxRetries = 30, retryDelayMs = 1000, maxPriorityFee = 100_000, } = args;
|
|
257
|
+
const rpc = toRpc(args.rpc);
|
|
258
|
+
const feePayerSigner = args.feePayerSigner
|
|
259
|
+
? await toKeyPairSigner(args.feePayerSigner)
|
|
260
|
+
: undefined;
|
|
257
261
|
const solanaNetwork = lookupX402Network(network);
|
|
258
|
-
const
|
|
259
|
-
const feePayerAddress =
|
|
260
|
-
const feePayerSigner = hasFeePayerKeypair
|
|
261
|
-
? await (async () => {
|
|
262
|
-
const { createKeyPairSignerFromBytes } = await import("@solana/kit");
|
|
263
|
-
return createKeyPairSignerFromBytes(feePayerKeypair.secretKey);
|
|
264
|
-
})()
|
|
265
|
-
: null;
|
|
262
|
+
const hasFeePayer = feePayerSigner !== undefined;
|
|
263
|
+
const feePayerAddress = feePayerSigner?.address;
|
|
266
264
|
const getChallenge = async (intent, pricing, _resourceURL, opts) => {
|
|
267
265
|
const methodDetails = {
|
|
268
266
|
network: caip2ToCluster(solanaNetwork.caip2) ?? solanaNetwork.caip2,
|
|
269
267
|
decimals: SOL_DECIMALS,
|
|
270
268
|
};
|
|
271
|
-
if (
|
|
269
|
+
if (hasFeePayer && feePayerAddress) {
|
|
272
270
|
const latestBlockhash = await rpc.getLatestBlockhash().send();
|
|
273
271
|
methodDetails.feePayer = true;
|
|
274
272
|
methodDetails.feePayerKey = feePayerAddress;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backwards-compatibility helpers for callers still using
|
|
3
|
+
* @solana/web3.js v1 types (PublicKey, Keypair).
|
|
4
|
+
*
|
|
5
|
+
* These use duck-typing so payment-solana never imports v1 at
|
|
6
|
+
* runtime. The v1 package stays out of our dependency tree while
|
|
7
|
+
* callers who still have it can pass v1 objects directly.
|
|
8
|
+
*
|
|
9
|
+
* Every helper logs a one-shot deprecation warning the first time
|
|
10
|
+
* the v1 code-path is taken.
|
|
11
|
+
*/
|
|
12
|
+
import { type Address, type KeyPairSigner, type Rpc, type SolanaRpcApi } from "@solana/kit";
|
|
13
|
+
/** Duck-type for @solana/web3.js v1 PublicKey. */
|
|
14
|
+
interface PublicKeyLike {
|
|
15
|
+
toBase58(): string;
|
|
16
|
+
}
|
|
17
|
+
/** Duck-type for @solana/web3.js v1 Keypair. */
|
|
18
|
+
interface KeypairLike {
|
|
19
|
+
secretKey: Uint8Array;
|
|
20
|
+
publicKey: PublicKeyLike;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Accepts an {@link Address} string or a v1 `PublicKey` and returns
|
|
24
|
+
* a kit `Address`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toAddress(input: Address | PublicKeyLike): Address;
|
|
27
|
+
/**
|
|
28
|
+
* Accepts a kit `KeyPairSigner`, a 64-byte secret key, or a v1
|
|
29
|
+
* `Keypair` and returns a `KeyPairSigner`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function toKeyPairSigner(input: KeyPairSigner | Uint8Array | KeypairLike): Promise<KeyPairSigner>;
|
|
32
|
+
/**
|
|
33
|
+
* Accepts an `Rpc<SolanaRpcApi>` or a URL string and returns
|
|
34
|
+
* an `Rpc<SolanaRpcApi>`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toRpc(input: Rpc<SolanaRpcApi> | string): Rpc<SolanaRpcApi>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=compat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../../src/compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAQrB,kDAAkD;AAClD,UAAU,aAAa;IACrB,QAAQ,IAAI,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,UAAU,WAAW;IACnB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,aAAa,CAAC;CAC1B;AAmBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,OAAO,CAiBjE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,aAAa,GAAG,UAAU,GAAG,WAAW,GAC9C,OAAO,CAAC,aAAa,CAAC,CA+BxB;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,CAK1E"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backwards-compatibility helpers for callers still using
|
|
3
|
+
* @solana/web3.js v1 types (PublicKey, Keypair).
|
|
4
|
+
*
|
|
5
|
+
* These use duck-typing so payment-solana never imports v1 at
|
|
6
|
+
* runtime. The v1 package stays out of our dependency tree while
|
|
7
|
+
* callers who still have it can pass v1 objects directly.
|
|
8
|
+
*
|
|
9
|
+
* Every helper logs a one-shot deprecation warning the first time
|
|
10
|
+
* the v1 code-path is taken.
|
|
11
|
+
*/
|
|
12
|
+
import { address, createKeyPairSignerFromBytes, createSolanaRpc, } from "@solana/kit";
|
|
13
|
+
import { getLogger } from "@faremeter/logs";
|
|
14
|
+
const logger = await getLogger(["faremeter", "payment-solana", "compat"]);
|
|
15
|
+
let warnedPublicKey = false;
|
|
16
|
+
let warnedKeypair = false;
|
|
17
|
+
function isPublicKeyLike(v) {
|
|
18
|
+
return (typeof v === "object" &&
|
|
19
|
+
v !== null &&
|
|
20
|
+
typeof v.toBase58 === "function");
|
|
21
|
+
}
|
|
22
|
+
function isKeypairLike(v) {
|
|
23
|
+
return (typeof v === "object" &&
|
|
24
|
+
v !== null &&
|
|
25
|
+
v.secretKey instanceof Uint8Array &&
|
|
26
|
+
isPublicKeyLike(v.publicKey));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Accepts an {@link Address} string or a v1 `PublicKey` and returns
|
|
30
|
+
* a kit `Address`.
|
|
31
|
+
*/
|
|
32
|
+
export function toAddress(input) {
|
|
33
|
+
if (typeof input === "string") {
|
|
34
|
+
return address(input);
|
|
35
|
+
}
|
|
36
|
+
if (isPublicKeyLike(input)) {
|
|
37
|
+
if (!warnedPublicKey) {
|
|
38
|
+
logger.warning("Passing a @solana/web3.js PublicKey is deprecated — " +
|
|
39
|
+
"use a plain address string or @solana/kit address() " +
|
|
40
|
+
"instead. v1 compatibility will be removed in a " +
|
|
41
|
+
"future release.");
|
|
42
|
+
warnedPublicKey = true;
|
|
43
|
+
}
|
|
44
|
+
return address(input.toBase58());
|
|
45
|
+
}
|
|
46
|
+
throw new TypeError("expected an Address string or PublicKey");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Accepts a kit `KeyPairSigner`, a 64-byte secret key, or a v1
|
|
50
|
+
* `Keypair` and returns a `KeyPairSigner`.
|
|
51
|
+
*/
|
|
52
|
+
export async function toKeyPairSigner(input) {
|
|
53
|
+
// Already a KeyPairSigner (has signMessages method)
|
|
54
|
+
if (typeof input === "object" &&
|
|
55
|
+
input !== null &&
|
|
56
|
+
"address" in input &&
|
|
57
|
+
"signMessages" in input) {
|
|
58
|
+
return input;
|
|
59
|
+
}
|
|
60
|
+
// Raw secret key bytes
|
|
61
|
+
if (input instanceof Uint8Array) {
|
|
62
|
+
return createKeyPairSignerFromBytes(input);
|
|
63
|
+
}
|
|
64
|
+
// v1 Keypair duck-type
|
|
65
|
+
if (isKeypairLike(input)) {
|
|
66
|
+
if (!warnedKeypair) {
|
|
67
|
+
logger.warning("Passing a @solana/web3.js Keypair is deprecated — " +
|
|
68
|
+
"use a Uint8Array secret key or @solana/kit " +
|
|
69
|
+
"KeyPairSigner instead. v1 compatibility will be " +
|
|
70
|
+
"removed in a future release.");
|
|
71
|
+
warnedKeypair = true;
|
|
72
|
+
}
|
|
73
|
+
return createKeyPairSignerFromBytes(input.secretKey);
|
|
74
|
+
}
|
|
75
|
+
throw new TypeError("expected a Uint8Array, KeyPairSigner, or Keypair");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Accepts an `Rpc<SolanaRpcApi>` or a URL string and returns
|
|
79
|
+
* an `Rpc<SolanaRpcApi>`.
|
|
80
|
+
*/
|
|
81
|
+
export function toRpc(input) {
|
|
82
|
+
if (typeof input === "string") {
|
|
83
|
+
return createSolanaRpc(input);
|
|
84
|
+
}
|
|
85
|
+
return input;
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compat.test.d.ts","sourceRoot":"","sources":["../../src/compat.test.ts"],"names":[],"mappings":""}
|