@nexus-cross/pop 1.4.0-beta.5 → 2.4.0-beta.1

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.
@@ -0,0 +1,305 @@
1
+ import { m as SafeDropApiPort, A as Address, v as SocialIdentifier, I as Id, H as Hex, O as OAuthProof, e as DropInbox, E as Envelope, L as LeaderboardPage, M as MyLeaderboardEntry, f as HistoryType, g as HistoryStatus, h as HistoryPage, X as XConnection, i as RecipientChangeSignature, j as RecipientSignature, l as BatchClaimSignature } from './SafeDropApiPort-BLewfSaK.js';
2
+ import { X as XAuthPort, a as XTokenResult } from './XAuthPort-BNJePosj.js';
3
+
4
+ /**
5
+ * ONEpop 배포 설정 — API와 컨트랙트 주소를 동일한 환경 식별자로 선택한다.
6
+ * Vite import.meta.env + Next literal process.env 컨벤션을 따른다.
7
+ *
8
+ * 우선순위 (override):
9
+ * 1) `VITE_ONE_POP_API_BASE_URL` (Vite 빌드)
10
+ * 2) `NEXT_PUBLIC_ONE_POP_API_BASE_URL` (Next.js)
11
+ * 3) 환경 식별(`VITE_ONE_POP_ENVIRONMENT` / `NEXT_PUBLIC_ONE_POP_ENVIRONMENT`
12
+ * / `ONE_POP_ENVIRONMENT`)에 따라 DEFAULT_BASE_URL의 dev/stage/production
13
+ *
14
+ * 스펙: packages/pop/src/infrastructure/openapi.json (basePath `/api`)
15
+ */
16
+ type PopEnvironment = 'dev' | 'stage' | 'production';
17
+ interface PopContracts {
18
+ onePop: `0x${string}`;
19
+ permitErc20: `0x${string}`;
20
+ nft: `0x${string}`;
21
+ nftMinter: `0x${string}`;
22
+ validator: `0x${string}`;
23
+ feeRecipient: `0x${string}`;
24
+ }
25
+ interface PopDeployment {
26
+ apiBaseUrl: string;
27
+ contracts?: PopContracts;
28
+ }
29
+ /** ONEpop protocol fee: 5% (500 / 10,000 basis points). */
30
+ declare const ONE_POP_FEE_BPS: bigint;
31
+ declare const ONE_POP_BPS_DENOMINATOR: bigint;
32
+ declare const ONE_POP_DEPLOYMENTS: Readonly<Record<PopEnvironment, PopDeployment>>;
33
+ interface PopApiPaths {
34
+ /** POST — 임시 claim 지갑 생성 (Bearer JWT). */
35
+ createWallet: string;
36
+ /** POST — tx hash에 메시지/봉투 메타데이터 연결 (Bearer JWT). */
37
+ dropMetadata: string;
38
+ /** POST — pending 지갑 개인키 수령 (X OAuth 토큰). */
39
+ retrievePrivateKey: string;
40
+ /** GET — 수령 대기 인박스 (Bearer JWT + 활성 X 연결). */
41
+ drops: string;
42
+ /** POST — pending 드롭 숨김 (Bearer JWT + 활성 X 연결). */
43
+ rejectDrops: string;
44
+ /** GET — 카드 디자인 목록 (공개). */
45
+ envelopes: string;
46
+ /** GET — 토큰별 pending 잔액 상위 (공개). */
47
+ leaderboards: string;
48
+ leaderboardMe: string;
49
+ /** GET — 내 온체인 이벤트 히스토리 (Bearer JWT). */
50
+ histories: string;
51
+ /** GET/POST/DELETE — X 연결 (Bearer JWT). */
52
+ xConnections: string;
53
+ /** POST — 일괄 수령 validator 서명 (Bearer JWT). */
54
+ batchClaimSignature: string;
55
+ feedbacks: string;
56
+ revealYou: string;
57
+ recipientChangeSignature: string;
58
+ recipientResetSignature: string;
59
+ /** GET — SSE 알림 스트림. pop은 구독하지 않는다(앱이 EventSource로 직접). */
60
+ eventsSubscribe: string;
61
+ }
62
+ declare const DEFAULT_POP_API_PATHS: PopApiPaths;
63
+ declare function getPopEnvironment(): PopEnvironment;
64
+ declare function resolvePopEnvironment(raw?: string): PopEnvironment;
65
+ declare function getOnePopDeployment(): PopDeployment;
66
+ declare function getOnePopContracts(): PopContracts;
67
+ /** 환경변수(override → 환경 식별)로 one-pop-api base URL을 결정한다. */
68
+ declare function getOnePopApiBaseUrl(): string;
69
+ /** 환경변수(override → 기본)로 cross-auth base URL을 결정한다. */
70
+ declare function getCrossAuthBaseUrl(): string;
71
+
72
+ /**
73
+ * one-pop-api HTTP 어댑터. 스키마: packages/pop/src/infrastructure/openapi.json
74
+ * POST /wallets (Bearer JWT) → {address}
75
+ * POST /wallets/private-key (oauth_token 본문) → {address,private_key}
76
+ * GET /drops (Bearer JWT) → 수령 인박스
77
+ * POST /drops/reject (Bearer JWT) → 인박스에서 숨김
78
+ * GET /envelopes (공개) → 카드 디자인
79
+ * GET /leaderboards?token= (공개) → pending 상위
80
+ * GET /histories (Bearer JWT) → 온체인 이벤트
81
+ * GET|POST|DELETE /x-connections (Bearer JWT) → X 연결
82
+ * POST /batch-claim-signature (Bearer JWT) → ValidatorClaim 서명
83
+ *
84
+ * base URL은 미지정 시 환경변수로 결정(getOnePopApiBaseUrl).
85
+ * wei-scale 값은 응답의 10진 문자열 → BigInt로 변환해 내보낸다(CLAUDE.md §4).
86
+ */
87
+ interface HttpSafeDropApiAdapterOptions {
88
+ /** 미지정 시 환경변수(VITE_/NEXT_PUBLIC_ONE_POP_*)로 결정. */
89
+ baseUrl?: string;
90
+ /** SIWE JWT 공급자. Bearer 인증이 필요한 모든 호출이 사용한다. */
91
+ getJwt?: () => string | undefined | Promise<string | undefined>;
92
+ /** 테스트/SSR용 fetch 주입. 기본 globalThis.fetch. */
93
+ fetchImpl?: typeof fetch;
94
+ /** 엔드포인트 경로 override. 기본 DEFAULT_POP_API_PATHS. */
95
+ paths?: Partial<PopApiPaths>;
96
+ }
97
+ declare class HttpSafeDropApiAdapter implements SafeDropApiPort {
98
+ private readonly baseUrl;
99
+ private readonly fetchImpl;
100
+ private readonly getJwt?;
101
+ private readonly paths;
102
+ constructor(options?: HttpSafeDropApiAdapterOptions);
103
+ createClaimWallet(params: {
104
+ sender: Address;
105
+ recipient: SocialIdentifier;
106
+ }): Promise<{
107
+ claimAddress: Address;
108
+ id: Id;
109
+ isMapped: boolean;
110
+ }>;
111
+ recordDropMetadata(params: {
112
+ txHash: Hex;
113
+ message?: string;
114
+ envelopeId?: string;
115
+ }): Promise<void>;
116
+ retrieveClaimKey(params: {
117
+ claimAddress: Address;
118
+ senderAddress: Address;
119
+ oauth: OAuthProof;
120
+ }): Promise<{
121
+ isMapped: true;
122
+ } | {
123
+ isMapped: false;
124
+ claimKey: Hex;
125
+ claimAddress: Address;
126
+ }>;
127
+ listDrops(params: {
128
+ token: Address;
129
+ dropKey?: Hex;
130
+ }): Promise<DropInbox>;
131
+ rejectDrops(params: {
132
+ dropIds: readonly number[];
133
+ }): Promise<number>;
134
+ listEnvelopes(params?: {
135
+ locale?: string;
136
+ }): Promise<readonly Envelope[]>;
137
+ getLeaderboard(params: {
138
+ token: Address;
139
+ page?: number;
140
+ pageSize?: number;
141
+ identifier?: string;
142
+ }): Promise<LeaderboardPage>;
143
+ getMyLeaderboardEntry(params: {
144
+ token: Address;
145
+ }): Promise<MyLeaderboardEntry>;
146
+ listHistories(params: {
147
+ type: HistoryType;
148
+ token?: Address;
149
+ status?: HistoryStatus;
150
+ rejected?: boolean;
151
+ page?: number;
152
+ pageSize?: number;
153
+ }): Promise<HistoryPage>;
154
+ getXConnection(): Promise<XConnection | null>;
155
+ connectX(params: {
156
+ oauth: OAuthProof;
157
+ }): Promise<XConnection>;
158
+ disconnectX(): Promise<void>;
159
+ submitFeedback(params: {
160
+ dropId: number;
161
+ message: string;
162
+ }): Promise<string>;
163
+ setRevealYou(params: {
164
+ revealYou: boolean;
165
+ }): Promise<boolean>;
166
+ requestRecipientChangeSignature(params: {
167
+ id: Id;
168
+ oauth: OAuthProof;
169
+ nonce: bigint;
170
+ deadline: bigint;
171
+ }): Promise<RecipientChangeSignature>;
172
+ requestRecipientResetSignature(params: {
173
+ id: Id;
174
+ oauth: OAuthProof;
175
+ nonce: bigint;
176
+ deadline: bigint;
177
+ }): Promise<RecipientSignature>;
178
+ requestBatchClaimSignature(params: {
179
+ id: Id;
180
+ oauth: OAuthProof;
181
+ nonce: bigint;
182
+ deadline: bigint;
183
+ }): Promise<BatchClaimSignature>;
184
+ private request;
185
+ }
186
+
187
+ /**
188
+ * cross-auth SIWE 로그인 클라이언트. createClaimWallet에 필요한 플랫폼 JWT를 얻는다.
189
+ * 1) unsignedHash(chainId, address) → { hash, message }
190
+ * 2) 앱이 지갑으로 message 서명(signMessage)
191
+ * 3) siweToken(address, signature) → { token, refresh }
192
+ *
193
+ * 서명은 앱(wagmi/walletClient)이 수행 — 이 클라이언트는 HTTP만 담당한다.
194
+ * domain은 cross-auth가 SIWE domain으로 사용(기본 window.location.origin).
195
+ */
196
+ interface CrossAuthClientOptions {
197
+ /** 미지정 시 환경변수(VITE_/NEXT_PUBLIC_CROSS_AUTH_URL) 또는 dev 기본값. */
198
+ baseUrl?: string;
199
+ /** SIWE domain. 미지정 시 globalThis.location.origin. */
200
+ domain?: string;
201
+ fetchImpl?: typeof fetch;
202
+ }
203
+ declare class CrossAuthClient {
204
+ private readonly baseUrl;
205
+ private readonly domain;
206
+ private readonly fetchImpl;
207
+ constructor(options?: CrossAuthClientOptions);
208
+ /** SIWE 서명 대상 메시지 발급. cross-auth 필드는 snake_case `chain_id`. */
209
+ unsignedHash(chainId: number, address: string): Promise<{
210
+ hash: string;
211
+ message: string;
212
+ }>;
213
+ /** 서명 → 플랫폼 JWT 교환. */
214
+ siweToken(address: string, signature: string): Promise<{
215
+ token: string;
216
+ refresh: string;
217
+ }>;
218
+ private post;
219
+ }
220
+
221
+ interface Pkce {
222
+ verifier: string;
223
+ challenge: string;
224
+ method: 'S256';
225
+ }
226
+ /** code_verifier(43자) + S256 code_challenge 생성. */
227
+ declare function generatePkce(): Promise<Pkce>;
228
+ /** CSRF 방지용 state (base64url 22자). */
229
+ declare function generateState(): string;
230
+
231
+ /**
232
+ * X OAuth2 프록시 어댑터. 앱의 same-origin Next 라우트를 호출한다(one-pop-fe와 동일 계약).
233
+ * POST {tokenPath} { clientId, redirectUri, code, verifier } → X 토큰 응답(access_token)
234
+ * GET {mePath} Authorization: Bearer <token> → GET /2/users/me ({ data: { username } })
235
+ *
236
+ * 프록시가 grant_type/Basic auth 조립과 X API 응답 전달을 담당한다. 다르면 옵션으로 경로만 조정.
237
+ */
238
+ interface HttpXAuthAdapterOptions {
239
+ /** 프록시 base URL. 기본 '' (same-origin 상대경로). */
240
+ baseUrl?: string;
241
+ /** 토큰 교환 프록시 경로. 기본 '/api/x/token'. */
242
+ tokenPath?: string;
243
+ /** 핸들 조회 프록시 경로. 기본 '/api/x/me'. */
244
+ mePath?: string;
245
+ fetchImpl?: typeof fetch;
246
+ }
247
+ declare class HttpXAuthAdapter implements XAuthPort {
248
+ private readonly baseUrl;
249
+ private readonly tokenPath;
250
+ private readonly mePath;
251
+ private readonly fetchImpl;
252
+ constructor(options?: HttpXAuthAdapterOptions);
253
+ exchangeCode(params: {
254
+ clientId: string;
255
+ redirectUri: string;
256
+ code: string;
257
+ codeVerifier: string;
258
+ }): Promise<XTokenResult>;
259
+ getHandle(accessToken: string): Promise<{
260
+ handle: string;
261
+ }>;
262
+ private call;
263
+ }
264
+
265
+ /** verifier/state 임시 보관. 기본은 sessionStorage 어댑터. */
266
+ interface SessionStore {
267
+ get(key: string): string | null;
268
+ set(key: string, value: string): void;
269
+ remove(key: string): void;
270
+ }
271
+ interface XAuthClientOptions {
272
+ clientId: string;
273
+ redirectUri: string;
274
+ /** 기본 'tweet.read users.read offline.access' (GET /2/users/me + refresh token). */
275
+ scope?: string;
276
+ /** 기본 X authorize 엔드포인트. */
277
+ authorizeUrl?: string;
278
+ /** 토큰 교환/핸들 조회 프록시 어댑터. */
279
+ port: XAuthPort;
280
+ /** 기본 globalThis.sessionStorage 어댑터. */
281
+ storage?: SessionStore;
282
+ }
283
+ /**
284
+ * X OAuth2 PKCE 플로우. UI/리다이렉트는 앱이 담당한다:
285
+ * const { authorizeUrl } = await xauth.start(); location.assign(authorizeUrl);
286
+ * // 콜백에서:
287
+ * const { oauth, handle } = await xauth.complete(location.href);
288
+ * await safeDrop.retrieveClaimKey({ senderAddress, oauth });
289
+ */
290
+ declare class XAuthClient {
291
+ private readonly opts;
292
+ constructor(options: XAuthClientOptions);
293
+ /** PKCE·state 생성·저장 후 X authorize URL 반환. 리다이렉트는 호출자 몫. */
294
+ start(): Promise<{
295
+ authorizeUrl: string;
296
+ state: string;
297
+ }>;
298
+ /** 콜백 URL에서 code/state 추출 → state 검증 → 토큰 교환 → 핸들 조회. */
299
+ complete(callbackUrl: string): Promise<{
300
+ oauth: OAuthProof;
301
+ handle: string;
302
+ }>;
303
+ }
304
+
305
+ export { CrossAuthClient as C, DEFAULT_POP_API_PATHS as D, type HttpSafeDropApiAdapterOptions as H, ONE_POP_BPS_DENOMINATOR as O, type Pkce as P, type SessionStore as S, XAuthClient as X, type CrossAuthClientOptions as a, HttpSafeDropApiAdapter as b, HttpXAuthAdapter as c, type HttpXAuthAdapterOptions as d, ONE_POP_DEPLOYMENTS as e, ONE_POP_FEE_BPS as f, type PopApiPaths as g, type PopContracts as h, type PopDeployment as i, type PopEnvironment as j, type XAuthClientOptions as k, generatePkce as l, generateState as m, getCrossAuthBaseUrl as n, getOnePopApiBaseUrl as o, getOnePopContracts as p, getOnePopDeployment as q, getPopEnvironment as r, resolvePopEnvironment as s };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { S as SafeDropChainPort, a as SafeDropApiPort, C as CryptoPort, D as DepositParams, b as DepositResult, B as BatchClaimParams, c as BatchClaimResult, d as ClaimSignerPort, W as WithdrawParams, e as WithdrawResult, R as RefundParams, f as RefundResult, A as Address, g as Secret } from './createSafeDrop-D23me-Z-.js';
2
- export { h as BatchClaimSignature, i as DropInbox, j as DropSender, k as DropState, E as Envelope, H as Hex, l as HistoryEntry, m as HistoryPage, n as HistoryParty, o as HistoryType, I as Id, L as LeaderboardEntry, p as LeaderboardPage, M as MappedDepositResult, q as MyLeaderboardEntry, O as OAuthProof, r as OnePopDrop, P as PendingDrop, s as RecipientChangeSignature, t as RecipientSignature, u as RejectedDrops, v as SafeDrop, w as SafeDropConfig, x as SafeDropDeps, y as SafeDropError, z as SafeDropErrorCode, F as SecretHash, G as SocialIdentifier, J as SocialProvider, U as UnmappedDepositResult, X as XConnection, K as createSafeDrop } from './createSafeDrop-D23me-Z-.js';
1
+ import { m as SafeDropApiPort, D as DepositParams, b as DepositResult, B as BatchClaimParams, k as BatchClaimResult, W as WithdrawParams, c as WithdrawResult, R as RefundParams, d as RefundResult, A as Address, S as Secret } from './SafeDropApiPort-BLewfSaK.js';
2
+ export { l as BatchClaimSignature, e as DropInbox, n as DropSender, E as Envelope, H as Hex, o as HistoryEntry, h as HistoryPage, p as HistoryParty, q as HistoryReceiverParty, g as HistoryStatus, f as HistoryType, I as Id, r as LeaderboardEntry, L as LeaderboardPage, s as MappedDepositResult, M as MyLeaderboardEntry, O as OAuthProof, P as PendingDrop, i as RecipientChangeSignature, j as RecipientSignature, t as SafeDropError, u as SafeDropErrorCode, a as SecretHash, v as SocialIdentifier, w as SocialProvider, U as UnmappedDepositResult, X as XConnection } from './SafeDropApiPort-BLewfSaK.js';
3
+ import { a as SafeDropChainPort, C as CryptoPort, b as ClaimSignerPort } from './createSafeDrop-DfiY8vHq.js';
4
+ export { D as DropState, O as OnePopDrop, S as SafeDrop, c as SafeDropConfig, d as SafeDropDeps, e as createSafeDrop } from './createSafeDrop-DfiY8vHq.js';
3
5
  export { X as XAuthPort, a as XTokenResult } from './XAuthPort-BNJePosj.js';
