@faremeter/payment-solana 0.19.0 → 0.20.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/README.md +70 -0
- package/dist/src/charge/client.d.ts +18 -0
- package/dist/src/charge/client.d.ts.map +1 -0
- package/dist/src/charge/client.js +205 -0
- package/dist/src/charge/common.d.ts +44 -0
- package/dist/src/charge/common.d.ts.map +1 -0
- package/dist/src/charge/common.js +29 -0
- package/dist/src/charge/index.d.ts +7 -0
- package/dist/src/charge/index.d.ts.map +1 -0
- package/dist/src/charge/index.js +3 -0
- package/dist/src/charge/logger.d.ts +2 -0
- package/dist/src/charge/logger.d.ts.map +1 -0
- package/dist/src/charge/logger.js +2 -0
- package/dist/src/charge/replay.d.ts +12 -0
- package/dist/src/charge/replay.d.ts.map +1 -0
- package/dist/src/charge/replay.js +24 -0
- package/dist/src/charge/server.d.ts +31 -0
- package/dist/src/charge/server.d.ts.map +1 -0
- package/dist/src/charge/server.js +395 -0
- package/dist/src/charge/verify.d.ts +40 -0
- package/dist/src/charge/verify.d.ts.map +1 -0
- package/dist/src/charge/verify.js +185 -0
- package/dist/src/common.d.ts +12 -0
- package/dist/src/common.d.ts.map +1 -0
- package/dist/src/common.js +1 -0
- package/dist/src/exact/client.d.ts.map +1 -1
- package/dist/src/exact/client.js +5 -2
- package/dist/src/exact/facilitator.d.ts +1 -0
- package/dist/src/exact/facilitator.d.ts.map +1 -1
- package/dist/src/exact/facilitator.js +6 -0
- package/dist/src/exact/index.d.ts +1 -1
- package/dist/src/exact/index.d.ts.map +1 -1
- package/dist/src/exact/memo.d.ts +4 -0
- package/dist/src/exact/memo.d.ts.map +1 -0
- package/dist/src/exact/memo.js +16 -0
- package/dist/src/exact/verify.d.ts +2 -1
- package/dist/src/exact/verify.d.ts.map +1 -1
- package/dist/src/exact/verify.js +48 -4
- package/dist/src/exact/verify.test.js +155 -16
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -15
package/README.md
CHANGED
|
@@ -20,6 +20,76 @@ pnpm install @faremeter/payment-solana
|
|
|
20
20
|
|
|
21
21
|
<!-- TSDOC_START -->
|
|
22
22
|
|
|
23
|
+
## Functions
|
|
24
|
+
|
|
25
|
+
- [isAccountNotFoundError](#isaccountnotfounderror)
|
|
26
|
+
- [getTokenBalance](#gettokenbalance)
|
|
27
|
+
|
|
28
|
+
### isAccountNotFoundError
|
|
29
|
+
|
|
30
|
+
Checks if an error indicates a token account was not found.
|
|
31
|
+
|
|
32
|
+
This handles various error formats from Solana RPC responses,
|
|
33
|
+
including TokenAccountNotFoundError and AccountNotFoundError names,
|
|
34
|
+
as well as message-based detection.
|
|
35
|
+
|
|
36
|
+
| Function | Type |
|
|
37
|
+
| ------------------------ | ------------------------- |
|
|
38
|
+
| `isAccountNotFoundError` | `(e: unknown) => boolean` |
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
|
|
42
|
+
- `e`: - The error to check
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
|
|
46
|
+
True if the error indicates the account does not exist
|
|
47
|
+
|
|
48
|
+
### getTokenBalance
|
|
49
|
+
|
|
50
|
+
Retrieves the SPL token balance for an account.
|
|
51
|
+
|
|
52
|
+
Looks up the associated token account (ATA) for the given wallet and
|
|
53
|
+
mint, then fetches the token balance. Returns null if the account
|
|
54
|
+
does not exist.
|
|
55
|
+
|
|
56
|
+
| Function | Type |
|
|
57
|
+
| ----------------- | ------------------------------------------------------------------------------------ |
|
|
58
|
+
| `getTokenBalance` | `(args: GetTokenBalanceArgs) => Promise<{ amount: bigint; decimals: any; } or null>` |
|
|
59
|
+
|
|
60
|
+
Parameters:
|
|
61
|
+
|
|
62
|
+
- `args`: - The asset, account, and RPC client
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
|
|
66
|
+
The balance amount and decimals, or null if the account does not exist
|
|
67
|
+
|
|
68
|
+
## Constants
|
|
69
|
+
|
|
70
|
+
- [TOKEN_2022_PROGRAM_ADDRESS](#token_2022_program_address)
|
|
71
|
+
|
|
72
|
+
### TOKEN_2022_PROGRAM_ADDRESS
|
|
73
|
+
|
|
74
|
+
| Constant | Type |
|
|
75
|
+
| ---------------------------- | -------------------------------------------------------- |
|
|
76
|
+
| `TOKEN_2022_PROGRAM_ADDRESS` | `Address<"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb">` |
|
|
77
|
+
|
|
78
|
+
## Interfaces
|
|
79
|
+
|
|
80
|
+
- [GetTokenBalanceArgs](#gettokenbalanceargs)
|
|
81
|
+
|
|
82
|
+
### GetTokenBalanceArgs
|
|
83
|
+
|
|
84
|
+
Arguments for retrieving an SPL token balance.
|
|
85
|
+
|
|
86
|
+
| Property | Type | Description |
|
|
87
|
+
| -------------- | -------------------------------- | ------------------------------------------------------------- |
|
|
88
|
+
| `asset` | `Base58Address` | The SPL token mint address |
|
|
89
|
+
| `account` | `Base58Address` | The wallet address to check the balance for |
|
|
90
|
+
| `rpcClient` | `Rpc<GetTokenAccountBalanceApi>` | Solana RPC client with token balance API support |
|
|
91
|
+
| `tokenProgram` | `Address or undefined` | The token program address (defaults to TOKEN_PROGRAM_ADDRESS) |
|
|
92
|
+
|
|
23
93
|
<!-- TSDOC_END -->
|
|
24
94
|
|
|
25
95
|
## Related Packages
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MPPPaymentHandler } from "@faremeter/types/mpp";
|
|
2
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
3
|
+
import type { Wallet } from "../exact/index.js";
|
|
4
|
+
export type CreateMPPSolanaChargeClientArgs = {
|
|
5
|
+
wallet: Wallet;
|
|
6
|
+
mint: PublicKey;
|
|
7
|
+
connection?: Connection;
|
|
8
|
+
tokenProgramId?: PublicKey;
|
|
9
|
+
broadcast?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function createMPPSolanaChargeClient(args: CreateMPPSolanaChargeClientArgs): MPPPaymentHandler;
|
|
12
|
+
export type CreateMPPSolanaNativeChargeClientArgs = {
|
|
13
|
+
wallet: Wallet;
|
|
14
|
+
connection?: Connection;
|
|
15
|
+
broadcast?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare function createMPPSolanaNativeChargeClient(args: CreateMPPSolanaNativeChargeClientArgs): MPPPaymentHandler;
|
|
18
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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;AAS9B,OAAO,EAEL,UAAU,EACV,SAAS,EAIV,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAqCvC,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,+BAA+B,GACpC,iBAAiB,CAoInB;AAED,MAAM,MAAM,qCAAqC,GAAG;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,qCAAqC,GAC1C,iBAAiB,CAmGnB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { decodeBase64URL } from "@faremeter/types/mpp";
|
|
2
|
+
import { isValidationError } from "@faremeter/types";
|
|
3
|
+
import { createTransferCheckedInstruction, getAssociatedTokenAddressSync, getMint, TOKEN_PROGRAM_ID, } from "@solana/spl-token";
|
|
4
|
+
import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction, } from "@solana/web3.js";
|
|
5
|
+
import { mppChargeRequest } from "./common.js";
|
|
6
|
+
async function broadcastAndConfirm(tx, wallet, connection, challenge, md, recentBlockhash) {
|
|
7
|
+
if (md?.feePayer) {
|
|
8
|
+
throw new Error("push mode is not allowed with fee sponsorship");
|
|
9
|
+
}
|
|
10
|
+
const { lastValidBlockHeight } = await connection.getLatestBlockhash({
|
|
11
|
+
commitment: "confirmed",
|
|
12
|
+
});
|
|
13
|
+
let signature;
|
|
14
|
+
if (wallet.sendTransaction) {
|
|
15
|
+
signature = await wallet.sendTransaction(tx);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
signature = await connection.sendRawTransaction(tx.serialize());
|
|
19
|
+
}
|
|
20
|
+
await connection.confirmTransaction({ signature, blockhash: recentBlockhash, lastValidBlockHeight }, "confirmed");
|
|
21
|
+
return {
|
|
22
|
+
challenge,
|
|
23
|
+
payload: { type: "signature", signature },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function createMPPSolanaChargeClient(args) {
|
|
27
|
+
const { wallet, mint, connection, broadcast = false } = args;
|
|
28
|
+
if (broadcast && !connection) {
|
|
29
|
+
throw new Error("connection is required when broadcast is true");
|
|
30
|
+
}
|
|
31
|
+
return async (challenge) => {
|
|
32
|
+
if (challenge.method !== "solana")
|
|
33
|
+
return null;
|
|
34
|
+
if (challenge.intent !== "charge")
|
|
35
|
+
return null;
|
|
36
|
+
let requestBody;
|
|
37
|
+
try {
|
|
38
|
+
requestBody = JSON.parse(decodeBase64URL(challenge.request));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const request = mppChargeRequest(requestBody);
|
|
44
|
+
if (isValidationError(request))
|
|
45
|
+
return null;
|
|
46
|
+
if (request.currency === "sol")
|
|
47
|
+
return null;
|
|
48
|
+
return {
|
|
49
|
+
challenge,
|
|
50
|
+
exec: async () => {
|
|
51
|
+
const md = request.methodDetails;
|
|
52
|
+
const amount = BigInt(request.amount);
|
|
53
|
+
const recipientKey = new PublicKey(request.recipient);
|
|
54
|
+
const feePayerKey = md?.feePayer === true ? md.feePayerKey : undefined;
|
|
55
|
+
let recentBlockhash;
|
|
56
|
+
if (md?.recentBlockhash) {
|
|
57
|
+
recentBlockhash = md.recentBlockhash;
|
|
58
|
+
}
|
|
59
|
+
else if (connection) {
|
|
60
|
+
recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
throw new Error("no blockhash available");
|
|
64
|
+
}
|
|
65
|
+
let decimals;
|
|
66
|
+
if (md?.decimals !== undefined) {
|
|
67
|
+
decimals = md.decimals;
|
|
68
|
+
}
|
|
69
|
+
else if (connection) {
|
|
70
|
+
const mintInfo = await getMint(connection, mint);
|
|
71
|
+
decimals = mintInfo.decimals;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
throw new Error("no decimals available");
|
|
75
|
+
}
|
|
76
|
+
const tokenProgramId = md?.tokenProgram
|
|
77
|
+
? new PublicKey(md.tokenProgram)
|
|
78
|
+
: (args.tokenProgramId ?? TOKEN_PROGRAM_ID);
|
|
79
|
+
const sourceAccount = getAssociatedTokenAddressSync(mint, wallet.publicKey, false, tokenProgramId);
|
|
80
|
+
const receiverAccount = getAssociatedTokenAddressSync(mint, recipientKey, false, tokenProgramId);
|
|
81
|
+
const instructions = [
|
|
82
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
83
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }),
|
|
84
|
+
createTransferCheckedInstruction(sourceAccount, mint, receiverAccount, wallet.publicKey, amount, decimals, undefined, tokenProgramId),
|
|
85
|
+
];
|
|
86
|
+
const payerKey = feePayerKey
|
|
87
|
+
? new PublicKey(feePayerKey)
|
|
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
|
+
}
|
|
104
|
+
if (broadcast) {
|
|
105
|
+
if (!connection)
|
|
106
|
+
throw new Error("connection is required");
|
|
107
|
+
return broadcastAndConfirm(tx, wallet, connection, challenge, md, recentBlockhash);
|
|
108
|
+
}
|
|
109
|
+
const wireBytes = tx.serialize();
|
|
110
|
+
const base64Transaction = btoa(String.fromCharCode.apply(null, [...wireBytes]));
|
|
111
|
+
return {
|
|
112
|
+
challenge,
|
|
113
|
+
payload: {
|
|
114
|
+
type: "transaction",
|
|
115
|
+
transaction: base64Transaction,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function createMPPSolanaNativeChargeClient(args) {
|
|
123
|
+
const { wallet, connection, broadcast = false } = args;
|
|
124
|
+
if (broadcast && !connection) {
|
|
125
|
+
throw new Error("connection is required when broadcast is true");
|
|
126
|
+
}
|
|
127
|
+
return async (challenge) => {
|
|
128
|
+
if (challenge.method !== "solana")
|
|
129
|
+
return null;
|
|
130
|
+
if (challenge.intent !== "charge")
|
|
131
|
+
return null;
|
|
132
|
+
let requestBody;
|
|
133
|
+
try {
|
|
134
|
+
requestBody = JSON.parse(decodeBase64URL(challenge.request));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const request = mppChargeRequest(requestBody);
|
|
140
|
+
if (isValidationError(request))
|
|
141
|
+
return null;
|
|
142
|
+
if (request.currency !== "sol")
|
|
143
|
+
return null;
|
|
144
|
+
return {
|
|
145
|
+
challenge,
|
|
146
|
+
exec: async () => {
|
|
147
|
+
const md = request.methodDetails;
|
|
148
|
+
const amount = BigInt(request.amount);
|
|
149
|
+
const recipientKey = new PublicKey(request.recipient);
|
|
150
|
+
const feePayerKey = md?.feePayer === true ? md.feePayerKey : undefined;
|
|
151
|
+
let recentBlockhash;
|
|
152
|
+
if (md?.recentBlockhash) {
|
|
153
|
+
recentBlockhash = md.recentBlockhash;
|
|
154
|
+
}
|
|
155
|
+
else if (connection) {
|
|
156
|
+
recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
throw new Error("no blockhash available");
|
|
160
|
+
}
|
|
161
|
+
const instructions = [
|
|
162
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
|
|
163
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 }),
|
|
164
|
+
SystemProgram.transfer({
|
|
165
|
+
fromPubkey: wallet.publicKey,
|
|
166
|
+
toPubkey: recipientKey,
|
|
167
|
+
lamports: amount,
|
|
168
|
+
}),
|
|
169
|
+
];
|
|
170
|
+
const payerKey = feePayerKey
|
|
171
|
+
? new PublicKey(feePayerKey)
|
|
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
|
+
}
|
|
188
|
+
if (broadcast) {
|
|
189
|
+
if (!connection)
|
|
190
|
+
throw new Error("connection is required");
|
|
191
|
+
return broadcastAndConfirm(tx, wallet, connection, challenge, md, recentBlockhash);
|
|
192
|
+
}
|
|
193
|
+
const wireBytes = tx.serialize();
|
|
194
|
+
const base64Transaction = btoa(String.fromCharCode.apply(null, [...wireBytes]));
|
|
195
|
+
return {
|
|
196
|
+
challenge,
|
|
197
|
+
payload: {
|
|
198
|
+
type: "transaction",
|
|
199
|
+
transaction: base64Transaction,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const solanaChargeMethodDetails: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
2
|
+
network?: string;
|
|
3
|
+
decimals?: number;
|
|
4
|
+
tokenProgram?: string;
|
|
5
|
+
feePayer?: boolean;
|
|
6
|
+
feePayerKey?: string;
|
|
7
|
+
recentBlockhash?: string;
|
|
8
|
+
splits?: {
|
|
9
|
+
recipient: string;
|
|
10
|
+
amount: string;
|
|
11
|
+
memo?: string;
|
|
12
|
+
}[];
|
|
13
|
+
}, {}>;
|
|
14
|
+
export type solanaChargeMethodDetails = typeof solanaChargeMethodDetails.infer;
|
|
15
|
+
export declare const mppChargeRequest: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
16
|
+
amount: string;
|
|
17
|
+
currency: string;
|
|
18
|
+
recipient: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
externalId?: string;
|
|
21
|
+
methodDetails?: {
|
|
22
|
+
network?: string;
|
|
23
|
+
decimals?: number;
|
|
24
|
+
tokenProgram?: string;
|
|
25
|
+
feePayer?: boolean;
|
|
26
|
+
feePayerKey?: string;
|
|
27
|
+
recentBlockhash?: string;
|
|
28
|
+
splits?: {
|
|
29
|
+
recipient: string;
|
|
30
|
+
amount: string;
|
|
31
|
+
memo?: string;
|
|
32
|
+
}[];
|
|
33
|
+
};
|
|
34
|
+
}, {}>;
|
|
35
|
+
export type mppChargeRequest = typeof mppChargeRequest.infer;
|
|
36
|
+
export declare const chargeCredentialPayload: import("arktype/internal/methods/object.ts").ObjectType<{
|
|
37
|
+
type: "transaction";
|
|
38
|
+
transaction: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "signature";
|
|
41
|
+
signature: string;
|
|
42
|
+
}, {}>;
|
|
43
|
+
export type chargeCredentialPayload = typeof chargeCredentialPayload.infer;
|
|
44
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/charge/common.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,yBAAyB;;;;;;;;;;;;MAYpC,CAAC;AAEH,MAAM,MAAM,yBAAyB,GAAG,OAAO,yBAAyB,CAAC,KAAK,CAAC;AAE/E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;MAO3B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,KAAK,CAAC;AAE7D,eAAO,MAAM,uBAAuB;;;;;;MAUnC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,OAAO,uBAAuB,CAAC,KAAK,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
export const solanaChargeMethodDetails = type({
|
|
3
|
+
"network?": "string",
|
|
4
|
+
"decimals?": "number",
|
|
5
|
+
"tokenProgram?": "string",
|
|
6
|
+
"feePayer?": "boolean",
|
|
7
|
+
"feePayerKey?": "string",
|
|
8
|
+
"recentBlockhash?": "string",
|
|
9
|
+
"splits?": type({
|
|
10
|
+
recipient: "string",
|
|
11
|
+
amount: "string",
|
|
12
|
+
"memo?": "string",
|
|
13
|
+
}).array(),
|
|
14
|
+
});
|
|
15
|
+
export const mppChargeRequest = type({
|
|
16
|
+
amount: "string.numeric",
|
|
17
|
+
currency: "string",
|
|
18
|
+
recipient: "string",
|
|
19
|
+
"description?": "string",
|
|
20
|
+
"externalId?": "string",
|
|
21
|
+
"methodDetails?": solanaChargeMethodDetails,
|
|
22
|
+
});
|
|
23
|
+
export const chargeCredentialPayload = type({
|
|
24
|
+
type: "'transaction'",
|
|
25
|
+
transaction: "string",
|
|
26
|
+
}, "|", {
|
|
27
|
+
type: "'signature'",
|
|
28
|
+
signature: "string",
|
|
29
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createMPPSolanaChargeHandler, createMPPSolanaNativeChargeHandler, } from "./server.js";
|
|
2
|
+
export type { CreateMPPSolanaChargeHandlerArgs, CreateMPPSolanaNativeChargeHandlerArgs, } from "./server.js";
|
|
3
|
+
export { createMPPSolanaChargeClient, createMPPSolanaNativeChargeClient, } from "./client.js";
|
|
4
|
+
export type { CreateMPPSolanaChargeClientArgs, CreateMPPSolanaNativeChargeClientArgs, } from "./client.js";
|
|
5
|
+
export { createInMemoryReplayStore } from "./replay.js";
|
|
6
|
+
export type { ReplayStore } from "./replay.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/charge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,kCAAkC,GACnC,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,gCAAgC,EAChC,sCAAsC,GACvC,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,+BAA+B,EAC/B,qCAAqC,GACtC,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/charge/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,kCAA0D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay protection store for MPP challenge IDs.
|
|
3
|
+
*
|
|
4
|
+
* `consume` atomically checks whether an ID is valid and marks it as
|
|
5
|
+
* used, preventing TOCTOU races in concurrent settlement attempts.
|
|
6
|
+
*/
|
|
7
|
+
export interface ReplayStore {
|
|
8
|
+
consume(id: string): Promise<boolean>;
|
|
9
|
+
add(id: string, expiresAt?: number): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createInMemoryReplayStore(): ReplayStore;
|
|
12
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../../src/charge/replay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,wBAAgB,yBAAyB,IAAI,WAAW,CAwBvD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function createInMemoryReplayStore() {
|
|
2
|
+
const store = new Map();
|
|
3
|
+
function prune() {
|
|
4
|
+
const now = Date.now();
|
|
5
|
+
for (const [id, expiresAt] of store) {
|
|
6
|
+
if (expiresAt > 0 && expiresAt <= now) {
|
|
7
|
+
store.delete(id);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
async add(id, expiresAt) {
|
|
13
|
+
prune();
|
|
14
|
+
store.set(id, expiresAt ?? 0);
|
|
15
|
+
},
|
|
16
|
+
async consume(id) {
|
|
17
|
+
prune();
|
|
18
|
+
if (!store.has(id))
|
|
19
|
+
return false;
|
|
20
|
+
store.delete(id);
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MPPMethodHandler } from "@faremeter/types/mpp";
|
|
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";
|
|
5
|
+
import type { ReplayStore } from "./replay.js";
|
|
6
|
+
export type CreateMPPSolanaChargeHandlerArgs = {
|
|
7
|
+
network: string | SolanaCAIP2Network;
|
|
8
|
+
rpc: Rpc<SolanaRpcApi>;
|
|
9
|
+
feePayerKeypair?: Keypair;
|
|
10
|
+
mint: PublicKey;
|
|
11
|
+
replayStore: ReplayStore;
|
|
12
|
+
realm: string;
|
|
13
|
+
secretKey: Uint8Array;
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
retryDelayMs?: number;
|
|
16
|
+
maxPriorityFee?: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function createMPPSolanaChargeHandler(args: CreateMPPSolanaChargeHandlerArgs): Promise<MPPMethodHandler>;
|
|
19
|
+
export type CreateMPPSolanaNativeChargeHandlerArgs = {
|
|
20
|
+
network: string | SolanaCAIP2Network;
|
|
21
|
+
rpc: Rpc<SolanaRpcApi>;
|
|
22
|
+
feePayerKeypair?: Keypair;
|
|
23
|
+
replayStore: ReplayStore;
|
|
24
|
+
realm: string;
|
|
25
|
+
secretKey: Uint8Array;
|
|
26
|
+
maxRetries?: number;
|
|
27
|
+
retryDelayMs?: number;
|
|
28
|
+
maxPriorityFee?: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function createMPPSolanaNativeChargeHandler(args: CreateMPPSolanaNativeChargeHandlerArgs): Promise<MPPMethodHandler>;
|
|
31
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +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,EAKL,KAAK,GAAG,EAER,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,OAAO,EAAoB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAiD5C,MAAM,MAAM,gCAAgC,GAAG;IAC7C,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,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,CAoN3B;AAID,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,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,CA6M3B"}
|