@thawgate/sdk 0.0.0-stage → 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/LICENSE +22 -0
- package/README.md +170 -2
- package/dist/index.d.mts +2391 -0
- package/dist/index.d.ts +2391 -0
- package/dist/index.js +7421 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7336 -0
- package/dist/index.mjs.map +1 -0
- package/dist/reasons.d.mts +95 -0
- package/dist/reasons.d.ts +95 -0
- package/dist/reasons.js +111 -0
- package/dist/reasons.js.map +1 -0
- package/dist/reasons.mjs +101 -0
- package/dist/reasons.mjs.map +1 -0
- package/package.json +85 -5
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module gate/reasons
|
|
3
|
+
* @description ThawGate's reason codes and the one parser for them.
|
|
4
|
+
*
|
|
5
|
+
* On every thaw and freeze decision the gate logs `TG:ALLOW:<CODE>` or `TG:DENY:<CODE>`
|
|
6
|
+
* (programs/thawgate-gate/src/decision.rs). `explain()`, `freezeIfInvalid()` and the keeper all read a transaction's
|
|
7
|
+
* logs through {@link classifyGateLogs}, so a wallet's reason is computed one way everywhere.
|
|
8
|
+
*
|
|
9
|
+
* This module has no imports. Services that don't want the SDK's Solana dependencies load it on its own:
|
|
10
|
+
* `import { classifyGateLogs } from "@thawgate/sdk/reasons"`.
|
|
11
|
+
*/
|
|
12
|
+
/** The ThawGate gating program (devnet). The same value as `THAWGATE_GATE_PROGRAM_ID`, as a string. */
|
|
13
|
+
declare const THAWGATE_GATE_ID = "THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ";
|
|
14
|
+
/** Which Token ACL permissionless instruction called the gate. */
|
|
15
|
+
type GateAction = "thaw" | "freeze";
|
|
16
|
+
/** Why a thaw passes (`TG:ALLOW:<code>` on thaw). */
|
|
17
|
+
declare const THAW_ALLOW_CODES: readonly ["KYC", "PDA_ALLOWLISTED", "ALLOWLISTED", "CLEAN"];
|
|
18
|
+
/** A policy flag on the owner: `TG:DENY:<code>` on thaw, `TG:ALLOW:<code>` on freeze. */
|
|
19
|
+
declare const FLAG_CODES: readonly ["BLACKLISTED", "NOT_ALLOWLISTED", "NO_CREDENTIAL", "CREDENTIAL_EXPIRED", "KYC_LEVEL_TOO_LOW"];
|
|
20
|
+
/** Denied before any policy decision, on thaw and on freeze. */
|
|
21
|
+
declare const STRUCTURAL_DENY_CODES: readonly ["MISSING_ACCOUNTS", "BAD_POLICY", "BAD_REGISTRY_ENTRY", "BAD_CREDENTIAL"];
|
|
22
|
+
type ThawAllowCode = (typeof THAW_ALLOW_CODES)[number];
|
|
23
|
+
type FlagCode = (typeof FLAG_CODES)[number];
|
|
24
|
+
type StructuralDenyCode = (typeof STRUCTURAL_DENY_CODES)[number];
|
|
25
|
+
/** Every code the gate logs. `NO_IMMUTABLE_OWNER` denies a thaw only; `COMPLIANT` denies a freeze only. */
|
|
26
|
+
type TgCode = ThawAllowCode | FlagCode | StructuralDenyCode | "NO_IMMUTABLE_OWNER" | "COMPLIANT";
|
|
27
|
+
/**
|
|
28
|
+
* The gate's custom error number for each `TG:DENY` code (errors.rs `Denied*`, 6009–6019). Used only when a
|
|
29
|
+
* simulation's logs were cut before the gate's own line.
|
|
30
|
+
*/
|
|
31
|
+
declare const GATE_DENY_ERRORS: Readonly<Record<number, TgCode>>;
|
|
32
|
+
declare function isTgCode(code: string): code is TgCode;
|
|
33
|
+
/**
|
|
34
|
+
* A human sentence for a gate decision, e.g. `describe("DENY", "NO_CREDENTIAL", "thaw")` →
|
|
35
|
+
* "Unlock denied: the owner has no SAS attestation …". The action matters: BLACKLISTED denies a thaw but allows a freeze.
|
|
36
|
+
*/
|
|
37
|
+
declare function describe(decision: "ALLOW" | "DENY", code: string, action: GateAction): string;
|
|
38
|
+
interface GateLogs {
|
|
39
|
+
/** A frame of the gating program appears in the logs. */
|
|
40
|
+
gateInvoked: boolean;
|
|
41
|
+
/** The gate's `TG:` line: the first one inside one of its own frames. */
|
|
42
|
+
decision: {
|
|
43
|
+
kind: "ALLOW" | "DENY";
|
|
44
|
+
code: string;
|
|
45
|
+
} | null;
|
|
46
|
+
/** The first `Program <id> failed: <message>` line. */
|
|
47
|
+
failure: {
|
|
48
|
+
program: string;
|
|
49
|
+
message: string;
|
|
50
|
+
} | null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reads a transaction's (or a simulation's) logs. Only `TG:` lines logged inside the gate's own frames count, so a
|
|
54
|
+
* different gating program can't impersonate a ThawGate decision. The first match wins: the gate's `msg!` line comes
|
|
55
|
+
* before Anchor's error line, which repeats the code.
|
|
56
|
+
*/
|
|
57
|
+
declare function parseGateLogs(logs: readonly string[], gateId?: string): GateLogs;
|
|
58
|
+
/** The gate's verdict on one thaw or freeze transaction. */
|
|
59
|
+
type GateVerdict =
|
|
60
|
+
/** The gate allowed it (a thaw unlocked the account, or a freeze froze it). */
|
|
61
|
+
{
|
|
62
|
+
outcome: "allowed";
|
|
63
|
+
code: string;
|
|
64
|
+
reason: string;
|
|
65
|
+
}
|
|
66
|
+
/** The gate denied it. On freeze, `COMPLIANT` means the owner passes the policy. */
|
|
67
|
+
| {
|
|
68
|
+
outcome: "denied";
|
|
69
|
+
code: string;
|
|
70
|
+
reason: string;
|
|
71
|
+
}
|
|
72
|
+
/** It succeeded without calling the gate: Token ACL's idempotent instructions return early on an account already
|
|
73
|
+
* in the target state. */
|
|
74
|
+
| {
|
|
75
|
+
outcome: "skipped";
|
|
76
|
+
reason: string;
|
|
77
|
+
}
|
|
78
|
+
/** It failed outside the gate's decision (Token ACL, Token-2022, the system program, fees, …). */
|
|
79
|
+
| {
|
|
80
|
+
outcome: "failed";
|
|
81
|
+
program: string;
|
|
82
|
+
message: string;
|
|
83
|
+
reason: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The verdict of a thaw or freeze transaction from its logs. `succeeded` is whether it landed (or simulated) without
|
|
87
|
+
* error. `errorCode` is the custom error number of a failed instruction, when known: it recovers the gate's code if
|
|
88
|
+
* the logs were cut.
|
|
89
|
+
*/
|
|
90
|
+
declare function classifyGateLogs(logs: readonly string[], succeeded: boolean, action: GateAction, opts?: {
|
|
91
|
+
gateId?: string;
|
|
92
|
+
errorCode?: number;
|
|
93
|
+
}): GateVerdict;
|
|
94
|
+
|
|
95
|
+
export { FLAG_CODES, type FlagCode, GATE_DENY_ERRORS, type GateAction, type GateLogs, type GateVerdict, STRUCTURAL_DENY_CODES, type StructuralDenyCode, THAWGATE_GATE_ID, THAW_ALLOW_CODES, type TgCode, type ThawAllowCode, classifyGateLogs, describe, isTgCode, parseGateLogs };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module gate/reasons
|
|
3
|
+
* @description ThawGate's reason codes and the one parser for them.
|
|
4
|
+
*
|
|
5
|
+
* On every thaw and freeze decision the gate logs `TG:ALLOW:<CODE>` or `TG:DENY:<CODE>`
|
|
6
|
+
* (programs/thawgate-gate/src/decision.rs). `explain()`, `freezeIfInvalid()` and the keeper all read a transaction's
|
|
7
|
+
* logs through {@link classifyGateLogs}, so a wallet's reason is computed one way everywhere.
|
|
8
|
+
*
|
|
9
|
+
* This module has no imports. Services that don't want the SDK's Solana dependencies load it on its own:
|
|
10
|
+
* `import { classifyGateLogs } from "@thawgate/sdk/reasons"`.
|
|
11
|
+
*/
|
|
12
|
+
/** The ThawGate gating program (devnet). The same value as `THAWGATE_GATE_PROGRAM_ID`, as a string. */
|
|
13
|
+
declare const THAWGATE_GATE_ID = "THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ";
|
|
14
|
+
/** Which Token ACL permissionless instruction called the gate. */
|
|
15
|
+
type GateAction = "thaw" | "freeze";
|
|
16
|
+
/** Why a thaw passes (`TG:ALLOW:<code>` on thaw). */
|
|
17
|
+
declare const THAW_ALLOW_CODES: readonly ["KYC", "PDA_ALLOWLISTED", "ALLOWLISTED", "CLEAN"];
|
|
18
|
+
/** A policy flag on the owner: `TG:DENY:<code>` on thaw, `TG:ALLOW:<code>` on freeze. */
|
|
19
|
+
declare const FLAG_CODES: readonly ["BLACKLISTED", "NOT_ALLOWLISTED", "NO_CREDENTIAL", "CREDENTIAL_EXPIRED", "KYC_LEVEL_TOO_LOW"];
|
|
20
|
+
/** Denied before any policy decision, on thaw and on freeze. */
|
|
21
|
+
declare const STRUCTURAL_DENY_CODES: readonly ["MISSING_ACCOUNTS", "BAD_POLICY", "BAD_REGISTRY_ENTRY", "BAD_CREDENTIAL"];
|
|
22
|
+
type ThawAllowCode = (typeof THAW_ALLOW_CODES)[number];
|
|
23
|
+
type FlagCode = (typeof FLAG_CODES)[number];
|
|
24
|
+
type StructuralDenyCode = (typeof STRUCTURAL_DENY_CODES)[number];
|
|
25
|
+
/** Every code the gate logs. `NO_IMMUTABLE_OWNER` denies a thaw only; `COMPLIANT` denies a freeze only. */
|
|
26
|
+
type TgCode = ThawAllowCode | FlagCode | StructuralDenyCode | "NO_IMMUTABLE_OWNER" | "COMPLIANT";
|
|
27
|
+
/**
|
|
28
|
+
* The gate's custom error number for each `TG:DENY` code (errors.rs `Denied*`, 6009–6019). Used only when a
|
|
29
|
+
* simulation's logs were cut before the gate's own line.
|
|
30
|
+
*/
|
|
31
|
+
declare const GATE_DENY_ERRORS: Readonly<Record<number, TgCode>>;
|
|
32
|
+
declare function isTgCode(code: string): code is TgCode;
|
|
33
|
+
/**
|
|
34
|
+
* A human sentence for a gate decision, e.g. `describe("DENY", "NO_CREDENTIAL", "thaw")` →
|
|
35
|
+
* "Unlock denied: the owner has no SAS attestation …". The action matters: BLACKLISTED denies a thaw but allows a freeze.
|
|
36
|
+
*/
|
|
37
|
+
declare function describe(decision: "ALLOW" | "DENY", code: string, action: GateAction): string;
|
|
38
|
+
interface GateLogs {
|
|
39
|
+
/** A frame of the gating program appears in the logs. */
|
|
40
|
+
gateInvoked: boolean;
|
|
41
|
+
/** The gate's `TG:` line: the first one inside one of its own frames. */
|
|
42
|
+
decision: {
|
|
43
|
+
kind: "ALLOW" | "DENY";
|
|
44
|
+
code: string;
|
|
45
|
+
} | null;
|
|
46
|
+
/** The first `Program <id> failed: <message>` line. */
|
|
47
|
+
failure: {
|
|
48
|
+
program: string;
|
|
49
|
+
message: string;
|
|
50
|
+
} | null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reads a transaction's (or a simulation's) logs. Only `TG:` lines logged inside the gate's own frames count, so a
|
|
54
|
+
* different gating program can't impersonate a ThawGate decision. The first match wins: the gate's `msg!` line comes
|
|
55
|
+
* before Anchor's error line, which repeats the code.
|
|
56
|
+
*/
|
|
57
|
+
declare function parseGateLogs(logs: readonly string[], gateId?: string): GateLogs;
|
|
58
|
+
/** The gate's verdict on one thaw or freeze transaction. */
|
|
59
|
+
type GateVerdict =
|
|
60
|
+
/** The gate allowed it (a thaw unlocked the account, or a freeze froze it). */
|
|
61
|
+
{
|
|
62
|
+
outcome: "allowed";
|
|
63
|
+
code: string;
|
|
64
|
+
reason: string;
|
|
65
|
+
}
|
|
66
|
+
/** The gate denied it. On freeze, `COMPLIANT` means the owner passes the policy. */
|
|
67
|
+
| {
|
|
68
|
+
outcome: "denied";
|
|
69
|
+
code: string;
|
|
70
|
+
reason: string;
|
|
71
|
+
}
|
|
72
|
+
/** It succeeded without calling the gate: Token ACL's idempotent instructions return early on an account already
|
|
73
|
+
* in the target state. */
|
|
74
|
+
| {
|
|
75
|
+
outcome: "skipped";
|
|
76
|
+
reason: string;
|
|
77
|
+
}
|
|
78
|
+
/** It failed outside the gate's decision (Token ACL, Token-2022, the system program, fees, …). */
|
|
79
|
+
| {
|
|
80
|
+
outcome: "failed";
|
|
81
|
+
program: string;
|
|
82
|
+
message: string;
|
|
83
|
+
reason: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The verdict of a thaw or freeze transaction from its logs. `succeeded` is whether it landed (or simulated) without
|
|
87
|
+
* error. `errorCode` is the custom error number of a failed instruction, when known: it recovers the gate's code if
|
|
88
|
+
* the logs were cut.
|
|
89
|
+
*/
|
|
90
|
+
declare function classifyGateLogs(logs: readonly string[], succeeded: boolean, action: GateAction, opts?: {
|
|
91
|
+
gateId?: string;
|
|
92
|
+
errorCode?: number;
|
|
93
|
+
}): GateVerdict;
|
|
94
|
+
|
|
95
|
+
export { FLAG_CODES, type FlagCode, GATE_DENY_ERRORS, type GateAction, type GateLogs, type GateVerdict, STRUCTURAL_DENY_CODES, type StructuralDenyCode, THAWGATE_GATE_ID, THAW_ALLOW_CODES, type TgCode, type ThawAllowCode, classifyGateLogs, describe, isTgCode, parseGateLogs };
|
package/dist/reasons.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/gate/reasons.ts
|
|
4
|
+
var THAWGATE_GATE_ID = "THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ";
|
|
5
|
+
var THAW_ALLOW_CODES = ["KYC", "PDA_ALLOWLISTED", "ALLOWLISTED", "CLEAN"];
|
|
6
|
+
var FLAG_CODES = ["BLACKLISTED", "NOT_ALLOWLISTED", "NO_CREDENTIAL", "CREDENTIAL_EXPIRED", "KYC_LEVEL_TOO_LOW"];
|
|
7
|
+
var STRUCTURAL_DENY_CODES = ["MISSING_ACCOUNTS", "BAD_POLICY", "BAD_REGISTRY_ENTRY", "BAD_CREDENTIAL"];
|
|
8
|
+
var FACTS = {
|
|
9
|
+
KYC: "the owner holds a live SAS attestation from the policy's credential and schema",
|
|
10
|
+
PDA_ALLOWLISTED: "the owner is a program address (PDA) on the issuer's allowlist (BypassForPdas)",
|
|
11
|
+
ALLOWLISTED: "the owner is on the issuer's allowlist",
|
|
12
|
+
CLEAN: "no policy check flags the owner",
|
|
13
|
+
BLACKLISTED: "the owner is on the issuer's blacklist",
|
|
14
|
+
NOT_ALLOWLISTED: "the policy is allowlist-only and the owner is not on the issuer's allowlist",
|
|
15
|
+
NO_CREDENTIAL: "the owner has no SAS attestation from the policy's credential and schema (never issued, or revoked)",
|
|
16
|
+
CREDENTIAL_EXPIRED: "the owner's SAS attestation has expired",
|
|
17
|
+
KYC_LEVEL_TOO_LOW: "the owner's kyc_level is below the policy minimum",
|
|
18
|
+
NO_IMMUTABLE_OWNER: "the token account lacks the ImmutableOwner extension (use an associated token account)",
|
|
19
|
+
MISSING_ACCOUNTS: "the transaction left out accounts the policy needs (resolve the gate's extra account metas)",
|
|
20
|
+
BAD_POLICY: "the policy account is not this mint's ThawGate policy",
|
|
21
|
+
BAD_REGISTRY_ENTRY: "a blacklist or allowlist account has the wrong owner, type or fields",
|
|
22
|
+
BAD_CREDENTIAL: "the account at the attestation address is not a SAS attestation of this credential, schema and owner",
|
|
23
|
+
COMPLIANT: "the owner passes the policy"
|
|
24
|
+
};
|
|
25
|
+
var GATE_DENY_ERRORS = {
|
|
26
|
+
6009: "MISSING_ACCOUNTS",
|
|
27
|
+
6010: "BAD_POLICY",
|
|
28
|
+
6011: "BAD_REGISTRY_ENTRY",
|
|
29
|
+
6012: "BAD_CREDENTIAL",
|
|
30
|
+
6013: "NO_IMMUTABLE_OWNER",
|
|
31
|
+
6014: "BLACKLISTED",
|
|
32
|
+
6015: "NOT_ALLOWLISTED",
|
|
33
|
+
6016: "NO_CREDENTIAL",
|
|
34
|
+
6017: "CREDENTIAL_EXPIRED",
|
|
35
|
+
6018: "KYC_LEVEL_TOO_LOW",
|
|
36
|
+
6019: "COMPLIANT"
|
|
37
|
+
};
|
|
38
|
+
function isTgCode(code) {
|
|
39
|
+
return Object.prototype.hasOwnProperty.call(FACTS, code);
|
|
40
|
+
}
|
|
41
|
+
function describe(decision, code, action) {
|
|
42
|
+
const fact = isTgCode(code) ? FACTS[code] : `the gate returned ${code}, a code this SDK version doesn't know`;
|
|
43
|
+
if (action === "thaw") return `${decision === "ALLOW" ? "Unlock allowed" : "Unlock denied"}: ${fact}.`;
|
|
44
|
+
if (decision === "ALLOW") return `Freezable by anyone: ${fact}.`;
|
|
45
|
+
return code === "COMPLIANT" ? `Not freezable: ${fact}.` : `Freeze denied: ${fact}.`;
|
|
46
|
+
}
|
|
47
|
+
var INVOKE = /^Program (\S+) invoke \[\d+\]$/;
|
|
48
|
+
var EXIT = /^Program (\S+) (?:success|failed: .*)$/;
|
|
49
|
+
var FAILED = /^Program (\S+) failed: (.*)$/;
|
|
50
|
+
var TG_LINE = /^Program log: .*?TG:(ALLOW|DENY):([A-Z_]+)/;
|
|
51
|
+
function parseGateLogs(logs, gateId = THAWGATE_GATE_ID) {
|
|
52
|
+
const stack = [];
|
|
53
|
+
let gateInvoked = false;
|
|
54
|
+
let decision = null;
|
|
55
|
+
let failure = null;
|
|
56
|
+
for (const line of logs) {
|
|
57
|
+
const invoke = INVOKE.exec(line);
|
|
58
|
+
if (invoke) {
|
|
59
|
+
stack.push(invoke[1]);
|
|
60
|
+
if (invoke[1] === gateId) gateInvoked = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const failed = FAILED.exec(line);
|
|
64
|
+
if (failed && !failure) failure = { program: failed[1], message: failed[2] };
|
|
65
|
+
if (EXIT.test(line)) {
|
|
66
|
+
stack.pop();
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!decision && stack[stack.length - 1] === gateId) {
|
|
70
|
+
const tg = TG_LINE.exec(line);
|
|
71
|
+
if (tg) decision = { kind: tg[1], code: tg[2] };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { gateInvoked, decision, failure };
|
|
75
|
+
}
|
|
76
|
+
function classifyGateLogs(logs, succeeded, action, opts = {}) {
|
|
77
|
+
const { gateInvoked, decision, failure } = parseGateLogs(logs, opts.gateId);
|
|
78
|
+
if (succeeded) {
|
|
79
|
+
if (!gateInvoked) {
|
|
80
|
+
const state = action === "thaw" ? "thawed" : "frozen";
|
|
81
|
+
return { outcome: "skipped", reason: `No change: the account is already ${state}, so Token ACL did not call the gate.` };
|
|
82
|
+
}
|
|
83
|
+
const code = decision?.kind === "ALLOW" ? decision.code : "UNKNOWN";
|
|
84
|
+
return { outcome: "allowed", code, reason: describe("ALLOW", code, action) };
|
|
85
|
+
}
|
|
86
|
+
if (decision?.kind === "DENY") return { outcome: "denied", code: decision.code, reason: describe("DENY", decision.code, action) };
|
|
87
|
+
const gateFailed = failure === null || failure.program === (opts.gateId ?? THAWGATE_GATE_ID);
|
|
88
|
+
const fromError = opts.errorCode !== void 0 && gateFailed ? GATE_DENY_ERRORS[opts.errorCode] : void 0;
|
|
89
|
+
if (fromError) return { outcome: "denied", code: fromError, reason: describe("DENY", fromError, action) };
|
|
90
|
+
if (failure) {
|
|
91
|
+
return {
|
|
92
|
+
outcome: "failed",
|
|
93
|
+
program: failure.program,
|
|
94
|
+
message: failure.message,
|
|
95
|
+
reason: `Failed before the gate decided: ${failure.program} returned "${failure.message}".`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { outcome: "failed", program: "unknown", message: "no logs", reason: "Failed, and the logs give no reason." };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
exports.FLAG_CODES = FLAG_CODES;
|
|
102
|
+
exports.GATE_DENY_ERRORS = GATE_DENY_ERRORS;
|
|
103
|
+
exports.STRUCTURAL_DENY_CODES = STRUCTURAL_DENY_CODES;
|
|
104
|
+
exports.THAWGATE_GATE_ID = THAWGATE_GATE_ID;
|
|
105
|
+
exports.THAW_ALLOW_CODES = THAW_ALLOW_CODES;
|
|
106
|
+
exports.classifyGateLogs = classifyGateLogs;
|
|
107
|
+
exports.describe = describe;
|
|
108
|
+
exports.isTgCode = isTgCode;
|
|
109
|
+
exports.parseGateLogs = parseGateLogs;
|
|
110
|
+
//# sourceMappingURL=reasons.js.map
|
|
111
|
+
//# sourceMappingURL=reasons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/gate/reasons.ts"],"names":[],"mappings":";;;AAaO,IAAM,gBAAA,GAAmB;AAMzB,IAAM,gBAAA,GAAmB,CAAC,KAAA,EAAO,iBAAA,EAAmB,eAAe,OAAO;AAE1E,IAAM,aAAa,CAAC,aAAA,EAAe,iBAAA,EAAmB,eAAA,EAAiB,sBAAsB,mBAAmB;AAEhH,IAAM,qBAAA,GAAwB,CAAC,kBAAA,EAAoB,YAAA,EAAc,sBAAsB,gBAAgB;AAS9G,IAAM,KAAA,GAAgC;AAAA,EAClC,GAAA,EAAK,gFAAA;AAAA,EACL,eAAA,EAAiB,gFAAA;AAAA,EACjB,WAAA,EAAa,wCAAA;AAAA,EACb,KAAA,EAAO,iCAAA;AAAA,EACP,WAAA,EAAa,wCAAA;AAAA,EACb,eAAA,EAAiB,6EAAA;AAAA,EACjB,aAAA,EAAe,qGAAA;AAAA,EACf,kBAAA,EAAoB,yCAAA;AAAA,EACpB,iBAAA,EAAmB,mDAAA;AAAA,EACnB,kBAAA,EAAoB,wFAAA;AAAA,EACpB,gBAAA,EAAkB,6FAAA;AAAA,EAClB,UAAA,EAAY,uDAAA;AAAA,EACZ,kBAAA,EAAoB,sEAAA;AAAA,EACpB,cAAA,EAAgB,sGAAA;AAAA,EAChB,SAAA,EAAW;AACf,CAAA;AAMO,IAAM,gBAAA,GAAqD;AAAA,EAC9D,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,iBAAA;AAAA,EACN,IAAA,EAAM,eAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA,EACN,IAAA,EAAM,mBAAA;AAAA,EACN,IAAA,EAAM;AACV;AAEO,SAAS,SAAS,IAAA,EAA8B;AACnD,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,OAAO,IAAI,CAAA;AAC3D;AAMO,SAAS,QAAA,CAAS,QAAA,EAA4B,IAAA,EAAc,MAAA,EAA4B;AAC3F,EAAA,MAAM,IAAA,GAAO,SAAS,IAAI,CAAA,GAAI,MAAM,IAAI,CAAA,GAAI,qBAAqB,IAAI,CAAA,sCAAA,CAAA;AACrE,EAAA,IAAI,MAAA,KAAW,QAAQ,OAAO,CAAA,EAAG,aAAa,OAAA,GAAU,gBAAA,GAAmB,eAAe,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAA;AACnG,EAAA,IAAI,QAAA,KAAa,OAAA,EAAS,OAAO,CAAA,qBAAA,EAAwB,IAAI,CAAA,CAAA,CAAA;AAC7D,EAAA,OAAO,SAAS,WAAA,GAAc,CAAA,eAAA,EAAkB,IAAI,CAAA,CAAA,CAAA,GAAM,kBAAkB,IAAI,CAAA,CAAA,CAAA;AACpF;AAWA,IAAM,MAAA,GAAS,gCAAA;AACf,IAAM,IAAA,GAAO,wCAAA;AACb,IAAM,MAAA,GAAS,8BAAA;AACf,IAAM,OAAA,GAAU,4CAAA;AAOT,SAAS,aAAA,CAAc,IAAA,EAAyB,MAAA,GAAiB,gBAAA,EAA4B;AAChG,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,IAAI,QAAA,GAAiC,IAAA;AACrC,EAAA,IAAI,OAAA,GAA+B,IAAA;AACnC,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACrB,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AAC/B,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AACpB,MAAA,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,MAAA,EAAQ,WAAA,GAAc,IAAA;AACxC,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AAC/B,IAAA,IAAI,MAAA,IAAU,CAAC,OAAA,EAAS,OAAA,GAAU,EAAE,OAAA,EAAS,MAAA,CAAO,CAAC,CAAA,EAAG,OAAA,EAAS,MAAA,CAAO,CAAC,CAAA,EAAE;AAC3E,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA,EAAG;AACjB,MAAA,KAAA,CAAM,GAAA,EAAI;AACV,MAAA;AAAA,IACJ;AACA,IAAA,IAAI,CAAC,QAAA,IAAY,KAAA,CAAM,MAAM,MAAA,GAAS,CAAC,MAAM,MAAA,EAAQ;AACjD,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAC5B,MAAA,IAAI,EAAA,EAAI,QAAA,GAAW,EAAE,IAAA,EAAM,EAAA,CAAG,CAAC,CAAA,EAAuB,IAAA,EAAM,EAAA,CAAG,CAAC,CAAA,EAAE;AAAA,IACtE;AAAA,EACJ;AACA,EAAA,OAAO,EAAE,WAAA,EAAa,QAAA,EAAU,OAAA,EAAQ;AAC5C;AAmBO,SAAS,iBACZ,IAAA,EACA,SAAA,EACA,MAAA,EACA,IAAA,GAAgD,EAAC,EACtC;AACX,EAAA,MAAM,EAAE,aAAa,QAAA,EAAU,OAAA,KAAY,aAAA,CAAc,IAAA,EAAM,KAAK,MAAM,CAAA;AAC1E,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,IAAI,CAAC,WAAA,EAAa;AACd,MAAA,MAAM,KAAA,GAAQ,MAAA,KAAW,MAAA,GAAS,QAAA,GAAW,QAAA;AAC7C,MAAA,OAAO,EAAE,OAAA,EAAS,SAAA,EAAW,MAAA,EAAQ,CAAA,kCAAA,EAAqC,KAAK,CAAA,qCAAA,CAAA,EAAwC;AAAA,IAC3H;AACA,IAAA,MAAM,IAAA,GAAO,QAAA,EAAU,IAAA,KAAS,OAAA,GAAU,SAAS,IAAA,GAAO,SAAA;AAC1D,IAAA,OAAO,EAAE,SAAS,SAAA,EAAW,IAAA,EAAM,QAAQ,QAAA,CAAS,OAAA,EAAS,IAAA,EAAM,MAAM,CAAA,EAAE;AAAA,EAC/E;AACA,EAAA,IAAI,UAAU,IAAA,KAAS,MAAA,EAAQ,OAAO,EAAE,SAAS,QAAA,EAAU,IAAA,EAAM,QAAA,CAAS,IAAA,EAAM,QAAQ,QAAA,CAAS,MAAA,EAAQ,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA,EAAE;AAGhI,EAAA,MAAM,aAAa,OAAA,KAAY,IAAA,IAAQ,OAAA,CAAQ,OAAA,MAAa,KAAK,MAAA,IAAU,gBAAA,CAAA;AAC3E,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,KAAc,MAAA,IAAa,aAAa,gBAAA,CAAiB,IAAA,CAAK,SAAS,CAAA,GAAI,MAAA;AAClG,EAAA,IAAI,SAAA,EAAW,OAAO,EAAE,OAAA,EAAS,QAAA,EAAU,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,QAAA,CAAS,MAAA,EAAQ,SAAA,EAAW,MAAM,CAAA,EAAE;AACxG,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAO;AAAA,MACH,OAAA,EAAS,QAAA;AAAA,MACT,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,CAAA,gCAAA,EAAmC,OAAA,CAAQ,OAAO,CAAA,WAAA,EAAc,QAAQ,OAAO,CAAA,EAAA;AAAA,KAC3F;AAAA,EACJ;AACA,EAAA,OAAO,EAAE,SAAS,QAAA,EAAU,OAAA,EAAS,WAAW,OAAA,EAAS,SAAA,EAAW,QAAQ,sCAAA,EAAuC;AACvH","file":"reasons.js","sourcesContent":["/**\n * @module gate/reasons\n * @description ThawGate's reason codes and the one parser for them.\n *\n * On every thaw and freeze decision the gate logs `TG:ALLOW:<CODE>` or `TG:DENY:<CODE>`\n * (programs/thawgate-gate/src/decision.rs). `explain()`, `freezeIfInvalid()` and the keeper all read a transaction's\n * logs through {@link classifyGateLogs}, so a wallet's reason is computed one way everywhere.\n *\n * This module has no imports. Services that don't want the SDK's Solana dependencies load it on its own:\n * `import { classifyGateLogs } from \"@thawgate/sdk/reasons\"`.\n */\n\n/** The ThawGate gating program (devnet). The same value as `THAWGATE_GATE_PROGRAM_ID`, as a string. */\nexport const THAWGATE_GATE_ID = \"THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ\";\n\n/** Which Token ACL permissionless instruction called the gate. */\nexport type GateAction = \"thaw\" | \"freeze\";\n\n/** Why a thaw passes (`TG:ALLOW:<code>` on thaw). */\nexport const THAW_ALLOW_CODES = [\"KYC\", \"PDA_ALLOWLISTED\", \"ALLOWLISTED\", \"CLEAN\"] as const;\n/** A policy flag on the owner: `TG:DENY:<code>` on thaw, `TG:ALLOW:<code>` on freeze. */\nexport const FLAG_CODES = [\"BLACKLISTED\", \"NOT_ALLOWLISTED\", \"NO_CREDENTIAL\", \"CREDENTIAL_EXPIRED\", \"KYC_LEVEL_TOO_LOW\"] as const;\n/** Denied before any policy decision, on thaw and on freeze. */\nexport const STRUCTURAL_DENY_CODES = [\"MISSING_ACCOUNTS\", \"BAD_POLICY\", \"BAD_REGISTRY_ENTRY\", \"BAD_CREDENTIAL\"] as const;\n\nexport type ThawAllowCode = (typeof THAW_ALLOW_CODES)[number];\nexport type FlagCode = (typeof FLAG_CODES)[number];\nexport type StructuralDenyCode = (typeof STRUCTURAL_DENY_CODES)[number];\n/** Every code the gate logs. `NO_IMMUTABLE_OWNER` denies a thaw only; `COMPLIANT` denies a freeze only. */\nexport type TgCode = ThawAllowCode | FlagCode | StructuralDenyCode | \"NO_IMMUTABLE_OWNER\" | \"COMPLIANT\";\n\n/** What each code says about the owner, as the second half of a sentence. */\nconst FACTS: Record<TgCode, string> = {\n KYC: \"the owner holds a live SAS attestation from the policy's credential and schema\",\n PDA_ALLOWLISTED: \"the owner is a program address (PDA) on the issuer's allowlist (BypassForPdas)\",\n ALLOWLISTED: \"the owner is on the issuer's allowlist\",\n CLEAN: \"no policy check flags the owner\",\n BLACKLISTED: \"the owner is on the issuer's blacklist\",\n NOT_ALLOWLISTED: \"the policy is allowlist-only and the owner is not on the issuer's allowlist\",\n NO_CREDENTIAL: \"the owner has no SAS attestation from the policy's credential and schema (never issued, or revoked)\",\n CREDENTIAL_EXPIRED: \"the owner's SAS attestation has expired\",\n KYC_LEVEL_TOO_LOW: \"the owner's kyc_level is below the policy minimum\",\n NO_IMMUTABLE_OWNER: \"the token account lacks the ImmutableOwner extension (use an associated token account)\",\n MISSING_ACCOUNTS: \"the transaction left out accounts the policy needs (resolve the gate's extra account metas)\",\n BAD_POLICY: \"the policy account is not this mint's ThawGate policy\",\n BAD_REGISTRY_ENTRY: \"a blacklist or allowlist account has the wrong owner, type or fields\",\n BAD_CREDENTIAL: \"the account at the attestation address is not a SAS attestation of this credential, schema and owner\",\n COMPLIANT: \"the owner passes the policy\",\n};\n\n/**\n * The gate's custom error number for each `TG:DENY` code (errors.rs `Denied*`, 6009–6019). Used only when a\n * simulation's logs were cut before the gate's own line.\n */\nexport const GATE_DENY_ERRORS: Readonly<Record<number, TgCode>> = {\n 6009: \"MISSING_ACCOUNTS\",\n 6010: \"BAD_POLICY\",\n 6011: \"BAD_REGISTRY_ENTRY\",\n 6012: \"BAD_CREDENTIAL\",\n 6013: \"NO_IMMUTABLE_OWNER\",\n 6014: \"BLACKLISTED\",\n 6015: \"NOT_ALLOWLISTED\",\n 6016: \"NO_CREDENTIAL\",\n 6017: \"CREDENTIAL_EXPIRED\",\n 6018: \"KYC_LEVEL_TOO_LOW\",\n 6019: \"COMPLIANT\",\n};\n\nexport function isTgCode(code: string): code is TgCode {\n return Object.prototype.hasOwnProperty.call(FACTS, code);\n}\n\n/**\n * A human sentence for a gate decision, e.g. `describe(\"DENY\", \"NO_CREDENTIAL\", \"thaw\")` →\n * \"Unlock denied: the owner has no SAS attestation …\". The action matters: BLACKLISTED denies a thaw but allows a freeze.\n */\nexport function describe(decision: \"ALLOW\" | \"DENY\", code: string, action: GateAction): string {\n const fact = isTgCode(code) ? FACTS[code] : `the gate returned ${code}, a code this SDK version doesn't know`;\n if (action === \"thaw\") return `${decision === \"ALLOW\" ? \"Unlock allowed\" : \"Unlock denied\"}: ${fact}.`;\n if (decision === \"ALLOW\") return `Freezable by anyone: ${fact}.`;\n return code === \"COMPLIANT\" ? `Not freezable: ${fact}.` : `Freeze denied: ${fact}.`;\n}\n\nexport interface GateLogs {\n /** A frame of the gating program appears in the logs. */\n gateInvoked: boolean;\n /** The gate's `TG:` line: the first one inside one of its own frames. */\n decision: { kind: \"ALLOW\" | \"DENY\"; code: string } | null;\n /** The first `Program <id> failed: <message>` line. */\n failure: { program: string; message: string } | null;\n}\n\nconst INVOKE = /^Program (\\S+) invoke \\[\\d+\\]$/;\nconst EXIT = /^Program (\\S+) (?:success|failed: .*)$/;\nconst FAILED = /^Program (\\S+) failed: (.*)$/;\nconst TG_LINE = /^Program log: .*?TG:(ALLOW|DENY):([A-Z_]+)/;\n\n/**\n * Reads a transaction's (or a simulation's) logs. Only `TG:` lines logged inside the gate's own frames count, so a\n * different gating program can't impersonate a ThawGate decision. The first match wins: the gate's `msg!` line comes\n * before Anchor's error line, which repeats the code.\n */\nexport function parseGateLogs(logs: readonly string[], gateId: string = THAWGATE_GATE_ID): GateLogs {\n const stack: string[] = [];\n let gateInvoked = false;\n let decision: GateLogs[\"decision\"] = null;\n let failure: GateLogs[\"failure\"] = null;\n for (const line of logs) {\n const invoke = INVOKE.exec(line);\n if (invoke) {\n stack.push(invoke[1]);\n if (invoke[1] === gateId) gateInvoked = true;\n continue;\n }\n const failed = FAILED.exec(line);\n if (failed && !failure) failure = { program: failed[1], message: failed[2] };\n if (EXIT.test(line)) {\n stack.pop();\n continue;\n }\n if (!decision && stack[stack.length - 1] === gateId) {\n const tg = TG_LINE.exec(line);\n if (tg) decision = { kind: tg[1] as \"ALLOW\" | \"DENY\", code: tg[2] };\n }\n }\n return { gateInvoked, decision, failure };\n}\n\n/** The gate's verdict on one thaw or freeze transaction. */\nexport type GateVerdict =\n /** The gate allowed it (a thaw unlocked the account, or a freeze froze it). */\n | { outcome: \"allowed\"; code: string; reason: string }\n /** The gate denied it. On freeze, `COMPLIANT` means the owner passes the policy. */\n | { outcome: \"denied\"; code: string; reason: string }\n /** It succeeded without calling the gate: Token ACL's idempotent instructions return early on an account already\n * in the target state. */\n | { outcome: \"skipped\"; reason: string }\n /** It failed outside the gate's decision (Token ACL, Token-2022, the system program, fees, …). */\n | { outcome: \"failed\"; program: string; message: string; reason: string };\n\n/**\n * The verdict of a thaw or freeze transaction from its logs. `succeeded` is whether it landed (or simulated) without\n * error. `errorCode` is the custom error number of a failed instruction, when known: it recovers the gate's code if\n * the logs were cut.\n */\nexport function classifyGateLogs(\n logs: readonly string[],\n succeeded: boolean,\n action: GateAction,\n opts: { gateId?: string; errorCode?: number } = {},\n): GateVerdict {\n const { gateInvoked, decision, failure } = parseGateLogs(logs, opts.gateId);\n if (succeeded) {\n if (!gateInvoked) {\n const state = action === \"thaw\" ? \"thawed\" : \"frozen\";\n return { outcome: \"skipped\", reason: `No change: the account is already ${state}, so Token ACL did not call the gate.` };\n }\n const code = decision?.kind === \"ALLOW\" ? decision.code : \"UNKNOWN\";\n return { outcome: \"allowed\", code, reason: describe(\"ALLOW\", code, action) };\n }\n if (decision?.kind === \"DENY\") return { outcome: \"denied\", code: decision.code, reason: describe(\"DENY\", decision.code, action) };\n // Truncated logs (\"Log truncated\") usually lose the gate's own failed line too, so accept a missing failure line.\n // 6009–6019 don't collide with Token ACL's or Token-2022's error numbers, which are small.\n const gateFailed = failure === null || failure.program === (opts.gateId ?? THAWGATE_GATE_ID);\n const fromError = opts.errorCode !== undefined && gateFailed ? GATE_DENY_ERRORS[opts.errorCode] : undefined;\n if (fromError) return { outcome: \"denied\", code: fromError, reason: describe(\"DENY\", fromError, action) };\n if (failure) {\n return {\n outcome: \"failed\",\n program: failure.program,\n message: failure.message,\n reason: `Failed before the gate decided: ${failure.program} returned \"${failure.message}\".`,\n };\n }\n return { outcome: \"failed\", program: \"unknown\", message: \"no logs\", reason: \"Failed, and the logs give no reason.\" };\n}\n"]}
|
package/dist/reasons.mjs
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// src/gate/reasons.ts
|
|
2
|
+
var THAWGATE_GATE_ID = "THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ";
|
|
3
|
+
var THAW_ALLOW_CODES = ["KYC", "PDA_ALLOWLISTED", "ALLOWLISTED", "CLEAN"];
|
|
4
|
+
var FLAG_CODES = ["BLACKLISTED", "NOT_ALLOWLISTED", "NO_CREDENTIAL", "CREDENTIAL_EXPIRED", "KYC_LEVEL_TOO_LOW"];
|
|
5
|
+
var STRUCTURAL_DENY_CODES = ["MISSING_ACCOUNTS", "BAD_POLICY", "BAD_REGISTRY_ENTRY", "BAD_CREDENTIAL"];
|
|
6
|
+
var FACTS = {
|
|
7
|
+
KYC: "the owner holds a live SAS attestation from the policy's credential and schema",
|
|
8
|
+
PDA_ALLOWLISTED: "the owner is a program address (PDA) on the issuer's allowlist (BypassForPdas)",
|
|
9
|
+
ALLOWLISTED: "the owner is on the issuer's allowlist",
|
|
10
|
+
CLEAN: "no policy check flags the owner",
|
|
11
|
+
BLACKLISTED: "the owner is on the issuer's blacklist",
|
|
12
|
+
NOT_ALLOWLISTED: "the policy is allowlist-only and the owner is not on the issuer's allowlist",
|
|
13
|
+
NO_CREDENTIAL: "the owner has no SAS attestation from the policy's credential and schema (never issued, or revoked)",
|
|
14
|
+
CREDENTIAL_EXPIRED: "the owner's SAS attestation has expired",
|
|
15
|
+
KYC_LEVEL_TOO_LOW: "the owner's kyc_level is below the policy minimum",
|
|
16
|
+
NO_IMMUTABLE_OWNER: "the token account lacks the ImmutableOwner extension (use an associated token account)",
|
|
17
|
+
MISSING_ACCOUNTS: "the transaction left out accounts the policy needs (resolve the gate's extra account metas)",
|
|
18
|
+
BAD_POLICY: "the policy account is not this mint's ThawGate policy",
|
|
19
|
+
BAD_REGISTRY_ENTRY: "a blacklist or allowlist account has the wrong owner, type or fields",
|
|
20
|
+
BAD_CREDENTIAL: "the account at the attestation address is not a SAS attestation of this credential, schema and owner",
|
|
21
|
+
COMPLIANT: "the owner passes the policy"
|
|
22
|
+
};
|
|
23
|
+
var GATE_DENY_ERRORS = {
|
|
24
|
+
6009: "MISSING_ACCOUNTS",
|
|
25
|
+
6010: "BAD_POLICY",
|
|
26
|
+
6011: "BAD_REGISTRY_ENTRY",
|
|
27
|
+
6012: "BAD_CREDENTIAL",
|
|
28
|
+
6013: "NO_IMMUTABLE_OWNER",
|
|
29
|
+
6014: "BLACKLISTED",
|
|
30
|
+
6015: "NOT_ALLOWLISTED",
|
|
31
|
+
6016: "NO_CREDENTIAL",
|
|
32
|
+
6017: "CREDENTIAL_EXPIRED",
|
|
33
|
+
6018: "KYC_LEVEL_TOO_LOW",
|
|
34
|
+
6019: "COMPLIANT"
|
|
35
|
+
};
|
|
36
|
+
function isTgCode(code) {
|
|
37
|
+
return Object.prototype.hasOwnProperty.call(FACTS, code);
|
|
38
|
+
}
|
|
39
|
+
function describe(decision, code, action) {
|
|
40
|
+
const fact = isTgCode(code) ? FACTS[code] : `the gate returned ${code}, a code this SDK version doesn't know`;
|
|
41
|
+
if (action === "thaw") return `${decision === "ALLOW" ? "Unlock allowed" : "Unlock denied"}: ${fact}.`;
|
|
42
|
+
if (decision === "ALLOW") return `Freezable by anyone: ${fact}.`;
|
|
43
|
+
return code === "COMPLIANT" ? `Not freezable: ${fact}.` : `Freeze denied: ${fact}.`;
|
|
44
|
+
}
|
|
45
|
+
var INVOKE = /^Program (\S+) invoke \[\d+\]$/;
|
|
46
|
+
var EXIT = /^Program (\S+) (?:success|failed: .*)$/;
|
|
47
|
+
var FAILED = /^Program (\S+) failed: (.*)$/;
|
|
48
|
+
var TG_LINE = /^Program log: .*?TG:(ALLOW|DENY):([A-Z_]+)/;
|
|
49
|
+
function parseGateLogs(logs, gateId = THAWGATE_GATE_ID) {
|
|
50
|
+
const stack = [];
|
|
51
|
+
let gateInvoked = false;
|
|
52
|
+
let decision = null;
|
|
53
|
+
let failure = null;
|
|
54
|
+
for (const line of logs) {
|
|
55
|
+
const invoke = INVOKE.exec(line);
|
|
56
|
+
if (invoke) {
|
|
57
|
+
stack.push(invoke[1]);
|
|
58
|
+
if (invoke[1] === gateId) gateInvoked = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const failed = FAILED.exec(line);
|
|
62
|
+
if (failed && !failure) failure = { program: failed[1], message: failed[2] };
|
|
63
|
+
if (EXIT.test(line)) {
|
|
64
|
+
stack.pop();
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!decision && stack[stack.length - 1] === gateId) {
|
|
68
|
+
const tg = TG_LINE.exec(line);
|
|
69
|
+
if (tg) decision = { kind: tg[1], code: tg[2] };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { gateInvoked, decision, failure };
|
|
73
|
+
}
|
|
74
|
+
function classifyGateLogs(logs, succeeded, action, opts = {}) {
|
|
75
|
+
const { gateInvoked, decision, failure } = parseGateLogs(logs, opts.gateId);
|
|
76
|
+
if (succeeded) {
|
|
77
|
+
if (!gateInvoked) {
|
|
78
|
+
const state = action === "thaw" ? "thawed" : "frozen";
|
|
79
|
+
return { outcome: "skipped", reason: `No change: the account is already ${state}, so Token ACL did not call the gate.` };
|
|
80
|
+
}
|
|
81
|
+
const code = decision?.kind === "ALLOW" ? decision.code : "UNKNOWN";
|
|
82
|
+
return { outcome: "allowed", code, reason: describe("ALLOW", code, action) };
|
|
83
|
+
}
|
|
84
|
+
if (decision?.kind === "DENY") return { outcome: "denied", code: decision.code, reason: describe("DENY", decision.code, action) };
|
|
85
|
+
const gateFailed = failure === null || failure.program === (opts.gateId ?? THAWGATE_GATE_ID);
|
|
86
|
+
const fromError = opts.errorCode !== void 0 && gateFailed ? GATE_DENY_ERRORS[opts.errorCode] : void 0;
|
|
87
|
+
if (fromError) return { outcome: "denied", code: fromError, reason: describe("DENY", fromError, action) };
|
|
88
|
+
if (failure) {
|
|
89
|
+
return {
|
|
90
|
+
outcome: "failed",
|
|
91
|
+
program: failure.program,
|
|
92
|
+
message: failure.message,
|
|
93
|
+
reason: `Failed before the gate decided: ${failure.program} returned "${failure.message}".`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { outcome: "failed", program: "unknown", message: "no logs", reason: "Failed, and the logs give no reason." };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { FLAG_CODES, GATE_DENY_ERRORS, STRUCTURAL_DENY_CODES, THAWGATE_GATE_ID, THAW_ALLOW_CODES, classifyGateLogs, describe, isTgCode, parseGateLogs };
|
|
100
|
+
//# sourceMappingURL=reasons.mjs.map
|
|
101
|
+
//# sourceMappingURL=reasons.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/gate/reasons.ts"],"names":[],"mappings":"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","file":"reasons.mjs","sourcesContent":["/**\n * @module gate/reasons\n * @description ThawGate's reason codes and the one parser for them.\n *\n * On every thaw and freeze decision the gate logs `TG:ALLOW:<CODE>` or `TG:DENY:<CODE>`\n * (programs/thawgate-gate/src/decision.rs). `explain()`, `freezeIfInvalid()` and the keeper all read a transaction's\n * logs through {@link classifyGateLogs}, so a wallet's reason is computed one way everywhere.\n *\n * This module has no imports. Services that don't want the SDK's Solana dependencies load it on its own:\n * `import { classifyGateLogs } from \"@thawgate/sdk/reasons\"`.\n */\n\n/** The ThawGate gating program (devnet). The same value as `THAWGATE_GATE_PROGRAM_ID`, as a string. */\nexport const THAWGATE_GATE_ID = \"THAW2daLXyUtCLtTsJWDTKZctiAGmGX4wT1kqKXugUZ\";\n\n/** Which Token ACL permissionless instruction called the gate. */\nexport type GateAction = \"thaw\" | \"freeze\";\n\n/** Why a thaw passes (`TG:ALLOW:<code>` on thaw). */\nexport const THAW_ALLOW_CODES = [\"KYC\", \"PDA_ALLOWLISTED\", \"ALLOWLISTED\", \"CLEAN\"] as const;\n/** A policy flag on the owner: `TG:DENY:<code>` on thaw, `TG:ALLOW:<code>` on freeze. */\nexport const FLAG_CODES = [\"BLACKLISTED\", \"NOT_ALLOWLISTED\", \"NO_CREDENTIAL\", \"CREDENTIAL_EXPIRED\", \"KYC_LEVEL_TOO_LOW\"] as const;\n/** Denied before any policy decision, on thaw and on freeze. */\nexport const STRUCTURAL_DENY_CODES = [\"MISSING_ACCOUNTS\", \"BAD_POLICY\", \"BAD_REGISTRY_ENTRY\", \"BAD_CREDENTIAL\"] as const;\n\nexport type ThawAllowCode = (typeof THAW_ALLOW_CODES)[number];\nexport type FlagCode = (typeof FLAG_CODES)[number];\nexport type StructuralDenyCode = (typeof STRUCTURAL_DENY_CODES)[number];\n/** Every code the gate logs. `NO_IMMUTABLE_OWNER` denies a thaw only; `COMPLIANT` denies a freeze only. */\nexport type TgCode = ThawAllowCode | FlagCode | StructuralDenyCode | \"NO_IMMUTABLE_OWNER\" | \"COMPLIANT\";\n\n/** What each code says about the owner, as the second half of a sentence. */\nconst FACTS: Record<TgCode, string> = {\n KYC: \"the owner holds a live SAS attestation from the policy's credential and schema\",\n PDA_ALLOWLISTED: \"the owner is a program address (PDA) on the issuer's allowlist (BypassForPdas)\",\n ALLOWLISTED: \"the owner is on the issuer's allowlist\",\n CLEAN: \"no policy check flags the owner\",\n BLACKLISTED: \"the owner is on the issuer's blacklist\",\n NOT_ALLOWLISTED: \"the policy is allowlist-only and the owner is not on the issuer's allowlist\",\n NO_CREDENTIAL: \"the owner has no SAS attestation from the policy's credential and schema (never issued, or revoked)\",\n CREDENTIAL_EXPIRED: \"the owner's SAS attestation has expired\",\n KYC_LEVEL_TOO_LOW: \"the owner's kyc_level is below the policy minimum\",\n NO_IMMUTABLE_OWNER: \"the token account lacks the ImmutableOwner extension (use an associated token account)\",\n MISSING_ACCOUNTS: \"the transaction left out accounts the policy needs (resolve the gate's extra account metas)\",\n BAD_POLICY: \"the policy account is not this mint's ThawGate policy\",\n BAD_REGISTRY_ENTRY: \"a blacklist or allowlist account has the wrong owner, type or fields\",\n BAD_CREDENTIAL: \"the account at the attestation address is not a SAS attestation of this credential, schema and owner\",\n COMPLIANT: \"the owner passes the policy\",\n};\n\n/**\n * The gate's custom error number for each `TG:DENY` code (errors.rs `Denied*`, 6009–6019). Used only when a\n * simulation's logs were cut before the gate's own line.\n */\nexport const GATE_DENY_ERRORS: Readonly<Record<number, TgCode>> = {\n 6009: \"MISSING_ACCOUNTS\",\n 6010: \"BAD_POLICY\",\n 6011: \"BAD_REGISTRY_ENTRY\",\n 6012: \"BAD_CREDENTIAL\",\n 6013: \"NO_IMMUTABLE_OWNER\",\n 6014: \"BLACKLISTED\",\n 6015: \"NOT_ALLOWLISTED\",\n 6016: \"NO_CREDENTIAL\",\n 6017: \"CREDENTIAL_EXPIRED\",\n 6018: \"KYC_LEVEL_TOO_LOW\",\n 6019: \"COMPLIANT\",\n};\n\nexport function isTgCode(code: string): code is TgCode {\n return Object.prototype.hasOwnProperty.call(FACTS, code);\n}\n\n/**\n * A human sentence for a gate decision, e.g. `describe(\"DENY\", \"NO_CREDENTIAL\", \"thaw\")` →\n * \"Unlock denied: the owner has no SAS attestation …\". The action matters: BLACKLISTED denies a thaw but allows a freeze.\n */\nexport function describe(decision: \"ALLOW\" | \"DENY\", code: string, action: GateAction): string {\n const fact = isTgCode(code) ? FACTS[code] : `the gate returned ${code}, a code this SDK version doesn't know`;\n if (action === \"thaw\") return `${decision === \"ALLOW\" ? \"Unlock allowed\" : \"Unlock denied\"}: ${fact}.`;\n if (decision === \"ALLOW\") return `Freezable by anyone: ${fact}.`;\n return code === \"COMPLIANT\" ? `Not freezable: ${fact}.` : `Freeze denied: ${fact}.`;\n}\n\nexport interface GateLogs {\n /** A frame of the gating program appears in the logs. */\n gateInvoked: boolean;\n /** The gate's `TG:` line: the first one inside one of its own frames. */\n decision: { kind: \"ALLOW\" | \"DENY\"; code: string } | null;\n /** The first `Program <id> failed: <message>` line. */\n failure: { program: string; message: string } | null;\n}\n\nconst INVOKE = /^Program (\\S+) invoke \\[\\d+\\]$/;\nconst EXIT = /^Program (\\S+) (?:success|failed: .*)$/;\nconst FAILED = /^Program (\\S+) failed: (.*)$/;\nconst TG_LINE = /^Program log: .*?TG:(ALLOW|DENY):([A-Z_]+)/;\n\n/**\n * Reads a transaction's (or a simulation's) logs. Only `TG:` lines logged inside the gate's own frames count, so a\n * different gating program can't impersonate a ThawGate decision. The first match wins: the gate's `msg!` line comes\n * before Anchor's error line, which repeats the code.\n */\nexport function parseGateLogs(logs: readonly string[], gateId: string = THAWGATE_GATE_ID): GateLogs {\n const stack: string[] = [];\n let gateInvoked = false;\n let decision: GateLogs[\"decision\"] = null;\n let failure: GateLogs[\"failure\"] = null;\n for (const line of logs) {\n const invoke = INVOKE.exec(line);\n if (invoke) {\n stack.push(invoke[1]);\n if (invoke[1] === gateId) gateInvoked = true;\n continue;\n }\n const failed = FAILED.exec(line);\n if (failed && !failure) failure = { program: failed[1], message: failed[2] };\n if (EXIT.test(line)) {\n stack.pop();\n continue;\n }\n if (!decision && stack[stack.length - 1] === gateId) {\n const tg = TG_LINE.exec(line);\n if (tg) decision = { kind: tg[1] as \"ALLOW\" | \"DENY\", code: tg[2] };\n }\n }\n return { gateInvoked, decision, failure };\n}\n\n/** The gate's verdict on one thaw or freeze transaction. */\nexport type GateVerdict =\n /** The gate allowed it (a thaw unlocked the account, or a freeze froze it). */\n | { outcome: \"allowed\"; code: string; reason: string }\n /** The gate denied it. On freeze, `COMPLIANT` means the owner passes the policy. */\n | { outcome: \"denied\"; code: string; reason: string }\n /** It succeeded without calling the gate: Token ACL's idempotent instructions return early on an account already\n * in the target state. */\n | { outcome: \"skipped\"; reason: string }\n /** It failed outside the gate's decision (Token ACL, Token-2022, the system program, fees, …). */\n | { outcome: \"failed\"; program: string; message: string; reason: string };\n\n/**\n * The verdict of a thaw or freeze transaction from its logs. `succeeded` is whether it landed (or simulated) without\n * error. `errorCode` is the custom error number of a failed instruction, when known: it recovers the gate's code if\n * the logs were cut.\n */\nexport function classifyGateLogs(\n logs: readonly string[],\n succeeded: boolean,\n action: GateAction,\n opts: { gateId?: string; errorCode?: number } = {},\n): GateVerdict {\n const { gateInvoked, decision, failure } = parseGateLogs(logs, opts.gateId);\n if (succeeded) {\n if (!gateInvoked) {\n const state = action === \"thaw\" ? \"thawed\" : \"frozen\";\n return { outcome: \"skipped\", reason: `No change: the account is already ${state}, so Token ACL did not call the gate.` };\n }\n const code = decision?.kind === \"ALLOW\" ? decision.code : \"UNKNOWN\";\n return { outcome: \"allowed\", code, reason: describe(\"ALLOW\", code, action) };\n }\n if (decision?.kind === \"DENY\") return { outcome: \"denied\", code: decision.code, reason: describe(\"DENY\", decision.code, action) };\n // Truncated logs (\"Log truncated\") usually lose the gate's own failed line too, so accept a missing failure line.\n // 6009–6019 don't collide with Token ACL's or Token-2022's error numbers, which are small.\n const gateFailed = failure === null || failure.program === (opts.gateId ?? THAWGATE_GATE_ID);\n const fromError = opts.errorCode !== undefined && gateFailed ? GATE_DENY_ERRORS[opts.errorCode] : undefined;\n if (fromError) return { outcome: \"denied\", code: fromError, reason: describe(\"DENY\", fromError, action) };\n if (failure) {\n return {\n outcome: \"failed\",\n program: failure.program,\n message: failure.message,\n reason: `Failed before the gate decided: ${failure.program} returned \"${failure.message}\".`,\n };\n }\n return { outcome: \"failed\", program: \"unknown\", message: \"no logs\", reason: \"Failed, and the logs give no reason.\" };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,86 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@thawgate/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for ThawGate: Token ACL gate policies, SSS-ACL stablecoins, SAS credentials",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AryaSingh22/thawgate.git",
|
|
9
|
+
"directory": "sdk"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/AryaSingh22/thawgate#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/AryaSingh22/thawgate/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./reasons": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/reasons.d.mts",
|
|
32
|
+
"default": "./dist/reasons.mjs"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/reasons.d.ts",
|
|
36
|
+
"default": "./dist/reasons.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"typesVersions": {
|
|
42
|
+
"*": {
|
|
43
|
+
"reasons": [
|
|
44
|
+
"./dist/reasons.d.ts"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=20"
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"dist",
|
|
53
|
+
"README.md"
|
|
54
|
+
],
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public",
|
|
57
|
+
"provenance": true
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup",
|
|
61
|
+
"dev": "tsup --watch",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:watch": "vitest",
|
|
65
|
+
"lint": "eslint src --ext .ts"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@coral-xyz/anchor": "^0.32.1",
|
|
69
|
+
"@solana/spl-token": "^0.4.14",
|
|
70
|
+
"@solana/web3.js": "^1.98.4",
|
|
71
|
+
"@types/bn.js": "^5.1.6",
|
|
72
|
+
"bn.js": "^5.2.1"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@solana/kit": "^5",
|
|
76
|
+
"@token-acl/sdk": "0.2.7",
|
|
77
|
+
"@types/node": "^20.0.0",
|
|
78
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
79
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
80
|
+
"eslint": "^10.0.1",
|
|
81
|
+
"sas-lib": "1.0.10",
|
|
82
|
+
"tsup": "^8.0.0",
|
|
83
|
+
"typescript": "^5.4.0",
|
|
84
|
+
"vitest": "^4.0.18"
|
|
85
|
+
}
|
|
86
|
+
}
|