@pepu-chain/sdk 0.0.0-stage → 0.2.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.
Files changed (232) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +565 -2
  3. package/dist/aa/kernel.cjs +40 -0
  4. package/dist/aa/kernel.cjs.map +1 -0
  5. package/dist/aa/kernel.d.cts +88 -0
  6. package/dist/aa/kernel.d.ts +88 -0
  7. package/dist/aa/kernel.js +11 -0
  8. package/dist/aa/kernel.js.map +1 -0
  9. package/dist/aa/react-direct.cjs +110 -0
  10. package/dist/aa/react-direct.cjs.map +1 -0
  11. package/dist/aa/react-direct.d.cts +42 -0
  12. package/dist/aa/react-direct.d.ts +42 -0
  13. package/dist/aa/react-direct.js +107 -0
  14. package/dist/aa/react-direct.js.map +1 -0
  15. package/dist/aa/react.cjs +151 -0
  16. package/dist/aa/react.cjs.map +1 -0
  17. package/dist/aa/react.d.cts +62 -0
  18. package/dist/aa/react.d.ts +62 -0
  19. package/dist/aa/react.js +147 -0
  20. package/dist/aa/react.js.map +1 -0
  21. package/dist/aa.cjs +84 -0
  22. package/dist/aa.cjs.map +1 -0
  23. package/dist/aa.d.cts +85 -0
  24. package/dist/aa.d.ts +85 -0
  25. package/dist/aa.js +11 -0
  26. package/dist/aa.js.map +1 -0
  27. package/dist/api-Dz4sCHH_.d.cts +654 -0
  28. package/dist/api-Dz4sCHH_.d.ts +654 -0
  29. package/dist/calls-BRPjKgjl.d.cts +70 -0
  30. package/dist/calls-z_EQOtFb.d.ts +70 -0
  31. package/dist/chains-DK13zGIp.d.cts +235 -0
  32. package/dist/chains-DK13zGIp.d.ts +235 -0
  33. package/dist/chunk-3XUGQODR.js +194 -0
  34. package/dist/chunk-3XUGQODR.js.map +1 -0
  35. package/dist/chunk-45KSZH26.cjs +145 -0
  36. package/dist/chunk-45KSZH26.cjs.map +1 -0
  37. package/dist/chunk-4GINFJO6.cjs +44 -0
  38. package/dist/chunk-4GINFJO6.cjs.map +1 -0
  39. package/dist/chunk-62M2LKQW.js +136 -0
  40. package/dist/chunk-62M2LKQW.js.map +1 -0
  41. package/dist/chunk-6DSZ4Z2N.js +498 -0
  42. package/dist/chunk-6DSZ4Z2N.js.map +1 -0
  43. package/dist/chunk-7MNJF2OB.cjs +72 -0
  44. package/dist/chunk-7MNJF2OB.cjs.map +1 -0
  45. package/dist/chunk-A5LP2UCS.cjs +2729 -0
  46. package/dist/chunk-A5LP2UCS.cjs.map +1 -0
  47. package/dist/chunk-AJS4UFQX.cjs +121 -0
  48. package/dist/chunk-AJS4UFQX.cjs.map +1 -0
  49. package/dist/chunk-C2XW44FI.cjs +200 -0
  50. package/dist/chunk-C2XW44FI.cjs.map +1 -0
  51. package/dist/chunk-C7IC36XS.cjs +13 -0
  52. package/dist/chunk-C7IC36XS.cjs.map +1 -0
  53. package/dist/chunk-D52PADVY.js +84 -0
  54. package/dist/chunk-D52PADVY.js.map +1 -0
  55. package/dist/chunk-DH6BFNVJ.cjs +272 -0
  56. package/dist/chunk-DH6BFNVJ.cjs.map +1 -0
  57. package/dist/chunk-DPCZJLB3.cjs +87 -0
  58. package/dist/chunk-DPCZJLB3.cjs.map +1 -0
  59. package/dist/chunk-EHBPIYY4.js +262 -0
  60. package/dist/chunk-EHBPIYY4.js.map +1 -0
  61. package/dist/chunk-ESMNAXUI.js +86 -0
  62. package/dist/chunk-ESMNAXUI.js.map +1 -0
  63. package/dist/chunk-ET3P6TX6.cjs +49 -0
  64. package/dist/chunk-ET3P6TX6.cjs.map +1 -0
  65. package/dist/chunk-FCXSP2YF.js +219 -0
  66. package/dist/chunk-FCXSP2YF.js.map +1 -0
  67. package/dist/chunk-IB2FSPMJ.js +66 -0
  68. package/dist/chunk-IB2FSPMJ.js.map +1 -0
  69. package/dist/chunk-IG7RSL2Y.cjs +178 -0
  70. package/dist/chunk-IG7RSL2Y.cjs.map +1 -0
  71. package/dist/chunk-IY3ALPUJ.js +2716 -0
  72. package/dist/chunk-IY3ALPUJ.js.map +1 -0
  73. package/dist/chunk-J3O2ZGG3.cjs +93 -0
  74. package/dist/chunk-J3O2ZGG3.cjs.map +1 -0
  75. package/dist/chunk-J6UXXHBE.js +37 -0
  76. package/dist/chunk-J6UXXHBE.js.map +1 -0
  77. package/dist/chunk-JLZ4LZ4Z.js +22 -0
  78. package/dist/chunk-JLZ4LZ4Z.js.map +1 -0
  79. package/dist/chunk-KIIFL4VM.js +274 -0
  80. package/dist/chunk-KIIFL4VM.js.map +1 -0
  81. package/dist/chunk-KVG3AYYR.js +99 -0
  82. package/dist/chunk-KVG3AYYR.js.map +1 -0
  83. package/dist/chunk-MBA4CLA5.js +45 -0
  84. package/dist/chunk-MBA4CLA5.js.map +1 -0
  85. package/dist/chunk-NE3QKDOB.cjs +24 -0
  86. package/dist/chunk-NE3QKDOB.cjs.map +1 -0
  87. package/dist/chunk-NVOPDXTA.cjs +95 -0
  88. package/dist/chunk-NVOPDXTA.cjs.map +1 -0
  89. package/dist/chunk-OH3QMAIB.js +11 -0
  90. package/dist/chunk-OH3QMAIB.js.map +1 -0
  91. package/dist/chunk-PHM3ZORG.cjs +110 -0
  92. package/dist/chunk-PHM3ZORG.cjs.map +1 -0
  93. package/dist/chunk-R5PXQNO3.cjs +209 -0
  94. package/dist/chunk-R5PXQNO3.cjs.map +1 -0
  95. package/dist/chunk-SLW7FRTL.cjs +285 -0
  96. package/dist/chunk-SLW7FRTL.cjs.map +1 -0
  97. package/dist/chunk-SVWRRALU.cjs +17 -0
  98. package/dist/chunk-SVWRRALU.cjs.map +1 -0
  99. package/dist/chunk-T43PQXJD.js +202 -0
  100. package/dist/chunk-T43PQXJD.js.map +1 -0
  101. package/dist/chunk-TIEAZYHF.cjs +225 -0
  102. package/dist/chunk-TIEAZYHF.cjs.map +1 -0
  103. package/dist/chunk-VBH3JLQ5.js +116 -0
  104. package/dist/chunk-VBH3JLQ5.js.map +1 -0
  105. package/dist/chunk-VF3D6KLQ.cjs +94 -0
  106. package/dist/chunk-VF3D6KLQ.cjs.map +1 -0
  107. package/dist/chunk-VTQIHMF3.js +82 -0
  108. package/dist/chunk-VTQIHMF3.js.map +1 -0
  109. package/dist/chunk-W3JN3YIW.js +15 -0
  110. package/dist/chunk-W3JN3YIW.js.map +1 -0
  111. package/dist/chunk-WANILJZQ.cjs +87 -0
  112. package/dist/chunk-WANILJZQ.cjs.map +1 -0
  113. package/dist/chunk-XC4IBCI2.js +86 -0
  114. package/dist/chunk-XC4IBCI2.js.map +1 -0
  115. package/dist/chunk-YKEWWQEJ.js +165 -0
  116. package/dist/chunk-YKEWWQEJ.js.map +1 -0
  117. package/dist/chunk-YQL7NISX.cjs +515 -0
  118. package/dist/chunk-YQL7NISX.cjs.map +1 -0
  119. package/dist/chunk-ZB6SVBK4.js +89 -0
  120. package/dist/chunk-ZB6SVBK4.js.map +1 -0
  121. package/dist/combo/react.cjs +235 -0
  122. package/dist/combo/react.cjs.map +1 -0
  123. package/dist/combo/react.d.cts +148 -0
  124. package/dist/combo/react.d.ts +148 -0
  125. package/dist/combo/react.js +230 -0
  126. package/dist/combo/react.js.map +1 -0
  127. package/dist/combo.cjs +262 -0
  128. package/dist/combo.cjs.map +1 -0
  129. package/dist/combo.d.cts +139 -0
  130. package/dist/combo.d.ts +139 -0
  131. package/dist/combo.js +249 -0
  132. package/dist/combo.js.map +1 -0
  133. package/dist/config-CHjz7ZjZ.d.cts +57 -0
  134. package/dist/config-COjhIZTh.d.ts +57 -0
  135. package/dist/doctor.cjs +354 -0
  136. package/dist/doctor.cjs.map +1 -0
  137. package/dist/doctor.d.cts +177 -0
  138. package/dist/doctor.d.ts +177 -0
  139. package/dist/doctor.js +345 -0
  140. package/dist/doctor.js.map +1 -0
  141. package/dist/erc7677-DOW58QBE.d.cts +111 -0
  142. package/dist/erc7677-DOW58QBE.d.ts +111 -0
  143. package/dist/errors-CgX1vNj7.d.cts +100 -0
  144. package/dist/errors-CgX1vNj7.d.ts +100 -0
  145. package/dist/index.cjs +802 -0
  146. package/dist/index.cjs.map +1 -0
  147. package/dist/index.d.cts +2398 -0
  148. package/dist/index.d.ts +2398 -0
  149. package/dist/index.js +559 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/paymaster/server.cjs +38 -0
  152. package/dist/paymaster/server.cjs.map +1 -0
  153. package/dist/paymaster/server.d.cts +57 -0
  154. package/dist/paymaster/server.d.ts +57 -0
  155. package/dist/paymaster/server.js +36 -0
  156. package/dist/paymaster/server.js.map +1 -0
  157. package/dist/paymaster.cjs +356 -0
  158. package/dist/paymaster.cjs.map +1 -0
  159. package/dist/paymaster.d.cts +1851 -0
  160. package/dist/paymaster.d.ts +1851 -0
  161. package/dist/paymaster.js +289 -0
  162. package/dist/paymaster.js.map +1 -0
  163. package/dist/privy/react.cjs +48 -0
  164. package/dist/privy/react.cjs.map +1 -0
  165. package/dist/privy/react.d.cts +194 -0
  166. package/dist/privy/react.d.ts +194 -0
  167. package/dist/privy/react.js +7 -0
  168. package/dist/privy/react.js.map +1 -0
  169. package/dist/privy/server.cjs +43 -0
  170. package/dist/privy/server.cjs.map +1 -0
  171. package/dist/privy/server.d.cts +192 -0
  172. package/dist/privy/server.d.ts +192 -0
  173. package/dist/privy/server.js +6 -0
  174. package/dist/privy/server.js.map +1 -0
  175. package/dist/privy/wagmi.d.ts +90 -0
  176. package/dist/privy/wagmi.js +55 -0
  177. package/dist/privy/wagmi.js.map +1 -0
  178. package/dist/privy.cjs +34 -0
  179. package/dist/privy.cjs.map +1 -0
  180. package/dist/privy.d.cts +4 -0
  181. package/dist/privy.d.ts +4 -0
  182. package/dist/privy.js +5 -0
  183. package/dist/privy.js.map +1 -0
  184. package/dist/react.cjs +72 -0
  185. package/dist/react.cjs.map +1 -0
  186. package/dist/react.d.cts +66 -0
  187. package/dist/react.d.ts +66 -0
  188. package/dist/react.js +68 -0
  189. package/dist/react.js.map +1 -0
  190. package/dist/requirements-C-QiJkL9.d.cts +134 -0
  191. package/dist/requirements-C-QiJkL9.d.ts +134 -0
  192. package/dist/scope-DBRkN7bR.d.cts +94 -0
  193. package/dist/scope-DBRkN7bR.d.ts +94 -0
  194. package/dist/server.cjs +59 -0
  195. package/dist/server.cjs.map +1 -0
  196. package/dist/server.d.cts +208 -0
  197. package/dist/server.d.ts +208 -0
  198. package/dist/server.js +8 -0
  199. package/dist/server.js.map +1 -0
  200. package/dist/session/kernel.cjs +82 -0
  201. package/dist/session/kernel.cjs.map +1 -0
  202. package/dist/session/kernel.d.cts +131 -0
  203. package/dist/session/kernel.d.ts +131 -0
  204. package/dist/session/kernel.js +13 -0
  205. package/dist/session/kernel.js.map +1 -0
  206. package/dist/session/react.cjs +37 -0
  207. package/dist/session/react.cjs.map +1 -0
  208. package/dist/session/react.d.cts +119 -0
  209. package/dist/session/react.d.ts +119 -0
  210. package/dist/session/react.js +8 -0
  211. package/dist/session/react.js.map +1 -0
  212. package/dist/session/server.cjs +267 -0
  213. package/dist/session/server.cjs.map +1 -0
  214. package/dist/session/server.d.cts +151 -0
  215. package/dist/session/server.d.ts +151 -0
  216. package/dist/session/server.js +252 -0
  217. package/dist/session/server.js.map +1 -0
  218. package/dist/session.cjs +74 -0
  219. package/dist/session.cjs.map +1 -0
  220. package/dist/session.d.cts +3 -0
  221. package/dist/session.d.ts +3 -0
  222. package/dist/session.js +5 -0
  223. package/dist/session.js.map +1 -0
  224. package/dist/status-CmOsyPmO.d.cts +295 -0
  225. package/dist/status-DwkkOrGQ.d.ts +295 -0
  226. package/dist/store-CjmLgp9g.d.cts +64 -0
  227. package/dist/store-CjmLgp9g.d.ts +64 -0
  228. package/dist/types-BEEM7WdG.d.cts +25 -0
  229. package/dist/types-BEEM7WdG.d.ts +25 -0
  230. package/dist/types-BilBTDqK.d.cts +27 -0
  231. package/dist/types-BilBTDqK.d.ts +27 -0
  232. package/package.json +209 -3
