@pafi-dev/issuer-postgres 0.1.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 +178 -0
- package/dist/entities/index.cjs +303 -0
- package/dist/entities/index.cjs.map +1 -0
- package/dist/entities/index.d.cts +122 -0
- package/dist/entities/index.d.ts +122 -0
- package/dist/entities/index.js +292 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/index.cjs +764 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +97 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.js +749 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.cjs +138 -0
- package/dist/migrations/index.cjs.map +1 -0
- package/dist/migrations/index.d.cts +41 -0
- package/dist/migrations/index.d.ts +41 -0
- package/dist/migrations/index.js +110 -0
- package/dist/migrations/index.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @pafi-dev/issuer-postgres
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@pafi-dev/issuer-postgres)
|
|
4
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
|
+
|
|
6
|
+
Postgres-backed `IPointLedger` implementation for `@pafi-dev/issuer`.
|
|
7
|
+
TypeORM entities, migrations, and a fully-baked `PostgresPointLedger`
|
|
8
|
+
service issuers can drop in directly — no need to reverse-engineer the
|
|
9
|
+
schema from the reference issuer.
|
|
10
|
+
|
|
11
|
+
> **Server-only.** Pulls in TypeORM (peer-dep). Do not bundle into a browser app.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why this exists
|
|
16
|
+
|
|
17
|
+
Every issuer needs the same ledger schema:
|
|
18
|
+
|
|
19
|
+
- `user_balances` (per `(user, token)`)
|
|
20
|
+
- `locked_mint_requests` (PENDING reservations during mint)
|
|
21
|
+
- `pending_credits` (reverse flow — burn → off-chain credit)
|
|
22
|
+
- `ledger_journal` (append-only audit trail)
|
|
23
|
+
- `indexer_cursors` (block cursors for `PointIndexer` / `BurnIndexer`)
|
|
24
|
+
|
|
25
|
+
Plus the same race-safe transactions, expired-lock sweeps,
|
|
26
|
+
multi-token guards, and `userOpHash` binding for bundler-receipt
|
|
27
|
+
fallback. This package ships all of it.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Node.js >= 18
|
|
34
|
+
- TypeScript >= 5.0
|
|
35
|
+
- `typeorm` ^0.3.0 (peer)
|
|
36
|
+
- `viem` ^2.0.0 (peer, transitively via `@pafi-dev/issuer`)
|
|
37
|
+
- Postgres 13+ (for `gen_random_uuid()` / `pgcrypto`)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm add @pafi-dev/issuer-postgres @pafi-dev/issuer typeorm viem
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick start (NestJS)
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
53
|
+
import {
|
|
54
|
+
PostgresPointLedger,
|
|
55
|
+
PostgresCursorStore,
|
|
56
|
+
PAFI_ENTITIES,
|
|
57
|
+
PAFI_MIGRATIONS,
|
|
58
|
+
} from "@pafi-dev/issuer-postgres";
|
|
59
|
+
|
|
60
|
+
@Module({
|
|
61
|
+
imports: [
|
|
62
|
+
TypeOrmModule.forRoot({
|
|
63
|
+
type: "postgres",
|
|
64
|
+
url: process.env.DATABASE_URL,
|
|
65
|
+
entities: [...PAFI_ENTITIES /*, ...yourCustomEntities */],
|
|
66
|
+
migrations: [...PAFI_MIGRATIONS /*, ...yourCustomMigrations */],
|
|
67
|
+
migrationsRun: true,
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
providers: [
|
|
71
|
+
{
|
|
72
|
+
provide: "POINT_LEDGER",
|
|
73
|
+
useFactory: (dataSource: DataSource) =>
|
|
74
|
+
new PostgresPointLedger(dataSource, { logger: console }),
|
|
75
|
+
inject: [DataSource],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
provide: "INDEXER_CURSOR_STORE",
|
|
79
|
+
useFactory: (dataSource: DataSource) =>
|
|
80
|
+
new PostgresCursorStore(dataSource),
|
|
81
|
+
inject: [DataSource],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
})
|
|
85
|
+
export class LedgerModule {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then wire `POINT_LEDGER` into `createIssuerService({ ledger, ... })`
|
|
89
|
+
from `@pafi-dev/issuer`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## What you get
|
|
94
|
+
|
|
95
|
+
### `PostgresPointLedger`
|
|
96
|
+
|
|
97
|
+
Implements every method on `IPointLedger` from `@pafi-dev/issuer`:
|
|
98
|
+
|
|
99
|
+
- **Reads** — `getBalance`, `getLockedRequests`, `getMintLock`,
|
|
100
|
+
`getPendingCredit`, `listUserTransactions`
|
|
101
|
+
- **Writes** — `lockForMinting`, `releaseLock`, `deductBalance`,
|
|
102
|
+
`creditBalance`, `updateMintStatus`
|
|
103
|
+
- **Reverse flow** — `reservePendingCredit`, `resolveCreditByBurnTx`,
|
|
104
|
+
`findPendingCreditLockId`
|
|
105
|
+
- **Mobile flow** — `bindMintUserOpHash`, `bindCreditUserOpHash`
|
|
106
|
+
(bundler-receipt fallback against `PointIndexer`'s amount race)
|
|
107
|
+
|
|
108
|
+
All mutating methods run inside a TypeORM `transaction()` so balance
|
|
109
|
+
+ lock + journal updates land atomically.
|
|
110
|
+
|
|
111
|
+
### `PostgresCursorStore`
|
|
112
|
+
|
|
113
|
+
Implements `IIndexerCursorStore`. Default key is `"default"` for the
|
|
114
|
+
primary mint indexer. Use `.forKey(id)` to derive sibling stores for
|
|
115
|
+
the burn indexer or per-token shards:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const mintCursor = new PostgresCursorStore(dataSource);
|
|
119
|
+
const burnCursor = mintCursor.forKey(`burn:${pointTokenAddress.toLowerCase()}`);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Entities + migrations
|
|
123
|
+
|
|
124
|
+
Drop into your TypeORM config:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { PAFI_ENTITIES, PAFI_MIGRATIONS } from "@pafi-dev/issuer-postgres";
|
|
128
|
+
|
|
129
|
+
new DataSource({
|
|
130
|
+
entities: [...PAFI_ENTITIES, ...yourCustomEntities],
|
|
131
|
+
migrations: [...PAFI_MIGRATIONS, ...yourCustomMigrations],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`InitialSchema1700000000000` is one consolidated migration covering
|
|
136
|
+
all five tables + indexes (including the `user_op_hash` columns and
|
|
137
|
+
the bundler-fallback indexes). For schema changes after this baseline,
|
|
138
|
+
generate a follow-up migration in your own repo — never edit shipped
|
|
139
|
+
migrations in place.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Multi-token
|
|
144
|
+
|
|
145
|
+
Every mutating method requires `tokenAddress`. There is no "default
|
|
146
|
+
token" bucket — single-token issuers pass the same address every
|
|
147
|
+
call. The package throws if you forget, with a clear error message:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
PostgresPointLedger: tokenAddress is required on every call (multi-token ledger)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Balances are keyed by composite `(userAddress, tokenAddress)` PK.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Issuer-specific extensions
|
|
158
|
+
|
|
159
|
+
Don't subclass `PostgresPointLedger`. Instead:
|
|
160
|
+
|
|
161
|
+
1. Add your tables in your own repo (`campaign_rules`,
|
|
162
|
+
`kyc_status`, etc.) with their own TypeORM entities and a follow-up
|
|
163
|
+
migration.
|
|
164
|
+
2. Wrap or compose `PostgresPointLedger` if you need to layer business
|
|
165
|
+
rules on top of `creditBalance` / `lockForMinting`.
|
|
166
|
+
3. For audit fields specific to your issuer (e.g. partner_id,
|
|
167
|
+
campaign_id), extend `LedgerJournalEntity` via inheritance in your
|
|
168
|
+
own entity, register it in your `entities[]`, and add a migration
|
|
169
|
+
that ALTERs the column.
|
|
170
|
+
|
|
171
|
+
The shipped entities use TypeORM Discriminator-friendly defaults —
|
|
172
|
+
extending without breaking the schema is straightforward.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
Apache-2.0
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/entities/index.ts
|
|
29
|
+
var entities_exports = {};
|
|
30
|
+
__export(entities_exports, {
|
|
31
|
+
IndexerCursorEntity: () => IndexerCursorEntity,
|
|
32
|
+
LedgerJournalEntity: () => LedgerJournalEntity,
|
|
33
|
+
LockedMintEntity: () => LockedMintEntity,
|
|
34
|
+
PAFI_ENTITIES: () => PAFI_ENTITIES,
|
|
35
|
+
PendingCreditEntity: () => PendingCreditEntity,
|
|
36
|
+
UserBalanceEntity: () => UserBalanceEntity
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(entities_exports);
|
|
39
|
+
|
|
40
|
+
// src/entities/locked-mint.entity.ts
|
|
41
|
+
var import_typeorm = require("typeorm");
|
|
42
|
+
var LockedMintEntity = class {
|
|
43
|
+
id;
|
|
44
|
+
userAddress;
|
|
45
|
+
tokenAddress;
|
|
46
|
+
amount;
|
|
47
|
+
status;
|
|
48
|
+
createdAt;
|
|
49
|
+
expiresAt;
|
|
50
|
+
txHash;
|
|
51
|
+
userOpHash;
|
|
52
|
+
};
|
|
53
|
+
__decorateClass([
|
|
54
|
+
(0, import_typeorm.PrimaryGeneratedColumn)("uuid")
|
|
55
|
+
], LockedMintEntity.prototype, "id", 2);
|
|
56
|
+
__decorateClass([
|
|
57
|
+
(0, import_typeorm.Column)({ name: "user_address", type: "varchar", length: 42 })
|
|
58
|
+
], LockedMintEntity.prototype, "userAddress", 2);
|
|
59
|
+
__decorateClass([
|
|
60
|
+
(0, import_typeorm.Column)({ name: "token_address", type: "varchar", length: 42 })
|
|
61
|
+
], LockedMintEntity.prototype, "tokenAddress", 2);
|
|
62
|
+
__decorateClass([
|
|
63
|
+
(0, import_typeorm.Column)({
|
|
64
|
+
name: "amount",
|
|
65
|
+
type: "numeric",
|
|
66
|
+
precision: 78,
|
|
67
|
+
scale: 0,
|
|
68
|
+
transformer: {
|
|
69
|
+
to: (value) => value.toString(),
|
|
70
|
+
from: (value) => BigInt(value)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
], LockedMintEntity.prototype, "amount", 2);
|
|
74
|
+
__decorateClass([
|
|
75
|
+
(0, import_typeorm.Column)({
|
|
76
|
+
name: "status",
|
|
77
|
+
type: "varchar",
|
|
78
|
+
length: 16,
|
|
79
|
+
default: "PENDING"
|
|
80
|
+
})
|
|
81
|
+
], LockedMintEntity.prototype, "status", 2);
|
|
82
|
+
__decorateClass([
|
|
83
|
+
(0, import_typeorm.CreateDateColumn)({ name: "created_at" })
|
|
84
|
+
], LockedMintEntity.prototype, "createdAt", 2);
|
|
85
|
+
__decorateClass([
|
|
86
|
+
(0, import_typeorm.Column)({ name: "expires_at", type: "timestamp with time zone" })
|
|
87
|
+
], LockedMintEntity.prototype, "expiresAt", 2);
|
|
88
|
+
__decorateClass([
|
|
89
|
+
(0, import_typeorm.Column)({ name: "tx_hash", type: "varchar", length: 66, nullable: true })
|
|
90
|
+
], LockedMintEntity.prototype, "txHash", 2);
|
|
91
|
+
__decorateClass([
|
|
92
|
+
(0, import_typeorm.Column)({
|
|
93
|
+
name: "user_op_hash",
|
|
94
|
+
type: "varchar",
|
|
95
|
+
length: 66,
|
|
96
|
+
nullable: true
|
|
97
|
+
})
|
|
98
|
+
], LockedMintEntity.prototype, "userOpHash", 2);
|
|
99
|
+
LockedMintEntity = __decorateClass([
|
|
100
|
+
(0, import_typeorm.Entity)({ name: "locked_mint_requests" }),
|
|
101
|
+
(0, import_typeorm.Index)(["userAddress", "status"])
|
|
102
|
+
], LockedMintEntity);
|
|
103
|
+
|
|
104
|
+
// src/entities/pending-credit.entity.ts
|
|
105
|
+
var import_typeorm2 = require("typeorm");
|
|
106
|
+
var PendingCreditEntity = class {
|
|
107
|
+
id;
|
|
108
|
+
userAddress;
|
|
109
|
+
tokenAddress;
|
|
110
|
+
amount;
|
|
111
|
+
status;
|
|
112
|
+
txHash;
|
|
113
|
+
userOpHash;
|
|
114
|
+
createdAt;
|
|
115
|
+
expiresAt;
|
|
116
|
+
resolvedAt;
|
|
117
|
+
};
|
|
118
|
+
__decorateClass([
|
|
119
|
+
(0, import_typeorm2.PrimaryGeneratedColumn)("uuid")
|
|
120
|
+
], PendingCreditEntity.prototype, "id", 2);
|
|
121
|
+
__decorateClass([
|
|
122
|
+
(0, import_typeorm2.Column)({ name: "user_address", type: "varchar", length: 42 })
|
|
123
|
+
], PendingCreditEntity.prototype, "userAddress", 2);
|
|
124
|
+
__decorateClass([
|
|
125
|
+
(0, import_typeorm2.Column)({ name: "token_address", type: "varchar", length: 42 })
|
|
126
|
+
], PendingCreditEntity.prototype, "tokenAddress", 2);
|
|
127
|
+
__decorateClass([
|
|
128
|
+
(0, import_typeorm2.Column)({
|
|
129
|
+
name: "amount",
|
|
130
|
+
type: "numeric",
|
|
131
|
+
precision: 78,
|
|
132
|
+
scale: 0,
|
|
133
|
+
transformer: {
|
|
134
|
+
to: (value) => value.toString(),
|
|
135
|
+
from: (value) => BigInt(value)
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
], PendingCreditEntity.prototype, "amount", 2);
|
|
139
|
+
__decorateClass([
|
|
140
|
+
(0, import_typeorm2.Column)({
|
|
141
|
+
name: "status",
|
|
142
|
+
type: "varchar",
|
|
143
|
+
length: 16,
|
|
144
|
+
default: "PENDING"
|
|
145
|
+
})
|
|
146
|
+
], PendingCreditEntity.prototype, "status", 2);
|
|
147
|
+
__decorateClass([
|
|
148
|
+
(0, import_typeorm2.Column)({ name: "tx_hash", type: "varchar", length: 66, nullable: true })
|
|
149
|
+
], PendingCreditEntity.prototype, "txHash", 2);
|
|
150
|
+
__decorateClass([
|
|
151
|
+
(0, import_typeorm2.Column)({
|
|
152
|
+
name: "user_op_hash",
|
|
153
|
+
type: "varchar",
|
|
154
|
+
length: 66,
|
|
155
|
+
nullable: true
|
|
156
|
+
})
|
|
157
|
+
], PendingCreditEntity.prototype, "userOpHash", 2);
|
|
158
|
+
__decorateClass([
|
|
159
|
+
(0, import_typeorm2.CreateDateColumn)({ name: "created_at" })
|
|
160
|
+
], PendingCreditEntity.prototype, "createdAt", 2);
|
|
161
|
+
__decorateClass([
|
|
162
|
+
(0, import_typeorm2.Column)({ name: "expires_at", type: "timestamp with time zone" })
|
|
163
|
+
], PendingCreditEntity.prototype, "expiresAt", 2);
|
|
164
|
+
__decorateClass([
|
|
165
|
+
(0, import_typeorm2.Column)({
|
|
166
|
+
name: "resolved_at",
|
|
167
|
+
type: "timestamp with time zone",
|
|
168
|
+
nullable: true
|
|
169
|
+
})
|
|
170
|
+
], PendingCreditEntity.prototype, "resolvedAt", 2);
|
|
171
|
+
PendingCreditEntity = __decorateClass([
|
|
172
|
+
(0, import_typeorm2.Entity)({ name: "pending_credits" }),
|
|
173
|
+
(0, import_typeorm2.Index)(["userAddress", "status"]),
|
|
174
|
+
(0, import_typeorm2.Index)(["txHash"])
|
|
175
|
+
], PendingCreditEntity);
|
|
176
|
+
|
|
177
|
+
// src/entities/user-balance.entity.ts
|
|
178
|
+
var import_typeorm3 = require("typeorm");
|
|
179
|
+
var UserBalanceEntity = class {
|
|
180
|
+
userAddress;
|
|
181
|
+
tokenAddress;
|
|
182
|
+
balance;
|
|
183
|
+
updatedAt;
|
|
184
|
+
};
|
|
185
|
+
__decorateClass([
|
|
186
|
+
(0, import_typeorm3.PrimaryColumn)({ name: "user_address", type: "varchar", length: 42 })
|
|
187
|
+
], UserBalanceEntity.prototype, "userAddress", 2);
|
|
188
|
+
__decorateClass([
|
|
189
|
+
(0, import_typeorm3.PrimaryColumn)({ name: "token_address", type: "varchar", length: 42 })
|
|
190
|
+
], UserBalanceEntity.prototype, "tokenAddress", 2);
|
|
191
|
+
__decorateClass([
|
|
192
|
+
(0, import_typeorm3.Column)({
|
|
193
|
+
name: "balance",
|
|
194
|
+
type: "numeric",
|
|
195
|
+
precision: 78,
|
|
196
|
+
scale: 0,
|
|
197
|
+
default: 0,
|
|
198
|
+
transformer: {
|
|
199
|
+
to: (value) => value.toString(),
|
|
200
|
+
from: (value) => BigInt(value)
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
], UserBalanceEntity.prototype, "balance", 2);
|
|
204
|
+
__decorateClass([
|
|
205
|
+
(0, import_typeorm3.UpdateDateColumn)({ name: "updated_at" })
|
|
206
|
+
], UserBalanceEntity.prototype, "updatedAt", 2);
|
|
207
|
+
UserBalanceEntity = __decorateClass([
|
|
208
|
+
(0, import_typeorm3.Entity)({ name: "user_balances" })
|
|
209
|
+
], UserBalanceEntity);
|
|
210
|
+
|
|
211
|
+
// src/entities/ledger-journal.entity.ts
|
|
212
|
+
var import_typeorm4 = require("typeorm");
|
|
213
|
+
var LedgerJournalEntity = class {
|
|
214
|
+
id;
|
|
215
|
+
userAddress;
|
|
216
|
+
tokenAddress;
|
|
217
|
+
delta;
|
|
218
|
+
reason;
|
|
219
|
+
txHash;
|
|
220
|
+
createdAt;
|
|
221
|
+
};
|
|
222
|
+
__decorateClass([
|
|
223
|
+
(0, import_typeorm4.PrimaryGeneratedColumn)("uuid")
|
|
224
|
+
], LedgerJournalEntity.prototype, "id", 2);
|
|
225
|
+
__decorateClass([
|
|
226
|
+
(0, import_typeorm4.Column)({ name: "user_address", type: "varchar", length: 42 })
|
|
227
|
+
], LedgerJournalEntity.prototype, "userAddress", 2);
|
|
228
|
+
__decorateClass([
|
|
229
|
+
(0, import_typeorm4.Column)({ name: "token_address", type: "varchar", length: 42 })
|
|
230
|
+
], LedgerJournalEntity.prototype, "tokenAddress", 2);
|
|
231
|
+
__decorateClass([
|
|
232
|
+
(0, import_typeorm4.Column)({
|
|
233
|
+
name: "delta",
|
|
234
|
+
type: "numeric",
|
|
235
|
+
precision: 78,
|
|
236
|
+
scale: 0,
|
|
237
|
+
transformer: {
|
|
238
|
+
to: (value) => value.toString(),
|
|
239
|
+
from: (value) => BigInt(value)
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
], LedgerJournalEntity.prototype, "delta", 2);
|
|
243
|
+
__decorateClass([
|
|
244
|
+
(0, import_typeorm4.Column)({ name: "reason", type: "varchar", length: 128 })
|
|
245
|
+
], LedgerJournalEntity.prototype, "reason", 2);
|
|
246
|
+
__decorateClass([
|
|
247
|
+
(0, import_typeorm4.Column)({ name: "tx_hash", type: "varchar", length: 66, nullable: true })
|
|
248
|
+
], LedgerJournalEntity.prototype, "txHash", 2);
|
|
249
|
+
__decorateClass([
|
|
250
|
+
(0, import_typeorm4.CreateDateColumn)({ name: "created_at" })
|
|
251
|
+
], LedgerJournalEntity.prototype, "createdAt", 2);
|
|
252
|
+
LedgerJournalEntity = __decorateClass([
|
|
253
|
+
(0, import_typeorm4.Entity)({ name: "ledger_journal" }),
|
|
254
|
+
(0, import_typeorm4.Index)(["userAddress", "createdAt"])
|
|
255
|
+
], LedgerJournalEntity);
|
|
256
|
+
|
|
257
|
+
// src/entities/indexer-cursor.entity.ts
|
|
258
|
+
var import_typeorm5 = require("typeorm");
|
|
259
|
+
var IndexerCursorEntity = class {
|
|
260
|
+
id;
|
|
261
|
+
nextBlock;
|
|
262
|
+
updatedAt;
|
|
263
|
+
};
|
|
264
|
+
__decorateClass([
|
|
265
|
+
(0, import_typeorm5.PrimaryColumn)({ type: "varchar", length: 64 })
|
|
266
|
+
], IndexerCursorEntity.prototype, "id", 2);
|
|
267
|
+
__decorateClass([
|
|
268
|
+
(0, import_typeorm5.Column)({
|
|
269
|
+
name: "next_block",
|
|
270
|
+
type: "numeric",
|
|
271
|
+
precision: 78,
|
|
272
|
+
scale: 0,
|
|
273
|
+
transformer: {
|
|
274
|
+
to: (value) => value.toString(),
|
|
275
|
+
from: (value) => BigInt(value)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
], IndexerCursorEntity.prototype, "nextBlock", 2);
|
|
279
|
+
__decorateClass([
|
|
280
|
+
(0, import_typeorm5.UpdateDateColumn)({ name: "updated_at" })
|
|
281
|
+
], IndexerCursorEntity.prototype, "updatedAt", 2);
|
|
282
|
+
IndexerCursorEntity = __decorateClass([
|
|
283
|
+
(0, import_typeorm5.Entity)({ name: "indexer_cursors" })
|
|
284
|
+
], IndexerCursorEntity);
|
|
285
|
+
|
|
286
|
+
// src/entities/index.ts
|
|
287
|
+
var PAFI_ENTITIES = [
|
|
288
|
+
LockedMintEntity,
|
|
289
|
+
PendingCreditEntity,
|
|
290
|
+
UserBalanceEntity,
|
|
291
|
+
LedgerJournalEntity,
|
|
292
|
+
IndexerCursorEntity
|
|
293
|
+
];
|
|
294
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
295
|
+
0 && (module.exports = {
|
|
296
|
+
IndexerCursorEntity,
|
|
297
|
+
LedgerJournalEntity,
|
|
298
|
+
LockedMintEntity,
|
|
299
|
+
PAFI_ENTITIES,
|
|
300
|
+
PendingCreditEntity,
|
|
301
|
+
UserBalanceEntity
|
|
302
|
+
});
|
|
303
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/entities/index.ts","../../src/entities/locked-mint.entity.ts","../../src/entities/pending-credit.entity.ts","../../src/entities/user-balance.entity.ts","../../src/entities/ledger-journal.entity.ts","../../src/entities/indexer-cursor.entity.ts"],"sourcesContent":["export { LockedMintEntity } from \"./locked-mint.entity\";\nexport {\n PendingCreditEntity,\n type PendingCreditStatus,\n} from \"./pending-credit.entity\";\nexport { UserBalanceEntity } from \"./user-balance.entity\";\nexport { LedgerJournalEntity } from \"./ledger-journal.entity\";\nexport { IndexerCursorEntity } from \"./indexer-cursor.entity\";\n\nimport { LockedMintEntity } from \"./locked-mint.entity\";\nimport { PendingCreditEntity } from \"./pending-credit.entity\";\nimport { UserBalanceEntity } from \"./user-balance.entity\";\nimport { LedgerJournalEntity } from \"./ledger-journal.entity\";\nimport { IndexerCursorEntity } from \"./indexer-cursor.entity\";\n\n/**\n * All entities in one array — drop into TypeORM's `entities` config or\n * NestJS's `TypeOrmModule.forFeature(PAFI_ENTITIES)`.\n */\nexport const PAFI_ENTITIES = [\n LockedMintEntity,\n PendingCreditEntity,\n UserBalanceEntity,\n LedgerJournalEntity,\n IndexerCursorEntity,\n] as const;\n","import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n PrimaryGeneratedColumn,\n} from \"typeorm\";\nimport type { MintingStatus } from \"@pafi-dev/issuer\";\n\n/**\n * A reservation against a user's off-chain balance.\n *\n * Lifecycle:\n * PENDING ── PointIndexer matches Mint event ──▶ MINTED\n * │\n * ├── deadline elapsed ────────────────────▶ EXPIRED\n * │\n * └── tx reverted ─────────────────────────▶ FAILED\n *\n * The `(userAddress, status)` composite index keeps the \"sum all\n * PENDING locks\" hot path of `lockForMinting()` fast under load.\n */\n@Entity({ name: \"locked_mint_requests\" })\n@Index([\"userAddress\", \"status\"])\nexport class LockedMintEntity {\n @PrimaryGeneratedColumn(\"uuid\")\n id!: string;\n\n @Column({ name: \"user_address\", type: \"varchar\", length: 42 })\n userAddress!: string;\n\n @Column({ name: \"token_address\", type: \"varchar\", length: 42 })\n tokenAddress!: string;\n\n @Column({\n name: \"amount\",\n type: \"numeric\",\n precision: 78,\n scale: 0,\n transformer: {\n to: (value: bigint) => value.toString(),\n from: (value: string) => BigInt(value),\n },\n })\n amount!: bigint;\n\n @Column({\n name: \"status\",\n type: \"varchar\",\n length: 16,\n default: \"PENDING\",\n })\n status!: MintingStatus;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt!: Date;\n\n @Column({ name: \"expires_at\", type: \"timestamp with time zone\" })\n expiresAt!: Date;\n\n @Column({ name: \"tx_hash\", type: \"varchar\", length: 66, nullable: true })\n txHash?: string | null;\n\n /**\n * ERC-4337 userOpHash returned by the bundler at /claim/submit.\n * Bound to the lock so `/claim/status` can fall back to the bundler\n * receipt — required when multiple PENDING locks share the same\n * `amount` (PointIndexer matches by amount and can pick the wrong\n * sibling lock).\n */\n @Column({\n name: \"user_op_hash\",\n type: \"varchar\",\n length: 66,\n nullable: true,\n })\n userOpHash?: string | null;\n}\n","import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n PrimaryGeneratedColumn,\n} from \"typeorm\";\n\nexport type PendingCreditStatus = \"PENDING\" | \"RESOLVED\" | \"EXPIRED\";\n\n/**\n * Reverse flow — user burns on-chain PT, `BurnIndexer` observes\n * `Transfer(user → 0x0)`, the credit is settled to the off-chain ledger.\n *\n * Lifecycle:\n * PENDING ── Burn tx observed by indexer ──▶ RESOLVED\n * │\n * └── deadline elapsed ────────────────▶ EXPIRED\n *\n * The credit is reserved BEFORE the UserOp is submitted so the\n * indexer can correlate `(user, amount, token)` back to the off-chain\n * row when the burn lands.\n */\n@Entity({ name: \"pending_credits\" })\n@Index([\"userAddress\", \"status\"])\n@Index([\"txHash\"])\nexport class PendingCreditEntity {\n @PrimaryGeneratedColumn(\"uuid\")\n id!: string;\n\n @Column({ name: \"user_address\", type: \"varchar\", length: 42 })\n userAddress!: string;\n\n @Column({ name: \"token_address\", type: \"varchar\", length: 42 })\n tokenAddress!: string;\n\n @Column({\n name: \"amount\",\n type: \"numeric\",\n precision: 78,\n scale: 0,\n transformer: {\n to: (value: bigint) => value.toString(),\n from: (value: string) => BigInt(value),\n },\n })\n amount!: bigint;\n\n @Column({\n name: \"status\",\n type: \"varchar\",\n length: 16,\n default: \"PENDING\",\n })\n status!: PendingCreditStatus;\n\n /** On-chain burn tx that settled this credit. Null until indexer resolves. */\n @Column({ name: \"tx_hash\", type: \"varchar\", length: 66, nullable: true })\n txHash?: string | null;\n\n /** ERC-4337 userOpHash bound at /redeem/submit. See `LockedMintEntity`. */\n @Column({\n name: \"user_op_hash\",\n type: \"varchar\",\n length: 66,\n nullable: true,\n })\n userOpHash?: string | null;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt!: Date;\n\n @Column({ name: \"expires_at\", type: \"timestamp with time zone\" })\n expiresAt!: Date;\n\n @Column({\n name: \"resolved_at\",\n type: \"timestamp with time zone\",\n nullable: true,\n })\n resolvedAt?: Date | null;\n}\n","import { Column, Entity, PrimaryColumn, UpdateDateColumn } from \"typeorm\";\n\n/**\n * Off-chain point balance per `(userAddress, tokenAddress)`.\n *\n * `balance` is the **total** owned; pending reservations live in\n * `LockedMintEntity`. Available balance = total − sum(PENDING locks)\n * — `getBalance` in `PostgresPointLedger` does this subtraction\n * inside a transaction so reads are race-free.\n *\n * All amounts are `numeric(78, 0)` for full bigint precision (uint256\n * fits in 78 decimal digits). TypeORM transforms bigint ↔ string at\n * the boundary; in JS/TS code we always deal with `bigint`.\n */\n@Entity({ name: \"user_balances\" })\nexport class UserBalanceEntity {\n @PrimaryColumn({ name: \"user_address\", type: \"varchar\", length: 42 })\n userAddress!: string;\n\n @PrimaryColumn({ name: \"token_address\", type: \"varchar\", length: 42 })\n tokenAddress!: string;\n\n @Column({\n name: \"balance\",\n type: \"numeric\",\n precision: 78,\n scale: 0,\n default: 0,\n transformer: {\n to: (value: bigint) => value.toString(),\n from: (value: string) => BigInt(value),\n },\n })\n balance!: bigint;\n\n @UpdateDateColumn({ name: \"updated_at\" })\n updatedAt!: Date;\n}\n","import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n PrimaryGeneratedColumn,\n} from \"typeorm\";\n\n/**\n * Append-only audit trail for every balance mutation. Used for\n * reconciliation, customer support, and regulatory reporting.\n *\n * Sign convention:\n * - positive `delta` — credit (merchant award, refund, manual top-up)\n * - negative `delta` — debit (mint confirmation against the off-chain\n * balance; `txHash` references the on-chain Mint event)\n */\n@Entity({ name: \"ledger_journal\" })\n@Index([\"userAddress\", \"createdAt\"])\nexport class LedgerJournalEntity {\n @PrimaryGeneratedColumn(\"uuid\")\n id!: string;\n\n @Column({ name: \"user_address\", type: \"varchar\", length: 42 })\n userAddress!: string;\n\n @Column({ name: \"token_address\", type: \"varchar\", length: 42 })\n tokenAddress!: string;\n\n @Column({\n name: \"delta\",\n type: \"numeric\",\n precision: 78,\n scale: 0,\n transformer: {\n to: (value: bigint) => value.toString(),\n from: (value: string) => BigInt(value),\n },\n })\n delta!: bigint;\n\n @Column({ name: \"reason\", type: \"varchar\", length: 128 })\n reason!: string;\n\n @Column({ name: \"tx_hash\", type: \"varchar\", length: 66, nullable: true })\n txHash?: string | null;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt!: Date;\n}\n","import { Column, Entity, PrimaryColumn, UpdateDateColumn } from \"typeorm\";\n\n/**\n * Persistent cursor for `PointIndexer` / `BurnIndexer`. Multiple rows\n * coexist keyed by `id` (e.g. `default` for the mint indexer,\n * `burn:0x...` for each per-token burn indexer).\n *\n * Stores the **next** block to scan, not the last processed one.\n * Indexer reads on startup and resumes from there.\n */\n@Entity({ name: \"indexer_cursors\" })\nexport class IndexerCursorEntity {\n @PrimaryColumn({ type: \"varchar\", length: 64 })\n id!: string;\n\n @Column({\n name: \"next_block\",\n type: \"numeric\",\n precision: 78,\n scale: 0,\n transformer: {\n to: (value: bigint) => value.toString(),\n from: (value: string) => BigInt(value),\n },\n })\n nextBlock!: bigint;\n\n @UpdateDateColumn({ name: \"updated_at\" })\n updatedAt!: Date;\n}\n"],"mappings":"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","names":["import_typeorm","import_typeorm","import_typeorm","import_typeorm"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { MintingStatus } from '@pafi-dev/issuer';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A reservation against a user's off-chain balance.
|
|
5
|
+
*
|
|
6
|
+
* Lifecycle:
|
|
7
|
+
* PENDING ── PointIndexer matches Mint event ──▶ MINTED
|
|
8
|
+
* │
|
|
9
|
+
* ├── deadline elapsed ────────────────────▶ EXPIRED
|
|
10
|
+
* │
|
|
11
|
+
* └── tx reverted ─────────────────────────▶ FAILED
|
|
12
|
+
*
|
|
13
|
+
* The `(userAddress, status)` composite index keeps the "sum all
|
|
14
|
+
* PENDING locks" hot path of `lockForMinting()` fast under load.
|
|
15
|
+
*/
|
|
16
|
+
declare class LockedMintEntity {
|
|
17
|
+
id: string;
|
|
18
|
+
userAddress: string;
|
|
19
|
+
tokenAddress: string;
|
|
20
|
+
amount: bigint;
|
|
21
|
+
status: MintingStatus;
|
|
22
|
+
createdAt: Date;
|
|
23
|
+
expiresAt: Date;
|
|
24
|
+
txHash?: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* ERC-4337 userOpHash returned by the bundler at /claim/submit.
|
|
27
|
+
* Bound to the lock so `/claim/status` can fall back to the bundler
|
|
28
|
+
* receipt — required when multiple PENDING locks share the same
|
|
29
|
+
* `amount` (PointIndexer matches by amount and can pick the wrong
|
|
30
|
+
* sibling lock).
|
|
31
|
+
*/
|
|
32
|
+
userOpHash?: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type PendingCreditStatus = "PENDING" | "RESOLVED" | "EXPIRED";
|
|
36
|
+
/**
|
|
37
|
+
* Reverse flow — user burns on-chain PT, `BurnIndexer` observes
|
|
38
|
+
* `Transfer(user → 0x0)`, the credit is settled to the off-chain ledger.
|
|
39
|
+
*
|
|
40
|
+
* Lifecycle:
|
|
41
|
+
* PENDING ── Burn tx observed by indexer ──▶ RESOLVED
|
|
42
|
+
* │
|
|
43
|
+
* └── deadline elapsed ────────────────▶ EXPIRED
|
|
44
|
+
*
|
|
45
|
+
* The credit is reserved BEFORE the UserOp is submitted so the
|
|
46
|
+
* indexer can correlate `(user, amount, token)` back to the off-chain
|
|
47
|
+
* row when the burn lands.
|
|
48
|
+
*/
|
|
49
|
+
declare class PendingCreditEntity {
|
|
50
|
+
id: string;
|
|
51
|
+
userAddress: string;
|
|
52
|
+
tokenAddress: string;
|
|
53
|
+
amount: bigint;
|
|
54
|
+
status: PendingCreditStatus;
|
|
55
|
+
/** On-chain burn tx that settled this credit. Null until indexer resolves. */
|
|
56
|
+
txHash?: string | null;
|
|
57
|
+
/** ERC-4337 userOpHash bound at /redeem/submit. See `LockedMintEntity`. */
|
|
58
|
+
userOpHash?: string | null;
|
|
59
|
+
createdAt: Date;
|
|
60
|
+
expiresAt: Date;
|
|
61
|
+
resolvedAt?: Date | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Off-chain point balance per `(userAddress, tokenAddress)`.
|
|
66
|
+
*
|
|
67
|
+
* `balance` is the **total** owned; pending reservations live in
|
|
68
|
+
* `LockedMintEntity`. Available balance = total − sum(PENDING locks)
|
|
69
|
+
* — `getBalance` in `PostgresPointLedger` does this subtraction
|
|
70
|
+
* inside a transaction so reads are race-free.
|
|
71
|
+
*
|
|
72
|
+
* All amounts are `numeric(78, 0)` for full bigint precision (uint256
|
|
73
|
+
* fits in 78 decimal digits). TypeORM transforms bigint ↔ string at
|
|
74
|
+
* the boundary; in JS/TS code we always deal with `bigint`.
|
|
75
|
+
*/
|
|
76
|
+
declare class UserBalanceEntity {
|
|
77
|
+
userAddress: string;
|
|
78
|
+
tokenAddress: string;
|
|
79
|
+
balance: bigint;
|
|
80
|
+
updatedAt: Date;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Append-only audit trail for every balance mutation. Used for
|
|
85
|
+
* reconciliation, customer support, and regulatory reporting.
|
|
86
|
+
*
|
|
87
|
+
* Sign convention:
|
|
88
|
+
* - positive `delta` — credit (merchant award, refund, manual top-up)
|
|
89
|
+
* - negative `delta` — debit (mint confirmation against the off-chain
|
|
90
|
+
* balance; `txHash` references the on-chain Mint event)
|
|
91
|
+
*/
|
|
92
|
+
declare class LedgerJournalEntity {
|
|
93
|
+
id: string;
|
|
94
|
+
userAddress: string;
|
|
95
|
+
tokenAddress: string;
|
|
96
|
+
delta: bigint;
|
|
97
|
+
reason: string;
|
|
98
|
+
txHash?: string | null;
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Persistent cursor for `PointIndexer` / `BurnIndexer`. Multiple rows
|
|
104
|
+
* coexist keyed by `id` (e.g. `default` for the mint indexer,
|
|
105
|
+
* `burn:0x...` for each per-token burn indexer).
|
|
106
|
+
*
|
|
107
|
+
* Stores the **next** block to scan, not the last processed one.
|
|
108
|
+
* Indexer reads on startup and resumes from there.
|
|
109
|
+
*/
|
|
110
|
+
declare class IndexerCursorEntity {
|
|
111
|
+
id: string;
|
|
112
|
+
nextBlock: bigint;
|
|
113
|
+
updatedAt: Date;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* All entities in one array — drop into TypeORM's `entities` config or
|
|
118
|
+
* NestJS's `TypeOrmModule.forFeature(PAFI_ENTITIES)`.
|
|
119
|
+
*/
|
|
120
|
+
declare const PAFI_ENTITIES: readonly [typeof LockedMintEntity, typeof PendingCreditEntity, typeof UserBalanceEntity, typeof LedgerJournalEntity, typeof IndexerCursorEntity];
|
|
121
|
+
|
|
122
|
+
export { IndexerCursorEntity, LedgerJournalEntity, LockedMintEntity, PAFI_ENTITIES, PendingCreditEntity, type PendingCreditStatus, UserBalanceEntity };
|