@cheqd/studio 3.17.0 → 3.18.0-develop.2
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 +13 -7
- package/dist/controllers/api/account.d.ts +8 -3
- package/dist/controllers/api/account.d.ts.map +1 -1
- package/dist/controllers/api/account.js +153 -34
- package/dist/controllers/api/account.js.map +1 -1
- package/dist/database/cache/store.d.ts +7 -0
- package/dist/database/cache/store.d.ts.map +1 -1
- package/dist/database/cache/store.js +6 -0
- package/dist/database/cache/store.js.map +1 -1
- package/dist/database/entities/faucet-request.entity.d.ts +25 -0
- package/dist/database/entities/faucet-request.entity.d.ts.map +1 -0
- package/dist/database/entities/faucet-request.entity.js +89 -0
- package/dist/database/entities/faucet-request.entity.js.map +1 -0
- package/dist/database/migrations/1791500000000-studio-migrations.d.ts +7 -0
- package/dist/database/migrations/1791500000000-studio-migrations.d.ts.map +1 -0
- package/dist/database/migrations/1791500000000-studio-migrations.js +14 -0
- package/dist/database/migrations/1791500000000-studio-migrations.js.map +1 -0
- package/dist/database/migrations/1791500000001-studio-migrations.d.ts +7 -0
- package/dist/database/migrations/1791500000001-studio-migrations.d.ts.map +1 -0
- package/dist/database/migrations/1791500000001-studio-migrations.js +16 -0
- package/dist/database/migrations/1791500000001-studio-migrations.js.map +1 -0
- package/dist/database/types/types.d.ts.map +1 -1
- package/dist/database/types/types.js +8 -0
- package/dist/database/types/types.js.map +1 -1
- package/dist/helpers/faucet-quota.d.ts +90 -0
- package/dist/helpers/faucet-quota.d.ts.map +1 -0
- package/dist/helpers/faucet-quota.js +99 -0
- package/dist/helpers/faucet-quota.js.map +1 -0
- package/dist/helpers/faucet.d.ts +3 -1
- package/dist/helpers/faucet.d.ts.map +1 -1
- package/dist/helpers/faucet.js +9 -2
- package/dist/helpers/faucet.js.map +1 -1
- package/dist/services/api/faucet-request.d.ts +39 -0
- package/dist/services/api/faucet-request.d.ts.map +1 -0
- package/dist/services/api/faucet-request.js +106 -0
- package/dist/services/api/faucet-request.js.map +1 -0
- package/dist/static/swagger-api.json +65 -3
- package/dist/types/account.d.ts +16 -0
- package/dist/types/account.d.ts.map +1 -1
- package/dist/types/constants.d.ts +14 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/constants.js +42 -5
- package/dist/types/constants.js.map +1 -1
- package/dist/types/swagger-api-types.d.ts +39 -0
- package/dist/types/swagger-api-types.d.ts.map +1 -1
- package/dist/types/swagger-api-types.js +39 -0
- package/dist/types/swagger-api-types.js.map +1 -1
- package/example.env +13 -1
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CustomerEntity } from './customer.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* - pending: reserved, faucet call in flight. Counts towards the quota until it times out.
|
|
4
|
+
* - completed: the faucet confirmed the credit.
|
|
5
|
+
* - unknown: the faucet call threw (e.g. a timeout), so tokens may have been sent. Always counts.
|
|
6
|
+
* - abandoned: stayed pending past the timeout (e.g. the process died). No longer counts.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FAUCET_REQUEST_STATUSES: readonly ["pending", "completed", "unknown", "abandoned"];
|
|
9
|
+
export type FaucetRequestStatus = (typeof FAUCET_REQUEST_STATUSES)[number];
|
|
10
|
+
/**
|
|
11
|
+
* One row per testnet faucet request made through `POST /account/faucet` or the account bootstrap top-up.
|
|
12
|
+
* Summed per customer to enforce the monthly faucet quota.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FaucetRequestEntity {
|
|
15
|
+
faucetRequestId: string;
|
|
16
|
+
customer: CustomerEntity;
|
|
17
|
+
address: string;
|
|
18
|
+
amountNcheq: string;
|
|
19
|
+
status: FaucetRequestStatus;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
completedAt?: Date;
|
|
22
|
+
setCreatedAt(): void;
|
|
23
|
+
constructor(customer: CustomerEntity, address: string, amountNcheq: bigint);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=faucet-request.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-request.entity.d.ts","sourceRoot":"","sources":["../../../src/database/entities/faucet-request.entity.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,2DAA4D,CAAC;AACjG,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3E;;;GAGG;AACH,qBAGa,mBAAmB;IAE/B,eAAe,EAAG,MAAM,CAAC;IAIzB,QAAQ,EAAG,cAAc,CAAC;IAM1B,OAAO,EAAG,MAAM,CAAC;IAOjB,WAAW,EAAG,MAAM,CAAC;IAOrB,MAAM,EAAG,mBAAmB,CAAC;IAM7B,SAAS,EAAG,IAAI,CAAC;IAMjB,WAAW,CAAC,EAAE,IAAI,CAAC;IAGnB,YAAY;gBAIA,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAO1E"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BeforeInsert, Check, Column, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
|
8
|
+
import * as dotenv from 'dotenv';
|
|
9
|
+
import { CustomerEntity } from './customer.entity.js';
|
|
10
|
+
dotenv.config();
|
|
11
|
+
/**
|
|
12
|
+
* - pending: reserved, faucet call in flight. Counts towards the quota until it times out.
|
|
13
|
+
* - completed: the faucet confirmed the credit.
|
|
14
|
+
* - unknown: the faucet call threw (e.g. a timeout), so tokens may have been sent. Always counts.
|
|
15
|
+
* - abandoned: stayed pending past the timeout (e.g. the process died). No longer counts.
|
|
16
|
+
*/
|
|
17
|
+
export const FAUCET_REQUEST_STATUSES = ['pending', 'completed', 'unknown', 'abandoned'];
|
|
18
|
+
/**
|
|
19
|
+
* One row per testnet faucet request made through `POST /account/faucet` or the account bootstrap top-up.
|
|
20
|
+
* Summed per customer to enforce the monthly faucet quota.
|
|
21
|
+
*/
|
|
22
|
+
let FaucetRequestEntity = class FaucetRequestEntity {
|
|
23
|
+
faucetRequestId;
|
|
24
|
+
customer;
|
|
25
|
+
address;
|
|
26
|
+
// ncheq amount, stored as bigint and surfaced as a string by the driver
|
|
27
|
+
amountNcheq;
|
|
28
|
+
status;
|
|
29
|
+
createdAt;
|
|
30
|
+
completedAt;
|
|
31
|
+
setCreatedAt() {
|
|
32
|
+
this.createdAt = new Date();
|
|
33
|
+
}
|
|
34
|
+
constructor(customer, address, amountNcheq) {
|
|
35
|
+
this.customer = customer;
|
|
36
|
+
this.address = address;
|
|
37
|
+
// TypeORM instantiates entities without arguments when building metadata
|
|
38
|
+
this.amountNcheq = amountNcheq?.toString();
|
|
39
|
+
this.status = 'pending';
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
__decorate([
|
|
43
|
+
PrimaryGeneratedColumn('uuid')
|
|
44
|
+
], FaucetRequestEntity.prototype, "faucetRequestId", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
ManyToOne(() => CustomerEntity, (customer) => customer.customerId, { nullable: false, onDelete: 'CASCADE' }),
|
|
47
|
+
JoinColumn({ name: 'customerId' })
|
|
48
|
+
], FaucetRequestEntity.prototype, "customer", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
Column({
|
|
51
|
+
type: 'text',
|
|
52
|
+
nullable: false,
|
|
53
|
+
})
|
|
54
|
+
], FaucetRequestEntity.prototype, "address", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
Column({
|
|
57
|
+
type: 'bigint',
|
|
58
|
+
nullable: false,
|
|
59
|
+
})
|
|
60
|
+
], FaucetRequestEntity.prototype, "amountNcheq", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
Column({
|
|
63
|
+
type: 'text',
|
|
64
|
+
nullable: false,
|
|
65
|
+
default: 'completed',
|
|
66
|
+
})
|
|
67
|
+
], FaucetRequestEntity.prototype, "status", void 0);
|
|
68
|
+
__decorate([
|
|
69
|
+
Column({
|
|
70
|
+
type: 'timestamptz',
|
|
71
|
+
nullable: false,
|
|
72
|
+
})
|
|
73
|
+
], FaucetRequestEntity.prototype, "createdAt", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
Column({
|
|
76
|
+
type: 'timestamptz',
|
|
77
|
+
nullable: true,
|
|
78
|
+
})
|
|
79
|
+
], FaucetRequestEntity.prototype, "completedAt", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
BeforeInsert()
|
|
82
|
+
], FaucetRequestEntity.prototype, "setCreatedAt", null);
|
|
83
|
+
FaucetRequestEntity = __decorate([
|
|
84
|
+
Entity('faucetRequest'),
|
|
85
|
+
Check('CHK_faucetRequest_status', `"status" IN (${FAUCET_REQUEST_STATUSES.map((status) => `'${status}'`).join(', ')})`),
|
|
86
|
+
Index(['customer', 'createdAt'])
|
|
87
|
+
], FaucetRequestEntity);
|
|
88
|
+
export { FaucetRequestEntity };
|
|
89
|
+
//# sourceMappingURL=faucet-request.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-request.entity.js","sourceRoot":"","sources":["../../../src/database/entities/faucet-request.entity.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEpH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,CAAU,CAAC;AAGjG;;;GAGG;AAII,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAE/B,eAAe,CAAU;IAIzB,QAAQ,CAAkB;IAM1B,OAAO,CAAU;IAEjB,wEAAwE;IAKxE,WAAW,CAAU;IAOrB,MAAM,CAAuB;IAM7B,SAAS,CAAQ;IAMjB,WAAW,CAAQ;IAGnB,YAAY;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,YAAY,QAAwB,EAAE,OAAe,EAAE,WAAmB;QACzE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,yEAAyE;QACzE,IAAI,CAAC,WAAW,GAAG,WAAW,EAAE,QAAQ,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,CAAC;CACD,CAAA;AAlDA;IADC,sBAAsB,CAAC,MAAM,CAAC;4DACN;AAIzB;IAFC,SAAS,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAC5G,UAAU,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;qDACT;AAM1B;IAJC,MAAM,CAAC;QACP,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;KACf,CAAC;oDACe;AAOjB;IAJC,MAAM,CAAC;QACP,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,KAAK;KACf,CAAC;wDACmB;AAOrB;IALC,MAAM,CAAC;QACP,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,WAAW;KACpB,CAAC;mDAC2B;AAM7B;IAJC,MAAM,CAAC;QACP,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,KAAK;KACf,CAAC;sDACe;AAMjB;IAJC,MAAM,CAAC;QACP,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,IAAI;KACd,CAAC;wDACiB;AAGnB;IADC,YAAY,EAAE;uDAGd;AA3CW,mBAAmB;IAH/B,MAAM,CAAC,eAAe,CAAC;IACvB,KAAK,CAAC,0BAA0B,EAAE,gBAAgB,uBAAuB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACvH,KAAK,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;GACpB,mBAAmB,CAoD/B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
2
|
+
export declare class StudioMigrations1791500000000 implements MigrationInterface {
|
|
3
|
+
name: string;
|
|
4
|
+
up(queryRunner: QueryRunner): Promise<void>;
|
|
5
|
+
down(queryRunner: QueryRunner): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=1791500000000-studio-migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1791500000000-studio-migrations.d.ts","sourceRoot":"","sources":["../../../src/database/migrations/1791500000000-studio-migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,6BAA8B,YAAW,kBAAkB;IACvE,IAAI,SAAmC;IAE1B,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAK1D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class StudioMigrations1791500000000 {
|
|
2
|
+
name = 'StudioMigrations1791500000000';
|
|
3
|
+
async up(queryRunner) {
|
|
4
|
+
await queryRunner.query(`CREATE TABLE "faucetRequest" ("faucetRequestId" uuid NOT NULL DEFAULT uuid_generate_v4(), "address" text NOT NULL, "amountNcheq" bigint NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "customerId" uuid NOT NULL, CONSTRAINT "PK_7a6c79df9596b24bdccfeeb6eb1" PRIMARY KEY ("faucetRequestId"))`);
|
|
5
|
+
await queryRunner.query(`CREATE INDEX "IDX_aa091848bbf777ec704825281f" ON "faucetRequest" ("customerId", "createdAt")`);
|
|
6
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ADD CONSTRAINT "FK_55b0239dcbc50483b22446dcee9" FOREIGN KEY ("customerId") REFERENCES "customer"("customerId") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
|
7
|
+
}
|
|
8
|
+
async down(queryRunner) {
|
|
9
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" DROP CONSTRAINT "FK_55b0239dcbc50483b22446dcee9"`);
|
|
10
|
+
await queryRunner.query(`DROP INDEX "public"."IDX_aa091848bbf777ec704825281f"`);
|
|
11
|
+
await queryRunner.query(`DROP TABLE "faucetRequest"`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=1791500000000-studio-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1791500000000-studio-migrations.js","sourceRoot":"","sources":["../../../src/database/migrations/1791500000000-studio-migrations.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,6BAA6B;IACzC,IAAI,GAAG,+BAA+B,CAAC;IAEhC,KAAK,CAAC,EAAE,CAAC,WAAwB;QACvC,MAAM,WAAW,CAAC,KAAK,CACtB,2SAA2S,CAC3S,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACtB,8FAA8F,CAC9F,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACtB,kLAAkL,CAClL,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACzC,MAAM,WAAW,CAAC,KAAK,CAAC,8EAA8E,CAAC,CAAC;QACxG,MAAM,WAAW,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAChF,MAAM,WAAW,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACvD,CAAC;CACD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
2
|
+
export declare class StudioMigrations1791500000001 implements MigrationInterface {
|
|
3
|
+
name: string;
|
|
4
|
+
up(queryRunner: QueryRunner): Promise<void>;
|
|
5
|
+
down(queryRunner: QueryRunner): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=1791500000001-studio-migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1791500000001-studio-migrations.d.ts","sourceRoot":"","sources":["../../../src/database/migrations/1791500000001-studio-migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D,qBAAa,6BAA8B,YAAW,kBAAkB;IACvE,IAAI,SAAmC;IAE1B,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAU3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAK1D"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class StudioMigrations1791500000001 {
|
|
2
|
+
name = 'StudioMigrations1791500000001';
|
|
3
|
+
async up(queryRunner) {
|
|
4
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ADD "status" text NOT NULL DEFAULT 'completed'`);
|
|
5
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ADD CONSTRAINT "CHK_faucetRequest_status" CHECK ("status" IN ('pending', 'completed', 'unknown', 'abandoned'))`);
|
|
6
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ADD "completedAt" TIMESTAMP WITH TIME ZONE`);
|
|
7
|
+
// Rows that already exist were recorded after the faucet call, so they are completed
|
|
8
|
+
await queryRunner.query(`UPDATE "faucetRequest" SET "completedAt" = "createdAt" WHERE "completedAt" IS NULL`);
|
|
9
|
+
}
|
|
10
|
+
async down(queryRunner) {
|
|
11
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" DROP COLUMN "completedAt"`);
|
|
12
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" DROP CONSTRAINT "CHK_faucetRequest_status"`);
|
|
13
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" DROP COLUMN "status"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=1791500000001-studio-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1791500000001-studio-migrations.js","sourceRoot":"","sources":["../../../src/database/migrations/1791500000001-studio-migrations.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,6BAA6B;IACzC,IAAI,GAAG,+BAA+B,CAAC;IAEhC,KAAK,CAAC,EAAE,CAAC,WAAwB;QACvC,MAAM,WAAW,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;QACtG,MAAM,WAAW,CAAC,KAAK,CACtB,4IAA4I,CAC5I,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAClG,qFAAqF;QACrF,MAAM,WAAW,CAAC,KAAK,CAAC,oFAAoF,CAAC,CAAC;IAC/G,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACzC,MAAM,WAAW,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACjF,MAAM,WAAW,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAClG,MAAM,WAAW,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC7E,CAAC;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAgErC,MAAM,WAAW,gBAAgB;IAChC,KAAK,IAAI,UAAU,CAAC;CACpB;AAED,qBAAa,MAAO,YAAW,gBAAgB;IAC9C,KAAK,IAAI,UAAU;CAWnB;AAED,qBAAa,QAAS,YAAW,gBAAgB;IAChD,KAAK,IAAI,UAAU;CAuGnB"}
|
|
@@ -18,6 +18,7 @@ import { CredentialProviderEntity } from '../entities/credential-provider.entity
|
|
|
18
18
|
import { ProviderConfigurationEntity } from '../entities/provider-configuration.entity.js';
|
|
19
19
|
import { IssuedCredentialEntity } from '../entities/issued-credential.entity.js';
|
|
20
20
|
import { StatusRegistryEntity } from '../entities/status-registry.entity.js';
|
|
21
|
+
import { FaucetRequestEntity } from '../entities/faucet-request.entity.js';
|
|
21
22
|
import { CreatePaymentTable1695740345977 } from '../migrations/archive/CreatePaymentTable.js';
|
|
22
23
|
import { CreateOperationTable1695740345977 } from '../migrations/archive/CreateOperationTable.js';
|
|
23
24
|
import { CreateCustomersTable1683723285946 } from '../migrations/archive/CreateCustomersTable.js';
|
|
@@ -53,6 +54,8 @@ import { StudioMigrations1761834657128 } from '../migrations/1761834657128-studi
|
|
|
53
54
|
import { MigrationsStatusLists1762775396083 } from '../migrations/1762775396083-MigrateStatusLists.js';
|
|
54
55
|
import { UpdateWriteCursors1762775500000 } from '../migrations/1762775500000-UpdateWriteCursors.js';
|
|
55
56
|
import { StudioMigrations1766408271347 } from '../migrations/1766408271347-studio-migrations.js';
|
|
57
|
+
import { StudioMigrations1791500000000 } from '../migrations/1791500000000-studio-migrations.js';
|
|
58
|
+
import { StudioMigrations1791500000001 } from '../migrations/1791500000001-studio-migrations.js';
|
|
56
59
|
dotenv.config();
|
|
57
60
|
const { EXTERNAL_DB_CONNECTION_URL, EXTERNAL_DB_CERT } = process.env;
|
|
58
61
|
export class Memory {
|
|
@@ -143,6 +146,10 @@ export class Postgres {
|
|
|
143
146
|
MigrationsStatusLists1762775396083,
|
|
144
147
|
UpdateWriteCursors1762775500000,
|
|
145
148
|
StudioMigrations1766408271347,
|
|
149
|
+
// Add faucetRequest table for the monthly faucet quota
|
|
150
|
+
StudioMigrations1791500000000,
|
|
151
|
+
// Track whether each faucet request completed, so abandoned reservations can stop counting
|
|
152
|
+
StudioMigrations1791500000001,
|
|
146
153
|
],
|
|
147
154
|
entities: [
|
|
148
155
|
...Entities,
|
|
@@ -162,6 +169,7 @@ export class Postgres {
|
|
|
162
169
|
ProviderConfigurationEntity,
|
|
163
170
|
IssuedCredentialEntity,
|
|
164
171
|
StatusRegistryEntity,
|
|
172
|
+
FaucetRequestEntity,
|
|
165
173
|
],
|
|
166
174
|
logging: ['error', 'info', 'warn'],
|
|
167
175
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,sBAAsB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAC;AAC3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { CustomerEntity } from '../database/entities/customer.entity.js';
|
|
2
|
+
import type { FaucetQuotaSummary } from '../types/account.js';
|
|
3
|
+
/**
|
|
4
|
+
* Helpers for the per-customer monthly faucet quota.
|
|
5
|
+
* The quota window is the current calendar month in UTC.
|
|
6
|
+
*/
|
|
7
|
+
export interface FaucetQuotaWindow {
|
|
8
|
+
start: Date;
|
|
9
|
+
resetsAt: Date;
|
|
10
|
+
}
|
|
11
|
+
/** Calendar-month (UTC) window containing `now`. */
|
|
12
|
+
export declare function getFaucetQuotaWindow(now?: Date): FaucetQuotaWindow;
|
|
13
|
+
/** Seconds until the quota window resets, rounded up and never below 1. */
|
|
14
|
+
export declare function secondsUntilReset(window: FaucetQuotaWindow, now?: Date): number;
|
|
15
|
+
/** True if `requestedNcheq` fits in what is left of `limitNcheq` after `usedNcheq`. */
|
|
16
|
+
export declare function fitsInQuota(usedNcheq: bigint, requestedNcheq: bigint, limitNcheq: bigint): boolean;
|
|
17
|
+
/** Most that can be requested now: the lower of the room under the address cap and the quota left, never negative. */
|
|
18
|
+
export declare function maxRequestableNcheq(roomUnderCapNcheq: bigint, quotaRemainingNcheq: bigint): bigint;
|
|
19
|
+
/** Remaining quota, never negative. */
|
|
20
|
+
export declare function remainingQuota(usedNcheq: bigint, limitNcheq: bigint): bigint;
|
|
21
|
+
export declare function buildFaucetQuotaSummary(usedNcheq: bigint, limitNcheq: bigint, window: FaucetQuotaWindow): FaucetQuotaSummary;
|
|
22
|
+
/** Seconds the customer still has to wait before their next faucet request; 0 if one is allowed now. */
|
|
23
|
+
export declare function secondsUntilNextRequest(lastRequestAt: Date | null, minIntervalSeconds: number, now?: Date): number;
|
|
24
|
+
export interface FaucetReserveOptions {
|
|
25
|
+
limitNcheq: bigint;
|
|
26
|
+
pendingTimeoutSeconds?: number;
|
|
27
|
+
minIntervalSeconds?: number;
|
|
28
|
+
}
|
|
29
|
+
export type FaucetReservation = {
|
|
30
|
+
reserved: true;
|
|
31
|
+
faucetRequestId: string;
|
|
32
|
+
usedNcheq: bigint;
|
|
33
|
+
window: FaucetQuotaWindow;
|
|
34
|
+
} | {
|
|
35
|
+
reserved: false;
|
|
36
|
+
reason: 'quota_exceeded';
|
|
37
|
+
usedNcheq: bigint;
|
|
38
|
+
window: FaucetQuotaWindow;
|
|
39
|
+
} | {
|
|
40
|
+
reserved: false;
|
|
41
|
+
reason: 'too_frequent';
|
|
42
|
+
retryAfterSeconds: number;
|
|
43
|
+
window: FaucetQuotaWindow;
|
|
44
|
+
};
|
|
45
|
+
/** Storage for quota reservations; implemented by `FaucetRequestService` and faked in tests. */
|
|
46
|
+
export interface FaucetQuotaLedger {
|
|
47
|
+
reserve(customer: CustomerEntity, address: string, amountNcheq: bigint, options: FaucetReserveOptions): Promise<FaucetReservation>;
|
|
48
|
+
release(faucetRequestId: string): Promise<void>;
|
|
49
|
+
/** The faucet confirmed the credit. */
|
|
50
|
+
complete(faucetRequestId: string): Promise<void>;
|
|
51
|
+
/** The faucet call threw, so tokens may have been sent: keep counting this reservation permanently. */
|
|
52
|
+
markUnknown(faucetRequestId: string): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export type FaucetCreditResult = {
|
|
55
|
+
outcome: 'quota_exceeded';
|
|
56
|
+
usedNcheq: bigint;
|
|
57
|
+
window: FaucetQuotaWindow;
|
|
58
|
+
} | {
|
|
59
|
+
outcome: 'too_frequent';
|
|
60
|
+
retryAfterSeconds: number;
|
|
61
|
+
window: FaucetQuotaWindow;
|
|
62
|
+
} | {
|
|
63
|
+
outcome: 'credited';
|
|
64
|
+
usedNcheq: bigint;
|
|
65
|
+
window: FaucetQuotaWindow;
|
|
66
|
+
} | {
|
|
67
|
+
outcome: 'faucet_failed';
|
|
68
|
+
status: number;
|
|
69
|
+
error: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Reserves quota, calls the faucet, and settles the reservation.
|
|
73
|
+
* - Quota does not fit: the faucet is not called.
|
|
74
|
+
* - Faucet answers with an error status: it definitely did not credit the account, so the quota is released.
|
|
75
|
+
* - Faucet call throws (e.g. a timeout): the outcome is unknown and tokens may have been sent, so the
|
|
76
|
+
* reservation is marked unknown and keeps counting for good (over-counting is safer than refunding quota
|
|
77
|
+
* for tokens that were credited).
|
|
78
|
+
* - Faucet confirms: the reservation is marked completed.
|
|
79
|
+
*/
|
|
80
|
+
export declare function creditWithinQuota(ledger: FaucetQuotaLedger, delegate: () => Promise<{
|
|
81
|
+
status: number;
|
|
82
|
+
error: string;
|
|
83
|
+
}>, params: {
|
|
84
|
+
customer: CustomerEntity;
|
|
85
|
+
address: string;
|
|
86
|
+
amountNcheq: bigint;
|
|
87
|
+
limitNcheq: bigint;
|
|
88
|
+
minIntervalSeconds?: number;
|
|
89
|
+
}): Promise<FaucetCreditResult>;
|
|
90
|
+
//# sourceMappingURL=faucet-quota.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-quota.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ncheqToCheq } from './denom.js';
|
|
2
|
+
/** Calendar-month (UTC) window containing `now`. */
|
|
3
|
+
export function getFaucetQuotaWindow(now = new Date()) {
|
|
4
|
+
const year = now.getUTCFullYear();
|
|
5
|
+
const month = now.getUTCMonth();
|
|
6
|
+
return {
|
|
7
|
+
start: new Date(Date.UTC(year, month, 1)),
|
|
8
|
+
resetsAt: new Date(Date.UTC(year, month + 1, 1)),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Seconds until the quota window resets, rounded up and never below 1. */
|
|
12
|
+
export function secondsUntilReset(window, now = new Date()) {
|
|
13
|
+
return Math.max(1, Math.ceil((window.resetsAt.getTime() - now.getTime()) / 1000));
|
|
14
|
+
}
|
|
15
|
+
/** True if `requestedNcheq` fits in what is left of `limitNcheq` after `usedNcheq`. */
|
|
16
|
+
export function fitsInQuota(usedNcheq, requestedNcheq, limitNcheq) {
|
|
17
|
+
return usedNcheq + requestedNcheq <= limitNcheq;
|
|
18
|
+
}
|
|
19
|
+
/** Most that can be requested now: the lower of the room under the address cap and the quota left, never negative. */
|
|
20
|
+
export function maxRequestableNcheq(roomUnderCapNcheq, quotaRemainingNcheq) {
|
|
21
|
+
const lowest = roomUnderCapNcheq < quotaRemainingNcheq ? roomUnderCapNcheq : quotaRemainingNcheq;
|
|
22
|
+
return lowest > 0n ? lowest : 0n;
|
|
23
|
+
}
|
|
24
|
+
/** Remaining quota, never negative. */
|
|
25
|
+
export function remainingQuota(usedNcheq, limitNcheq) {
|
|
26
|
+
return usedNcheq >= limitNcheq ? 0n : limitNcheq - usedNcheq;
|
|
27
|
+
}
|
|
28
|
+
export function buildFaucetQuotaSummary(usedNcheq, limitNcheq, window) {
|
|
29
|
+
const remainingNcheq = remainingQuota(usedNcheq, limitNcheq);
|
|
30
|
+
const amount = (ncheq) => ({ cheq: ncheqToCheq(ncheq), ncheq: ncheq.toString() });
|
|
31
|
+
return {
|
|
32
|
+
period: 'month',
|
|
33
|
+
limit: amount(limitNcheq),
|
|
34
|
+
used: amount(usedNcheq),
|
|
35
|
+
remaining: amount(remainingNcheq),
|
|
36
|
+
resetsAt: window.resetsAt.toISOString(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Seconds the customer still has to wait before their next faucet request; 0 if one is allowed now. */
|
|
40
|
+
export function secondsUntilNextRequest(lastRequestAt, minIntervalSeconds, now = new Date()) {
|
|
41
|
+
if (!lastRequestAt || minIntervalSeconds <= 0)
|
|
42
|
+
return 0;
|
|
43
|
+
const remainingMs = minIntervalSeconds * 1000 - (now.getTime() - lastRequestAt.getTime());
|
|
44
|
+
return remainingMs > 0 ? Math.ceil(remainingMs / 1000) : 0;
|
|
45
|
+
}
|
|
46
|
+
async function markUnknownBestEffort(ledger, faucetRequestId) {
|
|
47
|
+
try {
|
|
48
|
+
await ledger.markUnknown(faucetRequestId);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(`Failed to mark faucet request ${faucetRequestId} as unknown:`, error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Reserves quota, calls the faucet, and settles the reservation.
|
|
56
|
+
* - Quota does not fit: the faucet is not called.
|
|
57
|
+
* - Faucet answers with an error status: it definitely did not credit the account, so the quota is released.
|
|
58
|
+
* - Faucet call throws (e.g. a timeout): the outcome is unknown and tokens may have been sent, so the
|
|
59
|
+
* reservation is marked unknown and keeps counting for good (over-counting is safer than refunding quota
|
|
60
|
+
* for tokens that were credited).
|
|
61
|
+
* - Faucet confirms: the reservation is marked completed.
|
|
62
|
+
*/
|
|
63
|
+
export async function creditWithinQuota(ledger, delegate, params) {
|
|
64
|
+
const reservation = await ledger.reserve(params.customer, params.address, params.amountNcheq, {
|
|
65
|
+
limitNcheq: params.limitNcheq,
|
|
66
|
+
minIntervalSeconds: params.minIntervalSeconds,
|
|
67
|
+
});
|
|
68
|
+
if (!reservation.reserved) {
|
|
69
|
+
return reservation.reason === 'too_frequent'
|
|
70
|
+
? { outcome: 'too_frequent', retryAfterSeconds: reservation.retryAfterSeconds, window: reservation.window }
|
|
71
|
+
: { outcome: 'quota_exceeded', usedNcheq: reservation.usedNcheq, window: reservation.window };
|
|
72
|
+
}
|
|
73
|
+
let faucet;
|
|
74
|
+
try {
|
|
75
|
+
faucet = await delegate();
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
await markUnknownBestEffort(ledger, reservation.faucetRequestId);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
if (faucet.status !== 200) {
|
|
82
|
+
await ledger.release(reservation.faucetRequestId);
|
|
83
|
+
return { outcome: 'faucet_failed', status: faucet.status, error: faucet.error };
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await ledger.complete(reservation.faucetRequestId);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// The tokens were sent, so do not fail the request. The reservation stays pending and keeps counting
|
|
90
|
+
// until it times out.
|
|
91
|
+
console.error(`Failed to mark faucet request ${reservation.faucetRequestId} as completed:`, error);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
outcome: 'credited',
|
|
95
|
+
usedNcheq: reservation.usedNcheq + params.amountNcheq,
|
|
96
|
+
window: reservation.window,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=faucet-quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-quota.js","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYzC,oDAAoD;AACpD,MAAM,UAAU,oBAAoB,CAAC,MAAY,IAAI,IAAI,EAAE;IAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO;QACN,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACzC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;KAChD,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,MAAyB,EAAE,MAAY,IAAI,IAAI,EAAE;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,cAAsB,EAAE,UAAkB;IACxF,OAAO,SAAS,GAAG,cAAc,IAAI,UAAU,CAAC;AACjD,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,mBAAmB,CAAC,iBAAyB,EAAE,mBAA2B;IACzF,MAAM,MAAM,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACjG,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,UAAkB;IACnE,OAAO,SAAS,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,UAAkB,EAClB,MAAyB;IAEzB,MAAM,cAAc,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,KAAa,EAAuB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC/G,OAAO;QACN,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE;KACvC,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,uBAAuB,CACtC,aAA0B,EAC1B,kBAA0B,EAC1B,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,aAAa,IAAI,kBAAkB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,kBAAkB,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1F,OAAO,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AA+BD,KAAK,UAAU,qBAAqB,CAAC,MAAyB,EAAE,eAAuB;IACtF,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,iCAAiC,eAAe,cAAc,EAAE,KAAK,CAAC,CAAC;IACtF,CAAC;AACF,CAAC;AASD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,MAAyB,EACzB,QAA0D,EAC1D,MAMC;IAED,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE;QAC7F,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;KAC7C,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC3B,OAAO,WAAW,CAAC,MAAM,KAAK,cAAc;YAC3C,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,WAAW,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE;YAC3G,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;IAChG,CAAC;IAED,IAAI,MAAyC,CAAC;IAC9C,IAAI,CAAC;QACJ,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,qBAAqB,CAAC,MAAM,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;QACjE,MAAM,KAAK,CAAC;IACb,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC3B,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACjF,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,qGAAqG;QACrG,sBAAsB;QACtB,OAAO,CAAC,KAAK,CAAC,iCAAiC,WAAW,CAAC,eAAe,gBAAgB,EAAE,KAAK,CAAC,CAAC;IACpG,CAAC;IACD,OAAO;QACN,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,WAAW,CAAC,SAAS,GAAG,MAAM,CAAC,WAAW;QACrD,MAAM,EAAE,WAAW,CAAC,MAAM;KAC1B,CAAC;AACH,CAAC"}
|
package/dist/helpers/faucet.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ICommonErrorResponse } from '../types/authentication.js';
|
|
2
|
+
/** True if the error is the faucet call timing out or being aborted, as opposed to the faucet answering. */
|
|
3
|
+
export declare function isFaucetTimeout(error: unknown): boolean;
|
|
2
4
|
export declare class FaucetHelper {
|
|
3
|
-
static delegateTokens(address: string, firstName: string, lastName: string, email: string, amount?: number): Promise<ICommonErrorResponse>;
|
|
5
|
+
static delegateTokens(address: string, firstName: string, lastName: string, email: string, amount?: number, timeoutMs?: number): Promise<ICommonErrorResponse>;
|
|
4
6
|
}
|
|
5
7
|
//# sourceMappingURL=faucet.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAWvE,4GAA4G;AAC5G,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGvD;AAED,qBAAa,YAAY;WAEX,cAAc,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,SAAgB,EACtB,SAAS,SAAwC,GAC/C,OAAO,CAAC,oBAAoB,CAAC;CA+BhC"}
|
package/dist/helpers/faucet.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { MINIMAL_DENOM, FAUCET_URI, FAUCET_AMOUNT, FAUCET_API_KEY, FAUCET_ACCESS_CLIENT_ID, FAUCET_ACCESS_CLIENT_SECRET, } from '../types/constants.js';
|
|
1
|
+
import { MINIMAL_DENOM, FAUCET_URI, FAUCET_AMOUNT, FAUCET_API_KEY, FAUCET_ACCESS_CLIENT_ID, FAUCET_ACCESS_CLIENT_SECRET, FAUCET_REQUEST_TIMEOUT_SECONDS, } from '../types/constants.js';
|
|
2
|
+
/** True if the error is the faucet call timing out or being aborted, as opposed to the faucet answering. */
|
|
3
|
+
export function isFaucetTimeout(error) {
|
|
4
|
+
const name = error?.name;
|
|
5
|
+
return name === 'TimeoutError' || name === 'AbortError';
|
|
6
|
+
}
|
|
2
7
|
export class FaucetHelper {
|
|
3
8
|
// ...
|
|
4
|
-
static async delegateTokens(address, firstName, lastName, email, amount = FAUCET_AMOUNT) {
|
|
9
|
+
static async delegateTokens(address, firstName, lastName, email, amount = FAUCET_AMOUNT, timeoutMs = FAUCET_REQUEST_TIMEOUT_SECONDS * 1000) {
|
|
5
10
|
const faucetURI = FAUCET_URI;
|
|
6
11
|
const faucetBody = {
|
|
7
12
|
denom: MINIMAL_DENOM,
|
|
@@ -22,6 +27,8 @@ export class FaucetHelper {
|
|
|
22
27
|
},
|
|
23
28
|
body: JSON.stringify(faucetBody),
|
|
24
29
|
method: 'POST',
|
|
30
|
+
// Covers waiting for the response and reading its body. On expiry fetch rejects with a TimeoutError.
|
|
31
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
25
32
|
});
|
|
26
33
|
return {
|
|
27
34
|
status: response.status,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet.js","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AACA,OAAO,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,2BAA2B,
|
|
1
|
+
{"version":3,"file":"faucet.js","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AACA,OAAO,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,2BAA2B,EAC3B,8BAA8B,GAC9B,MAAM,uBAAuB,CAAC;AAE/B,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAAC,KAAc;IAC7C,MAAM,IAAI,GAAI,KAAkC,EAAE,IAAI,CAAC;IACvD,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,YAAY;IACxB,MAAM;IACN,MAAM,CAAC,KAAK,CAAC,cAAc,CAC1B,OAAe,EACf,SAAiB,EACjB,QAAgB,EAChB,KAAa,EACb,MAAM,GAAG,aAAa,EACtB,SAAS,GAAG,8BAA8B,GAAG,IAAI;QAEjD,MAAM,SAAS,GAAG,UAAU,CAAC;QAC7B,MAAM,UAAU,GAAG;YAClB,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,OAAO;YAChB,KAAK,EAAE,KAAK;YACZ,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,4BAA4B;YACrC,MAAM;YACN,eAAe,EAAE,KAAK;SACtB,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACvC,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,WAAW,EAAE,cAAc;gBAC3B,qBAAqB,EAAE,uBAAuB;gBAC9C,yBAAyB,EAAE,2BAA2B;aACtD;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;YAChC,MAAM,EAAE,MAAM;YACd,qGAAqG;YACrG,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACtC,CAAC,CAAC;QACH,OAAO;YACN,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE;YAC5B,IAAI,EAAE,EAAE;SACR,CAAC;IACH,CAAC;CAED"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Repository } from 'typeorm';
|
|
2
|
+
import type { CustomerEntity } from '../../database/entities/customer.entity.js';
|
|
3
|
+
import { FaucetRequestEntity } from '../../database/entities/faucet-request.entity.js';
|
|
4
|
+
import { type FaucetQuotaLedger, type FaucetReservation, type FaucetReserveOptions } from '../../helpers/faucet-quota.js';
|
|
5
|
+
export declare class FaucetRequestService implements FaucetQuotaLedger {
|
|
6
|
+
faucetRequestRepository: Repository<FaucetRequestEntity>;
|
|
7
|
+
static instance: FaucetRequestService;
|
|
8
|
+
constructor(repository?: Repository<FaucetRequestEntity>);
|
|
9
|
+
private static pendingCutoff;
|
|
10
|
+
/**
|
|
11
|
+
* Total ncheq requested by the customer since `since`, using the given manager (and so its transaction).
|
|
12
|
+
* Completed and unknown requests always count; a pending one counts only until it is older than
|
|
13
|
+
* `pendingCutoff`, after which it is treated as abandoned.
|
|
14
|
+
*/
|
|
15
|
+
private static sumRequestedNcheq;
|
|
16
|
+
/** When the customer last made a faucet request, or null if never. */
|
|
17
|
+
private static lastRequestAt;
|
|
18
|
+
/**
|
|
19
|
+
* Seconds the customer must still wait before their next request (0 if allowed). This is a cheap read taken
|
|
20
|
+
* before any expensive work; `reserve` repeats the check under the lock so concurrent requests cannot slip through.
|
|
21
|
+
*/
|
|
22
|
+
getSecondsUntilNextAllowed(customer: CustomerEntity, minIntervalSeconds: number, now?: Date): Promise<number>;
|
|
23
|
+
/** Total ncheq requested by the customer in the current quota window. */
|
|
24
|
+
getUsedNcheq(customer: CustomerEntity, now?: Date, pendingTimeoutSeconds?: number): Promise<bigint>;
|
|
25
|
+
/**
|
|
26
|
+
* Atomically checks the customer's monthly quota and, if the amount fits, records it as pending.
|
|
27
|
+
* A per-customer advisory lock serialises concurrent requests so they cannot overshoot the limit.
|
|
28
|
+
* Afterwards call `complete` when the faucet confirms, `release` if it definitely failed, or `markUnknown`
|
|
29
|
+
* if the call threw.
|
|
30
|
+
*/
|
|
31
|
+
reserve(customer: CustomerEntity, address: string, amountNcheq: bigint, { limitNcheq, minIntervalSeconds, pendingTimeoutSeconds, }: FaucetReserveOptions, now?: Date): Promise<FaucetReservation>;
|
|
32
|
+
/** Releases a reservation, e.g. when the upstream faucet reported that it did not credit the account. */
|
|
33
|
+
release(faucetRequestId: string): Promise<void>;
|
|
34
|
+
/** The faucet confirmed the credit. Also applies to a reservation that was already marked abandoned. */
|
|
35
|
+
complete(faucetRequestId: string): Promise<void>;
|
|
36
|
+
/** The faucet call threw, so tokens may have been sent: keep counting this reservation permanently. */
|
|
37
|
+
markUnknown(faucetRequestId: string): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=faucet-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faucet-request.d.ts","sourceRoot":"","sources":["../../../src/services/api/faucet-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAGvF,OAAO,EAIN,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,MAAM,+BAA+B,CAAC;AAEvC,qBAAa,oBAAqB,YAAW,iBAAiB;IACtD,uBAAuB,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;IAEhE,OAAc,QAAQ,uBAA8B;gBAExC,UAAU,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC;IAKxD,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B;;;;OAIG;mBACkB,iBAAiB;IAmBtC,sEAAsE;mBACjD,aAAa;IAUlC;;;OAGG;IACU,0BAA0B,CACtC,QAAQ,EAAE,cAAc,EACxB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,GAAE,IAAiB,GACpB,OAAO,CAAC,MAAM,CAAC;IASlB,yEAAyE;IAC5D,YAAY,CACxB,QAAQ,EAAE,cAAc,EACxB,GAAG,GAAE,IAAiB,EACtB,qBAAqB,GAAE,MAAuC,GAC5D,OAAO,CAAC,MAAM,CAAC;IAUlB;;;;;OAKG;IACU,OAAO,CACnB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,EACC,UAAU,EACV,kBAAsB,EACtB,qBAAsD,GACtD,EAAE,oBAAoB,EACvB,GAAG,GAAE,IAAiB,GACpB,OAAO,CAAC,iBAAiB,CAAC;IA8C7B,yGAAyG;IAC5F,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,wGAAwG;IAC3F,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7D,uGAAuG;IAC1F,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhE"}
|