@@ -0,0 +1,267 @@
1
+ 'use strict';
2
+
3
+ var chunkC7IC36XS_cjs = require('../chunk-C7IC36XS.cjs');
4
+ var chunk4GINFJO6_cjs = require('../chunk-4GINFJO6.cjs');
5
+ var chunkVF3D6KLQ_cjs = require('../chunk-VF3D6KLQ.cjs');
6
+ var chunkDPCZJLB3_cjs = require('../chunk-DPCZJLB3.cjs');
7
+ var chunk45KSZH26_cjs = require('../chunk-45KSZH26.cjs');
8
+ var chunkSVWRRALU_cjs = require('../chunk-SVWRRALU.cjs');
9
+ var chunkC2XW44FI_cjs = require('../chunk-C2XW44FI.cjs');
10
+ var chunkIG7RSL2Y_cjs = require('../chunk-IG7RSL2Y.cjs');
11
+ var chunkYQL7NISX_cjs = require('../chunk-YQL7NISX.cjs');
12
+ var viem = require('viem');
13
+
14
+ chunkSVWRRALU_cjs.assertServerOnly("@pepu-chain/sdk/session/server");
15
+ function isBlank(address) {
16
+ return address === void 0 || address === null || address.trim() === "";
17
+ }
18
+ function unconfiguredResponse(envName) {
19
+ return chunkYQL7NISX_cjs.toErrorResponse(
20
+ new chunkYQL7NISX_cjs.PepuConfigError(
21
+ "CONFIG_MISSING_ENV",
22
+ `${envName ?? "The session contract address"} is not set to a valid address. See .env.example.`,
23
+ { hint: envName ? `Set ${envName} and restart the server.` : "Set the contract address and restart the server." }
24
+ )
25
+ );
26
+ }
27
+ function missing(names) {
28
+ return new chunkYQL7NISX_cjs.PepuConfigError(
29
+ "CONFIG_MISSING_ENV",
30
+ `Missing required environment variable(s): ${names.join(", ")}.`
31
+ );
32
+ }
33
+ function resolvePrivy(o) {
34
+ return o.privy ?? chunk45KSZH26_cjs.createPrivyServer();
35
+ }
36
+ async function authenticate(request, privy, verifyUser) {
37
+ let result;
38
+ if (verifyUser) {
39
+ result = await verifyUser(request);
40
+ } else {
41
+ result = await chunk45KSZH26_cjs.verifyRequestUser(request, { privy });
42
+ }
43
+ const userId = typeof result === "string" ? result : result?.userId;
44
+ if (!userId) {
45
+ throw new chunkYQL7NISX_cjs.PepuAuthError("AUTH_INVALID_TOKEN", "Missing or invalid Privy access token.");
46
+ }
47
+ return userId;
48
+ }
49
+ async function findDelegated(privy, userId) {
50
+ const wallet = chunk45KSZH26_cjs.findDelegatedEthereumWallet(await privy.users()._get(userId));
51
+ return wallet ? { id: wallet.id ?? null, address: wallet.address } : null;
52
+ }
53
+ function validateScope(scope) {
54
+ chunkIG7RSL2Y_cjs.buildSessionPolicy({ ...scope, expiresAtUnixSeconds: 1 });
55
+ }
56
+ function json(body, status = 200) {
57
+ return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
58
+ }
59
+ function upstream(e, op) {
60
+ const err = chunkYQL7NISX_cjs.toPepuError(e, { op });
61
+ if (err.code === "UNKNOWN" && !(e instanceof chunkYQL7NISX_cjs.PepuError)) {
62
+ return new chunkYQL7NISX_cjs.PepuChainError("UNKNOWN", err.message, { status: 502, cause: e });
63
+ }
64
+ if (err.status === 409 && !err.code.startsWith("SESSION_")) {
65
+ return new chunkYQL7NISX_cjs.PepuChainError(err.code, err.message, { status: 502, cause: e });
66
+ }
67
+ return err;
68
+ }
69
+ async function guard(op, fn) {
70
+ try {
71
+ return await fn();
72
+ } catch (e) {
73
+ return chunkYQL7NISX_cjs.toErrorResponse(upstream(e, op));
74
+ }
75
+ }
76
+ function createSessionRoutes(o) {
77
+ if (isBlank(o.scope.contractAddress)) {
78
+ const unset = async () => unconfiguredResponse(o.contractAddressEnv);
79
+ return { grant: { POST: unset }, status: { GET: unset }, revoke: { POST: unset } };
80
+ }
81
+ const scope = o.scope;
82
+ validateScope(scope);
83
+ const scopeContract = scope.contractAddress.toLowerCase();
84
+ return {
85
+ grant: {
86
+ POST: (req) => guard("session grant", async () => {
87
+ const signerId = o.keyQuorumId ?? chunk45KSZH26_cjs.privyEnvVar("keyQuorumId");
88
+ if (!signerId) throw missing([chunkC2XW44FI_cjs.PRIVY_ENV.keyQuorumId]);
89
+ const privy = resolvePrivy(o);
90
+ const userId = await authenticate(req, privy, o.verifyUser);
91
+ try {
92
+ if (await findDelegated(privy, userId)) return json({ alreadyGranted: true });
93
+ } catch {
94
+ }
95
+ const minutes = o.minutes ?? chunkIG7RSL2Y_cjs.resolveSessionMinutes(process.env.SESSION_DURATION_MINUTES);
96
+ const expiresAt = chunkIG7RSL2Y_cjs.sessionExpiry(Math.floor(Date.now() / 1e3), minutes);
97
+ const policy = await privy.policies().create(
98
+ chunkIG7RSL2Y_cjs.buildSessionPolicy({ ...scope, expiresAtUnixSeconds: expiresAt })
99
+ );
100
+ return json({
101
+ alreadyGranted: false,
102
+ policyId: policy.id,
103
+ expiresAt,
104
+ signerId,
105
+ durationMinutes: minutes
106
+ });
107
+ })
108
+ },
109
+ status: {
110
+ GET: (req) => guard("session status", async () => {
111
+ const privy = resolvePrivy(o);
112
+ const userId = await authenticate(req, privy, o.verifyUser);
113
+ const wallet = await findDelegated(privy, userId);
114
+ return json({ delegated: wallet !== null, active: wallet !== null, address: wallet?.address ?? null });
115
+ })
116
+ },
117
+ revoke: {
118
+ POST: (req) => guard("session revoke", async () => {
119
+ const privy = resolvePrivy(o);
120
+ const userId = await authenticate(req, privy, o.verifyUser);
121
+ const wallet = await findDelegated(privy, userId);
122
+ if (wallet) {
123
+ return json({ revoked: false, delegated: true, policyDeleted: false });
124
+ }
125
+ let policyDeleted = false;
126
+ const body = await req.json().catch(() => null);
127
+ const policyId = typeof body?.policyId === "string" ? body.policyId : void 0;
128
+ if (policyId) {
129
+ try {
130
+ const policy = await privy.policies().get(policyId);
131
+ const scoped = policy.rules.every(
132
+ (r) => r.conditions.some(
133
+ (c) => c.field_source === "ethereum_transaction" && c.field === "to" && typeof c.value === "string" && c.value.toLowerCase() === scopeContract
134
+ )
135
+ );
136
+ if (scoped && policy.rules.length > 0) {
137
+ await privy.policies()._delete(policyId);
138
+ policyDeleted = true;
139
+ }
140
+ } catch {
141
+ }
142
+ }
143
+ return json({ revoked: true, delegated: false, policyDeleted });
144
+ })
145
+ }
146
+ };
147
+ }
148
+ function defaultClient() {
149
+ const { rpcUrl } = chunkVF3D6KLQ_cjs.resolvePepuEndpoints();
150
+ return chunkC7IC36XS_cjs.getPublicClient(chunkDPCZJLB3_cjs.pepuMainnet, { transport: viem.http(rpcUrl) });
151
+ }
152
+ function privySessionSigner(o) {
153
+ let client = o.client;
154
+ const signTransaction = async (tx) => {
155
+ const key = o.authorizationKey ?? chunk45KSZH26_cjs.privyEnvVar("authorizationKey");
156
+ if (!key) throw missing([chunkC2XW44FI_cjs.PRIVY_ENV.authorizationKey]);
157
+ const signed = await o.privy.wallets().ethereum().signTransaction(o.walletId, {
158
+ params: { transaction: tx },
159
+ authorization_context: { authorization_private_keys: [key] }
160
+ });
161
+ return signed.signed_transaction;
162
+ };
163
+ return {
164
+ signTransaction,
165
+ signAndBroadcast: (p) => {
166
+ client ??= defaultClient();
167
+ return chunkIG7RSL2Y_cjs.signAndBroadcast({
168
+ ...p,
169
+ client,
170
+ from: o.address,
171
+ chainId: chunkDPCZJLB3_cjs.pepuMainnet.id,
172
+ sign: signTransaction
173
+ });
174
+ }
175
+ };
176
+ }
177
+ function createSessionActionRoute(o) {
178
+ if (isBlank(o.scope.contractAddress)) {
179
+ return { POST: async () => unconfiguredResponse(o.contractAddressEnv) };
180
+ }
181
+ const scope = o.scope;
182
+ validateScope(scope);
183
+ const contract = scope.contractAddress.toLowerCase();
184
+ const selectors = new Set(scope.functions.map((f) => viem.toFunctionSelector(f)));
185
+ const hits = /* @__PURE__ */ new Map();
186
+ const denied = (message) => new chunkYQL7NISX_cjs.PepuPolicyError("POLICY_UNSCOPED", message, { status: 403 });
187
+ return {
188
+ POST: (req) => guard("session action", async () => {
189
+ const privy = resolvePrivy(o);
190
+ const userId = await authenticate(req, privy, o.verifyUser);
191
+ if (o.rateLimit) {
192
+ const now = Date.now();
193
+ const recent = (hits.get(userId) ?? []).filter((t) => now - t < 6e4);
194
+ if (recent.length >= o.rateLimit.perMinute) {
195
+ throw new chunkYQL7NISX_cjs.PepuChainError("UNKNOWN", "Too many session actions; slow down.", {
196
+ status: 429,
197
+ retryable: true,
198
+ hint: "Wait a few seconds and retry."
199
+ });
200
+ }
201
+ recent.push(now);
202
+ hits.set(userId, recent);
203
+ }
204
+ const wallet = await findDelegated(privy, userId);
205
+ if (!wallet || !wallet.id) {
206
+ throw new chunkYQL7NISX_cjs.PepuSessionError(
207
+ "SESSION_NONE",
208
+ "No delegated session signer found for this account. Grant a session first."
209
+ );
210
+ }
211
+ let input;
212
+ try {
213
+ input = o.parse(await req.json().catch(() => ({})));
214
+ } catch (e) {
215
+ throw new chunkYQL7NISX_cjs.PepuPolicyError("UNKNOWN", e instanceof Error ? e.message : "Invalid request body.", {
216
+ status: 400,
217
+ hint: "Fix the request body.",
218
+ cause: e
219
+ });
220
+ }
221
+ const call = o.buildCall(input, { from: wallet.address });
222
+ if (!viem.isAddress(call.to, { strict: false }) || call.to.toLowerCase() !== contract) {
223
+ throw denied("The call targets a contract outside the session scope.");
224
+ }
225
+ if ((call.value ?? 0n) !== 0n) throw denied("A session call must carry zero value.");
226
+ const fn = call.functionName ? scope.functions.find((f) => f.name === call.functionName) : void 0;
227
+ const selector = call.data ? call.data.slice(0, 10).toLowerCase() : fn ? viem.toFunctionSelector(fn) : void 0;
228
+ if (!selector || !selectors.has(selector)) {
229
+ throw denied("The call uses a function outside the session scope.");
230
+ }
231
+ const signer = privySessionSigner({
232
+ privy,
233
+ walletId: wallet.id,
234
+ address: wallet.address,
235
+ ...o.authorizationKey !== void 0 ? { authorizationKey: o.authorizationKey } : {},
236
+ ...o.client !== void 0 ? { client: o.client } : {}
237
+ });
238
+ const hash = await signer.signAndBroadcast({
239
+ to: call.to,
240
+ ...call.data !== void 0 ? { data: call.data } : {},
241
+ ...call.abi !== void 0 ? { abi: call.abi } : {},
242
+ ...call.functionName !== void 0 ? { functionName: call.functionName } : {},
243
+ ...call.args !== void 0 ? { args: call.args } : {},
244
+ value: 0n
245
+ });
246
+ return json({ hash, address: wallet.address, explorerUrl: chunk4GINFJO6_cjs.txUrl(chunkDPCZJLB3_cjs.pepuMainnet, hash) });
247
+ })
248
+ };
249
+ }
250
+
251
+ Object.defineProperty(exports, "broadcastSignedTransaction", {
252
+ enumerable: true,
253
+ get: function () { return chunkIG7RSL2Y_cjs.broadcastSignedTransaction; }
254
+ });
255
+ Object.defineProperty(exports, "signAndBroadcast", {
256
+ enumerable: true,
257
+ get: function () { return chunkIG7RSL2Y_cjs.signAndBroadcast; }
258
+ });
259
+ Object.defineProperty(exports, "withGasLimitBuffer", {
260
+ enumerable: true,
261
+ get: function () { return chunkIG7RSL2Y_cjs.withGasLimitBuffer; }
262
+ });
263
+ exports.createSessionActionRoute = createSessionActionRoute;
264
+ exports.createSessionRoutes = createSessionRoutes;
265
+ exports.privySessionSigner = privySessionSigner;
266
+ //# sourceMappingURL=server.cjs.map
267
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/session/server.ts"],"names":["assertServerOnly","toErrorResponse","PepuConfigError","createPrivyServer","verifyRequestUser","PepuAuthError","findDelegatedEthereumWallet","buildSessionPolicy","toPepuError","PepuError","PepuChainError","privyEnvVar","PRIVY_ENV","resolveSessionMinutes","sessionExpiry","resolvePepuEndpoints","getPublicClient","pepuMainnet","http","signAndBroadcast","toFunctionSelector","PepuPolicyError","PepuSessionError","isAddress","txUrl"],"mappings":"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","file":"server.cjs","sourcesContent":["import type { PrivyClient } from \"@privy-io/node\";\nimport type { PrivyPoliciesService } from \"@privy-io/node\";\nimport { http, isAddress, toFunctionSelector } from \"viem\";\nimport type { Abi, AbiFunction, Address, Hash, Hex } from \"viem\";\nimport { pepuMainnet } from \"../chains.js\";\nimport { getPublicClient } from \"../client.js\";\nimport { resolvePepuEndpoints } from \"../endpoints.js\";\nimport {\n PepuAuthError,\n PepuChainError,\n PepuConfigError,\n PepuError,\n PepuPolicyError,\n PepuSessionError,\n toErrorResponse,\n toPepuError,\n} from \"../errors.js\";\nimport { txUrl } from \"../explorer.js\";\nimport { assertServerOnly } from \"../internal/assert-server.js\";\nimport { PRIVY_ENV } from \"../privy/requirements.js\";\nimport { createPrivyServer, findDelegatedEthereumWallet, privyEnvVar, verifyRequestUser } from \"../privy/server.js\";\nimport {\n buildSessionPolicy,\n resolveSessionMinutes,\n sessionExpiry,\n signAndBroadcast as signAndBroadcastFlow,\n} from \"./policy.js\";\nimport type { PrivyUnsignedTransaction, SignAndBroadcastClient, SignAndBroadcastParams } from \"./policy.js\";\n\nassertServerOnly(\"@pepu-chain/sdk/session/server\");\n\n/** Derived from the exported service type; the package does not re-export PolicyCreateParams (game-app lib/session-policy.ts). */\ntype PolicyCreateParams = Parameters<PrivyPoliciesService[\"create\"]>[0];\n\n/**\n * Server half of the Privy-mode session layer. Ported from\n * templates/game-app (app/api/session/*, app/api/game/tap/route.ts).\n *\n * Trust model: scope is enforced by Privy's off-chain policy engine. The\n * client `removeSigners` call is the real revoke; the server revoke route is\n * cleanup only.\n */\n\nexport { signAndBroadcast, broadcastSignedTransaction, withGasLimitBuffer } from \"./policy.js\";\n\n/** Contract and functions a session may touch. */\nexport interface SessionScopeInput {\n contractAddress: Address;\n functions: readonly AbiFunction[];\n}\n\n/**\n * Like {@link SessionScopeInput}, but the address may be empty or undefined,\n * which is what an unset env variable looks like. The route factories accept\n * it and answer every request with a `CONFIG_MISSING_ENV` JSON error instead\n * of throwing when the module loads.\n */\nexport interface LazySessionScopeInput {\n contractAddress: Address | \"\" | undefined;\n functions: readonly AbiFunction[];\n}\n\nfunction isBlank(address: string | undefined | null): boolean {\n return address === undefined || address === null || address.trim() === \"\";\n}\n\n/** The handler used when the scope's contract address is unset: a `CONFIG_MISSING_ENV` JSON error. */\nfunction unconfiguredResponse(envName: string | undefined): Response {\n return toErrorResponse(\n new PepuConfigError(\n \"CONFIG_MISSING_ENV\",\n `${envName ?? \"The session contract address\"} is not set to a valid address. See .env.example.`,\n { hint: envName ? `Set ${envName} and restart the server.` : \"Set the contract address and restart the server.\" },\n ),\n );\n}\n\n/**\n * Resolves the authenticated user for a request. Return the user id (or\n * `{ userId }`), return `null` for \"not authenticated\", or throw a\n * `PepuAuthError`. Integration point for WP-B1: pass\n * `async (req) => { const r = await requireUser(req); ... }`. Default (when\n * omitted): `verifyRequestUser` from `@pepu-chain/sdk/privy/server`.\n */\nexport type VerifyUser = (request: Request) => Promise<{ userId: string } | string | null>;\n\n/** The slice of a Privy client these routes use. Tests pass a fake. */\nexport type SessionPrivyClient = PrivyClient;\n\ninterface CommonOptions {\n privy?: PrivyClient;\n /** Custom auth. Default: verify the bearer access token with the Privy client. */\n verifyUser?: VerifyUser;\n}\n\nfunction missing(names: readonly string[]): PepuConfigError {\n return new PepuConfigError(\n \"CONFIG_MISSING_ENV\",\n `Missing required environment variable(s): ${names.join(\", \")}.`,\n );\n}\n\nfunction resolvePrivy(o: CommonOptions): PrivyClient {\n // One source for env names and client caching: B1's createPrivyServer (PRIVY_ENV).\n return o.privy ?? createPrivyServer();\n}\n\nasync function authenticate(request: Request, privy: PrivyClient, verifyUser?: VerifyUser): Promise<string> {\n let result: { userId: string } | string | null;\n if (verifyUser) {\n result = await verifyUser(request);\n } else {\n // Default: B1's verifyRequestUser (throws PepuAuthError AUTH_INVALID_TOKEN with a redacted reason).\n result = await verifyRequestUser(request, { privy });\n }\n const userId = typeof result === \"string\" ? result : result?.userId;\n if (!userId) {\n throw new PepuAuthError(\"AUTH_INVALID_TOKEN\", \"Missing or invalid Privy access token.\");\n }\n return userId;\n}\n\ninterface DelegatedWallet {\n id: string | null;\n address: Address;\n}\n\n/** The user's embedded Ethereum wallet that has a session signer attached. Ported from game-app lib/privy-user.ts. */\nasync function findDelegated(privy: PrivyClient, userId: string): Promise<DelegatedWallet | null> {\n const wallet = findDelegatedEthereumWallet(await privy.users()._get(userId));\n return wallet ? { id: wallet.id ?? null, address: wallet.address as Address } : null;\n}\n\nfunction validateScope(scope: SessionScopeInput): void {\n // Same checks as buildSessionPolicy, run at factory time so misuse fails at startup.\n buildSessionPolicy({ ...scope, expiresAtUnixSeconds: 1 });\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), { status, headers: { \"content-type\": \"application/json\" } });\n}\n\n/** Anything unclassified that reaches a route is an upstream failure, not our 500. */\nfunction upstream(e: unknown, op: string): PepuError {\n const err = toPepuError(e, { op });\n if (err.code === \"UNKNOWN\" && !(e instanceof PepuError)) {\n return new PepuChainError(\"UNKNOWN\", err.message, { status: 502, cause: e });\n }\n // Only SESSION_* may be 409: clients treat 409 as \"no session\" (a nonce race is a failed tx, not a lost session).\n if (err.status === 409 && !err.code.startsWith(\"SESSION_\")) {\n return new PepuChainError(err.code, err.message, { status: 502, cause: e });\n }\n return err;\n}\n\nasync function guard(op: string, fn: () => Promise<Response>): Promise<Response> {\n try {\n return await fn();\n } catch (e) {\n return toErrorResponse(upstream(e, op));\n }\n}\n\nexport interface CreateSessionRoutesOptions extends CommonOptions {\n /**\n * An empty or undefined `contractAddress` is allowed: the routes then answer\n * every request with `CONFIG_MISSING_ENV`. A non-empty malformed one still\n * throws at creation.\n */\n scope: LazySessionScopeInput;\n /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset, e.g. `\"NEXT_PUBLIC_GAME_CONTRACT_ADDRESS\"`. */\n contractAddressEnv?: string;\n /** Session length. Default env `SESSION_DURATION_MINUTES`, clamped to 1..1440. */\n minutes?: number;\n /** The Privy key quorum the user grants. Default env `NEXT_PUBLIC_PRIVY_KEY_QUORUM_ID`. */\n keyQuorumId?: string;\n}\n\n/**\n * Route handlers for granting, checking and revoking a Privy session.\n *\n * - `grant.POST` creates a fresh scoped, time-bound policy and returns\n * `{ alreadyGranted: false, policyId, expiresAt, signerId, durationMinutes }`,\n * or `{ alreadyGranted: true }` when a signer is already attached (the client\n * must then NOT call `addSigners` again: Privy fails with \"Duplicate signer\").\n * - `status.GET` returns `{ delegated, active, address }`. Privy does not\n * expose a policy's expiry, so the client tracks `expiresAt` from the grant.\n * - `revoke.POST` is server cleanup. Call it after the client `removeSigners`;\n * body `{ policyId }` deletes that policy if it is this scope's and the\n * wallet is no longer delegated.\n *\n * @example\n * // app/api/session/grant/route.ts\n * const routes = createSessionRoutes({ scope: { contractAddress, functions: [tapAbi] } });\n * export const { POST } = routes.grant;\n */\nexport function createSessionRoutes(o: CreateSessionRoutesOptions): {\n grant: { POST(req: Request): Promise<Response> };\n status: { GET(req: Request): Promise<Response> };\n revoke: { POST(req: Request): Promise<Response> };\n} {\n if (isBlank(o.scope.contractAddress)) {\n const unset = async (): Promise<Response> => unconfiguredResponse(o.contractAddressEnv);\n return { grant: { POST: unset }, status: { GET: unset }, revoke: { POST: unset } };\n }\n const scope = o.scope as SessionScopeInput;\n validateScope(scope);\n const scopeContract = scope.contractAddress.toLowerCase();\n\n return {\n grant: {\n POST: (req) =>\n guard(\"session grant\", async () => {\n const signerId = o.keyQuorumId ?? privyEnvVar(\"keyQuorumId\");\n if (!signerId) throw missing([PRIVY_ENV.keyQuorumId]);\n const privy = resolvePrivy(o);\n const userId = await authenticate(req, privy, o.verifyUser);\n\n try {\n if (await findDelegated(privy, userId)) return json({ alreadyGranted: true });\n } catch {\n // The check failing must not block a first grant; a real Privy problem surfaces below.\n }\n\n const minutes = o.minutes ?? resolveSessionMinutes(process.env.SESSION_DURATION_MINUTES);\n const expiresAt = sessionExpiry(Math.floor(Date.now() / 1000), minutes);\n const policy = await privy.policies().create(\n buildSessionPolicy({ ...scope, expiresAtUnixSeconds: expiresAt }) as unknown as PolicyCreateParams,\n );\n return json({\n alreadyGranted: false,\n policyId: policy.id,\n expiresAt,\n signerId,\n durationMinutes: minutes,\n });\n }),\n },\n status: {\n GET: (req) =>\n guard(\"session status\", async () => {\n const privy = resolvePrivy(o);\n const userId = await authenticate(req, privy, o.verifyUser);\n const wallet = await findDelegated(privy, userId);\n return json({ delegated: wallet !== null, active: wallet !== null, address: wallet?.address ?? null });\n }),\n },\n revoke: {\n POST: (req) =>\n guard(\"session revoke\", async () => {\n const privy = resolvePrivy(o);\n const userId = await authenticate(req, privy, o.verifyUser);\n const wallet = await findDelegated(privy, userId);\n if (wallet) {\n // Client removeSigners has not happened yet; never delete a policy that is still attached.\n return json({ revoked: false, delegated: true, policyDeleted: false });\n }\n let policyDeleted = false;\n const body = (await req.json().catch(() => null)) as { policyId?: unknown } | null;\n const policyId = typeof body?.policyId === \"string\" ? body.policyId : undefined;\n if (policyId) {\n try {\n const policy = await privy.policies().get(policyId);\n const scoped = policy.rules.every((r) =>\n r.conditions.some(\n (c) =>\n c.field_source === \"ethereum_transaction\" &&\n c.field === \"to\" &&\n typeof c.value === \"string\" &&\n c.value.toLowerCase() === scopeContract,\n ),\n );\n if (scoped && policy.rules.length > 0) {\n await privy.policies()._delete(policyId);\n policyDeleted = true;\n }\n } catch {\n // Cleanup is best effort; the signer is already removed.\n }\n }\n return json({ revoked: true, delegated: false, policyDeleted });\n }),\n },\n };\n}\n\nexport interface PrivySessionSignerOptions {\n privy: PrivyClient;\n walletId: string;\n /** The wallet's address (the `from` of the transaction). */\n address: Address;\n /** Default env `PRIVY_AUTHORIZATION_KEY`. Never logged or put in errors. */\n authorizationKey?: string;\n /** Default: a viem client on the PEPU RPC (`PEPU_RPC_URL` aware). */\n client?: SignAndBroadcastClient;\n}\n\nfunction defaultClient(): SignAndBroadcastClient {\n const { rpcUrl } = resolvePepuEndpoints();\n return getPublicClient(pepuMainnet, { transport: http(rpcUrl) }) as unknown as SignAndBroadcastClient;\n}\n\n/**\n * A signer for a Privy session wallet. Signs with `eth_signTransaction` and\n * broadcasts the raw transaction itself, because Privy's server-side\n * `eth_sendTransaction` rejects chain 97741. Ported from\n * templates/game-app/app/api/game/tap/route.ts.\n *\n * @example\n * const signer = privySessionSigner({ privy, walletId, address });\n * const hash = await signer.signAndBroadcast({ to, abi, functionName: \"tap\" });\n */\nexport function privySessionSigner(o: PrivySessionSignerOptions): {\n signTransaction(tx: PrivyUnsignedTransaction): Promise<Hex>;\n signAndBroadcast(p: Omit<SignAndBroadcastParams, \"client\" | \"sign\" | \"from\" | \"chainId\">): Promise<Hash>;\n} {\n let client = o.client;\n const signTransaction = async (tx: PrivyUnsignedTransaction): Promise<Hex> => {\n const key = o.authorizationKey ?? privyEnvVar(\"authorizationKey\");\n if (!key) throw missing([PRIVY_ENV.authorizationKey]);\n const signed = await o.privy.wallets().ethereum().signTransaction(o.walletId, {\n params: { transaction: tx },\n authorization_context: { authorization_private_keys: [key] },\n });\n return signed.signed_transaction as Hex;\n };\n return {\n signTransaction,\n signAndBroadcast: (p) => {\n client ??= defaultClient();\n return signAndBroadcastFlow({\n ...p,\n client,\n from: o.address,\n chainId: pepuMainnet.id,\n sign: signTransaction,\n });\n },\n };\n}\n\nexport interface SessionCall {\n to: Address;\n abi?: Abi;\n functionName?: string;\n args?: readonly unknown[];\n data?: Hex;\n value?: bigint;\n}\n\nexport interface CreateSessionActionRouteOptions<I> extends CommonOptions {\n /** As in {@link CreateSessionRoutesOptions}: an unset address yields `CONFIG_MISSING_ENV` at request time. */\n scope: LazySessionScopeInput;\n /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset. */\n contractAddressEnv?: string;\n /** Validate the client input. Throw to reject with 400. */\n parse(body: unknown): I;\n /** Turn the input into the one call to make. Checked against `scope` before signing. */\n buildCall(i: I, ctx: { from: Address }): SessionCall;\n /** Per-user in-memory limit (per server instance). */\n rateLimit?: { perMinute: number };\n authorizationKey?: string;\n client?: SignAndBroadcastClient;\n}\n\n/**\n * Route that acts on the user's behalf with their session: finds the\n * delegated wallet (409 `SESSION_NONE` if none, the only \"no session\"\n * signal), re-checks the call against `scope`, signs with Privy and\n * broadcasts. Responds `{ hash, address, explorerUrl }`.\n *\n * A non-409 failure (policy denial, revert, nonce race) is a failed\n * transaction, not proof the session is gone.\n *\n * @example\n * export const { POST } = createSessionActionRoute({\n * scope: { contractAddress, functions: [tapAbi] },\n * parse: () => ({}),\n * buildCall: () => ({ to: contractAddress, abi: [tapAbi], functionName: \"tap\" }),\n * });\n */\nexport function createSessionActionRoute<I>(o: CreateSessionActionRouteOptions<I>): {\n POST(req: Request): Promise<Response>;\n} {\n if (isBlank(o.scope.contractAddress)) {\n return { POST: async () => unconfiguredResponse(o.contractAddressEnv) };\n }\n const scope = o.scope as SessionScopeInput;\n validateScope(scope);\n const contract = scope.contractAddress.toLowerCase();\n const selectors = new Set(scope.functions.map((f) => toFunctionSelector(f)));\n const hits = new Map<string, number[]>();\n\n const denied = (message: string): PepuPolicyError =>\n new PepuPolicyError(\"POLICY_UNSCOPED\", message, { status: 403 });\n\n return {\n POST: (req) =>\n guard(\"session action\", async () => {\n const privy = resolvePrivy(o);\n const userId = await authenticate(req, privy, o.verifyUser);\n\n if (o.rateLimit) {\n const now = Date.now();\n const recent = (hits.get(userId) ?? []).filter((t) => now - t < 60_000);\n if (recent.length >= o.rateLimit.perMinute) {\n throw new PepuChainError(\"UNKNOWN\", \"Too many session actions; slow down.\", {\n status: 429,\n retryable: true,\n hint: \"Wait a few seconds and retry.\",\n });\n }\n recent.push(now);\n hits.set(userId, recent);\n }\n\n const wallet = await findDelegated(privy, userId);\n if (!wallet || !wallet.id) {\n throw new PepuSessionError(\n \"SESSION_NONE\",\n \"No delegated session signer found for this account. Grant a session first.\",\n );\n }\n\n let input: I;\n try {\n input = o.parse(await req.json().catch(() => ({})));\n } catch (e) {\n throw new PepuPolicyError(\"UNKNOWN\", e instanceof Error ? e.message : \"Invalid request body.\", {\n status: 400,\n hint: \"Fix the request body.\",\n cause: e,\n });\n }\n\n const call = o.buildCall(input, { from: wallet.address });\n if (!isAddress(call.to, { strict: false }) || call.to.toLowerCase() !== contract) {\n throw denied(\"The call targets a contract outside the session scope.\");\n }\n if ((call.value ?? 0n) !== 0n) throw denied(\"A session call must carry zero value.\");\n const fn = call.functionName\n ? scope.functions.find((f) => f.name === call.functionName)\n : undefined;\n const selector = call.data\n ? (call.data.slice(0, 10).toLowerCase() as Hex)\n : fn\n ? toFunctionSelector(fn)\n : undefined;\n if (!selector || !selectors.has(selector as Hex)) {\n throw denied(\"The call uses a function outside the session scope.\");\n }\n\n const signer = privySessionSigner({\n privy,\n walletId: wallet.id,\n address: wallet.address,\n ...(o.authorizationKey !== undefined ? { authorizationKey: o.authorizationKey } : {}),\n ...(o.client !== undefined ? { client: o.client } : {}),\n });\n const hash = await signer.signAndBroadcast({\n to: call.to,\n ...(call.data !== undefined ? { data: call.data } : {}),\n ...(call.abi !== undefined ? { abi: call.abi } : {}),\n ...(call.functionName !== undefined ? { functionName: call.functionName } : {}),\n ...(call.args !== undefined ? { args: call.args } : {}),\n value: 0n,\n });\n return json({ hash, address: wallet.address, explorerUrl: txUrl(pepuMainnet, hash) });\n }),\n };\n}\n"]}
@@ -0,0 +1,151 @@
1
+ import { PrivyClient } from '@privy-io/node';
2
+ import { Address, AbiFunction, Abi, Hex, Hash } from 'viem';
3
+ import { SignAndBroadcastClient, PrivyUnsignedTransaction, SignAndBroadcastParams } from '../server.cjs';
4
+ export { broadcastSignedTransaction, signAndBroadcast, withGasLimitBuffer } from '../server.cjs';
5
+
6
+ /** Contract and functions a session may touch. */
7
+ interface SessionScopeInput {
8
+ contractAddress: Address;
9
+ functions: readonly AbiFunction[];
10
+ }
11
+ /**
12
+ * Like {@link SessionScopeInput}, but the address may be empty or undefined,
13
+ * which is what an unset env variable looks like. The route factories accept
14
+ * it and answer every request with a `CONFIG_MISSING_ENV` JSON error instead
15
+ * of throwing when the module loads.
16
+ */
17
+ interface LazySessionScopeInput {
18
+ contractAddress: Address | "" | undefined;
19
+ functions: readonly AbiFunction[];
20
+ }
21
+ /**
22
+ * Resolves the authenticated user for a request. Return the user id (or
23
+ * `{ userId }`), return `null` for "not authenticated", or throw a
24
+ * `PepuAuthError`. Integration point for WP-B1: pass
25
+ * `async (req) => { const r = await requireUser(req); ... }`. Default (when
26
+ * omitted): `verifyRequestUser` from `@pepu-chain/sdk/privy/server`.
27
+ */
28
+ type VerifyUser = (request: Request) => Promise<{
29
+ userId: string;
30
+ } | string | null>;
31
+ /** The slice of a Privy client these routes use. Tests pass a fake. */
32
+ type SessionPrivyClient = PrivyClient;
33
+ interface CommonOptions {
34
+ privy?: PrivyClient;
35
+ /** Custom auth. Default: verify the bearer access token with the Privy client. */
36
+ verifyUser?: VerifyUser;
37
+ }
38
+ interface CreateSessionRoutesOptions extends CommonOptions {
39
+ /**
40
+ * An empty or undefined `contractAddress` is allowed: the routes then answer
41
+ * every request with `CONFIG_MISSING_ENV`. A non-empty malformed one still
42
+ * throws at creation.
43
+ */
44
+ scope: LazySessionScopeInput;
45
+ /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset, e.g. `"NEXT_PUBLIC_GAME_CONTRACT_ADDRESS"`. */
46
+ contractAddressEnv?: string;
47
+ /** Session length. Default env `SESSION_DURATION_MINUTES`, clamped to 1..1440. */
48
+ minutes?: number;
49
+ /** The Privy key quorum the user grants. Default env `NEXT_PUBLIC_PRIVY_KEY_QUORUM_ID`. */
50
+ keyQuorumId?: string;
51
+ }
52
+ /**
53
+ * Route handlers for granting, checking and revoking a Privy session.
54
+ *
55
+ * - `grant.POST` creates a fresh scoped, time-bound policy and returns
56
+ * `{ alreadyGranted: false, policyId, expiresAt, signerId, durationMinutes }`,
57
+ * or `{ alreadyGranted: true }` when a signer is already attached (the client
58
+ * must then NOT call `addSigners` again: Privy fails with "Duplicate signer").
59
+ * - `status.GET` returns `{ delegated, active, address }`. Privy does not
60
+ * expose a policy's expiry, so the client tracks `expiresAt` from the grant.
61
+ * - `revoke.POST` is server cleanup. Call it after the client `removeSigners`;
62
+ * body `{ policyId }` deletes that policy if it is this scope's and the
63
+ * wallet is no longer delegated.
64
+ *
65
+ * @example
66
+ * // app/api/session/grant/route.ts
67
+ * const routes = createSessionRoutes({ scope: { contractAddress, functions: [tapAbi] } });
68
+ * export const { POST } = routes.grant;
69
+ */
70
+ declare function createSessionRoutes(o: CreateSessionRoutesOptions): {
71
+ grant: {
72
+ POST(req: Request): Promise<Response>;
73
+ };
74
+ status: {
75
+ GET(req: Request): Promise<Response>;
76
+ };
77
+ revoke: {
78
+ POST(req: Request): Promise<Response>;
79
+ };
80
+ };
81
+ interface PrivySessionSignerOptions {
82
+ privy: PrivyClient;
83
+ walletId: string;
84
+ /** The wallet's address (the `from` of the transaction). */
85
+ address: Address;
86
+ /** Default env `PRIVY_AUTHORIZATION_KEY`. Never logged or put in errors. */
87
+ authorizationKey?: string;
88
+ /** Default: a viem client on the PEPU RPC (`PEPU_RPC_URL` aware). */
89
+ client?: SignAndBroadcastClient;
90
+ }
91
+ /**
92
+ * A signer for a Privy session wallet. Signs with `eth_signTransaction` and
93
+ * broadcasts the raw transaction itself, because Privy's server-side
94
+ * `eth_sendTransaction` rejects chain 97741. Ported from
95
+ * templates/game-app/app/api/game/tap/route.ts.
96
+ *
97
+ * @example
98
+ * const signer = privySessionSigner({ privy, walletId, address });
99
+ * const hash = await signer.signAndBroadcast({ to, abi, functionName: "tap" });
100
+ */
101
+ declare function privySessionSigner(o: PrivySessionSignerOptions): {
102
+ signTransaction(tx: PrivyUnsignedTransaction): Promise<Hex>;
103
+ signAndBroadcast(p: Omit<SignAndBroadcastParams, "client" | "sign" | "from" | "chainId">): Promise<Hash>;
104
+ };
105
+ interface SessionCall {
106
+ to: Address;
107
+ abi?: Abi;
108
+ functionName?: string;
109
+ args?: readonly unknown[];
110
+ data?: Hex;
111
+ value?: bigint;
112
+ }
113
+ interface CreateSessionActionRouteOptions<I> extends CommonOptions {
114
+ /** As in {@link CreateSessionRoutesOptions}: an unset address yields `CONFIG_MISSING_ENV` at request time. */
115
+ scope: LazySessionScopeInput;
116
+ /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset. */
117
+ contractAddressEnv?: string;
118
+ /** Validate the client input. Throw to reject with 400. */
119
+ parse(body: unknown): I;
120
+ /** Turn the input into the one call to make. Checked against `scope` before signing. */
121
+ buildCall(i: I, ctx: {
122
+ from: Address;
123
+ }): SessionCall;
124
+ /** Per-user in-memory limit (per server instance). */
125
+ rateLimit?: {
126
+ perMinute: number;
127
+ };
128
+ authorizationKey?: string;
129
+ client?: SignAndBroadcastClient;
130
+ }
131
+ /**
132
+ * Route that acts on the user's behalf with their session: finds the
133
+ * delegated wallet (409 `SESSION_NONE` if none, the only "no session"
134
+ * signal), re-checks the call against `scope`, signs with Privy and
135
+ * broadcasts. Responds `{ hash, address, explorerUrl }`.
136
+ *
137
+ * A non-409 failure (policy denial, revert, nonce race) is a failed
138
+ * transaction, not proof the session is gone.
139
+ *
140
+ * @example
141
+ * export const { POST } = createSessionActionRoute({
142
+ * scope: { contractAddress, functions: [tapAbi] },
143
+ * parse: () => ({}),
144
+ * buildCall: () => ({ to: contractAddress, abi: [tapAbi], functionName: "tap" }),
145
+ * });
146
+ */
147
+ declare function createSessionActionRoute<I>(o: CreateSessionActionRouteOptions<I>): {
148
+ POST(req: Request): Promise<Response>;
149
+ };
150
+
151
+ export { type CreateSessionActionRouteOptions, type CreateSessionRoutesOptions, type LazySessionScopeInput, type PrivySessionSignerOptions, type SessionCall, type SessionPrivyClient, type SessionScopeInput, type VerifyUser, createSessionActionRoute, createSessionRoutes, privySessionSigner };
@@ -0,0 +1,151 @@
1
+ import { PrivyClient } from '@privy-io/node';
2
+ import { Address, AbiFunction, Abi, Hex, Hash } from 'viem';
3
+ import { SignAndBroadcastClient, PrivyUnsignedTransaction, SignAndBroadcastParams } from '../server.js';
4
+ export { broadcastSignedTransaction, signAndBroadcast, withGasLimitBuffer } from '../server.js';
5
+
6
+ /** Contract and functions a session may touch. */
7
+ interface SessionScopeInput {
8
+ contractAddress: Address;
9
+ functions: readonly AbiFunction[];
10
+ }
11
+ /**
12
+ * Like {@link SessionScopeInput}, but the address may be empty or undefined,
13
+ * which is what an unset env variable looks like. The route factories accept
14
+ * it and answer every request with a `CONFIG_MISSING_ENV` JSON error instead
15
+ * of throwing when the module loads.
16
+ */
17
+ interface LazySessionScopeInput {
18
+ contractAddress: Address | "" | undefined;
19
+ functions: readonly AbiFunction[];
20
+ }
21
+ /**
22
+ * Resolves the authenticated user for a request. Return the user id (or
23
+ * `{ userId }`), return `null` for "not authenticated", or throw a
24
+ * `PepuAuthError`. Integration point for WP-B1: pass
25
+ * `async (req) => { const r = await requireUser(req); ... }`. Default (when
26
+ * omitted): `verifyRequestUser` from `@pepu-chain/sdk/privy/server`.
27
+ */
28
+ type VerifyUser = (request: Request) => Promise<{
29
+ userId: string;
30
+ } | string | null>;
31
+ /** The slice of a Privy client these routes use. Tests pass a fake. */
32
+ type SessionPrivyClient = PrivyClient;
33
+ interface CommonOptions {
34
+ privy?: PrivyClient;
35
+ /** Custom auth. Default: verify the bearer access token with the Privy client. */
36
+ verifyUser?: VerifyUser;
37
+ }
38
+ interface CreateSessionRoutesOptions extends CommonOptions {
39
+ /**
40
+ * An empty or undefined `contractAddress` is allowed: the routes then answer
41
+ * every request with `CONFIG_MISSING_ENV`. A non-empty malformed one still
42
+ * throws at creation.
43
+ */
44
+ scope: LazySessionScopeInput;
45
+ /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset, e.g. `"NEXT_PUBLIC_GAME_CONTRACT_ADDRESS"`. */
46
+ contractAddressEnv?: string;
47
+ /** Session length. Default env `SESSION_DURATION_MINUTES`, clamped to 1..1440. */
48
+ minutes?: number;
49
+ /** The Privy key quorum the user grants. Default env `NEXT_PUBLIC_PRIVY_KEY_QUORUM_ID`. */
50
+ keyQuorumId?: string;
51
+ }
52
+ /**
53
+ * Route handlers for granting, checking and revoking a Privy session.
54
+ *
55
+ * - `grant.POST` creates a fresh scoped, time-bound policy and returns
56
+ * `{ alreadyGranted: false, policyId, expiresAt, signerId, durationMinutes }`,
57
+ * or `{ alreadyGranted: true }` when a signer is already attached (the client
58
+ * must then NOT call `addSigners` again: Privy fails with "Duplicate signer").
59
+ * - `status.GET` returns `{ delegated, active, address }`. Privy does not
60
+ * expose a policy's expiry, so the client tracks `expiresAt` from the grant.
61
+ * - `revoke.POST` is server cleanup. Call it after the client `removeSigners`;
62
+ * body `{ policyId }` deletes that policy if it is this scope's and the
63
+ * wallet is no longer delegated.
64
+ *
65
+ * @example
66
+ * // app/api/session/grant/route.ts
67
+ * const routes = createSessionRoutes({ scope: { contractAddress, functions: [tapAbi] } });
68
+ * export const { POST } = routes.grant;
69
+ */
70
+ declare function createSessionRoutes(o: CreateSessionRoutesOptions): {
71
+ grant: {
72
+ POST(req: Request): Promise<Response>;
73
+ };
74
+ status: {
75
+ GET(req: Request): Promise<Response>;
76
+ };
77
+ revoke: {
78
+ POST(req: Request): Promise<Response>;
79
+ };
80
+ };
81
+ interface PrivySessionSignerOptions {
82
+ privy: PrivyClient;
83
+ walletId: string;
84
+ /** The wallet's address (the `from` of the transaction). */
85
+ address: Address;
86
+ /** Default env `PRIVY_AUTHORIZATION_KEY`. Never logged or put in errors. */
87
+ authorizationKey?: string;
88
+ /** Default: a viem client on the PEPU RPC (`PEPU_RPC_URL` aware). */
89
+ client?: SignAndBroadcastClient;
90
+ }
91
+ /**
92
+ * A signer for a Privy session wallet. Signs with `eth_signTransaction` and
93
+ * broadcasts the raw transaction itself, because Privy's server-side
94
+ * `eth_sendTransaction` rejects chain 97741. Ported from
95
+ * templates/game-app/app/api/game/tap/route.ts.
96
+ *
97
+ * @example
98
+ * const signer = privySessionSigner({ privy, walletId, address });
99
+ * const hash = await signer.signAndBroadcast({ to, abi, functionName: "tap" });
100
+ */
101
+ declare function privySessionSigner(o: PrivySessionSignerOptions): {
102
+ signTransaction(tx: PrivyUnsignedTransaction): Promise<Hex>;
103
+ signAndBroadcast(p: Omit<SignAndBroadcastParams, "client" | "sign" | "from" | "chainId">): Promise<Hash>;
104
+ };
105
+ interface SessionCall {
106
+ to: Address;
107
+ abi?: Abi;
108
+ functionName?: string;
109
+ args?: readonly unknown[];
110
+ data?: Hex;
111
+ value?: bigint;
112
+ }
113
+ interface CreateSessionActionRouteOptions<I> extends CommonOptions {
114
+ /** As in {@link CreateSessionRoutesOptions}: an unset address yields `CONFIG_MISSING_ENV` at request time. */
115
+ scope: LazySessionScopeInput;
116
+ /** Env variable named in the `CONFIG_MISSING_ENV` message when the address is unset. */
117
+ contractAddressEnv?: string;
118
+ /** Validate the client input. Throw to reject with 400. */
119
+ parse(body: unknown): I;
120
+ /** Turn the input into the one call to make. Checked against `scope` before signing. */
121
+ buildCall(i: I, ctx: {
122
+ from: Address;
123
+ }): SessionCall;
124
+ /** Per-user in-memory limit (per server instance). */
125
+ rateLimit?: {
126
+ perMinute: number;
127
+ };
128
+ authorizationKey?: string;
129
+ client?: SignAndBroadcastClient;
130
+ }
131
+ /**
132
+ * Route that acts on the user's behalf with their session: finds the
133
+ * delegated wallet (409 `SESSION_NONE` if none, the only "no session"
134
+ * signal), re-checks the call against `scope`, signs with Privy and
135
+ * broadcasts. Responds `{ hash, address, explorerUrl }`.
136
+ *
137
+ * A non-409 failure (policy denial, revert, nonce race) is a failed
138
+ * transaction, not proof the session is gone.
139
+ *
140
+ * @example
141
+ * export const { POST } = createSessionActionRoute({
142
+ * scope: { contractAddress, functions: [tapAbi] },
143
+ * parse: () => ({}),
144
+ * buildCall: () => ({ to: contractAddress, abi: [tapAbi], functionName: "tap" }),
145
+ * });
146
+ */
147
+ declare function createSessionActionRoute<I>(o: CreateSessionActionRouteOptions<I>): {
148
+ POST(req: Request): Promise<Response>;
149
+ };
150
+
151
+ export { type CreateSessionActionRouteOptions, type CreateSessionRoutesOptions, type LazySessionScopeInput, type PrivySessionSignerOptions, type SessionCall, type SessionPrivyClient, type SessionScopeInput, type VerifyUser, createSessionActionRoute, createSessionRoutes, privySessionSigner };