4
6
 
5
7
  /**
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as e,b as r,c as o,d as t,e as p,f as a,g as s,h as i,i as f,j as n,k as d}from"./chunk-C2BI3ADY.js";export{i as BatchClaimUseCase,s as DepositUseCase,n as RefundUseCase,e as SafeDropError,f as WithdrawUseCase,a as buildComposeUrl,p as buildDmText,o as buildWithdrawLink,d as createSafeDrop,r as normalizeHandle,t as parseWithdrawLink};
1
+ import{a as e,b as o,c as t,d as p,e as a,f as s,g as i,h as f,i as n,j as m}from"./chunk-GS7GCYQD.js";import{a as r}from"./chunk-5DT3H2VX.js";export{i as BatchClaimUseCase,s as DepositUseCase,n as RefundUseCase,r as SafeDropError,f as WithdrawUseCase,a as buildComposeUrl,p as buildDmText,o as buildWithdrawLink,m as createSafeDrop,e as normalizeHandle,t as parseWithdrawLink};
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { v as SafeDrop } from '../createSafeDrop-D23me-Z-.js';
3
+ import { S as SafeDrop } from '../createSafeDrop-DfiY8vHq.js';
4
+ import '../SafeDropApiPort-BLewfSaK.js';
4
5
 
5
6
  interface SafeDropProviderProps {
6
7
  client: SafeDrop;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexus-cross/pop",
3
- "version": "1.4.0-beta.5",
3
+ "version": "2.4.0-beta.1",
4
4
  "description": "pop — framework-agnostic core + React adapter.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,6 +18,10 @@
18
18
  "types": "./dist/adapters/index.d.ts",
19
19
  "import": "./dist/adapters/index.js"
20
20
  },
21
+ "./api": {
22
+ "types": "./dist/api/index.d.ts",
23
+ "import": "./dist/api/index.js"
24
+ },
21
25
  "./AGENTS.md": "./AGENTS.md",
22
26
  "./openapi.json": "./src/infrastructure/openapi.json"
23
27
  },
@@ -80,7 +80,7 @@
80
80
  "BearerAuth": []
81
81
  }
82
82
  ],
83
- "description": "Resolves the caller's handle from their active X connection (POST /x-connections) — no per-request X token. Senders with an active X connection carry handle/profile; others are address-only. amount/total_amount are wei decimal strings. mapped_recipient/pending_change_to mirror the on-chain id→recipient mapping (empty string = unmapped/none); when mapped, deposits must use depositMapped and POST /wallets responds 409. items[].drop_key is the bytes32 id for withdrawMapped/withdrawUnmapped calls. has_claimed_before is derived from the mapping row.",
83
+ "description": "Resolves the caller's handle from their active X connection (POST /x-connections) — no per-request X token. items[].sender profile fields are the snapshot recorded when the deposit was indexed, not the sponsor's current X mapping; empty means the sponsor had no active X connection at that time (address-only sender). amount/total_amount are wei decimal strings. mapped_recipient/pending_change_to mirror the on-chain id→recipient mapping (empty string = unmapped/none); when mapped, deposits must use depositMapped and POST /wallets responds 409. items[].drop_key is the bytes32 id for withdrawMapped/withdrawUnmapped calls. has_claimed_before is derived from the mapping row. items[].rejected marks drops the caller rejected — display-only, and excluded from count/total_amount. token is required; drop_key narrows to one drop. Both filters apply to items and to count/total_amount alike.",
84
84
  "produces": [
85
85
  "application/json"
86
86
  ],
@@ -91,8 +91,15 @@
91
91
  "parameters": [
92
92
  {
93
93
  "type": "string",
94
- "description": "ERC20 token address filter (0x hex)",
94
+ "description": "ERC20 token address filter (0x hex, required)",
95
95
  "name": "token",
96
+ "in": "query",
97
+ "required": true
98
+ },
99
+ {
100
+ "type": "string",
101
+ "description": "on-chain bytes32 drop id (0x + 64 hex) — single-drop lookup",
102
+ "name": "drop_key",
96
103
  "in": "query"
97
104
  }
98
105
  ],
@@ -213,7 +220,7 @@
213
220
  "BearerAuth": []
214
221
  }
215
222
  ],
216
- "description": "Marks the given pending drops as rejected for the caller's X account. Display filter only — batch-claim and on-chain flows are untouched, so this does NOT prevent claiming: a batch claim that does not exclude these keys (withdrawUnmappedBatchByKeys) will still claim them. Rejected drops disappear from GET /drops (list and totals) and appear in GET /drops/rejected. All-or-nothing: every id must be a pending drop addressed to the caller's connected X handle, otherwise the whole request fails with 40003 (ids cannot be enumerated). Re-rejecting an already-rejected drop is idempotent. Duplicate ids are deduped; count reports unique ids. Max 100 ids per request. No un-reject.",
223
+ "description": "Marks the given pending drops as rejected for the caller's X account. Display filter only — batch-claim and on-chain flows are untouched, so this does NOT prevent claiming: a batch claim that does not exclude these keys (withdrawUnmappedBatchByKeys) will still claim them. Rejected drops stay in GET /drops flagged with items[].rejected, but are excluded from count/total_amount. All-or-nothing: every id must be a pending drop addressed to the caller's connected X handle, otherwise the whole request fails with 40003 (ids cannot be enumerated). Re-rejecting an already-rejected drop is idempotent. Duplicate ids are deduped; count reports unique ids. Max 100 ids per request. No un-reject.",
217
224
  "consumes": [
218
225
  "application/json"
219
226
  ],
@@ -223,7 +230,7 @@
223
230
  "tags": [
224
231
  "drops"
225
232
  ],
226
- "summary": "Reject pending drops (hide from the claim inbox)",
233
+ "summary": "Reject pending drops (flagged in the claim inbox, excluded from its totals)",
227
234
  "parameters": [
228
235
  {
229
236
  "description": "drop row ids to reject",
@@ -275,49 +282,6 @@
275
282
  }
276
283
  }
277
284
  },
278
- "/drops/rejected": {
279
- "get": {
280
- "security": [
281
- {
282
- "BearerAuth": []
283
- }
284
- ],
285
- "description": "Lists drops the caller rejected that are still pending on-chain, newest first (max 100). Same item shape as GET /drops. A drop refunded on-chain after rejection drops out of this list (status leaves pending). Identity comes from the wallet's active X connection, like GET /drops.",
286
- "produces": [
287
- "application/json"
288
- ],
289
- "tags": [
290
- "drops"
291
- ],
292
- "summary": "List the caller's rejected drops",
293
- "responses": {
294
- "200": {
295
- "description": "OK",
296
- "schema": {
297
- "$ref": "#/definitions/types.RejectedDropsResp"
298
- }
299
- },
300
- "401": {
301
- "description": "20003 UNAUTHORIZED",
302
- "schema": {
303
- "$ref": "#/definitions/handler.errorBody"
304
- }
305
- },
306
- "404": {
307
- "description": "40002 X_CONNECTION_NOT_FOUND",
308
- "schema": {
309
- "$ref": "#/definitions/handler.errorBody"
310
- }
311
- },
312
- "500": {
313
- "description": "10002 INTERNAL",
314
- "schema": {
315
- "$ref": "#/definitions/handler.errorBody"
316
- }
317
- }
318
- }
319
- }
320
- },
321
285
  "/envelopes": {
322
286
  "get": {
323
287
  "description": "Public endpoint. Returns active envelopes within their display window, sorted. name resolves by locale with English fallback.",
@@ -461,7 +425,7 @@
461
425
  "BearerAuth": []
462
426
  }
463
427
  ],
464
- "description": "Drops-based activity history. type=sent returns drops sponsored by the authenticated wallet; type=received returns every drop addressed to the wallet's active X handle, independent of which wallet claimed it (an active, non-expired X connection is required — 404 40002 otherwise). Both tabs include every status (pending/claimed/refunded), newest first. Each item carries both parties: sender (sponsor wallet + mapped X profile) and receiver (drop identifier handle + claiming wallet once withdrawn + mapped X profile); unmapped sides keep empty profile fields. amount is wei as a decimal string. The queried wallet always comes from the bearer token. rejected is true when the caller's X account rejected that drop (received only; always false on sent) — display-only, so rejected drops are still listed and counted, and status keeps its on-chain value.",
428
+ "description": "Drops-based activity history. type=sent returns drops sponsored by the authenticated wallet; type=received returns every drop addressed to the wallet's active X handle, independent of which wallet claimed it (an active, non-expired X connection is required — 404 40002 otherwise). Both tabs include every status (pending/claimed/refunded), newest first. sender/receiver profiles are the point-in-time snapshot recorded when the deposit was indexed, not the current X mapping: changing an avatar or disconnecting X does not alter past items. Empty profile fields mean that side had no active X connection at that time. receiver.handle is the drop's identifier and receiver.address is the claiming wallet (null until claimed) — for a recycled handle these two can refer to different accounts. amount is wei as a decimal string. The queried wallet always comes from the bearer token. rejected is true when the caller's X account rejected that drop (received only; always false on sent) — display-only, so rejected drops are still listed and counted, and status keeps its on-chain value. status narrows to one on-chain status; rejected narrows the received tab to the caller's own reject marks and is ignored on the sent tab. total reflects both filters.",
465
429
  "produces": [
466
430
  "application/json"
467
431
  ],
@@ -483,6 +447,18 @@
483
447
  "name": "token",
484
448
  "in": "query"
485
449
  },
450
+ {
451
+ "type": "string",
452
+ "description": "pending | claimed | refunded (default: all)",
453
+ "name": "status",
454
+ "in": "query"
455
+ },
456
+ {
457
+ "type": "string",
458
+ "description": "true | false — received tab only, ignored on sent",
459
+ "name": "rejected",
460
+ "in": "query"
461
+ },
486
462
  {
487
463
  "type": "integer",
488
464
  "description": "1-based page (default 1)",
@@ -1257,6 +1233,11 @@
1257
1233
  "type": "string",
1258
1234
  "example": "Happy birthday!"
1259
1235
  },
1236
+ "rejected": {
1237
+ "description": "Rejected reports whether the caller's X account rejected this\ndrop. Display-only, same meaning as HistoryItem.rejected:\nrejected drops stay in items but are excluded from count and\ntotal_amount, which report what is actually claimable.",
1238
+ "type": "boolean",
1239
+ "example": false
1240
+ },
1260
1241
  "sender": {
1261
1242
  "$ref": "#/definitions/types.DropSender"
1262
1243
  },
@@ -1307,7 +1288,7 @@
1307
1288
  "example": "Amy Ryoon"
1308
1289
  },
1309
1290
  "handle": {
1310
- "description": "Handle/DisplayName/ProfileImageURL are empty when the sponsor has\nno active X connection (address-only sender per the UI spec).",
1291
+ "description": "Handle/DisplayName/ProfileImageURL are the X snapshot recorded on\nthe drops row when the deposit was indexed, not the sponsor's\ncurrent X mapping. Empty when the sponsor had no active X\nconnection when the deposit was indexed (address-only sender per\nthe UI spec).",
1311
1292
  "type": "string",
1312
1293
  "example": "amyhryoon"
1313
1294
  },
@@ -1715,21 +1696,6 @@
1715
1696
  }
1716
1697
  }
1717
1698
  },
1718
- "types.RejectedDropsResp": {
1719
- "type": "object",
1720
- "properties": {
1721
- "count": {
1722
- "type": "integer",
1723
- "example": 3
1724
- },
1725
- "items": {
1726
- "type": "array",
1727
- "items": {
1728
- "$ref": "#/definitions/types.DropItem"
1729
- }
1730
- }
1731
- }
1732
- },
1733
1699
  "types.RetrievePrivateKeyReq": {
1734
1700
  "type": "object",
1735
1701
  "required": [
@@ -1 +0,0 @@
1
- var i=class extends Error{constructor(e,t,n){super(t),this.name="SafeDropError",this.code=e,this.details=n}};function g(a){return a.trim().replace(/^@/,"").toLowerCase()}function w(a){let e=a.baseUrl.replace(/\/$/,""),t=e.includes("?")?"&":"?",n=a.claimAddress?`&claim=${encodeURIComponent(a.claimAddress)}`:"",s=`${e}${t}sender=${encodeURIComponent(a.sender)}${n}`;return a.secret?`${s}#${encodeURIComponent(a.secret)}`:s}function b(a){let e=a.indexOf("#"),t=e===-1?a:a.slice(0,e),n=e===-1?"":a.slice(e+1),s=t.indexOf("?"),d=s===-1?"":t.slice(s+1);return{sender:A(d,"sender"),secret:n?decodeURIComponent(n):null}}function M(a){return`\u{1F381} You've received a one-pop drop! Claim your tokens here: ${a}`}function H(a,e){let t=[`text=${encodeURIComponent(a)}`];return e&&t.push(`recipient_id=${encodeURIComponent(e)}`),`https://x.com/messages/compose?${t.join("&")}`}function A(a,e){if(!a)return null;for(let t of a.split("&")){if(!t)continue;let n=t.indexOf("=");if(decodeURIComponent(n===-1?t:t.slice(0,n))===e)return decodeURIComponent(n===-1?"":t.slice(n+1))}return null}var y=class{constructor(e,t,n,s){this.chain=e;this.api=t;this.crypto=n;this.claimBaseUrl=s}async execute(e){if(!e.sender)throw new i("MISSING_SENDER","sender address is required");if(!e.recipient?.handle)throw new i("MISSING_RECIPIENT","recipient social handle is required");if(!e.token)throw new i("INVALID_TOKEN","token address is required (ERC20 only)");if(e.amount<=0n)throw new i("INVALID_AMOUNT","amount must be a positive BigInt");if(e.message&&[...e.message].length>140)throw new i("INVALID_PARAM","message must be <= 140 runes");let t=g(e.recipient.handle);if(!t)throw new i("MISSING_RECIPIENT","recipient handle normalizes to empty");let n=await this.api.createClaimWallet({sender:e.sender,recipient:{...e.recipient,handle:t}}),s=n.id,d=async m=>{if(this.api.recordDropMetadata)try{await this.api.recordDropMetadata({txHash:m,message:e.message,envelopeId:e.envelopeId})}catch(c){if(c instanceof i&&c.details?.codeName==="ENVELOPE_NOT_FOUND"&&e.envelopeId)try{await this.api.recordDropMetadata({txHash:m,message:e.message})}catch{}}};if(n.isMapped){let{txHash:m,dropKey:c}=await this.chain.depositMapped({token:e.token,amount:e.amount,id:s,onSubmitted:d});return{isMapped:!0,dropKey:c,depositTxHash:m}}let o=e.secret??this.crypto.randomSecret();if(!o)throw new i("MISSING_SECRET","secret is empty");let r=this.crypto.keccak256(o),l={claimAddress:n.claimAddress,token:e.token,amount:e.amount,id:s,secretHash:r,onSubmitted:d},{txHash:h,dropKey:u}=await this.chain.depositWithPermit(l);return{isMapped:!1,dropKey:u,claimAddress:n.claimAddress,secretHash:r,withdrawLink:w({baseUrl:this.claimBaseUrl,sender:e.sender,claimAddress:n.claimAddress,secret:o}),depositTxHash:h}}};var N=BigInt(1800),x=30,f=class{constructor(e,t,n,s=()=>Date.now()){this.chain=e;this.api=t;this.crypto=n;this.now=s}async execute(e){let t=g(e.id);if(!t)throw new i("MISSING_RECIPIENT","batchClaim requires an id");if(!e.claimAddress)throw new i("MISSING_CLAIM_ADDRESS","claim address is required");if(!e.sponsor)throw new i("MISSING_SENDER","sponsor address is required");if(!e.claimKey)throw new i("MISSING_CLAIM_KEY","claim key is required");if(!e.secret)throw new i("MISSING_SECRET","secret is required");if(!this.api.requestBatchClaimSignature)throw new i("API_ERROR","requestBatchClaimSignature is not implemented");if(!this.chain.getValidatorNonce||!this.chain.getValidatorClaimDigest||!this.chain.getValidator)throw new i("CHAIN_ERROR","validator reads are not implemented");let n=await this.chain.getDropKeyByClaimAddress(e.claimAddress,e.sponsor);if(!n)throw new i("DROP_NOT_FOUND","anchor drop not found");let s=await this.chain.getDropByKey(n);if(!s||s.id!==t)throw new i("DROP_NOT_FOUND","anchor drop id does not match");let d=await this.chain.getPendingDropsById({id:t});if(!d.length)throw new i("DROP_NOT_FOUND",`No pending drops for ${t}`);let o=e.maxCount??x;if(!Number.isSafeInteger(o)||o<=0)throw new i("INVALID_PARAM","maxCount must be a positive safe integer");let r=e.deadline??BigInt(Math.floor(this.now()/1e3))+(e.deadlineTtlSeconds??N),l=[],h="",u=BigInt(0);for(;d.length;){let m=await this.chain.getValidatorNonce({id:t}),c=await this.api.requestBatchClaimSignature({id:t,oauth:e.oauth,nonce:m,deadline:r});if(e.recipient&&e.recipient.toLowerCase()!==c.recipient.toLowerCase())throw new i("SIGN_FAILED","validator signature is bound to a different recipient");if(h&&h.toLowerCase()!==c.recipient.toLowerCase())throw new i("SIGN_FAILED","validator recipient changed between batches");h=c.recipient,l.length||(u=c.nonce);let R=await this.chain.getValidatorClaimDigest({recipient:h,id:t,nonce:c.nonce,deadline:c.deadline});await this.assertSignedByValidator(R,c.signature);let I=d.length,C=await this.chain.withdrawUnmappedBatchByKeys({claimKey:e.claimKey,recipient:h,id:t,anchorDropKey:n,secret:e.secret,nonce:c.nonce,deadline:c.deadline,dropKeys:d.slice(0,o).map(S=>S.dropKey).filter(S=>S.toLowerCase()!==n.toLowerCase()),validatorSignature:c.signature});if(l.push(C),d=await this.chain.getPendingDropsById({id:t}),d.length>=I)throw new i("CHAIN_ERROR","batch claim made no progress",{id:t,txHash:C})}return{txHashes:l,recipient:h,id:t,nonce:u,deadline:r}}async assertSignedByValidator(e,t){if(!this.crypto.recoverAddress||!this.chain.getValidator)return;let[n,s]=await Promise.all([this.crypto.recoverAddress({digest:e,signature:t}),this.chain.getValidator()]);if(n.toLowerCase()!==s.toLowerCase())throw new i("SIGN_FAILED","batch-claim signature does not recover to validator")}};var D=class{constructor(e,t){this.chain=e;this.signer=t}async execute(e){if(!e.recipient)throw new i("MISSING_RECIPIENT","recipient address is required");if(!e.claimAddress)throw new i("MISSING_CLAIM_ADDRESS","claim address is required");if(!e.secret)throw new i("MISSING_SECRET","secret is required");if(!e.claimKey)throw new i("MISSING_CLAIM_KEY","claim key is required");if(!e.sponsor)throw new i("MISSING_SENDER","sponsor address is required");let t=await this.chain.getDropKeyByClaimAddress(e.claimAddress,e.sponsor);if(!t)throw new i("DROP_NOT_FOUND",`No drop for claim address ${e.claimAddress}`);let n=await this.chain.getDropByKey(t);if(!n)throw new i("DROP_NOT_FOUND",`No drop for key ${t}`);let s=await this.chain.getClaimDigest({recipient:e.recipient,id:n.id,dropKey:t}),d=await this.signer.signDigest({claimKey:e.claimKey,digest:s});return{txHash:await this.chain.withdrawUnmapped({claimKey:e.claimKey,recipient:e.recipient,id:n.id,dropKey:t,signature:d,secret:e.secret})}}};var P=class{constructor(e){this.chain=e}async execute(e){if(!e.claimAddress)throw new i("MISSING_CLAIM_ADDRESS","claim address is required");if(!e.sponsor)throw new i("MISSING_SENDER","sponsor address is required");let t=await this.chain.getDropKeyByClaimAddress(e.claimAddress,e.sponsor);if(!t)throw new i("DROP_NOT_FOUND",`No drop for claim address ${e.claimAddress}`);return{txHash:await this.chain.refundByKey({dropKey:t})}}};function p(a,e){if(!a)throw new i("API_ERROR",`SafeDropApiPort.${e} is not implemented by the injected api adapter`);return a}function z(a,e){let t=new y(e.chain,e.api,e.crypto,a.claimBaseUrl),n=new D(e.chain,e.signer),s=new P(e.chain),d=new f(e.chain,e.api,e.crypto),{api:o}=e;return{config:a,deposit:r=>t.execute(r),retrieveClaimKey:r=>o.retrieveClaimKey(r),withdraw:r=>n.execute(r),refund:r=>s.execute(r),getDrop:(r,l)=>e.chain.getDrop(r,l),getDropByKey:r=>e.chain.getDropByKey(r),getDropKeyByClaimAddress:(r,l)=>e.chain.getDropKeyByClaimAddress(r,l),getClaimedRecipient:r=>e.chain.getClaimedRecipient({id:r}),getPendingDropsById:r=>e.chain.getPendingDropsById({id:r}),getPendingDropsByRecipient:r=>e.chain.getPendingDropsByRecipient({recipient:r}),getPendingDropsBySponsor:r=>e.chain.getPendingDropsBySponsor({sponsor:r}),withdrawUnmapped:r=>e.chain.withdrawUnmapped(r),withdrawMapped:r=>e.chain.withdrawMapped(r),batchWithdrawMapped:r=>e.chain.batchWithdrawMapped({count:r}),batchWithdrawMappedByKeys:r=>e.chain.batchWithdrawMappedByKeys({dropKeys:r}),refundByKey:r=>e.chain.refundByKey({dropKey:r}),getChangeNonce:r=>e.chain.getChangeNonce({id:r}),getPendingRecipientChange:r=>e.chain.getPendingRecipientChange({id:r}),requestRecipientChange:(r,l)=>e.chain.requestRecipientChange({id:r,newRecipient:l}),completeRecipientChange:r=>e.chain.completeRecipientChange(r),cancelRecipientChange:r=>e.chain.cancelRecipientChange({id:r}),resetRecipient:r=>e.chain.resetRecipient(r),listDrops:r=>p(o.listDrops,"listDrops").call(o,r),rejectDrops:r=>p(o.rejectDrops,"rejectDrops").call(o,{dropIds:r}),listRejectedDrops:()=>p(o.listRejectedDrops,"listRejectedDrops").call(o),listEnvelopes:r=>p(o.listEnvelopes,"listEnvelopes").call(o,r),getLeaderboard:r=>p(o.getLeaderboard,"getLeaderboard").call(o,r),getMyLeaderboardEntry:r=>p(o.getMyLeaderboardEntry,"getMyLeaderboardEntry").call(o,r),listHistories:r=>p(o.listHistories,"listHistories").call(o,r),getXConnection:()=>p(o.getXConnection,"getXConnection").call(o),connectX:r=>p(o.connectX,"connectX").call(o,r),disconnectX:()=>p(o.disconnectX,"disconnectX").call(o),submitFeedback:r=>p(o.submitFeedback,"submitFeedback").call(o,r),setRevealYou:r=>p(o.setRevealYou,"setRevealYou").call(o,{revealYou:r}),requestRecipientChangeSignature:r=>p(o.requestRecipientChangeSignature,"requestRecipientChangeSignature").call(o,r),requestRecipientResetSignature:r=>p(o.requestRecipientResetSignature,"requestRecipientResetSignature").call(o,r),batchClaim:r=>d.execute(r),requestBatchClaimSignature:r=>p(o.requestBatchClaimSignature,"requestBatchClaimSignature").call(o,r)}}export{i as a,g as b,w as c,b as d,M as e,H as f,y as g,f as h,D as i,P as j,z as k};