@flayerlabs/gamemode-gate 0.5.6 → 0.7.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/DEPLOY.md +31 -3
- package/README.md +10 -5
- package/dist/chain/chains.d.ts +14 -6
- package/dist/chain/chains.d.ts.map +1 -1
- package/dist/chain/chains.js +11 -5
- package/dist/chain/chains.js.map +1 -1
- package/dist/chain/discover.d.ts +61 -3
- package/dist/chain/discover.d.ts.map +1 -1
- package/dist/chain/discover.js +181 -21
- package/dist/chain/discover.js.map +1 -1
- package/dist/chain/secure.d.ts +28 -0
- package/dist/chain/secure.d.ts.map +1 -1
- package/dist/chain/secure.js +55 -20
- package/dist/chain/secure.js.map +1 -1
- package/dist/chain/signer.d.ts +19 -13
- package/dist/chain/signer.d.ts.map +1 -1
- package/dist/chain/signer.js +11 -10
- package/dist/chain/signer.js.map +1 -1
- package/dist/claims.d.ts +14 -30
- package/dist/claims.d.ts.map +1 -1
- package/dist/claims.js +55 -76
- package/dist/claims.js.map +1 -1
- package/dist/demo.d.ts +1 -1
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +3 -3
- package/dist/demo.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +39 -45
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +71 -172
- package/dist/ledger.js.map +1 -1
- package/dist/main.d.ts +4 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +11 -6
- package/dist/main.js.map +1 -1
- package/dist/room.d.ts +16 -0
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +29 -6
- package/dist/room.js.map +1 -1
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +43 -34
- package/dist/server.js.map +1 -1
- package/dist/settlement.d.ts +0 -3
- package/dist/settlement.d.ts.map +1 -1
- package/dist/settlement.js +12 -44
- package/dist/settlement.js.map +1 -1
- package/dist/standings.d.ts.map +1 -1
- package/dist/standings.js +7 -1
- package/dist/standings.js.map +1 -1
- package/dist/store.d.ts +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +36 -19
- package/dist/store.js.map +1 -1
- package/dist/version.js +2 -2
- package/package.json +2 -2
- package/src/chain/chains.ts +17 -7
- package/src/chain/discover.ts +212 -21
- package/src/chain/secure.ts +75 -31
- package/src/chain/signer.ts +24 -30
- package/src/claims.ts +54 -78
- package/src/demo.ts +5 -5
- package/src/index.ts +6 -6
- package/src/ledger.ts +100 -248
- package/src/main.ts +11 -6
- package/src/room.ts +29 -3
- package/src/server.ts +58 -34
- package/src/settlement.ts +12 -43
- package/src/standings.ts +9 -1
- package/src/store.ts +36 -19
- package/src/version.ts +2 -2
package/dist/chain/secure.d.ts
CHANGED
|
@@ -18,4 +18,32 @@ export interface SecureConfig {
|
|
|
18
18
|
poolSwap?: `0x${string}`;
|
|
19
19
|
}
|
|
20
20
|
export declare function assertSecureConfig(client: PublicClient, config: SecureConfig): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* The verdict of {@link checkPositionManagerWiring}: wired, with the dispatcher the manager routes
|
|
23
|
+
* through, or not, with a reason written for the operator who has to fix it.
|
|
24
|
+
*/
|
|
25
|
+
export type PositionManagerWiring = {
|
|
26
|
+
ok: true;
|
|
27
|
+
dispatcher: `0x${string}`;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Is this manager's fee wiring routed to this calculator?
|
|
34
|
+
*
|
|
35
|
+
* The manager must route fees through a dispatcher that knows the calculator, and the calculator
|
|
36
|
+
* must accept calls from that dispatcher. Both are one-time owner actions on chain, and a stack
|
|
37
|
+
* missing either ADOPTS launches whose swaps never reach the gate: the pool trades ungated while
|
|
38
|
+
* the gate believes it is enforcing a cap. That is the whole failure this function refuses.
|
|
39
|
+
*
|
|
40
|
+
* It is also how a manager is APPROVED for a gate. The gate never holds a list of managers: a coin
|
|
41
|
+
* offered for adoption names its own manager, and the manager is accepted when these three reads
|
|
42
|
+
* say its fees reach the gate's calculator. Wiring a new manager to the dispatcher on chain is the
|
|
43
|
+
* approval, and needs no gate release. Boot asks the same question of the primary manager.
|
|
44
|
+
*/
|
|
45
|
+
export declare function checkPositionManagerWiring(client: Pick<PublicClient, 'readContract'>, wiring: {
|
|
46
|
+
positionManager: `0x${string}`;
|
|
47
|
+
spendGatedCalculator: `0x${string}`;
|
|
48
|
+
}): Promise<PositionManagerWiring>;
|
|
21
49
|
//# sourceMappingURL=secure.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure.d.ts","sourceRoot":"","sources":["../../src/chain/secure.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAelD,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,2CAA2C,CAAC;AAE5E,0GAA0G;AAC1G,eAAO,MAAM,qBAAqB,eAAsC,CAAC;AASzE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,KAAK,MAAM,EAAE,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBlG"}
|
|
1
|
+
{"version":3,"file":"secure.d.ts","sourceRoot":"","sources":["../../src/chain/secure.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAelD,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,2CAA2C,CAAC;AAE5E,0GAA0G;AAC1G,eAAO,MAAM,qBAAqB,eAAsC,CAAC;AASzE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,KAAK,MAAM,EAAE,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBlG;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,KAAK,MAAM,EAAE,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAC1C,MAAM,EAAE;IAAE,eAAe,EAAE,KAAK,MAAM,EAAE,CAAC;IAAC,oBAAoB,EAAE,KAAK,MAAM,EAAE,CAAA;CAAE,GAC9E,OAAO,CAAC,qBAAqB,CAAC,CAmDhC"}
|
package/dist/chain/secure.js
CHANGED
|
@@ -45,51 +45,86 @@ export async function assertSecureConfig(client, config) {
|
|
|
45
45
|
await assertWiring(client, config);
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Is this manager's fee wiring routed to this calculator?
|
|
49
|
+
*
|
|
50
|
+
* The manager must route fees through a dispatcher that knows the calculator, and the calculator
|
|
49
51
|
* must accept calls from that dispatcher. Both are one-time owner actions on chain, and a stack
|
|
50
52
|
* missing either ADOPTS launches whose swaps never reach the gate: the pool trades ungated while
|
|
51
53
|
* the gate believes it is enforcing a cap. That is the whole failure this function refuses.
|
|
52
54
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
55
|
+
* It is also how a manager is APPROVED for a gate. The gate never holds a list of managers: a coin
|
|
56
|
+
* offered for adoption names its own manager, and the manager is accepted when these three reads
|
|
57
|
+
* say its fees reach the gate's calculator. Wiring a new manager to the dispatcher on chain is the
|
|
58
|
+
* approval, and needs no gate release. Boot asks the same question of the primary manager.
|
|
56
59
|
*/
|
|
57
|
-
async function
|
|
58
|
-
const manager =
|
|
59
|
-
const calculator =
|
|
60
|
+
export async function checkPositionManagerWiring(client, wiring) {
|
|
61
|
+
const manager = wiring.positionManager;
|
|
62
|
+
const calculator = wiring.spendGatedCalculator;
|
|
60
63
|
let dispatcher;
|
|
61
64
|
try {
|
|
62
|
-
dispatcher = await client.readContract({
|
|
65
|
+
dispatcher = (await client.readContract({
|
|
66
|
+
address: manager,
|
|
67
|
+
abi: wiringAbi,
|
|
68
|
+
functionName: 'feeCalculator',
|
|
69
|
+
}));
|
|
63
70
|
}
|
|
64
71
|
catch {
|
|
65
|
-
|
|
72
|
+
return { ok: false, reason: `${manager} does not answer feeCalculator() — is it a Flaunch PositionManager?` };
|
|
66
73
|
}
|
|
67
74
|
const [registered, hasRole] = await Promise.all([
|
|
68
|
-
client
|
|
75
|
+
client
|
|
76
|
+
.readContract({
|
|
69
77
|
address: dispatcher,
|
|
70
78
|
abi: wiringAbi,
|
|
71
79
|
functionName: 'registeredCalculators',
|
|
72
80
|
args: [calculator],
|
|
73
|
-
})
|
|
74
|
-
|
|
81
|
+
})
|
|
82
|
+
.catch(() => false),
|
|
83
|
+
client
|
|
84
|
+
.readContract({
|
|
75
85
|
address: calculator,
|
|
76
86
|
abi: wiringAbi,
|
|
77
87
|
functionName: 'hasRole',
|
|
78
88
|
args: [POSITION_MANAGER_ROLE, dispatcher],
|
|
79
|
-
})
|
|
89
|
+
})
|
|
90
|
+
.catch(() => false),
|
|
80
91
|
]);
|
|
81
92
|
if (!registered) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
reason: `the fee dispatcher ${dispatcher} of PositionManager ${manager} does not have the spend-gated calculator ` +
|
|
96
|
+
`${calculator} registered — its owner must call setCalculatorRegistered(${calculator}, true) before ` +
|
|
97
|
+
'a gate on that calculator can serve launches on that manager',
|
|
98
|
+
};
|
|
85
99
|
}
|
|
86
100
|
if (!hasRole) {
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
reason: `the spend-gated calculator ${calculator} has not granted the PositionManager role to the dispatcher ` +
|
|
104
|
+
`${dispatcher} — its admin must call grantRole(${POSITION_MANAGER_ROLE}, ${dispatcher})`,
|
|
105
|
+
};
|
|
89
106
|
}
|
|
90
|
-
|
|
107
|
+
return { ok: true, dispatcher };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Boot's assertion about the PRIMARY manager: {@link checkPositionManagerWiring}, thrown rather
|
|
111
|
+
* than returned, because a gate whose own address book is unwired must not come up at all.
|
|
112
|
+
*
|
|
113
|
+
* A paired stack additionally needs its PoolSwap approved as a router, because a paired buy arrives
|
|
114
|
+
* through it and the calculator binds a smart wallet's buyer identity to an approved router's
|
|
115
|
+
* `msgSender()` — unapproved, every such buy reverts at the swap.
|
|
116
|
+
*/
|
|
117
|
+
async function assertWiring(client, config) {
|
|
118
|
+
const calculator = config.spendGatedCalculator;
|
|
119
|
+
const wiring = await checkPositionManagerWiring(client, {
|
|
120
|
+
positionManager: config.positionManager,
|
|
121
|
+
spendGatedCalculator: calculator,
|
|
122
|
+
});
|
|
123
|
+
if (!wiring.ok)
|
|
124
|
+
throw new Error(`POSITION_MANAGER: ${wiring.reason}`);
|
|
125
|
+
if (config.flaunchVariant === 'v1_3' || config.flaunchVariant === 'any_v1_3') {
|
|
91
126
|
if (!config.poolSwap) {
|
|
92
|
-
throw new Error(
|
|
127
|
+
throw new Error(`a ${config.flaunchVariant} stack needs POOL_SWAP — the router a paired-token buy goes through`);
|
|
93
128
|
}
|
|
94
129
|
const approved = await client
|
|
95
130
|
.readContract({ address: calculator, abi: wiringAbi, functionName: 'approvedRouters', args: [config.poolSwap] })
|
package/dist/chain/secure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure.js","sourceRoot":"","sources":["../../src/chain/secure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAqB,MAAM,MAAM,CAAC;AAGrE;;;;;;;GAOG;AAEH,mGAAmG;AACnG,MAAM,gBAAgB,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAChD,MAAM,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAExC,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,wCAAwC,CAAC;AAE5E,0GAA0G;AAC1G,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAEzE,MAAM,SAAS,GAAG,QAAQ,CAAC;IACzB,iDAAiD;IACjD,6DAA6D;IAC7D,qEAAqE;IACrE,uDAAuD;CACxD,CAAC,CAAC;AAgBH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAoB,EAAE,MAAoB;IACjF,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC;IAClD,IAAI,GAAG,KAAK,gBAAgB,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,MAAM,CAAC,aAAa,KAAK,mBAAmB,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IAED,gGAAgG;IAChG,gGAAgG;IAChG,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1C,IAAI,OAAO,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,qBAAqB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,+FAA+F;IAC/F,4FAA4F;IAC5F,mBAAmB;IACnB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,oBAAoB,2BAA2B,OAAO,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,IAAI,MAAM,CAAC,eAAe;QAAE,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACjE,CAAC;
|
|
1
|
+
{"version":3,"file":"secure.js","sourceRoot":"","sources":["../../src/chain/secure.ts"],"names":[],"mappings":"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"}
|
package/dist/chain/signer.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { type GateReadiness } from '@flayerlabs/gamemode-spec/embed';
|
|
2
2
|
/**
|
|
3
|
-
* Issues the signed
|
|
3
|
+
* Issues the signed authorisations SpendGatedSignerFeeCalculator.trackSwap verifies.
|
|
4
4
|
*
|
|
5
|
-
* EIP-712, over the `SpendAuthorization` struct the calculator declares
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* EIP-712, over the `SpendAuthorization` struct the calculator declares, under the
|
|
6
|
+
* `FlaunchSpendGate` version `2` domain. The struct carries a cumulative ceiling: the most this
|
|
7
|
+
* buyer may EVER have spent on this pool, which the calculator compares against its own
|
|
8
|
+
* `walletSpentWei` accumulator. There is no nonce and no per-purchase amount, because there is
|
|
9
|
+
* nothing to consume — two authorisations with the same ceiling cannot add up, so reusing one is
|
|
10
|
+
* harmless by construction and the gate holds nothing against it.
|
|
11
|
+
*
|
|
12
|
+
* The domain matters: the protocol deploys deterministically across chains, so the same poolId
|
|
13
|
+
* genuinely recurs, and one signing key shared between two environments would otherwise make an
|
|
14
|
+
* authorisation issued for one valid on the other. Version `2` also keeps 0.5.x signatures (a
|
|
15
|
+
* five-field struct with a nonce) from verifying on this calculator and vice versa.
|
|
12
16
|
*
|
|
13
17
|
* The digest MUST match `hashSpendAuthorization` on the calculator exactly. That function is
|
|
14
18
|
* public for this reason — a domain drift is otherwise a silent `InvalidSigner` at swap time
|
|
@@ -19,8 +23,7 @@ export interface SignedPayload {
|
|
|
19
23
|
buyer: `0x${string}`;
|
|
20
24
|
poolId: `0x${string}`;
|
|
21
25
|
deadline: bigint;
|
|
22
|
-
|
|
23
|
-
nonce: bigint;
|
|
26
|
+
spendCeilingWei: bigint;
|
|
24
27
|
signature: `0x${string}`;
|
|
25
28
|
signer: `0x${string}`;
|
|
26
29
|
}
|
|
@@ -38,12 +41,15 @@ export declare class PayloadSigner {
|
|
|
38
41
|
/** Fixed non-spend payload. The caller cannot choose a domain, type or deadline. */
|
|
39
42
|
readiness(origin: string, positionManager: `0x${string}`, nonce: `0x${string}`): Promise<GateReadiness>;
|
|
40
43
|
/** The digest the calculator reconstructs; mirrors its `hashSpendAuthorization`. */
|
|
41
|
-
messageHash(buyer: `0x${string}`, poolId: `0x${string}`, deadline: bigint,
|
|
42
|
-
sign(buyer: `0x${string}`, poolId: `0x${string}`, deadline: bigint,
|
|
44
|
+
messageHash(buyer: `0x${string}`, poolId: `0x${string}`, deadline: bigint, spendCeilingWei: bigint): `0x${string}`;
|
|
45
|
+
sign(buyer: `0x${string}`, poolId: `0x${string}`, deadline: bigint, spendCeilingWei: bigint): Promise<SignedPayload>;
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* Encodes a signed payload as the hookData the swap must carry:
|
|
46
|
-
* `abi.encode(address referrer, SignedMessage{buyer, poolId, deadline,
|
|
49
|
+
* `abi.encode(address referrer, SignedMessage{buyer, poolId, deadline, spendCeilingWei, signature})`
|
|
50
|
+
*
|
|
51
|
+
* The referrer word is always zero. The calculator ignores it, but the trusted page checks it, so
|
|
52
|
+
* a creator iframe cannot redirect attribution while keeping a valid signature.
|
|
47
53
|
*/
|
|
48
54
|
export declare function encodeHookData(payload: SignedPayload): `0x${string}`;
|
|
49
55
|
//# sourceMappingURL=signer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/chain/signer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,KAAK,aAAa,EAA+B,MAAM,iCAAiC,CAAC;AAEjJ
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/chain/signer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,KAAK,aAAa,EAA+B,MAAM,iCAAiC,CAAC;AAEjJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,aAAa;IAUtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAVpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C;;;;OAIG;gBAED,UAAU,EAAE,KAAK,MAAM,EAAE,EACR,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,KAAK,MAAM,EAAE;IAKnD,IAAI,OAAO,IAAI,KAAK,MAAM,EAAE,CAE3B;IAED,oFAAoF;IAC9E,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ7G,oFAAoF;IACpF,WAAW,CACT,KAAK,EAAE,KAAK,MAAM,EAAE,EACpB,MAAM,EAAE,KAAK,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,GACtB,KAAK,MAAM,EAAE;IAMV,IAAI,CACR,KAAK,EAAE,KAAK,MAAM,EAAE,EACpB,MAAM,EAAE,KAAK,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC;CAM1B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,KAAK,MAAM,EAAE,CA0BpE"}
|
package/dist/chain/signer.js
CHANGED
|
@@ -27,17 +27,20 @@ export class PayloadSigner {
|
|
|
27
27
|
return { ...proof, signature: await this.account.signTypedData(gateReadinessTypedData(proof)) };
|
|
28
28
|
}
|
|
29
29
|
/** The digest the calculator reconstructs; mirrors its `hashSpendAuthorization`. */
|
|
30
|
-
messageHash(buyer, poolId, deadline,
|
|
31
|
-
return hashTypedData(spendAuthorisationTypedData({ buyer, poolId, deadline,
|
|
30
|
+
messageHash(buyer, poolId, deadline, spendCeilingWei) {
|
|
31
|
+
return hashTypedData(spendAuthorisationTypedData({ buyer, poolId, deadline, spendCeilingWei }, this.chainId, this.verifyingContract));
|
|
32
32
|
}
|
|
33
|
-
async sign(buyer, poolId, deadline,
|
|
34
|
-
const signature = await this.account.signTypedData(spendAuthorisationTypedData({ buyer, poolId, deadline,
|
|
35
|
-
return { buyer, poolId, deadline,
|
|
33
|
+
async sign(buyer, poolId, deadline, spendCeilingWei) {
|
|
34
|
+
const signature = await this.account.signTypedData(spendAuthorisationTypedData({ buyer, poolId, deadline, spendCeilingWei }, this.chainId, this.verifyingContract));
|
|
35
|
+
return { buyer, poolId, deadline, spendCeilingWei, signature, signer: this.account.address };
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Encodes a signed payload as the hookData the swap must carry:
|
|
40
|
-
* `abi.encode(address referrer, SignedMessage{buyer, poolId, deadline,
|
|
40
|
+
* `abi.encode(address referrer, SignedMessage{buyer, poolId, deadline, spendCeilingWei, signature})`
|
|
41
|
+
*
|
|
42
|
+
* The referrer word is always zero. The calculator ignores it, but the trusted page checks it, so
|
|
43
|
+
* a creator iframe cannot redirect attribution while keeping a valid signature.
|
|
41
44
|
*/
|
|
42
45
|
export function encodeHookData(payload) {
|
|
43
46
|
return encodeAbiParameters([
|
|
@@ -48,8 +51,7 @@ export function encodeHookData(payload) {
|
|
|
48
51
|
{ name: 'buyer', type: 'address' },
|
|
49
52
|
{ name: 'poolId', type: 'bytes32' },
|
|
50
53
|
{ name: 'deadline', type: 'uint256' },
|
|
51
|
-
{ name: '
|
|
52
|
-
{ name: 'nonce', type: 'uint256' },
|
|
54
|
+
{ name: 'spendCeilingWei', type: 'uint256' },
|
|
53
55
|
{ name: 'signature', type: 'bytes' },
|
|
54
56
|
],
|
|
55
57
|
},
|
|
@@ -59,8 +61,7 @@ export function encodeHookData(payload) {
|
|
|
59
61
|
buyer: payload.buyer,
|
|
60
62
|
poolId: payload.poolId,
|
|
61
63
|
deadline: payload.deadline,
|
|
62
|
-
|
|
63
|
-
nonce: payload.nonce,
|
|
64
|
+
spendCeilingWei: payload.spendCeilingWei,
|
|
64
65
|
signature: payload.signature,
|
|
65
66
|
},
|
|
66
67
|
]);
|
package/dist/chain/signer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/chain/signer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAA0B,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAsB,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/chain/signer.ts"],"names":[],"mappings":"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"}
|
package/dist/claims.d.ts
CHANGED
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
import type { Pool } from 'pg';
|
|
2
2
|
import type { PlayerId } from '@flayerlabs/gamemode-spec';
|
|
3
3
|
import type { EconomyBalance } from '@flayerlabs/gamemode-spec/live';
|
|
4
|
-
import { Ledger
|
|
4
|
+
import { Ledger } from './ledger.js';
|
|
5
5
|
import type { PayloadSigner, SignedPayload } from './chain/signer.js';
|
|
6
|
-
/**
|
|
7
|
-
* Turning points into something a player can actually spend.
|
|
8
|
-
*
|
|
9
|
-
* Two parties have to agree before a buy can happen, and they check different things. The ledger
|
|
10
|
-
* knows what this player earned and what they have already been authorised for. The signer knows
|
|
11
|
-
* whether it is willing to vouch for this pool at all. Only when both say yes does an
|
|
12
|
-
* authorisation exist.
|
|
13
|
-
*
|
|
14
|
-
* The order matters and is not interchangeable: the hold is recorded first, then signed. A
|
|
15
|
-
* signature that exists without a hold is spending nobody is tracking; a hold without a signature
|
|
16
|
-
* is a refund waiting to happen. Only one of those costs money.
|
|
17
|
-
*/
|
|
18
|
-
/** How long an authorisation stays valid. Long enough to approve in a wallet, short enough that an
|
|
19
|
-
* abandoned one does not sit around spendable. */
|
|
20
|
-
export declare const CLAIM_AUTHORIZATION_TTL_MS: number;
|
|
21
6
|
/** Request ids cross an unauthenticated parser before they reach Postgres, so bound them here too. */
|
|
22
7
|
export declare function isClaimRequestId(value: unknown): value is string;
|
|
23
8
|
/** The signed message carries uint256, while JavaScript's bigint has no upper bound. */
|
|
24
9
|
export declare function isClaimAmount(value: unknown): value is bigint;
|
|
25
10
|
export type Authorisation = {
|
|
26
11
|
ok: true;
|
|
27
|
-
payload: SignedPayload;
|
|
12
|
+
payload: SignedPayload; /** Exact-in amount for this buy, clamped to what was available. */
|
|
13
|
+
spend: bigint;
|
|
28
14
|
} | {
|
|
29
15
|
ok: false;
|
|
30
16
|
refuse: string;
|
|
@@ -33,27 +19,25 @@ export declare class Claims {
|
|
|
33
19
|
private readonly pool;
|
|
34
20
|
private readonly ledger;
|
|
35
21
|
private readonly signer;
|
|
36
|
-
/** Called when a hold could neither be signed nor released, and needs a human. */
|
|
37
|
-
private readonly onStrandedHold?;
|
|
38
22
|
/** An internal seam for exact window-boundary tests. Production uses the process clock. */
|
|
39
23
|
private readonly now;
|
|
40
24
|
constructor(pool: Pool, ledger: Ledger, signer: PayloadSigner,
|
|
41
|
-
/** Called when a hold could neither be signed nor released, and needs a human. */
|
|
42
|
-
onStrandedHold?: ((nonce: bigint, error: unknown) => void) | undefined,
|
|
43
25
|
/** An internal seam for exact window-boundary tests. Production uses the process clock. */
|
|
44
26
|
now?: () => number);
|
|
45
27
|
/**
|
|
46
|
-
* Authorise a player to spend up to `
|
|
28
|
+
* Authorise a player to spend up to `spendWei`, or explain why not.
|
|
47
29
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
30
|
+
* Returns the largest amount available rather than refusing an over-ask: a player pressing buy
|
|
31
|
+
* wants to spend what they have, and telling them "you asked for too much" when they asked for
|
|
32
|
+
* everything is a worse product than giving them everything. The ceiling in the signature is the
|
|
33
|
+
* same either way — it is what they have earned, not what they asked for.
|
|
34
|
+
*
|
|
35
|
+
* The ledger's view of spend is a conservative mirror of the chain's. Being behind it means
|
|
36
|
+
* signing a ceiling the calculator will refuse at the swap, which to a player is
|
|
37
|
+
* indistinguishable from being robbed; settlement keeps the gap to one reconciliation pass.
|
|
52
38
|
*/
|
|
53
|
-
authorise(roundId: string, player: PlayerId,
|
|
54
|
-
private
|
|
55
|
-
/** Give back every authorisation that expired unspent. */
|
|
56
|
-
releaseExpired(): Promise<BalanceKey[]>;
|
|
39
|
+
authorise(roundId: string, player: PlayerId, spendWei: bigint, requestId: string): Promise<Authorisation>;
|
|
40
|
+
private answer;
|
|
57
41
|
/** What this player could spend right now. */
|
|
58
42
|
available(roundId: string, player: PlayerId): Promise<bigint>;
|
|
59
43
|
/** Full authoritative balance for live snapshots. */
|
package/dist/claims.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EAAE,MAAM,EAA6C,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAkBtE,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE7D;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC,CAAC,mEAAmE;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvH;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAJH,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa;IACtC,2FAA2F;IAC1E,GAAG,GAAE,MAAM,MAAiB;IAG/C;;;;;;;;;;;OAWG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAgC/G,OAAO,CAAC,MAAM;IAgBd,8CAA8C;IAC9C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7D,qDAAqD;IACrD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;CAG3E"}
|
package/dist/claims.js
CHANGED
|
@@ -4,18 +4,15 @@ import { Ledger } from './ledger.js';
|
|
|
4
4
|
* Turning points into something a player can actually spend.
|
|
5
5
|
*
|
|
6
6
|
* Two parties have to agree before a buy can happen, and they check different things. The ledger
|
|
7
|
-
* knows what this player earned and what
|
|
8
|
-
*
|
|
9
|
-
* authorisation exist.
|
|
7
|
+
* knows what this player earned and what the chain says they have spent. The signer knows whether
|
|
8
|
+
* it is willing to vouch for this pool at all. Only when both say yes does an authorisation exist.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* What gets signed is a ceiling, not a purchase: the most this wallet may ever have spent on the
|
|
11
|
+
* pool. The calculator compares it against its own running total, so the gate reserves nothing
|
|
12
|
+
* and a signature that is never used costs nobody anything. The one thing this file must get
|
|
13
|
+
* right is idempotency — a retried request must receive the same bytes — and that is a primary
|
|
14
|
+
* key, not a lock.
|
|
14
15
|
*/
|
|
15
|
-
/** How long an authorisation stays valid. Long enough to approve in a wallet, short enough that an
|
|
16
|
-
* abandoned one does not sit around spendable. */
|
|
17
|
-
export const CLAIM_AUTHORIZATION_TTL_MS = 5 * 60_000;
|
|
18
|
-
const DEADLINE_SECONDS = CLAIM_AUTHORIZATION_TTL_MS / 1_000;
|
|
19
16
|
const REQUEST_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
20
17
|
/** Request ids cross an unauthenticated parser before they reach Postgres, so bound them here too. */
|
|
21
18
|
export function isClaimRequestId(value) {
|
|
@@ -29,79 +26,74 @@ export class Claims {
|
|
|
29
26
|
pool;
|
|
30
27
|
ledger;
|
|
31
28
|
signer;
|
|
32
|
-
onStrandedHold;
|
|
33
29
|
now;
|
|
34
30
|
constructor(pool, ledger, signer,
|
|
35
|
-
/** Called when a hold could neither be signed nor released, and needs a human. */
|
|
36
|
-
onStrandedHold,
|
|
37
31
|
/** An internal seam for exact window-boundary tests. Production uses the process clock. */
|
|
38
32
|
now = Date.now) {
|
|
39
33
|
this.pool = pool;
|
|
40
34
|
this.ledger = ledger;
|
|
41
35
|
this.signer = signer;
|
|
42
|
-
this.onStrandedHold = onStrandedHold;
|
|
43
36
|
this.now = now;
|
|
44
37
|
}
|
|
45
38
|
/**
|
|
46
|
-
* Authorise a player to spend up to `
|
|
39
|
+
* Authorise a player to spend up to `spendWei`, or explain why not.
|
|
47
40
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
41
|
+
* Returns the largest amount available rather than refusing an over-ask: a player pressing buy
|
|
42
|
+
* wants to spend what they have, and telling them "you asked for too much" when they asked for
|
|
43
|
+
* everything is a worse product than giving them everything. The ceiling in the signature is the
|
|
44
|
+
* same either way — it is what they have earned, not what they asked for.
|
|
45
|
+
*
|
|
46
|
+
* The ledger's view of spend is a conservative mirror of the chain's. Being behind it means
|
|
47
|
+
* signing a ceiling the calculator will refuse at the swap, which to a player is
|
|
48
|
+
* indistinguishable from being robbed; settlement keeps the gap to one reconciliation pass.
|
|
52
49
|
*/
|
|
53
|
-
async authorise(roundId, player,
|
|
50
|
+
async authorise(roundId, player, spendWei, requestId) {
|
|
54
51
|
if (!isClaimRequestId(requestId))
|
|
55
52
|
return { ok: false, refuse: 'claim.request_invalid' };
|
|
56
|
-
if (!isClaimAmount(
|
|
53
|
+
if (!isClaimAmount(spendWei))
|
|
57
54
|
return { ok: false, refuse: 'claim.amount_invalid' };
|
|
58
55
|
const round = await this.pool.query(`select pool_id from rounds where id = $1`, [roundId]);
|
|
59
56
|
const row = round.rows[0];
|
|
60
57
|
if (!row)
|
|
61
58
|
return { ok: false, refuse: 'claim.no_such_round' };
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
70
|
-
return { ok: false, refuse:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const completed = await this.ledger.completeClaim(claim.nonce, candidate.signature, candidate.signer);
|
|
85
|
-
if (!completed?.signature || !completed.signer)
|
|
86
|
-
throw new Error('claim could not be completed');
|
|
87
|
-
return { ok: true, payload: payload(poolId, completed) };
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
// The hold was taken and nothing will ever be spent against it, so it goes back. Without
|
|
91
|
-
// this, a signer outage quietly eats a player's allowance for the rest of the round.
|
|
92
|
-
//
|
|
93
|
-
// If the release ALSO fails, the original error still wins. Replacing it with a database
|
|
94
|
-
// error would hide the reason and leave the nonce nowhere in the logs — so it is named here,
|
|
95
|
-
// because that number is what a person needs to give the allowance back by hand.
|
|
96
|
-
await this.ledger.releaseUnsigned(claim.nonce).catch((releaseError) => {
|
|
97
|
-
this.onStrandedHold?.(claim.nonce, releaseError);
|
|
98
|
-
});
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
59
|
+
const poolId = row.pool_id;
|
|
60
|
+
// An existing request deliberately wins over the clock and the current balance: it was issued
|
|
61
|
+
// under the earlier call's window, and a retry must get the same answer.
|
|
62
|
+
const existing = await this.ledger.findAuthorisation(roundId, player, requestId);
|
|
63
|
+
if (existing)
|
|
64
|
+
return this.answer(poolId, existing, spendWei);
|
|
65
|
+
const quote = await this.ledger.quote(roundId, player, this.now());
|
|
66
|
+
if (!quote.ok)
|
|
67
|
+
return { ok: false, refuse: quote.refuse };
|
|
68
|
+
const spend = spendWei < quote.available ? spendWei : quote.available;
|
|
69
|
+
const signed = await this.signer.sign(player, poolId, quote.deadlineSeconds, quote.ceiling);
|
|
70
|
+
const stored = await this.ledger.storeAuthorisation(roundId, {
|
|
71
|
+
player,
|
|
72
|
+
requestId,
|
|
73
|
+
requestedWei: spendWei,
|
|
74
|
+
spendCeilingWei: signed.spendCeilingWei,
|
|
75
|
+
spendWei: spend,
|
|
76
|
+
deadline: signed.deadline,
|
|
77
|
+
signature: signed.signature,
|
|
78
|
+
signer: signed.signer,
|
|
79
|
+
});
|
|
80
|
+
return this.answer(poolId, stored, spendWei);
|
|
101
81
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
answer(poolId, stored, requestedWei) {
|
|
83
|
+
if (stored.requestedWei !== requestedWei)
|
|
84
|
+
return { ok: false, refuse: 'claim.request_mismatch' };
|
|
85
|
+
return {
|
|
86
|
+
ok: true,
|
|
87
|
+
payload: {
|
|
88
|
+
buyer: stored.player,
|
|
89
|
+
poolId,
|
|
90
|
+
deadline: stored.deadline,
|
|
91
|
+
spendCeilingWei: stored.spendCeilingWei,
|
|
92
|
+
signature: stored.signature,
|
|
93
|
+
signer: stored.signer,
|
|
94
|
+
},
|
|
95
|
+
spend: stored.spendWei,
|
|
96
|
+
};
|
|
105
97
|
}
|
|
106
98
|
/** What this player could spend right now. */
|
|
107
99
|
available(roundId, player) {
|
|
@@ -112,17 +104,4 @@ export class Claims {
|
|
|
112
104
|
return this.ledger.economyBalance(roundId, player);
|
|
113
105
|
}
|
|
114
106
|
}
|
|
115
|
-
function payload(poolId, claim) {
|
|
116
|
-
if (!claim.signature || !claim.signer)
|
|
117
|
-
throw new Error('claim is not signed');
|
|
118
|
-
return {
|
|
119
|
-
buyer: claim.player,
|
|
120
|
-
poolId: poolId,
|
|
121
|
-
deadline: claim.deadline,
|
|
122
|
-
maxSpendWei: claim.wei,
|
|
123
|
-
nonce: claim.nonce,
|
|
124
|
-
signature: claim.signature,
|
|
125
|
-
signer: claim.signer,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
107
|
//# sourceMappingURL=claims.js.map
|
package/dist/claims.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,MAAM,EAA6C,MAAM,aAAa,CAAC;AAGhF;;;;;;;;;;;;GAYG;AAEH,MAAM,UAAU,GAAG,qCAAqC,CAAC;AAEzD,sGAAsG;AACtG,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,IAAI,UAAU,CAAC;AACxE,CAAC;AAMD,MAAM,OAAO,MAAM;IAEE;IACA;IACA;IAEA;IALnB,YACmB,IAAU,EACV,MAAc,EACd,MAAqB;IACtC,2FAA2F;IAC1E,MAAoB,IAAI,CAAC,GAAG;QAJ5B,SAAI,GAAJ,IAAI,CAAM;QACV,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAe;QAErB,QAAG,GAAH,GAAG,CAAyB;IAC5C,CAAC;IAEJ;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,MAAgB,EAAE,QAAgB,EAAE,SAAiB;QACpF,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QACxF,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QAEnF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAsB,0CAA0C,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAChH,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,GAAG,CAAC,OAAwB,CAAC;QAE5C,8FAA8F;QAC9F,yEAAyE;QACzE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACjF,IAAI,QAAQ;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAE7D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAE1D,MAAM,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAuB,EAAE,MAAM,EAAE,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7G,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE;YAC3D,MAAM;YACN,SAAS;YACT,YAAY,EAAE,QAAQ;YACtB,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAEO,MAAM,CAAC,MAAqB,EAAE,MAA2B,EAAE,YAAoB;QACrF,IAAI,MAAM,CAAC,YAAY,KAAK,YAAY;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QACjG,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE;gBACP,KAAK,EAAE,MAAM,CAAC,MAAuB;gBACrC,MAAM;gBACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;aACtB;YACD,KAAK,EAAE,MAAM,CAAC,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,SAAS,CAAC,OAAe,EAAE,MAAgB;QACzC,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,qDAAqD;IACrD,cAAc,CAAC,OAAe,EAAE,MAAgB;QAC9C,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;CACF"}
|
package/dist/demo.d.ts
CHANGED
package/dist/demo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA+B5D,MAAM,WAAW,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;IAC/E,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,eAAe,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,aAAa,EAAE,KAAK,MAAM,EAAE,CAAC;IAC7B,wFAAwF;IACxF,SAAS,EAAE;QAAE,
|
|
1
|
+
{"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA+B5D,MAAM,WAAW,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;IAC/E,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,eAAe,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,aAAa,EAAE,KAAK,MAAM,EAAE,CAAC;IAC7B,wFAAwF;IACxF,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;CACf;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EACvF,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,GACzE,OAAO,CAAC,IAAI,CAAC,CAmGf"}
|